This is an old revision of the document!

Request for Comments: Strict and weak parameter type checking

This RFC provides a proposal for auto-converting parameter type checking for function/method parameters and the disadvantages of introducing strict scalar type hinting to PHP.


Several people still have asked to expand array/object type hinting to cover other data types, which mostly ask for similar strict type checking (without any type juggling) as for arrays and objects, while also triggering an E_RECOVERABLE_ERROR for failed checks. However this means that the burden for explicit type casting is now on the user of the function/method. This RFC tries to address this issue.

Why is strict type checking problematic?

PHP's type system was designed from the ground up so that scalars auto-convert depending on the context. That feature became an inherent property of the language, and other than a couple of exceptions - the internal type of a scalar value is not exposed to end users. The most important exception is the === operator - however, this operator is used in very specific situations. While there are other exceptions (e.g. gettype()) - in the vast majority of scenarios in PHP, scalar types auto-convert to the necessary scalar depending on the context.

For that reason, developers - even seasoned ones - will feel very comfortable sending the string “123” to a function that semantically expects an integer. If they know how PHP works internally - they rely on the fact the function will auto-convert the type to an integer. If they don't (and many don't) - they don't even think about the fact that their “123” is a string. It's a meaningless implementation detail.

For these reasons - strict type checking is an alien concept to PHP. It goes against PHP's type system by making the implementation detail (zval.type) become much more of a front-stage actor.

In addition, strict type checking puts the burden of validating input on the callers of an API, instead of the API itself. Since typically functions are designed so that they're called numerous times - requiring the user to do necessary conversions on the input before calling the function is counterintuitive and inefficient. It makes much more sense, and it's also much more efficient - to move the conversions to be the responsibility of the called function instead. It's also more likely that the author of the function, the one choosing to use scalar type hints in the first place - would be more knowledgeable about PHP's types than those using his API.

Proponents of strict type hinting often argue that input coming from end users (forms) should be filtered and sanitized anyway, and that this makes for a great opportunity to do necessary type conversions. While that may be true, it covers a small subset of type checking scenarios. For example, it doesn't cover input coming from 'trusted' sources like a database or files. It also doesn't account for the many developers who are simply unaware of PHP's internal type system, or that presently don't see the need to explicitly do type conversions even if they do sanitize their input. Not to mention those that don't sanitize their input at all...

Introducing 'weak' or auto-converting type hinting

The proposed solution implements a 'weaker' kind of type hinting - which arguably is more consistent with the rest of PHP's type system. Instead of validating the zval.type property only - it uses similar rules to those used by PHP's auto-conversion system to look into the value in question, and determine whether it 'makes sense' in the required context. If it does - it will be converted to the required type (if it isn't already of that type); If it doesn't - an error will be generated.

For example, consider a function getUserById() that expects an integer value. With strict type hinting, if you feed it with $id, which happens to hold a piece of data from the database with the string value “42”, will be rejected. With auto-converting type hinting, PHP will determine that $id is a string that has an integer format - and it is therefore suitable to be fed into getUserById(). It will then convert the value it to an integer, and pass it on to getUserById(). That means that getUserById() can rely that it will *always* get its input as an integer - but the caller will still have the luxury of sending non-integer but integer-formatted input to it.

The key advantages of the proposed solution are that there's less burden on those calling APIs, it's consistent with the rest of PHP, and perhaps most importantly - it does not require everyone to become intimately familiar with PHP's type system.

Here is a short list of examples to illustrate the weak type hinting. Note that just like the current array/object hints, a NULL is only allowed if the parameter defaults to NULL.

value string float int numeric scalar bool array
true (boolean) fail fail fail fail pass pass fail
false (boolean) fail fail fail fail pass pass fail
0 (integer) fail pass pass pass pass pass fail
1 (integer) fail pass pass pass pass pass fail
12 (integer) fail pass pass pass pass fail fail
12 (double) fail pass fail pass pass fail fail
12.34 (double) fail pass fail pass pass fail fail
'true' (string) pass fail fail fail pass fail fail
'false' (string) pass fail fail fail pass fail fail
'0' (string) pass fail fail pass pass pass fail
'1' (string) pass fail fail pass pass pass fail
'12' (string) pass fail fail pass pass fail fail
'12abc' (string) pass fail fail fail pass fail fail
'12.0' (string) pass fail fail pass pass fail fail
'12.34' (string) pass fail fail pass pass fail fail
'foo' (string) pass fail fail fail pass fail fail
array () (array) fail fail fail fail fail fail pass
array (0 => 12) (array) fail fail fail fail fail fail pass
NULL (NULL) fail fail fail fail fail fail fail
'' (string) pass fail fail fail pass fail fail

Furthermore, weak type hinting may be a step on the way to create generic type casting magic methods along the lines of __toString(), allowing objects to auto-convert to scalar types as necessary (TBD).


rfc/typecheckingstrictandweak.1274545655.txt.gz · Last modified: 2017/09/22 13:28 (external edit)