Skip to content
Fernando Valler

Modern PHP in 2026: what changed for those still working with PHP

A comparison between PHP of the past and PHP 8.5: typing, enums, match, property hooks, the pipe operator, and the culture shift in the ecosystem.

Modern PHP in 2026: what changed for those still working with PHP

When someone mentions PHP, a familiar image tends to come up: legacy applications, .php files mixed with HTML, untyped variables, and systems that grew for years without much concern for architecture.

That image is not entirely unfair. For a long time, PHP was indeed used that way.

But still associating PHP with that scenario in 2026 ignores a huge part of the evolution the language has gone through in recent years.

Since PHP 7, and especially since PHP 8, the language has gained features that significantly changed how we can write applications.

Today, working with modern PHP means working with stronger typing, more expressive objects, object-oriented features that didn’t exist before, better support for immutability, and syntax much closer to what we find in modern languages.

And this evolution didn’t happen only in PHP.

The ecosystem has matured too: Composer, modern frameworks, automated testing, static analysis, CI/CD, containers, APIs, and service-based architectures are part of the reality of many PHP projects today.

In this article, I want to compare a bit of that “PHP of the past” with the PHP we find today.

Context: PHP didn’t stop at PHP 5

For many years, especially during the PHP 5 era, it was very common to find code like this:

php
function calcularTotal($preco, $quantidade)
{
    return $preco * $quantidade;
}

There was nothing necessarily wrong with that code.

The problem appeared when the application grew.

Without explicit types, clear contracts, and more rigid structures, it was possible to pass practically anything to a function:

php
calcularTotal('100', '2');
calcularTotal([], null);
calcularTotal('banana', 'abc');

In small systems this could go unnoticed.

In large systems, however, that kind of flexibility could turn simple errors into hard-to-trace problems.

Starting with PHP 7, and mainly with PHP 8, the language began offering a series of features to make these contracts more explicit.

PHP 8.0, released in 2020, brought important features such as union types, named arguments, attributes, constructor property promotion, match, and the nullsafe operator.

Then came enums, readonly, readonly classes, more advanced types, property hooks, asymmetric visibility, and other improvements.

In 2026, the latest stable version is PHP 8.5, released on November 20, 2025. Among its features are the pipe operator, the new URI extension, clone() with property modifications, and the #[\NoDiscard] attribute.

On top of that, the official support cycle has become quite predictable: each version receives two years of active support plus two more years of critical security fixes. As of September 2026, PHP 8.2 is still receiving security support, while PHP 8.3, 8.4, and 8.5 remain supported for later periods.

In other words: modern PHP is not just an impression or a change in style. The language itself changed.

Before and now

1. Untyped variables x explicit typing

Before

Functions like this were common:

php
function cadastrarUsuario($nome, $idade, $email)
{
    // ...
}

The function’s contract had to be discovered by looking at the implementation, the documentation, or the code calling it.

Now

We can make that contract explicit:

php
function cadastrarUsuario(
    string $nome,
    int $idade,
    string $email
): Usuario {
    // ...
}

This may look like a small change, but it substantially affects code quality.

The IDE can help better, static analysis tools can detect problems, and PHP itself can reject incompatible values at runtime.

The current type system supports scalar types, union types, intersection types, never, mixed, true, false, null, and other more expressive combinations.

The main difference is that types are no longer just documentation.

They became part of the code’s contract.

2. Arrays for everything x objects that represent concepts

A very common pattern in legacy applications was:

php
$cliente = [
    'id' => 10,
    'nome' => 'Fernando',
    'email' => '[email protected]',
];

This works.

But an array doesn’t clearly say which information is required, which is optional, or what behavior that set of data has.

In modern PHP, we can represent these concepts better:

php
final readonly class ClienteDto
{
    public function __construct(
        public int $id,
        public string $nome,
        public string $email,
    ) {}
}

The use of immutable objects to represent data has become far more natural with the features introduced in recent PHP versions.

PHP 8.1 introduced readonly properties, which are particularly useful for DTOs and value objects, and PHP 8.2 introduced readonly classes.

The result is code that communicates its intent better.

3. Constants to represent states x Enums

