为什么PHP属性不允许使用函数?

为什么PHP属性不允许使用函数?,第1张

为什么PHP属性不允许使用函数?

编译器代码建议这是设计使然,尽管我不知道其背后的官方原因是什么。我也不确定要可靠地实现此功能需要花费多少精力,但是目前完成工作的方式肯定存在一些限制。

尽管我对PHP编译器的了解并不广泛,但我将尝试说明我认为的情况,以便您可以发现问题所在。您的代码示例非常适合此过程,因此我们将使用它:

class Foo {    public $path = array(        realpath(".")    );}

如您所知,这会导致语法错误。这是PHP语法的结果,该语法进行了以下相关定义:

class_variable_declaration:       //...      | T_VARIABLE '=' static_scalar //...;

因此,在定义变量值(例如)时

$path
,期望值必须与静态标量的定义匹配。毫不奇怪,考虑到静态标量的定义还包括其值也是静态标量的数组类型,这有点用词不当:

static_scalar:       //...      | T_ARRAY '(' static_array_pair_list ')' // ...      //...;

让我们先假设语法是不同的,并且类变量delcaration规则中的注释行看起来更像以下内容,它将与您的代码示例相匹配(尽管会破坏有效的赋值):

class_variable_declaration:       //...      | T_VARIABLE '=' T_ARRAY '(' array_pair_list ')' // ...;

重新编译PHP之后,示例脚本将不再因该语法错误而失败。相反,它将因编译时错误 “ Invalid binding type”
而失败。由于代码现在基于语法是有效的,因此这表明实际上在编译器设计中存在某些特定问题,这些问题会引起麻烦。为了弄清楚是什么,让我们暂时恢复原始语法,并假设代码示例的有效分配为

$path= array( 2 );

使用语法作为指导,可以在解析此代码示例时逐步浏览编译器代码中调用的 *** 作。我省略了一些不太重要的部分,但是过程看起来像这样:

// ...// Begins the class declarationzend_do_begin_class_declaration(znode, "Foo", znode);    // Set some modifiers on the current znode...    // ...    // Create the array    array_init(znode);    // Add the value we specified    zend_do_add_static_array_element(znode, NULL, 2);    // Declare the property as a member of the class    zend_do_declare_property('$path', znode);// End the class declarationzend_do_end_class_declaration(znode, "Foo");// ...zend_do_early_binding();// ...zend_do_end_compilation();

尽管编译器在这些各种方法中做了很多事情,但重要的是要注意一些事情。

  1. 呼叫会
    zend_do_begin_class_declaration()
    导致呼叫
    get_next_op()
    。这意味着它将向当前 *** 作码数组添加新的 *** 作码。
  2. array_init()
    并且
    zend_do_add_static_array_element()
    不生成新的 *** 作码。而是立即创建该数组并将其添加到当前类的属性表中。方法声明通过中的特殊情况以类似的方式工作
    zend_do_begin_function_declaration()
  3. zend_do_early_binding()
    消耗 电流 *** 作码阵列上的最后一个 *** 作码,检查其设置为NOP之前,以下类型之一:
    • ZEND_DECLARE_FUNCTION
    • ZEND_DECLARE_CLASS
    • ZEND_DECLARE_INHERITED_CLASS
    • ZEND_VERIFY_ABSTRACT_CLASS
    • ZEND_ADD_INTERFACE

请注意,在最后一种情况下,如果 *** 作码类型不是预期的类型之一,则会引发错误- “无效的绑定类型”
错误。由此可见,允许以某种方式分配非静态值会导致最后一个 *** 作码不是预期的。那么,当我们将非静态数组与经过修改的语法一起使用时,会发生什么呢?

array_init()
编译器不调用而是准备参数和调用
zend_do_init_array()
。依次调用
get_next_op()
并添加一个新的INIT_ARRAY *** 作码,产生如下所示的内容:

DECLARE_CLASS   'Foo'SEND_VAL        '.'DO_FCALL        'realpath'INIT_ARRAY

这就是问题的根源。通过添加这些 *** 作码,

zend_do_early_binding()
将获得意外的输入并引发异常。由于早期绑定类和函数定义的过程似乎是PHP编译过程不可或缺的一部分,因此不能仅仅忽略它(尽管DECLARE_CLASS的生产/消耗有些混乱)。同样,尝试内联评估这些其他 *** 作码也是不切实际的(您无法确定给定的函数或类是否已解决),因此无法避免生成 *** 作码。

潜在的解决方案是建立一个新的 *** 作码数组,该数组的作用域为类变量声明,类似于处理方法定义的方式。这样做的问题是决定何时评估这样的一次运行序列。当包含该类的文件被加载,第一次访问该属性或构造该类型的对象时,会完成该 *** 作吗?

正如您所指出的那样,其他动态语言也找到了一种处理这种情况的方法,因此做出决定并使之生效并非并非不可能。据我所知,在PHP的情况下这样做不是一站式解决方案,而语言设计人员似乎已经决定,此时不值得这样做。



欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5588319.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-14
下一篇 2022-12-15

发表评论

登录后才能评论

评论列表(0条)

保存