├── ChangeLog.md ├── LICENSE ├── README.md ├── SECURITY.md ├── composer.json └── src ├── Console.php └── Runtime.php /ChangeLog.md: -------------------------------------------------------------------------------- 1 | # Changes in sebastianbergmann/environment 2 | 3 | All notable changes in `sebastianbergmann/environment` are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles. 4 | 5 | ## [8.0.2] - 2025-05-21 6 | 7 | ### Fixed 8 | 9 | * [#74](https://github.com/sebastianbergmann/environment/pull/74): Regression introduced in version 8.0.0 10 | 11 | ## [8.0.1] - 2025-05-21 12 | 13 | ### Fixed 14 | 15 | * Take Xdebug mode into account for `Runtime::canCollectCodeCoverage()` 16 | 17 | ## [8.0.0] - 2025-02-07 18 | 19 | ### Removed 20 | 21 | * This component is no longer supported on PHP 8.2 22 | 23 | ## [7.2.1] - 2025-05-21 24 | 25 | ### Fixed 26 | 27 | * Take Xdebug mode into account for `Runtime::canCollectCodeCoverage()` 28 | 29 | ## [7.2.0] - 2024-07-03 30 | 31 | ### Changed 32 | 33 | * Synced `Console::hasColorSupport()` with Symfony's `StreamOutput::hasColorSupport()` implementation 34 | * Removed code left over from a time before PHP 5.4 and when HHVM was still supported 35 | * This project now uses PHPStan instead of Psalm for static analysis 36 | 37 | ### Deprecated 38 | 39 | * The `Runtime::getBinary()` method is now deprecated, use `escapeshellarg(PHP_BINARY)` instead 40 | * The `Runtime::getRawBinary()` method is now deprecated, use the `PHP_BINARY` constant instead 41 | 42 | ## [7.1.0] - 2024-03-23 43 | 44 | ### Added 45 | 46 | * [#72](https://github.com/sebastianbergmann/environment/pull/72): `Runtime::getRawBinary()` 47 | 48 | ## [7.0.0] - 2024-02-02 49 | 50 | ### Removed 51 | 52 | * This component is no longer supported on PHP 8.1 53 | 54 | ## [6.1.1] - 2024-MM-DD 55 | 56 | ### Changed 57 | 58 | * Synced `Console::hasColorSupport()` with Symfony's `StreamOutput::hasColorSupport()` implementation 59 | 60 | ## [6.1.0] - 2024-03-23 61 | 62 | ### Added 63 | 64 | * [#72](https://github.com/sebastianbergmann/environment/pull/72): `Runtime::getRawBinary()` 65 | 66 | ## [6.0.1] - 2023-04-11 67 | 68 | ### Fixed 69 | 70 | * [#68](https://github.com/sebastianbergmann/environment/pull/68): The Just-in-Time compiler is disabled when `opcache.jit_buffer_size` is set to `0` 71 | * [#70](https://github.com/sebastianbergmann/environment/pull/70): The first `0` of `opcache.jit` only disables CPU-specific optimizations, not the Just-in-Time compiler itself 72 | 73 | ## [6.0.0] - 2023-02-03 74 | 75 | ### Removed 76 | 77 | * Removed `SebastianBergmann\Environment\OperatingSystem::getFamily()` because this component is no longer supported on PHP versions that do not have `PHP_OS_FAMILY` 78 | * Removed `SebastianBergmann\Environment\Runtime::isHHVM()` 79 | * This component is no longer supported on PHP 7.3, PHP 7.4, and PHP 8.0 80 | 81 | ## [5.1.5] - 2022-MM-DD 82 | 83 | ### Fixed 84 | 85 | * [#59](https://github.com/sebastianbergmann/environment/issues/59): Wrong usage of `stream_isatty()`, `fstat()` used without checking whether the function is available 86 | 87 | ## [5.1.4] - 2022-04-03 88 | 89 | ### Fixed 90 | 91 | * [#63](https://github.com/sebastianbergmann/environment/pull/63): `Runtime::getCurrentSettings()` does not correctly process INI settings 92 | 93 | ## [5.1.3] - 2020-09-28 94 | 95 | ### Changed 96 | 97 | * Changed PHP version constraint in `composer.json` from `^7.3 || ^8.0` to `>=7.3` 98 | 99 | ## [5.1.2] - 2020-06-26 100 | 101 | ### Added 102 | 103 | * This component is now supported on PHP 8 104 | 105 | ## [5.1.1] - 2020-06-15 106 | 107 | ### Changed 108 | 109 | * Tests etc. are now ignored for archive exports 110 | 111 | ## [5.1.0] - 2020-04-14 112 | 113 | ### Added 114 | 115 | * `Runtime::performsJustInTimeCompilation()` returns `true` if PHP 8's JIT is active, `false` otherwise 116 | 117 | ## [5.0.2] - 2020-03-31 118 | 119 | ### Fixed 120 | 121 | * [#55](https://github.com/sebastianbergmann/environment/issues/55): `stty` command is executed even if no tty is available 122 | 123 | ## [5.0.1] - 2020-02-19 124 | 125 | ### Changed 126 | 127 | * `Runtime::getNameWithVersionAndCodeCoverageDriver()` now prioritizes PCOV over Xdebug when both extensions are loaded (just like php-code-coverage does) 128 | 129 | ## [5.0.0] - 2020-02-07 130 | 131 | ### Removed 132 | 133 | * This component is no longer supported on PHP 7.1 and PHP 7.2 134 | 135 | ## [4.2.3] - 2019-11-20 136 | 137 | ### Changed 138 | 139 | * [#50](https://github.com/sebastianbergmann/environment/pull/50): Windows improvements to console capabilities 140 | 141 | ### Fixed 142 | 143 | * [#49](https://github.com/sebastianbergmann/environment/issues/49): Detection how OpCache handles docblocks does not work correctly when PHPDBG is used 144 | 145 | ## [4.2.2] - 2019-05-05 146 | 147 | ### Fixed 148 | 149 | * [#44](https://github.com/sebastianbergmann/environment/pull/44): `TypeError` in `Console::getNumberOfColumnsInteractive()` 150 | 151 | ## [4.2.1] - 2019-04-25 152 | 153 | ### Fixed 154 | 155 | * Fixed an issue in `Runtime::getCurrentSettings()` 156 | 157 | ## [4.2.0] - 2019-04-25 158 | 159 | ### Added 160 | 161 | * [#36](https://github.com/sebastianbergmann/environment/pull/36): `Runtime::getCurrentSettings()` 162 | 163 | ## [4.1.0] - 2019-02-01 164 | 165 | ### Added 166 | 167 | * Implemented `Runtime::getNameWithVersionAndCodeCoverageDriver()` method 168 | * [#34](https://github.com/sebastianbergmann/environment/pull/34): Support for PCOV extension 169 | 170 | ## [4.0.2] - 2019-01-28 171 | 172 | ### Fixed 173 | 174 | * [#33](https://github.com/sebastianbergmann/environment/issues/33): `Runtime::discardsComments()` returns true too eagerly 175 | 176 | ### Removed 177 | 178 | * Removed support for Zend Optimizer+ in `Runtime::discardsComments()` 179 | 180 | ## [4.0.1] - 2018-11-25 181 | 182 | ### Fixed 183 | 184 | * [#31](https://github.com/sebastianbergmann/environment/issues/31): Regressions in `Console` class 185 | 186 | ## [4.0.0] - 2018-10-23 [YANKED] 187 | 188 | ### Fixed 189 | 190 | * [#25](https://github.com/sebastianbergmann/environment/pull/25): `Console::hasColorSupport()` does not work on Windows 191 | 192 | ### Removed 193 | 194 | * This component is no longer supported on PHP 7.0 195 | 196 | ## [3.1.0] - 2017-07-01 197 | 198 | ### Added 199 | 200 | * [#21](https://github.com/sebastianbergmann/environment/issues/21): Equivalent of `PHP_OS_FAMILY` (for PHP < 7.2) 201 | 202 | ## [3.0.4] - 2017-06-20 203 | 204 | ### Fixed 205 | 206 | * [#20](https://github.com/sebastianbergmann/environment/pull/20): PHP 7 mode of HHVM not forced 207 | 208 | ## [3.0.3] - 2017-05-18 209 | 210 | ### Fixed 211 | 212 | * [#18](https://github.com/sebastianbergmann/environment/issues/18): `Uncaught TypeError: preg_match() expects parameter 2 to be string, null given` 213 | 214 | ## [3.0.2] - 2017-04-21 215 | 216 | ### Fixed 217 | 218 | * [#17](https://github.com/sebastianbergmann/environment/issues/17): `Uncaught TypeError: trim() expects parameter 1 to be string, boolean given` 219 | 220 | ## [3.0.1] - 2017-04-21 221 | 222 | ### Fixed 223 | 224 | * Fixed inverted logic in `Runtime::discardsComments()` 225 | 226 | ## [3.0.0] - 2017-04-21 227 | 228 | ### Added 229 | 230 | * Implemented `Runtime::discardsComments()` for querying whether the PHP runtime discards annotations 231 | 232 | ### Removed 233 | 234 | * This component is no longer supported on PHP 5.6 235 | 236 | [8.0.2]: https://github.com/sebastianbergmann/environment/compare/8.0.1...8.0.2 237 | [8.0.1]: https://github.com/sebastianbergmann/environment/compare/8.0.0...8.0.1 238 | [8.0.0]: https://github.com/sebastianbergmann/environment/compare/7.2...8.0.0 239 | [7.2.1]: https://github.com/sebastianbergmann/environment/compare/7.2.0...7.2.1 240 | [7.2.0]: https://github.com/sebastianbergmann/environment/compare/7.1.0...7.2.0 241 | [7.1.0]: https://github.com/sebastianbergmann/environment/compare/7.0.0...7.1.0 242 | [7.0.0]: https://github.com/sebastianbergmann/environment/compare/6.1...7.0.0 243 | [6.1.1]: https://github.com/sebastianbergmann/environment/compare/6.1.0...6.1 244 | [6.1.0]: https://github.com/sebastianbergmann/environment/compare/6.0.1...6.1.0 245 | [6.0.1]: https://github.com/sebastianbergmann/environment/compare/6.0.0...6.0.1 246 | [6.0.0]: https://github.com/sebastianbergmann/environment/compare/5.1.5...6.0.0 247 | [5.1.5]: https://github.com/sebastianbergmann/environment/compare/5.1.4...5.1.5 248 | [5.1.4]: https://github.com/sebastianbergmann/environment/compare/5.1.3...5.1.4 249 | [5.1.3]: https://github.com/sebastianbergmann/environment/compare/5.1.2...5.1.3 250 | [5.1.2]: https://github.com/sebastianbergmann/environment/compare/5.1.1...5.1.2 251 | [5.1.1]: https://github.com/sebastianbergmann/environment/compare/5.1.0...5.1.1 252 | [5.1.0]: https://github.com/sebastianbergmann/environment/compare/5.0.2...5.1.0 253 | [5.0.2]: https://github.com/sebastianbergmann/environment/compare/5.0.1...5.0.2 254 | [5.0.1]: https://github.com/sebastianbergmann/environment/compare/5.0.0...5.0.1 255 | [5.0.0]: https://github.com/sebastianbergmann/environment/compare/4.2.3...5.0.0 256 | [4.2.3]: https://github.com/sebastianbergmann/environment/compare/4.2.2...4.2.3 257 | [4.2.2]: https://github.com/sebastianbergmann/environment/compare/4.2.1...4.2.2 258 | [4.2.1]: https://github.com/sebastianbergmann/environment/compare/4.2.0...4.2.1 259 | [4.2.0]: https://github.com/sebastianbergmann/environment/compare/4.1.0...4.2.0 260 | [4.1.0]: https://github.com/sebastianbergmann/environment/compare/4.0.2...4.1.0 261 | [4.0.2]: https://github.com/sebastianbergmann/environment/compare/4.0.1...4.0.2 262 | [4.0.1]: https://github.com/sebastianbergmann/environment/compare/66691f8e2dc4641909166b275a9a4f45c0e89092...4.0.1 263 | [4.0.0]: https://github.com/sebastianbergmann/environment/compare/3.1.0...66691f8e2dc4641909166b275a9a4f45c0e89092 264 | [3.1.0]: https://github.com/sebastianbergmann/environment/compare/3.0...3.1.0 265 | [3.0.4]: https://github.com/sebastianbergmann/environment/compare/3.0.3...3.0.4 266 | [3.0.3]: https://github.com/sebastianbergmann/environment/compare/3.0.2...3.0.3 267 | [3.0.2]: https://github.com/sebastianbergmann/environment/compare/3.0.1...3.0.2 268 | [3.0.1]: https://github.com/sebastianbergmann/environment/compare/3.0.0...3.0.1 269 | [3.0.0]: https://github.com/sebastianbergmann/environment/compare/2.0...3.0.0 270 | 271 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2014-2025, Sebastian Bergmann 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Latest Stable Version](https://poser.pugx.org/sebastian/environment/v)](https://packagist.org/packages/sebastian/environment) 2 | [![CI Status](https://github.com/sebastianbergmann/environment/workflows/CI/badge.svg)](https://github.com/sebastianbergmann/environment/actions) 3 | [![codecov](https://codecov.io/gh/sebastianbergmann/environment/branch/main/graph/badge.svg)](https://codecov.io/gh/sebastianbergmann/environment) 4 | 5 | # sebastian/environment 6 | 7 | This component provides functionality that helps writing PHP code that has runtime-specific (PHP / HHVM) execution paths. 8 | 9 | ## Installation 10 | 11 | You can add this library as a local, per-project dependency to your project using [Composer](https://getcomposer.org/): 12 | 13 | ``` 14 | composer require sebastian/environment 15 | ``` 16 | 17 | If you only need this library during development, for instance to run your project's test suite, then you should add it as a development-time dependency: 18 | 19 | ``` 20 | composer require --dev sebastian/environment 21 | ``` 22 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | If you believe you have found a security vulnerability in the library that is developed in this repository, please report it to us through coordinated disclosure. 4 | 5 | **Please do not report security vulnerabilities through public GitHub issues, discussions, or pull requests.** 6 | 7 | Instead, please email `sebastian@phpunit.de`. 8 | 9 | Please include as much of the information listed below as you can to help us better understand and resolve the issue: 10 | 11 | * The type of issue 12 | * Full paths of source file(s) related to the manifestation of the issue 13 | * The location of the affected source code (tag/branch/commit or direct URL) 14 | * Any special configuration required to reproduce the issue 15 | * Step-by-step instructions to reproduce the issue 16 | * Proof-of-concept or exploit code (if possible) 17 | * Impact of the issue, including how an attacker might exploit the issue 18 | 19 | This information will help us triage your report more quickly. 20 | 21 | ## Web Context 22 | 23 | The library that is developed in this repository was either extracted from [PHPUnit](https://github.com/sebastianbergmann/phpunit) or developed specifically as a dependency for PHPUnit. 24 | 25 | The library is developed with a focus on development environments and the command-line. No specific testing or hardening with regard to using the library in an HTTP or web context or with untrusted input data is performed. The library might also contain functionality that intentionally exposes internal application data for debugging purposes. 26 | 27 | If the library is used in a web application, the application developer is responsible for filtering inputs or escaping outputs as necessary and for verifying that the used functionality is safe for use within the intended context. 28 | 29 | Vulnerabilities specific to the use outside a development context will be fixed as applicable, provided that the fix does not have an averse effect on the primary use case for development purposes. 30 | 31 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sebastian/environment", 3 | "description": "Provides functionality to handle HHVM/PHP environments", 4 | "keywords": ["environment","hhvm","xdebug"], 5 | "homepage": "https://github.com/sebastianbergmann/environment", 6 | "license": "BSD-3-Clause", 7 | "authors": [ 8 | { 9 | "name": "Sebastian Bergmann", 10 | "email": "sebastian@phpunit.de" 11 | } 12 | ], 13 | "support": { 14 | "issues": "https://github.com/sebastianbergmann/environment/issues", 15 | "security": "https://github.com/sebastianbergmann/environment/security/policy" 16 | }, 17 | "config": { 18 | "platform": { 19 | "php": "8.3.0" 20 | }, 21 | "optimize-autoloader": true, 22 | "sort-packages": true 23 | }, 24 | "prefer-stable": true, 25 | "require": { 26 | "php": ">=8.3" 27 | }, 28 | "require-dev": { 29 | "phpunit/phpunit": "^12.0" 30 | }, 31 | "suggest": { 32 | "ext-posix": "*" 33 | }, 34 | "autoload": { 35 | "classmap": [ 36 | "src/" 37 | ] 38 | }, 39 | "extra": { 40 | "branch-alias": { 41 | "dev-main": "8.0-dev" 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Console.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace SebastianBergmann\Environment; 11 | 12 | use const DIRECTORY_SEPARATOR; 13 | use const STDIN; 14 | use const STDOUT; 15 | use function assert; 16 | use function defined; 17 | use function fclose; 18 | use function fstat; 19 | use function function_exists; 20 | use function getenv; 21 | use function in_array; 22 | use function is_array; 23 | use function is_int; 24 | use function is_resource; 25 | use function is_string; 26 | use function posix_isatty; 27 | use function preg_match; 28 | use function proc_close; 29 | use function proc_open; 30 | use function sapi_windows_vt100_support; 31 | use function shell_exec; 32 | use function stream_get_contents; 33 | use function stream_isatty; 34 | use function strtoupper; 35 | use function trim; 36 | 37 | final class Console 38 | { 39 | /** 40 | * @var int 41 | */ 42 | public const int STDIN = 0; 43 | 44 | /** 45 | * @var int 46 | */ 47 | public const int STDOUT = 1; 48 | 49 | /** 50 | * @var int 51 | */ 52 | public const int STDERR = 2; 53 | 54 | /** 55 | * Returns true if STDOUT supports colorization. 56 | * 57 | * This code has been copied and adapted from 58 | * Symfony\Component\Console\Output\StreamOutput. 59 | */ 60 | public function hasColorSupport(): bool 61 | { 62 | if (!defined('STDOUT')) { 63 | return false; 64 | } 65 | 66 | if (isset($_SERVER['NO_COLOR']) || false !== getenv('NO_COLOR')) { 67 | return false; 68 | } 69 | 70 | if (!@stream_isatty(STDOUT) && 71 | !in_array(strtoupper((string) getenv('MSYSTEM')), ['MINGW32', 'MINGW64'], true)) { 72 | return false; 73 | } 74 | 75 | if ($this->isWindows() && 76 | function_exists('sapi_windows_vt100_support') && 77 | @sapi_windows_vt100_support(STDOUT)) { 78 | return true; 79 | } 80 | 81 | if ('Hyper' === getenv('TERM_PROGRAM') || 82 | false !== getenv('COLORTERM') || 83 | false !== getenv('ANSICON') || 84 | 'ON' === getenv('ConEmuANSI')) { 85 | return true; 86 | } 87 | 88 | if ('dumb' === $term = (string) getenv('TERM')) { 89 | return false; 90 | } 91 | 92 | return (bool) preg_match('/^((screen|xterm|vt100|vt220|putty|rxvt|ansi|cygwin|linux).*)|(.*-256(color)?(-bce)?)$/', $term); 93 | } 94 | 95 | /** 96 | * Returns the number of columns of the terminal. 97 | * 98 | * @codeCoverageIgnore 99 | */ 100 | public function getNumberOfColumns(): int 101 | { 102 | if (!$this->isInteractive(defined('STDIN') ? STDIN : self::STDIN)) { 103 | return 80; 104 | } 105 | 106 | if ($this->isWindows()) { 107 | return $this->getNumberOfColumnsWindows(); 108 | } 109 | 110 | return $this->getNumberOfColumnsInteractive(); 111 | } 112 | 113 | /** 114 | * Returns if the file descriptor is an interactive terminal or not. 115 | * 116 | * Normally, we want to use a resource as a parameter, yet sadly it's not always available, 117 | * eg when running code in interactive console (`php -a`), STDIN/STDOUT/STDERR constants are not defined. 118 | * 119 | * @param int|resource $fileDescriptor 120 | */ 121 | public function isInteractive(mixed $fileDescriptor = self::STDOUT): bool 122 | { 123 | assert(is_int($fileDescriptor) || is_resource($fileDescriptor)); 124 | 125 | if (is_resource($fileDescriptor)) { 126 | if (function_exists('stream_isatty') && @stream_isatty($fileDescriptor)) { 127 | return true; 128 | } 129 | 130 | if (function_exists('fstat')) { 131 | $stat = @fstat(STDOUT); 132 | 133 | return $stat !== false && 0o020000 === ($stat['mode'] & 0o170000); 134 | } 135 | 136 | return false; 137 | } 138 | 139 | return function_exists('posix_isatty') && @posix_isatty($fileDescriptor); 140 | } 141 | 142 | private function isWindows(): bool 143 | { 144 | return DIRECTORY_SEPARATOR === '\\'; 145 | } 146 | 147 | /** 148 | * @codeCoverageIgnore 149 | */ 150 | private function getNumberOfColumnsInteractive(): int 151 | { 152 | if (function_exists('shell_exec')) { 153 | $stty = shell_exec('stty size'); 154 | 155 | if ($stty === false || $stty === null) { 156 | $stty = ''; 157 | } 158 | 159 | if (preg_match('#\d+ (\d+)#', $stty, $match) === 1) { 160 | if ((int) $match[1] > 0) { 161 | return (int) $match[1]; 162 | } 163 | } 164 | 165 | $stty = shell_exec('stty'); 166 | 167 | if ($stty === false || $stty === null) { 168 | $stty = ''; 169 | } 170 | 171 | if (preg_match('#columns = (\d+);#', $stty, $match) === 1) { 172 | if ((int) $match[1] > 0) { 173 | return (int) $match[1]; 174 | } 175 | } 176 | } 177 | 178 | return 80; 179 | } 180 | 181 | /** 182 | * @codeCoverageIgnore 183 | */ 184 | private function getNumberOfColumnsWindows(): int 185 | { 186 | $ansicon = getenv('ANSICON'); 187 | $columns = 80; 188 | 189 | /** @phpstan-ignore booleanAnd.rightNotBoolean */ 190 | if (is_string($ansicon) && preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim($ansicon), $matches)) { 191 | $columns = (int) $matches[1]; 192 | } elseif (function_exists('proc_open')) { 193 | $process = proc_open( 194 | 'mode CON', 195 | [ 196 | 1 => ['pipe', 'w'], 197 | 2 => ['pipe', 'w'], 198 | ], 199 | $pipes, 200 | null, 201 | null, 202 | ['suppress_errors' => true], 203 | ); 204 | 205 | assert(is_array($pipes)); 206 | assert(isset($pipes[1]) && is_resource($pipes[1])); 207 | assert(isset($pipes[2]) && is_resource($pipes[2])); 208 | 209 | if (is_resource($process)) { 210 | $info = stream_get_contents($pipes[1]); 211 | 212 | fclose($pipes[1]); 213 | fclose($pipes[2]); 214 | proc_close($process); 215 | 216 | /** @phpstan-ignore if.condNotBoolean */ 217 | if (preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', (string) $info, $matches)) { 218 | $columns = (int) $matches[2]; 219 | } 220 | } 221 | } 222 | 223 | return $columns - 1; 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /src/Runtime.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace SebastianBergmann\Environment; 11 | 12 | use const PHP_BINARY; 13 | use const PHP_SAPI; 14 | use const PHP_VERSION; 15 | use function array_map; 16 | use function array_merge; 17 | use function assert; 18 | use function escapeshellarg; 19 | use function explode; 20 | use function extension_loaded; 21 | use function in_array; 22 | use function ini_get; 23 | use function is_array; 24 | use function parse_ini_file; 25 | use function php_ini_loaded_file; 26 | use function php_ini_scanned_files; 27 | use function phpversion; 28 | use function sprintf; 29 | use function strrpos; 30 | use function version_compare; 31 | use function xdebug_info; 32 | 33 | final class Runtime 34 | { 35 | /** 36 | * Returns true when Xdebug or PCOV is available or 37 | * the runtime used is PHPDBG. 38 | */ 39 | public function canCollectCodeCoverage(): bool 40 | { 41 | if ($this->hasPHPDBGCodeCoverage()) { 42 | return true; 43 | } 44 | 45 | if ($this->hasPCOV()) { 46 | return true; 47 | } 48 | 49 | if (!$this->hasXdebug()) { 50 | return false; 51 | } 52 | 53 | $xdebugVersion = phpversion('xdebug'); 54 | 55 | assert($xdebugVersion !== false); 56 | 57 | if (version_compare($xdebugVersion, '3', '<')) { 58 | return true; 59 | } 60 | 61 | $xdebugMode = xdebug_info('mode'); 62 | 63 | assert(is_array($xdebugMode)); 64 | 65 | if (in_array('coverage', $xdebugMode, true)) { 66 | return true; 67 | } 68 | 69 | return false; 70 | } 71 | 72 | /** 73 | * Returns true when Zend OPcache is loaded, enabled, 74 | * and is configured to discard comments. 75 | */ 76 | public function discardsComments(): bool 77 | { 78 | if (!$this->isOpcacheActive()) { 79 | return false; 80 | } 81 | 82 | if (ini_get('opcache.save_comments') !== '0') { 83 | return false; 84 | } 85 | 86 | return true; 87 | } 88 | 89 | /** 90 | * Returns true when Zend OPcache is loaded, enabled, 91 | * and is configured to perform just-in-time compilation. 92 | */ 93 | public function performsJustInTimeCompilation(): bool 94 | { 95 | if (!$this->isOpcacheActive()) { 96 | return false; 97 | } 98 | 99 | if (ini_get('opcache.jit_buffer_size') === '0') { 100 | return false; 101 | } 102 | 103 | $jit = (string) ini_get('opcache.jit'); 104 | 105 | if (($jit === 'disable') || ($jit === 'off')) { 106 | return false; 107 | } 108 | 109 | if (strrpos($jit, '0') === 3) { 110 | return false; 111 | } 112 | 113 | return true; 114 | } 115 | 116 | /** 117 | * Returns the raw path to the binary of the current runtime. 118 | * 119 | * @deprecated 120 | */ 121 | public function getRawBinary(): string 122 | { 123 | return PHP_BINARY; 124 | } 125 | 126 | /** 127 | * Returns the escaped path to the binary of the current runtime. 128 | * 129 | * @deprecated 130 | */ 131 | public function getBinary(): string 132 | { 133 | return escapeshellarg(PHP_BINARY); 134 | } 135 | 136 | public function getNameWithVersion(): string 137 | { 138 | return $this->getName() . ' ' . $this->getVersion(); 139 | } 140 | 141 | public function getNameWithVersionAndCodeCoverageDriver(): string 142 | { 143 | if ($this->hasPCOV()) { 144 | return sprintf( 145 | '%s with PCOV %s', 146 | $this->getNameWithVersion(), 147 | phpversion('pcov'), 148 | ); 149 | } 150 | 151 | if ($this->hasXdebug()) { 152 | return sprintf( 153 | '%s with Xdebug %s', 154 | $this->getNameWithVersion(), 155 | phpversion('xdebug'), 156 | ); 157 | } 158 | 159 | return $this->getNameWithVersion(); 160 | } 161 | 162 | public function getName(): string 163 | { 164 | if ($this->isPHPDBG()) { 165 | // @codeCoverageIgnoreStart 166 | return 'PHPDBG'; 167 | // @codeCoverageIgnoreEnd 168 | } 169 | 170 | return 'PHP'; 171 | } 172 | 173 | public function getVendorUrl(): string 174 | { 175 | return 'https://www.php.net/'; 176 | } 177 | 178 | public function getVersion(): string 179 | { 180 | return PHP_VERSION; 181 | } 182 | 183 | /** 184 | * Returns true when the runtime used is PHP and Xdebug is loaded. 185 | */ 186 | public function hasXdebug(): bool 187 | { 188 | return $this->isPHP() && extension_loaded('xdebug'); 189 | } 190 | 191 | /** 192 | * Returns true when the runtime used is PHP without the PHPDBG SAPI. 193 | */ 194 | public function isPHP(): bool 195 | { 196 | return !$this->isPHPDBG(); 197 | } 198 | 199 | /** 200 | * Returns true when the runtime used is PHP with the PHPDBG SAPI. 201 | */ 202 | public function isPHPDBG(): bool 203 | { 204 | return PHP_SAPI === 'phpdbg'; 205 | } 206 | 207 | /** 208 | * Returns true when the runtime used is PHP with the PHPDBG SAPI 209 | * and the phpdbg_*_oplog() functions are available (PHP >= 7.0). 210 | */ 211 | public function hasPHPDBGCodeCoverage(): bool 212 | { 213 | return $this->isPHPDBG(); 214 | } 215 | 216 | /** 217 | * Returns true when the runtime used is PHP with PCOV loaded and enabled. 218 | */ 219 | public function hasPCOV(): bool 220 | { 221 | return $this->isPHP() && extension_loaded('pcov') && ini_get('pcov.enabled') === '1'; 222 | } 223 | 224 | /** 225 | * Parses the loaded php.ini file (if any) as well as all 226 | * additional php.ini files from the additional ini dir for 227 | * a list of all configuration settings loaded from files 228 | * at startup. Then checks for each php.ini setting passed 229 | * via the `$values` parameter whether this setting has 230 | * been changed at runtime. Returns an array of strings 231 | * where each string has the format `key=value` denoting 232 | * the name of a changed php.ini setting with its new value. 233 | * 234 | * @param list $values 235 | * 236 | * @return array 237 | */ 238 | public function getCurrentSettings(array $values): array 239 | { 240 | $diff = []; 241 | $files = []; 242 | 243 | $file = php_ini_loaded_file(); 244 | 245 | if ($file !== false) { 246 | $files[] = $file; 247 | } 248 | 249 | $scanned = php_ini_scanned_files(); 250 | 251 | if ($scanned !== false) { 252 | $files = array_merge( 253 | $files, 254 | array_map( 255 | 'trim', 256 | explode(",\n", $scanned), 257 | ), 258 | ); 259 | } 260 | 261 | foreach ($files as $ini) { 262 | $config = parse_ini_file($ini, true); 263 | 264 | foreach ($values as $value) { 265 | $set = ini_get($value); 266 | 267 | if ($set === false || $set === '') { 268 | continue; 269 | } 270 | 271 | if ((!isset($config[$value]) || ($set !== $config[$value]))) { 272 | $diff[$value] = sprintf('%s=%s', $value, $set); 273 | } 274 | } 275 | } 276 | 277 | return $diff; 278 | } 279 | 280 | private function isOpcacheActive(): bool 281 | { 282 | if (!extension_loaded('Zend OPcache')) { 283 | return false; 284 | } 285 | 286 | if ((PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') && ini_get('opcache.enable_cli') === '1') { 287 | return true; 288 | } 289 | 290 | if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg' && ini_get('opcache.enable') === '1') { 291 | return true; 292 | } 293 | 294 | return false; 295 | } 296 | } 297 | --------------------------------------------------------------------------------