PHP 7.2禁止类名为Object的巨坑
2018年1月4日PHP 7.2.1
发布,很多之前处于观望的小伙伴开始打算升级PHP
了,不过要注意到2017年11月30日发布的PHP 7.2.0
开始禁止类名为Object
,这将导致很多包出错。特别是很多包的兼容性只写了最低版本,没有写最高版本,使用PHP 7.2
的时候composer
并不会提示兼容性错误,而PHP
的autoload
的延迟加载特性,又会导致仅在涉及的时候才会提示错误。例如这样的场景:
程序使用框架F
,使用第三方模块M
,而模块M
中在特定参数的情况会调用模块N
,模块N
中包含一个类名为Object
的类。在开发的时候,模块N
并不会涉及到,因此在PHP 7.2
中开发很久也不会遇到这个问题。可是给客户部署的时候,出现了使用模块N
的情况。
这时候,如果降级到PHP 7.1
,那么程序中已经大量使用的PHP 7.2
的特性就都需要修改,特别是禁止类名使用Object
的特性的根源——Object
类型提示,就全部要修改,而且需要增加相应的程序判断。
如果不降级,那么就需要修改模块N
中所有涉及到Object
的地方,程序员都知道修改别人的代码多么痛苦。
最后说说这个坑。
PHP 7.2.0 Released 中包含 Object typehint 对象类型提示 ,其中提到 Backward Incompatible Changes 向后兼容变更
Although ‘object’ is already a soft reserved word, this RFC adds object as fully reserved classname.
从这里可以看出,object
一直是一个软soft
保留字,程序中本来就不应该使用,这次只不过变成了完全保留字而已。
例如 yii2 也做了修改 https://github.com/yiisoft/yii2/blob/master/framework/base/Object.php
尽管这么大的坑,不过好处也多多,官方给出的例子如下:
参数类型
function acceptsObject(object $obj) {
...
}
// This code can be statically analyzed to be correct
acceptsObject(json_decode('{}'));
// This code can be statically analyzed to be correct
acceptsObject(new \MyObject());
// This can be statically analysed to contain an error.
// and would throw an TypeError at runtime.
acceptsObject("Ceci n'est pas une object.");
返回类型
// This function can be statically analysed to conform to the
// return type
function correctFunction() : object {
$obj = json_decode('{}');
return $obj;
}
// This function can be statically analysed to contain an error
// and will also fail at runtime.
function errorFunction() : object {
return [];
}
转换器和数据解析器
interface Hydration {
// Hydrate an object by populating data
public function hydrate(array $data, object $object) : object;
}
interface Extraction {
// Extract values from an object
public function extract(object $object) : array;
}
服务容器和依赖注入容器
interface ServiceContainer {
// Set service definition
public function set(string $id, object $service);
// Retrieve service object from container
public function get(string $id) : object;
}
捕获返回值错误
function unserialize($data) : object {
$type = $data['type'];
switch ($type) {
case 'foo': { return new Foo(); }
case 'bar': { return new Bar(); }
case 'zot': { new zot(); } // Ooops, this is an error
}
}
继承中强制函数签名
class WidgetFactory {
function create() : object {
return new Widget();
}
}
class CustomWidgetFactory extends WidgetFactory {
// This class would not compile, as the signature of the metod in
// the child class is not compatible with the method signature in
// the parent class.
function create() : bool {
...
}
}
参数类型提示逆变
class Foo {
}
class Bar {
public function foo(Foo $object) : object {
return $object;
}
}
class Baz extends Bar {
public function foo(object $object) : object {
return $object;
}
}
返回值类型协变
class Foo {
}
class Bar {
public function foo(object $object) : object {
return $object;
}
}
class Baz extends Bar {
public function foo(object $object) : Foo {
return $object;
}
}