5.4 Migration Guide
The 5.4.0 release is backwards compatible with 5.0. It adds new functionality and introduces new deprecations. Any functionality deprecated in 5.x will be removed in 6.0.0.
Upgrade Tool
The upgrade tool provides rector rules for automating some of the migration work. Run rector before updating your composer.json dependencies:
bin/cake upgrade rector --rules cakephp54 <path/to/app/src>Behavior Changes
Commands
BaseCommand::initialize()is now being triggered AFTER arguments and options have been parsed.- Command instances are now added to the event manager by default. This means, that event hook methods like
beforeExecuteandafterExecuteare always triggered. To avoid duplicate triggering you should remove any existing calls attaching a command to the event manager from your app code.
Console
Running bin/cake without providing a command name no longer displays the "No command provided" error message. Instead, the help command is shown directly.
Unknown tokens after a parent command with subcommands are now rejected (e.g. bin/cake i18n nonsense) instead of silently invoking the parent. See Subcommand Validation.
The help command is now hidden from command listings (via CommandHiddenInterface). It remains accessible by running bin/cake help or bin/cake help <command>.
The CakePHP version header in help output is now only shown when the CakePHP version can be determined. When used outside a CakePHP application (where the version is reported as unknown), the header is omitted.
Events
Events being registered in either Application::events() or Plugin::events() now work in both web and CLI contexts. It is therefore highly recommended to move your event listeners from the config/bootstrap.php file to the eventListeners() method in your Application or Plugin class. Use the events() method when you need custom registration logic or anonymous listeners. See Application and Plugin Events for more details.
Application::eventListeners() and Plugin::eventListeners() were added to register event listener classes declaratively. These listeners are resolved through the application's dependency injection container, so they can use constructor-injected dependencies.
EventAwareApplicationInterface::pluginEvents() has been deprecated. Plugin events are now registered while each plugin is bootstrapped.
I18n
Number::parseFloat()now returnsnullinstead of0.0when parsing fails. This also affectsFloatTypeandDecimalTypedatabase types.
ORM
- The default eager loading strategy for
HasManyandBelongsToManyassociations has changed fromselecttosubquery. If you need the previous behavior, explicitly set'strategy' => 'select'when defining associations. See Associations for more details. Model.afterSaveCommitandModel.afterDeleteCommitevents are now fired whensave()ordelete()is called inside an outer transaction. Previously, these events were silently suppressed. They are now deferred until the outermost transaction commits, and discarded on rollback. See Table Objects for more details.- Table methods
save(),delete(),patchEntity(),patchEntities()andloadInto()will now throw an exception if the entity being passed down does not belong to the table instance. This will prevent accidental data corruption or deleted records. If you don't want this new behavior, you can disable it by calling$this->disableEntityClassAssertion();in yourinitialize()method.
Controller
- Loading a component with the same alias as the controller's default table now triggers a warning. See Component Alias Conflicts.
View
FormHelpernow wraps hidden form blocks (CSRF, FormProtection,postLink()/postButton()) with the HTML5 booleanhiddenattribute instead of an inlinestyle="display:none;". This makes the default markup compatible with a strict Content-Security-Policy (no need forstyle-src 'unsafe-inline'). If you previously selected those wrappers via CSS (e.g.div[style="display:none;"]), switch to[hidden]or set thehiddenClasstemplate option to opt out and emit a class instead.
Deprecations
Command Helpers
- Command helpers under the
Cake\Command\Helpernamespace have been deprecated. Instead they have been moved under theCake\Console\Helpernamespace.
Mailer
- The
Mailer::$nameproperty is unused and has been deprecated.
ORM
SelectQuery::disableHydration()has been deprecated. UseTable::unhydratedFind()instead, which returns anUnhydratedSelectQuerywhose static type matches the array result shape.disableHydration()will be removed in 6.0.
New Features
Core
PluginConfig::getInstalledPlugins()was added to retrieve a list of all installed plugins including flags to indicate about their scope and state.- A backwards compatible Container implementation has been added to the core. You can opt-in to use it instead of the current
league/containerimplementation by settingApp.containertocakeinside yourconfig/app.php. See Dependency Injection Container for more details. - Added the
Cake\Lock\Lockfacade with pluggable lock engines for Redis, Memcached, local files, and testing/no-op usage. See Locking.
Collection
- Added
keys()andvalues()methods for extracting keys or re-indexing values. - Added
implode()method to concatenate elements into a string. - Added
when()andunless()methods for conditional method chaining.
Commands
- You can use
$this->ioand$this->argsinside your commands to access input/output and argument objects without needing to pass them down from theexecute()method. This will be the default in CakePHP 6.0 as those arguments will be removed from theexecute()method signature.
Console
- Added
ConsoleHelpHeaderProviderInterfaceto allow host applications to provide a custom header in console help output. See Customizing the Help Header.
Controller
- Added
#[RequestToDto]attribute for automatic mapping of request data to Data Transfer Objects in controller actions. See Request to DTO Mapping. - Added
unlockActions()andunlockFields()convenience methods toFormProtectionComponent. See Form Protection Component.
Http
- Added
JsonStreamResponseclass for memory-efficient streaming of large JSON datasets using generators. Supports standard JSON arrays and NDJSON formats, envelope structures with metadata, transform callbacks, and graceful mid-stream error handling. See Streaming JSON Responses.
Database
- Added
notBetween()method forNOT BETWEENexpressions. See Query Builder. - Added
inOrNull()andnotInOrNull()methods for combiningINconditions withIS NULL. - Added
isDistinctFrom()andisNotDistinctFrom()methods for null-safe comparisons. - Added
FunctionsBuilder::stringAgg()for portable string aggregation. Translates toSTRING_AGGorGROUP_CONCATper driver. See Query Builder. - Added
Connection::afterCommit()to register callbacks that run after the outermost transaction commits. Callbacks are discarded on rollback. See Database Basics for more details. - Added
except()andexceptAll()methods onSelectQueryforEXCEPTandEXCEPT ALLset operations.EXCEPT ALLis supported on PostgreSQL and recent MySQL/MariaDB versions; it is not supported on SQLite or SQL Server. See Query Builder. - Added PostgreSQL index access method reflection. Non-btree indexes (
gin,gist,spgist,brin,hash) are now reflected with anaccessMethodfield and regenerated with the correctUSINGclause. TheIndexclass provides constants (Index::GIN,Index::GIST,Index::SPGIST,Index::BRIN,Index::HASH) for these access methods. See Reading Indexes and Constraints. - Added
Cake\Database\Type\EnumLabelTraitand theCake\Database\Type\Attribute\Labelattribute. The trait provides a defaultlabel()implementation backed by the translator and the attribute lets individual cases override the derived label. See EnumLabelTrait and the Label Attribute. - Added
LoggedQuery::setRedactor()to scrub sensitive values from query logs. Applies to__toString(),getContext(), andjsonSerialize(), so file logs, structured loggers, and anything that JSON-encodes aLoggedQueryall see the redacted shape. See Redacting Sensitive Values from Query Logs.
Http
- Added PSR-13 Link implementation with
Cake\Http\Link\LinkandCake\Http\Link\LinkProviderclasses for hypermedia link support. Links added to responses are automatically emitted as HTTPLinkheaders. See Hypermedia Links.
I18n
Number::toReadableSize()now uses decimal units (KB = 1000 bytes) by default. Binary units (KiB = 1024 bytes) can be enabled via parameter orNumber::setUseIecUnits().- Added
TranslatorRegistry::setCacheKeyPrefix()to isolate translator caches per tenant when a custom loader produces different messages for the same domain and locale. Accepts a static string or aClosureresolved on every lookup. See Isolating Translations Per Tenant. - Added
TranslatorRegistry::clear()to drop the in-memory translator map without touching the persistent cacher. Intended for long-running workers that switch tenants between jobs. - Added
I18n::setCacheConfig()to route translator persistence to a Cache config other than the default_cake_translations_. - The
cake i18n extractcommand now also extracts enum labels using the #[Label] attribute. - Added
PluralRules::setRule()to register a custom Gettext plural rule for a locale whose built-in form is missing or differs from the layout used by your .po/.mo files. See Customizing Plural Rules.
ORM
- The
associatedoption innewEntity()andpatchEntity()now supports nested array format matchingcontain()syntax. See Converting Request Data into Entities. - Added
Table::unhydratedFind()and theUnhydratedSelectQueryclass for type-safe non-hydrated reads. Unlikefind()->disableHydration(), the returned query's static type matches its array result shape, so static analyzers no longer seeentity|arrayonfirst(),all(),toArray()and iteration. See Getting Arrays Instead of Entities.
Testsuite
TestCase::mockModel()has been added to allow mocking of model classes in tests using Mockery mocks.
Utility
- Added
Cake\Utility\Fs\Finderclass for fluent file discovery with pattern matching, depth control, and custom filters. AddedCake\Utility\Fs\Pathfor cross-platform path manipulation. See Filesystem Utilities. Security::encrypt()can now be configured to use longer keys with separate encryption and authentication keys that are derived from the provided key. You can setSecurity.encryptWithRawKeyto enable this behavior. See here for more details.- Added
Text::mask()method which masks a portion of a string with a repeated character. See Text Masking for more details. - Added
Text::maskValue()method which masks all occurrences of given substrings within a string using a repeated character. See Text Masking for more details.
View
- Added
template variable toinputContaineranderrortemplates in FormHelper. See Built-in Template Variables. FormHelper::enumOptions()is now public. This lets you buildselectoptions from a backed enum class even when the form was created without an entity context. See Creating Select Pickers.