Learn about Rector, Upgrades and Planning
Notes on automated refactoring, static analysis and keeping PHP projects alive.
As of Rector 2.5, Composer-based sets now support Drupal! Instead of manually adding dozens of configuration sets and keeping the list up to date as you upgrade, you enable a single feature in your rector.php.
Rector inspects your composer.json, detects your exact installed Drupal version, and runs the relevant refactoring sets automatically. Upgrade core, run Rector, stay up to date, no manual config changes.
PHPUnit 12 was released a year ago, but only PHPUnit 12.5 released in December 2025 includes valuable features that are worth it.
The most important change, that will affect your code, is that mocks are now much stricter. There are also stubs, a mock that does nothing. How do you spot them and separate them?
Curious how to get from 4000 notices to under 100 in 7 diffs? Read on.
PHP's declare(strict_types=1) directive is a powerful tool for preventing subtle bugs.
Yet most existing, mature projects don't use it consistently, if at all.
Why? Because automatically adding it to all your files at once will cause your application to explode: expect thousands of errors.
That leaves you fixing files by hand, one by one, and relying on team members to remember to add it to new files.
Without a safe and automated process, adoption hardly ever sticks.
Until now. The new SafeDeclareStrictTypesRector only adds strict types to files that are already type-safe, making safe, progressive adoption finally possible and preventing the file from becoming non-strict in the future.
Rector helps you improve PHP code, upgrade it to latest PHP version, make use of modern features and faster code structures. But did you know it can make your PHPUnit tests faster and easier to read?
New PHPUnit version have more precise and reliable asserts, but most people don't know about them. They make tests run faster and in case of failure, provide more clear error message you'll understand.
Rector can help you with that!
As you know, we provide an upgrade services to speed up the modernization of codebases. Part of this service is getting PHPStan to level 8 with no baseline (only edge cases).
Level 6 is known for requesting more detailed types over array,
iterable or Iterator type hints. Bare mixed or array should be replaced with explicit key/value types, e.g., string[] or array<int, SomeObject>.
At first, we did this work manually. Later, we made custom Rector rules that we kept private.
Today, we are open-sourcing these rules to help you with the same task.
Hey PHP folks! 👋 We're thrilled to share Jack, a new experimental CLI tool to help you lift your Composer dependencies one version at a time – safely and steadily. If you've ever dreaded the "oh no, our dependencies are years old" moment, Jack's here to make upgrades less painful.
We've tested it internally for a couple months, published silently to pass 3500 downloads in 20 days, it's time to share it with the world.
It fits both legacy projects to reach higher faster, and modern projects not to fall behind.
Legacy projects can be upgraded in 2 directions - PHP-wise and infrastructure-wise. We can get a new Ubuntu 25 but still have to run old PHP.
The same goes for upgrading a legacy project. Locally, we use Ubuntu 25, but we work on a project that uses PHP 7.2.
By default, Ubuntu releases ship only one PHP version. Often we need one of the other ~10 versions. How do we run old or new PHP on the latest Ubuntu?
Nearly half of the projects we help upgrade have tried the upgrade before on their own. They've introduced the infamous "strangle pattern". It's a way to upgrade a project separating one part of the codebase from the rest at a time.
Those companies reach us because while the strangle pattern is great in theory, it rather strangles the project in practice. They're unable to move, they use now 2 frameworks instead of one and the team has to work with complexity squared.
Today we'd like to share why it's a terrible choice and what upgrade strategy to take instead.
In 7 Traits of Successful Upgrade Companies, we wrote about the behavior patterns of companies that make the impossible upgrade happen.
Today we look closely at an anti-pattern that repeats in every framework, in every custom solution, and even in testing tools. A lot of CLI tools that should help us write better code also suffer from it. Frameworks are trying to get rid of it, but most drag mountain piles of history BC promise and so never do.
In this post, we'll look at spotting this anti-pattern, think about better ways to achieve the same result, and share our strategy to get rid of it in our projects.
After 7 years since the first Rector commit, we have a new logo!
Today, we're proud to share a streamlined, smooth, and modernized version of the Rector logo. Similar to a project upgrade, the final version looks great, but the journey is the destination. Did you know it took 40 commits, 5 sketched paper pages, and 3 months to get there?
Today, I'd like to share the backstory with you.
Rector 2 is out! We've upgraded to PHPStan 2 and PHP-Parser 5. Based on testing on several huge legacy projects, Rector now runs 10-15 % faster.
We've also managed to fit in a couple of new features.
Packages that ship a lot of versions can have a lot of sets to apply. For, twig/twig has 6 sets in Rector, a couple for v1 and a couple for v2. What about v3? We must always check for our locally installed version and then keep rector.php up to date.
This could lead to errors as we run sets with new features from v3 that we don't have yet.
Today I want to talk about how I added an optimization that made Rector 20-30% faster!
Our business is built on open-source and with open-source software. We're using "free" PHP language, "free" PHP frameworks, and "free" packages. But those are not free to develop and maintain.
That's why we're joining Open Source Pledge. To put actual numbers on the table and commit to long-term support.
CodeIgniter was created in 2006 and was one of the first MVC PHP frameworks. Yet it never gained traction and got stuck.
Is your project running CodeIgniter, and do your developers want a change?
We receive a few client requests a year for CodeIgniter project upgrades, so we'll share a few tips on migrating it to Symfony/Laravel.
To this day, Rector provides over 535 rules spread in 4 repositories - core, PHPUnit, Symfony, and Doctrine. If you are looking for a rule that does a specific job, you'd have to go through 4 markdown files, find it on a page, and hope to get it right. That is frustrating, especially when you look for a "constant," but rules have "const" in their name.
We heard your feedback and worked on a single place to search rules past couple of months. We're proud to share the final page.
When dealing with legacy, we first focus on safety by knowing the important types. Code must have reliable type declarations to be refactored safely.
Over time, we've been adding our custom rules to address PHPStan blind spots first. Today, we're proud to publish them in one solid package.
Rector and its extensions already consist of many rules for PHP and Framework upgrades, improving code quality and type coverage. However, you may have your own needs - that's when you need to write your own custom rules.
There is documentation for how to write custom rules, but the following tricks can help you more.
Carbon is an excellent library for working with dates and times in PHP. It's being used by Laravel as the default date-time library.
But it's not only syntax sugar wrapped around the DateTime class. It provides a reliable way to test your code that depends on exact dates and times.
For the past 10 years, we've been working with over 50 companies on legacy PHP upgrades. We've already written about our approach and technical process of upgrades.
We've met many companies we helped turn their projects from legacy projects that were hard to work with to code that is full of joy, safety, and smoothness.
Over time, we've noticed that those companies' traits often repeat and are shared with other similar companies. We'd like to share these observations so you can mimic them for your company and make your upgrade project successful.
Would you upgrade your Nokia 3310 to a newer Nokia or change it to an iPhone with USB-C? Would you upgrade your old Ford Fiesta to a newer Ford or change it to a Tesla Model 3? Would you upgrade your house's wooden windows for better wood or use plastic 3-layers?
If you use any PHP framework, it doesn't mean you have to stick with it for the end of your project life. The upgrade or change can be both valid options, depending on your project state, PHP community in your country or version.
Zend is the second most requested project upgrade in our client group and online forums. If you have no idea where to start, how should you approach the Zend upgrade? What criteria should you consider? What are the alternatives?
Phalcon is a PHP framework that is written in C and is known for its speed. It was created in 2012 and it has own PHP-like language - Zephir. After CakePHP, this is the most requested framework to handle. We though we'll share your options if you want to upgrade your project running on Phalcon.
The stable Rector version is here. It was about time and we've done all planned changes by the end of 2023.
What is the most requested project we get from our clients? PHP upgrade, Symfony upgrade, framework switch... yes, these belong to the most common ones. But one of the requests is far beyond the most requested one. From CakePHP 2 to Symfony/Laravel.
We are thrilled to introduce the latest update to our book, along with long-awaited Rector 1.0 from February 2024. This release includes 2 new commands, brand new configuration with smart IDE autocomplete, brand new chapter and DX improvements to help you master code refactoring with ease.
We've worked on this release back and forth past 3 weeks and we're excited to share it with you.
Symfony configs in YAML are a pile of strings. No IDE autocomplete, no static analysis, and no @deprecated warning when an option is on its way out.
PHP configs give us a way in. Today, we'll show you how to get there without a single manual flip, and how to let Rector clean up the service definitions once you're there.
Rector is becoming a standard tool to automate PHP/package upgrades and code quality improvements. Last month, we crossed 60 000 downloads a day.
Past 2 months, we've also improved CPU and memory performance, making Rector a lighter version.
Yet, even fast and lightweight Rector can get stuck on simple config mistakes. We'll talk about the 5 most common ones and how to avoid them.
Rector no longer supports parent node lookup since version 0.17. To look up a specific node, we'll have to traverse from parent to child node instead.
Here is how we can achieve it.
Since the first Rector version, we used Symfony container to inject the services. It worked very well. The new PHP 8.0 came with attributes, and Symfony started to use them extensively.
We're downgrading Rector down to PHP 7.2, and this forced us to lock with unmaintained Symfony 6.1. We needed a hacky patch to make Rector config work...
This made us think: Is there a better way?
The developer experience is a priority when it comes to contributing tools, fixing bugs, and delivering merge requests fast. Rector 0.17 tests could eat up enough memory to crash on 16 GB RAM and took 3-5 minutes to complete.
This was painful and lead developers to skip test run locally and wait for the CI.
We wanted fast feedback, so everyone can enjoy fast feedback. We worked hard past 2 months to make our tests faster than a sip of a good coffee.
Before going to Rector 1.0, we need to refocus solely on PHP files. In this release, we're leaving a not-so-well-known feature that could handle some changes in configs and templates.
Rector has matured enough to start thinking about a stable version. This year, we want to release Rector 1.0. Before that happens, we want to ensure it is available to variety of users and the known splinters are removed.
One of them is to lower memory consumption - so Rector runs faster on any laptop anywhere in the world.
Correctness has more priority than speed. Since version 0.14.x, Rector has better scope refresh handling for multiple rules and handle more crash that happen on 0.13.x. On 0.15.x, Rector give optimization a chance to raise.
We're working hard to make the developer experience as smooth as possible. The fewer steps to your first run and full automation with Rector, the better.
In February, we added improvement for the first run. Now we add a new command to set up Rector in your CI to work for you.
PHPUnit 10 was released today. Do you fancy an early upgrade?
We'll show you how to do it with Rector and what other changes you have to handle. Ready?
Rector is helping with PHP upgrades and framework migrations. It also helps to rise the type coverage of your project.
Rector is built for and on the whole PHP community right from the start. But there are also somewhat "local" PHP communities around a specific framework. Each framework has specific needs that are best known to the community member.
That's why we entirely moved Typo3 and Nette Rector extensions to their communities. They know best how to handle rules for the framework.
We want to encourage the community to build their own packages on top of Rector core, so we also decided to move Laravel and CakePHP to the community.
We added support for annotation to attribute upgrade in Rector 0.12. Since then, PHP 8.1 has come with nested attributes. Rector supports these, e.g., for Symfony validator.
Yet, Doctrine already took a path of its own and unwrapped nested annotations to flat attributes to be exclusively open to PHP 8.0 users.
Next Rector comes with support for these too.
In August, we've been working hard to make Rector lighter. As use the count of users grows, developers use Rector on more legacy projects than before, and user experience b becomes a higher priority. The easy use, installation, and writing of custom rules is the key to the success of any project upgrade.
We cut down dependencies that it really does not need, removed a few niche features, and made the test case simpler.
You can benefit from this change if you're using Rector to write your custom rules and test those. What has changed and how?
I'll be speaking there in October at Paris on Forum PHP 2022 about Rector. I was asked for a simple interview to warmup the talk topic. The 3 questions - each looking at different angle, but going deep.
Contrary to mostly technical content on this blog, this post will give you behind the scenes insights on wider Rector vision.
When Rector started, it was a small project that handled upgrading a vast amount of PHP packages.
As the project grew and expanded, more local PHP communities joined with community packages that build custom rules on top Rector core.
It makes sense to separate these projects from the core and let the community handle them. Who does a better job at growing the vegetable than farmers themselves, right?
Code filled with docblocks param, var, and return types is a gold mine. Not in the meaning of valuable resource, but rather as exploding metal covered with a thin piece of gold, so we grab it without thinking. While these docblocks give us much information about the code, they might be nothing more than a wish, dream, or promise.
Have you ever blindly trusted docblocks and switched them to type declarations? Then you know the explosive regression this move brings.
Yet, how can we turn to add strict types to our code without fear of breaking it?
Rector is using PHPStan to detect types of various expressions. That means every node has access to PHPStan Scope, e.g., with types or class reflection. From code $value = 1; we know, that $value is type of int. But what if we change the node?
Rector is using Symfony container configuration to build the service model. While it brings automated autowiring, array autowiring, and native container features, the syntax to configure Rector has been complex and talkative.
The hard question is: how can we refactor from Symfony, have a custom Rector config class but keep using its features?
Today, I'm very excited to talk about the full story of our successful automated framework migration how Rector saved our product by refactoring our 400k+lines PHP web application!
Migrating legacy PHP applications is always a challenging task.
Today I'll introduce one strategy to make these migrations easier by using the power of the Rector. With this strategy, we successfully migrated a legacy PHP application over a period of one year, without stopping developing new features!
Configurable rules are the most powerful building stone for instant upgrade sets. Do you want to upgrade from Symfony 5 to 6? You'll primarily deal with renamed classes, renamed methods, new default arguments in method, or renamed class constants.
In the end, we have to configure around 10 rules to get the most job done. That's why we focused on developer experience and added a new configure() method in Rector 0.12.
The most used feature of Rector is to keep you updated with the latest PHP. PHP 8.1 was released almost a month ago, so many projects started to use Rector to upgrade to PHP 8.1. There is a new import in your rector.php with every new version.
Soon, your config is cluttered with a list of imports. How can we reduce this complexity to a single line? How can we handle your-favorite-framework upgrade in second?
Imagine hypothetical situation: new major Symfony and Laravel are released in December 2021. We'll already have PHP 8.1 out by that time. There have been a lot of positive vibes about new PHP versions in the last year, so let's say the frameworks will take a brave leap forward.
Symfony 6 and Laravel 9 will require PHP 8.1 as a minimal version in their composer.json.
How would you react to such a move? What if you could keep using your current PHP version while using Symfony 6 or Laravel 9?
We used @annotations in PHP 7.4 and below. Now we can use native #[attributes] in PHP 8. They have better support in PHPStan and Rector, thanks to their native language nature.
The Internet is full of questions "How can I use PHP 8 attributes instead of annotations in Doctrine?" or "Converting Annotations to Attributes".
Do you want to know the answer? Rector has a simple solution for you.
Some developers see Docker as default-to-use for Rector. Yet they struggle to run it successfully with fundamental changes like renaming class from underscore to namespace.
It's very frustrating for them, and they often end-up up deleting the tool with a bad feeling inside.
This cannot happen.
Last week we introduced Prefixed Rector by Default. The main advantage of this release is that you have a single package to install, with no conflicts and minimal PHP version.
Rector can be used on PHP 7.1+ platforms. Yet, we bumped a minimal version to PHP 8. Is that a BC break?
Today we're introducing a big step up in making Rector developer experience more smooth and intuitive. It will also ease development for Rector contributors. We won't have to think about dependencies in composer.json anymore.
Are these goals in contradiction? Quite the contrary.
Until recently, we used doctrine/annotations to parse class annotations that you know @ORM\Entity or @Route. Last 2 weeks, we rewrote this parser from scratch to our custom solution to improve spaces, constants and use static reflection.
During refactoring, the parser got reduced from 6700 lines to just 2700.
What we changed, why, and how can we benefit from a static reflection in annotations?
Today we're releasing Rector that brings the most significant improvement for usability yet. It took 2 months of hard work of our team and Rector community, but we're here.
What is new, and what makes your life easier?
Properly configured class autoloading have been a big requirement problem for many projects that do not use flawless PSR-4 autoload. It took two months of hard work of our team and Rector community, but we're here.
What is a static reflection, and how can you use it?
When it comes to completing type declaration from docblocks, we rely on trust and hopes in commented code. One way out of is dynamic analysis that works with real data that enter the method. But we have to log it, wait for it, and update our codebase based on logged data.
Is there a faster, simpler solution we can just plugin?
Do your Doctrine repositories extend a parent Symfony service? Do you use magic methods of parent Doctrine\ORM\EntityRepository?
Would you like switch to decoupled service design and use composition over inheritance?
If you're looking for "why", read How to use Repository with Doctrine as Service in Symfony.
If you know why and look for "how", keep reading this post.
Action Injections are much fun a first, but they turn your fresh project into legacy code very fast. With PHP 8 and promoted properties, there is no reason to pollute method arguments with services.
How to refactor out of the legacy back to constructor injection today?
Nette 3.1 was released almost a month ago. Packages using it had enough time to give support to small BC breaks and now it's ready to run on your project. Let's look at what has changed and how to upgrade today.
Last December, we started to use PHP 8.0 and Symfony 5.2. This exact combination opens many cool tricks we could never use before.
One of those tricks is using constants for route name in #[Route] attribute.
2020 was a big year for us. We had 4 large projects with only tests in CI. Adding ECS with 10 basic sets, PHPStan to level 8, PSR-4 to all classes. In the end, we successfully upgraded Nette 2.2 to 3.0, Symfony 2.7 to 3.4 and Laravel 5.5 to 5.8, to Symfony, and from PHP 5.6 to 7.4. Oh, we also migrated Phalcon to Symfony.
The secret of a successful migration is speed and fast merges. During these 8 months of intense work, sometimes even 200 hours a month, we failed a lot. We try to learn from our mistakes.
Today we want to share what we've learned from our clients in 2020.
More than 45 days have passed since the last Rector release. Since then, we pushed 292 commits in over 220 pull-requests. No wonder the most common question in issues was "when will the next Rector be released?".
Today, we're proud to finally tag and launch Rector 0.9!
Last month, we merged a total 188 pull-requests to Rector code. We could not afford such a high rate without having a robust CI setup we trust. Dozens of custom PHPStan rules help us on every commit.
Today we'll share with you 4 of them. You can use them in your code to save time and let robots work for you.
Laravel facades are known as static service locators. The idea is get any service anywhere, which comes very handy for project bootstrapping.
Around Laravel 6, released in March 2019, the Laravel community started moving away from facades towards clearly typed constructor injection.
Today we'll take 1st step to make it happen.
PHP 8 was released more than 2 weeks ago. Do you want to know what is new? Check colorful post series about PHP 8 news by Brent.
Do you want to upgrade your project today? Continue reading...
Rector can upgrade legacy code to a modern one. But in reality, that's ~5 % of usage. On the other hand, more than 300 projects use Rector daily, on every commit in Github Actions, Travis, and Gitlab CI.
And that's only open-source projects. The number of private projects using Rector would be much higher.
Rector uses PHP Symfony configs for many good reasons.
One of them is the possibility to have control over complex configurations with value objects. Would you like such features in your configs too? Unfortunately, Symfony does not support it out of the box.
What can we do about it?
In July 2020, we started to move from the configuration in YAML to one defined in PHP. The YAML configuration is now deleted in Rector core and won't be supported next 0.8 release.
What benefits PHP brings, how the rule configuration changes, and how to prepare yourself?
PHPExcel is a package for working with Excel files in PHP. The last version was released in 2015, and it was deprecated in 2017. Still, it has over 27 000 daily downloads - that's tons of legacy code.
Do you use it too? Do you want to switch to PHPSpreadsheet? You can do it today.
In April 2019 we upgraded Glami's big codebase to follow PSR-4.
It was a great success! In this part, we will go through what PSR-4 is and it's benefits.
Rector is a composer package. If you install it, it has to meet install requirements conditions.
But how can you upgrade your Symfony 2.8, when Rector needs at least Symfony 4.4?