rfc:assignment-overloading

PHP RFC: Separate overloaded operations when assigning

Introduction

PHP allows for basic operator overloading for builtin objects. For example, in the following code, gmp_init() produces an object instance of class “GMP”, yet the addition operation is not only valid, it produces a valid result (a new object with an updated value).

<?php
$g = gmp_init(123);
$h = $g + 2;
var_dump($h); // object(GMP)#2 { ["num"]=>125 }

Specifically, the following basic operators are overloaded: + - * / ** .

In addition, the assignment versions of these operators ( += -= *= /= **= .= ) are supported by executing a normal operation followed by a store. Thus the following two blocks of code are effectively identical.

<?php
$g = gmp_init(123);
$g += 2; // GMP(125)
<?php
$g = gmp_init(123);
$g = $g + 2; // GMP(125)

From a simplisitic math standpoint, this makes sense, however it lacks consistency when reference like behavior is introduced.

<?php
$g = gmp_init(123);
$h = $g;
$h += 2;

At this point, what should the numeric value of $g be? Because it's an object, we would expect the reference-like behavior of objects in PHP 5 and 7 to cause both $g and $h to be referencing the same object value, and thus it would equal “125”. However, because the assign-add operation is implemented as add-then-assign, we find that the reference-like set is separated, and $g is still pointing as object(GMP)#1 { [“num”]⇒123 } while $h is pointing at object(GMP)#2 { [“num”]⇒125 }

It could be argues that an add-assign operation is exactly that: “add & assign”, but the same holds true of the pre/post inc/dec operators:

<?php
$g = gmp_init(123);
$h = $g;
$h++; // Currently implemented as $h = $h + 1;

It would be hard to argue that the intent here was to create a new object instance rather than mutate an existing object in place.

Apart from violating expectations, this behavior has the potential of being far more costly from a performance point of view since new objects are created to replace existing ones (which will be destroyed immediately, in most cases).

Proposal

1. Modify the existing places in the engine which invoke zend_object_handlers.do_operation to specify the ASSIGN operations as distinct overloads so that implementations (such as GMP) can handle assign ops distinct from return driven expressions.

2. Make pre/post inc/dec their own distinct operation in operator overload callbacks rather than converting to add/sub 1 operations.

3. Update GMP to handle these cases as the reference implementation of operator overloading.

Backward Incompatible Changes

Any GMP code relying on the implicity separation of an object during an assign-op operation will now experience reference-like behavior. If there's any code relying on that behavior, they can modify their code in a backward-compatible way to explicitly perform `$x = $x op $y` rather than `$x op= $y`.

Proposed PHP Version(s)

7.1

RFC Impact to existing extensions

GMP will need to be updated.

Proposed Voting Choices

Q. Separate assign-op and pre/post inc/dec operations in operator overload callbacks? 50% majority

rfc/assignment-overloading.txt · Last modified: 2017/09/22 13:28 (external edit)