├── .gitignore ├── .scrutinizer.yml ├── .travis.yml ├── README.md ├── composer.json ├── modman ├── phpcs.xml └── src ├── lib ├── Colors │ └── Color.php └── Local │ └── Shell │ └── Formatter.php └── shell └── local ├── abstract.php └── sample.php /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS 2 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | tools: 2 | php_sim: true 3 | php_pdepend: true 4 | php_analyzer: true 5 | filter: 6 | paths: [src/*] 7 | excluded_paths: [src/lib/Colors/Color.php, src/shell/local/sample.php] 8 | checks: 9 | php: 10 | use_self_instead_of_fqcn: true 11 | uppercase_constants: true 12 | unused_variables: true 13 | unused_properties: true 14 | return_doc_comments: true 15 | return_doc_comment_if_not_inferrable: true 16 | parameters_in_camelcaps: true 17 | parameter_doc_comments: true 18 | function_in_camel_caps: true 19 | encourage_single_quotes: true 20 | encourage_postdec_operator: true 21 | coding_standard: Magento 22 | avoid_multiple_statements_on_same_line: true 23 | remove_php_closing_tag: true 24 | one_class_per_file: true 25 | psr2_class_declaration: false 26 | no_underscore_prefix_in_properties: false 27 | no_underscore_prefix_in_methods: false 28 | coding_style: 29 | php: 30 | spaces: 31 | around_operators: 32 | concatenation: true 33 | braces: 34 | classes_functions: 35 | class: new-line 36 | function: new-line 37 | closure: end-of-line 38 | if: 39 | opening: end-of-line 40 | for: 41 | opening: end-of-line 42 | while: 43 | opening: end-of-line 44 | do_while: 45 | opening: end-of-line 46 | switch: 47 | opening: end-of-line 48 | try: 49 | opening: end-of-line 50 | upper_lower_casing: 51 | keywords: 52 | general: lower 53 | constants: 54 | true_false_null: lower 55 | build_failure_conditions: 56 | - 'elements.rating(<= D).new.exists' 57 | - 'issues.new.exists' -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | branches: 3 | only: 4 | - master 5 | - dev 6 | php: 7 | - 5.3 8 | - 5.4 9 | - 5.5 10 | before_script: 11 | - curl -OL https://squizlabs.github.io/PHP_CodeSniffer/phpcs.phar 12 | script: 13 | - php phpcs.phar -v --standard=./phpcs.xml --encoding=utf-8 ./src/lib/Local/ ./src/shell/local/abstract.php -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Magento Shell 2 | === 3 | 4 | [![Join the chat at https://gitter.im/steverobbins/Magento-Shell](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/steverobbins/Magento-Shell?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 5 | 6 | [![Master Build Status](https://img.shields.io/travis/steverobbins/Magento-Shell/master.svg?style=flat-square)](https://travis-ci.org/steverobbins/Magento-Shell) 7 | [![Master Code Quality](https://img.shields.io/scrutinizer/g/steverobbins/Magento-Shell/master.svg?style=flat-square)](https://scrutinizer-ci.com/g/steverobbins/Magento-Shell/?branch=master) 8 | 9 | ## OMG COLORS 10 | 11 | ![Drool](http://i.imgur.com/a2JyNnj.gif) 12 | 13 | Add some style to your Magento scripts. 14 | 15 | ![Sample](http://i.imgur.com/gqs1UcS.gif) 16 | 17 | # Usage 18 | 19 | Extend the `shell/local/abstract.php` class and put your logic in the `run()` method, like you normally would with a Magento shell script. 20 | 21 | ## Output 22 | 23 | Instead of `echo`ing, use the `$this->log` object. You can output messages using one of `Zend_Log`'s levels 24 | 25 | ``` 26 | log->debug('Hello World!'); 53 | } 54 | } 55 | ``` 56 | 57 | ## Progress Bar 58 | 59 | Create a progress bar with `$bar = $this->progressBar($count);`, where `$count` is an integer of how many items you will iterate over. `$bar->update($i);` as you walk through, then `$bar->finish();` when complete. 60 | 61 | Example: 62 | 63 | ``` 64 | getCollection(); 73 | $count = $collection->count(); 74 | $bar = $this->progressBar($count); 75 | $i = 0; 76 | foreach ($collection as $product) { 77 | $product->setDescription(strip_tags($product->getDescription())) 78 | ->save(); 79 | $bar->update(++$i); 80 | } 81 | $bar->finish(); 82 | } 83 | } 84 | ``` 85 | 86 | # Logging 87 | 88 | Output is automatically logged to `var/log/shell_local_.log`. 89 | 90 | Alternatively, you can specify your own log file by setting the public `$logFile` variable in you script. 91 | 92 | # Support 93 | 94 | Please [create an issue](https://github.com/steverobbins/Magento-Shell/issues/new) for all bugs and feature requests 95 | 96 | # Contributing 97 | 98 | Fork this repository and send a pull request to the `dev` branch 99 | 100 | # License 101 | 102 | [WTF Public License 2.0](http://wtfpl2.com/) -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "steverobbins/magento-shell", 3 | "description": "Magento Improved Shell", 4 | "keywords": ["magento"], 5 | "type": "magento-module", 6 | "license": "WTFPL", 7 | "authors": [ 8 | { 9 | "name": "Steve Robbins", 10 | "email": "steven.j.robbins@gmail.com" 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /modman: -------------------------------------------------------------------------------- 1 | src/lib/Colors/Color.php lib/Colors/Color.php 2 | src/lib/Local lib/Local 3 | src/shell/local shell/local 4 | -------------------------------------------------------------------------------- /phpcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Magento extension ruleset based on PSR-2 but modified for Magento 1 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/lib/Colors/Color.php: -------------------------------------------------------------------------------- 1 | (.*?)#s'; 6 | /** @link http://www.php.net/manual/en/functions.user-defined.php */ 7 | const STYLE_NAME_PATTERN = '/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/'; 8 | 9 | const ESC = "\033["; 10 | const ESC_SEQ_PATTERN = "\033[%sm"; 11 | 12 | protected $initial = ''; 13 | protected $wrapped = ''; 14 | // italic and blink may not work depending of your terminal 15 | protected $styles = array( 16 | 'reset' => '0', 17 | 'bold' => '1', 18 | 'dark' => '2', 19 | 'italic' => '3', 20 | 'underline' => '4', 21 | 'blink' => '5', 22 | 'reverse' => '7', 23 | 'concealed' => '8', 24 | 25 | 'default' => '39', 26 | 'black' => '30', 27 | 'red' => '31', 28 | 'green' => '32', 29 | 'yellow' => '33', 30 | 'blue' => '34', 31 | 'magenta' => '35', 32 | 'cyan' => '36', 33 | 'light_gray' => '37', 34 | 35 | 'dark_gray' => '90', 36 | 'light_red' => '91', 37 | 'light_green' => '92', 38 | 'light_yellow' => '93', 39 | 'light_blue' => '94', 40 | 'light_magenta' => '95', 41 | 'light_cyan' => '96', 42 | 'white' => '97', 43 | 44 | 'bg_default' => '49', 45 | 'bg_black' => '40', 46 | 'bg_red' => '41', 47 | 'bg_green' => '42', 48 | 'bg_yellow' => '43', 49 | 'bg_blue' => '44', 50 | 'bg_magenta' => '45', 51 | 'bg_cyan' => '46', 52 | 'bg_light_gray' => '47', 53 | 54 | 'bg_dark_gray' => '100', 55 | 'bg_light_red' => '101', 56 | 'bg_light_green' => '102', 57 | 'bg_light_yellow' => '103', 58 | 'bg_light_blue' => '104', 59 | 'bg_light_magenta' => '105', 60 | 'bg_light_cyan' => '106', 61 | 'bg_white' => '107', 62 | ); 63 | protected $userStyles = array(); 64 | protected $isStyleForced = false; 65 | 66 | public function __construct($string = '') 67 | { 68 | $this->setInternalState($string); 69 | } 70 | 71 | public function __invoke($string) 72 | { 73 | return $this->setInternalState($string); 74 | } 75 | 76 | public function __call($method, $args) 77 | { 78 | if (count($args) >= 1) { 79 | return $this->apply($method, $args[0]); 80 | } 81 | 82 | return $this->apply($method); 83 | } 84 | 85 | public function __get($name) 86 | { 87 | return $this->apply($name); 88 | } 89 | 90 | public function __toString() 91 | { 92 | return $this->wrapped; 93 | } 94 | 95 | public function setForceStyle($force) 96 | { 97 | $this->isStyleForced = (bool) $force; 98 | } 99 | 100 | public function isStyleForced() 101 | { 102 | return $this->isStyleForced; 103 | } 104 | 105 | /** 106 | * Returns true if the stream supports colorization. 107 | * 108 | * Colorization is disabled if not supported by the stream: 109 | * 110 | * - Windows without Ansicon and ConEmu 111 | * - non tty consoles 112 | * 113 | * @return bool true if the stream supports colorization, false otherwise 114 | * 115 | * @link https://github.com/symfony/Console/blob/master/Output/StreamOutput.php#L95-L102 116 | * @codeCoverageIgnore 117 | */ 118 | public function isSupported() 119 | { 120 | if (DIRECTORY_SEPARATOR == '\\') { 121 | return false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI'); 122 | } 123 | 124 | return function_exists('posix_isatty') && @posix_isatty(STDOUT); 125 | } 126 | 127 | /** 128 | * @codeCoverageIgnore 129 | */ 130 | public function are256ColorsSupported() 131 | { 132 | return DIRECTORY_SEPARATOR === '/' && false !== strpos(getenv('TERM'), '256color'); 133 | } 134 | 135 | protected function setInternalState($string) 136 | { 137 | $this->initial = $this->wrapped = (string) $string; 138 | return $this; 139 | } 140 | 141 | protected function stylize($style, $text) 142 | { 143 | if (!$this->shouldStylize()) { 144 | return $text; 145 | } 146 | 147 | $style = strtolower($style); 148 | 149 | if ($this->isUserStyleExists($style)) { 150 | return $this->applyUserStyle($style, $text); 151 | } 152 | 153 | if ($this->isStyleExists($style)) { 154 | return $this->applyStyle($style, $text); 155 | } 156 | 157 | if (preg_match('/^((?:bg_)?)color\[([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\]$/', $style, $matches)) { 158 | $option = $matches[1] == 'bg_' ? 48 : 38; 159 | return $this->buildEscSeq("{$option};5;{$matches[2]}") . $text . $this->buildEscSeq($this->styles['reset']); 160 | } 161 | 162 | throw new Exception("Invalid style $style"); 163 | } 164 | 165 | protected function shouldStylize() 166 | { 167 | return $this->isStyleForced() || $this->isSupported(); 168 | } 169 | 170 | protected function isStyleExists($style) 171 | { 172 | return array_key_exists($style, $this->styles); 173 | } 174 | 175 | protected function applyStyle($style, $text) 176 | { 177 | return $this->buildEscSeq($this->styles[$style]) . $text . $this->buildEscSeq($this->styles['reset']); 178 | } 179 | 180 | protected function buildEscSeq($style) 181 | { 182 | return sprintf(self::ESC_SEQ_PATTERN, $style); 183 | } 184 | 185 | protected function isUserStyleExists($style) 186 | { 187 | return array_key_exists($style, $this->userStyles); 188 | } 189 | 190 | protected function applyUserStyle($userStyle, $text) 191 | { 192 | $styles = (array) $this->userStyles[$userStyle]; 193 | 194 | foreach ($styles as $style) { 195 | $text = $this->stylize($style, $text); 196 | } 197 | 198 | return $text; 199 | } 200 | 201 | public function apply($style, $text = null) 202 | { 203 | if ($text === null) { 204 | $this->wrapped = $this->stylize($style, $this->wrapped); 205 | return $this; 206 | } 207 | 208 | return $this->stylize($style, $text); 209 | } 210 | 211 | public function fg($color, $text = null) 212 | { 213 | return $this->apply($color, $text); 214 | } 215 | 216 | public function bg($color, $text = null) 217 | { 218 | return $this->apply('bg_' . $color, $text); 219 | } 220 | 221 | public function highlight($color, $text = null) 222 | { 223 | return $this->bg($color, $text); 224 | } 225 | 226 | public function reset() 227 | { 228 | $this->wrapped = $this->initial; 229 | return $this; 230 | } 231 | 232 | public function center($width = 80, $text = null) 233 | { 234 | if ($text === null) { 235 | $text = $this->wrapped; 236 | } 237 | 238 | $centered = ''; 239 | foreach (explode(PHP_EOL, $text) as $line) { 240 | $line = trim($line); 241 | $lineWidth = strlen($line) - mb_strlen($line, 'UTF-8') + $width; 242 | $centered .= str_pad($line, $lineWidth, ' ', STR_PAD_BOTH) . PHP_EOL; 243 | } 244 | 245 | $this->setInternalState(trim($centered, PHP_EOL)); 246 | return $this; 247 | } 248 | 249 | protected function stripColors($text) 250 | { 251 | return preg_replace('/' . preg_quote(self::ESC) . '\d+m/', '', $text); 252 | } 253 | 254 | public function clean($text = null) 255 | { 256 | if ($text === null) { 257 | $this->wrapped = $this->stripColors($this->wrapped); 258 | return $this; 259 | } 260 | 261 | return $this->stripColors($text); 262 | } 263 | 264 | public function strip($text = null) 265 | { 266 | return $this->clean($text); 267 | } 268 | 269 | public function isAValidStyleName($name) 270 | { 271 | return preg_match(self::STYLE_NAME_PATTERN, $name); 272 | } 273 | 274 | /** 275 | * @deprecated 276 | * @codeCoverageIgnore 277 | */ 278 | public function setTheme(array $theme) 279 | { 280 | return $this->setUserStyles($theme); 281 | } 282 | 283 | public function setUserStyles(array $userStyles) 284 | { 285 | foreach ($userStyles as $name => $styles) { 286 | if (!$this->isAValidStyleName($name)) { 287 | throw new Exception("$name is not a valid style name"); 288 | } 289 | } 290 | 291 | $this->userStyles = $userStyles; 292 | return $this; 293 | } 294 | 295 | protected function colorizeText($text) 296 | { 297 | return preg_replace_callback(self::FORMAT_PATTERN, array($this, 'replaceStyle'), $text); 298 | } 299 | 300 | /** 301 | * @link https://github.com/symfony/Console/blob/master/Formatter/OutputFormatter.php#L124-162 302 | */ 303 | public function colorize($text = null) 304 | { 305 | if ($text === null) { 306 | $this->wrapped = $this->colorizeText($this->wrapped); 307 | return $this; 308 | } 309 | 310 | return $this->colorizeText($text); 311 | } 312 | 313 | protected function replaceStyle($matches) 314 | { 315 | return $this->apply($matches[1], $this->colorize($matches[2])); 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /src/lib/Local/Shell/Formatter.php: -------------------------------------------------------------------------------- 1 | color($event['timestamp'])->dark_gray() 28 | . str_repeat(' ', 7 - strlen($event['priorityName'])) 29 | . '[' . $this->priorityColor($event['priorityName'], $event['priority']) . '] ' 30 | . $event['message'] . PHP_EOL; 31 | } 32 | 33 | /** 34 | * Colorize by zend log level 35 | * 36 | * @param string $message 37 | * @param integer $priority 38 | * @return Colors_Color 39 | */ 40 | public function priorityColor($message, $priority) 41 | { 42 | switch ($priority) { 43 | case Zend_Log::INFO: 44 | return $this->color($message)->green(); 45 | case Zend_Log::NOTICE: 46 | return $this->color($message)->cyan(); 47 | case Zend_Log::WARN: 48 | return $this->color($message)->yellow(); 49 | case Zend_Log::ERR: 50 | return $this->color($message)->red(); 51 | case Zend_Log::CRIT: 52 | return $this->color($message)->red()->bold(); 53 | case Zend_Log::ALERT: 54 | return $this->color($message)->white()->bg_red(); 55 | case Zend_Log::EMERG: 56 | return $this->color($message)->white()->bg_red()->bold()->blink(); 57 | } 58 | return $this->color($message)->light_green(); 59 | } 60 | 61 | /** 62 | * Color a string 63 | * 64 | * @param string $message 65 | * @return Colors_Color 66 | */ 67 | public function color($message) 68 | { 69 | return new Colors_Color($message); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/shell/local/abstract.php: -------------------------------------------------------------------------------- 1 | log->debug($message) 28 | * 29 | * For Zend_Log::ERR 30 | * 31 | * $this->log->err($message) 32 | * 33 | * @var Zend_Log 34 | */ 35 | public $log; 36 | 37 | /** 38 | * Script execution start time 39 | * 40 | * @var double 41 | */ 42 | protected $_timeStart; 43 | 44 | /** 45 | * Memory used at script start 46 | * 47 | * @var integer 48 | */ 49 | protected $_memoryUsageStart; 50 | 51 | /** 52 | * Readable memory units 53 | * 54 | * @var array 55 | */ 56 | protected $_memoryUnits = array( 57 | 'bytes', 58 | 'KB', 59 | 'MB', 60 | 'GB', 61 | 'TB', 62 | 'PB', 63 | 'EB', 64 | 'ZB', 65 | 'YB' 66 | ); 67 | 68 | /** 69 | * Initialize application and parse input parameters 70 | */ 71 | public function __construct() 72 | { 73 | $this->_timeStart = (float) microtime(true); 74 | $this->_memoryUsageStart = $this->getMemoryUsage(); 75 | parent::__construct(); 76 | $this->initLog(); 77 | $this->log->debug('== Script execution started =='); 78 | } 79 | 80 | /** 81 | * Show execution time 82 | */ 83 | public function __destruct() 84 | { 85 | if (!$this->log) { 86 | return; 87 | } 88 | $this->log->debug( 89 | 'Complete in ' 90 | . round(microtime(true) - $this->_timeStart, 3) 91 | . ' seconds. ' 92 | . $this->getMemoryUsageNow() 93 | . ' of memory used.' 94 | ); 95 | $this->log->debug('== Script execution completed =='); 96 | } 97 | 98 | /** 99 | * Returns the memory usage in bytes at this point 100 | * 101 | * @return string 102 | */ 103 | public function getMemoryUsageNow() 104 | { 105 | return $this->_formatSize( 106 | max($this->getMemoryUsage() - $this->_memoryUsageStart, 0) 107 | ); 108 | } 109 | 110 | /** 111 | * Returns the memory usage in bits 112 | * 113 | * @return integer 114 | */ 115 | public function getMemoryUsage() 116 | { 117 | return memory_get_usage(); 118 | } 119 | 120 | /** 121 | * Formats bits into bytes 122 | * 123 | * @param string $size 124 | * @return string 125 | */ 126 | protected function _formatSize($size) 127 | { 128 | $i = floor(log($size, 1024)); 129 | return $size 130 | ? round($size / pow(1024, $i), 2) . ' ' . $this->_memoryUnits[$i] 131 | : '0 ' . $this->_memoryUnits[0]; 132 | } 133 | 134 | /** 135 | * Initialize a Zend style logger 136 | */ 137 | public function initLog() 138 | { 139 | // Output to shell 140 | $writer = new Zend_Log_Writer_Stream('php://output'); 141 | $writer->setFormatter(new Local_Shell_Formatter); 142 | $this->log = new Zend_Log($writer); 143 | 144 | // Log to file 145 | if (!$this->logFile) { 146 | $bits = explode('/', $GLOBALS['argv'][0]); 147 | $bits = explode('.', $bits[count($bits) - 1]); 148 | $this->logFile = 'shell_local_' . $bits[0] . '.log'; 149 | } 150 | $logDir = Mage::getBaseDir('var') . DS . 'log'; 151 | $logFile = $logDir . DS . $this->logFile; 152 | if (!is_dir($logDir)) { 153 | mkdir($logDir); 154 | chmod($logDir, 0777); 155 | } 156 | if (!file_exists($logFile)) { 157 | file_put_contents($logFile, ''); 158 | chmod($logFile, 0777); 159 | } 160 | $writer = new Zend_Log_Writer_Stream($logFile); 161 | $format = '%timestamp% %priorityName% (%priority%): %message%' . PHP_EOL; 162 | $formatter = new Zend_Log_Formatter_Simple($format); 163 | $writer->setFormatter($formatter); 164 | $this->log->addWriter($writer); 165 | } 166 | 167 | /** 168 | * Create a new Zend style progress bar 169 | * 170 | * Example usage: 171 | * $count = 10; 172 | * $bar = $this->progressBar($count); 173 | * for ($i = 1; $i <= $count; $i++) $bar->update($i); 174 | * $bar->finish(); 175 | * 176 | * @param integer $batches 177 | * @param integer $start 178 | * @return Zend_ProgressBar 179 | */ 180 | public function progressBar($batches, $start = 0) 181 | { 182 | return new Zend_ProgressBar( 183 | new Zend_ProgressBar_Adapter_Console( 184 | array( 185 | 'elements' => array( 186 | Zend_ProgressBar_Adapter_Console::ELEMENT_PERCENT, 187 | Zend_ProgressBar_Adapter_Console::ELEMENT_BAR, 188 | Zend_ProgressBar_Adapter_Console::ELEMENT_ETA, 189 | Zend_ProgressBar_Adapter_Console::ELEMENT_TEXT 190 | ) 191 | ) 192 | ), 193 | $start, 194 | $batches 195 | ); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/shell/local/sample.php: -------------------------------------------------------------------------------- 1 | log->debug('These are'); usleep(300000); 28 | $this->log->info('the different'); usleep(300000); 29 | $this->log->notice('log levels'); usleep(300000); 30 | $this->log->warn('you can'); usleep(300000); 31 | $this->log->err('use. Now'); usleep(300000); 32 | $this->log->crit('look at'); usleep(300000); 33 | $this->log->alert('this progress'); usleep(300000); 34 | $this->log->emerg('bar.'); usleep(300000); 35 | $count = 10; 36 | $bar = $this->progressBar($count); 37 | for ($i = 1; $i <= $count; $i++) { 38 | usleep(mt_rand(200000, 1000000)); 39 | $bar->update($i, $this->_messages[$i]); 40 | } 41 | $bar->finish(); 42 | } 43 | } 44 | 45 | $shell = new Local_Shell_SampleScript(); 46 | $shell->run(); 47 | --------------------------------------------------------------------------------