PHP at Scale #15
Welcome to the fifteenth edition of PHP at Scale.
I am diving deep into the principles, best practices and practical lessons learned from scaling PHP projects — not only performance-wise but also code quality and maintainability.
Cloud downtime bingo
What a crazy month November has been. One month, and we had the AWS outage first that took down a big chunk of the Internet, followed by an even bigger [yet shorter] downtime of Cloudflare on November 18th. It was a bit funny to suddenly notice how many things stop working when Cloudflare is down. They sit in front of 20% of the Internet. I switched through at least a couple of tasks, and each one had to be postponed due to that issue.
I don’t think we should call now cloud bad, and revert from it. In some cases, it makes perfect sense, but from my experience, both AWS and Cloudflare are doing a great job, especially for younger projects. I don’t mean you need a Kubernetes cluster for an MVP; I just mean once you get traction, it’s nice to be able to spin up new servers within minutes.
The best way to learn is to learn from the failures of others ;) So read the summaries, and also brainstorm how your company could act in similar cases. Not only the issues that the summaries directly describe, but also if a provider goes down, and what you can do about it. How can you restore your services? Do you just wait for AWS to get back online, or is there a way to switch to a different region or cloud provider?
—
PHP Foundation seeking a new Executive Director
The PHP Foundation is seeking a New Executive Director. I am really keen to learn who the new ED will be, and look forward to his work. If you think you are a good fit, there is still time to apply!
Getting Business Support for Refactoring - How Do You Do It?
After my last newsletter release, I was curious how other people handle similar cases in their projects. It was interesting to see how many opinions there are, and especially how often the way is to do it quietly, without telling the business. I have to be honest, I am surprised a lot, but I guess I had luck with the business owners I worked with, as I could have been honest with them. I agree we need to act as professionals, and should lead to code quality improvement even if the business owners do not understand that need, but we need to make sure we understand why they reject it. Maybe there is a reason?
—
It’s upgrade time
Another big topic in November was (for me) the release of PHP 8.5 and Symfony 7.4/8.0.
The Pipe Operator – Cool Syntax or Production Tool?
It lets you chain function calls left-to-right instead of nesting them:
// Old way
$result = strtoupper(trim(str_replace(’_’, ‘ ‘, $input)));
// New way with pipes
$result = $input
|> str_replace(’_’, ‘ ‘, ...)
|> trim(...)
|> strtoupper(...);The PHP Foundation’s deep dive explains more intriguing scenarios where it might come in useful. You can find even more examples in Freek Van der Herten’s examples.
What is nice is that the PHP Compiler can optimize pipes better than nested function calls. No temporary variables, better memory usage - in some cases, that might be important.
URI Extension – Finally Standards-Compliant URL Handling
PHP 8.5 fixes this with a new URI extension that implements both RFC 3986 and WHATWG URL standards. The PHP Foundation article explains why this matters: they built it on battle-tested libraries.
Honestly, I usually rely on the abstraction provided by Symfony, and this won’t help me that much, but seeing a modern approach to URLs that provides an immutable object has to be appreciated. No more arrays!
use Uri\Rfc3986\Uri;
$uri = new Uri(’HTTPS://example.com:443/path’);
$normalized = $uri->withPort(null); // Removes default portMore examples here.
If you are building a SaaS product, this might help with handling the multi-tenant routing.
Clone With – The Feature That Makes Readonly Classes Usable
final readonly class User {
public function __construct(
public string $email,
public string $name,
public bool $active
) {}
}
$updated = clone($user, [’email’ => ‘new@example.com’]);Brent’s writeup captures this well – the syntax is clean, and the use case is obvious. His earlier article on readonly cloning shows the journey to get here and why the 8.3 changes weren’t enough.
That’s not all, you can find more here, or in the above-mentioned Brent’s article.
Symfony 8.0 – The Performance Release You’ve Been Waiting For
I think we sometimes tend not to consider the small improvements that add up. Some time ago, we bumped one of our projects from PHP 8.1 to 8.3, and Symfony 5.4 to 6.4. The result was a 35% performance improvement, with barely any work. What is even more interesting - it directly translates into better conversion and higher revenue!
Symfony 8.0 leverages PHP 8.4’s lazy objects feature, and the results are impressive. The fsck.sh analysis claims up to 70% cache performance improvements – I haven’t run a benchmark yet, but any improvement is more than welcome!
The big change: no more code generation for proxies. PHP 8.4’s native lazy objects mean Doctrine and Symfony can create proxy objects at runtime with minimal overhead. Benjamin Eberlei’s deep dive explains the technical details, but here’s what matters in production:
Our Doctrine entities have 50+ lazy-loaded relationships. Before, the proxy generation added complexity to deployments and cache warming. Now it’s just... gone. The proxies are created on demand, initialization is faster, and memory usage is better.
Another win: UUID v7 is now the default in Symfony 7.4/8.0. This matters because UUID v4 (random) causes index fragmentation in databases. UUID v7 is time-based with better sortability, which means better database performance over time.
I’ve seen a couple of projects struggle with their UUID implementation due to the index fragmentation issues. In one of our projects, we actually had to migrate away to Snowflake (UUID v7 was not available back then).
I really look forward to battle-testing the changes on our projects!
Smaller changes
This is obviously not everything. PHP has new array_first and array_last functions, stack traces for fatal errors, a #[\NoDiscard] attribute, or an .ini diff feature in CLI.
Symfony also comes with some smaller upgrades, form flow, native FrankenPHP support, and more.
Although not as meaningful as the previously described changes, they might become handy in many situations. I can remind myself of a situation in the past, where each of the changes would be useful.
What is your favourite change/improvement?
Why is this newsletter for me?
If you are passionate about well-crafted software products and despise poor software design, this newsletter is for you! With a focus on mature PHP usage, best practices, and effective tools, you'll gain valuable insights and techniques to enhance your PHP projects and keep your skills up to date.
I hope this edition of PHP at Scale is informative and inspiring. I aim to provide the tools and knowledge you need to excel in your PHP development journey. As always, I welcome your feedback and suggestions for future topics. Stay tuned for more insights, tips, and best practices in our upcoming issues.
May thy software be mature!



