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
AuthComponent
and related classes have been deprecated and will be removed in 5.0.0. You should use the authentication and authorization libs mentioned above instead.SecurityComponent
is deprecated. Instead use theFormProtectionComponent
for 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 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::_properties
has been renamed toEntity::_fields
.
View
- The
_serialize
,_jsonOptions
and_jsonp
special view variables ofJsonView
are now deprecated. Instead you should useviewBuilder()->setOption($optionName, $optionValue)
to set these options. - The
_serialize
,_rootNode
and_xmlOptions
special view variables ofXmlView
are 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\Email
class has been deprecated. UseCake\Mailer\Mailer
instead.
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()
returnsnull
instead offalse
if 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 thelocal
parameter 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 stringforgotpassword
in URL will not match as action name.
Console
ConsoleIo::styles()
has been split into agetStyle()
andsetStyle()
. This also reflects inConsoleOutput
.
Component
Cake\Controller\Component\RequestHandlerComponent
now setsisAjax
as 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
RequestHandlerComponent
have been removed. You should use the Body Parser Middleware instead. Cake\Controller\Component\PaginatorComponent
now 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\TypeInterface
no longer inherit fromType
, and leverageBatchCastingInterface
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 time objects by default now.
BoolType
no longer marshals non-empty string values totrue
and empty string tofalse
. Instead non-boolean string values are converted tonull
.DecimalType
now uses strings to represent decimal values instead of floats. Using floats caused loss in precision.JsonType
now preservesnull
when preparing values for database context. In 3.x it would emit'null'
.StringType
now marshals array values tonull
instead of an empty string.Cake\Database\Connection::setLogger()
no longer acceptsnull
to disable logging. Instead pass an instance ofPsr\Log\NullLogger
to disable logging.- The internals of
Database\Log\LoggingStatement
,Database\QueryLogger
andDatabase\Log\LoggedQuery
have changed. If you extend these classes you will need to update your code. - The internals of
Cake\Database\Log\LoggingStatement
,Cake\Database\QueryLogger
andCake\Database\Log\LoggedQuery
have changed. If you extend these classes you will need to update your code. - The internals of
Cake\Database\Schema\CacheCollection
andCake\Database\SchemaCache
have changed. If you extend these classes you will need to update your code. Cake\Database\QueryCompiler
now quotes aliases inSELECT
clause only when auto-quoting is enabled. Quoting is retained for Postgres to avoid identifiers being auto-casted to lowercase.- The database schemas now map
CHAR
columns to the newchar
type 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)
=>datetimefractional
TIMESTAMP(1-6)
=>timestampfractional
- PostgreSQL
TIMESTAMP
=>timestampfractional
TIMESTAMP(1-6)
=>timestampfractional
- SqlServer
DATETIME2
=>datetimefractional
DATETIME2(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
=>timestamptimezone
TIMESTAMPTZ(0-6)
=>timestamptimezone
TIMESTAMP WITH TIME ZONE
=>timestamptimezone
TIMESTAMP(0-6) WITH TIME ZONE
=>timestamptimezone
- PostgreSQL
Datasources
ModelAwareTrait::$modelClass
is now protected.
Error
- The internals of error handler classes
BaseErrorHandler
,ErrorHandler
andConsoleErrorHandler
have changed. If you have extended these classes you should update them accordingly. ErrorHandlerMiddleware
now 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 thelocal
parameter 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 nownull
and notfalse
. Cake\Http\Client\Request::body()
has been removed. UsegetBody()
orwithBody()
instead.Cake\Http\Client\Response::isOk()
now returnstrue
for 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 returnsnull
when 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.uploadedFilesAsObjects
is nowtrue
. If your application uses file uploads you can set this flag tofalse
to retain compatibility with the behavior in 3.x. - The keys of array returned by
Cake\Http\Response::getCookie()
have changed.expire
is changed toexpires
andhttpOnly
tohttponly
.
HttpSession
- The Session cookie name is no longer set to
CAKEPHP
by default. Instead the default cookie name defined in yourphp.ini
file is used. You can use theSession.cookie
configuration option to set the cookie name. - Session cookies now have
SameSite
attribute set toLax
by default. Check Session Configuration section for more info.
I18n
- JSON encoding
Cake\I18n\Date
andCake\I18n\FrozenDate
objects now results in strings with only the date part, in formatyyyy-MM-dd
instead 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 = NULL
in SQL which will always matches 0 rows, thus returning incorrect results. When comparing withnull
you must use theIS
operator like['name IS' => null]
. - Stopping the
Model.beforeSave
event 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.IsUnique
rule no longer acceptsallowMultipleNulls
option 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$options
argument.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$options
argument.Router
maintains 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$current
argument.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::$fixtures
cannot be a comma separated string anymore. It must be an array.
Utility
Cake\Utility\Xml::fromArray()
now requires an array for the$options
parameter.Cake\Filesystem\Folder::copy($to, array $options = [])
andCake\Filesystem\Folder::move($to, array $options = [])
have now the target path extracted as first argument.- The
readFile
option ofXml::build()
is no longer true by default. Instead you must enablereadFile
to read local files. Inflector::pluralize()
now inflectsindex
toindexes
instead 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 thesrc
folder now only contains files with classes that are autoloaded via composer's autoloader. - Special templates folders like
Cell
,Element
,Email
,Layout
andPlugin
have be renamed to lower casecell
,element
,email
,layout
andplugin
respectively. This provides better visual distinction between special folders and 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/IDEs to be configured to recognise files with.ctp
extension as PHP files. - You can no longer use
false
as argument forViewBuilder::setLayout()
orView::setLayout()
to setView::$layout
property tofalse
. Instead useViewBuilder::disableAutoLayout()
andView::disableAutoLayout()
to render a view template without a layout. Cake\View\View
will re-render views ifrender()
is called multiple times instead of returningnull
.- Constants
View::NAME_ELEMENT
andView::NAME_LAYOUT
have been removed. You can useView::TYPE_ELEMENT
andView::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
null
as 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\FormHelper
andCake\View\Helper\HtmlHelper
now use HTML data attributedata-confirm-message
to hold the confirmation message for methods which have theconfirm
option.Cake\View\Helper\FormHelper::button()
now HTML entity encodes the button text and HTML attributes by default. A new optionescapeTitle
has been added to allow controlling escaping the title separately from other HTML attributes.Cake\View\Helper\SecureFieldTokenTrait
has 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
safe
option forHtmlHelper::scriptBlock()
andHtmlHelper::scriptStart()
has been removed. When enabled it generatedCDATA
tags 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$message
argument. This change was necessary to align the API with PSR-3 standard.
Miscellaneous
- Your app's
config/bootstrap.php
should now contain a call toRouter::fullBaseUrl()
. Consult the latest skeleton app'sbootstrap.php
and update accordingly. App::path()
now uses$type
andtemplates
instead ofTemplate
to get templates path. Similarlylocales
is used instead ofLocale
to 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 returnnull
if object with given name is not loaded.- Locale files have been moved from
src/Locale
toresources/locales
. - The
cacert.pem
file 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 otherorFail
methods these methods will raise an exception when the requested key doesn't exist or has anull
value.
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\LoggedQuery
now implementsJsonSerializable
.Cake\Database\Connection
now allows using any PSR-3 logger. As a result those using the standalone database package are no longer forced to use thecakephp/log
package for logging.Cake\Database\Connection
now allows using any PSR-16 cacher. As a result those using the standalone database package are no longer forced to use thecakephp/cache
package for caching. New methodsCake\Database\Connection::setCacher()
andCake\Database\Connection::getCacher()
have been added.Cake\Database\ConstraintsInterface
was extracted fromCake\Datasource\FixtureInterface
. This interface should be implemented by fixture implementations that support constraints, which from our experience is generally relational databases.- The
char
abstract type was added. This type handles fixed length string columns. - The
datetimefractional
andtimestampfractional
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
andtimestamptimezone
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 is available. This behavior is only applied whendebug
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 the middlewares now implement
Psr\Http\Server\MiddlewareInterface
. CakePHP 3.x style invokable double pass middlewares are still supported for backwards compatibility. Cake\Http\Client
now follows PSR-18: HTTP Client specifications.Cake\Http\Client\Response::isSuccess()
was added. This method returns true if the response status code is 2xx.CspMiddleware
was added to make defining Content Security Policy headers simpler.HttpsEnforcerMiddleware
was added. This replaced therequireSecure
feature ofSecurityComponent
.- Cookies now support the
SameSite
attribute.
I18n
Date
andFrozenDate
now 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 howEmail
class 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 throwPersistenceFailedException
with 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
DateTimeFractionalType
has been added for datetime types with microsecond precision. You can opt into using this type by adding it to theTypeFactory
as the defaultdatetime
type or re-mapping individual columns. See the Database migration notes for how this type is automatically mapped to database types. - A new type class
DateTimeTimezoneType
has been added for datetime types with time zone support. You can opt into using this type by adding it to theTypeFactory
as the defaultdatetime
type or re-mapping individual columns. See the Database migration notes for how this type is automatically mapped to database types.
Routing
Cake\Routing\Asset
was 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_ASC
andSORT_DESC
constants in the direction parameter.
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 in Shim plugin with the old behavior/generation (4.x branch).FormHelper
now sets the default step size to seconds fordatetime
widgets with a time component. The default is milliseconds if the field is from the newdatetimefractional
ortimestampfractional
database types.