Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
57.14% covered (warning)
57.14%
16 / 28
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
AbstractSchemaValidator
57.14% covered (warning)
57.14%
16 / 28
0.00% covered (danger)
0.00%
0 / 2
17.87
0.00% covered (danger)
0.00%
0 / 1
 __construct
25.00% covered (danger)
25.00%
2 / 8
0.00% covered (danger)
0.00%
0 / 1
6.80
 validate
70.00% covered (warning)
70.00%
14 / 20
0.00% covered (danger)
0.00%
0 / 1
8.32
1<?php
2
3/**
4 * @license GPL-2.0-or-later
5 * @file
6 */
7
8namespace MediaWiki\DB;
9
10use JsonSchema\Validator;
11use Seld\JsonLint\DuplicateKeyException;
12use Seld\JsonLint\JsonParser;
13use Seld\JsonLint\ParsingException;
14use function class_exists;
15use function file_get_contents;
16use function is_array;
17use function is_object;
18
19/**
20 * Validate abstract schema json files against their JSON schema.
21 *
22 * This is used for static validation from the command-line via
23 * generateSchemaSql.php, generateSchemaChangeSql, and the PHPUnit structure test suite
24 * (AbstractSchemaTest).
25 *
26 * The files are normally read by the generateSchemaSql.php and generateSchemaSqlChange.php maintenance scripts.
27 *
28 * @since 1.38
29 */
30class AbstractSchemaValidator {
31    /** @throws AbstractSchemaValidationError */
32    public function __construct() {
33        if ( !class_exists( Validator::class ) ) {
34            throw new AbstractSchemaValidationError(
35                'The JsonSchema library cannot be found, please install it through composer.'
36            );
37        }
38
39        if ( !class_exists( JsonParser::class ) ) {
40            throw new AbstractSchemaValidationError(
41                'The JSON lint library cannot be found, please install it through composer.'
42            );
43        }
44    }
45
46    /**
47     * @param string $path file to validate
48     * @return bool true if passes validation
49     * @throws AbstractSchemaValidationError on any failure
50     */
51    public function validate( string $path ): bool {
52        $contents = file_get_contents( $path );
53        $jsonParser = new JsonParser();
54        try {
55            $data = $jsonParser->parse( $contents, JsonParser::DETECT_KEY_CONFLICTS );
56        } catch ( DuplicateKeyException $e ) {
57            throw new AbstractSchemaValidationError( $e->getMessage(), $e->getCode(), $e );
58        } catch ( ParsingException $e ) {
59            throw new AbstractSchemaValidationError( "$path is not valid JSON", $e->getCode(), $e );
60        }
61
62        // Regular schema's are arrays, schema changes are objects.
63        if ( is_array( $data ) ) {
64            $schemaPath = __DIR__ . '/../../docs/abstract-schema.schema.json';
65        } elseif ( is_object( $data ) ) {
66            $schemaPath = __DIR__ . '/../../docs/abstract-schema-changes.schema.json';
67        } else {
68            throw new AbstractSchemaValidationError( "$path is not a supported JSON object" );
69        }
70
71        $validator = new Validator;
72        $validator->check( $data, (object)[ '$ref' => 'file://' . $schemaPath ] );
73        if ( $validator->isValid() ) {
74            // All good.
75            return true;
76        }
77
78        $out = "$path did not pass validation.\n";
79        foreach ( $validator->getErrors() as $error ) {
80            $out .= "[{$error['property']}{$error['message']}\n";
81        }
82        throw new AbstractSchemaValidationError( $out );
83    }
84}