This is an old revision of the document!
PHP RFC: Operator Overrides -- Lite Edition
- Version: 0.0
- Date: 2024-06-28
- Author: Robert Landers, landers.robert@gmail.com
- Status: Under Discussion (or Under Discussion or Accepted or Declined)
- First Published at: http://wiki.php.net/rfc/operator_overrides_lite
Introduction
Nearly three years ago, PHP RFC: User Defined Operator Overloads was declined due to scope and new syntax concerns. However, the GMP class, which represents numbers in the GMP extension, was (accidentally) left non-final. This RFC explores the potential of extending GMP with a limited form of operator overriding, providing cleaner expressions for mathematical constructs and allowing new types of numerical representations such as units, complex numbers, and more.
Proposal
The \GMP class will have the following methods added as protected static:
- add(mixed $left, mixed $right): static
- subtract(mixed $left, mixed $right): static
- multiply(mixed $left, mixed $right): static
- divide(mixed $left, mixed $right): static
- mod(mixed $left, mixed $right): static
- exp(mixed $left, mixed $right): static
- comparable(mixed $left, mixed right): bool
These methods will enable operator-like behavior when extended. Overriding equals, less-than, greater-than, etc., is not included to maintain simplicity and avoid non-sensical comparisons.
Binary operations will be handled as such:
- If both operands are instances of `\GMP`, standard behavior applies.
- If one operand is a subclass of `\GMP`, the subclass's method is invoked.
- If both operands are subclasses, only the left operand's method is called. Compatibility checks are the subclass's responsibility.
Here's a basic implementation example for duration represented in seconds:
class Duration extends \GMP { public function __construct(int|\GMP|float $time) { parent::__construct($time, 10); } private static function assertCompatible($arg): void { if ($arg instanceof self || is_numeric($arg) || (is_object($arg) && get_class($arg) === '\GMP')) { return; } throw new LogicException('Duration is not compatible with ' . gettype($arg)); } private static function assertNumber($arg): void { if (is_numeric($arg) || (is_object($arg) && get_class($arg) === '\GMP')) { return; } throw new LogicException('Argument is not a number: ' . gettype($arg)); } protected static function add(mixed $left, mixed $right): static { self::assertCompatible($right); return new self(parent::add($left, $right)); } protected static function subtract(mixed $left, mixed $right): static { self::assertCompatible($right); return new self(parent::subtract($left, $right)); } protected static function multiply(mixed $left, mixed $right): static { if ($left instanceof self) self::assertNumber($right); if ($right instanceof self) self::assertNumber($left); return new self(parent::multiply($left, $right)); } protected static function divide(mixed $left, mixed $right): static { self::assertNumber($right); return new self(parent::divide($left, $right)); } protected static function mod(mixed $left, mixed $right): static { throw new LogicException('Not implemented'); } protected static function exp(mixed $left, mixed $right): static { throw new LogicException('Not implemented'); } protected static function comparable(mixed $left, mixed $right): bool { return $left instanceof self && $right instanceof self; } public function asMinutes(): float { return $this / 60; } /* and so on */ }
This approach provides valuable semantics and type safety, especially in contexts like attributes and properties:
define('SECOND', new Duration(1)); // Use in attributes #[Delay(5 * SECOND)] // Use in properties public Duration $delay = 5 * SECOND;
Backward Incompatible Changes
There are no backward incompatible changes. Existing GMP-based code will remain unaffected.
Proposed PHP Version(s)
8.4 if time allows, or the next version.
RFC Impact
To SAPIs
No impact.
To Existing Extensions
Only GMP will be affected.
To Opcache
There should be no impact to Opcache.
Open Issues
None, yet.
Unaffected PHP Functionality
Code using the GMP extension without extending it will remain unchanged.
Future Scope
- support for other operators
- serialization/unserialization
Proposed Voting Choices
- Allow extending the \GMP class and use a form of operator overloading
- Disallow extending the \GMP class
A “NO” vote implies making the `\GMP` class final to prevent further extensions.
Patches and Tests
A prototype patch will be provided before voting.
Implementation
After the project is implemented, this section should contain
- the version(s) it was merged into
- a link to the git commit(s)
- a link to the PHP manual entry for the feature
- a link to the language specification section (if any)
References
Links to external references, discussions or RFCs
Rejected Features
Keep this updated with features that were discussed on the mail lists.