这主要是实验,但我认为它确实有一些实际用途。
我们的想法是能够在运行时修补PHP脚本,而无需重新加载请求。
例如,假设我在PHP中编写了一个WebSocket服务器,并且该服务器正在运行以下类...
class MyServerApp extends WebSocketServerApp {
protected $clients = array();
public function onConnect($client){
$this->clients[$client->getId()] = $client;
}
public function onDisconnect($client){
unset($this->clients[$client->getId()]);
}
public function onData($client, $data){
$client->send($data); // perform echo functionality
}
}
基本上,服务器创建一个MyServerApp
类的实例并适当地调用它。上面的应用程序是一个echo服务器;它完全响应所有客户请求。
现在假设我修改了服务器源代码并希望保持现有服务器运行,但是改变行为(以免松散现有客户端)。应用服务器方便地有一个onTick()
事件,我们可以用它来检查源代码的更改:
class MyServerApp extends WebSocketServerApp {
// the existing code from above goes here
/**
* @var integer Timestamp of when the server was last patched.
*/
public $last_patch = 0;
public function __construct(){
$this->last_patch = time();
}
public function onTick(){
if($this->last_patch < filemtime(__FILE__)){
// include __FILE__;
}
}
}
补丁检查可能会很好地工作,但实际的补丁方法不会(顺便说一句,它目前已经过评论)。 主要原因是服务器类已经定义。
那么你将如何进行实际修补?以某种方式覆盖函数或类?
[*] server.php
看起来像是:
$server['onData'] = function(){ /* new function body */ };
另一方面,这种架构提出了一些真正需要解决的问题:
答案 0 :(得分:1)
你可以在没有任何扩展的情况下做到这一点......但是溶剂不仅仅是脏的......它是代码的敌基督!
如果在方法中使用eval,则评估的代码可以使用$ this来访问私有和受保护的属性和方法!
您可以将源代码存储在任何类型的文件或数据库中,每当更新源代码时,您都可以将更新的源代码加载到字符串中并在方法中对其进行评估。
如果您希望能够将类作为普通类或动态源类使用,那么事情会变得有点复杂:
这不是斯巴达......这是疯狂的。
如果您想了解有关PHP文件结构的更多信息,请继续。
但是从来没有在高效的环境中使用它!!!!!!!
答案 1 :(得分:0)
您可能需要查看classkit(特别是其功能classkit_method_redefine和classkit_import)。
但那真是太可怕了。你确定在重启脚本时你不能承受毫秒的停机时间(就像每个人一样)吗?
这也可能无法长期持续(如果被覆盖的方法被泄露,我不会感到惊讶)。此外,我不确定tick回调是否真的是为了在生产服务器上使用而构建的(它可能会有一些性能开销)。
编辑:
可悲的是,有关PHP垃圾收集器的文章数量相当有限。 Here是一个(它是PHP中性能优化系列的一部分),但它没有详细说明如何处理函数或类型。
根据我的经验,连续运行数天的PHP脚本需要每隔几天重新启动一次(我使用Cron-job尝试每隔几分钟启动一次脚本以及一个锁定文件),因为之后,脚本变得更少了而且不太稳定。
答案 2 :(得分:0)
如果你想重新定义一个类,我认为你需要使用像runkit这样的扩展。但是,您也可以为新类提供某种形式的序列名称,如MyClassVersion56。使用工厂来快速启动类,并且工厂知道最新类的名称,因为您在某处设置了变量。您甚至可以str_replace源代码来自动进行串行命名。
如果长时间运行php脚本,请密切关注内存使用情况......
有动态定义程序行为的方法,但是在某些时候你开始重新编写一种编程语言,当你已经使用像php这样的动态语言时。但是,如果行为的变化范围有限,请考虑动态代码执行,就像您尝试的那样。
答案 3 :(得分:0)
我能想到的唯一好方法是将工作分成两个脚本: 一个脚本保存连接,一个脚本完成实际工作。
connction scrpts将请求数据写入数据库或memcache驱动的“作业池”,并查找可以在“结果池”中返回的结果。
工作脚本从池中获取作业,处理它们,并将结果放在“结果池”中。
通过这种方式,您可以在工作脚本中编辑逻辑并重新启动它而不会丢失连接。
我不认为这是实现服务器的好方法,但选择取决于你。