Compatibility Breaks in CakePHP 3.0

There will be a number of backwards compatibility (BC) breaks in the CakePHP 3.0.0 release. I thought it might be helpful to go over some of the reasons breaks in compatibility have been made. Each time we’ve had to break compatibility with 2.x we’ve done so because the existing behaviour fell into a few categories of problems. I’ll go over a few of the bigger categories in detail.

PHP has changed

In the years since CakePHP 2.0.0 was released, PHP has dramatically changed. The features in PHP5.3+, and composer have radically changed how developers work in PHP. To be frank, CakePHP has fallen a bit behind. While we were really good about backwards compatibility, we might have been a bit slow to make breaking changes and adopt new tools of our trade. For CakePHP 3.0, we’ve decided to move the version requirements up to PHP5.4, and fully embrace composer. By adding namespaces, all the class names in CakePHP have changed. Changing all the class names represents a significant break in compatibility. We’ve tried to reduce the tedium of this change by providing an upgrade tool to help with some of the more tedious naming changes.

Adopting composer has meant that the app skeleton and framework have become separate repositories. With the huge selection of great packages available throught composer, CakePHP has started to pull in a few dependencies. There are only a handful of dependencies and they are all compact libraries that provide functionality that wouldn’t help make CakePHP better if we wrote it ourselves. For example carbon, and password_compat allow us to have much better datetime and password hashing functionality without having to spend time building it ourselves. Plugins in CakePHP 3.0, will also be installable through composer, and the old vendor directories should be replaced entirely via composer.

Ideas that didn’t work out so well

Another class of things that have been ‘broken’ in CakePHP 3.0 are what I refer to as the ‘good idea at the time’ features. A few specific examples of this are:

  • Named parameters – I’m not personally convinced these were ever a good idea. They were non-standard, provided no measurable value, and were a pain to integrate with others tools.
  • Full page caching – The 1.x era full page caching worked at the time, but has aged poorly. It has a number of limitations now that are not addressable without breaking the behaviour. If we were going to replace the feature I would want to use Edge Side Includes which is a W3C draft. ESI is the same specification that standalone response caching tools like Varnish use. However, upon reflection it didn’t make sense to build a poor-man’s varnish. Generally applications dealing with the volumes of traffic that justify varnish, can afford the additional investment required to deploy varnish.
  • JsHelper – While useful in some circumstances, web development has shifted in the past 3 years. Applications rely on javascript more each year, and JsHelper was not providing enough value to continue existing.

Outdated implementations

One area where we’ve intentionally not broken compatibility since the 1.2 days is the model layer. Because models and your application’s data are so fundamental to how you work, we’ve intentionally not changed the model layr for many years. While some people really like the models in CakePHP, many do not. I personally feel that object mappers like SQLAlchemy and DataMapper are more elegant solutions that make it easier to work with relational databases. For CakePHP 3.0, the ORM has been entirely replaced with a datamapper inspired approach. Jose Lorenzo has put a monumental amount of work into the ORM and I think it is pretty fantastic. Being able to do things like:

Show Plain Text
  1. // Use multiple custom finder methods.
  2. // Conditions and contain associations.
  3. $query = $this->Articles->find()
  4.     ->recent()
  5.     ->published()
  6.     ->where(['Articles.user_id' => 2])
  7.     ->contain(['Comments', 'Tags']);

Is pretty fantastic in my books. Association eager loading has been made a key part of the ORM, and annoying features like recursive have been removed. There are also new easier to use features for connecting associations and creating validators. Of all the changes in CakePHP, I think the ORM is the most exciting. I suggest you checkout the ORM documentation for more details.

Improve consistency

In more than a few places compatibility was broken to create better cohesion across CakePHP. I can’t count the times that I’ve had to look up how specific classes need to be configured. For 3.0, AD7six has done a great job in unifying all the important parts of the framework. If you want to read or write configuration you can:

Show Plain Text
  1. // Read all configuration
  2. $obj->config();
  4. // Read a single config value.
  5. $obj->config('engine');
  7. // Set multiple config values
  8. $obj->config(['key' => 'value', 'key2' => 'value']);
  10. // Set a single value.
  11. $obj->config('key', 'value');

