Runtime.php 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. <?php declare(strict_types=1);
  2. /*
  3. * This file is part of sebastian/environment.
  4. *
  5. * (c) Sebastian Bergmann <sebastian@phpunit.de>
  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. * Utility class for HHVM/PHP environment handling.
  13. */
  14. final class Runtime
  15. {
  16. /**
  17. * @var string
  18. */
  19. private static $binary;
  20. /**
  21. * Returns true when Xdebug or PCOV is available or
  22. * the runtime used is PHPDBG.
  23. */
  24. public function canCollectCodeCoverage(): bool
  25. {
  26. return $this->hasXdebug() || $this->hasPCOV() || $this->hasPHPDBGCodeCoverage();
  27. }
  28. /**
  29. * Returns true when Zend OPcache is loaded, enabled, and is configured to discard comments.
  30. */
  31. public function discardsComments(): bool
  32. {
  33. if (!\extension_loaded('Zend OPcache')) {
  34. return false;
  35. }
  36. if (\ini_get('opcache.save_comments') !== '0') {
  37. return false;
  38. }
  39. if (\PHP_SAPI === 'cli' && \ini_get('opcache.enable_cli') === '1') {
  40. return true;
  41. }
  42. if (\PHP_SAPI !== 'cli' && \ini_get('opcache.enable') === '1') {
  43. return true;
  44. }
  45. return false;
  46. }
  47. /**
  48. * Returns the path to the binary of the current runtime.
  49. * Appends ' --php' to the path when the runtime is HHVM.
  50. */
  51. public function getBinary(): string
  52. {
  53. // HHVM
  54. if (self::$binary === null && $this->isHHVM()) {
  55. // @codeCoverageIgnoreStart
  56. if ((self::$binary = \getenv('PHP_BINARY')) === false) {
  57. self::$binary = \PHP_BINARY;
  58. }
  59. self::$binary = \escapeshellarg(self::$binary) . ' --php' .
  60. ' -d hhvm.php7.all=1';
  61. // @codeCoverageIgnoreEnd
  62. }
  63. if (self::$binary === null && \PHP_BINARY !== '') {
  64. self::$binary = \escapeshellarg(\PHP_BINARY);
  65. }
  66. if (self::$binary === null) {
  67. // @codeCoverageIgnoreStart
  68. $possibleBinaryLocations = [
  69. \PHP_BINDIR . '/php',
  70. \PHP_BINDIR . '/php-cli.exe',
  71. \PHP_BINDIR . '/php.exe',
  72. ];
  73. foreach ($possibleBinaryLocations as $binary) {
  74. if (\is_readable($binary)) {
  75. self::$binary = \escapeshellarg($binary);
  76. break;
  77. }
  78. }
  79. // @codeCoverageIgnoreEnd
  80. }
  81. if (self::$binary === null) {
  82. // @codeCoverageIgnoreStart
  83. self::$binary = 'php';
  84. // @codeCoverageIgnoreEnd
  85. }
  86. return self::$binary;
  87. }
  88. public function getNameWithVersion(): string
  89. {
  90. return $this->getName() . ' ' . $this->getVersion();
  91. }
  92. public function getNameWithVersionAndCodeCoverageDriver(): string
  93. {
  94. if (!$this->canCollectCodeCoverage() || $this->hasPHPDBGCodeCoverage()) {
  95. return $this->getNameWithVersion();
  96. }
  97. if ($this->hasXdebug()) {
  98. return \sprintf(
  99. '%s with Xdebug %s',
  100. $this->getNameWithVersion(),
  101. \phpversion('xdebug')
  102. );
  103. }
  104. if ($this->hasPCOV()) {
  105. return \sprintf(
  106. '%s with PCOV %s',
  107. $this->getNameWithVersion(),
  108. \phpversion('pcov')
  109. );
  110. }
  111. }
  112. public function getName(): string
  113. {
  114. if ($this->isHHVM()) {
  115. // @codeCoverageIgnoreStart
  116. return 'HHVM';
  117. // @codeCoverageIgnoreEnd
  118. }
  119. if ($this->isPHPDBG()) {
  120. // @codeCoverageIgnoreStart
  121. return 'PHPDBG';
  122. // @codeCoverageIgnoreEnd
  123. }
  124. return 'PHP';
  125. }
  126. public function getVendorUrl(): string
  127. {
  128. if ($this->isHHVM()) {
  129. // @codeCoverageIgnoreStart
  130. return 'http://hhvm.com/';
  131. // @codeCoverageIgnoreEnd
  132. }
  133. return 'https://secure.php.net/';
  134. }
  135. public function getVersion(): string
  136. {
  137. if ($this->isHHVM()) {
  138. // @codeCoverageIgnoreStart
  139. return HHVM_VERSION;
  140. // @codeCoverageIgnoreEnd
  141. }
  142. return \PHP_VERSION;
  143. }
  144. /**
  145. * Returns true when the runtime used is PHP and Xdebug is loaded.
  146. */
  147. public function hasXdebug(): bool
  148. {
  149. return ($this->isPHP() || $this->isHHVM()) && \extension_loaded('xdebug');
  150. }
  151. /**
  152. * Returns true when the runtime used is HHVM.
  153. */
  154. public function isHHVM(): bool
  155. {
  156. return \defined('HHVM_VERSION');
  157. }
  158. /**
  159. * Returns true when the runtime used is PHP without the PHPDBG SAPI.
  160. */
  161. public function isPHP(): bool
  162. {
  163. return !$this->isHHVM() && !$this->isPHPDBG();
  164. }
  165. /**
  166. * Returns true when the runtime used is PHP with the PHPDBG SAPI.
  167. */
  168. public function isPHPDBG(): bool
  169. {
  170. return \PHP_SAPI === 'phpdbg' && !$this->isHHVM();
  171. }
  172. /**
  173. * Returns true when the runtime used is PHP with the PHPDBG SAPI
  174. * and the phpdbg_*_oplog() functions are available (PHP >= 7.0).
  175. */
  176. public function hasPHPDBGCodeCoverage(): bool
  177. {
  178. return $this->isPHPDBG();
  179. }
  180. /**
  181. * Returns true when the runtime used is PHP with PCOV loaded and enabled
  182. */
  183. public function hasPCOV(): bool
  184. {
  185. return $this->isPHP() && \extension_loaded('pcov') && \ini_get('pcov.enabled');
  186. }
  187. /**
  188. * Parses the loaded php.ini file (if any) as well as all
  189. * additional php.ini files from the additional ini dir for
  190. * a list of all configuration settings loaded from files
  191. * at startup. Then checks for each php.ini setting passed
  192. * via the `$values` parameter whether this setting has
  193. * been changed at runtime. Returns an array of strings
  194. * where each string has the format `key=value` denoting
  195. * the name of a changed php.ini setting with its new value.
  196. *
  197. * @return string[]
  198. */
  199. public function getCurrentSettings(array $values): array
  200. {
  201. $diff = [];
  202. $files = [];
  203. if ($file = \php_ini_loaded_file()) {
  204. $files[] = $file;
  205. }
  206. if ($scanned = \php_ini_scanned_files()) {
  207. $files = \array_merge(
  208. $files,
  209. \array_map(
  210. 'trim',
  211. \explode(",\n", $scanned)
  212. )
  213. );
  214. }
  215. foreach ($files as $ini) {
  216. $config = \parse_ini_file($ini, true);
  217. foreach ($values as $value) {
  218. $set = \ini_get($value);
  219. if (isset($config[$value]) && $set != $config[$value]) {
  220. $diff[] = \sprintf('%s=%s', $value, $set);
  221. }
  222. }
  223. }
  224. return $diff;
  225. }
  226. }