rfc:user_defined_operator_overloads

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
Next revisionBoth sides next revision
rfc:user_defined_operator_overloads [2021/12/09 07:49] – copy edit jordanrlrfc:user_defined_operator_overloads [2021/12/12 01:14] – Expanded non-callable section jordanrl
Line 107: Line 107:
 <code php> <code php>
 // These all should result in an error // These all should result in an error
-$obj->'+'(1, OperandPosition::LeftSide);+$obj->{'+'}(1, OperandPosition::LeftSide);
 call_user_func([$obj, '+'], 1, OperandPosition::LeftSide); call_user_func([$obj, '+'], 1, OperandPosition::LeftSide);
 call_user_func('ObjClass::+', 1, OperandPosition::LeftSide); call_user_func('ObjClass::+', 1, OperandPosition::LeftSide);
 +</code>
 +
 +They can be directly invoked with a Closure however. This is to fully support Reflection, and to allow direct calls while still making it obvious to PHP developers that they should not be treating these as normal methods.
 +
 +<code php>
 +// Manually creating a closure allows a direct function call
 +$closure = Closure::fromCallable([$obj, '+']);
 +$closure(1, OperandPosition::LeftSide);
 +
 +// You can also retrieve the closure through Reflection
 +$closure = (new ReflectionMethod($obj, '+'))->getClosure($obj);
 +$closure(1, OperandPosition::LeftSide);
 +
 +$closure = (new ReflectionObject($obj))->getOperator('+')->getClosure($obj);
 +$closure(1, OperandPosition::LeftSide);
 </code> </code>
  
Line 352: Line 367:
 Any return value larger than 0 will be normalized to 1, and any return value smaller than 0 will be normalized to -1. Any return value larger than 0 will be normalized to 1, and any return value smaller than 0 will be normalized to -1.
  
-The $operandPos argument is omitted as it could only be used for evil e.g. implementing different comparison logic depending on which side its on. Instead of passing $operandPos the engine will multiply the result of the call by (-1) where appropriate:+The $operandPos argument is omitted as it could only be used for evil e.g. implementing different comparison logic depending on which side it'on. Instead of passing $operandPos the engine will multiply the result of the call by (-1) where appropriate:
  
 <code php> <code php>
Line 418: Line 433:
 These methods need to be updated to ignore the operator methods. Since these are stored internally like any other function on the class entry, they need to be filtered from the results. These methods need to be updated to ignore the operator methods. Since these are stored internally like any other function on the class entry, they need to be filtered from the results.
  
-The reason for removing the operators from this result is because the operator methods are not callable with string literals on the object. Since they cannot be called like a method is, the should be returned with the other methods on a class.+The reason for removing the operators from this result is because the operator methods are not callable with string literals on the object. Since they cannot be called like a method is, they should not be returned with the other methods on a class.
  
 == Adding getOperators(), getOperator(), and hasOperator() == == Adding getOperators(), getOperator(), and hasOperator() ==
Line 523: Line 538:
 } }
  
-$result = new Money(5, 'USD') + new Vextor2d(5, 10);+$result = new Money(5, 'USD') + new Vector2d(5, 10);
  
 // Type error, Vector2d can't be used as Money // Type error, Vector2d can't be used as Money
Line 622: Line 637:
 // The rest of the extension's do_operation handler // The rest of the extension's do_operation handler
 </code> </code>
 +
 +To further help extensions support this feature, there are two helper functions:
 +
 +<code c>
 +int has_overload = zend_std_has_op_overload(opcode, &zval);
 +
 +zend_function overload_method = zend_std_get_op_overload(opcode, &ce);
 +</code>
 +
 +It is safe to pass any zval pointer to ''zend_std_has_op_overload()'', as it first checks whether or not the ''Z_TYPE_P(zval) == IS_OBJECT'' and returns 0 if it doesn't.
  
 ==== To Opcache ==== ==== To Opcache ====
Line 646: Line 671:
 ==== Global Operator Overloads ==== ==== Global Operator Overloads ====
 This RFC deals with allowing each class to define its own interaction with operators. However, if overloading the operator itself were desired for the entire application, a different approach would be needed. This is also something that the ''operator'' keyword future proofs against, but is not an intended proposal of this RFC author. This RFC deals with allowing each class to define its own interaction with operators. However, if overloading the operator itself were desired for the entire application, a different approach would be needed. This is also something that the ''operator'' keyword future proofs against, but is not an intended proposal of this RFC author.
 +
 +==== Enum Return Type For <=> ====
 +
 +Returning an enum for the <=> would be preferable for two reasons.
 +
 +  - It allows the function to return an equivalent of 'uncomparable' (where all variations are false)
 +  - It is easier to read and understand the behavior in code, while integer values often require a moment to remember the meaning
 +
 +This is listed as future scope because there is a separate RFC which covers this feature: https://wiki.php.net/rfc/sorting_enum
 +
 +It is listed as a separate RFC because it is something that could be delivered whether or not this RFC passes.
  
 ===== Proposed Voting Choices ===== ===== Proposed Voting Choices =====
rfc/user_defined_operator_overloads.txt · Last modified: 2022/01/17 01:16 by jordanrl