1495aedab9e57b8f8243c38dee0ca07cc8fa2d15
[moodle.git] / lib / clilib.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * Command line utility functions and classes
20  *
21  * @package    core
22  * @subpackage cli
23  * @copyright  2009 Petr Skoda (http://skodak.org)
24  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25  */
27 // NOTE: no MOODLE_INTERNAL test here, sometimes we use this before requiring Moodle libs!
29 /**
30  * Write a text to the given stream
31  *
32  * @param string $text text to be written
33  * @param resource $stream output stream to be written to, defaults to STDOUT
34  */
35 function cli_write($text, $stream=STDOUT) {
36     fwrite($stream, $text);
37 }
39 /**
40  * Write a text followed by an end of line symbol to the given stream
41  *
42  * @param string $text text to be written
43  * @param resource $stream output stream to be written to, defaults to STDOUT
44  */
45 function cli_writeln($text, $stream=STDOUT) {
46     cli_write($text.PHP_EOL, $stream);
47 }
49 /**
50  * Get input from user
51  * @param string $prompt text prompt, should include possible options
52  * @param string $default default value when enter pressed
53  * @param array $options list of allowed options, empty means any text
54  * @param bool $casesensitive true if options are case sensitive
55  * @return string entered text
56  */
57 function cli_input($prompt, $default='', array $options=null, $casesensitiveoptions=false) {
58     cli_writeln($prompt);
59     cli_write(': ');
60     $input = fread(STDIN, 2048);
61     $input = trim($input);
62     if ($input === '') {
63         $input = $default;
64     }
65     if ($options) {
66         if (!$casesensitiveoptions) {
67             $input = strtolower($input);
68         }
69         if (!in_array($input, $options)) {
70             cli_writeln(get_string('cliincorrectvalueretry', 'admin'));
71             return cli_input($prompt, $default, $options, $casesensitiveoptions);
72         }
73     }
74     return $input;
75 }
77 /**
78  * Returns cli script parameters.
79  * @param array $longoptions array of --style options ex:('verbose'=>false)
80  * @param array $shortmapping array describing mapping of short to long style options ex:('h'=>'help', 'v'=>'verbose')
81  * @return array array of arrays, options, unrecognised as optionlongname=>value
82  */
83 function cli_get_params(array $longoptions, array $shortmapping=null) {
84     $shortmapping = (array)$shortmapping;
85     $options      = array();
86     $unrecognized = array();
88     if (empty($_SERVER['argv'])) {
89         // bad luck, we can continue in interactive mode ;-)
90         return array($options, $unrecognized);
91     }
92     $rawoptions = $_SERVER['argv'];
94     //remove anything after '--', options can not be there
95     if (($key = array_search('--', $rawoptions)) !== false) {
96         $rawoptions = array_slice($rawoptions, 0, $key);
97     }
99     //remove script
100     unset($rawoptions[0]);
101     foreach ($rawoptions as $raw) {
102         if (substr($raw, 0, 2) === '--') {
103             $value = substr($raw, 2);
104             $parts = explode('=', $value);
105             if (count($parts) == 1) {
106                 $key   = reset($parts);
107                 $value = true;
108             } else {
109                 $key = array_shift($parts);
110                 $value = implode('=', $parts);
111             }
112             if (array_key_exists($key, $longoptions)) {
113                 $options[$key] = $value;
114             } else {
115                 $unrecognized[] = $raw;
116             }
118         } else if (substr($raw, 0, 1) === '-') {
119             $value = substr($raw, 1);
120             $parts = explode('=', $value);
121             if (count($parts) == 1) {
122                 $key   = reset($parts);
123                 $value = true;
124             } else {
125                 $key = array_shift($parts);
126                 $value = implode('=', $parts);
127             }
128             if (array_key_exists($key, $shortmapping)) {
129                 $options[$shortmapping[$key]] = $value;
130             } else {
131                 $unrecognized[] = $raw;
132             }
133         } else {
134             $unrecognized[] = $raw;
135             continue;
136         }
137     }
138     //apply defaults
139     foreach ($longoptions as $key=>$default) {
140         if (!array_key_exists($key, $options)) {
141             $options[$key] = $default;
142         }
143     }
144     // finished
145     return array($options, $unrecognized);
148 /**
149  * This sets the cli process title suffix
150  *
151  * An example is appending current Task API info so a sysadmin can immediately
152  * see what task a cron process is running at any given moment.
153  *
154  * @param string $suffix process suffix
155  */
156 function cli_set_process_title_suffix(string $suffix) {
157     if (defined('CLI_SCRIPT') && isset($_SERVER['argv'])) {
158         $command = join(' ', $_SERVER['argv']);
159         @cli_set_process_title("php $command ($suffix)");
160     }
163 /**
164  * Print or return section separator string
165  * @param bool $return false means print, true return as string
166  * @return mixed void or string
167  */
168 function cli_separator($return=false) {
169     $separator = str_repeat('-', 79).PHP_EOL;
170     if ($return) {
171         return $separator;
172     } else {
173         cli_write($separator);
174     }
177 /**
178  * Print or return section heading string
179  * @param string $string text
180  * @param bool $return false means print, true return as string
181  * @return mixed void or string
182  */
183 function cli_heading($string, $return=false) {
184     $string = "== $string ==".PHP_EOL;
185     if ($return) {
186         return $string;
187     } else {
188         cli_write($string);
189     }
192 /**
193  * Write error notification
194  * @param $text
195  * @return void
196  */
197 function cli_problem($text) {
198     cli_writeln($text, STDERR);
201 /**
202  * Write to standard error output and exit with the given code
203  *
204  * @param string $text
205  * @param int $errorcode
206  * @return void (does not return)
207  */
208 function cli_error($text, $errorcode=1) {
209     cli_writeln($text.PHP_EOL, STDERR);
210     die($errorcode);
213 /**
214  * Print an ASCII version of the Moodle logo.
215  *
216  * @param int $padding left padding of the logo
217  * @param bool $return should we print directly (false) or return the string (true)
218  * @return mixed void or string
219  */
220 function cli_logo($padding=2, $return=false) {
222     $lines = array(
223         '                               .-..-.       ',
224         ' _____                         | || |       ',
225         '/____/-.---_  .---.  .---.  .-.| || | .---. ',
226         '| |  _   _  |/  _  \\/  _  \\/  _  || |/  __ \\',
227         '* | | | | | || |_| || |_| || |_| || || |___/',
228         '  |_| |_| |_|\\_____/\\_____/\\_____||_|\\_____)',
229     );
231     $logo = '';
233     foreach ($lines as $line) {
234         $logo .= str_repeat(' ', $padding);
235         $logo .= $line;
236         $logo .= PHP_EOL;
237     }
239     if ($return) {
240         return $logo;
241     } else {
242         cli_write($logo);
243     }
246 /**
247  * Substitute cursor, colour, and bell placeholders in a CLI output to ANSI escape characters when ANSI is available.
248  *
249  * @param string $message
250  * @return string
251  */
252 function cli_ansi_format(string $message): string {
253     global $CFG;
255     $replacements = [
256         "<newline>" => "\n",
257         "<bell>" => "\007",
259         // Cursor movement: https://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x361.html.
260         "<cursor:save>"     => "\033[s",
261         "<cursor:restore>"  => "\033[u",
262         "<cursor:up>"       => "\033[1A",
263         "<cursor:down>"     => "\033[1B",
264         "<cursor:forward>"  => "\033[1C",
265         "<cursor:back>"     => "\033[1D",
266     ];
268     $colours = [
269         'normal'        => '0;0',
270         'black'         => '0;30',
271         'darkGray'      => '1;30',
272         'red'           => '0;31',
273         'lightRed'      => '1;31',
274         'green'         => '0;32',
275         'lightGreen'    => '1;32',
276         'brown'         => '0;33',
277         'yellow'        => '1;33',
278         'lightYellow'   => '0;93',
279         'blue'          => '0;34',
280         'lightBlue'     => '1;34',
281         'purple'        => '0;35',
282         'lightPurple'   => '1;35',
283         'cyan'          => '0;36',
284         'lightCyan'     => '1;36',
285         'lightGray'     => '0;37',
286         'white'         => '1;37',
287     ];
288     $bgcolours = [
289         'black'         => '40',
290         'red'           => '41',
291         'green'         => '42',
292         'yellow'        => '43',
293         'blue'          => '44',
294         'magenta'       => '45',
295         'cyan'          => '46',
296         'white'         => '47',
297     ];
299     foreach ($colours as $colour => $code) {
300         $replacements["<colour:{$colour}>"] = "\033[{$code}m";
301     }
302     foreach ($bgcolours as $colour => $code) {
303         $replacements["<bgcolour:{$colour}>"] = "\033[{$code}m";
304     }
306     // Windows don't support ANSI code by default, but does if ANSICON is available.
307     $isansicon = getenv('ANSICON');
308     if (($CFG->ostype === 'WINDOWS') && empty($isansicon)) {
309         return str_replace(array_keys($replacements), '', $message);
310     }
312     return str_replace(array_keys($replacements), array_values($replacements), $message);