Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
71.43% covered (warning)
71.43%
15 / 21
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
AbstractSchemaValidator
71.43% covered (warning)
71.43%
15 / 21
50.00% covered (danger)
50.00%
1 / 2
13.82
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 checkDependencies
n/a
0 / 0
n/a
0 / 0
3
 validate
70.00% covered (warning)
70.00%
14 / 20
0.00% covered (danger)
0.00%
0 / 1
8.32
1<?php
2
3/**
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 * http://www.gnu.org/copyleft/gpl.html
18 *
19 * @file
20 */
21
22namespace MediaWiki\DB;
23
24use JsonSchema\Validator;
25use Seld\JsonLint\DuplicateKeyException;
26use Seld\JsonLint\JsonParser;
27use Seld\JsonLint\ParsingException;
28use function class_exists;
29use function file_get_contents;
30use function is_array;
31use function is_object;
32
33/**
34 * Validate abstract schema json files against their JSON schema.
35 *
36 * This is used for static validation from the command-line via
37 * generateSchemaSql.php, generateSchemaChangeSql, and the PHPUnit structure test suite
38 * (AbstractSchemaValidationTest).
39 *
40 * The files are normally read by the generateSchemaSql.php and generateSchemaSqlChange.php maintenance scripts.
41 *
42 * @since 1.38
43 */
44class AbstractSchemaValidator {
45    /**
46     * @var callable(string):void
47     */
48    private $missingDepCallback;
49
50    /**
51     * @param callable(string):void $missingDepCallback
52     */
53    public function __construct( callable $missingDepCallback ) {
54        $this->missingDepCallback = $missingDepCallback;
55    }
56
57    /**
58     * @codeCoverageIgnore
59     * @return bool
60     */
61    public function checkDependencies(): bool {
62        if ( !class_exists( Validator::class ) ) {
63            ( $this->missingDepCallback )(
64                'The JsonSchema library cannot be found, please install it through composer.'
65            );
66            return false;
67        }
68
69        if ( !class_exists( JsonParser::class ) ) {
70            ( $this->missingDepCallback )(
71                'The JSON lint library cannot be found, please install it through composer.'
72            );
73            return false;
74        }
75
76        return true;
77    }
78
79    /**
80     * @param string $path file to validate
81     * @return bool true if passes validation
82     * @throws AbstractSchemaValidationError on any failure
83     */
84    public function validate( string $path ): bool {
85        $contents = file_get_contents( $path );
86        $jsonParser = new JsonParser();
87        try {
88            $data = $jsonParser->parse( $contents, JsonParser::DETECT_KEY_CONFLICTS );
89        } catch ( DuplicateKeyException $e ) {
90            throw new AbstractSchemaValidationError( $e->getMessage(), $e->getCode(), $e );
91        } catch ( ParsingException $e ) {
92            throw new AbstractSchemaValidationError( "$path is not valid JSON", $e->getCode(), $e );
93        }
94
95        // Regular schema's are arrays, schema changes are objects.
96        if ( is_array( $data ) ) {
97            $schemaPath = __DIR__ . '/../../docs/abstract-schema.schema.json';
98        } elseif ( is_object( $data ) ) {
99            $schemaPath = __DIR__ . '/../../docs/abstract-schema-changes.schema.json';
100        } else {
101            throw new AbstractSchemaValidationError( "$path is not a supported JSON object" );
102        }
103
104        $validator = new Validator;
105        $validator->check( $data, (object)[ '$ref' => 'file://' . $schemaPath ] );
106        if ( $validator->isValid() ) {
107            // All good.
108            return true;
109        }
110
111        $out = "$path did not pass validation.\n";
112        foreach ( $validator->getErrors() as $error ) {
113            $out .= "[{$error['property']}{$error['message']}\n";
114        }
115        throw new AbstractSchemaValidationError( $out );
116    }
117}