检测 __sleep 中的关闭以防止其序列化
当我尝试序列化一个包含闭包成员的对象时,会抛出异常。 为了避免成员的序列化(包括闭包),我尝试了以下操作:
function __sleep(){
$ref = new ReflectionClass($this);
$props = $ref->getProperties();
foreach ($props as $prop){
$name = $prop->name;
if (is_callable($this->$name)===false){
$dream[] = $prop->name;
}
}
return $dream;
}
不幸的是,这不起作用。有没有更好的方法来检测属性是否是闭包。
编辑:我通过让闭包知道是否序列化来解决我的问题
为此,我正在包装闭包本身。这是一个例子:
/**
* Wrapper-class to prevent closure to be serialized.
*/
class WrappedClosure {
private $closure = NULL;
protected $reflection = NULL;
public function __construct($function){
if ( ! $function instanceOf Closure)
throw new InvalidArgumentException();
$this->closure = $function;
$this->reflection = new ReflectionFunction($function);
}
/**
* When the instance is invoked, redirect invocation to closure.
*/
public function __invoke(){
$args = func_get_args();
return $this->reflection->invokeArgs($args);
}
// do nothing on serialization
public function __sleep(){}
// do nothing on serialization
public function __wakeup(){}
}
// Assigning a wrapped closure to a member
$myObject->memberHoldingAClosure =
// Wrapping the closure
new WrappedClosure(
function (){
echo "I'am the inner closure.";
}
)
);
// the serialization doesn't throw an exception anymore
serialize($myObject);
When I'm trying to serialize an object which has members including closures an exception is thrown.
To avoid the serialization of the members including closures I tried the following:
function __sleep(){
$ref = new ReflectionClass($this);
$props = $ref->getProperties();
foreach ($props as $prop){
$name = $prop->name;
if (is_callable($this->$name)===false){
$dream[] = $prop->name;
}
}
return $dream;
}
Unfortunately this does not work. Is there a better way to detect whether a property is a closure or not.
EDIT: I solved my problem by letting the closure know whether to serialize or not
To do this I am wrapping the closure itself. Here's an example:
/**
* Wrapper-class to prevent closure to be serialized.
*/
class WrappedClosure {
private $closure = NULL;
protected $reflection = NULL;
public function __construct($function){
if ( ! $function instanceOf Closure)
throw new InvalidArgumentException();
$this->closure = $function;
$this->reflection = new ReflectionFunction($function);
}
/**
* When the instance is invoked, redirect invocation to closure.
*/
public function __invoke(){
$args = func_get_args();
return $this->reflection->invokeArgs($args);
}
// do nothing on serialization
public function __sleep(){}
// do nothing on serialization
public function __wakeup(){}
}
// Assigning a wrapped closure to a member
$myObject->memberHoldingAClosure =
// Wrapping the closure
new WrappedClosure(
function (){
echo "I'am the inner closure.";
}
)
);
// the serialization doesn't throw an exception anymore
serialize($myObject);
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
对我来说效果很好:
关于检查值是否是
Closure
类的实例(来自手册):因此,我将实现 is_closure($value) 函数作为 return !is_string($value) && !is_array($value) && is_callable($value) 而不是
return $value instanceof Closure
并希望有一天 PHP 开发人员能够添加原生is_closure()
函数。Works fine for me:
About checking if value is instance of
Closure
class (from manual):Therefore I would implement
is_closure($value)
function asreturn !is_string($value) && !is_array($value) && is_callable($value)
rather thanreturn $value instanceof Closure
and hope that some day PHP developers will add nativeis_closure()
function.老实说,我认为您正在尝试解决错误的问题。如果你在课堂上睡觉,那么如果你不能序列化所有内容,那么成功的睡眠不是错误的吗?否则,您可能会醒来进入不一致的状态(或者至少是与当前状态不同的状态)。所以我认为你应该将所有内容放入结果数组中,然后让 PHP 告诉你它是否不可序列化。
否则,您是否需要检查是否有任何存储的对象是可序列化的?那么您应该检查
Serialized
接口还是__sleep
是否存在?你在哪里划清界限?所以我想说,您不应该序列化您明确知道如何在唤醒函数中重新创建的资源和变量(例如数据库连接,或您明确知道如何重新创建的任何闭包)。但这里要小心,因为如果你让这些闭包/资源通过对象的 API 进行更改,你如何才能确保成功唤醒到先前的状态。简而言之,我建议只返回所有内容,并让 PHP 处理不可序列化的变量。否则,您需要将其列入白名单(这不切实际)或黑名单(这并不完整)。这两者都不是一个很好的解决方案。只要在异常发生时对其进行处理即可(抛出和捕获异常也不错)。
至于您的确切问题,我将按如下方式实现它:
它仍然依赖于对象类型的闭包的实现细节,但我认为这是我们目前能做的最好的事情。最好的解决方案是请求核心添加一个 is_closure() 函数,该函数将独立于实现......
Honestly, I think you're trying to solve the wrong problem. If you're sleeping the class, then isn't it wrong to have a successful sleep if you can't serialize everything? Otherwise you can wake up to an inconsistent state (or at least a state that's different than the current one). So I would argue that you should just put everything into the resultant array and then let PHP tell you if it's not serializable.
Otherwise, do you then need to check to see if any stored objects are serialzable? Should you then be checking for
Serializable
interface or the existence of__sleep
? Where do you draw the line? So I would say that you should only not serialize resources and variables that you explicitly know how to recreate in the wakeup function (such as a database connection, or any closures you explicitly know how to recreate). But be careful here, since if you let those closures/resources be changed via the object's API, how can you be sure of a successful wakeup to the prior state.So in short, I would recommend just returning everything, and letting PHP handle unserializable variables. Otherwise you'd need to either white-list (which isn't going to be practical) or black-list (which isn't going to be complete). And neither is a great solution. Just handle the exception when it comes (throwing and catching exceptions isn't bad).
As far as your exact question, I would implement it as follows:
It still relies on the implementation detail of the closure being of a Object type, but I think that's the best we can do at this point. The best solution would be to petition the core to add a
is_closure()
function which would be implementation independent...