Status.php 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  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. $callable = [$this, 'report'.$op];
  71. if (!is_callable($callable)) {
  72. throw new \InvalidArgumentException('Unknown op handler: '.$op);
  73. }
  74. $params = $data !== null ? [$data] : [];
  75. call_user_func_array($callable, $params);
  76. }
  77. }
  78. /**
  79. * Outputs a status message
  80. */
  81. private function output(string $text, ?string $level = null): void
  82. {
  83. if ($this->logger !== null) {
  84. $this->logger->log($level !== null ? $level: LogLevel::DEBUG, $text);
  85. }
  86. if ($this->debug) {
  87. fwrite(STDERR, sprintf('xdebug-handler[%d] %s', getmypid(), $text.PHP_EOL));
  88. }
  89. }
  90. /**
  91. * Checking status message
  92. */
  93. private function reportCheck(string $loaded): void
  94. {
  95. list($version, $mode) = explode('|', $loaded);
  96. if ($version !== '') {
  97. $this->loaded = '('.$version.')'.($mode !== '' ? ' xdebug.mode='.$mode : '');
  98. }
  99. $this->modeOff = $mode === 'off';
  100. $this->output('Checking '.$this->envAllowXdebug);
  101. }
  102. /**
  103. * Error status message
  104. */
  105. private function reportError(string $error): void
  106. {
  107. $this->output(sprintf('No restart (%s)', $error), LogLevel::WARNING);
  108. }
  109. /**
  110. * Info status message
  111. */
  112. private function reportInfo(string $info): void
  113. {
  114. $this->output($info);
  115. }
  116. /**
  117. * No restart status message
  118. */
  119. private function reportNoRestart(): void
  120. {
  121. $this->output($this->getLoadedMessage());
  122. if ($this->loaded !== null) {
  123. $text = sprintf('No restart (%s)', $this->getEnvAllow());
  124. if (!((bool) getenv($this->envAllowXdebug))) {
  125. $text .= ' Allowed by '.($this->modeOff ? 'xdebug.mode' : 'application');
  126. }
  127. $this->output($text);
  128. }
  129. }
  130. /**
  131. * Restart status message
  132. */
  133. private function reportRestart(): void
  134. {
  135. $this->output($this->getLoadedMessage());
  136. Process::setEnv(self::ENV_RESTART, (string) microtime(true));
  137. }
  138. /**
  139. * Restarted status message
  140. */
  141. private function reportRestarted(): void
  142. {
  143. $loaded = $this->getLoadedMessage();
  144. $text = sprintf('Restarted (%d ms). %s', $this->time, $loaded);
  145. $level = $this->loaded !== null ? LogLevel::WARNING : null;
  146. $this->output($text, $level);
  147. }
  148. /**
  149. * Restarting status message
  150. */
  151. private function reportRestarting(string $command): void
  152. {
  153. $text = sprintf('Process restarting (%s)', $this->getEnvAllow());
  154. $this->output($text);
  155. $text = 'Running '.$command;
  156. $this->output($text);
  157. }
  158. /**
  159. * Returns the _ALLOW_XDEBUG environment variable as name=value
  160. */
  161. private function getEnvAllow(): string
  162. {
  163. return $this->envAllowXdebug.'='.getenv($this->envAllowXdebug);
  164. }
  165. /**
  166. * Returns the Xdebug status and version
  167. */
  168. private function getLoadedMessage(): string
  169. {
  170. $loaded = $this->loaded !== null ? sprintf('loaded %s', $this->loaded) : 'not loaded';
  171. return 'The Xdebug extension is '.$loaded;
  172. }
  173. }