This page summarizes the changes from CakePHP 2.x that will assist in migrating
a project to 3.0, as well as a reference to get up to date with the changes made
to the core since the CakePHP 2.x branch. Be sure to read the other pages in
this guide for all the new features and API changes.

While this document covers all the breaking changes and improvements made in
CakePHP 3.0, we’ve also created a console application to help you complete some
of the time consuming mechanical changes. You can get the upgrade tool from
github.

All of CakePHP’s core classes are now namespaced and follow PSR-4 autoloading
specifications. For example src/Cache/Cache.php is namespaced as
Cake\Cache\Cache. Global constants and helper methods like __()
and debug() are not namespaced for convenience sake.

Configuration in CakePHP 3.0 is significantly different than in previous
versions. You should read the Configuration documentation
for how configuration is done in 3.0.

You can no longer use App::build() to configure additional class paths.
Instead you should map additional paths using your application’s autoloader. See
the section on Additional Class Paths for more information.

Three new configure variables provide the path configuration for plugins,
views and locale files. You can add multiple paths to App.paths.templates,
App.paths.plugins, App.paths.locales to configure multiple paths for
templates, plugins and locale files respectively.

The config key www_root has been changed to wwwRoot for consistency. Please adjust
your app.php config file as well as any usage of Configure::read('App.wwwRoot').

CakePHP 3.0 features a new ORM that has been re-built from the ground up. The
new ORM is significantly different and incompatible with the previous one.
Upgrading to the new ORM will require extensive changes in any application that
is being upgraded. See the new Database Access & ORM documentation for information on how
to use the new ORM.

Objects used in CakePHP now have a consistent instance-configuration storage/retrieval
system. Code which previously accessed for example: $object->settings should instead
be updated to use $object->config().

Cache configurations are now immutable. If you need to change configuration
you must first drop the configuration and then re-create it. This prevents
synchronization issues with configuration options.

Cache::set() has been removed. It is recommended that you create multiple
cache configurations to replace runtime configuration tweaks previously
possible with Cache::set().

All Cake\Cache\Cache\CacheEngine methods now honor/are responsible for handling the
configured key prefix. The Cake\Cache\CacheEngine::write() no longer permits setting
the duration on write - the duration is taken from the cache engine’s runtime config. Calling a
cache method with an empty key will now throw an InvalidArgumentException, instead
of returning false.

The Object class has been removed. It formerly contained a grab bag of
methods that were used in various places across the framework. The most useful
of these methods have been extracted into traits. You can use the
Cake\Log\LogTrait to access the log() method. The
Cake\Routing\RequestActionTrait provides requestAction().

This class has been renamed to Cake\Console\TaskRegistry.
See the section on Registry Objects for more information
on the features provided by the new class. You can use the cakeupgraderename_collections to assist in upgrading your code. Tasks no longer have
access to callbacks, as there were never any callbacks to use.

Shell::__construct() has changed. It now takes an instance of
Cake\Console\ConsoleIo.

Shell::param() has been added as convenience access to the params.

Additionally all shell methods will be transformed to camel case when invoked.
For example, if you had a hello_world() method inside a shell and invoked it
with bin/cakemy_shellhello_world, you will need to rename the method
to helloWorld. There are no changes required in the way you invoke commands.

The SchemaShell was removed as it was never a complete database migration implementation
and better tools such as Phinx have emerged. It has been replaced by
the CakePHP Migrations Plugin which acts as a wrapper between
CakePHP and Phinx.

The getEventManager() method, was removed on all objects that had it. An
eventManager() method is now provided by the EventManagerTrait. The
EventManagerTrait contains the logic of instantiating and keeping
a reference to a local event manager.

The Event subsystem has had a number of optional features removed. When
dispatching events you can no longer use the following options:

passParams This option is now enabled always implicitly. You
cannot turn it off.

Log configurations are now immutable. If you need to change configuration
you must first drop the configuration and then re-create it. This prevents
synchronization issues with configuration options.

Log engines are now lazily loaded upon the first write to the logs.

Cake\Log\Log::engine() has been added.

The following methods have been removed from Cake\Log\Log ::
defaultLevels(), enabled(), enable(), disable().

You can no longer create custom levels using Log::levels().

When configuring loggers you should use 'levels' instead of 'types'.

You can no longer specify custom log levels. You must use the default set of
log levels. You should use logging scopes to create custom log files or
specific handling for different sections of your application. Using
a non-standard log level will now throw an exception.

Cake\Log\LogTrait was added. You can use this trait in your
classes to add the log() method.

The logging scope passed to Cake\Log\Log::write() is now
forwarded to the log engines’ write() method in order to provide better
context to the engines.

