Repository navigation
Important
2.13.0 adds namespaces (Cake\ for the framework, App.namespace and the CakePHP 5 layout for applications and plugins) and keeps the global class names working. Before you update, check these points in UPGRADE.md:
- A class that overrides a method that now takes variadic arguments (
Model::query(),AuthComponent::allow(), ...) must declare the same parameters, or PHP stops when the class is loaded. App::uses()is deprecated; the first call logs a warning.- Class names in logs, debug output and
get_class()are the namespaced names; comparisons of class names as strings may need changes. - Some bug fixes change results (see "Bug Fixes That Change Results").
- If you use pieceofcake2/phpstan-cakephp2, update it to
^1.1.
Breaking Changes: Variadic Arguments (PR #89)
func_get_args()replaced with variadic parameters: The methods that took a variable number of arguments declare them as...$args, so that PHP and static analysis see them:Shell::dispatchShell(),AuthComponent::allow()/deny(),SecurityComponent::require*(),Controller::setAction()/validate()/validateErrors(),ContainableBehavior::contain(),DboSource::query(),Model::isUnique()/query(),CakeRequest::data()/param()/input()/allowMethod()/onlyAllow(),Router::parseExtensions(),CakeTestCase::loadFixtures(),Hash::merge(),Sanitize::stripTags(),Set::merge(),HtmlHelper::css()/useTag(). The calls do not change. A class that overrides one of these methods with the former signature fails with a fatal error: see UPGRADE
Enhancements: Plugins in the CakePHP 5 Layout (PR #88)
- Plugin directories: Plugins may use the directories of the CakePHP 5 layout, as the application already does:
src/for the namespaced classes (src/Controller/,src/Model/, ..., loaded by Composer),templates/for the views,resources/locales/for the translations,vendor/for the vendor files andconfig/forbootstrap.php,routes.php, configuration files and schemas.App::path()andI18nsearch them, when they exist, before the directories of the CakePHP 2 layout (Controller/,View/,Locale/,Vendor/), which are still searched.App::uses()andApp::load()keep loading global classes from the CakePHP 2 directories - CakePlugin::configPath(): A new method returns the
config/directory of a plugin, orConfig/when the plugin has noconfig/;CakePlugin::bootstrap(),CakePlugin::routes(),PhpReader,IniReaderandCakeSchemause it
Enhancements: Templates in the CakePHP 5 Style (PR #94)
.phptemplates: Intemplates/directories (of the application, of plugins, of themes), templates, layouts and elements may use the.phpextension of CakePHP 5. In each directory a.phptemplate comes before the.ctptemplate of the same name, and the order of the directories is unchanged, so themes and plugins still override the templates of the application. TheView/directories of the CakePHP 2 layout, which also hold the view and helper classes, are not searched for.phptemplates- CakePHP 5 directory names: In
templates/directories,layout/,element/,email/andError/are searched beforeLayouts/,Elements/,Emails/andErrors/. Email templates may be intemplates/email/html/andtemplates/email/text/, and their layouts intemplates/layout/email/html/andtemplates/layout/email/text/
Enhancements: Classes in a Namespace (App::className(), App.namespace) (PR #91)
- App::className(): A new method, backported from CakePHP 3 and later, returns the class name for a name used in the configuration, or
null. A name with a backslash is a fully qualified class name; the suffix is added only when the name does not end with it, soGreetingComponent::classand'Acme\Controller\Component\Greeting'both returnAcme\Controller\Component\GreetingComponent. Other names are looked up in the namespace of the plugin or ofApp.namespace(<Namespace>\<Type>\<Name><Suffix>), then as the global class declared withApp::uses(), as before - Fully qualified class names: Cache engines, email transports,
AuthComponentauthenticate/authorizeobjects, password hashers, view classes (Controller::$viewClass,CakeEmail::viewRender(),RequestHandlerComponent::viewClassMap()), components, helpers, behaviors, shell tasks, shell helpers and thePaginatorHelperajaxhelper useApp::className(). They acceptFoo::class, where the suffix was added a second time before (MemoryEngine::classlooked forMemoryEngineEngine) - App.namespace: When
Configure::write('App.namespace', 'App')is set, controllers (Dispatcher,ControllerTestCase::generate()), shells (ShellDispatcher), models (ClassRegistry::init(),$uses,CakeTestCase::getMockForModel()) and the classes above are also found by their short name in that namespace, e.g.App\Controller\PostsControllerfor/posts, before the global classes. A controller in the namespace merges$componentsand$helperswith theAppControllerof the namespace - The other class settings: Log engines, the exception renderer (
Exception.renderer, also inIntegrationTestTrait), datasources, session handlers, route classes ('routeClass'), dispatcher filters,Acl.classname, theengineofTextHelper,NumberHelperandTimeHelper, the readers ofHtmlHelper::loadConfig(), the localized classes ofValidation(NlValidation), the response class and authentication methods ofHttpSocket, test reporters, the models of fixture imports,CakeSchemaand theExtractTask, and the shells ofCommandTaskalso useApp::className(), as on thefeature/phpstan-level5branch. Their global names and their fully qualified names work as before, and they are also found inApp.namespaceand in the namespace of a plugin - Plugin AppController and AppModel: A plugin controller merges the vars of the
AppControllerof the namespace of its plugin (MyPlugin\Controller\MyPluginAppController),ClassRegistry::init()uses theAppModelof that namespace for a plugin model without a class, and a model inApp.namespacemerges$actsAsand$findMethodswithApp\Model\AppModel - Names without the namespace:
Controller::$name,Model::$nameandShell::$nameare derived from the class name without its namespace, e.g.PostsforApp\Controller\PostsController - Namespaced fixtures: Fixtures are also loaded from
Cake\Test\Fixture(core.*),<App.namespace>\Test\Fixture(app.*) and<Plugin>\Test\Fixture(plugin.*), before the global classes; the name and the table come from the class name without the namespace - App::import() and App::load():
App::import()also finds namespaced classes,App::load()includes each file once, and an application withAppController,AppModel,AppHelperorAppShellonly inApp.namespacegets the global name for it, so that core classes such asCakeErrorControllerthat extend it work - AppController in any namespace: A controller merges the vars of a parent class named
AppControllerin any namespace, also withoutApp.namespace - ClassRegistry::init(Foo::class): A fully qualified class name without an alias gets the short class name as its alias, e.g.
PostforClassRegistry::init(Post::class), as a short name does; before, the alias contained the backslashes - ClassRegistry keys:
ClassRegistry::getObject(),isKeySet()andremoveObject()also find an object stored with the short name of its class by the fully qualified class name, e.g.ClassRegistry::getObject(TranslateBehavior::class)orClassRegistry::getObject(Post::class)for the model with the aliasPost, and otherwise the only object of exactly that class, e.g. aPostwith the aliasArticle; with several objects of the class nothing is found - Class names as aliases: Fully qualified class names as keys of
$components,$helpers,$actsAs, Shell$tasks,$uses(Controller and Shell) and the model associations, or given toload()of the collections andloadModel(), get the short class name without the namespace and the suffix as alias and property name, e.g.public $components = [GreetingComponent::class]gives$this->Greeting,$uses = [Post::class]gives$this->Postand$modelClassPost, and$belongsTo = [Author::class]gives the associationAuthorwithclassNameAuthor::class, the same as the short name withclassName. A class name key with its ownclassNamealso gets the short alias and keeps thatclassName; before, the alias contained the backslashes.enable(),disable(),unload(),loaded(),enabled()andset()of the collections andbindModel()/unbindModel()also take the class name - Merged class names: When the vars of a parent class are merged (
$components,$helpers,$actsAs, Shell$tasks, also from a plugin AppController and the AppModel), a class name key is replaced by its alias with the class name in'className', so'Greeting' => [...]in the AppController andGreetingComponent::class => [...]in a controller give one entryGreetingwith the merged settings, as two short names do. The newObjectCollection::normalizeClassNames()does this - Join models by class name:
'with' => PostsTag::classof a hasAndBelongsToMany association works like'with' => 'PostsTag'when reading, saving, validating and deleting (joinModel(),__isset(),_saveMulti(),_deleteLinks()), and inCakeSchema::read(); the scaffold names the controller of an association with a class name asclassNameby the short name - Missing classes: The errors for a missing component, helper, behavior, task or controller (
ControllerTestCase::generate()) add the suffix to a fully qualified class name only when it does not end with it, e.g.Acme\Controller\Component\GreetingComponentinstead of...GreetingComponentComponent - Cached views:
Controller::$controllerClassandView::$controllerClasshold the class name of the controller, and the views cached byCacheHelpercreate a namespaced controller by that name - Fixes for namespaced classes: Dispatcher filters with settings,
CakeSchema::read()for plugin models,ClassRegistry::init()for a stored alias, the plugin and name of namespaced models in the rendered view, and the plugin AppController of a request whose plugin is not loaded (noMissingPluginException)
Enhancements: The Framework in the Cake Namespace (PR #92)
- Namespaced classes: The classes of the framework are in the
Cakenamespace, following their directories:Cake\Controller\Controller,Cake\Network\CakeRequest,Cake\Utility\Hash, ... Applications and plugins canusethem, e.g.use Cake\Controller\Component; src/and PSR-4: As on thefeature/phpstan-level5branch, the classes are insrc/(src/Controller/Controller.php) instead ofsrc/Cake/, and Composer loads them with"psr-4": {"Cake\\": "src/"}instead of a classmap. The files that declared several classes are split into one file per class (src/Error/NotFoundException.php,src/Controller/Component/Acl/PhpAco.php,src/TestSuite/ControllerTestDispatcher.php, ...).CAKEissrc/.src/Cake/bootstrap.phpandsrc/Cake/TestSuite/Fixture/CakeFixtureInjector.phpstay at their former paths for the applications and plugins that name them- Global names: The global names of CakePHP 2 keep working as aliases of the same classes (
class_alias()). Each class file declares its alias when the class is loaded, andsrc/aliases.php, loaded by Composer, loads the namespaced class when an application uses the global name first.ControllerandCake\Controller\Controllerare the same class:extends,instanceof, type declarations,catchblocks andApp::uses()work with either name - Class names in output:
get_class(),::classand the messages, logs, debug output and error pages that show them use the namespaced names, e.g.[Cake\Error\NotFoundException]in the error log - Overriding core classes: A global class registered with
App::uses()no longer replaces a core class, as the core uses the namespaced names. Override a core class with a copy insrc/Cake/(e.g.src/Cake/Routing/Dispatcher.php) loaded by the classmap of the application, and exclude the core file withexclude-from-classmap - phpcs:
SlevomatCodingStandard.Namespaces.UnusedUsescounts the class names in annotations, as the core imports the classes that its docblocks name
Enhancements: Fixture Class Names (PR #108)
- Fixtures by class name: A fixture is preferably named by its fully qualified class name, e.g.
public $fixtures = [ArticleFixture::class]and$this->loadFixtures(ArticleFixture::class), as the other classes in the configuration are.$fixtures,loadFixtures()andCakeFixtureManager::loadSingle()accept it and use exactly that class, so that fixtures with the same name in different namespaces do not collide;loadSingle()throwsUnexpectedValueExceptionfor a class that is not among the fixtures of the test case. A leading backslash is ignored, and theFixturesuffix is added only when the name does not end with it. A class given more than once, also in another notation, has one instance, so that its table is created and dropped once. The former notation,core.article,app.article,plugin.foo.barandArticle, works as before
Deprecations: App::uses() (PR #98)
- App::uses(): Deprecated. Composer loads the classes of the core, of
pieceofcake2/appand of the plugins installed with it, and the core finds the classes named in the configuration without it. The first call in a process logs a warning with the file and line of the call, and triggersE_USER_DEPRECATEDwhendebugis above 0. The call still works - Core: The core no longer calls
App::uses().AppController,AppModel,AppHelperandAppShellof the application are found in their packages without it, and the classes that the core loads from an application or a plugin are found withApp::className(). The code thatCacheHelperwrites into cached views and the code that the error pages suggest no longer contain it
Bug Fixes: PHP Deprecations (PR #96)
- Null passed to built-in functions: Many methods passed a value that can be
null(e.g.env()of a missing variable, an unset option, the plugin ofpluginSplit()) to a string function of PHP, which is deprecated since PHP 8.1. They cast it to a string (or an int for the decimals ofCakeNumber), so the result is the same as before. This covers among othersCakeSession,CakeRequest,CakeResponse,CakeEmail,HttpSocket,Security,Configure::consume(),Xml::build(),FormHelperdate and radio inputs,CakeText::cleanInsert(),Hash/Setmatching,File,pluginSplit(),env(),clearCache()and the missing class exceptions of the object collections - False converted to an array:
I18n(on a cache miss of a domain) andCakeRoute(with named parameters beforeRouter::connectNamed()) no longer add keys tofalse, deprecated since PHP 8.1 - Xml:
Xml::build()no longer callslibxml_disable_entity_loader(), deprecated since PHP 8.0. External entities stay disabled: libxml 2.9 and later, which PHP 8 requires, does not load them withoutLIBXML_NOENT, whichXmldoes not pass - Xml
loadEntitiesoption removed: The option ofXml::build()only decided whether to calllibxml_disable_entity_loader(), so it no longer has any effect and is removed. Passing it is ignored - Security:
Security::hash()withsha256always useshash()instead ofmhash(), deprecated since PHP 8.1. The digest is the same - SessionHandlerAdapter: The methods of
SessionHandlerInterfacehave#[\ReturnTypeWillChange], so PHP 8.1 and later no longer report their missing return types - CakeTestFixture:
CakeTestFixturedeclares$import, so setting it on a fixture that does not declare it no longer creates a dynamic property, deprecated since PHP 8.2 - Null as an array key:
I18n,Helper::setEntity(),FormHelper,RequestHandlerComponent::respondAs(),CakeResponse::getMimeType(),CakeSession::check(),Configure::consume(),ClassRegistry::getObject(),ConsoleOptionParser::parse()/help()andSet::extract()check that a name or key is given before they use it as an array key, instead of usingnull, deprecated since PHP 8.5. A missing name finds nothing, as before.I18nlooks up the translations without a context under the empty context, where they are stored - Reflection:
Debugger::export()callsReflectionProperty::setAccessible()only before PHP 8.1, where it has an effect. It is deprecated since PHP 8.5 - No request input or URL:
CakeRequest::input()with a decoding callback returnsnullwithout calling it when the request has no body, or when_readInput()givesnull, e.g. in a subclass or a mock, instead of passing the empty input on.RequestHandlerComponentsets the same empty data as before. A callback of your own that handled an empty body now gets no call.CacheHelperwrites no cache file whenhere()gives no URL, instead of passingnullto the string functions of PHP - Mysql: The
Mysqldatasource uses thePdo\Mysql::ATTR_*constants instead ofPDO::MYSQL_ATTR_*, deprecated since PHP 8.5. The core requiressymfony/polyfill-php84, which definesPdo\Mysqlwith the same values before PHP 8.4 - RedisEngine:
RedisEngine::delete()deletes the key withRedis::del()instead ofRedis::delete(), which phpredis deprecates - Security:
Security::encrypt()encryptsnullas the empty string, instead of passing it tostrlen()(deprecated since PHP 8.1) - Sqlserver:
Sqlserver::limit()checks that the offset is a string beforectype_digit(),renderStatement()checks that a limit is given before it looks forTOPorFETCH, andcreate()checks that the table has an identity column before it looks for its value, instead of passingnullor other types to them (deprecated since PHP 8.1)
Bug Fixes: Null Direction in DboSource::order() (PR #101)
- DboSource::order(): A field without a direction (e.g.
order('Post.title', null)) no longer passesnulltotrim(), deprecated since PHP 8.1. The SQL is unchanged - Tests: The test of the directions passes the errors other than
E_USER_DEPRECATEDto PHPUnit, so it reports deprecations again
Bug Fixes: Console (PR #104)
- ShellDispatcher: An unknown shell gives
MissingShellException, notMissingPluginException - AclShell: The parser and help no longer set a dynamic property (the unsupported
defaultof the action arguments is removed) - AclShell:
nodeExists aco <number>usesforeign_key, not the missingobject_idcolumn - AclShell: A
Model.foreign_keyreference that is not found is reported with "Could not find node using reference" instead of an uncaught exception - ProgressShellHelper:
draw()passes an integer tostr_repeat(), as a float is deprecated since PHP 8.1 - TestShell: A file in
src/of a plugin maps to its test case intests/TestCase/ - TestShell: The "cannot be run via this shell" message names the given file
- ApiShell: The input
land other text that is not a number no longer fail on PHP 8.3 and later - ApiShell: Each lookup reads the classes of its own path, not the cached list of the first one
- ApiShell: A class file that cannot be included is reported without a
ReflectionException - ApiShell:
api help classNameandapi help typeshow their help - ApiShell:
api helper HtmlfindsHtmlHelper; the type suffix is added before the lookup - ConsoleShell:
unbindwith an unknown model prints the "Please verify" message - ConsoleShell:
bindandunbinduse the stripped names - File: Creating a file whose directory cannot be created no longer passes
nulltopathinfo()andis_dir()
Bug Fixes: Found by the Tests (PR #107)
- Test runner page: The HTML reporter of
test.phpescapes the request values that it writes into the page (case, plugin, filter and the links) and the pages escape the base directory taken fromPHP_SELF, which could run a script in the page (XSS). Invalid UTF-8 in them is replaced instead of being dropped. The page header has its doctype,<html>,<head>, charset and title again, and the pages for PHPUnit, Xdebug and a missing connection get the base directory for their stylesheet and images - Test reporters: Skipped and incomplete tests, and tests with a warning, are no longer painted as passes. A failure shows the exception that the test threw instead of the wrapper of PHPUnit, with its class, file, line and trace, and the text reporter shows the line of the test that failed, also when the test throws it itself or runs in another test. The case list of the text reporter links plugin and core cases with
&plugin=and&core=trueand encoded names, and prints an empty list once instead of twice. The summaries count only the finished tests as complete and show the skipped and incomplete tests: see UPGRADE - Test runner: A
core.*fixture is looked up only in the fixture path of the core. A test case of a plugin that does not exist raisesMissingPluginExceptioninstead of looking for the file under/ - JsHelper engines:
escape()writes a character outside the Basic Multilingual Plane as a UTF-16 surrogate pair, other C0 control characters as\u00XXinstead of dropping them, and a question mark for bytes that are not valid UTF-8 (a stray continuation byte, a sequence cut off by the end of the string or by another byte, an overlong sequence, a surrogate, the lead bytes0xF8to0xFF).value()writes a float with a dot whatever the locale.object()replaces invalid UTF-8 with U+FFFD instead of returning only the prefix and postfix.object()andvalue()throwCakeExceptionforINF,NANand other data thatjson_encode()cannot encode.redirect()escapes the URL in its JavaScript string. The query string of thedataofrequest()writes nested arrays askey[sub]=valueinstead ofArray, leaves a null value out and writesfalseas0, asRouterdoes; the values are still not URL-encoded. Empty callbacks are left out instead of givingerror: - Scaffold: The
listaction is removed. It was the former name ofindex, kept as an alias when the views were renamed in CakePHP 1.2, but there was nolistview, so it failed withMissingViewException. A request tolistnow givesMissingActionException, as other actions that are not scaffolded do.createandupdatestay - CakeSocket: A fractional
timeoutis passed tostream_set_timeout()as seconds and microseconds. Through a proxy, theCONNECTrequest sends the target asHost, and a status other than 2xx throwsSocketException.reset()closes the open connection before it sets a state with a connection. The connection warnings are cleared at eachconnect() - CakeSession:
clear()empties the session data before it renews the session, so that the data is not carried over to the new id. In debug mode, the save path ofSession.inisession.save_pathis created for thefileshandler, also in theN;pathform; nothing is created for other handlers - DataSource:
insertQueryData()accepts an integer0as the id, and throwsNotImplementedExceptionwhen the datasource has novalue()method - Model:
TreeBehavior::reorder()stops on a tree that is not valid.DboSource::create()andSqlserver::create()without fields insert the data of the model.getColumnTypes()of a model without a schema no longer raises a PHP warning fromforeach()besides its own warning.Sqlserver::lastAffected()returns the rows changed by anUPDATEor aDELETEinstead of 0 - CakeSchema:
write()without tables writes the tables thatread()finds - Folder:
realpath()resolves a relative path with..from the current directory, socd('../sibling')works - Debugger:
excerpt()highlights the given line in the middle of the lines it returns: see UPGRADE - Translations of "0":
__()and the other translation functions translate the message'0'instead of returningnull, and a translation whosemsgstris"0"is used. Po files load the messages"0"and other numbers, which were dropped or stored under another number - Multibyte:
utf8()andascii()decode and encode the characters outside the Basic Multilingual Plane (4 bytes in UTF-8), sostrtolower(),strtoupper()and the other methods keep them instead of breaking them
Bug Fixes: AclNode::node() of a Number (PR #110)
- AclNode::node(): A reference that is not an array, a path or a model, such as the number that
AclShellpasses for a numeric argument (cake acl check 5 3), finds nothing again, as before v2.12.6, instead of failing with aTypeError
Bug Fixes: The Key '0' (PR #111)
- The key
'0':Configure::consume(),CakeResponse::getMimeType(),ClassRegistry::getObject()and the subcommands ofConsoleOptionParsertake'0'as a key again. The checks for a missing key of PR #96 usedempty(), which also rejected'0'; they now check fornullonly
Bug Fixes: Found by the Tests of the Namespace Pull Requests (PR #112)
- PhpAcl:
check()of an ACO without nodes, such as'/', returns the policy instead of raising "Undefined array key 0";PhpAco::path()returns an empty path for it - ControllerTestCase::$loadRoutes:
falseresets the routes again beforetestAction()parses the URL, so the routes that the test connected are not used. The Dispatcher has not calledControllerTestDispatcher::_loadRoutes()since the routes are loaded lazily (CakePHP 2.3);ControllerTestDispatcher::parseParams()now calls it - Cache::drop(): The dropped configuration is removed from its groups, so
Cache::groupConfigs()no longer lists it, and a group that no configuration uses any more is removed - Cache::config(): An engine whose
init()fails is not kept, so configuring it again with the same settings throws theCacheExceptionagain instead of using the engine that failed - AppShell: The shells and tasks of the framework extend the global
AppShellagain, which is theAppShellof the application when it has one, as before PR #92.Cake\Console\Command\AppShellno longer declares the global name itself;src/aliases.phpdeclares it only for an application without anAppShell. Loading a shell of the framework after theAppShellof the application no longer fails with "Cannot redeclare class AppShell", and loadingCake\Console\Command\AppShellfirst no longer hides theAppShellof the application
Tests: Coverage (PR #106)
- More tests: Tests for the test suite (reporters, runner, fixture manager), the Js helpers, the scaffold, the network and session classes, the datasources, the models and behaviors,
CakeSchemaand the utilities. The bugs that they found are fixed in PR #107
Tests: Patch Coverage and Repeated Runs (PR #112)
- Coverage of PR #92 and PR #98: Tests for the global class names in a new process, the former paths in
src/Cake/,CakeFixtureInjector,PhpAcl,MissingConnectionException,ControllerTestCasewithoutautoMockand the cached views of global and plugin controllers. The bugs that they found are fixed above - Repeated runs: The tests restore the state that they change, so they also pass with
--repeat 2or alone; the tests that need classes that are not loaded yet, or define a constant, run in their own process - Folder sort by time: The tests of
Folder::SORT_TIMEwait until the files have later change times, instead ofsleep(1)
Tests (PR #96)
- Deprecations reported: The tests report deprecations and fail on them.
ConfigureTest::testBootstrap()restores the error level afterConfigure::bootstrap(), which had hidden deprecations from the following tests, and the tests no longer create dynamic properties, or useutf8_decode(),new DateTime(null),str_getcsv()without the escape character orsession.referer_check. TheDATABASE_CONFIGof the tests allows dynamic properties, whichConnectionManager::create()adds. The test helpers callsetAccessible()of reflection only before PHP 8.1.ApplicationControllerTesttests the redirect without cookies on every version, and the session ID in the URL, which needssession.use_trans_siddeprecated since PHP 8.4, only before PHP 8.4. The tests that use mcrypt check that it reports the deprecation of its functions, andMemcacheEngineTestreplacesMemcache::set()with a small subclass, as a mock would copy the implicitly nullable parameters of the extension (deprecated since PHP 8.4)
Tests: Error Handler of the Test Bootstrap (PR #103)
- Application tests follow their phpunit.xml: The test bootstrap no longer turns every deprecation into an exception in the tests of applications
Tests: Namespaced Tests and AllLegacyTests (PR #95)
- Namespaced tests:
tests/holds the namespaced tests offeature/phpstan-level5(Cake\Test\TestCase\...), with a test application in the CakePHP 5 layout (tests/test_app/,TestApp\). The classes of the application are real classes of the test application, not aliases, and the tests that need it setApp.namespaceinsetUp() AllLegacyTests: A second test suite, run withvendor/bin/phpunit --testsuite AllLegacyTests, checks that an application in the CakePHP 2 layout (tests/test_legacy_app/) and plugins in the legacy layout keep working with the 2.x notation and withoutApp.namespace, and that a namespaced plugin works in it and a legacy plugin in a namespaced application- Test plugins: The test plugins are named
Test<Name>Plugin, andTest<Name>LegacyPluginin the legacy layout
Upgrading
composer require pieceofcake2/cakephp:^2.13
composer require --dev pieceofcake2/phpstan-cakephp2:^1.1 # if you use itSee UPGRADE.md, the migration guide, Migrating to the Modern Layout and Autoloading. pieceofcake2/upgrade moves an application or a plugin to the new layout and to namespaces.
Full Changelog: v2.12.11...v2.13.0