4.0 Migration Guide

CakePHP 4.0 contains breaking changes, and is not backwards compatible with 3.xreleases. Before attempting to upgrade to 4.0, first upgrade to 3.8 and resolveall deprecation warnings.

Refer to the 4.0 Upgrade Guide for step by step instructionson how to upgrade to 4.0.

Deprecated Features Removed

All methods, properties and functionality that were emitting deprecation warningsas of 3.8 have been removed.

Authentication functionality has been split into standalone pluginsAuthentication andAuthorization. The formerRssHelper can be found as standalone Feed plugin with similar functionality.

Deprecations

The following is a list of deprecated methods, properties and behaviors. Thesefeatures will continue to function in 4.x and will be removed in 5.0.0.

Component

  • AuthComponent and related classes have been deprecated and will be removedin 5.0.0. You should use the authentication and authorization libs mentionedabove instead.
  • SecurityComponent is deprecated. Instead use the FormProtectionComponentfor form tampering protection and the HTTPS Enforcer Middleware forrequireSecure feature.

Filesystem

  • This package is deprecated and will be removed in 5.0. It has a number ofdesign problems and fixing this infrequently used package does not seem worththe effort when there are a great selection of packages already.

ORM

  • Using Entity::isNew() as a setter is deprecated. Use setNew() instead.
  • Entity::unsetProperty() has been renamed to Entity::unset() to matchthe other methods.
  • TableSchemaInterface::primaryKey() has been renamed to TableSchemaInterface::getPrimaryKey().

View

  • The _serialize, _jsonOptions and _jsonp special view variables ofJsonView arenow deprecated. Instead you should useviewBuilder()->setOption($optionName, $optionValue) to set these options.
  • The _serialize, _rootNode and _xmlOptions special view variables ofXmlView arenow deprecated. Instead you should useviewBuilder()->setOption($optionName, $optionValue) to set these options.
  • HtmlHelper::tableHeaders() now prefers header cells with attributes to bedefined as a nested list. e.g ['Title', ['class' => 'special']].
  • ContextInterface::primaryKey() has been renamed to ContextInterface::getPrimaryKey().

Mailer

  • The Cake\Mailer\Email class has been deprecated. Use Cake\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 breakingchanges made:

Cache

  • Cake\Cache\CacheEngine::gc() and all implementations of this method havebeen removed. This method was a no-op in most cache drivers and was only usedin file caching.

Controller

  • Cake\Controller\Controller::referer() now defaults the localparameter to true, instead of false. This makes using referer headers safer asthey 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 stringforgotpassword in URL will not match as action name.

Console

  • ConsoleIo::styles() has been split into a getStyle() andsetStyle(). This also reflects in ConsoleOutput.

Component

  • The input data parsing feature of RequestHandlerComponent which for e.g.allowed parsing JSON/XML input into request data array has been removed. You shouldinstead use the Cake\Http\Middleware\BodyParserMiddleware in your applicationif you need input data parsing.
  • Cake\Controller\Component\RequestHandlerComponent now sets isAjax as arequest attribute instead of request param. Hence you should now use$request->getAttribute('isAjax') instead of $request->getParam('isAjax').
  • The request body parsing features of RequestHandlerComponent have beenremoved and now emit a deprecation warning. You should use theBody Parser Middleware instead.
  • Cake\Controller\Component\PagingComponent now sets paging params info asrequest attribute instead of request param. Hence you should now use$request->getAttribute('paging') instead of $request->getParam('paging').

