rfc:userspace_operator_overloading

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:userspace_operator_overloading [2020/02/27 19:45] jbtronicsrfc:userspace_operator_overloading [2020/04/06 19:31] (current) jbtronics
Line 1: Line 1:
 ====== PHP RFC: Userspace operator overloading ====== ====== PHP RFC: Userspace operator overloading ======
-  * Version: 0.3.1+  * Version: 0.5
   * Date: 2020-02-01   * Date: 2020-02-01
   * Author: Jan Böhmer, jan.h.boehmer@gmx.de   * Author: Jan Böhmer, jan.h.boehmer@gmx.de
-  * Status: Under Discussion +  * Status: Declined 
-  * First Published athttp://wiki.php.net/rfc/userspace_operator_overloading+  * Target VersionPHP 8.0 
 +  * Implementation: https://github.com/php/php-src/pull/5156 
 ===== Introduction ===== ===== Introduction =====
 At the moment expressions like ''$a + $b'' or ''$a * 2'' are only valid if ''$a'' and ''$b'' are scalar types like int or float. However, in many other programming languages like Python, C# or C++ it is possible to overload these operators to use them on classes, to implement custom math (and other) objects. This RFC proposes userspace operator overloading for PHP. At the moment expressions like ''$a + $b'' or ''$a * 2'' are only valid if ''$a'' and ''$b'' are scalar types like int or float. However, in many other programming languages like Python, C# or C++ it is possible to overload these operators to use them on classes, to implement custom math (and other) objects. This RFC proposes userspace operator overloading for PHP.
Line 25: Line 26:
 Operator overloading is done by static magic functions per operator in a class. These functions receive both operands and must return a non null value: Operator overloading is done by static magic functions per operator in a class. These functions receive both operands and must return a non null value:
 <PHP> <PHP>
