Updated Rector to commit f254215a5ac2877d4fb99dbf7f7ab44c070f1333

f254215a5a [Scope] Fix resolve Scope from fluent call (#5743)
This commit is contained in:
Tomas Votruba 2024-03-26 19:25:47 +00:00
parent 014254f0df
commit 045db2fd56
11 changed files with 153 additions and 76 deletions

View File

@ -19,12 +19,12 @@ final class VersionResolver
* @api * @api
* @var string * @var string
*/ */
public const PACKAGE_VERSION = '83046fcc340c19fc65bc09db25bbc018b01d6f30'; public const PACKAGE_VERSION = 'f254215a5ac2877d4fb99dbf7f7ab44c070f1333';
/** /**
* @api * @api
* @var string * @var string
*/ */
public const RELEASE_DATE = '2024-03-26 10:26:04'; public const RELEASE_DATE = '2024-03-27 02:23:32';
/** /**
* @var int * @var int
*/ */

View File

@ -123,7 +123,7 @@ final class CustomRuleCommand extends Command
$this->symfonyStyle->warning('No <testsuites> element found in ' . $phpunitFilePath . '. Rector could not add the rector test suite to it'); $this->symfonyStyle->warning('No <testsuites> element found in ' . $phpunitFilePath . '. Rector could not add the rector test suite to it');
return; return;
} }
$phpunitXML = $this->updatePHPUnitXMLFile($domDocument, $testsuitesElement, $phpunitFilePath); $phpunitXML = $this->updatePHPUnitXMLFile($domDocument, $testsuitesElement);
FileSystem::write($phpunitFilePath, $phpunitXML, null); FileSystem::write($phpunitFilePath, $phpunitXML, null);
$this->symfonyStyle->success('We also update ' . $phpunitFilePath . ", to add a rector test suite.\n You can run the rector tests by running: phpunit --testsuite rector"); $this->symfonyStyle->success('We also update ' . $phpunitFilePath . ", to add a rector test suite.\n You can run the rector tests by running: phpunit --testsuite rector");
} }
@ -146,7 +146,7 @@ final class CustomRuleCommand extends Command
} }
return \false; return \false;
} }
private function updatePHPUnitXMLFile(DOMDocument $domDocument, DOMElement $testsuitesElement, string $phpunitFilePath) : string private function updatePHPUnitXMLFile(DOMDocument $domDocument, DOMElement $testsuitesElement) : string
{ {
$domElement = $domDocument->createElement('testsuite'); $domElement = $domDocument->createElement('testsuite');
$domElement->setAttribute('name', 'rector'); $domElement->setAttribute('name', 'rector');

View File

@ -59,7 +59,7 @@ final class ExprScopeFromStmtNodeVisitor extends NodeVisitorAbstract
if (!$node instanceof Expr) { if (!$node instanceof Expr) {
return null; return null;
} }
if ($node->getAttribute(AttributeKey::EXPRESSION_DEPTH) < 2 && $node->getAttribute(AttributeKey::IS_ARG_VALUE) !== \true) { if ($this->shouldSkipExpr($node)) {
return null; return null;
} }
$scope = $node->getAttribute(AttributeKey::SCOPE); $scope = $node->getAttribute(AttributeKey::SCOPE);
@ -75,4 +75,8 @@ final class ExprScopeFromStmtNodeVisitor extends NodeVisitorAbstract
} }
return null; return null;
} }
private function shouldSkipExpr(Expr $expr) : bool
{
return $expr->getAttribute(AttributeKey::EXPRESSION_DEPTH) < 2 && $expr->getAttribute(AttributeKey::IS_ARG_VALUE) !== \true && $expr->getAttribute(AttributeKey::IS_PARAM_VAR) === \true;
}
} }

View File