Another traditional pattern was creating a class just to store constants:

php
class Status
{
    public const PENDENTE = 'pending';
    public const PAGO = 'paid';
    public const CANCELADO = 'cancelled';
}

And then:

php
function processarPedido(string $status)
{
    // ...
}

The problem is that any string could be sent in.

Today we can use an enum:

php
enum StatusPedido: string
{
    case Pendente = 'pending';
    case Pago = 'paid';
    case Cancelado = 'cancelled';
}

Now we can declare:

php
function processarPedido(StatusPedido $status): void
{
    // ...
}

PHP comes to know that closed set of possibilities.

Enums were introduced in PHP 8.1 and allow states previously represented by strings or constants to become real types in the language.

This is especially interesting in business systems.

Instead of:

php
if ($status === 'paid') {
    // ...
}

we can work with:

php
if ($status === StatusPedido::Pago) {
    // ...
}

The code starts to express the application’s domain.

4. switch full of break x match

Before:

php
switch ($status) {
    case 'pending':
        $label = 'Pending';
        break;

    case 'paid':
        $label = 'Paid';
        break;

    case 'cancelled':
        $label = 'Cancelled';
        break;
}

Now:

php
$label = match ($status) {
    'pending' => 'Pending',
    'paid' => 'Paid',
    'cancelled' => 'Cancelled',
};

Besides reducing code, match is an expression, uses strict comparison, and requires no break.

5. Verbose constructors x constructor property promotion

A classic example of old code:

php
class Produto
{
    private string $nome;
    private float $preco;

    public function __construct(
        string $nome,
        float $preco
    ) {
        $this->nome = $nome;
        $this->preco = $preco;
    }
}

In modern PHP:

php
class Produto
{
    public function __construct(
        private string $nome,
        private float $preco,
    ) {}
}

This feature was introduced in PHP 8 and greatly reduced the boilerplate needed for simple classes.

It’s a good example of how the evolution of a language doesn’t necessarily mean creating a new possibility.

Sometimes it simply removes repetitive code.

6. PHPDoc to make up for limitations x native attributes

For a long time, it was common to rely heavily on PHPDoc:

php
/**
 * @param User $user
 * @return Response
 */
public function salvar($user)
{
    // ...
}

PHP 8 introduced Attributes:

php
#[Route('/usuarios')]
public function listar(): Response
{
    // ...
}

Attributes now allow structured metadata within the language’s own syntax.

This also opened room for frameworks and tools to use native metadata without relying exclusively on special comments.

7. Getters and setters to control properties x property hooks

A traditional class might need something like this:

php
class Usuario
{
    private string $nome;

    public function getNome(): string
    {
        return $this->nome;
    }

    public function setNome(string $nome): void
    {
        $this->nome = trim($nome);
    }
}

PHP 8.4 brought property hooks, allowing behavior to be attached directly to the property.

For example:

php
class Usuario
{
    public string $nome {
        set => trim($value);
    }
}

This feature doesn’t mean getters and setters are gone.

It means PHP now has a more expressive tool for certain cases.

8. Uncontrolled public property x asymmetric visibility

Another interesting change is the ability to separate reading and writing of a property.

In certain situations, we want something that can be read externally but modified only internally.

Modern PHP lets you express this directly with asymmetric visibility:

php
class Pedido
{
    public private(set) string $status;
}

This feature arrived in PHP 8.4 together with property hooks.

It’s yet another sign that the language is moving toward more precise object models.

9. Nested calls x the pipe operator

PHP 8.5 also brought the pipe operator:

php
$result = $value
    |> trim(...)
    |> strtolower(...);

It was created to make chaining operations more linear and easier to follow.

Before, depending on the situation, we could end up with something harder to read:

php
$result = strtolower(
    trim($value)
);

The pipe operator doesn’t automatically make code better.

But it becomes yet another option for expressing certain transformations in a readable way.

10. URL handling with scattered functions x the URI API

Another novelty in PHP 8.5 is the new URI extension, which provides a dedicated API to parse, normalize, and manipulate URLs according to standards like RFC 3986 and the WHATWG URL spec.

This is important because it shows a bigger trend in the language:

