RSSAmplifier

Blog

Read about upgrades, static analysis, GPT, Symfony and Laravel

tomasvotruba.comRSS feed ↗281 posts

Latest posts

How I use Agents to Extract Hidden Feedback from Github and Improved Rector

I was randomly drifting through Github trending PHP repositories, and checking how popular projects use `rector.php`. A lot of them use it extensively, with [prepared sets](https://getrector.com/documentation/set-lists) and [even levels](https://getrector.com/documentation/levels). What I didn't like to see is the *skip section*. It's like a baseline in PHPStan, but I take it really seriously. In…

7 tips to Improve your ECS Config

Easy Coding Standard got a [new release last week](/blog/ecs-13-2-lighter-smarter-more-fun-for-large-legacy-projects) with a couple of cool features and new dead docblock rules. But the tool is only as powerful as its master. With agents, even more so. I've noticed people use ECS in an overly complex way, which is not needed... and definitely not fun. If it's longer than 15 lines, you're probably…

ECS 13.2: Lighter, Smarter and More Fun for Large/Legacy Projects

Easy Coding Standard focuses on easy setup, easy run, and easy use. Last year we [simplified the config](/blog/zen-config-in-ecs) to a single fluent line. Today we ship **ECS 13.2** - a release that makes ECS lighter to install, smarter about docblocks, and more fun to integreate to a large project, step by step. Let's look at what's new.

Find the One PHPStan Rule You Need, Beyond the Core

PHPStan core gets you to level 10 with solid type checks. But some gems, the rules that catch *your* anti-patterns, live scattered across a dozen community packages most people never hear about. I got tired of hunting through GitHub READMEs to find "is there a rule for this?", so I built a single searchable page that indexes 11 custom PHPStan rules packages.

Introducing CTOR: Prefer Constructor over Always-Called Setters

We've all seen it: `new Human()` followed by three `setName()`, `setAge()`, `setEmail()` calls that are always there. Not optional - just pretending. These are constructor arguments in a fluent disguise, and they leave your objects half-valid every time someone forgets one. So I made a small PHPStan extension to find them.

Alice, Nelmio, Hautelook, Faker - How to Upgrade Doctrine Fixtures - Part 3

In previous parts ([part 1](/blog/alice-nelmio-hautelook-faker-and-how-to-upgrade-doctrine-fixtures-part-1) and [part 2](/blog/alice-nelmio-hautelook-faker-and-how-to-upgrade-doctrine-fixtures-part-2)) we looked at a situation where we are stuck with Alice, Nelmio, Hautelook, Faker legacy mess and need a way out to modern maintainable Doctrine. This is the final part. **The upgrade is finished and…

5 Books I Enjoyed Reading in 2025

Keeping the tradition from [previous](/blog/5-books-i-enjoyed-reading-in-2024) [years](/blog/5-books-i-enyjoed-reading-in-2023), here is my short list of 5 books I really enjoyed reading in 2025.

The Patch for Laravel Container

[I switched this website from Symfony to Laravel](/blog/why-I-migrated-this-website-from-symfony-to-laravel) 2,5 years ago, and I [love Laravel Container](/blog/what-i-prefer-about-laravel-dependency-injection-over-symfony) ever since. Symfony and Laravel containers are very similar - read this [compare post](/blog/experiment-how-i-replaced-symfony-di-with-laravel-container-in-ecs) if you know one…

How we Maintain Dozens of Symfony Workflows with Peace

There are dozens of posts and talks about how Symfony Workflows work and what they are for. To give you the simplest example, if you have a post - it can be drafted, reviewed, or published. The workflow component makes sure the transitions between these states are valid. That's it. Yet, there is not a single post about how terrible the configuration is. On one hand, we have Symfony components,…

Upgrading Sensio Security Annotation: The Full Story

The `@Security` annotation, which originated in the Sensio extra bundle, goes a long way. The official upgrade docs have a few misleading pointers, that force you to use unnecessary verbose language. Fortunately, few hidden levels make code much less verbose and more readable. This post sums up upgrading the Sensio `@Security` annotation to Symfony `#[IsGranted()]` attribute in one place.

Create Weird Fun PHPStan Rules like Nobody's Watching

There are 2 ways to use PHPStan. You can use native levels, and official extensions and raise the level from 0 to 8. This is a good start, but it often requires enormous work and brings must-have value. There is also a 2nd way: I wanted PHPStan **to be more fun and more tailored to the unique projects I work with**. That's why I made…

Why AST beats GPTs - featuring php-parser, ChatGPT 4.5 and Grok 3

As I'm manually writing this article, GPTs are on the hype train now. In this post, we'll use freshly released ChatGPT 4.5 and Grok 3 and see, if they know the AST of PHP well enough to be used on **a large PHP project**. Understanding AST takes longer than writing an English sentence in a chat. But once you see the abstract syntax tree in a code you're reading, it cannot be unseen. Today I'll try…

Custom PHPStan Rules to Improve Every Symfony project

Using PHPStan is not just about getting to level 8 with less than 100 ignored cases. Yes, there are also [official extensions](https://packagist.org/?query=phpstan%2Fphpstan-) that improve the type support of Symfony, Doctrine, and Laravel projects. But more rules are needed to get our PHP project into a future-proof state. **It takes less effort than getting to level 5 and we can use them since…

Cost-effective Container Smoke Tests every Symfony Project must have

Today, I'll share a trademark secret that allows us to move fast and make huge changes without fear. When we're starting a new [Symfony 2.8-7.2 upgrade project](/blog/off-the-beaten-path-to-upgrade-symfony-28-to-72), we cover it with a couple of tests first. These tests are not units, but smoke tests - with a couple of lines they cover a huge portion of the Symfony framework layer we use. With a…

5 Books I enjoyed Reading in 2024

Another year is gone, and it's [tradition](/blog/5-books-i-enyjoed-reading-in-2023) to pick 5 books I enjoyed reading in 2024.

Alice, Nelmio, Hautelook, Faker - How to upgrade Doctrine Fixtures - Part 2

In the first part, [we've kicked off the plan](/blog/alice-nelmio-hautelook-faker-and-how-to-upgrade-doctrine-fixtures-part-1) to upgrade all these packages to their latest version, like a blind map into unknown territory. Since then, we've put in a couple of months of hard work and climbed the terrain. Today, we look at the practical steps we've taken and the new challenges we discovered [after…

Symfony @required - Avoid the Temptation and Use it Right

Symfony 3 introduced a [`@required` annotation](https://symfony.com/doc/3.x/service_container/calls.html) (now also an attribute) that allows injecting more services via the setter method apart constructor. At the time, it was good. The goal was to solve circular dependencies: when A needs B, B needs C, and C needs A. But more often than not, I see PHP projects where it got completely out of hand.…

Unused Definitions with Behat Static Analysis

Recently, I've been working on projects with Behat tests. There are hundreds of definitions that can be used in feature file scenarios. I accidentally noticed that one of the definitions is not used at all and could be removed. This would result in less code to maintain, less code to read, and less code to upgrade. But I thought, "That's weird." **Why did not Behat report this definition** in our…

Why Final Classes make Rector and PHPStan more powerful

Final classes bring much more value than `extends` it lacks. It teaches composition over inheritance, makes upgrades easier, and [even mocking](/blog/2019/03/28/how-to-mock-final-classes-in-phpunit) is fine. If you're lazy like me, you can [automate the `final` keyword addition](/blog/finalize-classes-automated-and-safe) to your code - quickly, safely, and check it the CI!

Off the Beaten Path to Upgrade Symfony 2.8 to 7.2

There are two types of upgrades. One follows only `UPGRADE.md` files on every release, replacing what has been removed with new alternatives. It works, and we could say that the codebase will be "up-to-date." The other upgrade doesn't stop at the required minimum but **makes use of all modern features the framework provides**. It will be faster, easier to understand, and easier to upgrade to the…

How to flip Doctrine ODM repositories to Services

While working with legacy projects, I often encountered this anti-pattern of misusing repositories. Instead of easy-to-inject service, projects are locked into a service locator. This makes code hard to upgrade and locks your project heavily to the Doctrine ODM packages. And there are plenty of them. Each extra package bites off its share of upgrade costs. Today, we look at how to refactor the ODM…

Alice, Nelmio, Hautelook, Faker - How to upgrade Doctrine Fixtures - Part 1

Upgrading Doctrine Fixtures can be challenging work. It requires the coordination of four different package groups on Github. One cannot be upgraded without the other. We first prepare for the jump, then take the leap with confidence. I'm writing this post as I handle the upgrade, so my thoughts are fresh, and the process is ongoing. It may evolve based on new information. Let's see where the…

5 Ways to Extract Value from Overmocked Tests

The legacy projects we work with are often flooded with mocks. I already wrote [How to Remove Dead Mock Calls from PHPUnit Tests](/blog/how-to-remove-dead-mock-calls-from-phpunit-tests), which focuses on dealing with PHPUnit bloated syntax. Today, we look at the next wave of improvements that make tests more valuable, more accessible to upgrade and read, and even avoid false types.

How to Upgrade deprecated PHPUnit withConsecutive()

The `withConsecutive()` method [was deprecated in PHPUnit 9](https://github.com/sebastianbergmann/phpunit/issues/4255#issuecomment-636422439) and removed in PHPUnit 10. It sparked many [questions](https://stackoverflow.com/questions/75389000/replace-phpunit-method-withconsecutive-abandoned-in-phpunit-10), [on…

PHP is the Best Choice for Long‑Term Business

Recently, I listened to Lex Friedman's [podcast with Pieter Levels](https://open.spotify.com/episode/6KBpL2XfR9VdojbKNpE7cX). Pieter talked about his technology stack for building startups: vanilla PHP, jQuery, and SQLite. Hype is exciting, but there is no better technology proof than long-term usage by sustainable business. The podcast inspired me to write my views about why PHP is the best…

Scope and Downgrade your PHP Tools for Everyone to Use

Yesterday, I came across a cool PHP tool. I wanted to try it, but the installation instructions were a bit tricky. The tool required a specific PHP version and a specific version of each dependency. It required Symfony 5.4+, but our project has Symfony 3.3. I was unable to use it. Many PHP tools suffer from the same issue, so I thought I'd share a way to do it better.

Awesome PHP Packages from Japan

Last month, I spent in Japan to travel and absorb the local culture. I've also pinged a few friends I knew from Rector and AST projects in the past. I was surprised by how productive the PHP community is around here, with tools I'd personally use and ideas that inspire me.

2 Tricks to get your Symfony configs lines to minimum

I believe that every Symfony app can fit service config under 5 lines. Configs are among the most underestimated parts of Symfony projects and deserve to be done right. Like a healthy tree trunk, which brings power to the branches and leaves, clear configs keep the design architecture clear and easy to grow.

How to reload code highlighter on Livewire 3 component update

I use Livewire 3 for interactive forms and fast clickable maps. Last week, I worked on a filter page for the Rector website, where you can use text input to search for core and community rules. I typed "add param" to the input and got the results that best matched the rules I could use. But the code highlight was gone; what now? Current proposed solutions in Laracast/Livewire forums are…

How to add visibility to 338 Class Constants in 25 seconds

In PHP, we have classes with methods inside them. Would making all your methods `public` be a good idea? No, because some of them should be used only by the class they're in and not anywhere else. What about class constants?

Cool features of Swiss Knife

When using a Swiss knife, we think of a tool with many practical abilities. They're useful for different situations we might experience in the wild. Opening a box of milk? Here is a knife. Cutting wood to start a fire? Here is a chainsaw. Are the letters on the paint bucket too tiny? Try this magnifying class. Now, we apply the same approach to PHP tooling.

Get Quick Error Count for each PHPStan level

When I come to a new project, I want to make a rough idea of what I'm dealing with in a few minutes. I usually check `composer.json` and [measure the lines](/blog/easy-and-quick-way-to-measure-lines-of-code-in-php). Then, I'd love to run PHPStan and get a rough idea of the current state. But are there baselines, custom extensions, global ignores, or no PHPStan at all?

How adding Type Declarations makes Your Code Dangerous

...and how to avoid it. [Type coverage](/blog/how-to-measure-your-type-coverage) is a way to gradually add type declarations to your PHP project—step by step, one by one. It's a PHPStan package that helps you maintain a specific minimal level from 0 % to 100 %. Once we reach high coverage of 80-90 %, we feel safer. But our code can actually be in worse, even dangerous, shape.

Master the Change

When we upgrade a new project to the best version possible, the latest PHP and framework versions, it's not only about changing syntax sugar to a more fancy one. It's about the vast focus shift in project management so far. It's a change to master. I want to share the basic rules we apply to make the "impossible" upgrades successful and steady.

Finalize Classes - Automated and Safe

Final classes have [many](https://ocramius.github.io/blog/when-to-declare-classes-final/) [great](https://tomasvotruba.com/blog/2019/01/24/how-to-kill-parents) [benefits](https://matthiasnoback.nl/2018/09/final-classes-by-default-why/) for future human readers of your code. They have even more benefits for static analysis and Rector rules. But what if we have a project with 1000+ classes and 10…

Rector Book 2024 Release with Brand new Chapter

I'm happy to introduce the latest update to our book - *Rector, the Power of Automated Refactoring*, along with Rector version 0.19.5 from this week. 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.

3 Signs Your Project is Becoming Legacy - Mock Rottening

In [the first post](/blog/3-signs-your-project-is-becoming-legacy-and-how-to-avoid-them), we looked at the long-term effects of our decisions. Turning a legacy project into a fresh one is a matter of the "just do it" approach. But there are 3 things we should take with care even if our project seems outside the legacy project category. The second of those is mocks.

Get Json output for PHPUnit 10

Early this year, I created a few custom Rector rules for our client. It modified the code based on the PHPUnit error result report. The only problem is that PHPUnit outputs a string. So, I had to parse it manually with regexes. Having a JSON output would make my life easier. I'm used to PHP tools that provide the JSON out of the box, but I could not find it in PHPUnit.

Zen Config in ECS 12

Easy Coding Standard focuses on easy run, setup, and use. From composer requirement through the automated setup to the config. The config was based on rather cumbersome Symfony closure service configs. But last year, I [switched the DI container to Laravel](/blog/experiment-how-i-replaced-symfony-di-with-laravel-container-in-ecs). Eventually, this opened the door to further innovation that I'll…

5 Books I enjoyed Reading in 2023

I've read over a dozen books in 2023. Here are 5 favorites that influenced me the most.

3 Signs Your Project is Becoming Legacy - Arrays Creep

In [the first post](/blog/3-signs-your-project-is-becoming-legacy-and-how-to-avoid-them), we looked at the long-term effects of our decisions. Turning a legacy project into a fresh one is a matter of the "just do it" approach. But there are 3 things we should take with care even if our project seems outside the legacy project category. First of those are arrays.

3 Signs Your Project is Becoming Legacy and How to Avoid Them

Taking care of a software project long-term is not about immediate action but routine and regular checks. We have to know we're heading in the right direction; otherwise, we might end up trapped in a legacy project that is hard to get out of. When clients contact our [Rector team](https://getrector.com/hire-team) for help with their project, **they often share a few common treats**. I wish they…

How to Autowire Multiple Instances of Same Type in Symfony and Laravel

Do you work with Symfony from 2 through 7? Then you know the main challenge in the upgrade path is to trim your YAML configs to a minimum. **Where we needed 300+ lines in configs, we are now good with 2**. How to get there fast and reliable? I'll show a trick we use in the PHP project to get there once and for all.

How to make your Tool Commands List Easy to Ready

Do you use Symfony-console based tools? If you're using Composer, Rector, PHPSpec, PHPStan, ECS or [Class Leak](https://github.com/TomasVotruba/class-leak), you probably know the long list of commands you can use. In most of the times, most of command you read are not part of the real package. Do we really need them?

From Symfony to Laravel - 5 Steps to Prepare your Symfony Project for Migration

Framework migration is a challenge few choose to take - yet in some cases, it makes sense for business, project health, and pure joy from coding. Once you know [the recipe](https://getrector.com/blog/how-to-migrate-legacy-php-applications-without-stopping-development-of-new-features), it's clear the switch [is…

From Symfony to Laravel - Can Laravel even Compiler Pass

Do you want to migrate your Symfony project to Laravel and not sure if it "handles it"? Switching containers is pretty [straightforward](/blog/experiment-how-i-replaced-symfony-di-with-laravel-container-in-ecs) for the most parts. But can Laravel handle advanced features such as compiler passes?

How to take Advantage of 3rd party Dependency Injection Container

When Nuno sent me a Pest plugin for type coverage that runs [TomasVotruba/type-coverage](https://github.com/TomasVotruba/type-coverage), I looked for the PHPStan container use. Why? Because the type-coverage package is PHPStan rules that easily plugin into PHPStan. But what if you want to use them in a tool that has a different container? I've found the solution the hard way - so it might be…

Easy and Quick way to Measure lines of Code in PHP

The famous [phploc](https://github.com/sebastianbergmann/phploc) package to measure project size was archived by Sebastian on Jan 10, 2023. I used this package to get feedback on [CLI apps vendor shrink](/blog/unleash-the-power-of-simplicity-php-cli-app-with-minimal-dependencies) and for [fast estimation of project size in Rector upgrades](https://getrector.com/hire-team). **That's why I needed a…

Removing Service from Laravel Container is not that Easy

Last month I successfully [switched the Symfony container for Laravel one](/blog/experiment-how-i-replaced-symfony-di-with-laravel-container-in-ecs) in Easy Coding Standard. The tiny container is a joy to work with - it consists of 2 files I can read and understand all its features. **I wanted to put this package into pressure test**, so I migrated the project I work on daily -…

How to Remove Transitional Dependencies You don't Need

In the last post [I shared a trick](/blog/unleash-the-power-of-simplicity-php-cli-app-with-minimal-dependencies) on how to **reduce CLI project /vendor size by 70 %**. Today we'll trim off a bit more with the no-so-known composer feature.