Status.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. <?php
  2. /*
  3. * This file is part of composer/xdebug-handler.
  4. *
  5. * (c) Composer <https://github.com/composer>
  6. *
  7. * For the full copyright and license information, please view
  8. * the LICENSE file that was distributed with this source code.
  9. */
  10. declare(strict_types=1);
  11. namespace Composer\XdebugHandler;
  12. use Psr\Log\LoggerInterface;
  13. use Psr\Log\LogLevel;
  14. /**
  15. * @author John Stevenson <john-stevenson@blueyonder.co.uk>
  16. * @internal
  17. */
  18. class Status
  19. {
  20. const ENV_RESTART = 'XDEBUG_HANDLER_RESTART';
  21. const CHECK = 'Check';
  22. const ERROR = 'Error';
  23. const INFO = 'Info';
  24. const NORESTART = 'NoRestart';
  25. const RESTART = 'Restart';
  26. const RESTARTING = 'Restarting';
  27. const RESTARTED = 'Restarted';
  28. /** @var bool */
  29. private $debug;
  30. /** @var string */
  31. private $envAllowXdebug;
  32. /** @var string|null */
  33. private $loaded;
  34. /** @var LoggerInterface|null */
  35. private $logger;
  36. /** @var bool */
  37. private $modeOff;
  38. /** @var float */
  39. private $time;
  40. /**
  41. * @param string $envAllowXdebug Prefixed _ALLOW_XDEBUG name
  42. * @param bool $debug Whether debug output is required
  43. */
  44. public function __construct(string $envAllowXdebug, bool $debug)
  45. {
  46. $start = getenv(self::ENV_RESTART);
  47. Process::setEnv(self::ENV_RESTART);
  48. $this->time = is_numeric($start) ? round((microtime(true) - $start) * 1000) : 0;
  49. $this->envAllowXdebug = $envAllowXdebug;
  50. $this->debug = $debug && defined('STDERR');
  51. $this->modeOff = false;
  52. }
  53. /**
  54. * Activates status message output to a PSR3 logger
  55. *
  56. * @return void
  57. */
  58. public function setLogger(LoggerInterface $logger): void
  59. {
  60. $this->logger = $logger;
  61. }
  62. /**
  63. * Calls a handler method to report a message
  64. *
  65. * @throws \InvalidArgumentException If $op is not known
  66. */
  67. public function report(string $op, ?string $data): void
  68. {
  69. if ($this->logger !== null || $this->debug) {
  70. $param = (string) $data;
  71. switch($op) {
  72. case self::CHECK:
  73. $this->reportCheck($param);
  74. break;
  75. case self::ERROR:
  76. $this->reportError($param);
  77. break;
  78. case self::INFO:
  79. $this->reportInfo($param);
  80. break;
  81. case self::NORESTART:
  82. $this->reportNoRestart();
  83. break;
  84. case self::RESTART:
  85. $this->reportRestart();
  86. break;
  87. case self::RESTARTED:
  88. $this->reportRestarted();
  89. break;
  90. case self::RESTARTING:
  91. $this->reportRestarting($param);
  92. break;
  93. default:
  94. throw new \InvalidArgumentException('Unknown op handler: '.$op);
  95. }
  96. }
  97. }
  98. /**
  99. * Outputs a status message
  100. */
  101. private function output(string $text, ?string $level = null): void
  102. {
  103. if ($this->logger !== null) {
  104. $this->logger->log($level !== null ? $level: LogLevel::DEBUG, $text);
  105. }
  106. if ($this->debug) {
  107. fwrite(STDERR, sprintf('xdebug-handler[%d] %s', getmypid(), $text.PHP_EOL));
  108. }
  109. }
  110. /**
  111. * Checking status message
  112. */
  113. private function reportCheck(string $loaded): void
  114. {
  115. list($version, $mode) = explode('|', $loaded);
  116. if ($version !== '') {
  117. $this->loaded = '('.$version.')'.($mode !== '' ? ' xdebug.mode='.$mode : '');
  118. }
  119. $this->modeOff = $mode === 'off';
  120. $this->output('Checking '.$this->envAllowXdebug);
  121. }
  122. /**
  123. * Error status message
  124. */
  125. private function reportError(string $error): void
  126. {
  127. $this->output(sprintf('No restart (%s)', $error), LogLevel::WARNING);
  128. }
  129. /**
  130. * Info status message
  131. */
  132. private function reportInfo(string $info): void
  133. {
  134. $this->output($info);
  135. }
  136. /**
  137. * No restart status message
  138. */
  139. private function reportNoRestart(): void
  140. {
  141. $this->output($this->getLoadedMessage());
  142. if ($this->loaded !== null) {
  143. $text = sprintf('No restart (%s)', $this->getEnvAllow());
  144. if (!((bool) getenv($this->envAllowXdebug))) {
  145. $text .= ' Allowed by '.($this->modeOff ? 'xdebug.mode' : 'application');
  146. }
  147. $this->output($text);
  148. }
  149. }
  150. /**
  151. * Restart status message
  152. */
  153. private function reportRestart(): void
  154. {
  155. $this->output($this->getLoadedMessage());
  156. Process::setEnv(self::ENV_RESTART, (string) microtime(true));
  157. }
  158. /**
  159. * Restarted status message
  160. */
  161. private function reportRestarted(): void
  162. {
  163. $loaded = $this->getLoadedMessage();
  164. $text = sprintf('Restarted (%d ms). %s', $this->time, $loaded);
  165. $level = $this->loaded !== null ? LogLevel::WARNING : null;
  166. $this->output($text, $level);
  167. }
  168. /**
  169. * Restarting status message
  170. */
  171. private function reportRestarting(string $command): void
  172. {
  173. $text = sprintf('Process restarting (%s)', $this->getEnvAllow());
  174. $this->output($text);
  175. $text = 'Running: '.$command;
  176. $this->output($text);
  177. }
  178. /**
  179. * Returns the _ALLOW_XDEBUG environment variable as name=value
  180. */
  181. private function getEnvAllow(): string
  182. {
  183. return $this->envAllowXdebug.'='.getenv($this->envAllowXdebug);
  184. }
  185. /**
  186. * Returns the Xdebug status and version
  187. */
  188. private function getLoadedMessage(): string
  189. {
  190. $loaded = $this->loaded !== null ? sprintf('loaded %s', $this->loaded) : 'not loaded';
  191. return 'The Xdebug extension is '.$loaded;
  192. }
  193. }