[PHP-DEV] SVN Account Request: dominis
Maintaining the documentation Maintaining an official, bundled PHP extension -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Extensions to traits
In fact, this is so nice, could I suggest it would be nice to allow other delegation-like forwarding to be done like this? You could have 'use' without a trait even, just like this: use { $queue->add as addToQueue; } Since the properties' object wouldn't be available at compile time, this extra ability would probably have to be implemented by basically generating an addToQueue method, and it wouldn't work with arguments passed by reference. It would basically be a shorthand for public function addToQueue() { return call_user_func(array($queue,'add'),func_get_args()); } but much more elegant. Of course should have been public function addToQueue() { return call_user_func(array($this->queue,'add'),func_get_args()); } Ben. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Extensions to traits
Hi, Stefan, I think if the other stuff goes ahead, we can probably scrap what I originally proposed here. But perhaps something else would be helpful. I won't comment very specifically on aspects of my original proposal, but will just raise some new ideas for consideration and further thought. It all hinges on aliasing: how it works and how it can be avoided when necessary. Your comment on my example made this most clear. abstract class ActiveRecord { protected $new; protected $id; protected $other_values; protected function __construct($id,$values,$new) { $this->id=$id; $this->other_values=$values; $this->new=$new; } public function save() { if ($this->new) { if (!create_in_the_database()) return false; if ($this->id===null) $this->id=last_insert_id(); } else { if (!update_in_the_database()) return false; } return true; } public static function new() { return new static(null,static::$default_values,true); } public static function get($id) { return new static($id,get_from_the_database(),false); } } trait LoggingOperations { public function save() { if ($this->new) { log("Creating ".get_called_class()); } else { log("Updating ".get_called_class()." ID ".$this->id); } if (!prev::save()) { log("Failed"); return false; } log("Succeeded"); return true; } } trait EnsuringNoConcurrentChanges { trait $original_values = array(); protected function setOriginalValues($values) { trait::$original_values = $values; } public static function get($id) { $record = prev::get($id); $record->setOriginalValues($record->other_values); return $record; } public function save() { $current_values=select_from_database(); if ($this->new&&$current_values) return false; if (!$this->new&&!$current_values) return false; if ($current_values!=trait::$original_values) return false; return prev::save(); } } trait UsingHashesForIDs { public function save() { if ($this->id===null) $this->id=random_hash(); return prev::save(); } } class SessionRecord extends ActiveRecord { protected static $default_values=array( 'user'=>'', 'time'=>'' ); use UsingHashesForIDs; } class Client extends ActiveRecord { protected static $default_values=array( 'user'=>'', 'name'=>'', 'address'=>'' ); use EnsuringNoConcurrentChanges, LoggingOperations { save = EnsuringNoConcurrentChanges::save, LoggingOperations::save; } } Ok, that example is, well, not actually helping you. You can do all the things here without your extensions, I believe. Before commenting speciically I'd like to point out that whether something *can* be done shouldn't be the only thing considered. Other important aspects are how easily it can be done, how tidily it can be done, how much code duplication it requires, and so on. Sometimes, a construct by merely providing elegance, without any additional power, is worthwhile. In fact, you *can* model any program as a Turing machine, so we actually need very little; but there aren't many popular languages like that! In this case, though, I think there are a couple of shortcomings in the current trait behaviour that mean this can't quite be done reliably. One thing of note is the use of the trait-scoped property, making the trait more robust. That was the subject of an earlier email, and one of the parts of the proposal you viewed most favourably, so I'm happy about that. I won't dwell on it here. Your problem with the save methods is solved by aliases and parent:: (parent:: only has semantics with respect to inheritance, so it will do the right thing. Your Client will have a save method that calls the aliased versions of the saves in the order you want. And in get() you can also just use parent::, no? Aliases come a long way, and to be honest, I am still coming to terms with how powerful they are, particularly in combination with traits' ability to have abstract methods. But there is at least one missing link. You can't reliably use parent:: in a trait method, because that jeopardises the reusability of the trait. In some composing classes, you might want it to call a method that isn't from the superclass. In this minimal example, parent:: happens to work; but that shouldn't be relied upon when writing a trait, and in the generalised case, where there are more traits and more composition combinations, it would fall apart. This means at the very least, you would have to use a method declared in the trait, and then write forwarding methods.
Re: [PHP-DEV] Extensions to traits
Hi again, Stefan, Continuing the conversation. On 7/01/11 10:18 AM, Stefan Marr wrote: On 02 Jan 2011, at 13:16, Ben Schmidt wrote: Extension - - - - - I suggest these two problems can be simply solved by introducing two additional uses of the trait keyword: as a scoping keyword and an access specifier. As a scoping keyword, it would be used analogously to self. Method calls such as $this->print() could be replaced with trait::print() when the programmer desires to ensure that their trait method, and only their trait method, is called--when there is no intention that overriding should be possible. It would only be able to be used in a trait, and could only be used to reference methods or properties defined in the same trait, using their original name. As an access specifier, it would be used instead of public, private, etc. in trait definitions, to mean that the member (data or method) can and can only be accessed using the mechanism above (trait::). Ok, that would actually get us around all the meta-programming problems. When you say that the 'trait'-access modifier always requires the access via a specific keyword (trait::) then mangling the name should be possible. On the other hand, what would iterating over the object properties show? Multiple properties with the same name, like with inherited private properties I suppose? Well, I hadn't thought about it before. :-) But yes, I think that makes perfect sense. And an occurrence of trait:: would mean, do a $this-> but mangle the name first with the trait's name the original definition was in. Should be possible, but would certainly impact the Zend Engine a bit more than what we have now. One complication I hadn't thought of before is whether it should be possible to access trait:: methods and properties in different objects. And if it should be, what syntax to use. $that->trait::method() seems somewhat ugly to me. That would also suggest we should use $this->trait::method() for the same-object case. Certainly an interesting approach. Has someone else an opinion on that? I think actually this is the most important part of my proposal, so if it could be accepted, I would be very pleased. Obviously it does need a bit more thought and discussion yet, though. Overriding == Limitation -- At present, the overriding semantics of traits are that a method defined in a class proper overrides a method defined in a used trait which in turn overrides a method defined in an ancestor class. Bye the way, where comes that terminology from: class proper? We are talking about the body of a class, right? Yes. 'Class proper' = 'class itself'. We seem to use 'proper' in English as an adjective after a noun with a Latin-like sense of 'own/itself'. I guess we probably got it from French, and surprisingly it hasn't died out. Cheers, Ben. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Extensions to traits
Hi, Stefan, Thanks for considering my ideas so carefully and for your detailed replies. The reason to go with insteadof is that the assignment syntax hides changes that might cause problems. Thus, when you change any of the traits participating in a composition in a way that a conflict would be introduced, the assignment syntax is hiding this. With the insteadof syntax you are actually forced to reevaluate whether your code still makes sense and include the offending change explicitly. OK. That makes sense. I'll rework any surviving parts of my proposal to be based on the insteadof syntax. This does not only have the mentioned benefit of making problematic changes in the traits hierarchy explicit, but the second reason to go with this design was the wish to have a 'exclude' operator without actually having a full-exclude operator. Thus, there is no way that you can leave out arbitrary methods from a trait. This seems wrong to me. Doesn't it go against the stated principle that the class author should have full control of traits? How is it full control if you can't exclude a method? What is the reasoning behind this wish not to have a full exclude operator? Hm, don't understand you here either. I needed to look up what impoverished actually means, you are not insulting me here, right? Just kidding ;) Sorry. I forget that a lot of you people aren't native English speakers and sometimes funny words like that slip in! I think all you people who communicate so fluently in additional languages are amazing. No error or warning will be generated, but the class will not work as intended; probably it will infinitely recurse; a nasty problem to track down. Furthermore, even if the incorrect method call didn't occur, there would be data-sharing problems, as both the ErrorReporting trait and the class' print() function make use of the $output data member, unintentially sharing data. Well, short answer: it is compiler-assisted copy-and-past, why isn't that trait just providing the glue that gets your class the necessary functionality to use a proper ErrorReporting class? Sorry, I know not a really helpful answer, but I hope that examples shows how I see traits. They are for reusing code in situations where the other concepts just break down. Not meant to replace those. I agree. I did say it was a poor example. I think the kinds of behavioural problems it demonstrates, though, could be found in situations where traits *are* truly appropriate. No need to argue over examples, though. Plenty of other things to argue about. :-) Warnings - - - - To avoid silent unintended shadowing, I suggest issuing a warning when a conflict between trait and class methods occurs. So this would trigger a warning: trait SaySomething { public function sayIt() { echo "Something\n"; } } class Sayer { use SaySomething; public function sayIt() { echo "Hello world!\n"; } } Ok, well, we could see the actual class body as another trait, and require it to follow the same composition rules, and that way make require the programmer to use insteadof in such a case. In return that would mean, that traits are not part of the inheritance chain at all anymore. Thus, first is the inheritance chain used to build up the implementation of a class, and afterwards all the traits are composed, while the original class is seen as another trait. That idea has certainly something to it. Yes, I think that would be good. Having read your other emails, as well as having allowed my proposal itself to clarify in my mind, I'm beginning to think it might be clearest and cleverest to avoid inheritance altogether with traits. Of course, this is in contrast to my original proposal, which was to increase traits' participation in inheritance. In this case, parent:: will always do what you expect then, and there's no need for prev::. Now to the other emails! Ben. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Extensions to traits
Hi, Jonathan, On 7/01/11 4:42 AM, Jonathan Bond-Caron wrote: - New ideas are always welcome That's great to hear! - Don't write long e-mails to a mailing list, write an RFC http://wiki.php.net/rfc?do=register Sure. Once I've digested and responded to Stefan's replies, I'll work on putting something up there. Perhaps this advice would be worth adding to the mailing list rules page, as nothing about post length is mentioned there at all; it only talks about attachments. - Don't overuse the word 'simple' IMHO, if something is simple it should take 1-2 days to create a patch. That doesn't seem to be the case with what you're proposing. But feel free to prove me wrong :) Yeah, well, I meant simple conceptually, and relatively speaking, not that it wouldn't take time and effort. I don't think I'll be proving you wrong! But as I said, I am willing to get my hands dirty with this. Creating a patch will help getting feedback about what you're proposing http://ca3.php.net/reST/php-src/README.MAILINGLIST_RULES I hope I haven't broken any of the mailing list rules, but my apologies if I have, and please point out specifically where I've gone wrong. As far as a patch goes, I don't think that is appropriate at this stage. I don't want to spend a lot of time creating a patch only to have it rejected if this could be avoided by a little discussion. My time is too valuable to me to waste like that, and with something as controversial as this, there's a real danger of that happening. Cheers, Ben. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] RFC: about class names as values
I think doing something like this is a good idea for classes and interfaces. Ben. On 7/01/11 1:16 AM, Martin Scotta wrote: Yes, my intention was to only add a magic constant with the class, similar to this namespace Bar { class Foo { const KLASS = __CLASS__; } } namespace Buzz { use \Bar\Foo as BazFoo; class Bar extends BazFoo { const KLASS = __CLASS__; } $bar = new Bar; $baz = new BazFoo; var_dump( get_class($baz), BazFoo::KLASS); var_dump( get_class($bar), Bar::KLASS ); } This is 100% valid PHP 5.3.3 code, but that includes a lot of effort from the developer. Someone miss to include the KLASS constant on a class and the result is undefined. If that PHP could add a magic constant --named CLASS or whatever you like-- to each class it will reduce the amount of class names hardcoded onto strings, probably to zero. The only issue that I found today is related to interfaces. I'm not sure if they should include this sort of magic constant, but I would rather include them just for consistency but, as I previously said, I'm not sure about this one. Martin Scotta 2011/1/5 John LeSueur 2011/1/5 Johannes Schlüter On Wed, 2011-01-05 at 21:53 -0300, Martin Scotta wrote: $obj = newInstance( MyClass ); // notice. undefined constant MyClass This describes the major change with your idea. What happens if a constant MyClass exists? Another question is something like this: To proper support this we'd have to make classes first class elements. For making this consistent it would make sense to make functions first class elements. And best drop the $ in front of variables and create a new language. Everything else becomes a mess. johannes -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php I think he's actually proposing creating for each class the magic class constant CLASS, so your example becomes: This is actually doable without a magic class constant, but requires a function or class constant to be declared in each class. Perhaps this could be simplified with traits, if __NAMESPACE__ and __CLASS__ work in traits that way. In fact, that's an interesting question, what is __NAMESPACE__ in a trait defined in one namespace, then used in a class in a different namespace? I think the point is that the factory function could exist without any knowledge of the namespaces of the classes it would work on. Then, somewhere else where the class has been aliased or is otherwise accessible without the full namespace path, the developer wouldn't need to specify the full namespace path to the factory, but could ask the class itself what it's full namespace path was. I don't know that I agree with the idea, but I don't think it requires making classes first class elements. John -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php