rfc:scalar_type_hints

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
rfc:scalar_type_hints [2015/01/25 01:05] – DokuWiki stop reverting my edits pl0x I <3 u kthxbai (pls) ajfrfc:scalar_type_hints [2017/09/22 13:28] (current) – external edit 127.0.0.1
Line 1: Line 1:
 ====== PHP RFC: Scalar Type Hints ====== ====== PHP RFC: Scalar Type Hints ======
-  * Version: 0.2.3 +  * Version: 0.3.1 
-  * Date: 2014-12-14 (initial draft; put Under Discussion 2014-12-31; version 0.2 created 2015-01-13)+  * Date: 2014-12-14 (initial draft; put Under Discussion 2014-12-31; version 0.2 created 2015-01-13; withdrawn 2015-02-15)
   * Author: Andrea Faulds, ajf@ajf.me   * Author: Andrea Faulds, ajf@ajf.me
-  * Status: Under Discussion+  * Status: Withdrawn
   * First Published at: http://wiki.php.net/rfc/scalar_type_hints   * First Published at: http://wiki.php.net/rfc/scalar_type_hints
  
 ===== Summary ===== ===== Summary =====
  
-This RFC proposes the addition of four type hints for scalar types: ''int'', ''float'', ''string'' and ''bool''. These type hints would have "weak" type-checking by default, following the same casting rules traditionally used for extension and built-in PHP functions.+This RFC proposes the addition of four type hints for scalar types: ''int'', ''float'', ''string'' and ''bool''. These type hints would have "weak" type-checking by default, following the same casting rules traditionally used for the parameters of extension and built-in PHP functions.
  
-This RFC further proposes the addition of a new optional per-file directive, ''declare(strict_types=1)'', which makes all function calls within a file have "strict" type-checking for parameters with scalar type hints, including for extension and built-in PHP functions. In addition, calls to extension and built-in PHP functions with this directive produce an ''E_RECOVERABLE_ERROR'' on parameter parsing failure, bringing them into line with userland type hints.+This RFC further proposes the addition of a new optional per-file directive, ''declare(strict_types=1)'', which makes all function calls and return statements within a file have "strict" type-checking for scalar type hints, including for extension and built-in PHP functions. In addition, calls to extension and built-in PHP functions with this directive produce an ''E_RECOVERABLE_ERROR'' on parameter parsing failure, bringing them into line with userland type hints.
  
 With these two features, it is hoped that more correct and self-documenting PHP programs can be written. With these two features, it is hoped that more correct and self-documenting PHP programs can be written.
Line 87: Line 87:
 </file> </file>
  
