Skip to content

Instantly share code, notes, and snippets.

@hikari-no-yume
Last active August 29, 2015 14:12
Show Gist options
  • Save hikari-no-yume/50d9628468cc738f6095 to your computer and use it in GitHub Desktop.
Save hikari-no-yume/50d9628468cc738f6095 to your computer and use it in GitHub Desktop.
"use strict" per-file scalar type hint strictness

Whether or not scalar type hints should be strict or not is a divisive issue in PHP. Both non-strict hints which cast values and reject others, and strict hints which only accept exactly matching types, both have advantages and disadvantages, supporters and detractors.

To avoid the problems with choosing one or the other, some people have previously suggested allowing both with different syntaxes:

function foobar((int) $a); // non-strict
function bazqux(int $a);   // strict

However, this would mean that different APIs would be using different approaches, and some even mixing the two. So, in your code, you'd have to deal with untyped, non-strict scalar and strict scalar types. It'd be quite confusing. I don't think this would be a good compromise. Some people want strict hints, some people want non-strict hints. I doubt anyone really wants to handle both. With this approach, the API designer forces their personal preference onto the API user, whether they like it or not.

So, I'm suggesting an alternative approach: per-file strictness, similar to Hack's Partial and Decl modes. By default, function calls would be non-strict, but if you put use strict; at the top of a file, all function calls within it (including in functions and methods it defines) would be strict. For example:

<?php
namespace foobar;
use strict;

function foo(int $a) { /* ... */ }

foo(1.0); // errors: int expected, float given

Here, we have a strict-mode file that defines and calls function. It has an integer typehint, and we pass a float value. Because this file is in strict-mode, this is an error.

But let's say we have another file, which is non-strict. It would be able to call it just fine:

<?php
namespace foobar;

foo(1.0); // succeeds: int expected, convertable float given, value converted to int

This file doesn't use strict mode, and so it has the non-strict behaviour where some arguments can be converted. This applies even though the function was declared in a strict mode file. Any function calls made in that file (including within functions in that file) follow strict mode. But any function calls made in this file do not follow strict mode.

This would also worth the other way round. Say we have file A:

<?php
namespace foobar;

function foo(int $a) { /* ... */ }

foo(1.0); // succeeds: int expected, convertable float given, value converted to int

It defines and calls a function. It's not using strict mode, so it is able to pass a float. But file B uses strict mode, and so can't:

<?php
namespace foobar;
use strict;

foo(1.0); // errors: int expected, float given

With this approach, everyone gets what they want. There is one set of scalar hints. If you want the strict behaviour, you can use use strict; and all your code will have it, but code that calls your code is not forced to use the strict behaviour. If you want the non-strict behaviour, you can omit use strict; and your code will not have it, but code that calls your code can opt to have the strict behaviour.

It also means that existing libraries can add scalar type hints with minimal compatibility issues. Strict hints break existing code that may (albeit unintentionally) rely on PHP's weak typing. However, weak hints don't break compatibility here in most cases, and since they'd be the default, you could safely add hints to existing code.

As well as affecting userland scalar type hints, this should also affect internal/extension functions (those built-in to PHP or defined by extensions). Without use strict; they would have their existing, non-strict behaviour. With it, they would have strict hints and, in addition, would produce E_RECOVERABLE_ERROR on failure rather than raising an E_WARNING and returning NULL. This would make them consistent with userland hints.

Thoughts?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment