我刚接触PHP,但是多年来我一直在使用类似的语言进行编程。我被以下内容弄糊涂了:
class Foo {
public $path = array(
realpath(".")
);
}
它产生了语法错误:Parse error: syntax error, unexpected '(', expecting ')' in test.php on line 5
这就是realpath
调用。
但这很好用:
$path = array(
realpath(".")
);
碰了一会儿之后,我被告知您不能在默认属性中调用函数。你必须在做__construct
。我的问题是:为什么?这是“功能”还是草率的实现?有什么根据?
编译器代码建议这是设计使然,尽管我不知道其背后的官方原因是什么。我也不确定要可靠地实现此功能需要花费多少精力,但是目前完成工作的方式肯定存在一些限制。
尽管我对PHP编译器的了解并不广泛,但我将尝试说明我认为的情况,以便您可以发现问题所在。您的代码示例非常适合此过程,因此我们将使用它:
class Foo {
public $path = array(
realpath(".")
);
}
如您所知,这会导致语法错误。这是PHP语法的结果,该语法进行了以下相关定义:
class_variable_declaration:
//...
| T_VARIABLE '=' static_scalar //...
;
因此,在定义变量值(例如)时$path
,期望值必须与静态标量的定义匹配。毫不奇怪,考虑到静态标量的定义还包括其值也是静态标量的数组类型,这有点用词不当:
static_scalar: /* compile-time evaluated scalars */
//...
| 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 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_do_begin_class_declaration()
导致呼叫get_next_op()
。这意味着它将向当前操作码数组添加新的操作码。array_init()
并且zend_do_add_static_array_element()
不生成新的操作码。而是立即创建该数组并将其添加到当前类的属性表中。方法声明通过中的特殊情况以类似的方式工作zend_do_begin_function_declaration()
。zend_do_early_binding()
消耗 电流操作码阵列上的最后一个操作码,检查其设置为NOP之前,以下类型之一:
请注意,在最后一种情况下,如果操作码类型不是预期的类型之一,则会引发错误- “无效的绑定类型”
错误。由此可见,允许以某种方式分配非静态值会导致最后一个操作码不是预期的。那么,当我们将非静态数组与经过修改的语法一起使用时,会发生什么呢?
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的情况下这样做不是一站式解决方案,而语言设计人员似乎已经决定,此时不值得这样做。
问题内容: 在JPA 2.0规范 说,第22页: 类的实例变量必须是私有的,受保护的或程序包可见性,而与使用字段访问还是属性访问无关。使用属性访问时,属性访问器方法必须是公共的或受保护的。 为什么不允许公共访问? 问题答案: 对于公共字段,将没有办法使代理可靠地工作-如果有人直接访问字段,那么持久性框架就没有简单的方法来拦截该调用并(例如)初始化包含对象(如果它是代理) 。 如果无法进行字段访问,
问题内容: 我有一个JPA实体,其属性设置为 但是,当我在JBoss 6上进行部署时,该应用程序会抛出一条错误消息: 我使用Hibernate 3.5作为JPA 2.0实现。 我应该使用什么来引用外键列? 问题答案: 使用代替:
我们有一个员工姓名表,其中列有ID、name、salary 查询1不起作用,但查询2起作用。从我的开发经验来看,我觉得对此可能的解释是: sum()处理参数中指定的一组值。这里传递了'salary'列,因此它必须将此列的所有值相加。但是在where子句中,记录是一个接一个地检查的,就像第一条记录1是为了测试而检查的,等等。因此,不会计算sum(工资),因为它需要访问所有列的值,然后只有它返回一个值
最近,我开始学习,并琢磨出函数可能是这样没有参数编写的: 这是怎么可能的,以及在引擎盖下正在做什么?不允许我们这样做。
问题内容: 除了JSONP,为什么要遵循相同的域策略? 问题答案: 出于安全原因,已实施“同源起源策略”;引用维基百科的相关句子: 这种机制对现代Web应用程序具有特殊的意义,因为Web服务器广泛依赖于HTTP cookie来维护经过身份验证的用户会话,因为服务器基于HTTP cookie信息进行操作以揭示敏感信息或执行状态更改操作。 必须在客户端维护不相关站点提供的内容之间的严格分隔,以防止丢失
问题内容: 来自C语言的Go语言最值得注意的方面之一是,如果在其中声明了一个未使用的变量,编译器将不会编译您的程序。那么,如果在函数中声明了一个未使用的参数,那么为什么要构建此程序呢? 问题答案: 没有正式的原因,但是在golang-nuts上给出的原因是: 未使用的变量始终是编程错误,而编写不使用其所有参数的函数是很常见的。 可以将这些参数保留为未命名(使用_),但这可能会与诸如 func fo