-The strict type checking mode also affects extension and built-in PHP functions:+The directive affects all function calls in the file (or ''declare()'' block if specified), regardless of whether the functions being called were declared in files which used strict type checking. So: 
 + 
 +<file php strict_mix.php> 
 +<?php 
 +require "ElePHPant.php"; 
 + 
 +// implicitly weakly type-checked code (default) 
 +$nelly = new ElePHPant(12345, "7 years", "0.9", "1"); // succeeds 
 + 
 +declare(strict_types=1) { 
 +    // explicitly strictly type-checked code 
 +     
 +    $nelly = new ElePHPant(12345, "7 years", "0.9", "1"); 
 +    // Catchable fatal error: Argument 1 passed to ElePHPant::__construct() must be of the type string, integer given 
 +
 +</file> 
 + 
 +This applies equally to nested function calls, which also use the strictness setting of the file: 
 + 
 +<file php strict_mix2.php> 
 +<?php 
 +require "ElePHPant.php"; 
 + 
 +// implicitly weakly type-checked code (default) 
 +function makeEllie() { 
 +    return new ElePHPant(42, "19", "0.7", 1); // will succeed, no matter where makeEllie() is called from 
 +
 + 
 +makeEllie(); // no error 
 + 
 +declare(strict_types=1) { 
 +    // explicitly strictly type-checked code 
 + 
 +    makeEllie(); // no error 
 +     
 +    function makeEllie_strict() { 
 +        return new ElePHPant(42, "19", "0.7", 1); // will fail, no matter where makeEllie_strict() is called from 
 +    } 
 + 
 +    makeEllie_strict(); // error 
 +
 + 
 +// implicitly weakly type-checked code, again 
 + 
 +makeEllie_strict(); // error 
 +</file> 
 + 
 +In addition to userland functions, the strict type checking mode also affects extension and built-in PHP functions:
  
 <file php main4.php> <file php main4.php>
Line 96: Line 143:
 $foo = sin(1); $foo = sin(1);
 // Catchable fatal error: sin() expects parameter 1 to be float, integer given // Catchable fatal error: sin() expects parameter 1 to be float, integer given
 +</file>
 +
 +Scalar type hints would also work for return values, as does strict type checking mode:
 +
 +<file php returns.php>
 +<?php
 +
 +function foobar(): int {
 +    return 1.0;
 +}
 +
 +var_dump(foobar()); // int(1)
 +
 +declare(strict_types=1) {
 +    function foobar2(): int {
 +        return 1.0;
 +    }
 +}
 +
 +var_dump(foobar2());
 +// Catchable fatal error: Return value of foobar() must be of the type integer, float returned
 +</file>
 +
 +However, there is a key difference between parameter and return type hints. The type-checking mode used for parameters is the one used by the file containing the function call, while the type-checking mode used for return values is the one used by the file containing the return statement (i.e. the file defining the function). So:
 +
 +<file php returns.php>
 +<?php
 +
 +declare(strict_types=1) {
 +    function foobar3(): int {
 +        return 1.0; // error, regardless of where it is called from
 +    }
 +
 +    foobar3(); // error
 +}
 +
 +foobar3(); // also error
 </file> </file>
  
Line 102: Line 186:
 ==== History ==== ==== History ====
  
-PHP has had parameter type hints for class names since PHP 5.0, arrays since PHP 5.1 and callables since PHP 5.4. These type hints allow the PHP runtime to ensure that correctly-typed arguments are passed to functions, and make function signatures more informative. Unfortunately, PHP's scalar types haven't been hintable.+PHP has had parameter type hints for interface and class names since PHP 5.0, arrays since PHP 5.1 and callables since PHP 5.4. These type hints allow the PHP runtime to ensure that correctly-typed arguments are passed to functions, and make function signatures more informative. Unfortunately, PHP's scalar types haven't been hintable.
  
 There have been some previous attempts at adding scalar type hints, such as the [[rfc:scalar_type_hinting_with_cast|Scalar Type Hints with Casts]] RFC. From what I can see, that specific RFC failed primarily for three reasons: There have been some previous attempts at adding scalar type hints, such as the [[rfc:scalar_type_hinting_with_cast|Scalar Type Hints with Casts]] RFC. From what I can see, that specific RFC failed primarily for three reasons:
Line 110: Line 194:
   * Its attempt at "stricter" weak typing failed to placate either strict typing or weak typing fans   * Its attempt at "stricter" weak typing failed to placate either strict typing or weak typing fans
  
-In creating this RFC attempts to learn from these failings.+In creating this RFC, I have attempted to learn from these failings.
  
 ==== Weak typing and strict typing ==== ==== Weak typing and strict typing ====
  
-There are two major approaches to how to check parameter types or type hints that have been proposed for PHP:+There are two major approaches to how to check parameter and return type hints that have been proposed for PHP:
  
   * Strict type checking, which is used by many popular programming languages, particularly ones which are statically-typed, such as Java, C#, Haskell, or Facebook's Hack. It is also used for non-scalar parameter type hints in PHP. With this approach, an argument is only accepted if its type is exactly the same as the parameter. So, for example, an integer is accepted for an integer parameter, but a float is not accepted. Such systems often also accept subclasses, so if ''Bar'' inherits from ''Foo'', it might be accepted for a ''Foo'' parameter.   * Strict type checking, which is used by many popular programming languages, particularly ones which are statically-typed, such as Java, C#, Haskell, or Facebook's Hack. It is also used for non-scalar parameter type hints in PHP. With this approach, an argument is only accepted if its type is exactly the same as the parameter. So, for example, an integer is accepted for an integer parameter, but a float is not accepted. Such systems often also accept subclasses, so if ''Bar'' inherits from ''Foo'', it might be accepted for a ''Foo'' parameter.
Line 128: Line 212:
         throw new Exception("Not an integer."); // this will never happen         throw new Exception("Not an integer."); // this will never happen
     }     }
 +}
 +</code>
 +
 +Similarly, in both approaches, a function will always return exactly the return type it claims to:
 +
 +<code php>
 +function barfoo(): int {
 +   /* ... */
 +}
 +if (!is_int(barfoo())) {
 +    throw new Exception("Not an integer."); // this will also never happen
 } }
 </code> </code>
Line 176: Line 271:
 ==== strict_types declare() directive ==== ==== strict_types declare() directive ====
  