Log engines are now required to implement Psr\Log\LogInterface instead of
Cake’s own LogInterface. In general, if you extended Cake\Log\Engine\BaseEngine
you just need to rename the write() method to log().

Cake\Log\Engine\FileLog now writes files in ROOT/logs instead of ROOT/tmp/logs.

Named parameters were removed in 3.0. Named parameters were added in 1.2.0 as
a ‘pretty’ version of query string parameters. While the visual benefit is
arguable, the problems named parameters created are not.

Named parameters required special handling in CakePHP as well as any PHP or
JavaScript library that needed to interact with them, as named parameters are
not implemented or understood by any library except CakePHP. The additional
complexity and code required to support named parameters did not justify their
existence, and they have been removed. In their place you should use standard
query string parameters or passed arguments. By default Router will treat
any additional parameters to Router::url() as query string arguments.

Since many applications will still need to parse incoming URLs containing named
parameters. Cake\Routing\Router::parseNamedParams() has
been added to allow backwards compatibility with existing URLs.

Dispatcher filters are no longer added to your application using Configure.
You now append them with Cake\Routing\DispatcherFactory. This
means if your application used Dispatcher.filters, you should now use
Cake\Routing\DispatcherFactory::add().

In addition to configuration changes, dispatcher filters have had some
conventions updated, and features added. See the
Dispatcher Filters documentation for more information.

Plugin & theme assets handled by the AssetFilter are no longer read via
include instead they are treated as plain text files. This fixes a number
of issues with JavaScript libraries like TinyMCE and environments with
short_tags enabled.

Support for the Asset.filter configuration and hooks were removed. This
feature should be replaced with a plugin or dispatcher filter.

The mapping of mimetype text/plain to extension csv has been removed.
As a consequence Cake\Controller\Component\RequestHandlerComponent
doesn’t set extension to csv if Accept header contains mimetype text/plain
which was a common annoyance when receiving a jQuery XHR request.

HttpClient has been re-written from the ground up. It has a simpler/easier to
use API, support for new authentication systems like OAuth, and file uploads.
It uses PHP’s stream APIs so there is no requirement for cURL. See the
Http Client documentation for more information.

Cake\Network\Email\Email::config() is now used to define
configuration profiles. This replaces the EmailConfig classes in previous
versions.

Cake\Network\Email\Email::profile() replaces config() as
the way to modify per instance configuration options.

Cake\Network\Email\Email::drop() has been added to allow the
removal of email configuration.

Cake\Network\Email\Email::configTransport() has been added to allow the
definition of transport configurations. This change removes transport options
from delivery profiles and allows you to re-use transports across email
profiles.

Cake\Network\Email\Email::dropTransport() has been added to allow the
removal of transport configuration.

The $helpers, $components properties are now merged
with all parent classes not just AppController and the plugin
AppController. The properties are merged differently now as well. Instead of
all settings in all classes being merged together, the configuration defined
in the child class will be used. This means that if you have some
configuration defined in your AppController, and some configuration defined in
a subclass, only the configuration in the subclass will be used.

Controller::httpCodes() has been removed, use
Cake\Network\Response::httpCodes() instead.

Controller::disableCache() has been removed, use
Cake\Network\Response::disableCache() instead.

Controller::flash() has been removed. This method was rarely used in real
applications and served no purpose anymore.

Controller::validate() and Controller::validationErrors() have been
removed. They were left over methods from the 1.x days where the concerns of
models + controllers were far more intertwined.

Controller::loadModel() now loads table objects.

The Controller::$scaffold property has been removed. Dynamic scaffolding
has been removed from CakePHP core. An improved scaffolding plugin, named CRUD, can be found here: https://github.com/FriendsOfCake/crud

The Controller::$ext property has been removed. You now have to extend and
override the View::$_ext property if you want to use a non-default view file
extension.

The Controller::$methods property has been removed. You should now use
Controller::isAction() to determine whether or not a method name is an
action. This change was made to allow easier customization of what is and is
not counted as an action.

The Controller::$Components property has been removed and replaced with
_components. If you need to load components at runtime you should use
$this->loadComponent() on your controller.

The signature of Cake\Controller\Controller::redirect() has been
changed to Controller::redirect(string|array$url,int$status=null).
The 3rd argument $exit has been dropped. The method can no longer send
response and exit script, instead it returns a Response instance with
appropriate headers set.

The base, webroot, here, data, action, and params
magic properties have been removed. You should access all of these properties
on $this->request instead.

Underscore prefixed controller methods like _someMethod() are no longer
treated as private methods. Use proper visibility keywords instead. Only
public methods can be used as controller actions.