@ -227,17 +227,17 @@
}, },
{ {
"name": "composer\/xdebug-handler", "name": "composer\/xdebug-handler",
"version": "3.0.3", "version": "3.0.4",
"version_normalized": "3.0.3.0", "version_normalized": "3.0.4.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https:\/\/github.com\/composer\/xdebug-handler.git", "url": "https:\/\/github.com\/composer\/xdebug-handler.git",
"reference": "ced299686f41dce890debac69273b47ffe98a40c" "reference": "4f988f8fdf580d53bdb2d1278fe93d1ed5462255"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https:\/\/api.github.com\/repos\/composer\/xdebug-handler\/zipball\/ced299686f41dce890debac69273b47ffe98a40c", "url": "https:\/\/api.github.com\/repos\/composer\/xdebug-handler\/zipball\/4f988f8fdf580d53bdb2d1278fe93d1ed5462255",
"reference": "ced299686f41dce890debac69273b47ffe98a40c", "reference": "4f988f8fdf580d53bdb2d1278fe93d1ed5462255",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -248,9 +248,9 @@
"require-dev": { "require-dev": {
"phpstan\/phpstan": "^1.0", "phpstan\/phpstan": "^1.0",
"phpstan\/phpstan-strict-rules": "^1.1", "phpstan\/phpstan-strict-rules": "^1.1",
"symfony\/phpunit-bridge": "^6.0" "phpunit\/phpunit": "^8.5 || ^9.6 || ^10.5"
}, },
"time": "2022-02-25T21:32:43+00:00", "time": "2024-03-26T18:29:49+00:00",
"type": "library", "type": "library",
"installation-source": "dist", "installation-source": "dist",
"autoload": { "autoload": {
@ -274,9 +274,9 @@
"performance" "performance"
], ],
"support": { "support": {
"irc": "irc:\/\/irc.freenode.org\/composer", "irc": "ircs:\/\/irc.libera.chat:6697\/composer",
"issues": "https:\/\/github.com\/composer\/xdebug-handler\/issues", "issues": "https:\/\/github.com\/composer\/xdebug-handler\/issues",
"source": "https:\/\/github.com\/composer\/xdebug-handler\/tree\/3.0.3" "source": "https:\/\/github.com\/composer\/xdebug-handler\/tree\/3.0.4"
}, },
"funding": [ "funding": [
{ {

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,9 @@
## [Unreleased] ## [Unreleased]
## [3.0.4] - 2024-03-26
* Added: Functional tests.
* Fixed: Incompatibility with PHPUnit 10.
## [3.0.3] - 2022-02-25 ## [3.0.3] - 2022-02-25
* Added: support for composer/pcre versions 2 and 3. * Added: support for composer/pcre versions 2 and 3.
@ -108,7 +112,8 @@
* Break: the following class was renamed: * Break: the following class was renamed:
- `Composer\XdebugHandler` -> `Composer\XdebugHandler\XdebugHandler` - `Composer\XdebugHandler` -> `Composer\XdebugHandler\XdebugHandler`
[Unreleased]: https://github.com/composer/xdebug-handler/compare/3.0.3...HEAD [Unreleased]: https://github.com/composer/xdebug-handler/compare/3.0.4...HEAD
[3.0.3]: https://github.com/composer/xdebug-handler/compare/3.0.3...3.0.4
[3.0.2]: https://github.com/composer/xdebug-handler/compare/3.0.2...3.0.3 [3.0.2]: https://github.com/composer/xdebug-handler/compare/3.0.2...3.0.3
[3.0.2]: https://github.com/composer/xdebug-handler/compare/3.0.1...3.0.2 [3.0.2]: https://github.com/composer/xdebug-handler/compare/3.0.1...3.0.2
[3.0.1]: https://github.com/composer/xdebug-handler/compare/3.0.0...3.0.1 [3.0.1]: https://github.com/composer/xdebug-handler/compare/3.0.0...3.0.1

View File

@ -51,6 +51,7 @@ The constructor takes a single parameter, `$envPrefix`, which is upper-cased and
* [Process configuration](#process-configuration) * [Process configuration](#process-configuration)
* [Troubleshooting](#troubleshooting) * [Troubleshooting](#troubleshooting)
* [Extending the library](#extending-the-library) * [Extending the library](#extending-the-library)
* [Examples](#examples)
### How it works ### How it works
@ -64,6 +65,8 @@ A temporary ini file is created from the loaded (and scanned) ini files, with an
* The application runs and exits. * The application runs and exits.
* The main process exits with the exit code from the restarted process. * The main process exits with the exit code from the restarted process.
See [Examples](#examples) for further information.
#### Signal handling #### Signal handling
Asynchronous signal handling is automatically enabled if the pcntl extension is loaded. `SIGINT` is set to `SIG_IGN` in the parent Asynchronous signal handling is automatically enabled if the pcntl extension is loaded. `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). process and restored to `SIG_DFL` in the restarted process (if no other handler has been set).
@ -74,7 +77,7 @@ From PHP 7.4 on Windows, `CTRL+C` and `CTRL+BREAK` handling is automatically ena
There are a few things to be aware of when running inside a restarted process. There are a few things to be aware of when running inside a restarted process.
* Extensions set on the command-line will not be loaded. * Extensions set on the command-line will not be loaded.
* Ini file locations will be reported as per the restart - see [getAllIniFiles()](#getallinifiles). * Ini file locations will be reported as per the restart - see [getAllIniFiles()](#getallinifiles-array).
* Php sub-processes may be loaded with Xdebug enabled - see [Process configuration](#process-configuration). * Php sub-processes may be loaded with Xdebug enabled - see [Process configuration](#process-configuration).
### Helper methods ### Helper methods
@ -200,12 +203,12 @@ Uses environment variables to remove Xdebug from the new process and persist the
>_If the new process calls a PHP sub-process, Xdebug will not be loaded in that sub-process._ >_If the new process calls a PHP sub-process, Xdebug will not be loaded in that sub-process._
This strategy can be used in the restart by calling [setPersistent()](#setpersistent). This strategy can be used in the restart by calling [setPersistent()](#setpersistent-self).
#### Sub-processes #### Sub-processes
The `PhpConfig` helper class makes it easy to invoke a PHP sub-process (with or without Xdebug loaded), regardless of whether there has been a restart. The `PhpConfig` helper class makes it easy to invoke a PHP sub-process (with or without Xdebug loaded), regardless of whether there has been a restart.
Each of its methods returns an array of PHP options (to add to the command-line) and sets up the environment for the required strategy. The [getRestartSettings()](#getrestartsettings) method is used internally. Each of its methods returns an array of PHP options (to add to the command-line) and sets up the environment for the required strategy. The [getRestartSettings()](#getrestartsettings-array) method is used internally.
* `useOriginal()` - Xdebug will be loaded in the new process. * `useOriginal()` - Xdebug will be loaded in the new process.
* `useStandard()` - Xdebug will **not** be loaded in the new process - see [standard settings](#standard-settings). * `useStandard()` - Xdebug will **not** be loaded in the new process - see [standard settings](#standard-settings).
@ -245,7 +248,7 @@ The API is defined by classes and their accessible elements that are not annotat
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. 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. 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. Note that the [setMainScript()](#setmainscriptstring-script-self) and [setPersistent()](#setpersistent-self) setters can be used here, if required.
#### _restart(array $command): void_ #### _restart(array $command): void_
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. 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.
@ -294,5 +297,9 @@ class MyRestarter extends XdebugHandler
} }
``` ```
### Examples
The `tests\App` directory contains command-line scripts that demonstrate the internal workings in a variety of scenarios.
See [Functional Test Scripts](./tests/App/README.md).
## License ## License
composer/xdebug-handler is licensed under the MIT License, see the LICENSE file for details. composer/xdebug-handler is licensed under the MIT License, see the LICENSE file for details.

View File

@ -14,7 +14,7 @@
} }
], ],
"support": { "support": {
"irc": "irc:\/\/irc.freenode.org\/composer", "irc": "ircs:\/\/irc.libera.chat:6697\/composer",
"issues": "https:\/\/github.com\/composer\/xdebug-handler\/issues" "issues": "https:\/\/github.com\/composer\/xdebug-handler\/issues"
}, },
"require": { "require": {
@ -23,9 +23,9 @@
"composer\/pcre": "^1 || ^2 || ^3" "composer\/pcre": "^1 || ^2 || ^3"
}, },
"require-dev": { "require-dev": {
"symfony\/phpunit-bridge": "^6.0",
"phpstan\/phpstan": "^1.0", "phpstan\/phpstan": "^1.0",
"phpstan\/phpstan-strict-rules": "^1.1" "phpstan\/phpstan-strict-rules": "^1.1",
"phpunit\/phpunit": "^8.5 || ^9.6 || ^10.5"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
@ -38,7 +38,7 @@
} }
}, },
"scripts": { "scripts": {
"test": "@php vendor\/bin\/simple-phpunit", "test": "@php vendor\/bin\/phpunit",
"phpstan": "@php vendor\/bin\/phpstan analyse" "phpstan": "@php vendor\/bin\/phpstan analyse"
} }
} }

View File

@ -36,6 +36,7 @@ class Process
} }
$quote = \strpbrk($arg, " \t") !== \false || $arg === ''; $quote = \strpbrk($arg, " \t") !== \false || $arg === '';
$arg = Preg::replace('/(\\\\*)"/', '$1$1\\"', $arg, -1, $dquotes); $arg = Preg::replace('/(\\\\*)"/', '$1$1\\"', $arg, -1, $dquotes);
$dquotes = (bool) $dquotes;
if ($meta) { if ($meta) {
$meta = $dquotes || Preg::isMatch('/%[^%]+%/', $arg); $meta = $dquotes || Preg::isMatch('/%[^%]+%/', $arg);
if (!$meta) { if (!$meta) {

View File

@ -69,12 +69,32 @@ class Status
public function report(string $op, ?string $data) : void public function report(string $op, ?string $data) : void
{ {
if ($this->logger !== null || $this->debug) { if ($this->logger !== null || $this->debug) {
$callable = [$this, 'report' . $op]; $param = (string) $data;
if (!\is_callable($callable)) { switch ($op) {
throw new \InvalidArgumentException('Unknown op handler: ' . $op); case self::CHECK:
$this->reportCheck($param);
break;
case self::ERROR:
$this->reportError($param);
break;
case self::INFO:
$this->reportInfo($param);
break;
case self::NORESTART:
$this->reportNoRestart();
break;
case self::RESTART:
$this->reportRestart();
break;
case self::RESTARTED:
$this->reportRestarted();
break;
case self::RESTARTING:
$this->reportRestarting($param);
break;
default:
throw new \InvalidArgumentException('Unknown op handler: ' . $op);
} }
$params = $data !== null ? [$data] : [];
\call_user_func_array($callable, $params);
} }
} }
/** /**
@ -154,7 +174,7 @@ class Status
{ {
$text = \sprintf('Process restarting (%s)', $this->getEnvAllow()); $text = \sprintf('Process restarting (%s)', $this->getEnvAllow());
$this->output($text); $this->output($text);
$text = 'Running ' . $command; $text = 'Running: ' . $command;
$this->output($text); $this->output($text);
} }
/** /**

View File

@ -116,8 +116,8 @@ class XdebugHandler
if (!(bool) $envArgs[0] && $this->requiresRestart(self::$xdebugActive)) { if (!(bool) $envArgs[0] && $this->requiresRestart(self::$xdebugActive)) {
// Restart required // Restart required
$this->notify(Status::RESTART); $this->notify(Status::RESTART);
if ($this->prepareRestart()) { $command = $this->prepareRestart();
$command = $this->getCommand(); if ($command !== null) {
$this->restart($command); $this->restart($command);
} }
return; return;
@ -147,9 +147,9 @@ class XdebugHandler
* Returns an array of php.ini locations with at least one entry * Returns an array of php.ini locations with at least one entry
* *
* The equivalent of calling php_ini_loaded_file then php_ini_scanned_files. * The equivalent of calling php_ini_loaded_file then php_ini_scanned_files.
* The loaded ini location is the first entry and may be empty. * The loaded ini location is the first entry and may be an empty string.
* *
* @return string[] * @return non-empty-list<string>
*/ */
public static function getAllIniFiles() : array public static function getAllIniFiles() : array
{ {
@ -212,7 +212,7 @@ class XdebugHandler
/** /**
* Allows an extending class to access the tmpIni * Allows an extending class to access the tmpIni
* *
* @param string[] $command * * @param non-empty-list<string> $command
*/ */
protected function restart(array $command) : void protected function restart(array $command) : void
{ {
@ -221,22 +221,24 @@ class XdebugHandler
/** /**
* Executes the restarted command then deletes the tmp ini * Executes the restarted command then deletes the tmp ini
* *
* @param string[] $command * @param non-empty-list<string> $command
* @phpstan-return never * @phpstan-return never
*/ */
private function doRestart(array $command) : void private function doRestart(array $command) : void
{ {
$this->tryEnableSignals();
$this->notify(Status::RESTARTING, \implode(' ', $command));
if (\PHP_VERSION_ID >= 70400) { if (\PHP_VERSION_ID >= 70400) {
$cmd = $command; $cmd = $command;
$displayCmd = \sprintf('[%s]', \implode(', ', $cmd));
} else { } else {
$cmd = Process::escapeShellCommand($command); $cmd = Process::escapeShellCommand($command);
if (\defined('PHP_WINDOWS_VERSION_BUILD')) { if (\defined('PHP_WINDOWS_VERSION_BUILD')) {
// Outer quotes required on cmd string below PHP 8 // Outer quotes required on cmd string below PHP 8
$cmd = '"' . $cmd . '"'; $cmd = '"' . $cmd . '"';
} }
$displayCmd = $cmd;
} }
$this->tryEnableSignals();
$this->notify(Status::RESTARTING, $displayCmd);
$process = \proc_open(\is_array($cmd) ? \implode(' ', \array_map('escapeshellarg', $cmd)) : $cmd, [], $pipes); $process = \proc_open(\is_array($cmd) ? \implode(' ', \array_map('escapeshellarg', $cmd)) : $cmd, [], $pipes);
if (\is_resource($process)) { if (\is_resource($process)) {
$exitCode = \proc_close($process); $exitCode = \proc_close($process);
@ -256,46 +258,63 @@ class XdebugHandler
exit($exitCode); exit($exitCode);
} }
/** /**
* Returns true if everything was written for the restart * Returns the command line array if everything was written for the restart
* *
* If any of the following fails (however unlikely) we must return false to * If any of the following fails (however unlikely) we must return false to
* stop potential recursion: * stop potential recursion:
* - tmp ini file creation * - tmp ini file creation
* - environment variable creation * - environment variable creation
*
* @return non-empty-list<string>|null
*/ */
private function prepareRestart() : bool private function prepareRestart() : ?array
{ {
if (!$this->cli) {
$this->notify(Status::ERROR, 'Unsupported SAPI: ' . \PHP_SAPI);
return null;
}
if (($argv = $this->checkServerArgv()) === null) {
$this->notify(Status::ERROR, '$_SERVER[argv] is not as expected');
return null;
}
if (!$this->checkConfiguration($info)) {
$this->notify(Status::ERROR, $info);
return null;
}
$mainScript = (string) $this->script;
if (!$this->checkMainScript($mainScript, $argv)) {
$this->notify(Status::ERROR, 'Unable to access main script: ' . $mainScript);
return null;
}
$tmpDir = \sys_get_temp_dir();
$iniError = 'Unable to create temp ini file at: ' . $tmpDir;
if (($tmpfile = @\tempnam($tmpDir, '')) === \false) {
$this->notify(Status::ERROR, $iniError);
return null;
}
$error = null; $error = null;
$iniFiles = self::getAllIniFiles(); $iniFiles = self::getAllIniFiles();
$scannedInis = \count($iniFiles) > 1; $scannedInis = \count($iniFiles) > 1;
$tmpDir = \sys_get_temp_dir(); if (!$this->writeTmpIni($tmpfile, $iniFiles, $error)) {
if (!$this->cli) { $this->notify(Status::ERROR, $error ?? $iniError);
$error = 'Unsupported SAPI: ' . \PHP_SAPI; @\unlink($tmpfile);
} elseif (!$this->checkConfiguration($info)) { return null;
$error = $info;
} elseif (!$this->checkMainScript()) {
$error = 'Unable to access main script: ' . $this->script;
} elseif (!$this->writeTmpIni($iniFiles, $tmpDir, $error)) {
$error = $error !== null ? $error : 'Unable to create temp ini file at: ' . $tmpDir;
} elseif (!$this->setEnvironment($scannedInis, $iniFiles)) {
$error = 'Unable to set environment variables';
} }
if ($error !== null) { if (!$this->setEnvironment($scannedInis, $iniFiles, $tmpfile)) {
$this->notify(Status::ERROR, $error); $this->notify(Status::ERROR, 'Unable to set environment variables');
@\unlink($tmpfile);
return null;
} }
return $error === null; $this->tmpIni = $tmpfile;
return $this->getCommand($argv, $tmpfile, $mainScript);
} }
/** /**
* Returns true if the tmp ini file was written * Returns true if the tmp ini file was written
* *
* @param string[] $iniFiles All ini files used in the current process * @param non-empty-list<string> $iniFiles All ini files used in the current process
*/ */
private function writeTmpIni(array $iniFiles, string $tmpDir, ?string &$error) : bool private function writeTmpIni(string $tmpFile, array $iniFiles, ?string &$error) : bool
{ {
if (($tmpfile = @\tempnam($tmpDir, '')) === \false) {
return \false;
}
$this->tmpIni = $tmpfile;
// $iniFiles has at least one item and it may be empty // $iniFiles has at least one item and it may be empty
if ($iniFiles[0] === '') { if ($iniFiles[0] === '') {
\array_shift($iniFiles); \array_shift($iniFiles);
@ -310,7 +329,7 @@ class XdebugHandler
return \false; return \false;
} }
// Check and remove directives after HOST and PATH sections // Check and remove directives after HOST and PATH sections
if (Preg::isMatchWithOffsets($sectionRegex, $data, $matches, \PREG_OFFSET_CAPTURE)) { if (Preg::isMatchWithOffsets($sectionRegex, $data, $matches)) {
$data = \substr($data, 0, $matches[0][1]); $data = \substr($data, 0, $matches[0][1]);
} }
$content .= Preg::replace($xdebugRegex, ';$1', $data) . \PHP_EOL; $content .= Preg::replace($xdebugRegex, ';$1', $data) . \PHP_EOL;
@ -325,31 +344,32 @@ class XdebugHandler
$content .= $this->mergeLoadedConfig($loaded, $config); $content .= $this->mergeLoadedConfig($loaded, $config);
// Work-around for https://bugs.php.net/bug.php?id=75932 // Work-around for https://bugs.php.net/bug.php?id=75932
$content .= 'opcache.enable_cli=0' . \PHP_EOL; $content .= 'opcache.enable_cli=0' . \PHP_EOL;
return (bool) @\file_put_contents($this->tmpIni, $content); return (bool) @\file_put_contents($tmpFile, $content);
} }
/** /**
* Returns the command line arguments for the restart * Returns the command line arguments for the restart
* *
* @return string[] * @param non-empty-list<string> $argv
* @return non-empty-list<string>
*/ */
private function getCommand() : array private function getCommand(array $argv, string $tmpIni, string $mainScript) : array
{ {
$php = [\PHP_BINARY]; $php = [\PHP_BINARY];
$args = \array_slice($_SERVER['argv'], 1); $args = \array_slice($argv, 1);
if (!$this->persistent) { if (!$this->persistent) {
// Use command-line options // Use command-line options
\array_push($php, '-n', '-c', $this->tmpIni); \array_push($php, '-n', '-c', $tmpIni);
} }
return \array_merge($php, [$this->script], $args); return \array_merge($php, [$mainScript], $args);
} }
/** /**
* Returns true if the restart environment variables were set * Returns true if the restart environment variables were set
* *
* No need to update $_SERVER since this is set in the restarted process. * No need to update $_SERVER since this is set in the restarted process.
* *
* @param string[] $iniFiles All ini files used in the current process * @param non-empty-list<string> $iniFiles All ini files used in the current process
*/ */
private function setEnvironment(bool $scannedInis, array $iniFiles) : bool private function setEnvironment(bool $scannedInis, array $iniFiles, string $tmpIni) : bool
{ {
$scanDir = \getenv('PHP_INI_SCAN_DIR'); $scanDir = \getenv('PHP_INI_SCAN_DIR');
$phprc = \getenv('PHPRC'); $phprc = \getenv('PHPRC');
@ -359,7 +379,7 @@ class XdebugHandler
} }
if ($this->persistent) { if ($this->persistent) {
// Use the environment to persist the settings // Use the environment to persist the settings
if (!\putenv('PHP_INI_SCAN_DIR=') || !\putenv('PHPRC=' . $this->tmpIni)) { if (!\putenv('PHP_INI_SCAN_DIR=') || !\putenv('PHPRC=' . $tmpIni)) {
return \false; return \false;
} }
} }
@ -398,28 +418,30 @@ class XdebugHandler
} }
/** /**
* Returns true if the script name can be used * Returns true if the script name can be used
*
* @param non-empty-list<string> $argv
*/ */
private function checkMainScript() : bool private function checkMainScript(string &$mainScript, array $argv) : bool
{ {
if ($this->script !== null) { if ($mainScript !== '') {
// Allow an application to set -- for standard input // Allow an application to set -- for standard input
return \file_exists($this->script) || '--' === $this->script; return \file_exists($mainScript) || '--' === $mainScript;
} }
if (\file_exists($this->script = $_SERVER['argv'][0])) { if (\file_exists($mainScript = $argv[0])) {
return \true; return \true;
} }
// Use a backtrace to resolve Phar and chdir issues. // Use a backtrace to resolve Phar and chdir issues.
$trace = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS); $trace = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS);
$main = \end($trace); $main = \end($trace);
if ($main !== \false && isset($main['file'])) { if ($main !== \false && isset($main['file'])) {
return \file_exists($this->script = $main['file']); return \file_exists($mainScript = $main['file']);
} }
return \false; return \false;
} }
/** /**
* Adds restart settings to the environment * Adds restart settings to the environment
* *
* @param string[] $envArgs * @param non-empty-list<string> $envArgs
*/ */
private function setEnvRestartSettings(array $envArgs) : void private function setEnvRestartSettings(array $envArgs) : void
{ {
@ -498,6 +520,24 @@ class XdebugHandler
}); });
} }
} }
/**
* Returns $_SERVER['argv'] if it is as expected
*
* @return non-empty-list<string>|null
*/
private function checkServerArgv() : ?array
{
$result = [];
if (isset($_SERVER['argv']) && \is_array($_SERVER['argv'])) {
foreach ($_SERVER['argv'] as $value) {
if (!\is_string($value)) {
return null;
}
$result[] = $value;
}
}
return \count($result) > 0 ? $result : null;
}
/** /**
* Sets static properties $xdebugActive, $xdebugVersion and $xdebugMode * Sets static properties $xdebugActive, $xdebugVersion and $xdebugMode
*/ */