Initiating a class from string with extra step?

后端 未结 2 1884
梦毁少年i
梦毁少年i 2021-01-23 19:01

I\'ve been looking into substantiating a new class instance from a string in PHP. This is seems to be an acceptable procedure, however I am curious why it can\'t be done with th

相关标签:
2条回答
  • 2021-01-23 19:28

    As stated in the PHP Manual the new keyword accepts a string or an object as a class name. Optionally you can provide arguments to the constructor in parentheses at the end.

    What this means is that the syntax roughly expects:

        new         [string|object]     ()
    //  ^^ keyword  ^^ name             ^^ optional parentheses
    

    Another important fact is that the keyword new has the highest precedence of all operators.

    Combining the two facts together means that we cannot use parentheses to increase the precedence of the operation with new keyword. If we use parentheses PHP will treat them as the optional part of new syntax. Something like this won't work.

    $bar = new   ($foo->callBar());
    //         ^ missing class name
    

    There is just no unambiguous way of telling PHP parser to treat this otherwise.

    There is also another caveat worth remembering, which Edward Surov has partially mentioned in his answer. The class name can come from any variable which is a string or an instance.
    The following code will create an object of class Bar:

    $obj = ['a'=>'Bar'];
    $bar = new $obj['a'];
    // or
    $obj = (object) ['a'=>'Bar'];
    $bar = new $obj->a;
    

    So, let's explain what your code does.

        new         $foo->callBar     ()
    //  ^^ keyword  ^^ name           ^^ optional parentheses
    

    Because your Foo class doesn't have a property callBar it will trigger Notice message, but PHP will check if it can be used as a class name anyway. And because it doesn't exist it can't be a string or an instance, which is why you see the error.

    This has been fixed in PHP 8.
    The Variable Syntax Tweaks RFC addressed this issue. Now you can use expressions when creating an instance of a class. The only thing to pay attention to is the operator precedence. When calling function/method you should use () to denote precedence. For example:

    $bar = new ( $foo->callBar() )  ();
    //           ^^ expression      ^^ optional parentheses
    
    0 讨论(0)
  • 2021-01-23 19:42

    There are exactly four options of creating a class instance using new that I know:

    • Name class directly: $bar = new Bar;
    • Use variable directly: $barName = 'Bar'; $bar = new $barName;
    • Use object property: $obj = (object) ['barName' => 'Bar']; $bar = new $obj->barName;
    • Use existing instance: $bar1 = new Bar; $bar2 = new $bar1;

    First half of an answer to your question is PHP parser: it prohibits many things like new (Foo) and new "Foo" that could used to build a hack.

    Second half may hide in PHP sources: here's the C function that throws that exception.

    0 讨论(0)
提交回复
热议问题