{"id":13562523,"url":"https://github.com/composer/xdebug-handler","last_synced_at":"2025-05-13T17:09:21.473Z","repository":{"id":37743241,"uuid":"111680504","full_name":"composer/xdebug-handler","owner":"composer","description":"Restart a CLI process without loading the xdebug extension.","archived":false,"fork":false,"pushed_at":"2024-08-27T15:04:10.000Z","size":355,"stargazers_count":2531,"open_issues_count":0,"forks_count":33,"subscribers_count":9,"default_branch":"main","last_synced_at":"2025-05-03T02:08:17.629Z","etag":null,"topics":["performance","php","restart","xdebug"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/composer.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"custom":"https://packagist.com","tidelift":"packagist/composer/composer","github":"composer"}},"created_at":"2017-11-22T12:18:41.000Z","updated_at":"2025-04-27T08:50:35.000Z","dependencies_parsed_at":"2023-02-09T16:16:30.181Z","dependency_job_id":"56a9d94b-1f49-4174-b417-f992ed505903","html_url":"https://github.com/composer/xdebug-handler","commit_stats":{"total_commits":196,"total_committers":21,"mean_commits":9.333333333333334,"dds":"0.18367346938775508","last_synced_commit":"b919b82441351d9c8450b8051e6cdac12df40b29"},"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/composer%2Fxdebug-handler","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/composer%2Fxdebug-handler/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/composer%2Fxdebug-handler/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/composer%2Fxdebug-handler/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/composer","download_url":"https://codeload.github.com/composer/xdebug-handler/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253990468,"owners_count":21995774,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["performance","php","restart","xdebug"],"created_at":"2024-08-01T13:01:09.536Z","updated_at":"2025-05-13T17:09:16.461Z","avatar_url":"https://github.com/composer.png","language":"PHP","readme":"# composer/xdebug-handler\n\n[![packagist](https://img.shields.io/packagist/v/composer/xdebug-handler)](https://packagist.org/packages/composer/xdebug-handler)\n[![Continuous Integration](https://github.com/composer/xdebug-handler/actions/workflows/continuous-integration.yml/badge.svg?branch=main)](https://github.com/composer/xdebug-handler/actions?query=branch:main)\n![license](https://img.shields.io/github/license/composer/xdebug-handler.svg)\n![php](https://img.shields.io/packagist/php-v/composer/xdebug-handler?colorB=8892BF)\n\nRestart a CLI process without loading the Xdebug extension, unless `xdebug.mode=off`.\n\nOriginally written as part of [composer/composer](https://github.com/composer/composer),\nnow extracted and made available as a stand-alone library.\n\n### Version 3\n\nRemoved support for legacy PHP versions and added type declarations.\n\nLong term support for version 2 (PHP 5.3.2 - 7.2.4) follows [Composer 2.2 LTS](https://blog.packagist.com/composer-2-2/) policy.\n\n## Installation\n\nInstall the latest version with:\n\n```bash\n$ composer require composer/xdebug-handler\n```\n\n## Requirements\n\n* PHP 7.2.5 minimum, although using the latest PHP version is highly recommended.\n\n## Basic Usage\n```php\nuse Composer\\XdebugHandler\\XdebugHandler;\n\n$xdebug = new XdebugHandler('myapp');\n$xdebug-\u003echeck();\nunset($xdebug);\n```\n\nThe 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:\n\n- `MYAPP_ALLOW_XDEBUG=1` to override automatic restart and allow Xdebug\n- `MYAPP_ORIGINAL_INIS` to obtain ini file locations in a restarted process\n\n## Advanced Usage\n\n* [How it works](#how-it-works)\n* [Limitations](#limitations)\n* [Helper methods](#helper-methods)\n* [Setter methods](#setter-methods)\n* [Process configuration](#process-configuration)\n* [Troubleshooting](#troubleshooting)\n* [Extending the library](#extending-the-library)\n* [Examples](#examples)\n\n### How it works\n\nA 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))\n\n* `MYAPP_ALLOW_XDEBUG` is set with internal data to flag and use in the restart.\n* The command-line and environment are [configured](#process-configuration) for the restart.\n* The application is restarted in a new process.\n    * The restart settings are stored in the environment.\n    * `MYAPP_ALLOW_XDEBUG` is unset.\n    * The application runs and exits.\n* The main process exits with the exit code from the restarted process.\n\nSee [Examples](#examples) for further information.\n\n#### Signal handling\nAsynchronous signal handling is automatically enabled if the pcntl extension is loaded. `SIGINT` is set to `SIG_IGN` in the parent\nprocess and restored to `SIG_DFL` in the restarted process (if no other handler has been set).\n\nFrom PHP 7.4 on Windows, `CTRL+C` and `CTRL+BREAK` handling is automatically enabled in the restarted process and ignored in the parent process.\n\n### Limitations\nThere are a few things to be aware of when running inside a restarted process.\n\n* Extensions set on the command-line will not be loaded.\n* Ini file locations will be reported as per the restart - see [getAllIniFiles()](#getallinifiles-array).\n* Php sub-processes may be loaded with Xdebug enabled - see [Process configuration](#process-configuration).\n\n### Helper methods\nThese static methods provide information from the current process, regardless of whether it has been restarted or not.\n\n#### _getAllIniFiles(): array_\nReturns 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.\n\n```php\nuse Composer\\XdebugHandler\\XdebugHandler;\n\n$files = XdebugHandler::getAllIniFiles();\n\n# $files[0] always exists, it could be an empty string\n$loadedIni = array_shift($files);\n$scannedInis = $files;\n```\n\nThese 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`.\n\n#### _getRestartSettings(): ?array_\nReturns an array of settings that can be used with PHP [sub-processes](#sub-processes), or null if the process was not restarted.\n\n```php\nuse Composer\\XdebugHandler\\XdebugHandler;\n\n$settings = XdebugHandler::getRestartSettings();\n/**\n * $settings: array (if the current process was restarted,\n * or called with the settings from a previous restart), or null\n *\n *    'tmpIni'      =\u003e the temporary ini file used in the restart (string)\n *    'scannedInis' =\u003e if there were any scanned inis (bool)\n *    'scanDir'     =\u003e the original PHP_INI_SCAN_DIR value (false|string)\n *    'phprc'       =\u003e the original PHPRC value (false|string)\n *    'inis'        =\u003e the original inis from getAllIniFiles (array)\n *    'skipped'     =\u003e the skipped version from getSkippedVersion (string)\n */\n```\n\n#### _getSkippedVersion(): string_\nReturns the Xdebug version string that was skipped by the restart, or an empty string if there was no restart (or Xdebug is still loaded, perhaps by an extending class restarting for a reason other than removing Xdebug).\n\n```php\nuse Composer\\XdebugHandler\\XdebugHandler;\n\n$version = XdebugHandler::getSkippedVersion();\n# $version: '3.1.1' (for example), or an empty string\n```\n\n#### _isXdebugActive(): bool_\nReturns 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`.\n\n### Setter methods\nThese methods implement a fluent interface and must be called before the main `check()` method.\n\n#### _setLogger(LoggerInterface $logger): self_\nEnables 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):\n\n```\n// No restart\nDEBUG    Checking MYAPP_ALLOW_XDEBUG\nDEBUG    The Xdebug extension is loaded (3.1.1) xdebug.mode=off\nDEBUG    No restart (APP_ALLOW_XDEBUG=0) Allowed by xdebug.mode\n\n// Restart overridden\nDEBUG    Checking MYAPP_ALLOW_XDEBUG\nDEBUG    The Xdebug extension is loaded (3.1.1) xdebug.mode=coverage,debug,develop\nDEBUG    No restart (MYAPP_ALLOW_XDEBUG=1)\n\n// Failed restart\nDEBUG    Checking MYAPP_ALLOW_XDEBUG\nDEBUG    The Xdebug extension is loaded (3.1.0)\nWARNING  No restart (Unable to create temp ini file at: ...)\n```\n\nStatus messages can also be output with `XDEBUG_HANDLER_DEBUG`. See [Troubleshooting](#troubleshooting).\n\n#### _setMainScript(string $script): self_\nSets 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.\n\n#### _setPersistent(): self_\nConfigures the restart using [persistent settings](#persistent-settings), so that Xdebug is not loaded in any sub-process.\n\nUse 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.\n\nAlternatively, 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:\n\n```php\nfunction SubProcessWithXdebug()\n{\n    $phpConfig = new Composer\\XdebugHandler\\PhpConfig();\n\n    # Set the environment to the original configuration\n    $phpConfig-\u003euseOriginal();\n\n    # run the process with Xdebug loaded\n    ...\n\n    # Restore Xdebug-free environment\n    $phpConfig-\u003eusePersistent();\n}\n```\n\n### Process configuration\nThe 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.\n\n#### Standard settings\nUses command-line options to remove Xdebug from the new process only.\n\n* The -n option is added to the command-line. This tells PHP not to scan for additional inis.\n* The temporary ini is added to the command-line with the -c option.\n\n\u003e_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)._\n\nThis is the default strategy used in the restart.\n\n#### Persistent settings\nUses environment variables to remove Xdebug from the new process and persist these settings to any sub-process.\n\n* `PHP_INI_SCAN_DIR` is set to an empty string. This tells PHP not to scan for additional inis.\n* `PHPRC` is set to the temporary ini.\n\n\u003e_If the new process calls a PHP sub-process, Xdebug will not be loaded in that sub-process._\n\nThis strategy can be used in the restart by calling [setPersistent()](#setpersistent-self).\n\n#### Sub-processes\nThe `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.\n\nEach 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.\n\n* `useOriginal()` - Xdebug will be loaded in the new process.\n* `useStandard()` - Xdebug will **not** be loaded in the new process - see [standard settings](#standard-settings).\n* `userPersistent()` - Xdebug will **not** be loaded in the new process - see [persistent settings](#persistent-settings)\n\nIf there was no restart, an empty options array is returned and the environment is not changed.\n\n```php\nuse Composer\\XdebugHandler\\PhpConfig;\n\n$config = new PhpConfig;\n\n$options = $config-\u003euseOriginal();\n# $options:     empty array\n# environment:  PHPRC and PHP_INI_SCAN_DIR set to original values\n\n$options = $config-\u003euseStandard();\n# $options:     [-n, -c, tmpIni]\n# environment:  PHPRC and PHP_INI_SCAN_DIR set to original values\n\n$options = $config-\u003eusePersistent();\n# $options:     empty array\n# environment:  PHPRC=tmpIni, PHP_INI_SCAN_DIR=''\n```\n\n### Troubleshooting\nThe following environment settings can be used to troubleshoot unexpected behavior:\n\n* `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.\n\n* `XDEBUG_HANDLER_DEBUG=2` As above, but additionally saves the temporary ini file and reports its location in a status message.\n\n### Extending the library\nThe 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:\n\n#### _requiresRestart(bool $default): bool_\nBy 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.\nIt 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.\n\nNote that the [setMainScript()](#setmainscriptstring-script-self) and [setPersistent()](#setpersistent-self) setters can be used here, if required.\n\n#### _restart(array $command): void_\nAn 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.\n\nThe `$command` parameter is an array of unescaped command-line arguments that will be used for the new process.\n\nRemember to finish with `parent::restart($command)`.\n\n#### Example\nThis example demonstrates two ways to extend basic functionality:\n\n* To avoid the overhead of spinning up a new process, the restart is skipped if a simple help command is requested.\n\n* 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.\n\n```php\nuse Composer\\XdebugHandler\\XdebugHandler;\nuse MyApp\\Command;\n\nclass MyRestarter extends XdebugHandler\n{\n    private $required;\n\n    protected function requiresRestart(bool $default): bool\n    {\n        if (Command::isHelp()) {\n            # No need to disable Xdebug for this\n            return false;\n        }\n\n        $this-\u003erequired = (bool) ini_get('phar.readonly');\n        return $this-\u003erequired || $default;\n    }\n\n    protected function restart(array $command): void\n    {\n        if ($this-\u003erequired) {\n            # Add required ini setting to tmpIni\n            $content = file_get_contents($this-\u003etmpIni);\n            $content .= 'phar.readonly=0'.PHP_EOL;\n            file_put_contents($this-\u003etmpIni, $content);\n        }\n\n        parent::restart($command);\n    }\n}\n```\n\n### Examples\nThe `tests\\App` directory contains command-line scripts that demonstrate the internal workings in a variety of scenarios.\nSee [Functional Test Scripts](./tests/App/README.md).\n\n## License\ncomposer/xdebug-handler is licensed under the MIT License, see the LICENSE file for details.\n","funding_links":["https://packagist.com","https://tidelift.com/funding/github/packagist/composer/composer","https://github.com/sponsors/composer"],"categories":["PHP","Tools"],"sub_categories":["Support"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcomposer%2Fxdebug-handler","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcomposer%2Fxdebug-handler","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcomposer%2Fxdebug-handler/lists"}