logo
podcast Podcast
get help Get Unstuck

How do strict types work if only enabled in some files?

One simple rule to remember

Joel Clermont
Joel Clermont
2024-11-21

I like using types in my PHP code, and our code standard used in all new projects includes declare(strict_types=1); at the top of every PHP file.

This directive makes sure that I don't accidentally call a function with the wrong type of argument or treat its return type as the wrong kind.

Prior to this directive, PHP would happily coerce strings to integers, integers to booleans, and so on, leading to subtle bugs that can be very tricky to catch.

Recently, I was working in a legacy client project with no types and minimal test coverage. In a situation like this, we are more cautious and only introduce declare(strict_types=1); to the top of new files we add, but not to existing files until they have reasonable test coverage.

But how does this work? If I have strict types enabled in one file, but not in another file, how is this enforced?

The key thing to remember is that the strict type check is only enforced on function calls made from within the file with strict types enabled.

It does not enforce strict types on other files calling into functions declared into this file, even though this file has strict types enabled.

A code example will help illustrate this:

// file1.php - strict types enabled
declare(strict_types=1);

function add(int $a, int $b): int {
    return $a + $b;
}

add('2', 3); // throws TypeError
multiply('2', 3); // throws TypeError

In our previous code example, notice how the multiply call throws a TypeError even though the file where that function is defined does not have strict types enabled.

Now let's look at the reverse scenario:

// file2.php - strict types not enabled
function multiply(int $a, int $b): int {
    return $a * $b;
}
add('2', 3); // no error, returns 5
multiply('2', 3); // no error, returns 6

Notice how the add call does not throw a TypeError even though the file where that function is defined has strict types enabled.

Why did PHP design it this way? When you're only calling functions inside a single project, it seems like you'd want it enforced everywhere.

But think about an older project without strict types. You install a Composer package that internally uses strict types. If strict types were enforced everywhere, this would break your project.

I understand the tradeoffs PHP made in the design. It can be a little confusing or surprising at times, but it makes sense when you take the long history of type coercion within PHP into consideration.

Here to help,

Joel

P.S. Adding types or tests to a big legacy project can seem like a huge ordeal. You may not even know where to start. We've done this many times and can help you get started.

Toss a coin in the jar if you found this helpful.
Want a tip like this in your inbox every weekday? Sign up below 👇🏼
email
No spam. Only real-world advice you can use.