Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

object-bound closures for initalization, modification of objects #17936

Open
procodix opened this issue Feb 26, 2025 · 6 comments
Open

object-bound closures for initalization, modification of objects #17936

procodix opened this issue Feb 26, 2025 · 6 comments

Comments

@procodix
Copy link

procodix commented Feb 26, 2025

Description

Objects (and nested ones) are often constructed with local variables:

$o = new Element();
$o->property1 = 'some string';
$o->property2 = new OtherObject();
$o->property2->property = 'some other string';

This can also be achieved by constructor parameters:

$o = new Object('some string', new OtherObject('some other string'));

Can this be done more elegantly?

Problem arises, when we have objects with too many properties. Of course we can use leverage properties and named arguments, but the constructors declarations become lengthy. Even more since we can also define property hooks within the promoted properties. But things get even more complicated, with inheritance and constructing objects with a mixture of ordered params, named params and even variadic params to be passed - for example - into the parent constructor.

Example is the creation of HTML DSL:

new Element(content:"test", classes:"something", elements:[
    new Input(name:"whatever", value:"somevalue", classes:"someclasses", disabled:true),
    //...
];

This gives me some problems.

  1. Introducing a new property: it requires a lot of constructor changes if one avoids variadic arguments passing - which conflicts with ordered parameters often.
  2. boilerplate code inside constructor executes for every property, even if it is not needed (yet)
  3. properties cannot be modified on-the-fly, just assigned.

Closures can solve this, but they are bulky, especially the non-shorthand version:

class Element {
    public function modify(Closure $fnc):static {$fnc($this); return $this;}
}

new Element()->modify(function(Element $e) {
    $e->classes += ' some more classes';
    $e->elements->replace('id3', new Input()->modify(function(Input $i) {
        $i->value = 'new content';
        $i->readonly = false;
    }));
});

Can we have a smoother syntax for this:

new Element({
    $this->classes += ' some more classes';
    $this->elements->replace('id3', new Input({
        $this->value = 'new content';
        $this->readonly = false;
    });
};

Maybe an operator would make sense to execute a block on an object's context:

new Element() <- {...}
new Element(<- {...})
$e <- {
   ...
}
@iluuu1994
Copy link
Member

You're looking for something like this proposal, which was declined. https://wiki.php.net/rfc/compact-object-property-assignment

@procodix
Copy link
Author

Not really. The proposal you mention is just for assignments. This has no added value over default values in constructors. Im not suggesting some limited functionality inside the block.

In my example one could write all PHP code to init or modify the object. More like a configuring closure. Difference to current possibilities is the sleeker syntax and rebinding of the "this" variable to save the declaration of a temporary variable.

@iluuu1994
Copy link
Member

I'm not sure why we need to reinvent the wheel. https://3v4l.org/kXGtb

Do you know of any other language that offers a similar feature?

@procodix
Copy link
Author

Hm, I feel the syntax should be easier. Also, IDEs will have problems, detecting what $this refers to, for example:

$this->subelement->modify(fn() => $this->whatever()); 

Swift uses implicit constructors, where you have full access to all language features. In PHP, promoted constructors only allow constant statements and new (method arguments allow even less as default).

Nim is very capable of allowing individual DSLs, but their brevity is due to blocks-by-indentation.

@iluuu1994
Copy link
Member

Also, IDEs will have problems, detecting what $this refers to

Most IDEs support generic templates now, although granted they probably won't support @param-closure-this I don't think.

https://phpstan.org/r/8a93650f-c7b9-4b19-b7bd-615f3a588c98

I'm not sure making the closure referring to $this is great anyway, as that makes it impossible (or unnecessarily hard) to access the original scope, including methods you may want to potentially call.

In PHP, promoted constructors only allow constant statements and new (method arguments allow even less as default).

Method default arguments allow less than property default values? I don't think that's true. E.g. new is only allowed as arguments, and as promoted arguments' default values are only used as constructor default args, it doesn't seem like this would add any additional restrictions.

@iluuu1994
Copy link
Member

So, this feature request is very vague, and out of scope for GH issues anyway. As mentioned in other issues, please raise a discussion on the internals mailing list. Feature requests on GH that require RFCs (as this one) auto-close after 3 months, so if you want to push this forward, a discussion with the wider community will achieve more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants