Re: Set object properties inline
| From: | Peter Cowburn | Date: | Wed, 01 Jun 2016 09:49:17 +0000 |
| Subject: | Re: Set object properties inline | ||
| References: | 1 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-93667@lists.php.net to get a copy of this message | ||
On 1 June 2016 at 03:15, Jesse Schalken <me@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 = ...,
> },
> }
> );
>
While it's not as concise as your example, anonymous classes can do this
already after a fashion.
$this->fooMethod(
$arg1,
$arg2,
new class() extends FooParams {
// Constructor because prop3's value isn't a constant expression
function __construct() {
$this->prop1 = '...';
$this->prop2 = '...';
$this->prop3 = new class() extends Obj1 {
public $prop1 = '...';
public $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
>