[ 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 */
  25      protected $loglevel = array(
  26          'debug' => array('', Colors::C_RESET, STDOUT),
  27          'info' => array('ℹ ', Colors::C_CYAN, STDOUT),
  28          'notice' => array('☛ ', Colors::C_CYAN, STDOUT),
  29          'success' => array('✓ ', Colors::C_GREEN, STDOUT),
  30          'warning' => array('⚠ ', Colors::C_BROWN, STDERR),
  31          'error' => array('✗ ', Colors::C_RED, STDERR),
  32          'critical' => array('☠ ', Colors::C_LIGHTRED, STDERR),
  33          'alert' => array('✖ ', Colors::C_LIGHTRED, STDERR),
  34          'emergency' => array('✘ ', Colors::C_LIGHTRED, STDERR),
  35      );
  36  
  37      protected $logdefault = 'info';
  38  
  39      /**
  40       * constructor
  41       *
  42       * Initialize the arguments, set up helper classes and set up the CLI environment
  43       *
  44       * @param bool $autocatch should exceptions be catched and handled automatically?
  45       */
  46      public function __construct($autocatch = true)
  47      {
  48          if ($autocatch) {
  49              set_exception_handler(array($this, 'fatal'));
  50          }
  51  
  52          $this->colors = new Colors();
  53          $this->options = new Options($this->colors);
  54      }
  55  
  56      /**
  57       * Register options and arguments on the given $options object
  58       *
  59       * @param Options $options
  60       * @return void
  61       *
  62       * @throws Exception
  63       */
  64      abstract protected function setup(Options $options);
  65  
  66      /**
  67       * Your main program
  68       *
  69       * Arguments and options have been parsed when this is run
  70       *
  71       * @param Options $options
  72       * @return void
  73       *
  74       * @throws Exception
  75       */
  76      abstract protected function main(Options $options);
  77  
  78      /**
  79       * Execute the CLI program
  80       *
  81       * Executes the setup() routine, adds default options, initiate the options parsing and argument checking
  82       * and finally executes main() - Each part is split into their own protected function below, so behaviour
  83       * can easily be overwritten
  84       *
  85       * @throws Exception
  86       */
  87      public function run()
  88      {
  89          if ('cli' != php_sapi_name()) {
  90              throw new Exception('This has to be run from the command line');
  91          }
  92  
  93          $this->setup($this->options);
  94          $this->registerDefaultOptions();
  95          $this->parseOptions();
  96          $this->handleDefaultOptions();
  97          $this->setupLogging();
  98          $this->checkArguments();
  99          $this->execute();
 100      }
 101  
 102      // region run handlers - for easier overriding
 103  
 104      /**
 105       * Add the default help, color and log options
 106       */
 107      protected function registerDefaultOptions()
 108      {
 109          $this->options->registerOption(
 110              'help',
 111              'Display this help screen and exit immediately.',
 112              'h'
 113          );
 114          $this->options->registerOption(
 115              'no-colors',
 116              'Do not use any colors in output. Useful when piping output to other tools or files.'
 117          );
 118          $this->options->registerOption(
 119              'loglevel',
 120              'Minimum level of messages to display. Default is ' . $this->colors->wrap($this->logdefault, Colors::C_CYAN) . '. ' .
 121              'Valid levels are: debug, info, notice, success, warning, error, critical, alert, emergency.',
 122              null,
 123              'level'
 124          );
 125      }
 126  
 127      /**
 128       * Handle the default options
 129       */
 130      protected function handleDefaultOptions()
 131      {
 132          if ($this->options->getOpt('no-colors')) {
 133              $this->colors->disable();
 134          }
 135          if ($this->options->getOpt('help')) {
 136              echo $this->options->help();
 137              exit(0);
 138          }
 139      }
 140  
 141      /**
 142       * Handle the logging options
 143       */
 144      protected function setupLogging()
 145      {
 146          $level = $this->options->getOpt('loglevel', $this->logdefault);
 147          if (!isset($this->loglevel[$level])) $this->fatal('Unknown log level');
 148          foreach (array_keys($this->loglevel) as $l) {
 149              if ($l == $level) break;
 150              unset($this->loglevel[$l]);
 151          }
 152      }
 153  
 154      /**
 155       * Wrapper around the option parsing
 156       */
 157      protected function parseOptions()
 158      {
 159          $this->options->parseOptions();
 160      }
 161  
 162      /**
 163       * Wrapper around the argument checking
 164       */
 165      protected function checkArguments()
 166      {
 167          $this->options->checkArguments();
 168      }
 169  
 170      /**
 171       * Wrapper around main
 172       */
 173      protected function execute()
 174      {
 175          $this->main($this->options);
 176      }
 177  
 178      // endregion
 179  
 180      // region logging
 181  
 182      /**
 183       * Exits the program on a fatal error
 184       *
 185       * @param \Exception|string $error either an exception or an error message
 186       * @param array $context
 187       */
 188      public function fatal($error, array $context = array())
 189      {
 190          $code = 0;
 191          if (is_object($error) && is_a($error, 'Exception')) {
 192              /** @var Exception $error */
 193              $this->logMessage('debug', get_class($error) . ' caught in ' . $error->getFile() . ':' . $error->getLine());
 194              $this->logMessage('debug', $error->getTraceAsString());
 195              $code = $error->getCode();
 196              $error = $error->getMessage();
 197  
 198          }
 199          if (!$code) {
 200              $code = Exception::E_ANY;
 201          }
 202  
 203          $this->logMessage('critical', $error, $context);
 204          exit($code);
 205      }
 206  
 207      /**
 208       * Normal, positive outcome (This is not a PSR-3 level)
 209       *
 210       * @param string $string
 211       * @param array $context
 212       */
 213      public function success($string, array $context = array())
 214      {
 215          $this->logMessage('success', $string, $context);
 216      }
 217  
 218      /**
 219       * @param string $level
 220       * @param string $message
 221       * @param array $context
 222       */
 223      protected function logMessage($level, $message, array $context = array())
 224      {
 225          // is this log level wanted?
 226          if (!isset($this->loglevel[$level])) return;
 227  
 228          /** @var string $prefix */
 229          /** @var string $color */
 230          /** @var resource $channel */
 231          list($prefix, $color, $channel) = $this->loglevel[$level];
 232          if (!$this->colors->isEnabled()) $prefix = '';
 233  
 234          $message = $this->interpolate($message, $context);
 235          $this->colors->ptln($prefix . $message, $color, $channel);
 236      }
 237  
 238      /**
 239       * Interpolates context values into the message placeholders.
 240       *
 241       * @param $message
 242       * @param array $context
 243       * @return string
 244       */
 245      protected function interpolate($message, array $context = array())
 246      {
 247          // build a replacement array with braces around the context keys
 248          $replace = array();
 249          foreach ($context as $key => $val) {
 250              // check that the value can be casted to string
 251              if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) {
 252                  $replace['{' . $key . '}'] = $val;
 253              }
 254          }
 255  
 256          // interpolate replacement values into the message and return
 257          return strtr((string)$message, $replace);
 258      }
 259  
 260      // endregion
 261  }