Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:97782 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 88805 invoked from network); 16 Jan 2017 09:40:43 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 16 Jan 2017 09:40:43 -0000 Authentication-Results: pb1.pair.com header.from=ocramius@gmail.com; sender-id=pass Authentication-Results: pb1.pair.com smtp.mail=ocramius@gmail.com; spf=pass; sender-id=pass Received-SPF: pass (pb1.pair.com: domain gmail.com designates 74.125.82.41 as permitted sender) X-PHP-List-Original-Sender: ocramius@gmail.com X-Host-Fingerprint: 74.125.82.41 mail-wm0-f41.google.com Received: from [74.125.82.41] ([74.125.82.41:35978] helo=mail-wm0-f41.google.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id A0/F6-00729-8159C785 for ; Mon, 16 Jan 2017 04:40:41 -0500 Received: by mail-wm0-f41.google.com with SMTP id c85so150901072wmi.1 for ; Mon, 16 Jan 2017 01:40:40 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:in-reply-to:references:from:date:message-id:subject:to :cc; bh=d6OPAUd8W/RuTtzKul532LrovFN8g7aVEP5OZsm22SM=; b=OJYqrRvDPGjZ5svYH+6BxWm09MvLTyQukqu3SBVuR8y1boiTmV3HlaqQHKBgnfVDd0 d7r/4QktNpVcV4RjsvS3CASStO22gWj2aWPTJ9xjgxwQFbSsm8kGln/GNqwcvBNQjAzg 6OoGClprW3JA8olgVhyIFqKfDsVnMgOHbtcQw0nyueXWGY0h7vRtESeV0/ip6W7nV+H/ aDVpJOsBKLbgdjZxRWQle/0clIrukKkrU8V2nZ7JBLKYa9mMRVu21ZnmNDp3/RAVDqzQ T65UCUF3/Sz/iKEDLG5qZJNJK/+rGWc8p053AKVl3UFXL+pEmkLxFOh6IHOQKnf+3R8m 766A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:in-reply-to:references:from:date :message-id:subject:to:cc; bh=d6OPAUd8W/RuTtzKul532LrovFN8g7aVEP5OZsm22SM=; b=dZ6ewiDVt+DDV0NuApBAGBn+qslaZVzZooXEFUTvBDEGinuUOqx0n42BttQSdZ9GEN ZBShvrPmxhaMMeJSLA/rZVM1aila1PuT6xUVqI5ulCMKysItQ1s3M/fjzLERDXZURb+T HAPuvwlOvbFLLuGmXoN5f96P8ST506JfzO5UnM+52K9FQomxGXVjYau5XErsTnn1kQnO WhLzsaR1SaDvW36k4+j1WPoRA9CAi1/XYqcJwtETPgpYGMsEmTXbV6J7TkUk+JWqrvro Gg02S4pYEkOa46VhyE86srBCSMnNAo7X/7lc3B02Ua1Vgh0xi1XT7yzzfgL7etRwrgF2 r6lQ== X-Gm-Message-State: AIkVDXL0yq0xubw0zRHI6haJX6rRRkGhGuKBOb29lqyaaVWXnZKWCflP6QzM3/r8hpRM1orqdFXFMTSVX6kIwQ== X-Received: by 10.28.5.70 with SMTP id 67mr11061325wmf.32.1484559637298; Mon, 16 Jan 2017 01:40:37 -0800 (PST) MIME-Version: 1.0 Received: by 10.194.34.197 with HTTP; Mon, 16 Jan 2017 01:40:16 -0800 (PST) In-Reply-To: References: Date: Mon, 16 Jan 2017 10:40:16 +0100 Message-ID: To: =?UTF-8?Q?Micha=C5=82_Brzuchalski?= Cc: Wes , PHP Internals Content-Type: multipart/alternative; boundary=001a11443a36d4466b054632f971 Subject: Re: [PHP-DEV] Unsetting declared fields From: ocramius@gmail.com (Marco Pivetta) --001a11443a36d4466b054632f971 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Hi Micha=C5=82, On Mon, Jan 16, 2017 at 10:30 AM, Micha=C5=82 Brzuchalski wrote: > As I am familiar with those interceptions, I tend to point out some dirty > hacks > when reflection tells you property exists while getting notice on set, se= e > https://3v4l.org/VDMHm > > > class Foo > { > public $bar =3D 'bar'; > public $baz =3D 'baz'; > } > > class FooHack extends Foo > { > public function __construct(Foo $wrapped) > { > unset($this->bar); > } > } > > $foo =3D new FooHack(new Foo); > > $reflectionFooBar =3D new \ReflectionProperty(Foo::class, 'bar'); > var_dump((new ReflectionClass(Foo::class))->getProperties()); > var_dump($reflectionFooBar->getValue($foo)); > var_dump( > property_exists(FooHack::class, 'bar'), > property_exists($foo, 'bar') > ); > > Funny PHP7 has different behaviour from 7.0.7 - 7.1 with raising a notice= . > Yes, some cleanups were applied to PHP 7.1 (Nikic worked on them, I think). Specifically around trying to access reflection properties across incompatible object types. Unsure if that also touched `property_exists()` > I may be just looking for dirty example without purpose right now so, don= 't > listen to me. > No, that's fine. These are dirty hacks, but it really is the only (currently) available way to build userland property accessors. Without this kind of approach, the only alternative is relying on https://pecl.php.net/package/AOP, and that's not really something that is going to happen. > But I do feel like this could bring someone crazy when something should > exists while it's not. > This is not something that users would do directly. AOP-ish libraries are extremely well tested in these scenarios, because if something magic breaks it is really hard to understand what is going on. > Those property_exists($foo, 'bar') shouldn't return false in that example= ?! > I'd say that `property_exists()` should always return `true` for static (class definition) defined fields, since the primary use-case for `property_exists()` is to provide information for consumers of a certain object for BC/FC. Most frameworks/libraries rely on `function_exists()`, `method_exists()` and `property_exists()` to skip tests, run tests, load polyfills, etc. My best guess is that the API shouldn't change though. That's the safest BC approach, whereas new behavior can be defined with a completely new function (for example: `property_is_statically_defined()`, `property_value_is_assigned()`, etc.). > > 2017-01-16 10:17 GMT+01:00 Marco Pivetta : > > > On Mon, Jan 16, 2017 at 9:49 AM, Micha=C5=82 Brzuchalski < > > michal@brzuchalski.com> wrote: > > > >> Hi Marco, > >> > >> 2017-01-16 0:27 GMT+01:00 Marco Pivetta : > >> > >>> Hi Wes, > >>> > >>> This has been discussed before, and it's currently used to intercept > >>> access > >>> to properties. Since we don't have property accessors (sigh), the cod= e > >>> (simplified version) would look like following: > >>> > >>> class Foo > >>> { > >>> public $bar =3D 'baz'; > >>> } > >>> > >>> class FooInterceptor extends Foo > >>> { > >>> private $wrapped; > >>> public function __construct(Foo $wrapped) > >>> { > >>> $this->wrapped =3D $wrapped; > >>> unset($this->bar); > >>> } > >>> public function __get(string $name) > >>> { > >>> var_dump('reading ' . $name); > >>> return $this->wrapped->$name; > >>> } > >>> } > >>> > >>> $foo =3D new FooInterceptor(new Foo); > >>> > >>> var_dump($foo->bar); > >>> > >>> You can see a working example at https://3v4l.org/UtugD > >> > >> > >> There is one more thing might be confusing - reflection tells there > still > >> exists bar property after unset while it's realy not. > >> For example https://3v4l.org/NAg1l > >> > >> $class =3D new ReflectionClass(FooInterceptor::class); > >> $property =3D $class->getProperty('bar'); > >> var_dump($property); // still exists while actually being unset may > cause > >> errors > >> > >> I'm sticking to extending class without magic _get method implemented. > >> > >> > >>> > >>> > >>> This behavior is protected from regressions since PHP 5.4, but has be= en > >>> working since 5.0: > >>> https://github.com/php/php-src/blob/cd2b462a2742c79256668d47 > >>> 36644e34573c33d9/tests/classes/unset_properties.phpt > >>> > >>> We can most probably get rid of this weird behavior once property > >>> accessors > >>> are in the language. > >>> > >>> Greets, > >>> > >>> Marco Pivetta > >>> > >>> http://twitter.com/Ocramius > >>> > >>> http://ocramius.github.com/ > >>> > >>> On Mon, Jan 16, 2017 at 12:20 AM, Wes wrote: > >>> > >>> > Hello elephpants. > >>> > > >>> > Currently PHP allows explicitly declared fields (eg public $foo =3D > 10;) > >>> to > >>> > be removed entirely through unset (eg unset($obj->foo)). > >>> > > >>> > Now that isn't really an issue as properties in php are currently > >>> untyped > >>> > and therefore nullable; at worst you would get a notice. But it wou= ld > >>> > become an issue with typed fields... that might get a second chance > >>> sooner > >>> > or later. > >>> > > >>> > But regardless of that, it looks very strange to me that this is > >>> allowed > >>> > for fields that are explicitly declared. I think unset() should set > the > >>> > field to null if it's declared in the class, and remove the field > >>> > altogether only if it was defined dynamically. > >>> > > >>> > On the other hand, this is just one of many ways of hacking php tha= t > >>> just > >>> > exist and we accept / don't care because we have faith in other > people > >>> not > >>> > doing nasty stuff with our code. This might sound ironic it is > >>> actually not > >>> > :P > >>> > > >>> > However, I am curious: what you think about this? Should PHP do > >>> something > >>> > in regard? Should this continue to work like it does now? Why do yo= u > >>> feel > >>> > it should do the one or the other? > >>> > > >>> > >> > > Hi Micha=C5=82, > > > > Reflection will also trigger `__get` in this scenario, which is expecte= d > > and was also reverted multiple times in "fixes" that worked around or > > forgot to call the property access guards. > > > > class Foo > > { > > public $bar =3D 'baz'; > > } > > > > class FooInterceptor extends Foo > > { > > private $wrapped; > > public function __construct(Foo $wrapped) > > { > > $this->wrapped =3D $wrapped; > > unset($this->bar); > > } > > public function __get(string $name) > > { > > var_dump('reading ' . $name); > > return $this->wrapped->$name; > > } > > } > > > > $foo =3D new FooInterceptor(new Foo); > > > > $reflectionFooBar =3D new \ReflectionProperty(Foo::class, 'bar'); > > > > var_dump($reflectionFooBar->getValue($foo)); > > > > See https://3v4l.org/6JtWT for a working example. > > > > You can see https://github.com/Ocramius/ProxyManager/tree/ > > cce5477857504997baf3168974b8f1283516a686/tests/language-feature-scripts > > for > > > > As I already mentioned, this hack is currently necessary to make proper= ty > > access interception transparent, which is common for most AOP-oriented > > code. We need an alternate approach to make this happen, before such a > > feature can be dropped. > > > > Marco Pivetta > > > > http://twitter.com/Ocramius > > > > http://ocramius.github.com/ > > > Marco Pivetta http://twitter.com/Ocramius http://ocramius.github.com/ --001a11443a36d4466b054632f971--