The dynamic scaffolding in CakePHP has been removed from CakePHP core. It was
infrequently used, and never intended for production use. An improved
scaffolding plugin, named CRUD, can be found here:
https://github.com/FriendsOfCake/crud

This class has been renamed to Cake\Controller\ComponentRegistry.
See the section on Registry Objects for more information
on the features provided by the new class. You can use the cakeupgraderename_collections to assist in upgrading your code.

The _Collection property is now _registry. It contains an instance
of Cake\Controller\ComponentRegistry now.

All components should now use the config() method to get/set
configuration.

Default configuration for components should be defined in the
$_defaultConfig property. This property is automatically merged with any
configuration provided to the constructor.

Configuration options are no longer set as public properties.

The Component::initialize() method is no longer an event listener.
Instead, it is a post-constructor hook like Table::initialize() and
Controller::initialize(). The new Component::beforeFilter() method is
bound to the same event that Component::initialize() used to be. The
initialize method should have the following signature initialize(array$config).

Uses Cake\Network\Request::cookie() to read cookie data,
this eases testing, and allows for ControllerTestCase to set cookies.

Cookies encrypted in previous versions of CakePHP using the cipher() method
are now un-readable because Security::cipher() has been removed. You will
need to re-encrypt cookies with the rijndael() or aes() method before upgrading.

CookieComponent::type() has been removed and replaced with configuration
data accessed through config().

write() no longer takes encryption or expires parameters. Both of
these are now managed through config data. See
Cookie for more information.

Default is now the default password hasher used by authentication classes.
It uses exclusively the bcrypt hashing algorithm. If you want to continue using
SHA1 hashing used in 2.x use 'passwordHasher'=>'Weak' in your authenticator configuration.

A new FallbackPasswordHasher was added to help users migrate old passwords
from one algorithm to another. Check AuthComponent’s documentation for more
info.

BlowfishAuthenticate class has been removed. Just use FormAuthenticate

BlowfishPasswordHasher class has been removed. Use
DefaultPasswordHasher instead.

The loggedIn() method has been removed. Use user() instead.

Configuration options are no longer set as public properties.

The methods allow() and deny() no longer accept “var args”. All method names need
to be passed as first argument, either as string or array of strings.

The method login() has been removed and replaced by setUser() instead.
To login a user you now have to call identify() which returns user info upon
successful identification and then use setUser() to save the info to
session for persistence across requests.

BaseAuthenticate::_password() has been removed. Use a PasswordHasher
class instead.

BaseAuthenticate::logout() has been removed.

AuthComponent now triggers two events Auth.afterIdentify and
Auth.logout after a user has been identified and before a user is
logged out respectively. You can set callback functions for these events by
returning a mapping array from implementedEvents() method of your
authenticate class.

ACL related classes were moved to a separate plugin. Password hashers, Authentication and
Authorization providers where moved to the \Cake\Auth namespace. You are
required to move your providers and hashers to the App\Auth namespace as
well.

The following methods have been removed from RequestHandler component::
isAjax(), isFlash(), isSSL(), isPut(), isPost(), isGet(), isDelete().
Use the Cake\Network\Request::is() method instead with relevant argument.

RequestHandler::setContent() was removed, use Cake\Network\Response::type() instead.

RequestHandler::getReferer() was removed, use Cake\Network\Request::referer() instead.

RequestHandler::getClientIP() was removed, use Cake\Network\Request::clientIp() instead.

RequestHandler::getAjaxVersion() was removed.

RequestHandler::mapType() was removed, use Cake\Network\Response::mapType() instead.

The following methods and their related properties have been removed from Security component:
requirePost(), requireGet(), requirePut(), requireDelete().
Use the Cake\Network\Request::allowMethod() instead.

SecurityComponent::$disabledFields() has been removed, use
SecurityComponent::$unlockedFields().

The CSRF related features in SecurityComponent have been extracted and moved
into a separate CsrfComponent. This allows you to use CSRF protection
without having to use form tampering prevention.

Configuration options are no longer set as public properties.

The methods requireAuth() and requireSecure() no longer accept “var args”.
All method names need to be passed as first argument, either as string or array of strings.

Custom ExceptionRenderers are now expected to either return
a Cake\Network\Response object or string when rendering errors. This means
that any methods handling specific exceptions must return a response or string
value.

Having themes and plugins as ways to create modular application components has
proven to be limited, and confusing. In CakePHP 3.0, themes no longer reside
inside the application. Instead they are standalone plugins. This solves
a few problems with themes:

