[PHP-DEV] First-class constructor callables

Hello internals,

I’d like to explore the possibility of adding a first-class callable syntax for object constructors:

$factory = new Foo(...);

The idea is that this expression would produce a Closure which, when invoked, calls the constructor with the provided arguments:

$factory = new Foo(...);
$object = $factory($arg1, $arg2);

This would conceptually mirror the existing first-class callable notation for functions and methods: trim(…), $object->method(…), Foo::method(…).

Today, the equivalent form requires boilerplate such as:

$factory = static fn($arg): Foo => new Foo($arg);

or defining a static factory:

class Foo
{
public static function new($arg): self
{
return new self($arg);
}
}

$factory = Foo::new(...);

However, the latter is not possible for 3rd-party classes.

Question for Larry and Arnaud:

In PFA v2, you note that constructor references pose significant technical challenges.
Could you elaborate on what those challenges are and whether they are fundamental, or potentially addressable with a more limited or explicit syntax such as new Foo(...)?

You also mention that “the use cases for partially applying a constructor are few, especially now that we have lazy objects (as of PHP 8.4).” I tend to disagree. For cases like:

$numbers = array_map(new BcMath\Number(...), $numericStrings);

lazy objects do not help, and a constructor-as-callable form remains valuable.

Best regards,
Valentin

Hi Valentin,

On Thu, Nov 20, 2025 at 2:20 PM Valentin Udaltsov
<udaltsov.valentin@gmail.com> wrote:

Question for Larry and Arnaud:

In PFA v2, you note that constructor references pose significant technical challenges.
Could you elaborate on what those challenges are and whether they are fundamental, or potentially addressable with a more limited or explicit syntax such as `new Foo(...)`?

One issue is that partial application needs to resolve the function
being applied, but constructors can not be resolved without
instantiating the class first (at least, not on all classes). So `new
Foo(...)` would need to instantiate Foo, resolve the constructor, and
discard the instance, before creating the PFA.

This is something that could be addressed as a follow up to PFA v2.

Best Regards,
Arnaud

On Thu, Nov 20, 2025, at 8:21 AM, Arnaud Le Blanc wrote:

Hi Valentin,

On Thu, Nov 20, 2025 at 2:20 PM Valentin Udaltsov
<udaltsov.valentin@gmail.com> wrote:

Question for Larry and Arnaud:

In PFA v2, you note that constructor references pose significant technical challenges.
Could you elaborate on what those challenges are and whether they are fundamental, or potentially addressable with a more limited or explicit syntax such as `new Foo(...)`?

One issue is that partial application needs to resolve the function
being applied, but constructors can not be resolved without
instantiating the class first (at least, not on all classes). So `new
Foo(...)` would need to instantiate Foo, resolve the constructor, and
discard the instance, before creating the PFA.

This is something that could be addressed as a follow up to PFA v2.

Best Regards,
Arnaud

I will defer to Arnaud on the details. For my part, I have no conceptual objection to partialing constructors. The reason it was omitted from PFAv2 was basically "it's hard and complicated and there's enough going on here as is."

If someone else wants to do a follow up RFC for FCC/PFA on constructors, I would not be opposed.

--Larry Garfield

On Thu, Nov 20, 2025 at 3:21 PM Arnaud Le Blanc <arnaud.lb@gmail.com> wrote:

Hi Valentin,

On Thu, Nov 20, 2025 at 2:20 PM Valentin Udaltsov
<udaltsov.valentin@gmail.com> wrote:
> Question for Larry and Arnaud:
>
> In PFA v2, you note that constructor references pose significant technical challenges.
> Could you elaborate on what those challenges are and whether they are fundamental, or potentially addressable with a more limited or explicit syntax such as `new Foo(...)`?

One issue is that partial application needs to resolve the function
being applied, but constructors can not be resolved without
instantiating the class first (at least, not on all classes).

To clarify, constructors are normally resolved after creating an
object, by calling the get_constructor() handler on the object. The
handler isn't known before creating the object, at least for internal
classes, so we can't resolve a constructor without creating an object
first. This would have to be addressed in order to support partial
application of classes.

On Thursday, 20 November 2025 at 16:40, Arnaud Le Blanc <arnaud.lb@gmail.com> wrote:

On Thu, Nov 20, 2025 at 3:21 PM Arnaud Le Blanc arnaud.lb@gmail.com wrote:

> Hi Valentin,
>
> On Thu, Nov 20, 2025 at 2:20 PM Valentin Udaltsov
> udaltsov.valentin@gmail.com wrote:
>
> > Question for Larry and Arnaud:
> >
> > In PFA v2, you note that constructor references pose significant technical challenges.
> > Could you elaborate on what those challenges are and whether they are fundamental, or potentially addressable with a more limited or explicit syntax such as `new Foo(...)`?
>
> One issue is that partial application needs to resolve the function
> being applied, but constructors can not be resolved without
> instantiating the class first (at least, not on all classes).

To clarify, constructors are normally resolved after creating an
object, by calling the get_constructor() handler on the object. The
handler isn't known before creating the object, at least for internal
classes, so we can't resolve a constructor without creating an object
first. This would have to be addressed in order to support partial
application of classes.

Oh so one more reason to get rid of the get_constructor() handler then.
Would the approach that I started prototyping in
core: set zend_pass_function as default constructor by Girgias · Pull Request #19797 · php/php-src · GitHub help as we would know the
zend_function the moment we have the CE?

Best regards,

Gina P. Banyard

Hi Gina,

On Fri, Nov 21, 2025 at 4:47 AM Gina P. Banyard <internals@gpb.moe> wrote:

Oh so one more reason to get rid of the get_constructor() handler then.
Would the approach that I started prototyping in
core: set zend_pass_function as default constructor by Girgias · Pull Request #19797 · php/php-src · GitHub help as we would know the
zend_function the moment we have the CE?

This would help as long as the get_constructor() handler is also
removed (so that zend_class_entry.constructor is always the truth), or
moved to zend_class_entry.

Best Regards,
Arnaud

On 20 November 2025 13:19:09 GMT, Valentin Udaltsov <udaltsov.valentin@gmail.com> wrote:

I'd like to explore the possibility of adding a first-class callable syntax
for object constructors:

$factory = new Foo(...);

The idea is that this expression would produce a Closure which, when
invoked, calls the constructor with the provided arguments:

$factory = new Foo(...);
$object = $factory($arg1, $arg2);

Something worth clarifying here is that what you want is not just to *call the constructor*, but to *create the object*. In some languages, that's the same thing - there's a base object constructor which has to be called - but in PHP, the constructor itself is just a callback hook, called *after* the object exists.

And that's where the complexity comes in: "new Foo" isn't a function call, it's an interpreter operation.

Here is Nikita's explanation of the issue in the First Class Callable RFC <PHP: rfc:first_class_callable_syntax;

[Begin Quote]

The new Foo() syntax is not considered a call, and as such also not supported by the new Foo(...) syntax. It should be noted that there is also no way to express object creation with traditional callable syntax, and it is thus also not supported by Closure::fromCallable().

The general expectation is that new Foo(...) would be creating a new instance of Foo on each call, rather than creating a single instance of Foo and then repeatedly calling the constructor. To support this, it would effectively be necessary to generate a trampoline function of the form

fn(...$args) => new Foo(...$args)

and acquire a callable to that trampoline instead. While certainly possible, this takes a step backwards from the straightforward semantics of the foo(...) syntax for ordinary calls.

[End Quote]

Rowan Tommins
[IMSoP]