一种常见的方法无需declare(ticks=1)
是使用分析器。分析器会注意到any调用的方法/函数、加载的文件等,甚至还可以获取计时信息,这样您不仅可以知道哪个函数在何时、通过什么代码调用以及打开了哪些文件,还可以知道程序的哪一部分花费了多长时间。
PHP 中著名的分析器带有著名的Xdebug 扩展。它还附带一个调试器:
- https://xdebug.org/ https://xdebug.org/
好处之一是您不需要更改代码来进行分析,只需采用 PHP 配置即可,这样您就可以根据需要打开和关闭它(例如调试/分析会话)。
PHP Userland(勾选函数)
作为一种解决方法,没有declare(ticks=1);
在每个文件的开头(之后#71448 https://bugs.php.net/bug.php?id=71448),可以通过流包装器动态添加此内容file注入它的协议(对于本地文件系统中的文件来说很常见)。
通过创建在文件协议上注册的流包装器来代理标准文件 I/O 操作,这在技术上是可行的。在这个PoC(Github 上的要点) https://gist.github.com/hakre/45df61688fb3f6f10101bd2cdb24d0e6显示了最低限度的实现,以证明它适用于包含。什么时候test.php
被处决,尽管如此other.php
还没有declare(ticks=1);
在磁盘上,注册的刻度函数在包含时被调用,如回溯打印所示:
...
tick_handler() called
#0 tick_handler(1) called at [/home/hakre/stream-wrapper-default-files/test.php:18]
#1 tick_handler() called at [/home/hakre/stream-wrapper-default-files/other.php:2]
#2 include(/home/hakre/stream-wrapper-default-files/other.php) called at [/home/hakre/stream-wrapper-default-files/test.php:24]
...
输出是从注册的 tick 函数生成的(此处:test.php):
<?php
/**
* Inject declare ticks on include
*/
declare(ticks=1);
require __DIR__ . '/streamwrapper.php';
FileStreamWrapper::init();
// using a function as the callback
register_tick_function('tick_handler', true);
// Function which is called on each tick-event
function tick_handler()
{
echo "tick_handler() called\n";
debug_print_backtrace();
}
register_tick_function('tick_handler');
include "other.php";
include "another.php"; # file does not exists
The gist 示例中的流包装器 https://gist.github.com/hakre/45df61688fb3f6f10101bd2cdb24d0e6#file-streamwrapper-php只实现了两个 include 语句所需的最少内容,因为 PHP 脚本通常会执行更多文件 I/O,因此需要根据需要进行扩展。当它进行查找等时,需要考虑动态插入等。但是每个文件操作(句柄)都有状态,因为每个文件操作都有一个实例,因此应该很好地封装。全局状态用于注册/注销每个操作的流包装器以代理到真实的文件系统函数,否则它会创建无限递归(包装器使用包装器使用包装器...)。到目前为止,PoC 展示了它的原理工作原理。
这也可以(误)用于其他用途,但此 PoC 适用于您的特定声明标记并包含用例。