Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 222
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 / 222
0.00% covered (danger)
0.00%
0 / 10
1332
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 / 13
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 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 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
12use Wikimedia\Parsoid\ParserTests\TestUtils;
13
14class ScriptUtils {
15    /**
16     * Split a tracing / debugging flag string into individual flags
17     * and return them as an associative array with flags as keys and true as value.
18     *
19     * @param string $origFlag The original flag string.
20     *
21     * @return array<string, true>
22     */
23    private static function fetchFlagsMap( string $origFlag ): array {
24        $objFlags = explode( ',', $origFlag );
25        if ( in_array( 'selser', $objFlags, true ) && !in_array( 'wts', $objFlags, true ) ) {
26            $objFlags[] = 'wts';
27        }
28        return array_fill_keys( $objFlags, true );
29    }
30
31    /**
32     * @return-taint none
33     */
34    private static function getScriptName(): string {
35        return basename( $_SERVER["SCRIPT_FILENAME"] );
36    }
37
38    /**
39     * Returns a help message for the tracing flags.
40     *
41     * @return string
42     */
43    public static function traceUsageHelp(): string {
44        $script = self::getScriptName();
45        return implode(
46            "\n", [
47                'Tracing',
48                '-------',
49                '- With one or more comma-separated flags, traces those specific phases',
50                '- Supported flags:',
51                '  * grammar   : shows rules processed by grammar',
52                '  * peg       : shows tokens emitted by tokenizer',
53                '  * thp:2     : shows tokens flowing through stage 2 of the parsing pipeline',
54                '  * thp:3     : shows tokens flowing through stage 3 of the parsing pipeline',
55                '  * tsp       : shows tokens flowing through the TokenStreamPatcher '
56                    . '(useful to see in-order token stream)',
57                '  * list      : shows actions of the list handler',
58                '  * sanitizer : shows actions of the sanitizer',
59                '  * pre       : shows actions of the pre handler',
60                '  * p-wrap    : shows actions of the paragraph wrapper',
61                '  * html      : shows tokens that are sent to the HTML tree builder',
62                '  * remex     : shows RemexHtml\'s tree mutation events',
63                '  * dsr       : shows dsr computation on the DOM',
64                '  * tplwrap   : traces template wrapping code (currently only range overlap/nest/merge code)',
65                '  * wts       : trace actions of the regular wikitext serializer',
66                '  * selser    : trace actions of the selective serializer',
67                '  * domdiff   : trace actions of the DOM diffing code',
68                '  * wt-escape : debug wikitext-escaping',
69                '  * apirequest: trace all API requests',
70                '  * time      : trace times for various phases',
71                '',
72                '--debug enables tracing of all the above phases except Token Transform Managers',
73                '',
74                'Examples:',
75                "$ php $script --trace pre,p-wrap,html < foo",
76                "$ php $script --trace thp:3,dsr < foo",
77                ''
78            ]
79        );
80    }
81
82    /**
83     * Returns a help message for the dump flags.
84     *
85     * @return string
86     */
87    public static function dumpUsageHelp(): string {
88        $script = self::getScriptName();
89        return implode(
90            "\n", [
91                'Dumping state',
92                '-------------',
93                '- Dumps state at different points of execution',
94                '- DOM dumps are always doc.outerHTML',
95                '- Supported flags:',
96                '',
97                '  * tplsrc            : dumps preprocessed template source that will be tokenized '
98                    . '(via ?action=expandtemplates)',
99                '  * extoutput         : dumps HTML output form extensions (via ?action=parse)',
100                '',
101                '  --- Dump flags for wt2html DOM passes ---',
102                '  * dom:pre-XXX       : dumps DOM before pass XXX runs',
103                '  * dom:pre-*         : dumps DOM before every pass',
104                '  * dom:post-XXX      : dumps DOM after pass XXX runs',
105                '  * dom:post-*        : dumps DOM after every pass',
106                '',
107                '    Available passes (in the order they run):',
108                '',
109                '      fostered, process-fixups, Normalize, pwrap, ',
110                '      media, migrate-metas, migrate-nls, dsr, tplwrap, ',
111                '      dom-unpack, pp:EXT (replace EXT with extension: Pre, Gallery, etc)',
112                '      fixups, strip-metas, lang-converter, redlinks, ',
113                '      displayspace, linkclasses, sections, convertoffsets',
114                '      i18n, cleanup',
115                '',
116                '  --- Dump flags for html2wt ---',
117                '  * dom:post-dom-diff : in selective serialization, dumps DOM after running dom diff',
118                '  * dom:post-normal   : in serialization, dumps DOM after normalization',
119                "  * wt2html:limits    : dumps used resources (along with configured limits)\n",
120                "--debug dumps state at these different stages\n",
121                'Examples:',
122                "$ php $script --dump dom:pre-dsr,dom:pre-tplwrap < foo",
123                "$ php $script --trace html --dump dom:pre-tplwrap < foo",
124                "\n"
125            ]
126        );
127    }
128
129    /**
130     * Returns a help message for the debug flags.
131     *
132     * @return string
133     */
134    public static function debugUsageHelp(): string {
135        return implode(
136            "\n", [
137                'Debugging',
138                '---------',
139                '- With one or more comma-separated flags, ' .
140                    'provides more verbose tracing than the equivalent trace flag',
141                '- Supported flags:',
142                '  * pre       : shows actions of the pre handler',
143                '  * wts       : trace actions of the regular wikitext serializer',
144                '  * selser    : trace actions of the selective serializer',
145                ''
146            ]
147        );
148    }
149
150    /**
151     * Set debugging flags on an object, based on an options object.
152     *
153     * @param array &$envOptions Options to be passed to the Env constructor.
154     * @param array $cliOpts The options object to use for setting the debug flags.
155     * @return array The modified object.
156     */
157    public static function setDebuggingFlags( array &$envOptions, array $cliOpts ): array {
158        $traceOpt = $cliOpts['trace'] ?? null;
159        $dumpOpt  = $cliOpts['dump'] ?? null;
160        $debugOpt = $cliOpts['debug'] ?? null;
161
162        // Handle the --help options
163        $exit = false;
164        if ( $traceOpt === 'help' ) {
165            print self::traceUsageHelp();
166            $exit = true;
167        }
168        if ( $dumpOpt === 'help' ) {
169            print self::dumpUsageHelp();
170            $exit = true;
171        }
172        if ( $debugOpt === 'help' ) {
173            print self::debugUsageHelp();
174            $exit = true;
175        }
176        if ( $exit ) {
177            die( 1 );
178        }
179
180        // Ok, no help requested: process the options.
181        if ( $debugOpt !== null ) {
182            // Continue to support generic debugging.
183            if ( $debugOpt === true ) {
184                error_log( 'Warning: Generic debugging, not handler-specific.' );
185                $envOptions['debug'] = self::booleanOption( $debugOpt );
186            } else {
187                // Setting --debug automatically enables --trace
188                $envOptions['debugFlags'] = self::fetchFlagsMap( $debugOpt );
189                $envOptions['traceFlags'] = $envOptions['debugFlags'];
190            }
191        }
192
193        if ( $traceOpt !== null ) {
194            if ( $traceOpt === true ) {
195                error_log(
196                    "Warning: Generic tracing is no longer supported. "
197                    . "Ignoring --trace flag. "
198                    . "Please provide handler-specific tracing flags, "
199                    . "e.g. '--trace pre,html5', to turn it on." );
200            } else {
201                // Add any new trace flags to the list of existing trace flags (if
202                // any were inherited from debug); otherwise, create a new list.
203                $envOptions['traceFlags'] = array_merge( $envOptions['traceFlags'] ?? [],
204                    self::fetchFlagsMap( $traceOpt ) );
205            }
206        }
207
208        if ( $dumpOpt !== null ) {
209            if ( $dumpOpt === true ) {
210                error_log( 'Warning: Generic dumping not enabled. Please set a flag.' );
211            } else {
212                $envOptions['dumpFlags'] = self::fetchFlagsMap( $dumpOpt );
213            }
214        }
215
216        return $envOptions;
217    }
218
219    /**
220     * Sets templating and processing flags on an object,
221     * based on an options object.
222     *
223     * @param array &$envOptions Options to be passed to the Env constructor.
224     * @param array $cliOpts The options object to use for setting the debug flags.
225     * @return array The modified object.
226     */
227    public static function setTemplatingAndProcessingFlags(
228        array &$envOptions, array $cliOpts
229    ): array {
230        $templateFlags = [
231            'fetchConfig',
232            'fetchTemplates',
233            'fetchImageInfo',
234            'expandExtensions',
235            'addHTMLTemplateParameters'
236        ];
237
238        foreach ( $templateFlags as $c ) {
239            if ( isset( $cliOpts[$c] ) ) {
240                $envOptions[$c] = self::booleanOption( $cliOpts[$c] );
241            }
242        }
243
244        if ( isset( $cliOpts['usePHPPreProcessor'] ) ) {
245            $envOptions['usePHPPreProcessor'] = $envOptions['fetchTemplates'] &&
246                self::booleanOption( $cliOpts['usePHPPreProcessor'] );
247        }
248
249        if ( isset( $cliOpts['maxDepth'] ) ) {
250            $envOptions['maxDepth'] =
251                is_numeric( $cliOpts['maxdepth'] ) ?
252                    $cliOpts['maxdepth'] : $envOptions['maxDepth'];
253        }
254
255        if ( isset( $cliOpts['apiURL'] ) ) {
256            $envOptions['mwApis'] ??= [];
257            $envOptions['mwApis'][] = [ 'prefix' => 'customwiki', 'uri' => $cliOpts['apiURL'] ];
258        }
259
260        if ( isset( $cliOpts['addHTMLTemplateParameters'] ) ) {
261            $envOptions['addHTMLTemplateParameters'] =
262                self::booleanOption( $cliOpts['addHTMLTemplateParameters'] );
263        }
264
265        if ( isset( $cliOpts['lint'] ) ) {
266            $envOptions['linting'] = true;
267        }
268
269        return $envOptions;
270    }
271
272    /**
273     * Parse a boolean option returned by our opts processor.
274     * The strings 'false' and 'no' are also treated as false values.
275     * This allows `--debug=no` and `--debug=false` to mean the same as
276     * `--no-debug`.
277     *
278     * @param bool|string $val
279     *   a boolean, or a string naming a boolean value.
280     * @return bool
281     */
282    public static function booleanOption( $val ): bool {
283        return $val && $val !== 'no' && $val !== 'false';
284    }
285
286    /**
287     * Set the color flags, based on an options object.
288     *
289     * @param array $options options object to use for setting the mode of the 'color' package.
290     *  - string|boolean options.color
291     *    Whether to use color.
292     *    Passing 'auto' will enable color only if stdout is a TTY device.
293     * @suppress PhanAccessPropertyInternal
294     */
295    public static function setColorFlags( array $options ): void {
296        if ( ( $options['color'] ?? 'auto' ) === 'auto' ) {
297            TestUtils::$colorMode = 'auto';
298        } else {
299            TestUtils::$colorMode = self::booleanOption( $options['color'] );
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. Auto-enabled by default for a TTY device.',
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}