Restart a CLI process without loading the xdebug extension.

Last update: Jun 29, 2022

composer/xdebug-handler

packagist Continuous Integration license php

Restart a CLI process without loading the Xdebug extension, unless xdebug.mode=off.

Originally written as part of composer/composer, now extracted and made available as a stand-alone library.

Version 2

Support added for Xdebug3. See UPGRADE for more information.

Installation

Install the latest version with:

$ 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

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

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)

  • MYAPP_ALLOW_XDEBUG is set with internal data to flag and use in the restart.
  • The command-line and environment are configured 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().
  • Php sub-processes may be loaded with Xdebug enabled - see 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.

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, or null if the process was not restarted.

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

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.

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, 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 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:

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().

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() 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.
  • userPersistent() - Xdebug will not be loaded in the new process - see persistent settings

If there was no restart, an empty options array is returned and the environment is not changed.

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() and 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.

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.

GitHub

https://github.com/composer/xdebug-handler
Comments
  • 1. Xdebug 3 support

    Xdebug 3 has an "off" switch so we can either set XDEBUG_MODE=off in env, or pass -dxdebug.mode=off to PHP when restarting. It should simplify things quite a bit as we don't need to rebuild the ini file and create a temp one etc.

    Reviewed by Seldaek at 2020-11-11 16:47
  • 2. Question of PHPRC

    Why was it decided to use -c over PHPRC?

    With the latter, the process is straightforward:

    • Create our our php.ini with all other files merged it.
    • Set PHPRC to point to a directory with our php.ini, set PHP_INI_SCAN_DIR to an empty string, with previous values saved somewhere.
    • Run the original command as it is in $argv in the updated environment.
    • Restore original PHPRC and PHP_INI_SCAN_DIR.

    No need to twiddle with command line options. No need for any workarounds. All other php invocations will a vanilla environment transparently. Hence, this is also backward compatible.

    Using our xdebug-free environment for any of sub-processes is as simple as setting our custom values for PHPRC and PHP_INI_SCAN_DIR. This can be done from a callback like below, so a user won't even need to know the details:

    XdebugHandler::runWithoutXdebug(function () {
        $process = new Process();
        // and so on
    });
    

    getRestartSettings() and specifically getRestartSettings()['tmpIni'] will became redundant with this approach. All complexity will be hidden from a user.

    (I've already changed Infection's xdebug-handler to follow roughly this approach, with success, and I think this project can be made to adopt it too. Surely not without an owner's approval.)

    Reviewed by sanmai at 2018-04-24 00:09
  • 3. Report status output with -vvv option

    This is something I wanted to add to the original, but it always seemed too much work with too many changes. Unfortunately this PR is not that easy to read, although the 2 new classes (Process and Status) and most of the XdebugHandler changes are clear.

    The end result looks like this, when xdebug is loaded:

    xdebug-handler-output

    And this, when it is not:

    xdebug-handler-output-none

    And this, if there is an error (or PHP < 5.4):

    xdebug-handler-output-error

    And this, if xdebug is allowed:

    xdebug-handler-output-allow

    The only downside is that it has increased the code-size (by 50%). I personally think it is worth it, but I'd like to hear what others think.

    Reviewed by johnstevenson at 2018-01-03 23:05
  • 4. Blocking 1.0 release

    It would be nice to have a list of what's blocking 1.0 release to contribute and have a rough ETA for adoption. Feel free to edit this issue or create milestone

    Reviewed by felixfbecker at 2017-12-19 19:58
  • 5. Process::addColorOption when double dash is one of args

    We faced an issue when we tried to add colour option for command that is using double dash, eg adding --ansi for php-cs-fixer fix -v -- fileA.php fileB.php

    With current state, --ansi ends always as last place, thus after --, which won't work and:

    • command won't be run with enforcing colour output
    • command will get --ansi as position argument (and not option) and fail to handle it

    More details at https://github.com/FriendsOfPHP/PHP-CS-Fixer/issues/3901

    Reviewed by keradus at 2018-07-11 10:39
  • 6. Updates for phpstan level 9

    Above level 7 phpstan gives us:

     ------ -----------------------------------------------------------------------------------------------
      Line   src\Process.php
     ------ -----------------------------------------------------------------------------------------------
      44     Parameter #2 $subject of function preg_match expects string, string|null given.
      47     Parameter #1 $string of function strpbrk expects string, string|null given.
      54     Parameter #3 $subject of function preg_replace expects array|string, string|null given.
      58     Parameter #3 $subject of function preg_replace expects array|string, string|null given.
      61     Method Composer\XdebugHandler\Process::escape() should return string but returns string|null.
     ------ -----------------------------------------------------------------------------------------------
    
     ------ ------------------------------------------------------------------------------
      Line   src\XdebugHandler.php
     ------ ------------------------------------------------------------------------------
      349    Parameter #1 $filename of function unlink expects string, string|null given.
     ------ ------------------------------------------------------------------------------
     [ERROR] Found 6 errors
    

    which this PR fixes by adding suitable (string) casts - which sort of feels a bit wrong.

    Reviewed by johnstevenson at 2021-11-30 19:03
  • 7. Unclear relevance for Composer

    README contains:

    Originally written as part of composer/composer, now extracted and made available as a stand-alone library.

    My interpretation was that this code had been written in Composer, but that the Composer project then decided the code should be removed from Composer, so the code was made available in a new project. But my colleague Jonny Bradley points out that this issue seems to be solved in current Composer, so perhaps "extracted" should not be understood as "moved out" but as "copied".

    Please make the README clear about whether the code is currently part of official Composer.

    Reviewed by Chealer at 2018-08-13 13:27
  • 8. Fixes usage of XDebugHandler in auto_prepend file

    This fixes the code that was supposed to fix main script arg when PHP was reading code from STDIN, but was actually a no-op.

    Also adds a kind of integration test for auto_prepend + code on STDIN use case.

    Reviewed by weirdan at 2018-04-20 16:59
  • 9. This library does not work when invoking a Phar via a relative path

    When the phar is invoked manually (e.g. php -d zend_extension=xdebug.so build/phan.phar), xdebug-handler fails to detect that the file exists.

    Adding debugging statements, I see that the Phar path (i.e. $args[0]) gets replaced by '--' (EDIT: Because it fails to detect that the file exists, xdebug-handler attempts to pass the phar (I assume) over stdin, which doesn't work. That causes the phar to hang after the restart, with no output)

    Patching getCommand() to use the following check would make it not replace the relative path with --: if (!file_exists($args[0]) && !file_exists(getcwd() . '/' . $args[0])) {

    • Obviously, you'd have to check if something is an absolute path, the above snippet is not what you'd really want to use. Something more like the below would make sense. I'm not sure if that's 100% correct -- You may wish to limit this to Phars only for now via https://secure.php.net/manual/en/phar.running.php (Feel free to use the below helper)
        public static function absScriptPath(string $relative_path)
        {   
            // Make sure its actually relative
            if (\DIRECTORY_SEPARATOR === \substr($relative_path, 0, 1)) {
                return $relative_path;
            }
            // Check for absolute path in windows, e.g. C:\ (https://en.wikipedia.org/wiki/Drive_letter_assignment)
            if (\DIRECTORY_SEPARATOR === "\\" &&
                    \strlen($relative_path) > 3 &&
                    \ctype_alpha($relative_path[0]) &&
                    $relative_path[1] === ':' &&
                    \strspn($relative_path, '/\\', 2, 1)) {
                return $relative_path;
            }
    
            return getcwd() . DIRECTORY_SEPARATOR . $relative_path;
        } 
    

    https://stackoverflow.com/a/18378785 sounds like it describes the generic problem for Phars:

    Working with file paths and Phar archives in PHP can be tricky. The PHP code inside of a Phar file will treat relative paths as being relative to the Phar archive, not relative to the current working directory.


    My use case is building phars for https://github.com/phan/phan/releases

    Reviewed by TysonAndre at 2018-03-24 21:31
  • 10. Allow output to be logged or disabled

    Submitted as the result of discussions after the original PR to add status output

    // send INFO and ERROR messages to a PSR3 Logger
    $xdebug->setLogger($myLogger);
    
    // disable output (overrides -vvv option)
    $xdebug->setLogger(null);
    
    Reviewed by johnstevenson at 2018-01-25 16:03
  • 11. Ignore SIGINTs to let the restarted process handle them

    Ran into this while trying to implement https://github.com/composer/composer/commit/a07f9ffba98fe5472a3004b56299ca99e22dc6c9 and going insane wondering why the signal handler was never called :) This commit fixes it.

    Reviewed by Seldaek at 2020-05-22 11:26
php-xdebug

PHP-Xdebug Useage 修改/ConfigurationFile/php/php.ini

Jan 30, 2022
Test your routes without hassle
Test your routes without hassle

Laravel Api Tester Live demo Try it out: laravel-api-tester.asva.by Docs Those are short and easy to read. Take a look. Interface FAQ Installation Req

Jun 22, 2022
Ray server is a beautiful, lightweight php app build on Laravel that helps you debug your app. It runs without installation on multiple platforms.
Ray server is a beautiful, lightweight php app build on Laravel that helps you debug your app. It runs without installation on multiple platforms.

RayServer is a beautiful, lightweight web server built on Laravel and VueJs that helps debugging your app. It runs without installation on multiple platforms.

Jun 28, 2022
😎 Tracy: the addictive tool to ease debugging PHP code for cool developers. Friendly design, logging, profiler, advanced features like debugging AJAX calls or CLI support. You will love it.
😎 Tracy: the addictive tool to ease debugging PHP code for cool developers. Friendly design, logging, profiler, advanced features like debugging AJAX calls or CLI support. You will love it.

Tracy - PHP debugger Introduction Tracy library is a useful helper for everyday PHP programmers. It helps you to: quickly detect and correct errors lo

Jun 26, 2022
QPM, the process management framework in PHP, the efficient toolkit for CLI development. QPM provides basic daemon functions and supervision mechanisms to simplify multi-process app dev.

QPM QPM全名是 Quick(or Q's) Process Management Framework for PHP. PHP 是强大的web开发语言,以至于大家常常忘记PHP 可以用来开发健壮的命令行(CLI)程序以至于daemon程序。 而编写daemon程序免不了与各种进程管理打交道。Q

Dec 21, 2021
Monitor for any changes in your php application and automatically restart it (suitable for async apps).
Monitor for any changes in your php application and automatically restart it (suitable for async apps).

PHP-watcher PHP-watcher helps develop long-running PHP applications by automatically restarting them when file changes in the directory are detected.

Jun 22, 2022
Quick start -d/reload/restart/stop hyperf(~2.1.0 & ~2.2.0) server

hyperf-helper 1. quick start -d/reload/restart/stop server 2. support hyperf ~2.1.0 & ~2.2.0 3. support CentOS7+, Ubuntu 18.0.4+, macOS 4. support swo

Jun 3, 2022
Process - The Process component executes commands in sub-processes.

Process Component The Process component executes commands in sub-processes. Sponsor The Process component for Symfony 5.4/6.0 is backed by SensioLabs.

Jun 28, 2022
Xdebug — Step Debugger and Debugging Aid for PHP

Xdebug Xdebug is a debugging tool for PHP. It provides step-debugging and a whole range of development aids, such as stack traces, a code profiler, fe

Jun 26, 2022
php-xdebug

PHP-Xdebug Useage 修改/ConfigurationFile/php/php.ini

Jan 30, 2022
Magento 2 Debug Helper Module for easy debugging with Xdebug and PHPStorm or any other IDE

Magento 2 Debug Helper Information and Usage Magento 2 Debug Helper Module usage with PHPStorm and Xdebug Installation To install the Magento 2 Debug

May 24, 2022
Dockerise Symfony Application (Symfony 6 + Clean Architecture+ DDD+ CQRS + Docker + Xdebug + PHPUnit + Doctrine ORM + JWT Auth + Static analysis)

Symfony Dockerise Symfony Application Install Docker Install Docker Compose Docker PHP & Nginx Create Symfony Application Debugging Install Xdebug Con

Jun 28, 2022
This extension facilitates the cms editing process in your store.
This extension facilitates the cms editing process in your store.

Magenerds_PageDesigner This extension facilitates the cms editing process in your store. Instead of just a wysiwyg editor you now have a drag and drop

Apr 24, 2022
Optimizes class loading performance by generating a single PHP file containing all of the autoloaded files.
Optimizes class loading performance by generating a single PHP file containing all of the autoloaded files.

Class Preloader for PHP This tool is used to generate a single PHP script containing all of the classes required for a specific use case. Using a sing

May 15, 2022
A package for adding loading spinners to your Laravel Artisan commands
A package for adding loading spinners to your Laravel Artisan commands

Table of Contents Overview Installation Requirements Install the Package Usage Adding Loading Spinners to Commands Adding Text to the Spinner Customis

Mar 24, 2022
This small PHP package assists in the loading and parsing of VTT files.

VTT Transcriptions This small PHP package assists in the loading and parsing of VTT files. Usage use Laracasts\Transcriptions\Transcription; $transcr

Jun 15, 2022
Customized loading ⌛ spinner for Laravel Artisan Console.

Laravel Console Spinner Laravel Console Spinner was created by Rahul Dey. It is just a custom Progress Bar inspired by icanhazstring/symfony-console-s

Jun 15, 2022
LOAD is a PHP library for configuration loading to APCu

LOAD LOAD is a PHP library for configuration loading to APCu Sources Available sources for configuration loading are: PHP file Consul Environment vari

Jan 18, 2022
A base API controller for Laravel that gives sorting, filtering, eager loading and pagination for your resources

Bruno Introduction A Laravel base controller class and a trait that will enable to add filtering, sorting, eager loading and pagination to your resour

Apr 25, 2022