This class has been renamed to Cake\View\HelperRegistry.
See the section on Registry Objects for more information
on the features provided by the new class. You can use the cakeupgraderename_collections to assist in upgrading your code.

The base, webroot, here, data, action, and params
magic properties have been removed. You should access all of these properties
on $this->request instead.

View::start() no longer appends to an existing block. Instead it will
overwrite the block content when end is called. If you need to combine block
contents you should fetch the block content when calling start a second time,
or use the capturing mode of append().

View::prepend() no longer has a capturing mode.

View::startIfEmpty() has been removed. Now that start() always overwrites
startIfEmpty serves no purpose.

The View::$Helpers property has been removed and replaced with
_helpers. If you need to load helpers at runtime you should use
$this->addHelper() in your view files.

View will now raise Cake\View\Exception\MissingTemplateException when
templates are missing instead of MissingViewException.

These methods were part used only by FormHelper, and part of the persistent
field features that have proven to be problematic over time. FormHelper no
longer relies on these methods and the complexity they provide is not necessary
anymore.

The following methods have been removed:

Helper::_parseAttributes()

Helper::_formatAttribute()

These methods can now be found on the StringTemplate class that helpers
frequently use. See the StringTemplateTrait for an easy way to integrate
string templates into your own helpers.

FormHelper has been entirely rewritten for 3.0. It features a few large changes:

FormHelper works with the new ORM. But has an extensible system for
integrating with other ORMs or datasources.

FormHelper features an extensible widget system that allows you to create new
custom input widgets and augment the built-in ones.

String templates are the foundation of the helper. Instead of munging arrays
together everywhere, most of the HTML FormHelper generates can be customized
in one central place using template sets.

In addition to these larger changes, some smaller breaking changes have been
made as well. These changes should help streamline the HTML FormHelper generates
and reduce the problems people had in the past:

