The ultimate upgrade guide to Cake 4

Here we go, time to upgrade again. And to document it.
CakePHP 4.1 is already released, and also at work now we consider the upgrade of a larger internal application.
This is basically the successor of the old upgrade notes from 3.x days.

The reason why now?
You don’t want to upgrade to a new major too soon. The core still needs to stabilize, many small bugs need to be fixed.
Plugins are not all upgraded yet, and so on.

You also don’t want to upgrade too late. Many bugs are now mainly only solved and fixed on master (4.x), and not 3.x.
You can sure help to backport, but the same game also applies to plugins now and the rest of the ecosystem.
So soon there is more work in trying to keep the outdated code base up to date.
The first minor of a major is usually a good fit.

So let’s get started – this is an interactive guide parallel to an upgrade.

First steps

  • Make sure you migrated to 3.9 latest. This is important as the gap from 3.9 to 4.1 is the smallest of all.
  • Make sure you removed all deprecated code and you are on PHP 7.2+ already! Run rector with 3.0…3.9 sets and check if all have been applied.
  • Make sure you got test coverage in place.
  • Make sure you got PHPStan level 1 minimum in place (better 2/3/4/5).

These steps can take weeks, but help to close the gap towards the upcoming migration.
Now you got a stable and up to date 3.9 app and can move on towards the next step.

If you want to further close the migration gap (to be forced to touch as little as possible when actually upgrading), you can check

  • 4.x migration guides and see what you can already FC (forward compatibility) shim.
  • Check out Shim plugin and how it could help you already prepare for the next major.
    For example database Type classes and other useful FC shims can be valuable in reducing the "unexpected" (BC) issues upfront. Enough of them and you also delay the upgrade by more than you planned. Better to already tackle them without time pressure beforehand, preparing a test case that will also then pass after the upgrade.

In my case I already replaced all (Event $event) {} usage with (EventInterface $event) {} in callbacks for example.
Then I shimmed the new Database Type classes for bool, string and decimal, referer() of Controller, newEmptyEntity() of Table etc.

Your Cake 3 app will work just fine with those, and once you are shifting over those lines don’t have to be touched anymore.
The resulting diff will be smaller and easier to review.

Another tip: Work with a powerful IDE here.
It will help you to refactor code without accidental errors (forgetting one variable/property, …) and provide useful tools.
It shows you in advance deprecations and can help to replace them at once.
PHPStorm IDE, for example, is very useful here. It also comes with a powerful regexp find/replace and works even across multiline snippets
using basic text-select. With this some custom replacements of signatures, return types can be done where needed across the system.

More useful things to do while you still have a working app

Check out these tools in advance:


Especially the latter contains some extra jobs for your Cake3 app to be on the latest version.

Also check out this composer migration tool. It can help to assess what plugins are available in 4.x and in what version.
Once you start upgrading you can then quickly use this to replace the versions.

Preparing a v2

We have a locally running app.local of the application.
Prepare now a 2nd env for it: app2.local or alike:

  • Copy the codebase
  • Copy the DB and configure everything. Open this 2nd app in a new IDE (PHPStorm) window.
  • You should now have this 2nd URL working and a completely separate environment for it.

Why a second one in parallel? Often, you need to switch back to the first version to check how things worked there.
Also, this upgrade can take days, weeks, or more if you get stuck for some reason.
It is super painful to switch branches in the same app, downgrade and run it to add more fixes for production.
Better to have things separated and plan at least for a longer game.

Now make sure you committed everything so far in the 3.x branch, and created a new 4.x branch of your project.
Switch to that 4.x branch and commit each upgrade step separately (so we can later review, undo or modify where needed).

The pre-runs

Check rector and if you can now apply also 4.0 and 4.1 sets. They will only run on a valid (syntax wise) codebase.
So if you don’t do that now, it will be harder to process them in the next steps.

This will be the last time now for a while to run also any shell/command.

The upgrade