Database

  • Type mapping classes in Cake\Database\TypeInterface no longer inherit fromType, and leverage BatchCastingInterface features 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 timeobjects by default now.
  • BoolType no longer marshals non-empty string values to true andempty string to false. Instead non-boolean string values are converted to null.
  • DecimalType now uses strings to represent decimal values instead of floats.Using floats caused loss in precision.
  • JsonType now preserves null when preparing values for databasecontext. In 3.x it would emit 'null'.
  • StringType now marshals array values to null instead of an emptystring.
  • Cake\Database\Connection::setLogger() no longer accepts null todisable logging. Instead pass an instance of Psr\Log\NullLogger to disablelogging.
  • The internals of Database\Log\LoggingStatement, Database\QueryLoggerand Database\Log\LoggedQuery have changed. If you extend these classes youwill need to update your code.
  • The internals of Cake\Database\Log\LoggingStatement, Cake\Database\QueryLoggerand Cake\Database\Log\LoggedQuery have changed. If you extend these classesyou will need to update your code.
  • The internals of Cake\Database\Schema\CacheCollection and Cake\Database\SchemaCachehave changed. If you extend these classes you will need to update your code.
  • The database schemas now map CHAR columns to the new char type instead ofstring.
  • SqlServer datetime columns now map to ‘datetime’ types instead of ‘timestamp’ to matchnames.
  • The MySQL, PostgreSQL and SqlServer database schemas now map columns that supportfractional seconds to the new abstract fractional types.
    • MySQL
      • DATETIME(1-6) => datetimefractional
      • TIMESTAMP(1-6) => timestampfractional
    • PostgreSQL
      • TIMESTAMP => timestampfractional
      • TIMESTAMP(1-6) => timestampfractional
    • SqlServer
      • DATETIME => datetimefractional
      • DATETIME2 => datetimefractional
      • DATETIME2(1-7) => ``datetimefractional
  • PostgreSQL schema now maps columns that support time zones to the new abstracttime zone types. Specifying (0) precision does not change the type mapping likeit does with regular fractional types above.
    • PostgreSQL
      • TIMESTAMPTZ => timestamptimezone
      • TIMESTAMPTZ(0-6) => timestamptimezone
      • TIMESTAMP WITH TIME ZONE => timestamptimezone
      • TIMESTAMP(0-6) WITH TIME ZONE => timestamptimezone

Datasources

  • ModelAwareTrait::$modelClass is now protected.

Error

  • The internals of error handler classes BaseErrorHandler, ErrorHandlerand ConsoleErrorHandler have changed. If you have extended these classesyou should update them accordingly.
  • ErrorHandlerMiddleware now takes an error handler class name or instanceas constructor argument instead of exception render class name or instance.

Event

  • Calling getSubject() on an event with no subject will now raise anexception.

Http

  • Cake\Http\ServerRequest::referer() now defaults the localparameter to true, instead of false. This makes using referer headers safer asthey will be constrained to your application’s domain by default.
  • The default value of Cake\Http\ServerRequest::getParam() when a parameter is missingis now null and not false.
  • Cake\Http\Client\Request::body() has been removed. Use getBody() orwithBody() instead.
  • Cake\Http\Client\Response::isOk() now returns true for all 2xx and 3xxresponse codes.
  • Cake\Http\Cookie\Cookie::getExpiresTimestamp() now returns an integer.This makes it type match the one used in setcookie().
  • Cake\Http\ServerRequest::referer() now returns null when the currentrequest has no referer. Previously it would return /.
  • The Session cookie name is no longer set to CAKEPHP by default. Insteadthe default cookie name defined in your php.ini file is used. You can usethe Session.cookie configuration option to set the cookie name.
  • Cake\Cookie\CookieCollection::get() now throws an exception when accessinga cookie that doesn’t exist. Use has() to check for cookie existence.
  • The signature of Cake\Http\ResponseEmitter::emit() has changed, it no longerhas the 2nd argument.
  • The default value of App.mergeFilesAsObjects is now true. If yourapplication uses file uploads you can set this flag to false to retaincompatibility with the behavior in 3.x.
  • The keys of array returned by Cake\Http\Response::getCookie() have changed.expire is changed to expires and httpOnly to httponly.

I18n

  • JSON encoding Cake\I18n\Date and Cake\I18n\FrozenDate objects now resultsin strings with only the date part, in format yyyy-MM-dd instead of earlier formatyyyy-MM-dd'T'HH:mm:ssxxx.

Mailer

  • Email::set() has been removed. Use Email::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 preventaccidental saves without validation being triggered. This means you must useTable::newEmptyEntity() to create entities without input.
  • Using condition like ['name' => null] for Query::where() will now raise an exception.In 3.x it would generate condition like name = NULL in SQL which willalways matches 0 rows, thus returning incorrect results. When comparing with nullyou must use the IS operator like ['name IS' => null].
  • Stopping the Model.beforeSave event with a non-false, non-entity resultwill now raise an exception. This change ensures that Table::save() alwaysreturns an entity or false.

Router

  • Routing prefixes created through Router::prefix() and$routes->prefix()` are now CamelCased instead of under_scored. Instead ofmy_admin, you need to use MyAdmin. This change normalizes prefixeswith other routing parameters and removes inflection overhead.
  • RouteBuilder::resources() now inflects resource names to dasherized forminstead of underscored by default in URLs. You can retain underscoredinflection by using 'inflect' => 'underscore' in $options argument.
  • Router::plugin() and Router::prefix() now use plugin/prefix name indasherized form in URL by default. You can retain underscored from (or any othercustom path) by using 'path' key in $options argument.
  • Router maintains reference to only a single instance of request now insteadof a stack of requests. Router::pushRequest(), Router::setRequestInfo()and Router::setRequestContext() have been removed, use Router::setRequest()instead. Router::popRequest() has been removed. Router::getRequest()no longer has a $current argument.

TestSuite

  • Cake\TestSuite\TestCase::$fixtures cannot be a comma separated stringanymore. 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 targetpath extracted as first argument.
  • The readFile option of Xml::build() is no longer true by default.Instead you must enable readFile to read local files.
  • Hash::sort() now accepts the SORT_ASC and SORT_DESC constants inthe direction parameter.
  • Inflector::pluralize() now inflects index to indexes instead of indices.This reflects the technical usage of this plural in the core as well as the ecosystem.

View

  • Templates have been moved from src/Template/ to templates/ folder onapp and plugin root. With this change the src folder now only contains fileswith classes that are autoloaded via composer’s autoloader.
  • Special templates folders like Cell, Element, Email and Pluginhave be renamed to lower case cell, element, email and pluginrespectively. This provides better visual distinction between special foldersand the folders corresponding to your app’s controller names which are inCamelCase form.
  • The template extension has also been changed from .ctp to .php.The special extension provided no real benefit and instead required editors/IDEsto be configured to recognise files with .ctp extension as PHP files.
  • You can no longer use false as argument for ViewBuilder::setLayout()or View::setLayout() to set View::$layout property to false.Instead use ViewBuilder::disableAutoLayout() and View::disableAutoLayout()to render a view template without a layout.
  • Cake\View\View will re-render views if render() is called multipletimes instead of returning null.
  • Constants View::NAME_ELEMENT and View::NAME_LAYOUT have been removed.You can use View::TYPE_ELEMENT and View::TYPE_LAYOUT.

Helper

  • Cake\View\Helper\PaginatorHelper::hasPage() has had its argumentsreversed. 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 thesecond parameter. You must use ['fullBase' => true] instead.
  • You must now only use null as 1st argument of FormHelper::create() tocreate a form without context. Passing any other value for which context cannotbe inferred will result in an exception being thrown.
  • Cake\View\Helper\FormHelper and Cake\View\Helper\HtmlHelper nowuse HTML data attribute data-confirm-message to hold the confirmationmessage for methods which have the confirm option.
  • Cake\View\Helper\FormHelper::button() now HTML entity encodes the buttontext and HTML attributes by default. A new option escapeTitle has beenadded to allow controlling escaping the title separately from other HTMLattributes.
  • Cake\View\Helper\SecureFieldTokenTrait has been removed. Its form tokendata building functionality is now included in the internal class FormProtector.
  • HtmlHelper::docType() method has been removed. HTML4 and XHTML are nowdefunct and doctype for HTML5 is pretty short and easy to type out directly.
  • The safe option for HtmlHelper::scriptBlock() and HtmlHelper::scriptStart()has been removed. When enabled it generated CDATA tags which are only requiredfor XHTML which is now defunct.

Miscellaneous

  • Your app’s config/bootstrap.php should now contain a call to Router::fullBaseUrl().Consult the lastest skeleton app’s bootstrap.php and update accordingly.
  • App::path() now uses $type templates instead of Template toget templates path. Similarly locales is used instead of Locale toget path to locales folder.
  • ObjectRegistry::get() now throws exception if object with provided name is not loaded.You should use ObjectRegistry::has() to ensure that the object exists in registry.The magic getter ObjectRegistry::__get() will continue to return null if objectwith given name is not loaded.
  • Locale files have been moved from src/Locale to resources/locales.
  • The cacert.pem file that was bundled in CakePHP has been replaced bya dependency on composer/ca-bundle __.

New Features

Console

  • Command classes can implement the defaultName() method to overwrite theconventions based CLI name.

Core

  • InstanceConfigTrait::getConfigOrFail() andStaticConfigTrait::getConfigOrFail() were added. Like other orFailmethods these methods will raise an exception when the requested key doesn’texist or has a null value.

Database

  • If your database’s timezone does not match PHP timezone then you can useDateTime::setDatabaseTimezone(). See DateTime Type for details.
  • DateTime::setKeepDatabaseTimezone() allows you to keep the database time zonein the DateTime objects created by queries.
  • Cake\Database\Log\LoggedQuery now implements JsonSerializable.
  • Cake\Database\Connection now allows using any PSR-3 logger. As a resultthose using the standalone database package are no longer forced to usethe cakephp/log package for logging.
  • Cake\Database\Connection now allows using any PSR-16 cacher. As a resultthose using the standalone database package are no longer forced to usethe cakephp/cache package for caching. New methods Cake\Database\Connection::setCache()and Cake\Database\Connection::getCache() have been added.
  • Cake\Database\ConstraintsInterface was extracted fromCake\Datasource\FixtureInterface. This interface should beimplemented by fixture implementations that support constraints, which fromour experience is generally relational databases.
  • The char abstract type was added. This type handles fixed length stringcolumns.
  • The datetimefractional and timestampfractional abstract 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 datetimetimezone and timestamptimezone abstract 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,ErrorController will attempt to use a prefixed error template if one isavailable. This behavior is only applied when debug is off.

Http

  • You can use cakephp/http without including the entire framework.
  • CakePHP now supports the PSR-15: HTTP Server Request Handlers specification. As a consequence themiddlewares now implement Psr\Http\Server\MiddlewareInterface. CakePHP3.x style invokable double pass middlewares are still supported for backwardscompatibility.
  • Cake\Http\Client now follows PSR-18: HTTP Client specifications.
  • Cake\Http\Client\Response::isSuccess() was added. This method returns trueif the response status code is 2xx.
  • CspMiddleware was added to make defining Content Security Policy headerssimpler.
  • HttpsEnforcerMiddleware was added. This replaced the requireSecurefeature of SecurityComponent.
  • Cookies now support the SameSite attribute.

I18n

  • Date and FrozenDate now respect the time zone parameter forvarious factory helpers like today('Asia/Tokyo').

Mailer

  • Email message generation responsibility has now been transferred toCake\Mailer\Renderer. This is mainly an architectural change and doesn’timpact how Email class is used. The only difference is that you now needto use Email::setViewVars() instead of Email::set() to set templatevariables.

ORM

  • Table::saveManyOrFail() method has been added that will throw PersistenceFailedExceptionwith the specific entity that failed in case of an error. The entities are saved transaction safe.
  • Table::deleteMany() and Table::deleteManyOrFail() methods have been added for removing manyentities at once including callbacks. The entities are removed transaction safe.
  • Table::newEmptyEntity() has been added to create a new and empty entityobject. This does not trigger any field validation. The entity can bepersisted without validation error as an empty record.
  • Cake\ORM\RulesChecker::isLinkedTo() and isNotLinkedTo() were added.These new application rules allow you to ensure an association has or doesn’thave related records.
  • A new type class DateTimeFractionalType has been added for datetime typeswith microsecond precision. You can opt into using this type by adding it tothe TypeFactory as the default datetime type or re-mapping individualcolumns. See the Database migration notes for how this type is automaticallymapped to database types.
  • A new type class DateTimeTimezoneType has been added for datetime typeswith time zone support. You can opt into using this type by adding it tothe TypeFactory as the default datetime type or re-mapping individualcolumns. See the Database migration notes for how this type is automaticallymapped to database types.

Routing

  • Cake\Routing\Asset was added. This class exposes asset URL generation ina static interface similar to Router::url(). See Generating Asset URLs formore information.

TestSuite

  • TestSuite\EmailTrait::assertMailContainsAttachment() was added.

Validation

  • Validation::dateTime() now accepts values with microseconds.

View

  • FormHelper now generates HTML5 validation messages for fields marked as“notEmpty” in an entity’s ORM table class. This feature can be toggled with theautoSetCustomValidity class configuration option.
  • FormHelper now 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 inShim plugin with the oldbehavior/generation (4.x branch).
  • FormHelper now sets the default step size to seconds for datetimewidgets with a time component. The default is milliseconds if the fieldis from the new datetimefractional or timestampfractional databasetypes.