Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 219
0.00% covered (danger)
0.00%
0 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
ScriptUtils
0.00% covered (danger)
0.00%
0 / 219
0.00% covered (danger)
0.00%
0 / 10
1260
0.00% covered (danger)
0.00%
0 / 1
 fetchFlagsMap
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 getScriptName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 traceUsageHelp
0.00% covered (danger)
0.00%
0 / 36
0.00% covered (danger)
0.00%
0 / 1
2
 dumpUsageHelp
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 1
2
 debugUsageHelp
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
2
 setDebuggingFlags
0.00% covered (danger)
0.00%
0 / 35
0.00% covered (danger)
0.00%
0 / 1
132
 setTemplatingAndProcessingFlags
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 1
110
 booleanOption
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
12
 setColorFlags
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 addStandardOptions
0.00% covered (danger)
0.00%
0 / 64
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2declare( strict_types = 1 );
3/**
4 * This file contains general utilities for scripts in
5 * the bin/, tools/, tests/ directories. This file should
6 * not contain any helpers that are needed by code in the
7 * lib/ directory.
8 */
9
10namespace Wikimedia\Parsoid\Utils;
11
12class ScriptUtils {
13    /**
14     * Split a tracing / debugging flag string into individual flags
15     * and return them as an associative array with flags as keys and true as value.
16     *
17     * @param string $origFlag The original flag string.
18     * @return array
19     */
20    private static function fetchFlagsMap( string $origFlag ): array {
21        $objFlags = explode( ',', $origFlag );
22        if ( in_array( 'selser', $objFlags, true ) && !in_array( 'wts', $objFlags, true ) ) {
23            $objFlags[] = 'wts';
24        }
25        return array_fill_keys( $objFlags, true );
26    }
27
28    /**
29     * @return-taint none
30     */
31    private static function getScriptName(): string {
32        return basename( $_SERVER["SCRIPT_FILENAME"] );
33    }
34
35    /**
36     * Returns a help message for the tracing flags.
37     *
38     * @return string
39     */
40    public static function traceUsageHelp(): string {
41        $script = self::getScriptName();
42        return implode(
43            "\n", [
44                'Tracing',
45                '-------',
46                '- With one or more comma-separated flags, traces those specific phases',
47                '- Supported flags:',
48                '  * grammar   : shows rules processed by grammar',
49                '  * peg       : shows tokens emitted by tokenizer',
50                '  * thp:2     : shows tokens flowing through stage 2 of the parsing pipeline',
51                '  * thp:3     : shows tokens flowing through stage 3 of the parsing pipeline',
52                '  * tsp       : shows tokens flowing through the TokenStreamPatcher '
53                    . '(useful to see in-order token stream)',
54                '  * list      : shows actions of the list handler',
55                '  * sanitizer : shows actions of the sanitizer',
56                '  * pre       : shows actions of the pre handler',
57                '  * p-wrap    : shows actions of the paragraph wrapper',
58                '  * html      : shows tokens that are sent to the HTML tree builder',
59                '  * remex     : shows RemexHtml\'s tree mutation events',
60                '  * dsr       : shows dsr computation on the DOM',
61                '  * tplwrap   : traces template wrapping code (currently only range overlap/nest/merge code)',
62                '  * wts       : trace actions of the regular wikitext serializer',
63                '  * selser    : trace actions of the selective serializer',
64                '  * domdiff   : trace actions of the DOM diffing code',
65                '  * wt-escape : debug wikitext-escaping',
66                '  * apirequest: trace all API requests',
67                '  * time      : trace times for various phases',
68                '',
69                '--debug enables tracing of all the above phases except Token Transform Managers',
70                '',
71                'Examples:',
72                "$ php $script --trace pre,p-wrap,html < foo",
73                "$ php $script --trace thp:3,dsr < foo",
74                ''
75            ]
76        );
77    }
78
79    /**
80     * Returns a help message for the dump flags.
81     *
82     * @return string
83     */
84    public static function dumpUsageHelp(): string {
85        $script = self::getScriptName();
86        return implode(
87            "\n", [
88                'Dumping state',
89                '-------------',
90                '- Dumps state at different points of execution',
91                '- DOM dumps are always doc.outerHTML',
92                '- Supported flags:',
93                '',
94                '  * tplsrc            : dumps preprocessed template source that will be tokenized '
95                    . '(via ?action=expandtemplates)',
96                '  * extoutput         : dumps HTML output form extensions (via ?action=parse)',
97                '',
98                '  --- Dump flags for wt2html DOM passes ---',
99                '  * dom:pre-XXX       : dumps DOM before pass XXX runs',
100                '  * dom:pre-*         : dumps DOM before every pass',
101                '  * dom:post-XXX      : dumps DOM after pass XXX runs',
102                '  * dom:post-*        : dumps DOM after every pass',
103                '',
104                '    Available passes (in the order they run):',
105                '',
106                '      fostered, process-fixups, Normalize, pwrap, ',
107                '      media, migrate-metas, migrate-nls, dsr, tplwrap, ',
108                '      dom-unpack, pp:EXT (replace EXT with extension: Pre, Gallery, etc)',
109                '      fixups, strip-metas, lang-converter, redlinks, ',
110                '      displayspace, linkclasses, sections, convertoffsets',
111                '      i18n, cleanup',
112                '',
113                '  --- Dump flags for html2wt ---',
114                '  * dom:post-dom-diff : in selective serialization, dumps DOM after running dom diff',
115                '  * dom:post-normal   : in serialization, dumps DOM after normalization',
116                "  * wt2html:limits    : dumps used resources (along with configured limits)\n",
117                "--debug dumps state at these different stages\n",
118                'Examples:',
119                "$ php $script --dump dom:pre-dsr,dom:pre-tplwrap < foo",
120                "$ php $script --trace html --dump dom:pre-tplwrap < foo",
121                "\n"
122            ]
123        );
124    }
125
126    /**
127     * Returns a help message for the debug flags.
128     *
129     * @return string
130     */
131    public static function debugUsageHelp(): string {
132        return implode(
133            "\n", [
134                'Debugging',
135                '---------',
136                '- With one or more comma-separated flags, ' .
137                    'provides more verbose tracing than the equivalent trace flag',
138                '- Supported flags:',
139                '  * pre       : shows actions of the pre handler',
140                '  * wts       : trace actions of the regular wikitext serializer',
141                '  * selser    : trace actions of the selective serializer'
142            ]
143        );
144    }
145
146    /**
147     * Set debugging flags on an object, based on an options object.
148     *
149     * @param array &$envOptions Options to be passed to the Env constructor.
150     * @param array $cliOpts The options object to use for setting the debug flags.
151     * @return array The modified object.
152     */
153    public static function setDebuggingFlags( array &$envOptions, array $cliOpts ): array {
154        $traceOpt = $cliOpts['trace'] ?? null;
155        $dumpOpt  = $cliOpts['dump'] ?? null;
156        $debugOpt = $cliOpts['debug'] ?? null;
157
158        // Handle the --help options
159        $exit = false;
160        if ( $traceOpt === 'help' ) {
161            print self::traceUsageHelp();
162            $exit = true;
163        }
164        if ( $dumpOpt === 'help' ) {
165            print self::dumpUsageHelp();
166            $exit = true;
167        }
168        if ( $debugOpt === 'help' ) {
169            print self::debugUsageHelp();
170            $exit = true;
171        }
172        if ( $exit ) {
173            die( 1 );
174        }
175
176        // Ok, no help requested: process the options.
177        if ( $debugOpt !== null ) {
178            // Continue to support generic debugging.
179            if ( $debugOpt === true ) {
180                error_log( 'Warning: Generic debugging, not handler-specific.' );
181                $envOptions['debug'] = self::booleanOption( $debugOpt );
182            } else {
183                // Setting --debug automatically enables --trace
184                $envOptions['debugFlags'] = self::fetchFlagsMap( $debugOpt );
185                $envOptions['traceFlags'] = $envOptions['debugFlags'];
186            }
187        }
188
189        if ( $traceOpt !== null ) {
190            if ( $traceOpt === true ) {
191                error_log(
192                    "Warning: Generic tracing is no longer supported. "
193                    . "Ignoring --trace flag. "
194                    . "Please provide handler-specific tracing flags, "
195                    . "e.g. '--trace pre,html5', to turn it on." );
196            } else {
197                // Add any new trace flags to the list of existing trace flags (if
198                // any were inherited from debug); otherwise, create a new list.
199                $envOptions['traceFlags'] = array_merge( $envOptions['traceFlags'] ?? [],
200                    self::fetchFlagsMap( $traceOpt ) );
201            }
202        }
203
204        if ( $dumpOpt !== null ) {
205            if ( $dumpOpt === true ) {
206                error_log( 'Warning: Generic dumping not enabled. Please set a flag.' );
207            } else {
208                $envOptions['dumpFlags'] = self::fetchFlagsMap( $dumpOpt );
209            }
210        }
211
212        return $envOptions;
213    }
214
215    /**
216     * Sets templating and processing flags on an object,
217     * based on an options object.
218     *
219     * @param array &$envOptions Options to be passed to the Env constructor.
220     * @param array $cliOpts The options object to use for setting the debug flags.
221     * @return array The modified object.
222     */
223    public static function setTemplatingAndProcessingFlags(
224        array &$envOptions, array $cliOpts
225    ): array {
226        $templateFlags = [
227            'fetchConfig',
228            'fetchTemplates',
229            'fetchImageInfo',
230            'expandExtensions',
231            'addHTMLTemplateParameters'
232        ];
233
234        foreach ( $templateFlags as $c ) {
235            if ( isset( $cliOpts[$c] ) ) {
236                $envOptions[$c] = self::booleanOption( $cliOpts[$c] );
237            }
238        }
239
240        if ( isset( $cliOpts['usePHPPreProcessor'] ) ) {
241            $envOptions['usePHPPreProcessor'] = $envOptions['fetchTemplates'] &&
242                self::booleanOption( $cliOpts['usePHPPreProcessor'] );
243        }
244
245        if ( isset( $cliOpts['maxDepth'] ) ) {
246            $envOptions['maxDepth'] =
247                is_numeric( $cliOpts['maxdepth'] ) ?
248                    $cliOpts['maxdepth'] : $envOptions['maxDepth'];
249        }
250
251        if ( isset( $cliOpts['apiURL'] ) ) {
252            $envOptions['mwApis'] ??= [];
253            $envOptions['mwApis'][] = [ 'prefix' => 'customwiki', 'uri' => $cliOpts['apiURL'] ];
254        }
255
256        if ( isset( $cliOpts['addHTMLTemplateParameters'] ) ) {
257            $envOptions['addHTMLTemplateParameters'] =
258                self::booleanOption( $cliOpts['addHTMLTemplateParameters'] );
259        }
260
261        if ( isset( $cliOpts['lint'] ) ) {
262            $envOptions['linting'] = true;
263        }
264
265        return $envOptions;
266    }
267
268    /**
269     * Parse a boolean option returned by our opts processor.
270     * The strings 'false' and 'no' are also treated as false values.
271     * This allows `--debug=no` and `--debug=false` to mean the same as
272     * `--no-debug`.
273     *
274     * @param bool|string $val
275     *   a boolean, or a string naming a boolean value.
276     * @return bool
277     */
278    public static function booleanOption( $val ): bool {
279        return $val && $val !== 'no' && $val !== 'false';
280    }
281
282    /**
283     * Set the color flags, based on an options object.
284     *
285     * @param array $options options object to use for setting the mode of the 'color' package.
286     *  - string|boolean options.color
287     *    Whether to use color.
288     *    Passing 'auto' will enable color only if stdout is a TTY device.
289     * @suppress PhanEmptyPublicMethod
290     */
291    public static function setColorFlags( array $options ): void {
292        /**
293         * PORT-FIXME:
294         * if ( $options->color === 'auto' ) {
295         * if ( !$process->stdout->isTTY ) {
296         * $colors->mode = 'none';
297         * }
298         * } elseif ( !self::booleanOption( $options->color ) ) {
299         * $colors->mode = 'none';
300         * }
301         */
302    }
303
304    /**
305     * PORT-FIXME: Should some of this functionality be moved to OptsProcessor directly?
306     *
307     * Add standard options to script-specific opts
308     * This handles options parsed by `setDebuggingFlags`,
309     * `setTemplatingAndProcessingFlags`, `setColorFlags`,
310     * and standard --help options.
311     *
312     * The `defaults` option is optional, and lets you override
313     * the defaults for the standard options.
314     *
315     * @param array $opts
316     * @param array $defaults
317     * @return array
318     */
319    public static function addStandardOptions( array $opts, array $defaults = [] ): array {
320        $standardOpts = [
321            // standard CLI options
322            'help' => [
323                'description' => 'Show this help message',
324                'boolean' => true,
325                'default' => false,
326                'alias' => 'h'
327            ],
328            // handled by `setDebuggingFlags`
329            'debug' => [
330                'description' => 'Provide optional flags. Use --debug=help for supported options'
331            ],
332            'trace' => [
333                'description' => 'Use --trace=help for supported options'
334            ],
335            'dump' => [
336                'description' => 'Dump state. Use --dump=help for supported options'
337            ],
338            // handled by `setTemplatingAndProcessingFlags`
339            'fetchConfig' => [
340                'description' => 'Whether to fetch the wiki config from the server or use our local copy',
341                'boolean' => true,
342                'default' => true
343            ],
344            'fetchTemplates' => [
345                'description' => 'Whether to fetch included templates recursively',
346                'boolean' => true,
347                'default' => true
348            ],
349            'fetchImageInfo' => [
350                'description' => 'Whether to fetch image info via the API',
351                'boolean' => true,
352                'default' => true
353            ],
354            'expandExtensions' => [
355                'description' => 'Whether we should request extension tag expansions from a wiki',
356                'boolean' => true,
357                'default' => true
358            ],
359            'usePHPPreProcessor' => [
360                'description' => 'Whether to use the PHP preprocessor to expand templates',
361                'boolean' => true,
362                'default' => true
363            ],
364            'addHTMLTemplateParameters' => [
365                'description' => 'Parse template parameters to HTML and add them to template data',
366                'boolean' => true,
367                'default' => false
368            ],
369            'maxdepth' => [
370                'description' => 'Maximum expansion depth',
371                'default' => 40
372            ],
373            'apiURL' => [
374                'description' => 'http path to remote API, e.g. http://en.wikipedia.org/w/api.php',
375                'default' => null
376            ],
377            // handled by `setColorFlags`
378            'color' => [
379                'description' => 'Enable color output Ex: --no-color',
380                'default' => 'auto'
381            ]
382        ];
383
384        // allow overriding defaults
385        foreach ( $defaults as $name => $default ) {
386            if ( isset( $standardOpts[$name] ) ) {
387                $standardOpts[$name]['default'] = $default;
388            }
389        }
390
391        // Values in $opts take precedence
392        return $opts + $standardOpts;
393    }
394}