-public class Vector3()+class Vector3()
 { {
-    // For simple cases type checking can be done by typehints. +    public static function __add($lhs, $rhs) {
-    public static function __add(Vector3 $lhs, Vector3 $rhs) {+
         // Do something with the values and return a non-null value         // Do something with the values and return a non-null value
-        // ... 
     }     }
          
-    public static function __mul($lhs, $rhs) { +    public static function __mul($lhs, $rhs): ?Vector3 
-        // For more complex type checking, the function can return a special const.+        // If the given types are not supported, the function can return a special const.
         //...         //...
         return PHP_OPERAND_TYPES_NOT_SUPPORTED;         return PHP_OPERAND_TYPES_NOT_SUPPORTED;
Line 45: Line 44:
 // Equivalent to $x = Vector3::__add($a, $b) // Equivalent to $x = Vector3::__add($a, $b)
 $x = $a + $b; $x = $a + $b;
-//Equivalent to $y = Vecotr3::__mul(2, $b)+//Equivalent to $y = Vector3::__mul(3, $b)
 $y = 3 * $b; $y = 3 * $b;
 </PHP> </PHP>
Line 51: Line 50:
 By passing both operands to the handler, it can decide between the cases on non-commutative operators (''$a / 2'' vs. ''2 / $a''), which would be more difficult when only the "other" operand (besides ''$this'') is passed. By passing both operands to the handler, it can decide between the cases on non-commutative operators (''$a / 2'' vs. ''2 / $a''), which would be more difficult when only the "other" operand (besides ''$this'') is passed.
  
-The magic function can accept any type, the function has to decide if it can handle the type (and the value). If it can not handle the given type, it has to return the constant ''PHP_OPERAND_TYPES_NOT_SUPPORTED'' (currently just null). Typehints can be used in function signature to signalwhich types are supported.+The magic function can accept any type, the function has to decide if it can handle the type (and the value). If it can not handle the given type, it has to return the constant ''PHP_OPERAND_TYPES_NOT_SUPPORTED'' (currently just null). 
 + 
 +The argument **must not** specify any argument typehints (an error is thrown otherwise), as typehints and occuring type errors would break operator evaluation ([[https://externals.io/message/108788#108993|see discussion]]). 
 + 
 +Handlers **can** specify return typehintsbut note that the return type has to be nullable (as PHP_OPERAND_TYPES_NOT_SUPPORTED has the value null).
  
 ==== Overloadable Operators ==== ==== Overloadable Operators ====
-Like mentioned above only basic arithmetic operators should be overloadable, not compare or equality operators. The operators which are allowed to be overloaded and their associated magic functions are:+=== Direct overloadable operators === 
 + 
 +Like mentioned above only basic arithmetic operators should be overloadable, not compare or equality operators. The operators which are allowed to be overloaded (explicitly) and their associated magic functions are:
  
 |                 ^ Operator      ^ Magic function     ^ |                 ^ Operator      ^ Magic function     ^
Line 64: Line 69:
 ^ Modulo          | ''%''         | %%__mod%%          | ^ Modulo          | ''%''         | %%__mod%%          |
 ^ Concatenation   | ''.''         | %%__concat%%       | ^ Concatenation   | ''.''         | %%__concat%%       |
-^ Bitwise shift left   | ''%%<<%%''         | %%__lshift%%       | +^ Bitwise shift left   | ''%%<<%%''         | %%__shiftLeft%%       | 
-^ Bitwise shift right   | ''%%>>%%''         | %%__rshift%%       | +^ Bitwise shift right   | ''%%>>%%''         | %%__shiftRight%%       | 
-^ Bitwise OR   | ''%%|%%''         | %%__or%%       | +^ Bitwise OR   | ''%%|%%''         | %%__bitwiseOr%%       | 
-^ Bitwise AND   | ''&''         | %%__and%%       | +^ Bitwise AND   | ''&''         | %%__bitwiseAnd%%       | 
-^ Bitwise XOR   | ''^''         | %%__xor%%       | +^ Bitwise XOR   | ''^''         | %%__bitwiseXor%%       | 
-^ Bitwise NOT   | ''~''         | %%__not%%       |+^ Bitwise NOT   | ''~''         | %%__bitwiseNot%%       |
  
 +=== Indirect overloadable operators ===
 +The following operators derive their behavior from the operators above and therefore can be overloaded by implementing functions from above. They can not be overloaded on their own:
  
-''-$a'' is interpreted as ''(-1 * $a)''+|               ^ Operator  ^                        | 
 +^ Negative value | ''- $a'' interpreted as **''(-1)*$a''**, can be overloaded by implementing %%__mul%% | 
 +^ Positive value | ''+ $a'' | interpreted as **''(+1)*$a''**, can be overloaded by implementing %%__mul%% | 
 +^ Shorthand assignment | ''+='', ''*='', ''.='', etc. | ''$a += $b'' is interpreted as ''$a = $a + $b'', can be overloaded by implementing %%__add%% | 
 +^ Increment | ''$a++'' (and ''++$a'') | interpreted as ''$a = $a + 1'', can be overloaded by implementing %%__add%% | 
 +^ Decrement | ''$a%%--%%'' (and ''%%--%%$a'' | interpreted as ''$a = $a - 1'', can be overloaded by implementing %%__sub%% |
  
-The shorthand assignment operators follow the normal logicthat ''$a +$b'' is the same as ''$a $a + $b'' and therefore can not be overloaded on their own+=== Operators that can not be overloaded === 
- +The following operators can not be overloaded by using this method (neither explicit or implicit): 
-The same applies for increment/decrement. These are treat as ''$a + 1'' respectively ''$a - 1''. If the class can not handle addition with an integerthe class has to signal that it can not handle them (by argument typehint or special return value).+|                       ^ Operator                                                   | 
 +| Comparision operators | ''<'', ''%%<=%%'', ''>'', ''=='' etc      | maybe subject of a future RFC | 
 +| Assignment operator   | ''='' | | 
 +| Logic operators        | ''||''''!'', ''&&'' | | 
 +| Object operators      | ''instanceof'', ''new'', ''clone'' |     | 
 +| Null concealing operator | ''??'' |  | 
 +| Tenary operator       | ''? :'' | | 
 +| Error control operator | ''@'' | | 
 +| Object access operator | ''%%->%%'' |  |
  
 ==== Evaluation order ==== ==== Evaluation order ====
Line 90: Line 110:
  
 An operand handler can signal that it does not support the given types by returning the const ''PHP_OPERAND_TYPES_NOT_SUPPORTED'' (at the moment null). In this case it is tried to use the handler on the left object. If both handlers do not support the given operand types an error is thrown. An operand handler can signal that it does not support the given types by returning the const ''PHP_OPERAND_TYPES_NOT_SUPPORTED'' (at the moment null). In this case it is tried to use the handler on the left object. If both handlers do not support the given operand types an error is thrown.
- 
-If the operator handler function declares typehints (e.g. ''public static function %%__%%add(Vector3 $lhs, int $rhs)''), the function handler is not called if the operand types do not match the signature, and the other operand's handler is tried to call. 
- 
-Both methods can be combined. 
  
 ==== Error handling ==== ==== Error handling ====
-If an operator is used with an object that does not overload this operator, an NOTICE is triggered, which gives the user a hint about the method that has to be overloaded. For backward compatibility objects, which do not overload the operator, are converted to integer 1 (current behavior).+If an operator is used with an object that does not overload this operator, an NOTICE is triggered (to not break existing code), which gives the user a hint about the method that has to be overloaded. For backward compatibility objects, which do not overload the operator, are converted to integer 1 (current behavior).
  
 If the class overloads the operator, and the magic method do not return a value, an ERROR is triggered. If the class overloads the operator, and the magic method do not return a value, an ERROR is triggered.
  
 If the given operand types are not supported on both objects, an ERROR is thrown. If the given operand types are not supported on both objects, an ERROR is thrown.
 +
 +If the operator handler declares argument type hints or arguments should be passed-by-reference an ERROR is thrown.
  
 ==== Other ==== ==== Other ====
 A user who overloads an operator MUST ensure, that the magic function do not change the existing operand objects, or it will cause undesirable behavior. At the moment there is no way to enforce immutability on objects, so the user is responsible. The documentation should include a warning about this. A user who overloads an operator MUST ensure, that the magic function do not change the existing operand objects, or it will cause undesirable behavior. At the moment there is no way to enforce immutability on objects, so the user is responsible. The documentation should include a warning about this.
- 
  
 ===== Backward Incompatible Changes ===== ===== Backward Incompatible Changes =====
Line 137: Line 154:
  
 ==== Immutable types ==== ==== Immutable types ====
-To ensure that objects can not be changed (which could cause undesirable behavior), immutable objects (see this [[rfc:immutability|old RFC]] could be helpful.+To ensure that objects can not be changed (which could cause undesirable behavior), immutable objects (see this [[rfc:immutability|old RFC]]could be helpful.
  
 ==== Allow overloading of shorthand assignment operators and increment/decrement operators ==== ==== Allow overloading of shorthand assignment operators and increment/decrement operators ====
Line 143: Line 160:
  
 ===== Proposed Voting Choices ===== ===== Proposed Voting Choices =====
-Accept the existing proposed changes for 8.0: yes/no+Add userspace operator overloading as described: yes/no 
 + 
 +===== Vote ===== 
 +Voting started 2020-03-23 and ends 2020-04-06. 
 +<doodle title="Add userspace operator overloading as described?" auth="jbtronics" voteType="single" closed="true"> 
 +   * Yes 
 +   * No 
 +</doodle>
  
 ===== Patches and Tests ===== ===== Patches and Tests =====
-Draft implementation can be found here: https://github.com/php/php-src/pull/5156+Implementation can be found here: https://github.com/php/php-src/pull/5156
  
 ===== References ===== ===== References =====
Line 152: Line 176:
    * [[https://externals.io/message/108300|First email discussion]]    * [[https://externals.io/message/108300|First email discussion]]
    * [[rfc:operator-overloading|Old RFC with an similar propose]]    * [[rfc:operator-overloading|Old RFC with an similar propose]]
 +   * [[https://externals.io/message/108608|Discussion part 1]]
 +   * [[https://externals.io/message/108788|Discussion part 2]]
  
-===== Rejected Features =====+===== Rejectected features ===== 
 +  * Use interfaces instead of magic methods 
 +  * Use type hints to declare supported types (this would introduce some special kind of function overloading)
  
 ===== Changelog ===== ===== Changelog =====
Line 160: Line 188:
   * **0.3:** Do not catch TypeErrors as they can be thrown in users code (signature checking is done separately from method calling).   * **0.3:** Do not catch TypeErrors as they can be thrown in users code (signature checking is done separately from method calling).
   * **0.3.1** Renamed shift handler to ''%%__%%lshift'' and ''%%__%%rshift''   * **0.3.1** Renamed shift handler to ''%%__%%lshift'' and ''%%__%%rshift''
 +  * **0.3.2** Renamed some functions handler. Added tables which operators can be overloaded indirectly and which can not be overloaded at all.
 +  * **0.4** Removed type support decisions based on typehints, because this introduces some kind of function overloading.
 +  * **0.5** Disallow argument typehints in operator handlers.
rfc/userspace_operator_overloading.1582832713.txt.gz · Last modified: 2020/02/27 19:45 by jbtronics