We will now apply first some (partially) manual steps.
The composer.json is often too complex to auto-adjust, here we set the new versions.

I used these snippets so far to quickly run some basic replace regexp over the codebase.
But you can also manually finish up the composer.json file.
In the end, you should be able to run composer update and get the 4.x core pulled in.

Now we can start applying more upgrades, e.g. by using the upgrade tools


Get familiar with the different useful parts, use what you need/want, enhance in your own fork what is needed.
The last part is especially important if you have more than one app, and you want to save time upgrading the following apps 🙂

One important command from dereuromark/upgrade is the skeleton one:

bin/cake upgrade_legacy skeleton /path/to/app2.local/ -o

This will replace all your basic files with the latest cakephp/app skeleton files.
Here make sure you committed first and compare the diff afterward before committing again.
You will have to merge back in some of your customizations in the configs, bootstrap/route files and Application class.

It now also contains 4.x tasks for methods and their signatures now:

  • method_naming
  • method_signatures

Finishing it up

You should now be able to browse your new version again to some extend.
It will need a lot of small tweaks still here and there. Especially if you upgraded before removing all deprecations in the 3.x version.

Final checklist:

  • IdeHelper tools bin/cake code_completion generate && bin/cake phpstorm generate is running with errors thrown
  • IdeHelper annotator is running and updated annotations on all your classes/files
  • PHPStan level from before (ideally at least 5) passes again. But even level 1 is super helpful as it will find basic issues.
  • Tests are adjusted and passing again (this can take a bit of patience)

Deploy the new app as a staging env and check it also using production settings (and without require-dev packages).
If all works you can plan the switch of v1 to v2.

Enjoy your upgraded app(s)!

Final note

This post will be subject to change and additions where needed in the next months based on more apps that are being migrated (by me or others).
So feel free to write to me, comment, or provide additional feedback.

Also please help to enhance the given upgrade tools, make PRs and enhancements where you can – so that everyone can benefit from it.

Update 2020-11

Added the composer migration tool above, as well as the new 4.x upgrade tool commands ready.

5.00 avg. rating (97% score) - 5 votes


  1. Hello,
    try’n to migrate from cakePhp 3.9 to 4.x, i got a lot of errors with installed third party plugins.

    Should i remove them before applying "bin/cake upgrade rector –rules phpunit80" ?

    Many thanks for this usefull article.

  2. Yes, either remove or maintain your own fork of it until those are fixed up.
    It always helps to give the author a PR with your changes to more quickly allow those plugins to be ready.
    Then a new tag/release of those is quickly done, as well.

  3. When hit this code –
    bin/cake upgrade rector –rules phpunit80
    Get message –
    The "–set" option does not exist.
    This is full text command –

    bin/cake upgrade rector –rules phpunit80 /opt/lampp/htdocs/newagent/cakefull/tests/ -v
    Detecting autoload file for /opt/lampp/htdocs/newagent/cakefull/tests
    -> Checking /opt/lampp/htdocs/newagent/cakefull/tests/vendor/autoload.php
    -> Checking /opt/lampp/htdocs/newagent/cakefull/vendor/autoload.php
    -> Found /opt/lampp/htdocs/newagent/cakefull/vendor/autoload.php
    Running /opt/lampp/htdocs/upgrade/vendor/bin/rector process –autoload-file=’/opt/lampp/htdocs/newagent/cakefull/vendor/autoload.php’ –set=’phpunit80′ –working-dir=’/opt/lampp/htdocs/newagent/cakefull/tests’ ‘/opt/lampp/htdocs/newagent/cakefull/tests’

    The "–set" option does not exist.

    process [-n|–dry-run] [-a|–autoload-file AUTOLOAD-FILE] [–no-progress-bar] [–no-diffs] [–output-format OUTPUT-FORMAT] [–debug] [–memory-limit MEMORY-LIMIT] [–clear-cache] [–port PORT] [–identifier IDENTIFIER] [–] […]

    Please tell what is the issue

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.