Table of Contents

PHP RFC: Operator Overloading

Introduction

This RFC adds the possiblity of overloading operators, which allows operators to act according to the type of data they are using. We could define our classes to behave and react to these operators in a custom way. It aims to extend the already extant builtin object overloading used by GMP to userspace classes.

Motivation

Let's say we want to create a custom class to represent imaginary numbers, which could be an array of two Integers. Let's have $a and $b, of type Complex. If we want to add them, or multiply them, we would have to use a custom function. However, it would be nice if we could overload + and * operators for this type, and just use $a + $b or $a * $b.

class Complex {
  public $real;
  public $imaginary;
  
  public function __construct($real = 0, $imaginary = 0) {
    $this->real = $real;
    $this->imaginary = $imaginary;
  }
  
  public function __add($rhs) {
    $ret = clone $this;
    if ($rhs instanceof Complex) {
      $ret->real += $rhs->real;
      $ret->imaginary += $rhs->imaginary;
    } else {
      $ret->real += (int)$rhs;
    }
    return $ret;
  }
  
  public function __mul($rhs) {
    if ($rhs instanceof Complex) {
      return new Complex(($this->real * $rhs->real) - ($this->imaginary * $rhs->imaginary),
                         ($this->real * $rhs->imaginary) + ($this->imaginary * $rhs->real));
    } else {
      $ret = clone $this;
      $ret->real *= (int)$rhs;
      $ret->imaginary *= (int)$rhs;
      return $ret;
    }
  }
}

Proposal

Add the following magical functions, available for every class.

public function __add($__value__); // Overloads + operator
public function __sub($__value__); // Overloads - operator
public function __mul($__value__); // Overloads * operator
public function __div($__value__); // Overloads / operator
public function __pow($__value__); // Overloads ** operator
public function __concat($__value__); // Overloads . operator

Opcache

The implementation of this RFC would add new zend_function pointers to user-defined zend_class_entry structures which opcaches would need to set upon rematerializations.

Proposed PHP Version(s)

7.1

Associativity

When both operands to a binary expression implement operator overloading, the left-hand operand's handler will be invoked. If only one of the two implement overloading, then that side is invoked with the relative operation inverted as necessary. For example (4 < $complex) would invoke the is_greater handler for the $complex object passing in 4 as though it were the RHS expression.

Open questions for discussion

Introduce interface(s) (a la ArrayAccess)?

Proposed Voting Choices

Implement user-space operator overloading as described. Requires 2/3 majority.

Future Expansion

Introduce dispatch for additional operations?

public function __mod($__value__); // Overloads % operator
public function __sl($__value__); // Overloads << operator
public function __sr($__value__); // Overloads >> operator
public function __or($__value__); // Overloads | operator
public function __and($__value__); // Overloads &
public function __xor($__value__); // Overloads ^
public function __is_identical($__value__); // Overloads ===
public function __is_not_identical($__value__); // Overloads !==
public function __is_equal($__value__); // Overloads ==
public function __is_not_equal($__value__); // Overloads !=
public function __is_lesser($__value__); // Overloads <
public function __is_lesser_or_equal($__value__); // Overloads <=
public function __is_greater($__value__); // Overloads >
public function __is_greater_or_equal($__value__); // Overloads >=
public function __assign_add($__value__); // Overloads +=
public function __assign_sub($__value__); // Overloads -=
public function __assign_mul($__value__); // Overloads *=
public function __assign_div($__value__); // Overloads /=
public function __assign_mod($__value__); // Overloads %=
public function __assign_sl($__value__); // Overloads <=
public function __assign_sr($__value__); // Overloads >>=
public function __assign_concat($__value__); // Overloads .=
public function __assign_or($__value__); // Overloads |=
public function __assign_and($__value__); // Overloads &=
public function __assign_xor($__value__); // Overloads ^=
public function __assign_pos($__value__); // Overloads **=
public function __pre_inc(); // Overloads ++$value
public function __pre_dec(); // Overloads --$value
public function __post_inc(); // Overloads $value++
public function __post_dec(); // Overloads $value--

Limiting Issue: Greater/Lesser are not distinct

Greater-than, and Greater-than-or-equal expressions are currently implemented by the compiler as inverted Less-than, and Less-than-or-equal expressions. The implementation of this RFC would need to either exclude the is_greater* functions, or it would need to implement is_greater in the runtime as a distinct expression from is_smaller.

References

https://github.com/php/pecl-php-operator exists as an extension based approach to operator overloading. A formal in-tree implementation would merit a new implementation in std_object_handlers.