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