instead of solving everything through strings, arrays, and legacy functions, modern PHP is also starting to offer more specific abstractions for certain problems.

11. PHP as a “scripting language” x PHP as a platform for applications

Perhaps this is the biggest change.

In the past, it was perfectly normal to think of PHP as:

text
PHP + HTML + database

Today, a PHP application may involve:

text
PHP
├── Composer
├── Framework
├── APIs
├── DTOs
├── Value Objects
├── Enums
├── Automated testing
├── Static analysis
├── CI/CD
├── Docker or containers
├── Queues
├── Cache
├── Observability
└── External services

PHP stopped being just the language used to render pages.

It can be used in APIs, admin systems, microservices, async processing, integrations, workers, and distributed applications.

That also changed how architecture is thought about.

Modern code got closer to other languages

One of the most interesting aspects of this evolution is noticing how concepts that once seemed strongly associated with languages like Java, C#, Kotlin, or TypeScript have come to exist more and more naturally in PHP.

Today we have:

php
enum Status
{
    case Ativo;
    case Inativo;
}

We have:

php
final readonly class Cliente
{
    public function __construct(
        public int $id,
        public string $nome,
    ) {}
}

We have types:

php
function calcular(int|float $valor): float
{
    return (float) $valor;
}

And we have objects that can represent business rules better.

PHP is still PHP.

But today’s PHP makes it possible to write far more structured applications.

Did legacy PHP become useless?

No.

That’s an important point.

A large part of the world’s existing infrastructure is still made up of old PHP applications.

There are systems on PHP 5, PHP 7, and older PHP 8 versions that keep working.

The problem is not necessarily the old code.

The problem is believing that we still need to write new code using the same techniques from ten or fifteen years ago.

Legacy maintenance requires pragmatism.

An old system may not allow a complete migration to enums, DTOs, readonly, static analysis, or a brand-new architecture.

But that doesn’t mean a new project has to repeat the same decisions.

The biggest leap may not be in the syntax

It’s tempting to sum modern PHP up to features like:

  • enum
  • readonly
  • match
  • property hooks
  • union types
  • constructor property promotion
  • the pipe operator

But I think the most important transformation lies somewhere else.

The expectation of how a PHP application should be built has changed.

Today it’s far more natural to find PHP code that follows:

text
typing
↓
objects
↓
contracts
↓
dependency injection
↓
tests
↓
static analysis
↓
automation
↓
continuous deployment

In other words, modern PHP is not just a language with new syntax.

It’s also a shift in development culture.

Is it still worth learning PHP in 2026?

For those already working with PHP, perhaps the most interesting question isn’t that one.

The question is:

How much of modern PHP am I actually using?

Because there’s a huge difference between:

php
<?php

$data = $_POST;

mysql_query(...);

and an application built with:

php
final readonly class ClienteDto
{
    public function __construct(
        public string $nome,
        public string $email,
    ) {}
}

integrated with a modern architecture, with tests, static analysis, Composer, CI/CD, and an up-to-date framework.

Both can be called “PHP”.

But the development experience is completely different.

Conclusion

PHP didn’t disappear.

It changed.

And perhaps that’s the main thing anyone arriving in this ecosystem now needs to understand.

The PHP many people know from old experiences doesn’t necessarily represent the PHP we find in modern projects.

Between PHP 5 and PHP 8.5 there has been an enormous evolution.

The language gained better types, enums, attributes, readonly, readonly classes, property promotion, match, property hooks, asymmetric visibility, and many other features.

But learning modern PHP doesn’t mean memorizing all of these features.

It means understanding when to use them.

Good PHP code in 2026 doesn’t need to use every new feature available.

It needs to use the right tools to make the code clearer, more predictable, more testable, and easier to maintain.

And perhaps that’s the greatest sign of the language’s maturity:

Modern PHP doesn’t just try to do more things. It allows us to do the same things with better contracts, less unnecessary code, and more clarity.

If you still work with PHP but stopped following the language a few years ago, you’ll probably find a language very different from the one you knew.

And for those who already develop with PHP professionally, this evolution is a great opportunity to revisit old practices and update the way applications are built.

References