-By default, all PHP files are in weak type-checking mode. A new ''declare()'' directive is added, ''strict_types'', which takes either ''1'' or ''0''. If ''1'', strict type-checking mode is used for function calls in the remainder of the file. If ''0'', weak type-checking mode is used.+By default, all PHP files are in weak type-checking mode. A new ''declare()'' directive is added, ''strict_types'', which takes either ''1'' or ''0''. If ''1'', strict type-checking mode is used for function calls and return statements in the remainder of the file. If ''0'', weak type-checking mode is used.
  
-This directive also supports the ''declare()'' block syntax (e.g. ''declare(strict_types=1) { foo(); }''), in which case it will only affect function calls made within the block.+This directive also supports the ''declare()'' block syntax (e.g. ''declare(strict_types=1) { foo(); }''), in which case it will only affect function calls and return statements within the block.
  
 Like the ''encoding'' directive, but unlike the ''ticks'' directive, the ''strict_types'' directive only affects the specific file it is used in, and does not affect either other files which include the file, nor other files that are included by the file. Like the ''encoding'' directive, but unlike the ''ticks'' directive, the ''strict_types'' directive only affects the specific file it is used in, and does not affect either other files which include the file, nor other files that are included by the file.
  
-The directive is entirely compile-time and affects any function call, including those within a function or method. For example:+The directive is entirely compile-time and cannot be controlled at runtime. It works by setting a flag on the opcodes for function calls (for parameter type hints) and return type checks (for return type hints). 
 + 
 +=== Parameter type hints === 
 + 
 +The directive affects any function call, including those within a function or method. For example:
  
 <file php strict_types_scope.php> <file php strict_types_scope.php>
Line 214: Line 313:
 </file> </file>
  
-Whether or not the function being called was declared in a file that uses strict or weak type checking is entirely irrelevant. The type checking mode depends on the file where the function is called.+Whether or not the function being called was declared in a file that uses strict or weak type checking is irrelevant. The type checking mode depends on the file where the function is called.
  
