Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/262.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么不';PHP属性是否允许函数?_Php_Language Design_Php Internals - Fatal编程技术网

为什么不';PHP属性是否允许函数?

为什么不';PHP属性是否允许函数?,php,language-design,php-internals,Php,Language Design,Php Internals,我对PHP非常陌生,但多年来我一直在用类似的语言编程。我被以下事情弄糊涂了: class Foo { public $path = array( realpath(".") ); } DECLARE_CLASS 'Foo' SEND_VAL '.' DO_FCALL 'realpath' INIT_ARRAY 它产生了一个语法错误:Parse error:syntax error,test.php中第5行的“(”,预期“)”,这

我对PHP非常陌生,但多年来我一直在用类似的语言编程。我被以下事情弄糊涂了:

class Foo {
    public $path = array(
        realpath(".")
    );
}
DECLARE_CLASS   'Foo'
SEND_VAL        '.'
DO_FCALL        'realpath'
INIT_ARRAY
它产生了一个语法错误:
Parse error:syntax error,test.php中第5行的“(”,预期“)”,这是
realpath
调用

但这很好:

$path = array(
    realpath(".")
);
在我的头撞了一会儿之后,有人告诉我不能在属性默认值中调用函数;您必须在
\u构造中执行此操作。我的问题是:为什么?!这是一个“特性”还是草率的实现?理由是什么

我的问题是:为什么?!这是一个“特性”还是草率的实现

我认为这绝对是一个特色。类定义是一个代码蓝图,不应该在定义时执行代码。它将破坏对象的抽象和封装


不过,这只是我的看法。我不能确定开发者在定义这个时有什么想法

这是一个草率的解析器实现。我没有正确的术语来描述它(我认为术语“beta reduce”在某种程度上是合适的…),但是PHP语言解析器比它需要的更复杂,因此,不同的语言结构需要各种特殊的大小写。

我的猜测是,如果错误不发生在可执行行上,您将无法获得正确的堆栈跟踪。。。由于使用常量初始化值不会出现任何错误,所以这没有问题,但函数可能会引发异常/错误,需要在可执行行中调用,而不是在声明行中调用。

您可能会实现类似的效果:

class Foo
{
    public $path = __DIR__;
}
// ...
// Begins the class declaration
zend_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 declaration
zend_do_end_class_declaration(znode, "Foo");
// ...
zend_do_early_binding();
// ...
zend_do_end_compilation();

IIRC
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。我也不确定可靠地实现这一功能需要多少努力,但目前的工作方式肯定存在一些限制

虽然我对PHP编译器的知识并不丰富,但我将尝试说明我所相信的情况,以便您可以看到哪里存在问题。您的代码示例非常适合此过程,因此我们将使用:

class Foo {
    public $path = array(
        realpath(".")
    );
}
您很清楚,这会导致语法错误。这是一个结果,这使得以下相关定义:

class_variable_declaration: 
      //...
      | T_VARIABLE '=' static_scalar //...
;
因此,在定义变量(如
$path
)的值时,预期值必须与静态标量的定义相匹配。毫不奇怪,这有点用词不当,因为静态标量的定义还包括值也是静态标量的数组类型:

static_scalar: /* compile-time evaluated scalars */
      //...
      | T_ARRAY '(' static_array_pair_list ')' // ...
      //...
;
让我们先假设语法是不同的,类变量delcaration规则中的注释行看起来更像以下内容,这将与您的代码示例相匹配(尽管破坏了其他有效的赋值):

在重新编译PHP之后,示例脚本将不再因该语法错误而失败。相反,它将因编译时错误“无效绑定类型”而失败。由于代码现在基于语法是有效的,这表明编译器的设计中确实存在一些特定的问题。为了弄清楚这是什么,让我们暂时回到原始语法,想象一下代码示例的有效赋值是
$path=array(2)

使用语法作为指导,可以在解析此代码示例时浏览在中调用的操作。我遗漏了一些不太重要的部分,但过程如下所示:

class Foo
{
    public $path = __DIR__;
}
// ...
// Begins the class declaration
zend_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 declaration
zend_do_end_class_declaration(znode, "Foo");
// ...
zend_do_early_binding();
// ...
zend_do_end_compilation();
虽然编译器在这些不同的方法中做了很多工作,但有几点需要注意

  • 调用
    zend\u do\u begin\u class\u declaration()
    会导致调用
    get\u next\u op()
    。这意味着它将向当前操作码数组添加一个新的操作码
  • array\u init()
    zend\u do\u add\u static\u array\u element()
    不生成新的操作码。相反,数组会立即创建并添加到当前类的属性表中。方法声明以类似的方式工作,通过
    zend\u do\u begin\u function\u declaration()
    中的一个特例
  • zend\u do\u early\u binding()
    使用当前操作码数组上的最后一个操作码,在将其设置为NOP之前检查以下类型之一:
    • ZEND_声明_函数
    • ZEND_声明_类
    • ZEND_声明_继承的_类
    • ZEND_验证_抽象_类
    • ZEND_添加_接口
  • 请注意,在最后一种情况下,如果操作码类型不是预期的类型之一,则会引发一个错误——“无效绑定类型”错误。由此,我们可以看出,允许非静态值以某种方式分配会导致最后一个操作码不是预期的。那个么,当我们使用修改语法的非静态数组时会发生什么呢

    编译器不调用
    array\u init()
    ,而是准备参数并调用
    zend\u do\u init\u array()
    。这将依次调用
    get\u next\u op()
    ,并添加一个新的,生成如下内容:

    class Foo {
        public $path = array(
            realpath(".")
        );
    }
    
    DECLARE_CLASS   'Foo'
    SEND_VAL        '.'
    DO_FCALL        'realpath'
    INIT_ARRAY
    
    这就是问题的根源所在。通过添加这些操作码,
    zend\u do\u early\u binding()
    获得意外输入并引发异常。由于早期绑定类和函数定义的过程似乎是PHP编译过程中不可或缺的一部分,因此不能忽略它(尽管DECLARE_类的生成/使用有点混乱)。同样,尝试内联计算这些额外的操作码也不切实际(您无法确定给定的函数或类是否已解析),因此无法避免生成操作码

    一个潜在的解决方案是构建一个新的操作码数组,其作用域为类变量声明,类似于ho