Download project

This commit is contained in:
Roman Pyrih
2024-11-20 09:09:44 +01:00
parent 547a138d6a
commit 5ff041757f
40737 changed files with 7766183 additions and 0 deletions

View File

@@ -0,0 +1,124 @@
## [Unreleased]
## [2.0.5] - 2022-02-24
* Fixed: regression in 2.0.4 affecting Xdebug 2.
## [2.0.4] - 2022-01-04
* Fixed: allow calling `isXdebugActive` before class instantiation.
## [2.0.3] - 2021-12-08
* Added: support, type annotations and refactoring for stricter PHPStan analysis.
## [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.
## [1.4.0] - 2019-11-06
* Added: support for `NO_COLOR` environment variable: https://no-color.org
* Added: color support for Hyper terminal: https://github.com/zeit/hyper
* Fixed: correct capitalization of Xdebug (apparently).
* Fixed: improved handling for uopz extension.
## [1.3.3] - 2019-05-27
* Fixed: add environment changes to `$_ENV` if it is being used.
## [1.3.2] - 2019-01-28
* Fixed: exit call being blocked by uopz extension, resulting in application code running twice.
## [1.3.1] - 2018-11-29
* Fixed: fail restart if `passthru` has been disabled in `disable_functions`.
* Fixed: fail restart if an ini file cannot be opened, otherwise settings will be missing.
## [1.3.0] - 2018-08-31
* Added: `setPersistent` method to use environment variables for the restart.
* Fixed: improved debugging by writing output to stderr.
* Fixed: no restart when `php_ini_scanned_files` is not functional and is needed.
## [1.2.1] - 2018-08-23
* Fixed: fatal error with apc, when using `apc.mmap_file_mask`.
## [1.2.0] - 2018-08-16
* Added: debug information using `XDEBUG_HANDLER_DEBUG`.
* Added: fluent interface for setters.
* Added: `PhpConfig` helper class for calling PHP sub-processes.
* Added: `PHPRC` original value to restart stettings, for use in a restarted process.
* Changed: internal procedure to disable ini-scanning, using `-n` command-line option.
* Fixed: replaced `escapeshellarg` usage to avoid locale problems.
* Fixed: improved color-option handling to respect double-dash delimiter.
* Fixed: color-option handling regression from main script changes.
* Fixed: improved handling when checking main script.
* Fixed: handling for standard input, that never actually did anything.
* Fixed: fatal error when ctype extension is not available.
## [1.1.0] - 2018-04-11
* Added: `getRestartSettings` method for calling PHP processes in a restarted process.
* Added: API definition and @internal class annotations.
* Added: protected `requiresRestart` method for extending classes.
* Added: `setMainScript` method for applications that change the working directory.
* Changed: private `tmpIni` variable to protected for extending classes.
* Fixed: environment variables not available in $_SERVER when restored in the restart.
* Fixed: relative path problems caused by Phar::interceptFileFuncs.
* Fixed: incorrect handling when script file cannot be found.
## [1.0.0] - 2018-03-08
* Added: PSR3 logging for optional status output.
* Added: existing ini settings are merged to catch command-line overrides.
* Added: code, tests and other artefacts to decouple from Composer.
* Break: the following class was renamed:
- `Composer\XdebugHandler` -> `Composer\XdebugHandler\XdebugHandler`
[Unreleased]: https://github.com/composer/xdebug-handler/compare/2.0.5...HEAD
[2.0.5]: https://github.com/composer/xdebug-handler/compare/2.0.4...2.0.5
[2.0.4]: https://github.com/composer/xdebug-handler/compare/2.0.3...2.0.4
[2.0.3]: https://github.com/composer/xdebug-handler/compare/2.0.2...2.0.3
[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
[1.3.2]: https://github.com/composer/xdebug-handler/compare/1.3.1...1.3.2
[1.3.1]: https://github.com/composer/xdebug-handler/compare/1.3.0...1.3.1
[1.3.0]: https://github.com/composer/xdebug-handler/compare/1.2.1...1.3.0
[1.2.1]: https://github.com/composer/xdebug-handler/compare/1.2.0...1.2.1
[1.2.0]: https://github.com/composer/xdebug-handler/compare/1.1.0...1.2.0
[1.1.0]: https://github.com/composer/xdebug-handler/compare/1.0.0...1.1.0
[1.0.0]: https://github.com/composer/xdebug-handler/compare/d66f0d15cb57...1.0.0

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Composer
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,291 @@
# composer/xdebug-handler
[![packagist](https://img.shields.io/badge/packagist-v2.0.4-blue)](https://packagist.org/packages/composer/xdebug-handler#2.0.4)
[![Continuous Integration](https://github.com/composer/xdebug-handler/actions/workflows/continuous-integration.yml/badge.svg?branch=2.0)](https://github.com/composer/xdebug-handler/actions?query=branch:2.0)
![license](https://img.shields.io/github/license/composer/xdebug-handler.svg)
![php](https://img.shields.io/packagist/php-v/composer/xdebug-handler/2.0.2?colorB=8892BF)
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:
```bash
$ composer require composer/xdebug-handler
```
## Requirements
* PHP 5.3.2 minimum, although functionality is disabled below PHP 5.4.0. Using the latest PHP version is highly recommended.
## Basic Usage
```php
use Composer\XdebugHandler\XdebugHandler;
$xdebug = new XdebugHandler('myapp');
$xdebug->check();
unset($xdebug);
```
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
## Advanced Usage
* [How it works](#how-it-works)
* [Limitations](#limitations)
* [Helper methods](#helper-methods)
* [Setter methods](#setter-methods)
* [Process configuration](#process-configuration)
* [Troubleshooting](#troubleshooting)
* [Extending the library](#extending-the-library)
### How it works
A temporary ini file is created from the loaded (and scanned) ini files, with any references to the Xdebug extension commented out. Current ini settings are merged, so that most ini settings made on the command-line or by the application are included (see [Limitations](#limitations))
* `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.
* 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.
* Extensions set on the command-line will not be loaded.
* Ini file locations will be reported as per the restart - see [getAllIniFiles()](#getallinifiles).
* Php sub-processes may be loaded with Xdebug enabled - see [Process configuration](#process-configuration).
### Helper methods
These static methods provide information from the current process, regardless of whether it has been restarted or not.
#### _getAllIniFiles()_
Returns an array of the original ini file locations. Use this instead of calling `php_ini_loaded_file` and `php_ini_scanned_files`, which will report the wrong values in a restarted process.
```php
use Composer\XdebugHandler\XdebugHandler;
$files = XdebugHandler::getAllIniFiles();
# $files[0] always exists, it could be an empty string
$loadedIni = array_shift($files);
$scannedInis = $files;
```
These locations are also available in the `MYAPP_ORIGINAL_INIS` environment variable. This is a path-separated string comprising the location returned from `php_ini_loaded_file`, which could be empty, followed by locations parsed from calling `php_ini_scanned_files`.
#### _getRestartSettings()_
Returns an array of settings that can be used with PHP [sub-processes](#sub-processes), or null if the process was not restarted.
```php
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()_
Returns the Xdebug version string that was skipped by the restart, or an empty value if there was no restart (or Xdebug is still loaded, perhaps by an extending class restarting for a reason other than removing Xdebug).
```php
use Composer\XdebugHandler\XdebugHandler;
$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.
#### _setLogger($logger)_
Enables the output of status messages to an external PSR3 logger. All messages are reported with either `DEBUG` or `WARNING` log levels. For example (showing the level and message):
```
// Restart overridden
DEBUG Checking MYAPP_ALLOW_XDEBUG
DEBUG The Xdebug extension is loaded (2.5.0)
DEBUG No restart (MYAPP_ALLOW_XDEBUG=1)
// Failed restart
DEBUG Checking MYAPP_ALLOW_XDEBUG
DEBUG The Xdebug extension is loaded (2.5.0)
WARNING No restart (Unable to create temp ini file at: ...)
```
Status messages can also be output with `XDEBUG_HANDLER_DEBUG`. See [Troubleshooting](#troubleshooting).
#### _setMainScript($script)_
Sets the location of the main script to run in the restart. This is only needed in more esoteric use-cases, or if the `argv[0]` location is inaccessible. The script name `--` is supported for standard input.
#### _setPersistent()_
Configures the restart using [persistent settings](#persistent-settings), so that Xdebug is not loaded in any sub-process.
Use this method if your application invokes one or more PHP sub-process and the Xdebug extension is not needed. This avoids the overhead of implementing specific [sub-process](#sub-processes) strategies.
Alternatively, this method can be used to set up a default _Xdebug-free_ environment which can be changed if a sub-process requires Xdebug, then restored afterwards:
```php
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();
}
```
### Process configuration
The library offers two strategies to invoke a new PHP process without loading Xdebug, using either _standard_ or _persistent_ settings. Note that this is only important if the application calls a PHP sub-process.
#### Standard settings
Uses command-line options to remove Xdebug from the new process only.
* The -n option is added to the command-line. This tells PHP not to scan for additional inis.
* The temporary ini is added to the command-line with the -c option.
>_If the new process calls a PHP sub-process, Xdebug will be loaded in that sub-process (unless it implements xdebug-handler, in which case there will be another restart)._
This is the default strategy used in the restart.
#### Persistent settings
Uses environment variables to remove Xdebug from the new process and persist these settings to any sub-process.
* `PHP_INI_SCAN_DIR` is set to an empty string. This tells PHP not to scan for additional inis.
* `PHPRC` is set to the temporary ini.
>_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).
#### 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.
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.
* `useOriginal()` - Xdebug will be loaded in the new process.
* `useStandard()` - Xdebug will **not** be loaded in the new process - see [standard settings](#standard-settings).
* `userPersistent()` - Xdebug will **not** be loaded in the new process - see [persistent settings](#persistent-settings)
If there was no restart, an empty options array is returned and the environment is not changed.
```php
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=''
```
### Troubleshooting
The following environment settings can be used to troubleshoot unexpected behavior:
* `XDEBUG_HANDLER_DEBUG=1` Outputs status messages to `STDERR`, if it is defined, irrespective of any PSR3 logger. Each message is prefixed `xdebug-handler[pid]`, where pid is the process identifier.
* `XDEBUG_HANDLER_DEBUG=2` As above, but additionally saves the temporary ini file and reports its location in a status message.
### 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($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.
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)`.
#### Example
This example demonstrates two ways to extend basic functionality:
* To avoid the overhead of spinning up a new process, the restart is skipped if a simple help command is requested.
* The application needs write-access to phar files, so it will force a restart if `phar.readonly` is set (regardless of whether Xdebug is loaded) and change this value in the temporary ini file.
```php
use Composer\XdebugHandler\XdebugHandler;
use MyApp\Command;
class MyRestarter extends XdebugHandler
{
private $required;
protected function requiresRestart($default)
{
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($command)
{
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);
}
}
```
## License
composer/xdebug-handler is licensed under the MIT License, see the LICENSE file for details.

View File

@@ -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.

View File

@@ -0,0 +1,44 @@
{
"name": "composer\/xdebug-handler",
"description": "Restarts a process without Xdebug.",
"type": "library",
"license": "MIT",
"keywords": [
"xdebug",
"performance"
],
"authors": [
{
"name": "John Stevenson",
"email": "john-stevenson@blueyonder.co.uk"
}
],
"support": {
"irc": "irc:\/\/irc.freenode.org\/composer",
"issues": "https:\/\/github.com\/composer\/xdebug-handler\/issues"
},
"require": {
"php": "^5.3.2 || ^7.0 || ^8.0",
"psr\/log": "^1 || ^2 || ^3",
"composer\/pcre": "^1"
},
"require-dev": {
"symfony\/phpunit-bridge": "^4.2 || ^5.0 || ^6.0",
"phpstan\/phpstan": "^1.0",
"phpstan\/phpstan-strict-rules": "^1.1"
},
"autoload": {
"psr-4": {
"ps_metrics_module_v4_0_5\\Composer\\XdebugHandler\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"ps_metrics_module_v4_0_5\\Composer\\XdebugHandler\\Tests\\": "tests"
}
},
"scripts": {
"test": "vendor\/bin\/simple-phpunit",
"phpstan": "vendor\/bin\/phpstan analyse"
}
}

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,84 @@
<?php
/*
* This file is part of composer/xdebug-handler.
*
* (c) Composer <https://github.com/composer>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace ps_metrics_module_v4_0_5\Composer\XdebugHandler;
/**
* @author John Stevenson <john-stevenson@blueyonder.co.uk>
*
* @phpstan-type restartData array{tmpIni: string, scannedInis: bool, scanDir: false|string, phprc: false|string, inis: string[], skipped: string}
*/
class PhpConfig
{
/**
* Use the original PHP configuration
*
* @return string[] Empty array of PHP cli options
*/
public function useOriginal()
{
$this->getDataAndReset();
return array();
}
/**
* Use standard restart settings
*
* @return string[] PHP cli options
*/
public function useStandard()
{
$data = $this->getDataAndReset();
if ($data !== null) {
return array('-n', '-c', $data['tmpIni']);
}
return array();
}
/**
* Use environment variables to persist settings
*
* @return string[] Empty array of PHP cli options
*/
public function usePersistent()
{
$data = $this->getDataAndReset();
if ($data !== null) {
$this->updateEnv('PHPRC', $data['tmpIni']);
$this->updateEnv('PHP_INI_SCAN_DIR', '');
}
return array();
}
/**
* Returns restart data if available and resets the environment
*
* @return array|null
* @phpstan-return restartData|null
*/
private function getDataAndReset()
{
$data = XdebugHandler::getRestartSettings();
if ($data !== null) {
$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
*
* @return void
*/
private function updateEnv($name, $value)
{
Process::setEnv($name, \false !== $value ? $value : null);
}
}

View File

@@ -0,0 +1,104 @@
<?php
/*
* This file is part of composer/xdebug-handler.
*
* (c) Composer <https://github.com/composer>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace ps_metrics_module_v4_0_5\Composer\XdebugHandler;
use ps_metrics_module_v4_0_5\Composer\Pcre\Preg;
/**
* Process utility functions
*
* @author John Stevenson <john-stevenson@blueyonder.co.uk>
*/
class Process
{
/**
* Escapes a string to be used as a shell argument.
*
* From https://github.com/johnstevenson/winbox-args
* MIT Licensed (c) John Stevenson <john-stevenson@blueyonder.co.uk>
*
* @param string $arg The argument to be escaped
* @param bool $meta Additionally escape cmd.exe meta characters
* @param bool $module The argument is the module to invoke
*
* @return string The escaped argument
*/
public static function escape($arg, $meta = \true, $module = \false)
{
if (!\defined('PHP_WINDOWS_VERSION_BUILD')) {
return "'" . \str_replace("'", "'\\''", $arg) . "'";
}
$quote = \strpbrk($arg, " \t") !== \false || $arg === '';
$arg = Preg::replace('/(\\\\*)"/', '$1$1\\"', $arg, -1, $dquotes);
if ($meta) {
$meta = $dquotes || Preg::isMatch('/%[^%]+%/', $arg);
if (!$meta) {
$quote = $quote || \strpbrk($arg, '^&|<>()') !== \false;
} elseif ($module && !$dquotes && $quote) {
$meta = \false;
}
}
if ($quote) {
$arg = '"' . Preg::replace('/(\\\\*)$/', '$1$1', $arg) . '"';
}
if ($meta) {
$arg = Preg::replace('/(["^&|<>()%])/', '^$1', $arg);
}
return $arg;
}
/**
* Escapes an array of arguments that make up a shell command
*
* @param string[] $args Argument list, with the module name first
*
* @return string The escaped command line
*/
public static function escapeShellCommand(array $args)
{
$command = '';
$module = \array_shift($args);
if ($module !== null) {
$command = self::escape($module, \true, \true);
foreach ($args as $arg) {
$command .= ' ' . self::escape($arg);
}
}
return $command;
}
/**
* Makes putenv environment changes available in $_SERVER and $_ENV
*
* @param string $name
* @param string|null $value A null value unsets the variable
*
* @return bool Whether the environment variable was set
*/
public static function setEnv($name, $value = null)
{
$unset = null === $value;
if (!\putenv($unset ? $name : $name . '=' . $value)) {
return \false;
}
if ($unset) {
unset($_SERVER[$name]);
} else {
$_SERVER[$name] = $value;
}
// Update $_ENV if it is being used
if (\false !== \stripos((string) \ini_get('variables_order'), 'E')) {
if ($unset) {
unset($_ENV[$name]);
} else {
$_ENV[$name] = $value;
}
}
return \true;
}
}

View File

@@ -0,0 +1,197 @@
<?php
/*
* This file is part of composer/xdebug-handler.
*
* (c) Composer <https://github.com/composer>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace ps_metrics_module_v4_0_5\Composer\XdebugHandler;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
/**
* @author John Stevenson <john-stevenson@blueyonder.co.uk>
* @internal
*/
class Status
{
const ENV_RESTART = 'XDEBUG_HANDLER_RESTART';
const CHECK = 'Check';
const ERROR = 'Error';
const INFO = 'Info';
const NORESTART = 'NoRestart';
const RESTART = 'Restart';
const RESTARTING = 'Restarting';
const RESTARTED = 'Restarted';
/** @var bool */
private $debug;
/** @var string */
private $envAllowXdebug;
/** @var string|null */
private $loaded;
/** @var LoggerInterface|null */
private $logger;
/** @var bool */
private $modeOff;
/** @var float */
private $time;
/**
* Constructor
*
* @param string $envAllowXdebug Prefixed _ALLOW_XDEBUG name
* @param bool $debug Whether debug output is required
*/
public function __construct($envAllowXdebug, $debug)
{
$start = \getenv(self::ENV_RESTART);
Process::setEnv(self::ENV_RESTART);
$this->time = \is_numeric($start) ? \round((\microtime(\true) - $start) * 1000) : 0;
$this->envAllowXdebug = $envAllowXdebug;
$this->debug = $debug && \defined('STDERR');
$this->modeOff = \false;
}
/**
* @param LoggerInterface $logger
*
* @return void
*/
public function setLogger(LoggerInterface $logger)
{
$this->logger = $logger;
}
/**
* Calls a handler method to report a message
*
* @param string $op The handler constant
* @param null|string $data Data required by the handler
*
* @return void
* @throws \InvalidArgumentException If $op is not known
*/
public function report($op, $data)
{
if ($this->logger !== null || $this->debug) {
$callable = array($this, 'report' . $op);
if (!\is_callable($callable)) {
throw new \InvalidArgumentException('Unknown op handler: ' . $op);
}
$params = $data !== null ? $data : array();
\call_user_func_array($callable, array($params));
}
}
/**
* Outputs a status message
*
* @param string $text
* @param string $level
*
* @return void
*/
private function output($text, $level = null)
{
if ($this->logger !== null) {
$this->logger->log($level !== null ? $level : LogLevel::DEBUG, $text);
}
if ($this->debug) {
\fwrite(\STDERR, \sprintf('xdebug-handler[%d] %s', \getmypid(), $text . \PHP_EOL));
}
}
/**
* @param string $loaded
*
* @return void
*/
private function reportCheck($loaded)
{
list($version, $mode) = \explode('|', $loaded);
if ($version !== '') {
$this->loaded = '(' . $version . ')' . ($mode !== '' ? ' mode=' . $mode : '');
}
$this->modeOff = $mode === 'off';
$this->output('Checking ' . $this->envAllowXdebug);
}
/**
* @param string $error
*
* @return void
*/
private function reportError($error)
{
$this->output(\sprintf('No restart (%s)', $error), LogLevel::WARNING);
}
/**
* @param string $info
*
* @return void
*/
private function reportInfo($info)
{
$this->output($info);
}
/**
* @return void
*/
private function reportNoRestart()
{
$this->output($this->getLoadedMessage());
if ($this->loaded !== null) {
$text = \sprintf('No restart (%s)', $this->getEnvAllow());
if (!(bool) \getenv($this->envAllowXdebug)) {
$text .= ' Allowed by ' . ($this->modeOff ? 'mode' : 'application');
}
$this->output($text);
}
}
/**
* @return void
*/
private function reportRestart()
{
$this->output($this->getLoadedMessage());
Process::setEnv(self::ENV_RESTART, (string) \microtime(\true));
}
/**
* @return void
*/
private function reportRestarted()
{
$loaded = $this->getLoadedMessage();
$text = \sprintf('Restarted (%d ms). %s', $this->time, $loaded);
$level = $this->loaded !== null ? LogLevel::WARNING : null;
$this->output($text, $level);
}
/**
* @param string $command
*
* @return void
*/
private function reportRestarting($command)
{
$text = \sprintf('Process restarting (%s)', $this->getEnvAllow());
$this->output($text);
$text = 'Running ' . $command;
$this->output($text);
}
/**
* Returns the _ALLOW_XDEBUG environment variable as name=value
*
* @return string
*/
private function getEnvAllow()
{
return $this->envAllowXdebug . '=' . \getenv($this->envAllowXdebug);
}
/**
* Returns the Xdebug status and version
*
* @return string
*/
private function getLoadedMessage()
{
$loaded = $this->loaded !== null ? \sprintf('loaded %s', $this->loaded) : 'not loaded';
return 'The Xdebug extension is ' . $loaded;
}
}

View File

@@ -0,0 +1,615 @@
<?php
/*
* This file is part of composer/xdebug-handler.
*
* (c) Composer <https://github.com/composer>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace ps_metrics_module_v4_0_5\Composer\XdebugHandler;
use ps_metrics_module_v4_0_5\Composer\Pcre\Preg;
use Psr\Log\LoggerInterface;
/**
* @author John Stevenson <john-stevenson@blueyonder.co.uk>
*
* @phpstan-import-type restartData from PhpConfig
*/
class XdebugHandler
{
const SUFFIX_ALLOW = '_ALLOW_XDEBUG';
const SUFFIX_INIS = '_ORIGINAL_INIS';
const RESTART_ID = 'internal';
const RESTART_SETTINGS = 'XDEBUG_HANDLER_SETTINGS';
const DEBUG = 'XDEBUG_HANDLER_DEBUG';
/** @var string|null */
protected $tmpIni;
/** @var bool */
private static $inRestart;
/** @var string */
private static $name;
/** @var string|null */
private static $skipped;
/** @var bool */
private static $xdebugActive;
/** @var string|null */
private static $xdebugMode;
/** @var string|null */
private static $xdebugVersion;
/** @var bool */
private $cli;
/** @var string|null */
private $debug;
/** @var string */
private $envAllowXdebug;
/** @var string */
private $envOriginalInis;
/** @var bool */
private $persistent;
/** @var string|null */
private $script;
/** @var Status */
private $statusWriter;
/**
* Constructor
*
* The $envPrefix is used to create distinct environment variables. It is
* uppercased and prepended to the default base values. For example 'myapp'
* would result in MYAPP_ALLOW_XDEBUG and MYAPP_ORIGINAL_INIS.
*
* @param string $envPrefix Value used in environment variables
* @throws \RuntimeException If the parameter is invalid
*/
public function __construct($envPrefix)
{
if (!\is_string($envPrefix) || $envPrefix === '') {
throw new \RuntimeException('Invalid constructor parameter');
}
self::$name = \strtoupper($envPrefix);
$this->envAllowXdebug = self::$name . self::SUFFIX_ALLOW;
$this->envOriginalInis = self::$name . self::SUFFIX_INIS;
self::setXdebugDetails();
self::$inRestart = \false;
if ($this->cli = \PHP_SAPI === 'cli') {
$this->debug = (string) \getenv(self::DEBUG);
}
$this->statusWriter = new Status($this->envAllowXdebug, (bool) $this->debug);
}
/**
* Activates status message output to a PSR3 logger
*
* @param LoggerInterface $logger
*
* @return $this
*/
public function setLogger(LoggerInterface $logger)
{
$this->statusWriter->setLogger($logger);
return $this;
}
/**
* Sets the main script location if it cannot be called from argv
*
* @param string $script
*
* @return $this
*/
public function setMainScript($script)
{
$this->script = $script;
return $this;
}
/**
* Persist the settings to keep Xdebug out of sub-processes
*
* @return $this
*/
public function setPersistent()
{
$this->persistent = \true;
return $this;
}
/**
* Checks if Xdebug is loaded and the process needs to be restarted
*
* This behaviour can be disabled by setting the MYAPP_ALLOW_XDEBUG
* environment variable to 1. This variable is used internally so that
* the restarted process is created only once.
*
* @return void
*/
public function check()
{
$this->notify(Status::CHECK, self::$xdebugVersion . '|' . self::$xdebugMode);
$envArgs = \explode('|', (string) \getenv($this->envAllowXdebug));
if (!(bool) $envArgs[0] && $this->requiresRestart(self::$xdebugActive)) {
// Restart required
$this->notify(Status::RESTART);
if ($this->prepareRestart()) {
$command = $this->getCommand();
$this->restart($command);
}
return;
}
if (self::RESTART_ID === $envArgs[0] && \count($envArgs) === 5) {
// Restarted, so unset environment variable and use saved values
$this->notify(Status::RESTARTED);
Process::setEnv($this->envAllowXdebug);
self::$inRestart = \true;
if (self::$xdebugVersion === null) {
// Skipped version is only set if Xdebug is not loaded
self::$skipped = $envArgs[1];
}
$this->tryEnableSignals();
// Put restart settings in the environment
$this->setEnvRestartSettings($envArgs);
return;
}
$this->notify(Status::NORESTART);
$settings = self::getRestartSettings();
if ($settings !== null) {
// Called with existing settings, so sync our settings
$this->syncSettings($settings);
}
}
/**
* 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 loaded ini location is the first entry and may be empty.
*
* @return string[]
*/
public static function getAllIniFiles()
{
if (self::$name !== null) {
$env = \getenv(self::$name . self::SUFFIX_INIS);
if (\false !== $env) {
return \explode(\PATH_SEPARATOR, $env);
}
}
$paths = array((string) \php_ini_loaded_file());
$scanned = \php_ini_scanned_files();
if ($scanned !== \false) {
$paths = \array_merge($paths, \array_map('trim', \explode(',', $scanned)));
}
return $paths;
}
/**
* Returns an array of restart settings or null
*
* Settings will be available if the current process was restarted, or
* called with the settings from an existing restart.
*
* @return array|null
* @phpstan-return restartData|null
*/
public static function getRestartSettings()
{
$envArgs = \explode('|', (string) \getenv(self::RESTART_SETTINGS));
if (\count($envArgs) !== 6 || !self::$inRestart && \php_ini_loaded_file() !== $envArgs[0]) {
return null;
}
return array('tmpIni' => $envArgs[0], 'scannedInis' => (bool) $envArgs[1], 'scanDir' => '*' === $envArgs[2] ? \false : $envArgs[2], 'phprc' => '*' === $envArgs[3] ? \false : $envArgs[3], 'inis' => \explode(\PATH_SEPARATOR, $envArgs[4]), 'skipped' => $envArgs[5]);
}
/**
* Returns the Xdebug version that triggered a successful restart
*
* @return string
*/
public static function getSkippedVersion()
{
return (string) self::$skipped;
}
/**
* Returns whether Xdebug is loaded and active
*
* 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
*/
public static function isXdebugActive()
{
self::setXdebugDetails();
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
*
* Do not typehint for 1.x compatibility
*
* @param string[] $command
*
* @return void
*/
protected function restart($command)
{
$this->doRestart($command);
}
/**
* Executes the restarted command then deletes the tmp ini
*
* @param string[] $command
*
* @return void
* @phpstan-return never
*/
private function doRestart(array $command)
{
$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);
} else {
@\unlink((string) $this->tmpIni);
}
exit($exitCode);
}
/**
* Returns true if everything was written for the restart
*
* If any of the following fails (however unlikely) we must return false to
* stop potential recursion:
* - tmp ini file creation
* - environment variable creation
*
* @return bool
*/
private function prepareRestart()
{
$error = null;
$iniFiles = self::getAllIniFiles();
$scannedInis = \count($iniFiles) > 1;
$tmpDir = \sys_get_temp_dir();
if (!$this->cli) {
$error = 'Unsupported SAPI: ' . \PHP_SAPI;
} elseif (!\defined('PHP_BINARY')) {
$error = 'PHP version is too old: ' . \PHP_VERSION;
} elseif (!$this->checkConfiguration($info)) {
$error = $info;
} elseif (!$this->checkScanDirConfig()) {
$error = 'PHP version does not report scanned inis: ' . \PHP_VERSION;
} 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) {
$this->notify(Status::ERROR, $error);
}
return $error === null;
}
/**
* Returns true if the tmp ini file was written
*
* @param string[] $iniFiles All ini files used in the current process
* @param string $tmpDir The system temporary directory
* @param null|string $error Set by method if ini file cannot be read
*
* @return bool
*/
private function writeTmpIni(array $iniFiles, $tmpDir, &$error)
{
if (($tmpfile = @\tempnam($tmpDir, '')) === \false) {
return \false;
}
$this->tmpIni = $tmpfile;
// $iniFiles has at least one item and it may be empty
if ($iniFiles[0] === '') {
\array_shift($iniFiles);
}
$content = '';
$sectionRegex = '/^\\s*\\[(?:PATH|HOST)\\s*=/mi';
$xdebugRegex = '/^\\s*(zend_extension\\s*=.*xdebug.*)$/mi';
foreach ($iniFiles as $file) {
// Check for inaccessible ini files
if (($data = @\file_get_contents($file)) === \false) {
$error = 'Unable to read ini: ' . $file;
return \false;
}
// Check and remove directives after HOST and PATH sections
if (Preg::isMatchWithOffsets($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
$config = \parse_ini_string($content);
$loaded = \ini_get_all(null, \false);
if (\false === $config || \false === $loaded) {
$error = 'Unable to parse ini data';
return \false;
}
$content .= $this->mergeLoadedConfig($loaded, $config);
// Work-around for https://bugs.php.net/bug.php?id=75932
$content .= 'opcache.enable_cli=0' . \PHP_EOL;
return (bool) @\file_put_contents($this->tmpIni, $content);
}
/**
* Returns the command line arguments for the restart
*
* @return string[]
*/
private function getCommand()
{
$php = array(\PHP_BINARY);
$args = \array_slice($_SERVER['argv'], 1);
if (!$this->persistent) {
// Use command-line options
\array_push($php, '-n', '-c', $this->tmpIni);
}
return \array_merge($php, array($this->script), $args);
}
/**
* Returns true if the restart environment variables were set
*
* No need to update $_SERVER since this is set in the restarted process.
*
* @param bool $scannedInis Whether there were scanned ini files
* @param string[] $iniFiles All ini files used in the current process
*
* @return bool
*/
private function setEnvironment($scannedInis, array $iniFiles)
{
$scanDir = \getenv('PHP_INI_SCAN_DIR');
$phprc = \getenv('PHPRC');
// Make original inis available to restarted process
if (!\putenv($this->envOriginalInis . '=' . \implode(\PATH_SEPARATOR, $iniFiles))) {
return \false;
}
if ($this->persistent) {
// Use the environment to persist the settings
if (!\putenv('PHP_INI_SCAN_DIR=') || !\putenv('PHPRC=' . $this->tmpIni)) {
return \false;
}
}
// Flag restarted process and save values for it to use
$envArgs = array(self::RESTART_ID, self::$xdebugVersion, (int) $scannedInis, \false === $scanDir ? '*' : $scanDir, \false === $phprc ? '*' : $phprc);
return \putenv($this->envAllowXdebug . '=' . \implode('|', $envArgs));
}
/**
* Logs status messages
*
* @param string $op Status handler constant
* @param null|string $data Optional data
*
* @return void
*/
private function notify($op, $data = null)
{
$this->statusWriter->report($op, $data);
}
/**
* Returns default, changed and command-line ini settings
*
* @param mixed[] $loadedConfig All current ini settings
* @param mixed[] $iniConfig Settings from user ini files
*
* @return string
*/
private function mergeLoadedConfig(array $loadedConfig, array $iniConfig)
{
$content = '';
foreach ($loadedConfig as $name => $value) {
// Value will either be null, string or array (HHVM only)
if (!\is_string($value) || \strpos($name, 'xdebug') === 0 || $name === 'apc.mmap_file_mask') {
continue;
}
if (!isset($iniConfig[$name]) || $iniConfig[$name] !== $value) {
// Double-quote escape each value
$content .= $name . '="' . \addcslashes($value, '\\"') . '"' . \PHP_EOL;
}
}
return $content;
}
/**
* Returns true if the script name can be used
*
* @return bool
*/
private function checkMainScript()
{
if (null !== $this->script) {
// Allow an application to set -- for standard input
return \file_exists($this->script) || '--' === $this->script;
}
if (\file_exists($this->script = $_SERVER['argv'][0])) {
return \true;
}
// Use a backtrace to resolve Phar and chdir issues.
$trace = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS);
$main = \end($trace);
if ($main !== \false && isset($main['file'])) {
return \file_exists($this->script = $main['file']);
}
return \false;
}
/**
* Adds restart settings to the environment
*
* @param string[] $envArgs
*
* @return void
*/
private function setEnvRestartSettings($envArgs)
{
$settings = array(\php_ini_loaded_file(), $envArgs[2], $envArgs[3], $envArgs[4], \getenv($this->envOriginalInis), self::$skipped);
Process::setEnv(self::RESTART_SETTINGS, \implode('|', $settings));
}
/**
* Syncs settings and the environment if called with existing settings
*
* @param array $settings
* @phpstan-param restartData $settings
*
* @return void
*/
private function syncSettings(array $settings)
{
if (\false === \getenv($this->envOriginalInis)) {
// Called by another app, so make original inis available
Process::setEnv($this->envOriginalInis, \implode(\PATH_SEPARATOR, $settings['inis']));
}
self::$skipped = $settings['skipped'];
$this->notify(Status::INFO, 'Process called with existing restart settings');
}
/**
* Returns true if there are scanned inis and PHP is able to report them
*
* php_ini_scanned_files will fail when PHP_CONFIG_FILE_SCAN_DIR is empty.
* Fixed in 7.1.13 and 7.2.1
*
* @return bool
*/
private function checkScanDirConfig()
{
if (\PHP_VERSION_ID >= 70113 && \PHP_VERSION_ID !== 70200) {
return \true;
}
return (string) \getenv('PHP_INI_SCAN_DIR') === '' || \PHP_CONFIG_FILE_SCAN_DIR !== '';
}
/**
* Returns true if there are no known configuration issues
*
* @param string $info Set by method
* @return bool
*/
private function checkConfiguration(&$info)
{
if (!\function_exists('proc_open')) {
$info = 'proc_open function is disabled';
return \false;
}
if (\extension_loaded('uopz') && !(bool) \ini_get('uopz.disable')) {
// uopz works at opcode level and disables exit calls
if (\function_exists('uopz_allow_exit')) {
@\uopz_allow_exit(\true);
} else {
$info = 'uopz extension is not compatible';
return \false;
}
}
// Check UNC paths when using cmd.exe
if (\defined('PHP_WINDOWS_VERSION_BUILD') && \PHP_VERSION_ID < 70400) {
$workingDir = \getcwd();
if ($workingDir === \false) {
$info = 'unable to determine working directory';
return \false;
}
if (0 === \strpos($workingDir, '\\\\')) {
$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+.
*
* @return void
*/
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) {
});
}
}
/**
* Sets static properties $xdebugActive, $xdebugVersion and $xdebugMode
*
* @return void
*/
private static function setXdebugDetails()
{
if (self::$xdebugActive !== null) {
return;
}
self::$xdebugActive = \false;
if (!\extension_loaded('xdebug')) {
return;
}
$version = \phpversion('xdebug');
self::$xdebugVersion = $version !== \false ? $version : 'unknown';
if (\version_compare(self::$xdebugVersion, '3.1', '>=')) {
$modes = \xdebug_info('mode');
self::$xdebugMode = \count($modes) === 0 ? 'off' : \implode(',', $modes);
self::$xdebugActive = self::$xdebugMode !== 'off';
return;
}
// See if xdebug.mode is supported in this version
$iniMode = \ini_get('xdebug.mode');
if ($iniMode === \false) {
self::$xdebugActive = \true;
return;
}
// Environment value wins but cannot be empty
$envMode = (string) \getenv('XDEBUG_MODE');
if ($envMode !== '') {
self::$xdebugMode = $envMode;
} else {
self::$xdebugMode = $iniMode !== '' ? $iniMode : 'off';
}
// An empty comma-separated list is treated as mode 'off'
if (Preg::isMatch('/^,+$/', \str_replace(' ', '', self::$xdebugMode))) {
self::$xdebugMode = 'off';
}
self::$xdebugActive = self::$xdebugMode !== 'off';
}
}

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;