Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Ezpublish Legacy Laravel Package

ezsystems/ezpublish-legacy

Legacy eZ Publish CMS codebase from eZ Systems, providing the classic PHP content management platform and extension APIs. Maintained for existing installations and migrations only; not recommended for new projects.

Deep Wiki
Context7

Getting Started

ezpublish-legacy is not a package you composer require into a Laravel app and call from a controller. It is a monolithic application kernel — it ships its own autoloader, front controllers (index.php, index_*.php), INI-based configuration cascade, template engine (XSLT), cache layer, and MySQL schema. Day-to-day, you are either maintaining a legacy eZ Publish install, or you are integrating it beside a Laravel app (eZ Platform legacy bridge style), not inside one.

Minimal steps to get oriented:

  1. Identify the install layout. Look for ezpublish_legacy/ (or the repo root), settings/, extension/, var/, and bin/php/. The front controller (index.php) and settings/siteaccess/<name>/ are your two entry points for understanding routing and config.
  2. Get it running in isolation first. PHP 7.4-era runtime, MySQL, extensions: mysqli, mbstring, gd, curl, xsl, json. Point a vhost at the docroot and verify siteaccess matching works before touching Laravel.
  3. Learn the config cascade. settings/override/*.ini.append.php overrides settings/*.ini, and settings/siteaccess/<sa>/*.ini.append.php overrides both. This is how you'll change almost anything — you rarely edit core INI files.
  4. Read bin/php/ezcache.php and bin/php/ezpgenerateautoloads.php. These two commands are 80% of your operational life.

First use case: a Laravel route needs content that lives in eZ Publish (a product page, an article). Start with the HTTP boundary — either a reverse-proxy split (/legacy/* → eZ Publish PHP-FPM pool) or a server-side subrequest — rather than trying to bootstrap the kernel in a Laravel process. Get one legacy page rendering from a Laravel-driven link before attempting in-process API calls.

Implementation Patterns

Two-runtime architecture (the sane default). Keep eZ Publish on its own PHP-FPM pool and vhost; front it with Laravel or nginx. Laravel owns new routes, legacy owns content rendering. Configure nginx location blocks so /content/..., /extension/..., /var/... go to legacy and everything else to Laravel, or use Laravel's Route::fallback() to proxy unmatched paths.

Data access patterns, safest first:

  • Legacy REST API (v2) — ezpublish_rest extension, /api/ezp/v2/content/objects/{id} with session auth or basic auth. Cleanest boundary; returns JSON you can consume with Laravel's HTTP client and cache in Redis. Best when you only read content.
  • Direct DB reads (read-only) — query ezcontentobject, ezcontentobject_attribute, ezcontentobject_tree, ezcontentclass_attribute via Laravel's query builder. Enormous throughput win, but you must replicate eZ's version/state filtering: ezcontentobject.status = 1 (published), ezcontentobject_tree.main_node_id, and the ezcontentobject_attribute.language_code matching the siteaccess. Do not write.
  • In-process bootstrap (last resort) — only inside the legacy PHP-FPM pool, in a small custom module or script. Use eZScript for CLI, eZCLI::instance() for output, and the object API:
$object = eZContentObject::fetch($objectId);          // null if not found
$node   = eZContentObjectTreeNode::fetchByURLPath('/products/widget');
$result = eZContentFunctions::createAndPublishObject([
    'parent_node_id' => $parentNodeId,
    'class_identifier' => 'article',
    'attributes' => ['title' => 'Hello'],
]);

Content creation/update. Always go through eZContentFunctions::createAndPublishObject() or eZContentOperationCollection — never raw INSERTs. eZ maintains object versions, ezcontentobject_tree paths, name patterns, and cache triggers; skipping the API corrupts all four.

Schema and migrations. Legacy has no migrations. Schema lives in kernel/sql/mysql/ plus per-extension sql/ files, applied by bin/php/ezmysqldb.php / extension sql/mysql/*.sql. For Laravel-side tables, use normal migrations, but keep them in a separate connection/prefix so the two schemas don't collide.

Extension points you'll actually use:

  • extension/<name>/settings/*.ini.append.php for config.
  • extension/<name>/modules/<module>/ for new routes (registered in module.ini.append.php).
  • extension/<name>/datatypes/ for new content types' storage/validation.
  • extension/<name>/template/operators/ for XSLT template operators.
  • workflow/eventtypes/ for hooks on content publish — very commonly used to push to Laravel or an index.

Auth bridge. Don't share sessions. Either: (a) Laravel issues its own session and you trust a signed header/SSO token in legacy via a custom login handler, or (b) legacy handles login and Laravel reads ezuser rows (hashed with eZ's own scheme — do not reimplement, use eZUser::fetch() or an endpoint). Shared cookies with different names, scoped to paths.

Search. Legacy's built-in search is slow and limited. If you need real search, index content into Laravel-side Scout/Meilisearch on the content/publish workflow trigger, not by polling.

Gotchas and Tips

PHP version is the hard wall. Last release 2020-10-16, archived, no further feature releases. The kernel targets PHP 7.x (7.4 works with deprecation noise); it is not PHP 8 compatible. A modern Laravel app on PHP 8.1+ therefore cannot load this code in-process. Budget for two PHP runtimes, and treat any "just require it" plan as a non-starter.

No security patches. Archived with NOASSERTION license metadata and no further releases. Isolate it behind a proxy, restrict admin (/ezinfo, /user/login, ezjscore) paths, and log everything. This is the single biggest risk.

Autoloader collisions. Legacy ships its own autoload/ and bundled eZ Components (ezc* classes) with global names. If you ever load both Composer and legacy autoloaders in one process, expect class redeclarations. Keep them in separate processes.

INI cache is aggressive and sticky. After editing any .ini.append.php, you get stale values until you clear cache: php bin/php/ezcache.php --clear-all (or targeted --clear-id=ini). In dev, set EZP_INI_CACHE=0 / [TemplateSettings] DevelopmentMode=enabled and [DebugSettings] DebugOutput=inline; Debug=debug writes to var/log/error.log or var/log/<siteaccess>/error.log. Most "my config change did nothing" tickets are this.

autoload must be regenerated after adding extension classes: php bin/php/ezpgenerateautoloads.php -e or your new class is invisible.

Siteaccess matching bites nginx configs. eZ relies on index.php and host/path prefix matching. If you put it behind a proxy that rewrites Host or X-Forwarded-Path, siteaccess resolution silently falls to the default — usually the wrong language, wrong design, wrong DB. Pin an explicit siteaccess match rule for the proxied host.

View caching lies to you. content/viewcache and template block caching will serve a page for minutes after a content update if you bypass the publish API (see above). If content "isn't updating," check whether the update happened through eZContentFunctions, not just whether the DB row changed.

Charset pitfalls. Old installs use utf8 (3-byte), not utf8mb4. Emoji and 4-byte characters truncate or throw. If you migrate data to Laravel, do the conversion in a one-shot script with explicit ALTER TABLE ... CONVERT TO CHARACTER SET utf8mb4, and test the legacy side after — it may still expect utf8.

Dates and timezone. Legacy stores timestamps as integers and formats per [TimeZoneSettings] INI, not php.ini. If you read published/modified columns directly from Laravel, expect UTC-ish integers you must convert yourself; don't assume Carbon defaults match what the legacy frontend shows.

Don't dual-write. The most common production incident in hybrid setups: a Laravel admin writes the same content tables that eZ Publish owns. Either make Laravel the system of record (and have eZ read from it via a custom datatype — significant work) or make eZ the system of record and have Laravel write only through legacy APIs/endpoints. Never both.

Cluster/cache handlers are the escape hatch. If you must squeeze more life out of it, [ClusterSettings] and custom cache handlers let you offload sessions and cache to Redis, and eZPersistentObject gives you a quick model layer for small custom tables without full datatypes.

Plan the exit. With no upstream, the realistic mid-term answer is: freeze legacy content read-only, export to Laravel-side models (or eZ Platform), and keep legacy serving old URLs until redirects are proven. Design your integration boundary now so that swap is a routing change, not a rewrite.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource