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