On Tue, May 31, 2016 at 7:15 PM, Jesse Schalken <m...@jesseschalken.com> wrote:
> Hi internals, > > I often have code dealing with plain old PHP objects with properties and no > methods, either as a substitute for keyword arguments, or to represent a > JSON or YAML document for a web service, configuration file or schemaless > database. > > At the moment, instantiating an object and setting public properties > requires a temporary variable for each object in the structure: > > $obj1 = new Obj1(); > $obj1->prop1 = ...; > $obj1->prop2 = ...; > > $params = new FooParams(); > $params->prop1 = ..; > $params->prop2 = ...; > $params->prop3 = $obj1; > > $this->fooMethod($arg1, $arg2, $params); > > > For large structures, this gets verbose very quick. There is a good example > of this here > < > https://github.com/jesseschalken/fail-whale/blob/72870b37c4c21d19f17324a966344ec476b432a7/src/FailWhale/Introspection.php#L22 > > > involving > 18 unnecessarily variables. > > I can remove the local variables by defining setters for all the > properties: > > $this->fooMethod( > > $arg1, > $arg2, > (new FooParams()) > > ->setProp1(...) > > ->setProp2(...) > > ->setProp3((new Obj1()) > > ->setProp1(...) > > ->setProp2(...)) > > ); > > > But now for each property I have to spend an extra 3-5 lines of code > defining a setter, which is more code than it saved (unless the class is > used heavily enough). > > I could define __construct() taking every property as a parameter, but then > each property has to be mentioned another three times (four times with a > doc comment), and the type twice: > > class FooParams { > > public int $prop1; > // ... > > > public function __construct( > int $prop1 > // ... > ) { > > $this->prop1 = $prop1; > // ... > > } > > } > > > and where the object is constructed, it isn't immediately visible what the > meaning of each positional parameter is without some IDE assistance (eg > Ctrl+P in PhpStorm), and only specifying some parameters requires filling > preceding ones with defaults. > > I could also define the __call() method to automatically expose setters, > but then IDEs and static analysis can't understand what's going on (it > can't see that those methods exist, what their parameters are and what they > return). @method doc comments on the class help, but that's another line > for every property which I have to manually keep in sync with the real > properties. > > It would be great if there was a simple shorthand syntax for setting > properties on an object in-line, without needing to extract a variable: > > $this->fooMethod( > $arg1, > $arg2, > new FooParams() { > prop1 = ..., > prop2 = ..., > prop3 = new Obj1() { > prop1 = ..., > prop2 = ..., > }, > } > ); > > > This way the structure can be written directly in the code as an expression > and FooParams and Obj1 remain simple containers for properties. > > The grammar might look like (I haven't used bison/yacc before): > > expr_without_variable: > /* ... */ > | expr '{' inline_set_properties '}' > ; > > inline_set_properties: > /* empty */ > | identifier '=' expr > | identifier '=' expr ',' inline_set_properties > ; > > > (Although I think that would conflict with the alternative $var{8} syntax > for array/string offset.) > > Has this been explored before? What problems can you foresee (or have been > foreseen) with such a feature? > > Thanks > Hi Jesse, It's fairly straightforward to implement a function that does this in userland, for example something like the following: function f($fqcn, $args) { $instance = new $fqcn; foreach ($args as $key => $value) { $instance->$key = $value; } return $instance; } ... $this->fooMethod( $arg1, $arg2, f('FooParams', [ 'prop1' => ..., 'prop2' => ..., 'prop3' => f('Obj1', [ 'prop1' => ..., 'prop2' => ..., ], ]) ); You may also use the approach Peter suggested. As such, I don't think introducing a new syntax for it is necessary. Best, --Matt