rfc:parameter_type_casting_hints

Request for Comments: Parameter Type Casting Hints

Introduction

Currently, PHP has no way to provide type hinting for function parameters which are not classes or arrays. This is on often requested feature that has been discussed on the internals list many many times. This RFC discusses one implementation of this feature that still attempts to remain in the spirit of PHP's dynamic type nature.

Philosophy

This RFC discusses a method of adding scalar type hints to PHP while attempting to embrace the dynamic nature of PHP variables. This means that passing a type that does not match the hinted type will cause a cast to happen (if possible, based on the normal type-casting rules used in other areas of the engine).

It's worth noting that this RFC does not attempt to add new errors to the casting paradigm in PHP. It's my feeling that issuing errors (E_NOTICE or E_STRICT) on data-loss from cast operations is important, but a more general issue to PHP. So as such it does not fall under this specific RFC, but as a more general RFC which can be added to the language as a whole.

So in an attempt at consistency, this RFC uses the normal Type Juggling Rules that the rest of the engine uses for casting operations.

Implementation

Syntax

The parameters are type hinted using the following syntax:

<?php

  function test((int) $intParam, (string) $strParam = "foo", (array) $array = array()) {}

?>

This will cause the following function to always return true for almost any combination of parameters:

<?php

  function test((int) $int, (float) $float, (bool) $bool, (string) $string) {
      return is_int($int) && is_float($float) && is_bool($bool) && is_string($string);
  }

?>

The one exception which will cause the function to not return true, is when the $string parameter is an object which does not implement __toString, which therefore will throw a Catchable fatal error on conversion to string. This is the default casting behavior for strings.

Cast Options

The following cast types are supported as parameter casts:

  • (int) - Cast to an integer (if not already)
  • (float) - Cast to a float (if not already)
  • (bool) - Cast to a boolean (if not already)
  • (string) - Cast to a string (if not already)
  • (array) - Cast to an array (if not already)
  • (object) - Cast to an object (if not already)

Note that the final 2 options (array and object) are there for both completeness, and to provide a less-strict type-hint (to be consistent with the new additions).

Default Values

For consistency, only matching types are allowed to be the default to a type-casted parameter. So (int) $foo = null and (int) $foo = 1 are both supported, but (int) $foo = “1” will generate an E_COMPILE_ERROR.

If null is not the default value, any attempt to pass null to a function which has a casting type hint will cause a cast from null to occur. If null is the default value for the parameter, passing null will not trigger a cast. So:

  function test1((int) $foo) { echo gettype($foo); }
  function test2((int) $foo = null) { echo gettype($foo); }
  

Calling test1(null) will produce int as the output. Calling test2(null) will produce null as the output.

Backwards compatibility breaks

This patch does not break existing functionality (with one exception of a C level API change that is not a public API). Current code will execute as normal.

Patch Details

Cast Type Definitions

The patch adds a series of “cast” definitions to the C type constants currently in use by the core. They are implemented in a way that the destination type of the cast can be found by masking the cast against 0x0F. They are:

  • IS_NULL_CAST 0x010
  • IS_LONG_CAST 0x011
  • IS_DOUBLE_CAST 0x012
  • IS_BOOL_CAST 0x013
  • IS_ARRAY_CAST 0x014
  • IS_OBJECT_CAST 0x015
  • IS_STRING_CAST 0x016

These are used to distinguish casting declarations in OPCode from a possible future implementation of more strict casts.

These have also been added to the zend_get_type_by_const() function to return the cast string that would be used.

API Changes

One API change was necessary for the cast to work. The third paramater of zend_verify_arg_type was changed from a single pointer zval, to a double pointer zval. This allows the argument to be separated if the cast happens and a copy-on-write event to be triggered. All implementations of this function in trunk have been updated (only occurs in the Zend directory).

References

To prevent odd behavior, it is a E_COMPILE_ERROR to define a parameter as both cast-hinted and a reference. This prevents issues where passing a variable to a function by reference changes the type of the original argument and possibly destroys data in the original variable.

Parser Tokens

No new parser tokens or reserved words are created by this RFC / implementation.

Changelog

rfc/parameter_type_casting_hints.txt · Last modified: 2017/09/22 13:28 by 127.0.0.1