4.0 Migration Guide
CakePHP 4.0 contains breaking changes, and is not backwards compatible with 3.x releases. Before attempting to upgrade to 4.0, first upgrade to 3.8 and resolve all deprecation warnings.
Refer to the 4.0 Upgrade Guide for step by step instructions on how to upgrade to 4.0.
Deprecated Features Removed
All methods, properties and functionality that were emitting deprecation warnings as of 3.8 have been removed.
Authentication functionality has been split into standalone plugins Authentication and Authorization. The former RssHelper can be found as standalone Feed plugin with similar functionality.
Deprecations
The following is a list of deprecated methods, properties and behaviors. These features will continue to function in 4.x and will be removed in 5.0.0.
Component
AuthComponentand related classes have been deprecated and will be removed in 5.0.0. You should use the authentication and authorization libs mentioned above instead.SecurityComponentis deprecated. Instead use theFormProtectionComponentfor form tampering protection and the Https Enforcer Middleware forrequireSecurefeature.
Filesystem
- This package is deprecated and will be removed in 5.0. It has a number of design problems and fixing this infrequently used package does not seem worth the effort when there are a great selection of packages already.
ORM
- Using
Entity::isNew()as a setter is deprecated. UsesetNew()instead. Entity::unsetProperty()has been renamed toEntity::unset()to match the other methods.TableSchemaInterface::primaryKey()has been renamed toTableSchemaInterface::getPrimaryKey().Entity::_propertieshas been renamed toEntity::_fields.
View
- The
_serialize,_jsonOptionsand_jsonpspecial view variables ofJsonVieware now deprecated. Instead you should useviewBuilder()->setOption($optionName, $optionValue)to set these options. - The
_serialize,_rootNodeand_xmlOptionsspecial view variables ofXmlVieware now deprecated. Instead you should useviewBuilder()->setOption($optionName, $optionValue)to set these options. HtmlHelper::tableHeaders()now prefers header cells with attributes to be defined as a nested list. e.g['Title', ['class' => 'special']].ContextInterface::primaryKey()has been renamed toContextInterface::getPrimaryKey().
Mailer
- The
Cake\Mailer\Emailclass has been deprecated. UseCake\Mailer\Mailerinstead.
App
App::path()has been deprecated for class paths. Use\Cake\Core\App::classPath()instead.
Breaking Changes
In addition to the removal of deprecated features there have been breaking changes made:
Cache
Cake\Cache\Cache::read()returnsnullinstead offalseif the data doesn't exist.Cake\Cache\CacheEngine::gc()and all implementations of this method have been removed. This method was a no-op in most cache drivers and was only used in file caching.
Controller
Cake\Controller\Controller::referer()now defaults thelocalparameter to true, instead of false. This makes using referer headers safer as they will be constrained to your application's domain by default.- Controller method name matching when invoking actions is now case sensitive. For example if your controller method is
forgotPassword()then using stringforgotpasswordin URL will not match as action name.
Console
ConsoleIo::styles()has been split into agetStyle()andsetStyle(). This also reflects inConsoleOutput.
Component
Cake\Controller\Component\RequestHandlerComponentnow setsisAjaxas a request attribute instead of request param. Hence you should now use$request->getAttribute('isAjax')instead of$request->getParam('isAjax').- The request body parsing features of
RequestHandlerComponenthave been removed. You should use the Body Parser Middleware instead. Cake\Controller\Component\PaginatorComponentnow sets paging params info as request attribute instead of request param. Hence you should now use$request->getAttribute('paging')instead of$request->getParam('paging').
CSRF Middleware
- Generated tokens now contain an HMAC signed with Security.salt. This ensures the tokens were generated by the same application that receives them. CSRF tokens generated in previous versions will now be rejected by 4.0.6+ as they do not contain the HMAC.
Database
- Type mapping classes in
Cake\Database\TypeInterfaceno longer inherit fromType, and leverageBatchCastingInterfacefeatures now. Cake\Database\Type::map()only functions as a setter now. You must useType::getMap()to inspect type instances.- Date, Time, Timestamp, and Datetime column types now return immutable time objects by default now.
BoolTypeno longer marshals non-empty string values totrueand empty string tofalse. Instead non-boolean string values are converted tonull.DecimalTypenow uses strings to represent decimal values instead of floats. Using floats caused loss in precision.JsonTypenow preservesnullwhen preparing values for database context. In 3.x it would emit'null'.StringTypenow marshals array values tonullinstead of an empty string.Cake\Database\Connection::setLogger()no longer acceptsnullto disable logging. Instead pass an instance ofPsr\Log\NullLoggerto disable logging.- The internals of
Database\Log\LoggingStatement,Database\QueryLoggerandDatabase\Log\LoggedQueryhave changed. If you extend these classes you will need to update your code. - The internals of
Cake\Database\Log\LoggingStatement,Cake\Database\QueryLoggerandCake\Database\Log\LoggedQueryhave changed. If you extend these classes you will need to update your code. - The internals of
Cake\Database\Schema\CacheCollectionandCake\Database\SchemaCachehave changed. If you extend these classes you will need to update your code. Cake\Database\QueryCompilernow quotes aliases inSELECTclause only when auto-quoting is enabled. Quoting is retained for Postgres to avoid identifiers being auto-casted to lowercase.- The database schemas now map
CHARcolumns to the newchartype instead ofstring. - SqlServer datetime columns now map to 'datetime' types instead of 'timestamp' to match names.
- The MySQL, PostgreSQL and SqlServer database schemas now map columns that support fractional seconds to the new abstract fractional types.
- MySQL
DATETIME(1-6)=>datetimefractionalTIMESTAMP(1-6)=>timestampfractional
- PostgreSQL
TIMESTAMP=>timestampfractionalTIMESTAMP(1-6)=>timestampfractional
- SqlServer
DATETIME2=>datetimefractionalDATETIME2(1-7) =>datetimefractional``
- MySQL
- PostgreSQL schema now maps columns that support time zones to the new abstract time zone types. Specifying (0) precision does not change the type mapping like it does with regular fractional types above.
- PostgreSQL
TIMESTAMPTZ=>timestamptimezoneTIMESTAMPTZ(0-6)=>timestamptimezoneTIMESTAMP WITH TIME ZONE=>timestamptimezoneTIMESTAMP(0-6) WITH TIME ZONE=>timestamptimezone
- PostgreSQL
Datasources
ModelAwareTrait::$modelClassis now protected.
Error
- The internals of error handler classes
BaseErrorHandler,ErrorHandlerandConsoleErrorHandlerhave changed. If you have extended these classes you should update them accordingly. ErrorHandlerMiddlewarenow takes an error handler class name or instance as constructor argument instead of exception render class name or instance.
Event
- Calling
getSubject()on an event with no subject will now raise an exception.
Http
Cake\Http\ServerRequest::referer()now defaults thelocalparameter to true, instead of false. This makes using referer headers safer as they will be constrained to your application's domain by default.- The default value of
Cake\Http\ServerRequest::getParam()when a parameter is missing is nownulland notfalse. Cake\Http\Client\Request::body()has been removed. UsegetBody()orwithBody()instead.Cake\Http\Client\Response::isOk()now returnstruefor all 2xx and 3xx response codes.Cake\Http\Cookie\Cookie::getExpiresTimestamp()now returns an integer. This makes it type match the one used insetcookie().Cake\Http\ServerRequest::referer()now returnsnullwhen the current request has no referer. Previously it would return/.Cake\Cookie\CookieCollection::get()now throws an exception when accessing a cookie that doesn't exist. Usehas()to check for cookie existence.- The signature of
Cake\Http\ResponseEmitter::emit()has changed, it no longer has the 2nd argument. - The default value of
App.uploadedFilesAsObjectsis nowtrue. If your application uses file uploads you can set this flag tofalseto retain compatibility with the behavior in 3.x. - The keys of array returned by
Cake\Http\Response::getCookie()have changed.expireis changed toexpiresandhttpOnlytohttponly.
HttpSession
- The Session cookie name is no longer set to
CAKEPHPby default. Instead the default cookie name defined in yourphp.inifile is used. You can use theSession.cookieconfiguration option to set the cookie name. - Session cookies now have
SameSiteattribute set toLaxby default. Check Session Configuration section for more info.
I18n
- JSON encoding
Cake\I18n\DateandCake\I18n\FrozenDateobjects now results in strings with only the date part, in formatyyyy-MM-ddinstead of earlier formatyyyy-MM-dd'T'HH:mm:ssxxx.
Mailer
Email::set()has been removed. UseEmail::setViewVars()instead.Email::createView()has been removed.Email::viewOptions()has been removed. Use$email->getRenderer()->viewBuilder()->setOptions()instead.
ORM
Table::newEntity()now requires an array as input and enforces validation to prevent accidental saves without validation being triggered. This means you must useTable::newEmptyEntity()to create entities without input.- Using condition like
['name' => null]forQuery::where()will now raise an exception. In 3.x it would generate condition likename = NULLin SQL which will always matches 0 rows, thus returning incorrect results. When comparing withnullyou must use theISoperator like['name IS' => null]. - Stopping the
Model.beforeSaveevent with a non-false, non-entity result will now raise an exception. This change ensures thatTable::save()always returns an entity or false. - Table will now throw an exception when aliases generated for the table name and column would be truncated by the database. This warns the user before hidden errors occur when CakePHP cannot match the alias in the result.
TableLocator::get()andTableRegistry::get()now expect that alias names are always CamelCased by your code. Passing incorrectly cased aliases will result in table and entity classes not being loaded correctly.IsUniquerule no longer acceptsallowMultipleNullsoption which was enabled by default. This was re-added in 4.2 but disabled by default.
Router
- Routing prefixes created through
Router::prefix()and$routes->prefix()are now CamelCased instead of under_scored. Instead ofmy_admin, you need to useMyAdmin. This change normalizes prefixes with other routing parameters and removes inflection overhead. RouteBuilder::resources()now inflects resource names to dasherized form instead of underscored by default in URLs. You can retain underscored inflection by using'inflect' => 'underscore'in$optionsargument.Router::plugin()andRouter::prefix()now use plugin/prefix name in dasherized form in URL by default. You can retain underscored from (or any other custom path) by using'path'key in$optionsargument.Routermaintains reference to only a single instance of request now instead of a stack of requests.Router::pushRequest(),Router::setRequestInfo()andRouter::setRequestContext()have been removed, useRouter::setRequest()instead.Router::popRequest()has been removed.Router::getRequest()no longer has a$currentargument.Router::url()and all routes generation methods (HtmlHelper::link(),UrlHelper::build(), ...) will not automatically move unknown variables to?query.Router::url(['_name' => 'route', 'c' => 1234])should be rewritten toRouter::url(['_name' => 'route', '?' => ['c' => 1234]]).
TestSuite
Cake\TestSuite\TestCase::$fixturescannot be a comma separated string anymore. It must be an array.
Utility
Cake\Utility\Xml::fromArray()now requires an array for the$optionsparameter.Cake\Filesystem\Folder::copy($to, array $options = [])andCake\Filesystem\Folder::move($to, array $options = [])have now the target path extracted as first argument.- The
readFileoption ofXml::build()is no longer true by default. Instead you must enablereadFileto read local files. Inflector::pluralize()now inflectsindextoindexesinstead ofindices. This reflects the technical usage of this plural in the core as well as the ecosystem.
View
- Templates have been moved from
src/Template/totemplates/folder on app and plugin root. With this change thesrcfolder now only contains files with classes that are autoloaded via composer's autoloader. - Special templates folders like
Cell,Element,Email,LayoutandPluginhave be renamed to lower casecell,element,email,layoutandpluginrespectively. This provides better visual distinction between special folders and the folders corresponding to your app's controller names which are inCamelCaseform. - The template extension has also been changed from
.ctpto.php. The special extension provided no real benefit and instead required editors/IDEs to be configured to recognise files with.ctpextension as PHP files. - You can no longer use
falseas argument forViewBuilder::setLayout()orView::setLayout()to setView::$layoutproperty tofalse. Instead useViewBuilder::disableAutoLayout()andView::disableAutoLayout()to render a view template without a layout. Cake\View\Viewwill re-render views ifrender()is called multiple times instead of returningnull.- Constants
View::NAME_ELEMENTandView::NAME_LAYOUThave been removed. You can useView::TYPE_ELEMENTandView::TYPE_LAYOUT.
Helper
Cake\View\Helper\PaginatorHelper::hasPage()has had its arguments reversed. This makes it consistent with other paginator methods where the 'model' is the second argument.Cake\View\Helper\UrlHelper::build()no longer accepts a boolean for the second parameter. You must use['fullBase' => true]instead.- You must now only use
nullas 1st argument ofFormHelper::create()to create a form without context. Passing any other value for which context cannot be inferred will result in an exception being thrown. Cake\View\Helper\FormHelperandCake\View\Helper\HtmlHelpernow use HTML data attributedata-confirm-messageto hold the confirmation message for methods which have theconfirmoption.Cake\View\Helper\FormHelper::button()now HTML entity encodes the button text and HTML attributes by default. A new optionescapeTitlehas been added to allow controlling escaping the title separately from other HTML attributes.Cake\View\Helper\SecureFieldTokenTraithas been removed. Its form token data building functionality is now included in the internal classFormProtector.HtmlHelper::docType()method has been removed. HTML4 and XHTML are now defunct and doctype for HTML5 is short to type out directly.- The
safeoption forHtmlHelper::scriptBlock()andHtmlHelper::scriptStart()has been removed. When enabled it generatedCDATAtags which are only required for XHTML which is now defunct.
Log
- Logging related methods like
Cake\Log\LogTrait::log(),Cake\Log\Log::write()etc. now only accept string for$messageargument. This change was necessary to align the API with PSR-3 standard.
Miscellaneous
- Your app's
config/bootstrap.phpshould now contain a call toRouter::fullBaseUrl(). Consult the latest skeleton app'sbootstrap.phpand update accordingly. App::path()now uses$typeandtemplatesinstead ofTemplateto get templates path. Similarlylocalesis used instead ofLocaleto get path to locales folder.ObjectRegistry::get()now throws exception if object with provided name is not loaded. You should useObjectRegistry::has()to ensure that the object exists in registry. The magic getterObjectRegistry::__get()will continue to returnnullif object with given name is not loaded.- Locale files have been moved from
src/Localetoresources/locales. - The
cacert.pemfile that was bundled in CakePHP has been replaced by a dependency on composer/ca-bundle.
New Features
Console
- Command classes can implement the
defaultName()method to overwrite the conventions based CLI name.
Core
InstanceConfigTrait::getConfigOrFail()andStaticConfigTrait::getConfigOrFail()were added. Like otherorFailmethods these methods will raise an exception when the requested key doesn't exist or has anullvalue.
Database
- If your database's timezone does not match PHP timezone then you can use
DateTime::setDatabaseTimezone(). See Datetime Type for details. DateTime::setKeepDatabaseTimezone()allows you to keep the database time zone in the DateTime objects created by queries.Cake\Database\Log\LoggedQuerynow implementsJsonSerializable.Cake\Database\Connectionnow allows using any PSR-3 logger. As a result those using the standalone database package are no longer forced to use thecakephp/logpackage for logging.Cake\Database\Connectionnow allows using any PSR-16 cacher. As a result those using the standalone database package are no longer forced to use thecakephp/cachepackage for caching. New methodsCake\Database\Connection::setCacher()andCake\Database\Connection::getCacher()have been added.Cake\Database\ConstraintsInterfacewas extracted fromCake\Datasource\FixtureInterface. This interface should be implemented by fixture implementations that support constraints, which from our experience is generally relational databases.- The
charabstract type was added. This type handles fixed length string columns. - The
datetimefractionalandtimestampfractionalabstract types were added. These types handle column data types with fractional seconds. - SqlServer schemas now support default values with functions in them like SYSDATETIME().
- The
datetimetimezoneandtimestamptimezoneabstract types were added. These types handle column data types with time zone support.
Error
- If an error is raised by a controller action in a prefixed route,
ErrorControllerwill attempt to use a prefixed error template if one is available. This behavior is only applied whendebugis off.
Http
- You can use
cakephp/httpwithout including the entire framework. - CakePHP now supports the PSR-15: HTTP Server Request Handlers specification. As a consequence the middlewares now implement
Psr\Http\Server\MiddlewareInterface. CakePHP 3.x style invokable double pass middlewares are still supported for backwards compatibility. Cake\Http\Clientnow follows PSR-18: HTTP Client specifications.Cake\Http\Client\Response::isSuccess()was added. This method returns true if the response status code is 2xx.CspMiddlewarewas added to make defining Content Security Policy headers simpler.HttpsEnforcerMiddlewarewas added. This replaced therequireSecurefeature ofSecurityComponent.- Cookies now support the
SameSiteattribute.
I18n
DateandFrozenDatenow respect the time zone parameter for various factory helpers liketoday('Asia/Tokyo').
Mailer
- Email message generation responsibility has now been transferred to
Cake\Mailer\Renderer. This is mainly an architectural change and doesn't impact howEmailclass is used. The only difference is that you now need to useEmail::setViewVars()instead ofEmail::set()to set template variables.
ORM
Table::saveManyOrFail()method has been added that will throwPersistenceFailedExceptionwith the specific entity that failed in case of an error. The entities are saved transaction safe.Table::deleteMany()andTable::deleteManyOrFail()methods have been added for removing many entities at once including callbacks. The entities are removed transaction safe.Table::newEmptyEntity()has been added to create a new and empty entity object. This does not trigger any field validation. The entity can be persisted without validation error as an empty record.Cake\ORM\RulesChecker::isLinkedTo()andisNotLinkedTo()were added. These new application rules allow you to ensure an association has or doesn't have related records.- A new type class
DateTimeFractionalTypehas been added for datetime types with microsecond precision. You can opt into using this type by adding it to theTypeFactoryas the defaultdatetimetype or re-mapping individual columns. See the Database migration notes for how this type is automatically mapped to database types. - A new type class
DateTimeTimezoneTypehas been added for datetime types with time zone support. You can opt into using this type by adding it to theTypeFactoryas the defaultdatetimetype or re-mapping individual columns. See the Database migration notes for how this type is automatically mapped to database types.
Routing
Cake\Routing\Assetwas added. This class exposes asset URL generation in a static interface similar toRouter::url(). See Asset Routing for more information.
TestSuite
TestSuite\EmailTrait::assertMailContainsAttachment()was added.
Utility
Hash::sort()now accepts theSORT_ASCandSORT_DESCconstants in the direction parameter.
Validation
Validation::dateTime()now accepts values with microseconds.
View
FormHelpernow generates HTML5 validation messages for fields marked as "notEmpty" in an entity's ORM table class. This feature can be toggled with theautoSetCustomValidityclass configuration option.FormHelpernow generates native HTML5 input tags for datetime fields. Check the Form Helper page for more details. If you need to retain the former markup, a shimmed FormHelper can be found in Shim plugin with the old behavior/generation (4.x branch).FormHelpernow sets the default step size to seconds fordatetimewidgets with a time component. The default is milliseconds if the field is from the newdatetimefractionalortimestampfractionaldatabase types.