On 2016-05-31 22:15, Jesse Schalken 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 = ...,
> },
> }
> );
Is there anything wrong with casting an array to an object? The syntax
is almost identical to what you want to achieve.
I can think of three ways to achieve, with the current PHP stable, what
you seem to desire (forgive me if I misunderstood). Your code becomes:
$this->fooMethod(
$arg1,
$arg2,
(object)[
'prop1' => ...,
'prop2' => ...,
'prop3' => (object)[
'prop1' => ...,
'prop2' => ...,
},
}
);
Pretty concise and close to your proposal, right?
Another option is anonymous classes
$this->fooMethod(
$arg1,
$arg2,
new class {
var $prop1 => ...,
var $prop2' => ...,
var $prop3' => new class {
var $prop1' => ...,
var $prop2' => ...,
},
}
);
Or if you need a specific class name for validation or anything:
$this->fooMethod(
$arg1,
$arg2,
new class extends FooClass {
public $prop1 => ...,
$prop2' => ...,
$prop3' => new class extends FooBar {
public $prop1' => ...,
$prop2' => ...;
},
}
);
Last option is a generic class with a __construct taking an array and
creating a populated instance, which can be used as a base/trait to
extend dumb classes that only require a name. But you've covered that
already.
Cheers,