Another thing that looks odd to me i that every time you call new
ReflectionClass, a new reflection_object gets created.
Isn't there a way to get this "cached" somehow in zend_class_entry?

On Mon, Apr 25, 2016 at 10:11 AM, guilhermebla...@gmail.com <
guilhermebla...@gmail.com> wrote:

>
>
> On Mon, Apr 25, 2016 at 3:42 AM, Dmitry Stogov <dmi...@zend.com> wrote:
>
>>
>>
>> On 04/22/2016 06:39 PM, guilhermebla...@gmail.com wrote:
>>
>>
>> On Fri, Apr 22, 2016 at 3:07 AM, Dmitry Stogov <dmi...@zend.com> wrote:
>>
>>>
>>>
>>> On 04/22/2016 04:05 AM, <guilhermebla...@gmail.com>
>>> guilhermebla...@gmail.com wrote:
>>>
>>> Hi Dmitry,
>>>
>>> As a previous suggester of metadata information built-in into PHP, and
>>> also one of developers of the most used metadata library written in PHP, I
>>> understand this feature implementation requires several design decisions
>>> and also a good understanding of specific situations users may require.
>>>
>>> While I am a strong supporter of a more robust solution, this is already
>>> a good start.
>>> A few things I'd like to ask for my own understanding and also
>>> suggestions too:
>>>
>>> 1- I understand you took a minimalistic approach towards a "dumb"
>>> implementation for attributes (when I mean "dumb", the idea here is towards
>>> a non-OO approach). Can you explain your motivations towards this approach?
>>>
>>> I see two distinct approaches of implementation for this feature. Both
>>> of them have some common demands, like lazy initialization of metadata.
>>> Here they are:
>>>
>>> - Simplistic approach, which lets consumers of the feature do all the
>>> work related to validation, assertion of valid keys, values, etc
>>> This does not invalidate the ability to leverage of some features that a
>>> more robust implementation demands.
>>>
>>> - Robust approach: language takes the burden of instantiating complex
>>> structures, validating, assertion of valid keys, values, if this complex
>>> structure is allowed to be instantiated in that given class, method, etc.
>>>
>>>
>>> I didn't exactly understand what do you suggest.
>>> If you are talking about Attribute objects initialization during
>>> compilation - this is just not possible from implementation point of view.
>>> Now attributes may be stored in opcache SHM and relive request boundary.
>>> Objects can't relive requests.
>>>
>>
>>
>> I know that object instances are not cross-requests. Explicitly, I
>> mentioned that both approaches require lazy-initialization (which means,
>> whenever you call getAttributes() or getAttribute()).
>>
>> What I mentioning is that your approach is basically a new key/value
>> syntax that are used specifically for Attributes. We could easily turn this
>> into a more robust approach if instead of defining key/value pairs, we
>> instantiate objects or call functions. You already demonstrated interest to
>> support <<ORM\Entity>> reusing the imports (which is our biggest headache
>> in Doctrine Annotations), so why not issue constructor or function calls
>> there? That would simplify the work needed for consumers and also add room
>> for later improvements.
>>
>> So basically in this example:
>>
>> use Doctrine\ORM;
>>
>> <<ORM\Entity("user")>>
>> class User {}
>>
>> $reflClass = new \ReflectionClass("User");
>> var_dump($reflClass->getAttributes());
>>
>> We'd be changing from this:
>>
>> array(1) {
>>   ["Doctrine\ORM\Entity"]=>
>>   array(1) {
>>     [0]=>
>>     string(4) "user"
>>   }
>> }
>>
>> Into this:
>>
>> array(1) {
>>   ["Doctrine\ORM\Entity"]=>
>>   object(Doctrine\ORM\Entity)#1 (1) {
>>     ["tableName"]=>
>>     string(4) "user"
>>   }
>> }
>>
>>
>> As I showed already, it's very easy to do this transformation at higher
>> layer.
>>
>> $reflClass = new \ReflectionClass("User");
>> $attributes = $reflClass->getAttributes()
>> foreach ($attributes as $key => &$val) {
>>     $val = new $key(...$val);
>> }
>> var_dump($attributes);
>>
>> Construction objects directly in Reflection*::getAttributes() method,
>> doesn't make significant benefits and even makes limitation.
>>
>
> Sorry, but I don't see how limitations are added. If you call a function,
> static method or constructor, you actually add whole new level of
> possibilities, and I fail to see which limitations are added. Could you
> provide me one?
>
> Calling the function/constructor/static method, not only helps to better
> segregate userland code, but it also adds subsequents extensibility. I can
> highlight examples:
>
> - Support for Inheritance and overrides, through @Inherit, @Override, etc.
> While you might not see how it could be used now, other developers might be
> weirdly creative.
> - Targeting of annotations, such as limiting its scope to be only class,
> method or property. We use this extensively in Doctrine, where you cannot
> define Doctrine\ODM\Entity over a property.
> - Separating what can be considered as an annotation and what cannot.
> Built-in @Annotation as a marker would differentiate that I can do call
> Doctrine\ORM\Entity and not Doctrine\ORM\UnitOfWork.
> - Make it easier to support an AOP extension, where it could detect
> annotations being used and override DO_FCALL to call before, after or
> around through the implementation of interfaces.
> - If we ever decide to support named parameters, taking advantage of that
> would become natural, like: <<ORM\Entity(tableName => "user")>>
>
>
>
>>
>>
>>
>>
>>>
>>> 1- Your approach is basically defining an array. Could you explain your
>>> line of thinking on why you didn't consider a syntax like the one below?
>>>
>>> <["key" => "value"]>
>>> class Foo {}
>>>
>>> I didn't try to invite new syntax. Just completely took it from HHVM.
>>>
>>
>> My idea was based on your current proposal, which is basically a way to
>> define key/value pairs.
>> If you decide to go minimalistic, that is probably my best line of
>> thinking.
>>
>>
>>>
>>>
>>>
>>> 2- I see that you added support over functions, classes, constants and
>>> properties. According to the RFC, getAttributes() was added over
>>> ReflectionFunction. Is there a reason why support was not added to methods
>>> (ReflectionMethod extends ReflectionFunctionAbstract, which was not
>>> mentioned on RFC)? Any reason to not support it in function/method
>>> parameters?
>>>
>>> ReflectionMethod is a child of ReflectinFunction, so it's supported.
>>>
>> Attributes are allowed for the same entities as doc-comments (they are
>>> not allowed for parameters)
>>>
>>
>> I was asking if there was a purpose to not support Attributes over
>> ReflectionParameter. Example:
>>
>> class Foo {
>>     public function bar(<<Qux>> Bar $bar) : bool {
>>         // ...
>>     }
>> }
>>
>> $reflClass = new \ReflectionClas("Foo");
>> $reflMethod = $reflClass->getMethod("bar");
>> $reflParameter = $reflMethod->getParameters()[0];
>>
>> var_dump($reflParameter->getAttributes());
>>
>>
>> I understood, we may add this ability later.
>>
>
> I'd say we should add this from day one.
> A quick use case that comes to my mind are parameters conversion that
> happens in Symfony2 through their "extra" bundle (doc:
> http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html
> ). In a controller action (it's a class method), you have the ability to
> convert the Request object into something else that makes more sense for
> you. Example:
>
> class UserController extends Controller {
>     public function viewAction(<<UserParameterConverter("userId")>> User
> $user = null) {
>         if ($user === null) {
>             throw new NotFoundException("User not found");
>         }
>
>         return ["me" => $this->getUser(), "user" => $user];
>     }
> }
>
>
>>
>>
>>
>>>
>>>
>>>
>>> 3- Did you put any thought on inheritance? What I mentioned in comment
>>> #1 is even smaller than what you implemented in RFC.
>>> Assuming you keep the RFC approach, did you consider support overrides,
>>> inherit, etc?
>>>
>>>
>>> In my opinion, attributes don't have to be inherited.
>>> If you think differently - please explain your point.
>>>
>>
>> Of source I can.
>> A simple case would be to increate visibility of the inherited property.
>> It was declared in a parent class as protected, but now you want public,
>> and you still want to keep all parent defined Attributes.
>>
>> Very questionable. If you redefine property, it shouldn't inherit
>> attributes.
>>
>
> This leads to some serious copy/paste, highly error prone... =(
>
>
>>
>>
>> Another example is like we do in Doctrine. We support a callback system
>> which we named as lifetime callbacks. Pre-persist is one of them, which is
>> called every time a given Entity is about to be persisted into DB. When
>> you're dealing with inheritance, you can potentially override the method
>> content and you still want to trigger the same operation as if it was
>> untouched. Example:
>>
>> use Doctrine\ORM;
>>
>> trait Timestampable {
>>     protected $created;
>>     protected $updated;
>>
>>     <<ORM\PrePersist>>
>>     public function prePersist() {
>>         $this->created = $this->updated = new \DateTime("now");
>>     }
>>
>>     <<ORM\PreUpdate>>
>>     public function preUpdate() {
>>         $this->updated = new \DateTime("now");
>>     }
>> }
>>
>> <<ORM\Entity>>
>> class User {
>>     use Timestampable;
>>
>>     public function prePersist() {
>>         // Add my custom logic
>>     }
>> }
>>
>> The implication is that through a simplistic approach, inheriting (or
>> overriding) is not clear and I can't figure it out an easy way to achieve
>> that.
>> Now if we go towards calling a function or class constructor like I
>> mentioned before, then we could easily build structures like __Inherit,
>> __Override, etc.
>>
>> It's definitely, not clear when attribute inheritance make sense and when
>> completely not. For example, if we mark some method to be JIT-ed, it
>> doesn't mean that we like to JIT methods of all children. So, I prefer not
>> to do inheritance at all. The higher layers may emulate "inheritance" of
>> some attributes their selves (like you do this with doc-comments).
>>
>
> As I said earlier, if you do a call based approach, we could create
> @Inherit or @Override, which would not only make us safe from support, but
> also gives more power to developers.
>
>
>>
>>
>>
>>
>>>
>>>
>>> 4- I understand that a more robust attribute solution would be required
>>> to achieve this, but one of the biggest advantages of AOP is the ability to
>>> perform custom logic before, after or around... However, I don't know if
>>> any kind of triggers came in your head or are planned as a future RFC.
>>> Let me highlight one example: Every time a class, property or method is
>>> called that is annotated as <<deprecated>>, I would like to issue an
>>> E_USER_DEPRECATED warning. A trigger-like solution would be required. Did
>>> this concept came to your mind?
>>>
>>> This is not a subject of this RFC.
>>> Attributes provides a storage for metadata, but don't define how to use
>>> them.
>>> Especially, for your use-case:
>>> 1) it's possible to create preprocessor that embeds corresponding
>>> trigger_error() call
>>> 2) it's possible to write a PHP extension that plugs-into compiler chain
>>> and checks <<deprecated>> attribute for each compiles function, then sets
>>> ZEND_ACC_DEPRECATED flag
>>> 3) It's also possible to override DO_FCALL opcodes and perform checks
>>> there (this is inefficient)
>>>
>>>
>> With this simplistic approach, I agree there's 0 value into considering
>> this.
>> However, taking a more robust approach would potentially open this
>> possibility through a simpler extension.
>>
>>
>> You saw, Sara named even this proposed solution a bit over-designed.
>> it make no sense to implement all functionality at language level.
>> Actually, keeping simple base interface, opens doors for more use-cases.
>>
>> Thanks. Dmitry.
>>
>>
>>
>>
>>
>>> Thanks. Dmitry.
>>>
>>>
>>>
>>>
>>>
>>> Regards,
>>>
>>> On Thu, Apr 21, 2016 at 7:44 PM, Dmitry Stogov < <dmi...@zend.com>
>>> dmi...@zend.com> wrote:
>>>
>>>>
>>>>
>>>> On 04/22/2016 02:16 AM, Dominic Grostate wrote:
>>>>
>>>>>
>>>>> This is amazing.  It would actually allow us to implement our
>>>>> automated assertions ourselves, as opposed to requiring it within the
>>>>> language.
>>>>>
>>>>> this was the idea - to give a good tool instead of implementing every
>>>> possible use-case in the language.
>>>>
>>>> Could it also support references?
>>>>>
>>>>> <<sanitize(&$a)>>
>>>>> function foo($a) {
>>>>>
>>>>> }
>>>>>
>>>>> yes. "&$a" is a valid PHP expression.
>>>>
>>>> If you plan to use this, I would appreciate, if you to build the
>>>> patched PHP and try it.
>>>> The early we find problems the better feature we will get at the end.
>>>>
>>>> Thanks. Dmitry.
>>>>
>>>>
>>>> On 21 Apr 2016 10:13 p.m., "Dmitry Stogov" <dmi...@zend.com <mailto:
>>>>> dmi...@zend.com>> wrote:
>>>>>
>>>>>     Hi,
>>>>>
>>>>>
>>>>>     I would like to present an RFC proposing support for native
>>>>>     annotation.
>>>>>
>>>>>     The naming, syntax and behavior are mostly influenced by HHVM
>>>>>     Hack, but not exactly the same.
>>>>>
>>>>>     The most interesting difference is an ability to use arbitrary PHP
>>>>>     expressions as attribute values.
>>>>>
>>>>>     These expressions are not evaluated, but stored as Abstract Syntax
>>>>>     Trees, and later may be accessed (node by node) in PHP extensions,
>>>>>     preprocessors and PHP scripts their selves. I think this ability
>>>>>     may be useful for "Design By Contract", other formal verification
>>>>>     systems, Aspect Oriented Programming, etc
>>>>>
>>>>>
>>>>>     https://wiki.php.net/rfc/attributes
>>>>>
>>>>>
>>>>>     Note that this approach is going to be native, in contrast to
>>>>>     doc-comment approach that uses not well defined syntax, and even
>>>>>     not parsed by PHP itself.
>>>>>
>>>>>
>>>>>     Additional ideas, endorsement and criticism are welcome.
>>>>>
>>>>>
>>>>>     Thanks. Dmitry.
>>>>>
>>>>>
>>>>
>>>
>>>
>>> --
>>> Guilherme Blanco
>>> Lead Architect at E-Block
>>>
>>>
>>>
>>
>>
>> --
>> Guilherme Blanco
>> Lead Architect at E-Block
>>
>>
>>
>
>
> --
> Guilherme Blanco
> Lead Architect at E-Block
>



-- 
Guilherme Blanco
Lead Architect at E-Block

Reply via email to