Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
GadgetDefinitionValidator
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 3
306
0.00% covered (danger)
0.00%
0 / 1
 isValidTitleSuffix
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
12
 isValidType
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
12
 validate
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 1
132
1<?php
2
3namespace MediaWiki\Extension\Gadgets\Content;
4
5use MediaWiki\Status\Status;
6
7/**
8 * Class responsible for validating Gadget definition contents
9 *
10 * @todo maybe this should use a formal JSON schema validator or something
11 */
12class GadgetDefinitionValidator {
13    /**
14     * @var array Validation metadata.
15     * 'foo.bar.baz' => [ 'type check callback',
16     *   'type name' [, 'member type check callback', 'member type name'] ]
17     */
18    protected static $propertyValidation = [
19        'settings' => [ 'is_array', 'array' ],
20        'settings.actions' => [ 'is_array', 'array', 'is_string', 'string' ],
21        'settings.categories' => [ 'is_array', 'array', 'is_string', 'string' ],
22        'settings.category' => [ 'is_string', 'string' ],
23        'settings.contentModels' => [ 'is_array', 'array', 'is_string', 'string' ],
24        'settings.default' => [ 'is_bool', 'boolean' ],
25        'settings.hidden' => [ 'is_bool', 'boolean' ],
26        'settings.namespaces' => [ 'is_array', 'array', 'is_int', 'integer' ],
27        'settings.package' => [ 'is_bool', 'boolean' ],
28        'settings.requiresES6' => [ 'is_bool', 'boolean' ],
29        'settings.rights' => [ 'is_array', 'array', 'is_string', 'string' ],
30        'settings.skins' => [ 'is_array', 'array', 'is_string', 'string' ],
31        'settings.supportsUrlLoad' => [ 'is_bool', 'boolean' ],
32
33        'module' => [ 'is_array', 'array' ],
34        'module.dependencies' => [ 'is_array', 'array', 'is_string', 'string' ],
35        'module.messages' => [ 'is_array', 'array', 'is_string', 'string' ],
36        'module.pages' => [ 'is_array', 'array', [ __CLASS__, 'isValidTitleSuffix' ], '.js, .css or .json page' ],
37        'module.peers' => [ 'is_array', 'array', 'is_string', 'string' ],
38        'module.type' => [ [ __CLASS__, 'isValidType' ], 'general or styles' ],
39    ];
40
41    public static function isValidTitleSuffix( string $title ): bool {
42        return str_ends_with( $title, '.js' ) || str_ends_with( $title, '.css' ) || str_ends_with( $title, '.json' );
43    }
44
45    public static function isValidType( string $type ): bool {
46        return $type === '' || $type === 'general' || $type === 'styles';
47    }
48
49    /**
50     * Check the validity of the given properties array
51     * @param array $properties Return value of FormatJson::decode( $blob, true )
52     * @param bool $tolerateMissing If true, don't complain about missing keys
53     * @return Status object with error message if applicable
54     */
55    public function validate( array $properties, $tolerateMissing = false ) {
56        foreach ( self::$propertyValidation as $property => $validation ) {
57            $path = explode( '.', $property );
58            $val = $properties;
59
60            // Walk down and verify that the path from the root to this property exists
61            foreach ( $path as $p ) {
62                if ( !array_key_exists( $p, $val ) ) {
63                    if ( $tolerateMissing ) {
64                        // Skip validation of this property altogether
65                        continue 2;
66                    }
67
68                    return Status::newFatal( 'gadgets-validate-notset', $property );
69                }
70                $val = $val[$p];
71            }
72
73            // Do the actual validation of this property
74            $func = $validation[0];
75            if ( !call_user_func( $func, $val ) ) {
76                return Status::newFatal(
77                    'gadgets-validate-wrongtype',
78                    $property,
79                    $validation[1],
80                    gettype( $val )
81                );
82            }
83
84            if ( isset( $validation[2] ) && isset( $validation[3] ) && is_array( $val ) ) {
85                // Descend into the array and check the type of each element
86                $func = $validation[2];
87                foreach ( $val as $i => $v ) {
88                    if ( !call_user_func( $func, $v ) ) {
89                        return Status::newFatal(
90                            'gadgets-validate-wrongtype',
91                            "{$property}[{$i}]",
92                            $validation[3],
93                            gettype( $v )
94                        );
95                    }
96                }
97            }
98        }
99
100        return Status::newGood();
101    }
102}