composer / xdebug-handler
重启进程而不启用Xdebug。
Requires
- php: ^7.2.5 || ^8.0
- composer/pcre: ^1 || ^2 || ^3
- psr/log: ^1 || ^2 || ^3
Requires (Dev)
- phpstan/phpstan: ^1.0
- phpstan/phpstan-strict-rules: ^1.1
- phpunit/phpunit: ^8.5 || ^9.6 || ^10.5
README
除非 xdebug.mode=off
,否则在加载Xdebug扩展的情况下重启CLI进程。
最初作为composer/composer的一部分编写,现在已提取并作为独立的库提供。
版本 3
移除了对旧版PHP版本的支持,并添加了类型声明。
对版本 2 (PHP 5.3.2 - 7.2.4) 的长期支持遵循Composer 2.2 LTS策略。
安装
使用以下命令安装最新版本
$ composer require composer/xdebug-handler
要求
- 最低PHP版本7.2.5,尽管强烈推荐使用最新的PHP版本。
基本用法
use Composer\XdebugHandler\XdebugHandler; $xdebug = new XdebugHandler('myapp'); $xdebug->check(); unset($xdebug);
构造函数接受一个参数$envPrefix
,它被转换为大写并附加到默认的基本值之前,以创建两个不同的环境变量。上述示例启用了对以下内容的访问
MYAPP_ALLOW_XDEBUG=1
以覆盖自动重启并允许XdebugMYAPP_ORIGINAL_INIS
以在重启的进程中获得ini文件位置
高级用法
工作原理
从加载(并扫描)的ini文件创建一个临时ini文件,任何对Xdebug扩展的引用都被注释掉。当前ini设置将被合并,因此大多数在命令行或应用程序中设置的ini设置都将包括在内(见限制)
MYAPP_ALLOW_XDEBUG
通过内部数据设置,以标记并用于重启。- 为重启配置命令行和环境。
- 在新的进程中重启应用程序。
- 重启设置存储在环境中。
MYAPP_ALLOW_XDEBUG
被取消设置。- 应用程序运行并退出。
- 主进程以重启进程的退出代码退出。
有关更多信息,请参阅示例。
信号处理
如果加载了pcntl扩展,则自动启用异步信号处理。SIGINT
在父进程中设置为SIG_IGN
,在重启的进程中恢复为SIG_DFL
(如果没有设置其他处理程序)。
从PHP 7.4开始,在Windows上自动启用重启进程中的CTRL+C
和CTRL+BREAK
处理,并在父进程中忽略。
限制
在重启进程中运行时需要注意以下几点。
- 命令行上设置的扩展将不会被加载。
- ini文件位置将按照重启方式报告 - 请参阅getAllIniFiles()。
- Php子进程可能已启用Xdebug加载 - 请参阅进程配置。
辅助方法
这些静态方法提供有关当前进程的信息,无论它是否已重启。
getAllIniFiles(): array
返回原始ini文件位置的数组。使用此方法代替调用php_ini_loaded_file
和php_ini_scanned_files
,这些方法在重启的进程中将报告错误值。
use Composer\XdebugHandler\XdebugHandler; $files = XdebugHandler::getAllIniFiles(); # $files[0] always exists, it could be an empty string $loadedIni = array_shift($files); $scannedInis = $files;
这些位置也包含在MYAPP_ORIGINAL_INIS
环境变量中。这是一个由路径分隔的字符串,包含来自php_ini_loaded_file
返回的位置(可能为空),然后是来自php_ini_scanned_files
解析的位置。
getRestartSettings(): ?array
返回一个可以用于PHP 子进程的设置数组,如果没有重启进程,则返回null。
use Composer\XdebugHandler\XdebugHandler; $settings = XdebugHandler::getRestartSettings(); /** * $settings: array (if the current process was restarted, * or called with the settings from a previous restart), or null * * 'tmpIni' => the temporary ini file used in the restart (string) * 'scannedInis' => if there were any scanned inis (bool) * 'scanDir' => the original PHP_INI_SCAN_DIR value (false|string) * 'phprc' => the original PHPRC value (false|string) * 'inis' => the original inis from getAllIniFiles (array) * 'skipped' => the skipped version from getSkippedVersion (string) */
getSkippedVersion(): string
返回重启过程中跳过的Xdebug版本字符串,如果没有重启(或Xdebug仍然加载,可能是通过扩展类以其他原因重启而不是移除Xdebug),则返回空字符串。
use Composer\XdebugHandler\XdebugHandler; $version = XdebugHandler::getSkippedVersion(); # $version: '3.1.1' (for example), or an empty string
isXdebugActive(): bool
如果Xdebug已加载并且在活动模式下运行(如果它支持模式),则返回true。如果Xdebug未加载,或者它正在运行xdebug.mode=off
,则返回false。
设置方法
这些方法实现了流畅的接口,必须在主check()
方法之前调用。
setLogger(LoggerInterface $logger): self
启用将状态消息输出到外部PSR3日志记录器的功能。所有消息都以DEBUG
或WARNING
日志级别报告。例如(显示级别和消息)
// No restart
DEBUG Checking MYAPP_ALLOW_XDEBUG
DEBUG The Xdebug extension is loaded (3.1.1) xdebug.mode=off
DEBUG No restart (APP_ALLOW_XDEBUG=0) Allowed by xdebug.mode
// Restart overridden
DEBUG Checking MYAPP_ALLOW_XDEBUG
DEBUG The Xdebug extension is loaded (3.1.1) xdebug.mode=coverage,debug,develop
DEBUG No restart (MYAPP_ALLOW_XDEBUG=1)
// Failed restart
DEBUG Checking MYAPP_ALLOW_XDEBUG
DEBUG The Xdebug extension is loaded (3.1.0)
WARNING No restart (Unable to create temp ini file at: ...)
状态消息还可以使用XDEBUG_HANDLER_DEBUG
输出。见故障排除。
setMainScript(string $script): self
设置重启中要运行的main脚本的路径。仅在更特殊的使用场景中或当argv[0]
路径不可访问时需要。支持脚本名--
用于标准输入。
setPersistent(): self
使用持久化设置配置重启,这样Xdebug就不会在任何子进程中加载。
如果您的应用程序调用了一个或多个PHP子进程且不需要Xdebug扩展,请使用此方法。这避免了实现特定的子进程策略的开销。
或者,可以使用此方法设置默认的无Xdebug环境,如果子进程需要Xdebug,则可以更改,然后之后恢复
function SubProcessWithXdebug() { $phpConfig = new Composer\XdebugHandler\PhpConfig(); # Set the environment to the original configuration $phpConfig->useOriginal(); # run the process with Xdebug loaded ... # Restore Xdebug-free environment $phpConfig->usePersistent(); }
进程配置
该库提供了两种策略来调用不加载Xdebug的新PHP进程,使用标准或持久设置。请注意,这只在应用程序调用PHP子进程时才重要。
标准设置
仅使用命令行选项从新进程中删除Xdebug。
- 添加了-n选项到命令行。这告诉PHP不要扫描额外的inis。
- 使用-c选项将临时ini添加到命令行。
如果新进程调用PHP子进程,Xdebug将加载到该子进程中(除非它实现了xdebug-handler,在这种情况下将会有另一个重启)。
这是重启中使用的默认策略。
持久设置
使用环境变量从新进程中删除Xdebug并将这些设置持久化到任何子进程中。
PHP_INI_SCAN_DIR
设置为空字符串。这告诉PHP不要扫描额外的inis。PHPRC
设置为临时ini。
如果新进程调用PHP子进程,Xdebug将不会加载到该子进程中。
可以通过调用setPersistent()来在重启中使用此策略。
子进程
PhpConfig
辅助类使得无论是否重启,都可以轻松调用PHP子进程(带或不带Xdebug加载)。
它的每个方法都返回一个PHP选项数组(用于添加到命令行),并为所需的策略设置环境。内部使用 getRestartSettings() 方法。
useOriginal()
- Xdebug将被加载到新进程中。useStandard()
- Xdebug将不会加载到新进程中 - 请参阅标准设置。userPersistent()
- Xdebug将不会加载到新进程中 - 请参阅持久设置
如果没有重启,则返回一个空选项数组,并且环境不会被更改。
use Composer\XdebugHandler\PhpConfig; $config = new PhpConfig; $options = $config->useOriginal(); # $options: empty array # environment: PHPRC and PHP_INI_SCAN_DIR set to original values $options = $config->useStandard(); # $options: [-n, -c, tmpIni] # environment: PHPRC and PHP_INI_SCAN_DIR set to original values $options = $config->usePersistent(); # $options: empty array # environment: PHPRC=tmpIni, PHP_INI_SCAN_DIR=''
故障排除
以下环境设置可用于排查意外行为
-
XDEBUG_HANDLER_DEBUG=1
将状态消息输出到STDERR
,如果已定义,则不受任何PSR3日志记录器的影响。每条消息都以前缀xdebug-handler[pid]
开头,其中pid是进程标识符。 -
XDEBUG_HANDLER_DEBUG=2
如上所述,但此外还会保存临时ini文件,并在状态消息中报告其位置。
扩展库
API由类及其未标记为@internal的可访问元素定义。主类有两个受保护的方法,可以覆盖以提供附加功能
requiresRestart(bool $default): bool
默认情况下,如果Xdebug被加载并且没有使用xdebug.mode=off
运行,进程将重新启动。扩展此方法允许应用程序通过返回布尔值(或等效值)来决定。只有在MYAPP_ALLOW_XDEBUG
为空时才会调用它,因此它不会在重启的进程(其中此变量包含内部数据)中调用,或者如果重启已被覆盖。
注意,如果需要,可以使用setMainScript()和setPersistent()设置器。
restart(array $command): void
应用程序可以扩展此功能以修改临时ini文件,其位置由tmpIni
属性给出。可以在数据的末尾安全地追加新设置,该数据以PHP_EOL
终止。
参数$command
是一个未转义的命令行参数数组,将用于新进程。
请记住以parent::restart($command)
结束。
示例
此示例演示了扩展基本功能两种方法
-
为了避免启动新进程的开销,如果请求简单的帮助命令,则跳过重启。
-
应用程序需要写入phar文件,因此如果设置
phar.readonly
,则将强制重启(无论是否加载Xdebug)并在临时ini文件中更改此值。
use Composer\XdebugHandler\XdebugHandler; use MyApp\Command; class MyRestarter extends XdebugHandler { private $required; protected function requiresRestart(bool $default): bool { if (Command::isHelp()) { # No need to disable Xdebug for this return false; } $this->required = (bool) ini_get('phar.readonly'); return $this->required || $default; } protected function restart(array $command): void { if ($this->required) { # Add required ini setting to tmpIni $content = file_get_contents($this->tmpIni); $content .= 'phar.readonly=0'.PHP_EOL; file_put_contents($this->tmpIni, $content); } parent::restart($command); } }
示例
tests\App
目录包含命令行脚本,这些脚本在各种场景中展示了内部工作方式。请参阅功能测试脚本。
许可
composer/xdebug-handler遵循MIT许可证,有关详细信息,请参阅LICENSE文件。