On Mon, Mar 21, 2016 at 4:37 PM, Rowan Collins <rowan.coll...@gmail.com>
wrote:

> Andrea Faulds wrote on 17/03/2016 22:32:
>
>> Hi Johannes,
>>
>> Johannes Schlüter wrote:
>>
>>> On Wed, 2016-03-16 at 19:15 +0100, Bob Weinand wrote:
>>>
>>>>
>>>> Eih, only to typed properties. Everything else would be insane ;-)
>>>> Sorry for being imprecise.
>>>>
>>>
>>> Ok, quite a lot better, but still weird difference to the language.
>>>
>>>
>>> There's a notable amount of places where references are being used and
>>> often users aren't aware. (Binding parameters in database queries, in
>>> order to support OUT parameters, comes to mind, many array functions
>>> take an array, some functions use it to report error codes, ...)
>>>
>>> I guess this will be a famous support issue.
>>>
>>> And yeah, I'd love to "fix" all those places and get rid of the
>>> references, but BC ... it's quite massive :-)
>>>
>>>
>> Disallowing references here might make users more aware of which
>> functions take parameters by reference, and avoid accidentally modifying
>> values. So this part of the RFC might be a good thing.
>>
>
>
> I'm not a fan of this kind of side-effect coupling - "because this feature
> is new, it also triggers other new features". If you want no-ref
> properties, that should be something a user chooses directly, e.g. "class
> Foo { public noref $bar; }".
>
> Adding an "array" typehint to something absolutely should not prevent it
> being passed to sort(), IMHO.
>
> Perhaps we could have an IS_TYPED_REF zval? You don't actually need to
> store the desired type, only assert that the old and new reference targets
> are of the *same* type when performing assignment.
>

While it is no secret that we don't particularly like references, this is
not the reason why they are forbidden. There is no conspiracy to punish
users of references by denying them use of new typing features. No, the
reasons here are technical. Let me illustrate some of the issues involved
in more detail.

Lets start with some more "conceptual" issue. Consider this code:

class Foo {
    public int $bar = 42;
}

$foo = new Foo;
$bar =& $foo->bar;
unset($foo);
$bar = "xyz";

How does this code behave? The assignment $bar = "xyz" violates the "int"
typehint. However, the object those typed property we're referencing has
already been destroyed. Is the reference still typed or not?

If you answered "yes" to this question, consider an additional fact: It is
an implementation invariant that a reference with refcount 1 can be
converted to a value (a reference-set containing a single variable has
value semantics). The places where this happens or does not happens are not
evident to userland developers. The above code would throw, but if you
inserted a switch on $bar before hand it wouldn't. Wat?

If you answered "no" to this question, also consider another issue: In the
above example, the point where $foo is destroyed is clear. But what if the
destruction is actually triggered by a garbage collection run? As its not
possible to predict when garbage collection runs, you may have two directly
consecutive and identical statements, one of which throws, while the other
doesn't. (Not even to mention how impractical it is to track the
destruction of the object those property you're referencing.)

That's one issue. Another problem is related to "spooky action at a
distance". If you deal with parameter types or typed properties, you know
what you're in for. If you know the type of the object, you know what type
is expected. With references, this is generally not the case. You do not
know where the reference is coming from originally and what type constraint
it might have. Unless we actually keep track of which typed property a
reference is associated with (and maybe also where in the code it was
taken), you couldn't even know *why* you're getting a type error.

Now, for the most part, references in PHP are fairly explicit. You need to
include a & sigil during assignment and argument passing. You could argue
that, if you see that you're accepting an argument by reference, you just
have to take the possibility of it being typed into account. However, this
is simply not true. While references are mostly explicit, they aren't
always. If you create an array $array = [&$foo->bar] and then pass around
that array *by value*, $array[0] will still be a reference. This means that
an $array[0] = "xyz" assignment, in a piece of code that does not itself
use references in any way, can still end up with a type error.

Typed references are a ticking time bomb. They contagiously pervade through
the code and may go off at any point. Any point at all, even if it doesn't
visibly involve references.

Now, these are some of the "conceptual" issues. Even discounting them, we'd
still be up against a large purely implementational problem if we wanted to
support typed references. Your suggestion of using IS_TYPED_REF that just
checks if the type of the new value matches with that of the existing value
will fail in the presence of object types, as they are instanceof based. We
would have to store the desired type of the value. For debugging purposes
at least, we'd likely also store the class and property enforcing the
constraint. This constraint would need to be enforced in all places that
might possibly assign to a reference. For the places in the virtual
machine, we could manage. There's a limited (albeit large) number of places
where reference assignments can occur and we could add checks to them (even
though ensuring that existing code does not performance regress might be
non-trivial). But reference assignments happen not only in the engine. We
have a large body of extension code that assumes you can change a reference
just by writing into a zval. PHP 7 contained a large change to the way
references are handled, and we are *still* finding new places (even in
bundled extension code) that haven't been updated. Changing these kinds of
fundamental assumptions always has fallout.

As things stand now, you essentially have a choice between typed properties
without reference for PHP 7.1, or typed properties with references for PHP
9.0. I'd also like to note that it is always possible to add support for
references at a later point in time, should we find a good way of dealing
with them.

Thanks,
Nikita

Reply via email to