PHP 7.2 released November 30th. The headline isn’t a language feature, it’s a removal. mcrypt is gone.

This is good news, even if it doesn’t feel that way when you’re the one migrating.

:lock: The mcrypt problem

mcrypt has been unmaintained since 2007. More than a decade of stagnation in a cryptography library. It was deprecated in 7.1, and 7.2 removes it entirely. The replacement is sodium, now bundled as a core extension.

Sodium is the PHP binding for libsodium, a modern cryptographic library built around safe defaults. Where mcrypt required you to pick the right cipher, mode, and padding (and get it wrong silently), sodium’s API makes dangerous choices structurally hard. sodium_crypto_secretbox() for symmetric encryption, sodium_crypto_box() for asymmetric, sodium_crypto_sign() for signatures. The names tell you what you’re doing.

If you have mcrypt code in production, the migration is unavoidable. Worth doing carefully too: cryptography code that “still works” can be silently broken in ways you won’t notice until it’s too late.

:package: The object type hint

7.2 adds object as a parameter and return type:

function serialize(object $data): string {
    // accepts any object
}

It’s broad — any object satisfies it — but it’s better than no type at all when you genuinely don’t care about the specific class. Complements the existing array, callable, and class-specific hints.

:key: Argon2 in password_hash

$hash = password_hash($password, PASSWORD_ARGON2I);

PASSWORD_BCRYPT was the default and still reasonable, but Argon2 won the Password Hashing Competition for a reason: it’s memory-hard, which makes GPU-based cracking significantly more expensive. Worth switching for new apps.

7.2 is more of a security release than a language one. Remove mcrypt, add sodium, and you’ve moved the platform to a place where you can actually trust it with sensitive data. The language features are incremental. The infrastructure shift is not.

Parameter types you can now drop on purpose

7.2 formalizes something that was previously just a smell: when you implement an interface or override a method, you can now omit the parameter type entirely. This is valid contravariance under the Liskov substitution principle.

interface Serializable {
    public function serialize(array $data): string;
}

class JsonSerializer implements Serializable {
    public function serialize($data): string { // no type — accepts more, still valid
        return json_encode($data);
    }
}

It reads oddly at first. But it’s logically sound: a method that accepts anything is strictly more permissive than one that only accepts arrays. The type system agrees, even if your code reviewer raises an eyebrow.

Abstract methods that evolve

When an abstract class extends another abstract class, it can now override the abstract method with a different signature. The constraint is directional: parameters can be widened (contravariant), return types can be narrowed (covariant).

abstract class BaseProcessor {
    abstract function process(string $input);
}

abstract class TypedProcessor extends BaseProcessor {
    abstract function process($input): int; // parameter widened, return type added
}

This was rejected before 7.2. It unlocks intermediate abstractions without forcing every leaf class to repeat the same signature.

Trailing commas in grouped use statements

Small, but I notice its absence when it’s missing. Grouped namespace imports can now have a trailing comma on the last item:

use App\Services\{
    UserService,
    OrderService,
    NotificationService, // comma here — finally
};

This means you can reorder or add lines without touching the previous last entry. Git diffs get cleaner, merge conflicts get rarer.

count() grew a conscience

Before 7.2, count(null) returned 0. Silently. No warning. That’s exactly the kind of thing that buries a bug for months. Now it emits E_WARNING when you pass something that isn’t an array or a Countable object.

count(null);  // Warning: count(): Parameter must be an array or an object that implements Countable
count(42);    // same
count("hi");  // same

The behavior didn’t change for valid inputs. Only the silence was broken. That’s the correct direction.

spl_object_id() — the thing you were emulating with SplObjectStorage

If you’ve ever built a map keyed on object identity, you’ve written something like this:

$storage = new SplObjectStorage();
$storage[$obj] = true;

7.2 adds spl_object_id(), which returns a unique integer for the lifetime of an object. It’s the same internal handle SplObjectStorage uses, made directly accessible:

$id = spl_object_id($obj); // e.g. 42
$map[$id] = 'something';

The integer is reused after the object is destroyed, so don’t hold onto it past the object’s lifetime. Within a well-scoped context though, it’s a cheap identity key.

PDO: national character strings

When working with databases that distinguish between regular and national character string types (Oracle, SQL Server), 7.2 adds the flags you needed:

$stmt = $pdo->prepare("SELECT * FROM users WHERE name = ?");
$stmt->bindValue(1, 'Ångström', PDO::PARAM_STR | PDO::PARAM_STR_NATL);

Or set a connection-level default:

$pdo->setAttribute(PDO::ATTR_DEFAULT_STR_PARAM, PDO::PARAM_STR_NATL);

PDO::PARAM_STR_NATL signals that the string is a national character type (NCHAR/NVARCHAR). Obscure, yes. Essential if you’ve ever watched your Unicode data come out mangled because the driver had no idea about the difference.

GD got BMP support and clipping rectangles

Two things worth knowing. First, BMP files are now first-class citizens in the GD extension:

$image = imagecreatefrombmp('photo.bmp');
imagebmp($image, 'output.bmp');

Second, you can now define a clipping rectangle so that drawing operations only affect a portion of the image:

imagesetclip($image, 10, 10, 200, 150); // x1, y1, x2, y2
// everything drawn outside this rectangle is silently ignored

Neither feature reshapes how most apps work, but both replace “install an extra library” with “it’s just in core now.”

mb_chr() and mb_ord() — Unicode’s chr() and ord()

PHP has had chr() and ord() forever. They work on bytes. For Unicode code points, you were on your own. 7.2 adds the multibyte equivalents:

$char = mb_chr(0x1F600); // returns the 😀 emoji
$code = mb_ord('é');     // returns 233

And mb_scrub(), which strips invalid byte sequences from a string rather than failing silently or throwing:

$clean = mb_scrub($untrustedInput, 'UTF-8');

Handy at any external boundary: API responses, file uploads, database reads from legacy systems.

Deprecations worth knowing before 7.4 arrives

Several things were soft-deprecated in 7.2 that will become errors in later versions. The ones most likely to bite:

__autoload() is deprecated. If you’re still registering a global autoload function instead of using spl_autoload_register(), fix it before it becomes a fatal.

create_function() is deprecated. It’s a wrapper around eval() and was always dangerous. Use a closure:

// before
$fn = create_function('$x', 'return $x * 2;');

// after
$fn = fn($x) => $x * 2;

each() is deprecated. The loop pattern it enabled is better written as foreach. There’s no loss here.

parse_str() without a second argument dumps parsed values into the local symbol table — a security issue that should never have been allowed. Always pass the output variable:

parse_str($queryString, $params); // correct

The (unset) cast is deprecated because it always returns null, which you can just write as null. Completely pointless syntax that should never have existed.