The data[ prefix was removed from all generated inputs. The prefix serves no real purpose anymore.

The various standalone input methods like text(), select() and others
no longer generate id attributes.

The inputDefaults option has been removed from create().

Options default and onsubmit of create() have been removed. Instead
one should use JavaScript event binding or set all required js code for onsubmit.

end() can no longer make buttons. You should create buttons with
button() or submit().

FormHelper::tagIsInvalid() has been removed. Use isFieldError()
instead.

FormHelper::inputDefaults() has been removed. You can use templates()
to define/augment the templates FormHelper uses.

The wrap and class options have been removed from the error()
method.

The showParents option has been removed from select().

The div, before, after, between and errorMessage options
have been removed from input(). You can use templates to update the
wrapping HTML. The templates option allows you to override the loaded
templates for one input.

The separator, between, and legend options have been removed from
radio(). You can use templates to change the wrapping HTML now.

The format24Hours parameter has been removed from hour().
It has been replaced with the format option.

The minYear, and maxYear parameters have been removed from year().
Both of these parameters can now be provided as options.

The dateFormat and timeFormat parameters have been removed from
datetime(). You can use the template to define the order the inputs should
be displayed in.

The submit() has had the div, before and after options
removed. You can customize the submitContainer template to modify this
content.

The inputs() method no longer accepts legend and fieldset in the
$fields parameter, you must use the $options parameter.
It now also requires $fields parameter to be an array. The $blacklist
parameter has been removed, the functionality has been replaced by specifying
'field'=>false in the $fields parameter.

The inline parameter has been removed from postLink() method.
You should use the block option instead. Setting block=>true will
emulate the previous behavior.

The timeFormat parameter for hour(), time() and dateTime() now
defaults to 24, complying with ISO 8601.

Checkbox and radio input types are now rendered inside of label elements
by default. This helps increase compatibility with popular CSS libraries like
Bootstrap and
Foundation.

Templates tags are now all camelBacked. Pre-3.0 tags formstart, formend, hiddenblock
and inputsubmit are now formStart, formEnd, hiddenBlock and inputSubmit.
Make sure you change them if they are customized in your app.

It is recommended that you review the Form
documentation for more details on how to use the FormHelper in 3.0.

numbers() no longer has ‘separator’, ‘tag’, ‘currentTag’, ‘currentClass’,
‘class’, ‘tag’, ‘ellipsis’ options. These options are now facilitated through
templates. It also requires the $options parameter to be an array now.

JsHelper and all associated engines have been removed. It could only
generate a very small subset of JavaScript code for selected library and
hence trying to generate all JavaScript code using just the helper often
became an impediment. It’s now recommended to directly use JavaScript library
of your choice.

CacheHelper has been removed. The caching functionality it provided was
non-standard, limited and incompatible with non-HTML layouts and data views.
These limitations meant a full rebuild would be necessary. Edge Side Includes
have become a standardized way to implement the functionality CacheHelper used
to provide. However, implementing Edge Side Includes in PHP has a number of
limitations and edge cases. Instead of building a sub-par solution, we recommend
that developers needing full response caching use Varnish or Squid instead.

The I18n subsystem was completely rewritten. In general, you can expect the same
behavior as in previous versions, specifically if you are using the __()
family of functions.

Internally, the I18n class uses Aura\Intl, and appropriate methods are
exposed to access the specific features of this library. For this reason most
methods inside I18n were removed or renamed.

Due to the use of ext/intl, the L10n class was completely removed. It
provided outdated and incomplete data in comparison to the data available from
the Locale class in PHP.

The default application language will no longer be changed automatically by the
browser accepted language nor by having the Config.language value set in the
browser session. You can, however, use a dispatcher filter to get automatic
language switching from the Accept-Language header sent by the browser:

// In config/bootstrap.phpDispatcherFactory::addFilter('LocaleSelector');

There is no built-in replacement for automatically selecting the language by
setting a value in the user session.

The default formatting function for translated messages is no longer
sprintf, but the more advanced and feature rich MessageFormatter class.
In general you can rewrite placeholders in messages as follows:

// Before:__('Today is a %s day in %s','Sunny','Spain');// After:__('Today is a {0} day in {1}','Sunny','Spain');

You can avoid rewriting your messages by using the old sprintf formatter:

I18n::defaultFormatter('sprintf');

Additionally, the Config.language value was removed and it can no longer be
used to control the current language of the application. Instead, you can use
the I18n class:

From Cake\I18n\Multibyte::checkMultibyte() to Cake\Utility\Text::isMultibyte()

Since CakePHP now requires the mbstring extension, the
Multibyte class has been removed.

Error messages throughout CakePHP are no longer passed through I18n
functions. This was done to simplify the internals of CakePHP and reduce
overhead. The developer facing messages are rarely, if ever, actually translated -
so the additional overhead reaps very little benefit.

The TestShell has been removed. CakePHP, the application skeleton and
newly baked plugins all use phpunit to run tests.

The webrunner (webroot/test.php) has been removed. CLI adoption has greatly
increased since the initial release of 2.x. Additionaly, CLI runners offer
superior integration with IDE’s and other automated tooling.

If you find yourself in need of a way to run tests from a browser you should
checkout VisualPHPUnit. It
offers many additional features over the old webrunner.

The default value for $replacement argument of Cake\Utility\Inflector::slug()
has been changed from underscore (_) to dash (-). Using dashes to
separate words in URLs is the popular choice and also recommended by Google.

Transliterations for Cake\Utility\Inflector::slug() have changed.
If you use custom transliterations you will need to update your code. Instead
of regular expressions, transliterations use simple string replacement. This
yielded significant performance improvements:

// Instead ofInflector::rules('transliteration',['/ä|æ/'=>'ae','/å/'=>'aa']);// You should useInflector::rules('transliteration',['ä'=>'ae','æ'=>'ae','å'=>'aa']);

Separate set of uninflected and irregular rules for pluralization and
singularization have been removed. Instead we now have a common list for each.
When using Cake\Utility\Inflector::rules() with type ‘singular’
and ‘plural’ you can no longer use keys like ‘uninflected’, ‘irregular’ in
$rules argument array.

You can add / overwrite the list of uninflected and irregular rules using
Cake\Utility\Inflector::rules() by using values ‘uninflected’ and
‘irregular’ for $type argument.

Security::cipher() has been removed. It is insecure and promoted bad
cryptographic practices. You should use Security::encrypt()
instead.

The Configure value Security.cipherSeed is no longer required. With the
removal of Security::cipher() it serves no use.

Backwards compatibility in Cake\Utility\Security::rijndael() for values encrypted prior
to CakePHP 2.3.1 has been removed. You should re-encrypt values using
Security::encrypt() and a recent version of CakePHP 2.x before migrating.

The ability to generate a blowfish hash has been removed. You can no longer use type
“blowfish” for Security::hash(). One should just use PHP’s password_hash()
and password_verify() to generate and verify blowfish hashes. The compability
library ircmaxell/password-compat
which is installed along with CakePHP provides these functions for PHP < 5.5.

OpenSSL is now used over mcrypt when encrypting/decrypting data. This change
provides better performance and future proofs CakePHP against distros dropping
support for mcrypt.

Security::rijndael() is deprecated and only available when using mcrypt.

Warning

Data encrypted with Security::encrypt() in previous versions is not
compatible with the openssl implementation. You should set the
implementation to mcrypt when upgrading.