Re: property de-referencing
| From: | Matthieu Napoli | Date: | Tue, 07 May 2013 16:01:10 +0000 |
| Subject: | Re: property de-referencing | ||
| References: | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-67331@lists.php.net to get a copy of this message | ||
I am really surprised that there is so few people interested in such a feature. IMO, it's an amazing idea, not just for forms, and not just for properties.
Here is a quick list of use cases where one would use "meta" on its code:
- Forms generation (examples have already been given)
- ORM mapping (Doctrine for example)
- DTO mapping (mapping some objects to other objects/data structures)
- Dependency Injection configuration (defining dependencies)
- Routing (mapping routes to controller class methods)
- AOP
- …
Another example: the PropertyAccess component of Symfony (http://symfony.com/blog/new-in-symfony-2-2-new-propertyaccess-component).
Here is what we can use today for doing some "meta":
- string references (PHP strings, XML, YAML…)
- annotations
If you have worked with ZF, Symfony, Doctrine, … you know that working with strings make refactoring, code completion and static analysis very difficult!
In modern frameworks, there are more and more annotations and config files, and that's a sign IMO: that's a sign that we do a lot of meta. And having a language feature for doing that (as an alternative, not a replacement for current solutions) would be amazing.
Here are some examples with some random syntax (probably problematic but that's not the point) to illustrate:
// Property
$propertyReference = ^$article->name; // returns a PropertyReference
$propertyReflection = ^My\Article::name // returns a ReflectionProperty
// Method
$methodReference = ^$article->getName(); // returns a MethodReference
$methodReflection = ^My\Article::getName(); // returns a ReflectionMethod
// Form generation
$form->addInput(^$article->name);
// ORM mapping
$class->mapField(^Article::name, 'string');
// DTO mapping
$mapper->map(^$article->name, ^ArticleDTO::label);
// DI configuration
$container->bind(^My\ServiceInterface)
->to(^My\Service)
->withSetter(^My\Service::setFoo(), ^My\Foo);
// Routing
$app->get('/hello/{name}', ^$myController->helloAction());
// AOP
$aop->beforeMethod(^$myObject->someMethod(), function() {
echo "Hello World!";}); In all those examples, you can refactor your code, find usages of properties, traverse call hierarchy, have 100% code completion, have better code analysis… PS: please please don't mind the syntax of the examples, let's keep the discussion on the idea for now. Le 07/05/2013 16:32, Rasmus Schultz a écrit :
And what do good developers do when the best ways have long since been identified - and the limitations of the language prevents them from implementing any new ideas? I have hundreds of PHP experiments collected in a sandbox over the years - a good way to build and handle web-forms is one of the last things I have not found a satisfying solution to, and I am not happy with what anybody else has come up with either. At some point, it's natural to start asking why, comparing to other languages, etc. - short of inventing DSLs, I have not seen much that really impresses me. I don't know, maybe I should just let that one go and accept that it's always going to be crap. Maybe the problem in the first place is trying to drive the client-side from the server-side, and maybe client-side frameworks is the right way to go - put the UI abstraction in the UI rather than on the server. We'll always need a few web-forms, I think, but maybe it's time to stop struggling for better server-side form handling and start moving towards fully client-side UI... (sorry if I'm going off on a tangent here - just sharing some of the thoughts that lead me down this path to begin with...) On Tue, May 7, 2013 at 9:41 AM, Seva Lapsha <seva.lapsha@gmail.com> wrote:Good developers research and find *best* ways to use the available tools before inventing new ones. On Mon, May 6, 2013 at 2:46 PM, Rasmus Schultz <rasmus@mindplay.dk> wrote:Well, I don't disagree as such - there's any number of (mostly bad) ways to work around missing language features... On Mon, May 6, 2013 at 1:12 PM, Seva Lapsha <seva.lapsha@gmail.com>wrote:BTW, I didn't propose to wrap any use of a property reference into a meta object, in this case a certain distinguishable string format could represent it with no extra handling. On Mon, May 6, 2013 at 12:44 PM, Rasmus Schultz <rasmus@mindplay.dk>wrote:Seva, I understand that you can reference properties more consistently using "{fullClassName}::{fieldName}" notation, but it's still a string, and although it's now almost practically safe to assume that strings formatted in that way are property-references, it still doesn't address the problem in a way that is elegant or expressive. I don't think the Symfony component could have done a much better job under the circumstances, at least not without the sacrifice of readable code - typing out new PropertyReference($object, 'User::$name') sure would be clunky, and not even really safe, since you can't guarantee that the class-name of $object is known, and in every property-reference, the User class-reference is now embedded statically in every property-reference, in the form of a string. I think this is a good example of those times when PHP developers tend to look far, far away from Java - as far away as possible - for solutions that are elegant and a good fit for PHP. new PropertyReference($object, 'User::$name') contains two static references too many, to both PropertyReference and User. As opposed to ^$user->name which contains the minimum amount of required information - the object and property-name, nothing else. On Mon, May 6, 2013 at 12:08 PM, Seva Lapsha <seva.lapsha@gmail.com>wrote:Hi Rasmus, I agree with you that strings are not the best way to refer to an element sometimes. However, to me your Symfony2 example only demonstrates the flaw of the component's design decision, not the limitation of the language. Sometimes developers (not just Symfony, but other frameworks too) don't hesitate to use contextless strings to refer to meta-data, because they underestimate the importance of keeping static referability of static entities. If they would use conventional full notation of references, e.g. "{fullClassName}::{fieldName}" in a string, this would solve your initial problem (and allow static analyzers which could be aware of the context of the framework to do their job). This is how these kind of dilemmas are solved in the world of Java for instance, where property references don't exist too. Regards, Seva On Tue, Apr 30, 2013 at 6:24 PM, Rasmus Schultz <rasmus@mindplay.dk>wrote:Any PHP dev who works with a mainstream framework does this daily, but the frameworks rely on strings for property-names. Take this example from the Symfony manual, for example:class Task { protected $task;protected $dueDate;public function getTask() { return $this->task; } public function setTask($task) { $this->task = $task; }public function getDueDate() { return $this->dueDate; } public function setDueDate(\DateTime $dueDate = null) { $this->dueDate = $dueDate; } }$form = $this->createFormBuilder($task) ->add('task', 'text') ->add('dueDate', 'date') ->getForm();In this example, 'task' and 'dueDate' are property-references - except of course that, no, they're not - they're obviously just strings... rewriting this example to use a (fictive) form builder API with static property-references:$form = $this->createFormBuilder() ->add(^$task->task, 'text') ->add(^$task->dueDate, 'date') ->getForm();We now have static property-references, which means the codebase can be proofed using static analysis, which also means better IDE support with property auto-completion, inline documentation, and automatic refactoring for operations like renaming properties, etc. Note that $task need not be passed to createFormBuilder() anymore - instead, we can now use PropertyReference::getObject() inside the form-builder to obtain the instance. For that matter, we can now scrap the form-builder entirely and introduce a simple form-helper in the view instead:Task name: <?= $form->textInput(^$task->task) ?> Due Date: <?= $form->dateInput(^$task->dueDate) ?>This is even better, because we now have the same level of IDE support and static analysis for textInput() and dateInput() which were previously unchecked strings. Or even simpler:Task name: <?= $form->input(^$task->task) ?> Due Date: <?= $form->input(^$task->dueDate) ?>Using PropertyReference::getObject() and reflection inside the form-helper's input() method, we can now use property-annotations to specify the input-type. This is a matter of preference of course, but use of annotations in Symfony is pretty popular. This is just one example - most PHP devs (at least those who do PHP for a living) use form abstractions and object/relational-mappers of some sort, so this has practical applications for practically everyone, everywhere. Rasmus Lerdorf wrote: It is certainly not worth overloading the XOR operator for Are we really going to quibble about syntax? This adds nothing to this discussion. And as I explained earlier, the ^ operator is used for the sake of discussion only - if it's more practical to use another character for this operator, I don't care what it looks like. On Tue, Apr 30, 2013 at 4:58 PM, Stas Malyshev < smalyshev@sugarcrm.com>wrote:Hi!property -I'm proposing we need a way to statically reference an objectItthe object property itself, not it's value:You probably have use case for that, and it should be pretty easy to write a class that does that, but why it should be in the language?certainly doesn't look like something sizeable portion of PHP devswoulddo frequently. -- Stanislav Malyshev, Software Architect SugarCRM: http://www.sugarcrm.com/ (408)454-6900 ext. 227