All classes that have settings can be interacted with via the same methods. CakePHP provides these configuration features as a trait making it a snap to have consistent methods in your plugins/application.

The callbacks in the models, components, and helpers has maintained compatibility with 2.0.0, throughout all of 2.x. However, in 2.2 CakePHP got a full fledged events subsystem. The new events subsystem had a number of advantages over the old callbacks, and for 3.0, we wanted to choose one solution. We have removed the old style callbacks, and now all framework callbacks are provided through events.

While CakePHP 3.0 does contain a number of backwards incompatible changes, I think they were all justified changes that will help make CakePHP faster, and simpler to use in the future. Migrating existing applications will not be easy, but I think the changes will be worth it in the end.


The fourth example in the “Improve Consistency” section feels inconsistent to me because a) after reading the second example I would have expected a different behavior (returning the values for two keys, respectively n keys in the general case), and b) it uses an implicit key/value relationship for the parameters whereas you could express this relationship explicitly with an array.

Daniel Hofstetter on 7/27/14

Great post! And big kudos to everyone contributing to the project, this really is an amazing community driven effort. Larry also wrote an interesting article on the history of CakePHP, which gives some ideas as to where some of the framework’s quirks originated from. In the end, 10 years is a long time!

James Watts on 7/27/14

Good post!

While migrating from 2.x to 3.x will be a pain (or just downright infeasible at times) new projects will be a lot better off starting with 3.x. It took me less than an hour to get used to the new layout and the new ORM just feels natural.

You’ve all done a great job with CakePHP 3!

Nick Cinger on 7/27/14

2015 will be the year of CakePHP 3 :)

kminek on 7/27/14

Yes 3.x migration is a bit difficult, But the pleasure of working with 3.x difficulties are causing forgetfulness.

thanks you and team

Saleh Souzanchi on 7/28/14

Daniel Hofstetter: Thanks for the feedback. I can see how the single set mode could be a bit confusing. However, it enables writing deep values for example $this->config('default.engine', 'MyEngine') which would be a bit more verbose if the set mode only accepted arrays.

mark story on 7/28/14

Thanks for the rundown! I actually went thru the blog tutorial last week and was kinda floored by how much had changed, so it’s nice to know the reasoning. I can’t say I instantly fell in love, but progress is progress. I’ll also admit that since it’s so utterly different, it’s inspired me to explore other options out there simply to compare & contrast. But I like Laravel even less, so I don’t really see myself abandoning CakePHP =]

Brade on 7/28/14

The thing that I didn’t like about the previous ORM is that there was no easy way to tell it to do an inner join. I see in the new model, you still have to manually specify the joins if you want to force an inner join. Is there a better way to do this?

GiulianoB on 7/28/14

GiulianoB: You can set the joinType when defining an association. This lets you force specific associations to always use INNER JOIN. Is that the kind of thing you were looking for?

mark story on 7/30/14

Yes, PHP is evolving and we now have PHP -S local:8080 too!
I am not convinced about the idea of PSR and composer though I do understand (it’s like having a Google plus account – just for the heck of it). PSR allows us to use multiple framework (and other) libs (which even Vendor did) but after working on enterprise apps all I can say is, using PSR will encourage random programmers choosing random libraries to accomplish 1 task which could have been achieved if they had let themselves into a steep learning curve. After working on one architecture with modules written with Zend + CI + Sym – with cross Auth! (jeez!) the whole “adapting PSR thing” may create a challenge for master of one. Point is, standard-ization and irony! I thought Cake would be away from it but, I guess market demands something else.
Just my critique. :)

PS: I am huge fan of CakePHP and have been following up actively on it.

Karma Dice on 7/30/14

Karma Dice: I don’t think you should confuse composer and PSR-* specs, they are very different things. While I can totally understand why one could not like the idea of PSR-* specs I don’t understand the resistance to composer. It is great tool that makes installing libraries and keeping them current easy.

mark story on 8/5/14

I am all new here, learning Cakephp 3.0 anf feels great.

Naresh on 5/2/15

Have your say: