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

Reply via email to