-==== Behaviour of weakly type-checked function calls ====+=== Return type hints === 
 + 
 +The directive affects any return statement in any function or method within a file. For example: 
 + 
 +<file php strict_types_scope.php> 
 +<?php 
 + 
 +declare(strict_types=1) { 
 +    function foobar(): int { 
 +        return 1.0; // strictly type-checked return 
 +    } 
 + 
 +    class baz { 
 +        function foobar(): int { 
 +            return 1.0; // strictly type-checked return 
 +        } 
 +    } 
 +
 + 
 +function foobar(): int { 
 +    return 1.0; // weakly type-checked return 
 +
 + 
 +class baz { 
 +    function foobar() { 
 +        return 1.0; // weakly type-checked return 
 +    } 
 +
 +</file> 
 + 
 +Unlike parameter type hints, the type checking mode used for return types depends on the file where the function is defined, not where the function is called. This is because returning the wrong type is a problem with the callee, while passing the wrong type is a problem with the caller. 
 + 
 +==== Behaviour of weak type checks ====
  
 A weakly type-checked call to an extension or built-in PHP function has exactly the same behaviour as it did in previous PHP versions. A weakly type-checked call to an extension or built-in PHP function has exactly the same behaviour as it did in previous PHP versions.
  
-The weak type checking rules for the new scalar type hints are mostly the same as those of extension and built-in PHP functions. The only exception to this is the handling of ''NULL'': in order to be consistent with our existing type hints for classes, callables and arrays, ''NULL'' is not accepted by default, unless the parameter is explicitly given a default value of ''NULL''. This would work well with the draft [[rfc:nullable_typehints|Declaring Nullable Types]] RFC.+The weak type checking rules for the new scalar type hints are mostly the same as those of extension and built-in PHP functions. The only exception to this is the handling of ''NULL'': in order to be consistent with our existing type hints for classes, callables and arrays, ''NULL'' is not accepted by default, unless it is a parameter and is explicitly given a default value of ''NULL''. This would work well with the draft [[rfc:nullable_typehints|Declaring Nullable Types]] RFC. If that RFC were to pass, it would be possible to mark return types as nullable, and so they would accept ''NULL''.
  
 For the reference of readers who may not be familiar with PHP's existing weak scalar parameter type rules, the following brief summary is provided. For the reference of readers who may not be familiar with PHP's existing weak scalar parameter type rules, the following brief summary is provided.
Line 238: Line 369:
 ‡Only if it has a ''__toString'' method. ‡Only if it has a ''__toString'' method.
  
-==== Behaviour of strictly type-checked function calls ====+==== Behaviour of strict type checks ====
  
 A strictly type-checked call to an extension or built-in PHP function changes the behaviour of ''zend_parse_parameters''. In particular, it will produce ''E_RECOVERABLE_ERROR'' rather than ''E_WARNING'' on failure, and it follows strict type checking rules for scalar typed parameters, rather than the traditional weak type checking rules. A strictly type-checked call to an extension or built-in PHP function changes the behaviour of ''zend_parse_parameters''. In particular, it will produce ''E_RECOVERABLE_ERROR'' rather than ''E_WARNING'' on failure, and it follows strict type checking rules for scalar typed parameters, rather than the traditional weak type checking rules.
  
-The strict type checking rules are quite straightforward: when the type of the passed argument matches that specified by the parameter type hint it is accepted, otherwise it is not.+The strict type checking rules are quite straightforward: when the type of the value matches that specified by the type hint it is accepted, otherwise it is not.
  
 These strict type checking rules are used for userland scalar type hints, and for extension and built-in PHP functions. These strict type checking rules are used for userland scalar type hints, and for extension and built-in PHP functions.
- 
  
 ===== Backward Incompatible Changes ===== ===== Backward Incompatible Changes =====
Line 269: Line 399:
 This doesn't affect the behaviour of cast operators. This doesn't affect the behaviour of cast operators.
  
-When the strict type-checking mode isn't in use (which is the default), function calls behave identically to previous PHP versions.+When the strict type-checking mode isn't in use (which is the default), function calls to built-in and extension PHP functions behave identically to previous PHP versions.
  
 ===== Open Issues ===== ===== Open Issues =====
  
-There are two open issues related to naming. These might be voted on if consensus isn't reached.+There is currently an open issue related to naming, described belowThis will go to a vote.
  
-  * Currently, this RFC and patch allows the aliases ''integer'' and ''boolean'' in addition to ''int'' and ''bool''. Should we only allow ''int'' and ''bool''? It is probably not a good idea to add too many new reserved class names. On the other hand, we use ''integer'' and ''boolean'' in many places in the manual, and programmers would be forgiven for expecting ''integer'' and ''boolean'' to work. We could opt to reserve them but prevent their use, telling people to use ''int'' and ''bool'' instead. That wouldn't reduce the number of prohibited class names, but it would prevent confusion and ensure consistency.+This RFC and patch allows the aliases ''integer'' and ''boolean'' in addition to ''int'' and ''bool''. Should we only allow ''int'' and ''bool''? It is probably not a good idea to add too many new reserved class names. On the other hand, we use ''integer'' and ''boolean'' in many places in the manual, and programmers would be forgiven for expecting ''integer'' and ''boolean'' to work. We could opt to reserve them but prevent their use, telling people to use ''int'' and ''bool'' instead. That wouldn't reduce the number of prohibited class names, but it would prevent confusion and ensure consistency.
  
-  * Should the scalar type hint names be prohibited from use as class names? The patch currently prohibits this (<php>class int {}</php> is an error), to avoid the situation where you can declare a class with the name of a scalar type hint yet not type hint against it (as the name would be interpreted as a scalar hint). Personally, I think it'd be best to avoid confusion and prevent classes from having the same names as scalar types. However, if this causes significant backwards-compatibility problems, we might have to allow it. I would note that at least some of the existing classes with such names are used as a stand-in for scalar type hints. +==== TODO ====
-     * The patch doesn't currently do this, but it would make sense to also prevent scalar type hint names being used with the <php>use</php> statement.+
  
-  * Should the ''strict_types'' directive have shorter name? It may be rather annoying to have to type out the whole ''declare(strict_types=1);'' for every single file+  * Produce different error message on strict type hint failure vs. on weak type hint failure, lest debugging be a pain. For practical reasons we may not be able to change the weak one, as it is an existing message (should we?)
 +  * Return types need more tests.
  
 ===== Future Scope ===== ===== Future Scope =====
- 
-If return types are added, such as with the [[rfc:returntypehinting|Return Type Hinting]] RFC, scalar type hints should be supported. Unlike parameter types, it would probably not make sense to allow weak type-checking for return types. Allowing weak type-checking for parameter types, but only strict type-checking for return types, would be consistent with [[https://en.wikipedia.org/wiki/Robustness_principle|the principle of "be conservative in what you send, be liberal in what you accept"]]. If we are to reject this principle and allow weak type-checking for return types, then the type-checking mode should depend on where the function is defined, rather than where it is called. This is because returning the wrong type is a problem with the callee, while passing the wrong type is a problem with the caller. 
  
 Because scalar type hints guarantee that a passed argument will be of a certain type within a function body (at least initially), this could be used in the Zend Engine for optimisations. For example, if a function takes two ''float''-hinted arguments and does arithmetic with them, there is no need for the arithmetic operators to check the types of their operands. As I understand it, HHVM already does such optimisations, and might benefit from this RFC. Because scalar type hints guarantee that a passed argument will be of a certain type within a function body (at least initially), this could be used in the Zend Engine for optimisations. For example, if a function takes two ''float''-hinted arguments and does arithmetic with them, there is no need for the arithmetic operators to check the types of their operands. As I understand it, HHVM already does such optimisations, and might benefit from this RFC.
  
-===== Proposed Voting Choices =====+In discussions around this RFC, the ''declare(strict_types=1);'' syntax has been controversial. It is arguably rather ugly, and it has all the scoping quirks of ''declare()''. I've now arrived at a point where I'm willing to change to a truly per-file syntax, ''<?php strict'' (or similar). However, the RFC is currently being voted on, and I'd rather not cancel the vote. So, this will be proposed in a subsequent RFC. 
 + 
 +Another issue that came up is PHP's lack of a typehint for numbers (which also came up with the previous Scalar Type Hinting with Cast RFC). I plan to propose a ''numeric'' typehint in a future RFC which would take either an integer or a float. 
 + 
 +===== Vote ===== 
 + 
 +As this is a language change, this RFC requires a 2/3 majority to pass. Voting for all three votes started on 2015-02-05 and all were to end on 2015-02-19, but the voting was cancelled and the RFC withdrawn on 2015-02-15. 
 + 
 +==== Main ==== 
 + 
 +This vote is for the RFC itself. 
 + 
 +<doodle title="Accept the Scalar Type Hints RFC and merge patch into master?" auth="ajf" voteType="single" closed="true"> 
 +   * Yes 
 +   * No 
 +</doodle> 
 + 
 +==== Type aliases ==== 
 + 
 +This second vote is to solve the open issue regarding the ''integer'' and ''boolean'' synonyms for ''int'' and ''bool''. Whichever option receives the most votes will be implemented. 
 + 
 +<doodle title="Type aliases" auth="ajf" voteType="single" closed="true"> 
 +   * Allow synonyms 
 +   * Reserve synonyms and produce error message when used 
 +   * Do not reserve 
 +</doodle> 
 + 
 +===Reserve for future use ==== 
 + 
 +This final vote is in case the RFC fails to pass. It's a 2/3 majority-required backwards compatibility-breaking language change, which is to reserve the type hint names proposed by the RFC, so that a future RFC could implement scalar type hints without requiring a backwards compatibility break. The type names reserved include the synonyms ''integer'' and ''boolean''. They would not be reserved words, merely prohibited from use as class/interface/trait names, like this RFC's type names.
  
-As this is a language change, this RFC requires a 2/3 majority to pass. It will be a Yes/No vote.+<doodle title="Reserve type names if RFC does not pass?" auth="ajf" voteType="single" closed="true"> 
 +   Yes 
 +   * No 
 +</doodle>
  
 ===== Patches and Tests ===== ===== Patches and Tests =====
  
-There is a working, but incomplete php-src pull request with limited tests here: https://github.com/php/php-src/pull/998+There is a working, but possibly buggy php-src pull request with tests here: https://github.com/php/php-src/pull/998 
  
-There is no language specification patch.+There is no language specification patch as yet.
  
 ===== Implementation ===== ===== Implementation =====
Line 310: Line 470:
 ===== Changelog ===== ===== Changelog =====
  
 +  * v0.3.1 - Noted ''numeric'' and syntax change in Future Scope
 +  * v0.3 - Return types support
   * v0.2.3 - ''strict_types=1'' rather than ''strict_typehints=TRUE''   * v0.2.3 - ''strict_types=1'' rather than ''strict_typehints=TRUE''
   * v0.2.2 - Follow robustness principle for return types under Future Scope   * v0.2.2 - Follow robustness principle for return types under Future Scope
rfc/scalar_type_hints.1422147905.txt.gz · Last modified: 2017/09/22 13:28 (external edit)