Dep: update
주로 PHAN
This commit is contained in:
+43
-1
@@ -1,5 +1,39 @@
|
||||
## [Unreleased]
|
||||
|
||||
## [2.0.2] - 2021-07-31
|
||||
* Added: support for `xdebug_info('mode')` in Xdebug 3.1.
|
||||
* Added: support for Psr\Log versions 2 and 3.
|
||||
* Fixed: remove ini directives from non-cli HOST/PATH sections.
|
||||
|
||||
## [2.0.1] - 2021-05-05
|
||||
* Fixed: don't restart if the cwd is a UNC path and cmd.exe will be invoked.
|
||||
|
||||
## [2.0.0] - 2021-04-09
|
||||
* Break: this is a major release, see [UPGRADE.md](UPGRADE.md) for more information.
|
||||
* Break: removed optional `$colorOption` constructor param and passthru fallback.
|
||||
* Break: renamed `requiresRestart` param from `$isLoaded` to `$default`.
|
||||
* Break: changed `restart` param `$command` from a string to an array.
|
||||
* Added: support for Xdebug3 to only restart if Xdebug is not running with `xdebug.mode=off`.
|
||||
* Added: `isXdebugActive()` method to determine if Xdebug is still running in the restart.
|
||||
* Added: feature to bypass the shell in PHP-7.4+ by giving `proc_open` an array of arguments.
|
||||
* Added: Process utility class to the API.
|
||||
|
||||
## [1.4.6] - 2021-03-25
|
||||
* Fixed: fail restart if `proc_open` has been disabled in `disable_functions`.
|
||||
* Fixed: enable Windows CTRL event handling in the restarted process.
|
||||
|
||||
## [1.4.5] - 2020-11-13
|
||||
* Fixed: use `proc_open` when available for correct FD forwarding to the restarted process.
|
||||
|
||||
## [1.4.4] - 2020-10-24
|
||||
* Fixed: exception if 'pcntl_signal' is disabled.
|
||||
|
||||
## [1.4.3] - 2020-08-19
|
||||
* Fixed: restore SIGINT to default handler in restarted process if no other handler exists.
|
||||
|
||||
## [1.4.2] - 2020-06-04
|
||||
* Fixed: ignore SIGINTs to let the restarted process handle them.
|
||||
|
||||
## [1.4.1] - 2020-03-01
|
||||
* Fixed: restart fails if an ini file is empty.
|
||||
|
||||
@@ -57,7 +91,15 @@
|
||||
* Break: the following class was renamed:
|
||||
- `Composer\XdebugHandler` -> `Composer\XdebugHandler\XdebugHandler`
|
||||
|
||||
[Unreleased]: https://github.com/composer/xdebug-handler/compare/1.4.1...HEAD
|
||||
[Unreleased]: https://github.com/composer/xdebug-handler/compare/2.0.2...HEAD
|
||||
[2.0.2]: https://github.com/composer/xdebug-handler/compare/2.0.1...2.0.2
|
||||
[2.0.1]: https://github.com/composer/xdebug-handler/compare/2.0.0...2.0.1
|
||||
[2.0.0]: https://github.com/composer/xdebug-handler/compare/1.4.6...2.0.0
|
||||
[1.4.6]: https://github.com/composer/xdebug-handler/compare/1.4.5...1.4.6
|
||||
[1.4.5]: https://github.com/composer/xdebug-handler/compare/1.4.4...1.4.5
|
||||
[1.4.4]: https://github.com/composer/xdebug-handler/compare/1.4.3...1.4.4
|
||||
[1.4.3]: https://github.com/composer/xdebug-handler/compare/1.4.2...1.4.3
|
||||
[1.4.2]: https://github.com/composer/xdebug-handler/compare/1.4.1...1.4.2
|
||||
[1.4.1]: https://github.com/composer/xdebug-handler/compare/1.4.0...1.4.1
|
||||
[1.4.0]: https://github.com/composer/xdebug-handler/compare/1.3.3...1.4.0
|
||||
[1.3.3]: https://github.com/composer/xdebug-handler/compare/1.3.2...1.3.3
|
||||
|
||||
+23
-20
@@ -1,16 +1,19 @@
|
||||
# composer/xdebug-handler
|
||||
|
||||
[](https://packagist.org/packages/composer/xdebug-handler)
|
||||
[](https://travis-ci.org/composer/xdebug-handler)
|
||||
[](https://ci.appveyor.com/project/Seldaek/xdebug-handler)
|
||||
[](https://github.com/composer/xdebug-handler/actions)
|
||||

|
||||

|
||||
|
||||
Restart a CLI process without loading the Xdebug extension.
|
||||
Restart a CLI process without loading the Xdebug extension, unless `xdebug.mode=off`.
|
||||
|
||||
Originally written as part of [composer/composer](https://github.com/composer/composer),
|
||||
now extracted and made available as a stand-alone library.
|
||||
|
||||
### Version 2
|
||||
|
||||
Support added for Xdebug3. See [UPGRADE](UPGRADE.md) for more information.
|
||||
|
||||
## Installation
|
||||
|
||||
Install the latest version with:
|
||||
@@ -32,21 +35,11 @@ $xdebug->check();
|
||||
unset($xdebug);
|
||||
```
|
||||
|
||||
The constructor takes two parameters:
|
||||
|
||||
#### _$envPrefix_
|
||||
This is used to create distinct environment variables and is upper-cased and prepended to default base values. The above example enables the use of:
|
||||
The constructor takes a single parameter, `$envPrefix`, which is upper-cased and prepended to default base values to create two distinct environment variables. The above example enables the use of:
|
||||
|
||||
- `MYAPP_ALLOW_XDEBUG=1` to override automatic restart and allow Xdebug
|
||||
- `MYAPP_ORIGINAL_INIS` to obtain ini file locations in a restarted process
|
||||
|
||||
#### _$colorOption_
|
||||
This optional value is added to the restart command-line and is needed to force color output in a piped child process. Only long-options are supported, for example `--ansi` or `--colors=always` etc.
|
||||
|
||||
If the original command-line contains an argument that pattern matches this value (for example `--no-ansi`, `--colors=never`) then _$colorOption_ is ignored.
|
||||
|
||||
If the pattern match ends with `=auto` (for example `--colors=auto`), the argument is replaced by _$colorOption_. Otherwise it is added at either the end of the command-line, or preceding the first double-dash `--` delimiter.
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
* [How it works](#how-it-works)
|
||||
@@ -63,12 +56,18 @@ A temporary ini file is created from the loaded (and scanned) ini files, with an
|
||||
|
||||
* `MYAPP_ALLOW_XDEBUG` is set with internal data to flag and use in the restart.
|
||||
* The command-line and environment are [configured](#process-configuration) for the restart.
|
||||
* The application is restarted in a new process using `passthru`.
|
||||
* The application is restarted in a new process.
|
||||
* The restart settings are stored in the environment.
|
||||
* `MYAPP_ALLOW_XDEBUG` is unset.
|
||||
* The application runs and exits.
|
||||
* The main process exits with the exit code from the restarted process.
|
||||
|
||||
#### Signal handling
|
||||
From PHP 7.1 with the pcntl extension loaded, asynchronous signal handling is automatically enabled. `SIGINT` is set to `SIG_IGN` in the parent
|
||||
process and restored to `SIG_DFL` in the restarted process (if no other handler has been set).
|
||||
|
||||
From PHP 7.4 on Windows, `CTRL+C` and `CTRL+BREAK` handling is ignored in the parent process and automatically enabled in the restarted process.
|
||||
|
||||
### Limitations
|
||||
There are a few things to be aware of when running inside a restarted process.
|
||||
|
||||
@@ -124,6 +123,9 @@ $version = XdebugHandler::getSkippedVersion();
|
||||
# $version: '2.6.0' (for example), or an empty string
|
||||
```
|
||||
|
||||
#### _isXdebugActive()_
|
||||
Returns true if Xdebug is loaded and is running in an active mode (if it supports modes). Returns false if Xdebug is not loaded, or it is running with `xdebug.mode=off`.
|
||||
|
||||
### Setter methods
|
||||
These methods implement a fluent interface and must be called before the main `check()` method.
|
||||
|
||||
@@ -232,15 +234,16 @@ The following environment settings can be used to troubleshoot unexpected behavi
|
||||
### Extending the library
|
||||
The API is defined by classes and their accessible elements that are not annotated as @internal. The main class has two protected methods that can be overridden to provide additional functionality:
|
||||
|
||||
#### _requiresRestart($isLoaded)_
|
||||
By default the process will restart if Xdebug is loaded. Extending this method allows an application to decide, by returning a boolean (or equivalent) value. It is only called if `MYAPP_ALLOW_XDEBUG` is empty, so it will not be called in the restarted process (where this variable contains internal data), or if the restart has been overridden.
|
||||
#### _requiresRestart($default)_
|
||||
By default the process will restart if Xdebug is loaded and not running with `xdebug.mode=off`. Extending this method allows an application to decide, by returning a boolean (or equivalent) value.
|
||||
It is only called if `MYAPP_ALLOW_XDEBUG` is empty, so it will not be called in the restarted process (where this variable contains internal data), or if the restart has been overridden.
|
||||
|
||||
Note that the [setMainScript()](#setmainscriptscript) and [setPersistent()](#setpersistent) setters can be used here, if required.
|
||||
|
||||
#### _restart($command)_
|
||||
An application can extend this to modify the temporary ini file, its location given in the `tmpIni` property. New settings can be safely appended to the end of the data, which is `PHP_EOL` terminated.
|
||||
|
||||
Note that the `$command` parameter is the escaped command-line string that will be used for the new process and must be treated accordingly.
|
||||
The `$command` parameter is an array of unescaped command-line arguments that will be used for the new process.
|
||||
|
||||
Remember to finish with `parent::restart($command)`.
|
||||
|
||||
@@ -259,7 +262,7 @@ class MyRestarter extends XdebugHandler
|
||||
{
|
||||
private $required;
|
||||
|
||||
protected function requiresRestart($isLoaded)
|
||||
protected function requiresRestart($default)
|
||||
{
|
||||
if (Command::isHelp()) {
|
||||
# No need to disable Xdebug for this
|
||||
@@ -267,7 +270,7 @@ class MyRestarter extends XdebugHandler
|
||||
}
|
||||
|
||||
$this->required = (bool) ini_get('phar.readonly');
|
||||
return $isLoaded || $this->required;
|
||||
return $this->required || $default;
|
||||
}
|
||||
|
||||
protected function restart($command)
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
## Upgrading from 1.x
|
||||
|
||||
The default behaviour has changed from always restarting if Xdebug is loaded, to only restarting if
|
||||
Xdebug is _active_. This has been introduced to support Xdebug3
|
||||
[modes](https://xdebug.org/docs/all_settings#mode). Xdebug is considered active if it is loaded, and
|
||||
for Xdebug3, if it is running in a mode other than `xdebug.mode=off`.
|
||||
|
||||
* Break: removed optional `$colorOption` constructor param and passthru fallback.
|
||||
|
||||
Just use `new XdebugHandler('myapp')` to instantiate the class and color support will be
|
||||
detectable in the restarted process.
|
||||
|
||||
* Added: `isXdebugActive()` method to determine if Xdebug is still running in the restart.
|
||||
|
||||
Returns true if Xdebug is loaded and is running in an active mode (if it supports modes).
|
||||
Returns false if Xdebug is not loaded, or it is running with `xdebug.mode=off`.
|
||||
|
||||
### Extending classes
|
||||
|
||||
* Break: renamed `requiresRestart` param from `$isLoaded` to `$default`.
|
||||
|
||||
This reflects the new default behaviour which is to restart only if Xdebug is active.
|
||||
|
||||
* Break: changed `restart` param `$command` from a string to an array.
|
||||
|
||||
Only important if you modified the string. `$command` is now an array of unescaped arguments.
|
||||
|
||||
* Added: Process utility class to the API.
|
||||
|
||||
This class was previously annotated as @internal and has been refactored due to recent changes.
|
||||
+5
-3
@@ -19,10 +19,11 @@
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.3.2 || ^7.0 || ^8.0",
|
||||
"psr/log": "^1.0"
|
||||
"psr/log": "^1 || ^2 || ^3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8"
|
||||
"symfony/phpunit-bridge": "^4.2 || ^5",
|
||||
"phpstan/phpstan": "^0.12.55"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@@ -35,6 +36,7 @@
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": "phpunit"
|
||||
"test": "vendor/bin/simple-phpunit",
|
||||
"phpstan": "vendor/bin/phpstan analyse"
|
||||
}
|
||||
}
|
||||
|
||||
+15
-4
@@ -49,8 +49,8 @@ class PhpConfig
|
||||
public function usePersistent()
|
||||
{
|
||||
if ($data = $this->getDataAndReset()) {
|
||||
Process::setEnv('PHPRC', $data['tmpIni']);
|
||||
Process::setEnv('PHP_INI_SCAN_DIR', '');
|
||||
$this->updateEnv('PHPRC', $data['tmpIni']);
|
||||
$this->updateEnv('PHP_INI_SCAN_DIR', '');
|
||||
}
|
||||
|
||||
return array();
|
||||
@@ -64,10 +64,21 @@ class PhpConfig
|
||||
private function getDataAndReset()
|
||||
{
|
||||
if ($data = XdebugHandler::getRestartSettings()) {
|
||||
Process::setEnv('PHPRC', $data['phprc']);
|
||||
Process::setEnv('PHP_INI_SCAN_DIR', $data['scanDir']);
|
||||
$this->updateEnv('PHPRC', $data['phprc']);
|
||||
$this->updateEnv('PHP_INI_SCAN_DIR', $data['scanDir']);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a restart settings value in the environment
|
||||
*
|
||||
* @param string $name
|
||||
* @param string|false $value
|
||||
*/
|
||||
private function updateEnv($name, $value)
|
||||
{
|
||||
Process::setEnv($name, false !== $value ? $value : null);
|
||||
}
|
||||
}
|
||||
|
||||
+12
-79
@@ -12,59 +12,12 @@
|
||||
namespace Composer\XdebugHandler;
|
||||
|
||||
/**
|
||||
* Provides utility functions to prepare a child process command-line and set
|
||||
* environment variables in that process.
|
||||
* Process utility functions
|
||||
*
|
||||
* @author John Stevenson <john-stevenson@blueyonder.co.uk>
|
||||
* @internal
|
||||
*/
|
||||
class Process
|
||||
{
|
||||
/**
|
||||
* Returns an array of parameters, including a color option if required
|
||||
*
|
||||
* A color option is needed because child process output is piped.
|
||||
*
|
||||
* @param array $args The script parameters
|
||||
* @param string $colorOption The long option to force color output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function addColorOption(array $args, $colorOption)
|
||||
{
|
||||
if (!$colorOption
|
||||
|| in_array($colorOption, $args)
|
||||
|| !preg_match('/^--([a-z]+$)|(^--[a-z]+=)/', $colorOption, $matches)) {
|
||||
return $args;
|
||||
}
|
||||
|
||||
if (isset($matches[2])) {
|
||||
// Handle --color(s)= options
|
||||
if (false !== ($index = array_search($matches[2].'auto', $args))) {
|
||||
$args[$index] = $colorOption;
|
||||
return $args;
|
||||
} elseif (preg_grep('/^'.$matches[2].'/', $args)) {
|
||||
return $args;
|
||||
}
|
||||
} elseif (in_array('--no-'.$matches[1], $args)) {
|
||||
return $args;
|
||||
}
|
||||
|
||||
// Check for NO_COLOR variable (https://no-color.org/)
|
||||
if (false !== getenv('NO_COLOR')) {
|
||||
return $args;
|
||||
}
|
||||
|
||||
if (false !== ($index = array_search('--', $args))) {
|
||||
// Position option before double-dash delimiter
|
||||
array_splice($args, $index, 0, $colorOption);
|
||||
} else {
|
||||
$args[] = $colorOption;
|
||||
}
|
||||
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes a string to be used as a shell argument.
|
||||
*
|
||||
@@ -109,53 +62,33 @@ class Process
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the output stream supports colors
|
||||
* Escapes an array of arguments that make up a shell command
|
||||
*
|
||||
* This is tricky on Windows, because Cygwin, Msys2 etc emulate pseudo
|
||||
* terminals via named pipes, so we can only check the environment.
|
||||
* @param array $args Argument list, with the module name first
|
||||
*
|
||||
* @param mixed $output A valid CLI output stream
|
||||
*
|
||||
* @return bool
|
||||
* @return string The escaped command line
|
||||
*/
|
||||
public static function supportsColor($output)
|
||||
public static function escapeShellCommand(array $args)
|
||||
{
|
||||
if ('Hyper' === getenv('TERM_PROGRAM')) {
|
||||
return true;
|
||||
$cmd = self::escape(array_shift($args), true, true);
|
||||
foreach ($args as $arg) {
|
||||
$cmd .= ' '.self::escape($arg);
|
||||
}
|
||||
|
||||
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
|
||||
return (function_exists('sapi_windows_vt100_support')
|
||||
&& sapi_windows_vt100_support($output))
|
||||
|| false !== getenv('ANSICON')
|
||||
|| 'ON' === getenv('ConEmuANSI')
|
||||
|| 'xterm' === getenv('TERM');
|
||||
}
|
||||
|
||||
if (function_exists('stream_isatty')) {
|
||||
return stream_isatty($output);
|
||||
}
|
||||
|
||||
if (function_exists('posix_isatty')) {
|
||||
return posix_isatty($output);
|
||||
}
|
||||
|
||||
$stat = fstat($output);
|
||||
// Check if formatted mode is S_IFCHR
|
||||
return $stat ? 0020000 === ($stat['mode'] & 0170000) : false;
|
||||
return $cmd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes putenv environment changes available in $_SERVER and $_ENV
|
||||
*
|
||||
* @param string $name
|
||||
* @param string|false $value A false value unsets the variable
|
||||
* @param string|null $value A null value unsets the variable
|
||||
*
|
||||
* @return bool Whether the environment variable was set
|
||||
*/
|
||||
public static function setEnv($name, $value = false)
|
||||
public static function setEnv($name, $value = null)
|
||||
{
|
||||
$unset = false === $value;
|
||||
$unset = null === $value;
|
||||
|
||||
if (!putenv($unset ? $name : $name.'='.$value)) {
|
||||
return false;
|
||||
|
||||
+9
-3
@@ -33,6 +33,7 @@ class Status
|
||||
private $envAllowXdebug;
|
||||
private $loaded;
|
||||
private $logger;
|
||||
private $modeOff;
|
||||
private $time;
|
||||
|
||||
/**
|
||||
@@ -91,7 +92,12 @@ class Status
|
||||
|
||||
private function reportCheck($loaded)
|
||||
{
|
||||
$this->loaded = $loaded;
|
||||
list($version, $mode) = explode('|', $loaded);
|
||||
|
||||
if ($version) {
|
||||
$this->loaded = '('.$version.')'.($mode ? ' mode='.$mode : '');
|
||||
}
|
||||
$this->modeOff = $mode === 'off';
|
||||
$this->output('Checking '.$this->envAllowXdebug);
|
||||
}
|
||||
|
||||
@@ -112,7 +118,7 @@ class Status
|
||||
if ($this->loaded) {
|
||||
$text = sprintf('No restart (%s)', $this->getEnvAllow());
|
||||
if (!getenv($this->envAllowXdebug)) {
|
||||
$text .= ' Allowed by application';
|
||||
$text .= ' Allowed by '.($this->modeOff ? 'mode' : 'application');
|
||||
}
|
||||
$this->output($text);
|
||||
}
|
||||
@@ -157,7 +163,7 @@ class Status
|
||||
*/
|
||||
private function getLoadedMessage()
|
||||
{
|
||||
$loaded = $this->loaded ? sprintf('loaded (%s)', $this->loaded) : 'not loaded';
|
||||
$loaded = $this->loaded ? sprintf('loaded %s', $this->loaded) : 'not loaded';
|
||||
return 'The Xdebug extension is '.$loaded;
|
||||
}
|
||||
}
|
||||
|
||||
+126
-42
@@ -30,13 +30,14 @@ class XdebugHandler
|
||||
private static $inRestart;
|
||||
private static $name;
|
||||
private static $skipped;
|
||||
private static $xdebugActive;
|
||||
|
||||
private $cli;
|
||||
private $colorOption;
|
||||
private $debug;
|
||||
private $envAllowXdebug;
|
||||
private $envOriginalInis;
|
||||
private $loaded;
|
||||
private $mode;
|
||||
private $persistent;
|
||||
private $script;
|
||||
/** @var Status|null */
|
||||
@@ -50,12 +51,11 @@ class XdebugHandler
|
||||
* would result in MYAPP_ALLOW_XDEBUG and MYAPP_ORIGINAL_INIS.
|
||||
*
|
||||
* @param string $envPrefix Value used in environment variables
|
||||
* @param string $colorOption Command-line long option to force color output
|
||||
* @throws \RuntimeException If a parameter is invalid
|
||||
* @throws \RuntimeException If the parameter is invalid
|
||||
*/
|
||||
public function __construct($envPrefix, $colorOption = '')
|
||||
public function __construct($envPrefix)
|
||||
{
|
||||
if (!is_string($envPrefix) || empty($envPrefix) || !is_string($colorOption)) {
|
||||
if (!is_string($envPrefix) || empty($envPrefix)) {
|
||||
throw new \RuntimeException('Invalid constructor parameter');
|
||||
}
|
||||
|
||||
@@ -63,13 +63,23 @@ class XdebugHandler
|
||||
$this->envAllowXdebug = self::$name.self::SUFFIX_ALLOW;
|
||||
$this->envOriginalInis = self::$name.self::SUFFIX_INIS;
|
||||
|
||||
$this->colorOption = $colorOption;
|
||||
|
||||
if (extension_loaded('xdebug')) {
|
||||
$ext = new \ReflectionExtension('xdebug');
|
||||
$this->loaded = $ext->getVersion() ?: 'unknown';
|
||||
$this->loaded = phpversion('xdebug') ?: 'unknown';
|
||||
|
||||
if (version_compare($this->loaded, '3.1', '>=')) {
|
||||
/** @phpstan-ignore-next-line */
|
||||
$modes = xdebug_info('mode');
|
||||
$this->mode = empty($modes) ? 'off' : implode(',', $modes);
|
||||
} elseif (false !== ($mode = ini_get('xdebug.mode'))) {
|
||||
$this->mode = getenv('XDEBUG_MODE') ?: ($mode ?: 'off');
|
||||
if (preg_match('/^,+$/', str_replace(' ', '', $this->mode))) {
|
||||
$this->mode = 'off';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self::$xdebugActive = $this->loaded && $this->mode !== 'off';
|
||||
|
||||
if ($this->cli = PHP_SAPI === 'cli') {
|
||||
$this->debug = getenv(self::DEBUG);
|
||||
}
|
||||
@@ -123,23 +133,22 @@ class XdebugHandler
|
||||
*/
|
||||
public function check()
|
||||
{
|
||||
$this->notify(Status::CHECK, $this->loaded);
|
||||
$this->notify(Status::CHECK, $this->loaded.'|'.$this->mode);
|
||||
$envArgs = explode('|', (string) getenv($this->envAllowXdebug));
|
||||
|
||||
if (empty($envArgs[0]) && $this->requiresRestart((bool) $this->loaded)) {
|
||||
if (empty($envArgs[0]) && $this->requiresRestart(self::$xdebugActive)) {
|
||||
// Restart required
|
||||
$this->notify(Status::RESTART);
|
||||
|
||||
if ($this->prepareRestart()) {
|
||||
$command = $this->getCommand();
|
||||
$this->notify(Status::RESTARTING, $command);
|
||||
$this->restart($command);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (self::RESTART_ID === $envArgs[0] && count($envArgs) === 5) {
|
||||
// Restarting, so unset environment variable and use saved values
|
||||
// Restarted, so unset environment variable and use saved values
|
||||
$this->notify(Status::RESTARTED);
|
||||
|
||||
Process::setEnv($this->envAllowXdebug);
|
||||
@@ -150,6 +159,8 @@ class XdebugHandler
|
||||
self::$skipped = $envArgs[1];
|
||||
}
|
||||
|
||||
$this->tryEnableSignals();
|
||||
|
||||
// Put restart settings in the environment
|
||||
$this->setEnvRestartSettings($envArgs);
|
||||
return;
|
||||
@@ -204,7 +215,7 @@ class XdebugHandler
|
||||
|
||||
if (count($envArgs) !== 6
|
||||
|| (!self::$inRestart && php_ini_loaded_file() !== $envArgs[0])) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
return array(
|
||||
@@ -228,21 +239,39 @@ class XdebugHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if Xdebug is loaded, or as directed by an extending class
|
||||
* Returns whether Xdebug is loaded and active
|
||||
*
|
||||
* @param bool $isLoaded Whether Xdebug is loaded
|
||||
* true: if Xdebug is loaded and is running in an active mode.
|
||||
* false: if Xdebug is not loaded, or it is running with xdebug.mode=off.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function requiresRestart($isLoaded)
|
||||
public static function isXdebugActive()
|
||||
{
|
||||
return $isLoaded;
|
||||
return self::$xdebugActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows an extending class to decide if there should be a restart
|
||||
*
|
||||
* The default is to restart if Xdebug is loaded and its mode is not "off".
|
||||
* Do not typehint for 1.x compatibility.
|
||||
*
|
||||
* @param bool $default The default behaviour
|
||||
*
|
||||
* @return bool Whether the process should restart
|
||||
*/
|
||||
protected function requiresRestart($default)
|
||||
{
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows an extending class to access the tmpIni
|
||||
*
|
||||
* @param string $command
|
||||
* Do not typehint for 1.x compatibility
|
||||
*
|
||||
* @param array $command
|
||||
*/
|
||||
protected function restart($command)
|
||||
{
|
||||
@@ -252,12 +281,35 @@ class XdebugHandler
|
||||
/**
|
||||
* Executes the restarted command then deletes the tmp ini
|
||||
*
|
||||
* @param string $command
|
||||
* @param array $command
|
||||
*/
|
||||
private function doRestart($command)
|
||||
private function doRestart(array $command)
|
||||
{
|
||||
passthru($command, $exitCode);
|
||||
$this->notify(Status::INFO, 'Restarted process exited '.$exitCode);
|
||||
$this->tryEnableSignals();
|
||||
$this->notify(Status::RESTARTING, implode(' ', $command));
|
||||
|
||||
if (PHP_VERSION_ID >= 70400) {
|
||||
$cmd = $command;
|
||||
} else {
|
||||
$cmd = Process::escapeShellCommand($command);
|
||||
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
|
||||
// Outer quotes required on cmd string below PHP 8
|
||||
$cmd = '"'.$cmd.'"';
|
||||
}
|
||||
}
|
||||
|
||||
$process = proc_open($cmd, array(), $pipes);
|
||||
if (is_resource($process)) {
|
||||
$exitCode = proc_close($process);
|
||||
}
|
||||
|
||||
if (!isset($exitCode)) {
|
||||
// Unlikely that php or the default shell cannot be invoked
|
||||
$this->notify(Status::ERROR, 'Unable to restart process');
|
||||
$exitCode = -1;
|
||||
} else {
|
||||
$this->notify(Status::INFO, 'Restarted process exited '.$exitCode);
|
||||
}
|
||||
|
||||
if ($this->debug === '2') {
|
||||
$this->notify(Status::INFO, 'Temp ini saved: '.$this->tmpIni);
|
||||
@@ -329,7 +381,8 @@ class XdebugHandler
|
||||
}
|
||||
|
||||
$content = '';
|
||||
$regex = '/^\s*(zend_extension\s*=.*xdebug.*)$/mi';
|
||||
$sectionRegex = '/^\s*\[(?:PATH|HOST)\s*=/mi';
|
||||
$xdebugRegex = '/^\s*(zend_extension\s*=.*xdebug.*)$/mi';
|
||||
|
||||
foreach ($iniFiles as $file) {
|
||||
// Check for inaccessible ini files
|
||||
@@ -337,7 +390,11 @@ class XdebugHandler
|
||||
$error = 'Unable to read ini: '.$file;
|
||||
return false;
|
||||
}
|
||||
$content .= preg_replace($regex, ';$1', $data).PHP_EOL;
|
||||
// Check and remove directives after HOST and PATH sections
|
||||
if (preg_match($sectionRegex, $data, $matches, PREG_OFFSET_CAPTURE)) {
|
||||
$data = substr($data, 0, $matches[0][1]);
|
||||
}
|
||||
$content .= preg_replace($xdebugRegex, ';$1', $data).PHP_EOL;
|
||||
}
|
||||
|
||||
// Merge loaded settings into our ini content, if it is valid
|
||||
@@ -353,9 +410,9 @@ class XdebugHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the restart command line
|
||||
* Returns the command line arguments for the restart
|
||||
*
|
||||
* @return string
|
||||
* @return array
|
||||
*/
|
||||
private function getCommand()
|
||||
{
|
||||
@@ -367,18 +424,7 @@ class XdebugHandler
|
||||
array_push($php, '-n', '-c', $this->tmpIni);
|
||||
}
|
||||
|
||||
if (defined('STDOUT') && Process::supportsColor(STDOUT)) {
|
||||
$args = Process::addColorOption($args, $this->colorOption);
|
||||
}
|
||||
|
||||
$args = array_merge($php, array($this->script), $args);
|
||||
|
||||
$cmd = Process::escape(array_shift($args), true, true);
|
||||
foreach ($args as $arg) {
|
||||
$cmd .= ' '.Process::escape($arg);
|
||||
}
|
||||
|
||||
return $cmd;
|
||||
return array_merge($php, array($this->script), $args);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -490,7 +536,7 @@ class XdebugHandler
|
||||
/**
|
||||
* Adds restart settings to the environment
|
||||
*
|
||||
* @param string $envArgs
|
||||
* @param string[] $envArgs
|
||||
*/
|
||||
private function setEnvRestartSettings($envArgs)
|
||||
{
|
||||
@@ -542,11 +588,12 @@ class XdebugHandler
|
||||
* Returns true if there are no known configuration issues
|
||||
*
|
||||
* @param string $info Set by method
|
||||
* @return bool
|
||||
*/
|
||||
private function checkConfiguration(&$info)
|
||||
{
|
||||
if (false !== strpos(ini_get('disable_functions'), 'passthru')) {
|
||||
$info = 'passthru function is disabled';
|
||||
if (!function_exists('proc_open')) {
|
||||
$info = 'proc_open function is disabled';
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -560,6 +607,43 @@ class XdebugHandler
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$workingDir = getcwd();
|
||||
if (0 === strpos($workingDir, '\\\\')) {
|
||||
if (defined('PHP_WINDOWS_VERSION_BUILD') && PHP_VERSION_ID < 70400) {
|
||||
$info = 'cmd.exe does not support UNC paths: '.$workingDir;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables async signals and control interrupts in the restarted process
|
||||
*
|
||||
* Available on Unix PHP 7.1+ with the pcntl extension and Windows PHP 7.4+.
|
||||
*/
|
||||
private function tryEnableSignals()
|
||||
{
|
||||
if (function_exists('pcntl_async_signals') && function_exists('pcntl_signal')) {
|
||||
pcntl_async_signals(true);
|
||||
$message = 'Async signals enabled';
|
||||
|
||||
if (!self::$inRestart) {
|
||||
// Restarting, so ignore SIGINT in parent
|
||||
pcntl_signal(SIGINT, SIG_IGN);
|
||||
} elseif (is_int(pcntl_signal_get_handler(SIGINT))) {
|
||||
// Restarted, no handler set so force default action
|
||||
pcntl_signal(SIGINT, SIG_DFL);
|
||||
}
|
||||
}
|
||||
|
||||
if (!self::$inRestart && function_exists('sapi_windows_set_ctrl_handler')) {
|
||||
// Restarting, so set a handler to ignore CTRL events in the parent.
|
||||
// This ensures that CTRL+C events will be available in the child
|
||||
// process without having to enable them there, which is unreliable.
|
||||
sapi_windows_set_ctrl_handler(function ($evt) {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user