[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/vendor/splitbrain/php-cli/src/ -> Base.php (source)

   1  <?php
   2  
   3  namespace splitbrain\phpcli;
   4  
   5  /**
   6   * Class CLIBase
   7   *
   8   * All base functionality is implemented here.
   9   *
  10   * Your commandline should not inherit from this class, but from one of the *CLI* classes
  11   *
  12   * @author Andreas Gohr <andi@splitbrain.org>
  13   * @license MIT
  14   */
  15  abstract class Base
  16  {
  17      /** @var string the executed script itself */
  18      protected $bin;
  19      /** @var  Options the option parser */
  20      protected $options;
  21      /** @var  Colors */
  22      public $colors;
  23  
  24      /** @var array PSR-3 compatible loglevels and their prefix, color, output channel, enabled status */
  25      protected $loglevel = array(
  26          'debug' => array(
  27              'icon' => '',
  28              'color' => Colors::C_RESET,
  29              'channel' => STDOUT,
  30              'enabled' => true
  31          ),
  32          'info' => array(
  33              'icon' => 'ℹ ',
  34              'color' => Colors::C_CYAN,
  35              'channel' => STDOUT,
  36              'enabled' => true
  37          ),
  38          'notice' => array(
  39              'icon' => '☛ ',
  40              'color' => Colors::C_CYAN,
  41              'channel' => STDOUT,
  42              'enabled' => true
  43          ),
  44          'success' => array(
  45              'icon' => '✓ ',
  46              'color' => Colors::C_GREEN,
  47              'channel' => STDOUT,
  48              'enabled' => true
  49          ),
  50          'warning' => array(
  51              'icon' => '⚠ ',
  52              'color' => Colors::C_BROWN,
  53              'channel' => STDERR,
  54              'enabled' => true
  55          ),
  56          'error' => array(
  57              'icon' => '✗ ',
  58              'color' => Colors::C_RED,
  59              'channel' => STDERR,
  60              'enabled' => true
  61          ),
  62          'critical' => array(
  63              'icon' => '☠ ',
  64              'color' => Colors::C_LIGHTRED,
  65              'channel' => STDERR,
  66              'enabled' => true
  67          ),
  68          'alert' => array(
  69              'icon' => '✖ ',
  70              'color' => Colors::C_LIGHTRED,
  71              'channel' => STDERR,
  72              'enabled' => true
  73          ),
  74          'emergency' => array(
  75              'icon' => '✘ ',
  76              'color' => Colors::C_LIGHTRED,
  77              'channel' => STDERR,
  78              'enabled' => true
  79          ),
  80      );
  81  
  82      /** @var string default log level */
  83      protected $logdefault = 'info';
  84  
  85      /**
  86       * constructor
  87       *
  88       * Initialize the arguments, set up helper classes and set up the CLI environment
  89       *
  90       * @param bool $autocatch should exceptions be catched and handled automatically?
  91       */
  92      public function __construct($autocatch = true)
  93      {
  94          if ($autocatch) {
  95              set_exception_handler(array($this, 'fatal'));
  96          }
  97  
  98          $this->colors = new Colors();
  99          $this->options = new Options($this->colors);
 100      }
 101  
 102      /**
 103       * Register options and arguments on the given $options object
 104       *
 105       * @param Options $options
 106       * @return void
 107       *
 108       * @throws Exception
 109       */
 110      abstract protected function setup(Options $options);
 111  
 112      /**
 113       * Your main program
 114       *
 115       * Arguments and options have been parsed when this is run
 116       *
 117       * @param Options $options
 118       * @return void
 119       *
 120       * @throws Exception
 121       */
 122      abstract protected function main(Options $options);
 123  
 124      /**
 125       * Execute the CLI program
 126       *
 127       * Executes the setup() routine, adds default options, initiate the options parsing and argument checking
 128       * and finally executes main() - Each part is split into their own protected function below, so behaviour
 129       * can easily be overwritten
 130       *
 131       * @throws Exception
 132       */
 133      public function run()
 134      {
 135          if ('cli' != php_sapi_name()) {
 136              throw new Exception('This has to be run from the command line');
 137          }
 138  
 139          $this->setup($this->options);
 140          $this->registerDefaultOptions();
 141          $this->parseOptions();
 142          $this->handleDefaultOptions();
 143          $this->setupLogging();
 144          $this->checkArguments();
 145          $this->execute();
 146      }
 147  
 148      // region run handlers - for easier overriding
 149  
 150      /**
 151       * Add the default help, color and log options
 152       */
 153      protected function registerDefaultOptions()
 154      {
 155          $this->options->registerOption(
 156              'help',
 157              'Display this help screen and exit immediately.',
 158              'h'
 159          );
 160          $this->options->registerOption(
 161              'no-colors',
 162              'Do not use any colors in output. Useful when piping output to other tools or files.'
 163          );
 164          $this->options->registerOption(
 165              'loglevel',
 166              'Minimum level of messages to display. Default is ' . $this->colors->wrap($this->logdefault, Colors::C_CYAN) . '. ' .
 167              'Valid levels are: debug, info, notice, success, warning, error, critical, alert, emergency.',
 168              null,
 169              'level'
 170          );
 171      }
 172  
 173      /**
 174       * Handle the default options
 175       */
 176      protected function handleDefaultOptions()
 177      {
 178          if ($this->options->getOpt('no-colors')) {
 179              $this->colors->disable();
 180          }
 181          if ($this->options->getOpt('help')) {
 182              echo $this->options->help();
 183              exit(0);
 184          }
 185      }
 186  
 187      /**
 188       * Handle the logging options
 189       */
 190      protected function setupLogging()
 191      {
 192          $level = $this->options->getOpt('loglevel', $this->logdefault);
 193          $this->setLogLevel($level);
 194      }
 195  
 196      /**
 197       * Wrapper around the option parsing
 198       */
 199      protected function parseOptions()
 200      {
 201          $this->options->parseOptions();
 202      }
 203  
 204      /**
 205       * Wrapper around the argument checking
 206       */
 207      protected function checkArguments()
 208      {
 209          $this->options->checkArguments();
 210      }
 211  
 212      /**
 213       * Wrapper around main
 214       */
 215      protected function execute()
 216      {
 217          $this->main($this->options);
 218      }
 219  
 220      // endregion
 221  
 222      // region logging
 223  
 224      /**
 225       * Set the current log level
 226       *
 227       * @param string $level
 228       */
 229      public function setLogLevel($level)
 230      {
 231          if (!isset($this->loglevel[$level])) $this->fatal('Unknown log level');
 232          $enable = false;
 233          foreach (array_keys($this->loglevel) as $l) {
 234              if ($l == $level) $enable = true;
 235              $this->loglevel[$l]['enabled'] = $enable;
 236          }
 237      }
 238  
 239      /**
 240       * Check if a message with the given level should be logged
 241       *
 242       * @param string $level
 243       * @return bool
 244       */
 245      public function isLogLevelEnabled($level)
 246      {
 247          if (!isset($this->loglevel[$level])) $this->fatal('Unknown log level');
 248          return $this->loglevel[$level]['enabled'];
 249      }
 250  
 251      /**
 252       * Exits the program on a fatal error
 253       *
 254       * @param \Exception|string $error either an exception or an error message
 255       * @param array $context
 256       */
 257      public function fatal($error, array $context = array())
 258      {
 259          $code = 0;
 260          if (is_object($error) && is_a($error, 'Exception')) {
 261              /** @var Exception $error */
 262              $this->logMessage('debug', get_class($error) . ' caught in ' . $error->getFile() . ':' . $error->getLine());
 263              $this->logMessage('debug', $error->getTraceAsString());
 264              $code = $error->getCode();
 265              $error = $error->getMessage();
 266  
 267          }
 268          if (!$code) {
 269              $code = Exception::E_ANY;
 270          }
 271  
 272          $this->logMessage('critical', $error, $context);
 273          exit($code);
 274      }
 275  
 276      /**
 277       * Normal, positive outcome (This is not a PSR-3 level)
 278       *
 279       * @param string $string
 280       * @param array $context
 281       */
 282      public function success($string, array $context = array())
 283      {
 284          $this->logMessage('success', $string, $context);
 285      }
 286  
 287      /**
 288       * @param string $level
 289       * @param string $message
 290       * @param array $context
 291       */
 292      protected function logMessage($level, $message, array $context = array())
 293      {
 294          // unknown level is always an error
 295          if (!isset($this->loglevel[$level])) $level = 'error';
 296  
 297          $info = $this->loglevel[$level];
 298          if (!$this->isLogLevelEnabled($level)) return; // no logging for this level
 299  
 300          $message = $this->interpolate($message, $context);
 301  
 302          // when colors are wanted, we also add the icon
 303          if ($this->colors->isEnabled()) {
 304              $message = $info['icon'] . $message;
 305          }
 306  
 307          $this->colors->ptln($message, $info['color'], $info['channel']);
 308      }
 309  
 310      /**
 311       * Interpolates context values into the message placeholders.
 312       *
 313       * @param $message
 314       * @param array $context
 315       * @return string
 316       */
 317      protected function interpolate($message, array $context = array())
 318      {
 319          // build a replacement array with braces around the context keys
 320          $replace = array();
 321          foreach ($context as $key => $val) {
 322              // check that the value can be casted to string
 323              if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) {
 324                  $replace['{' . $key . '}'] = $val;
 325              }
 326          }
 327  
 328          // interpolate replacement values into the message and return
 329          return strtr((string)$message, $replace);
 330      }
 331  
 332      // endregion
 333  }