Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
JsonSchemaReferenceResolver
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 4
56
0.00% covered (danger)
0.00%
0 / 1
 normalizeRef
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 unpackRef
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 getDefinitionName
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 resolveRef
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace MediaWiki\Settings\Source;
4
5use Error;
6use InvalidArgumentException;
7
8/**
9 * Utility for resolving references ($ref) in JSON-schemas
10 * and building a collection of the referenced definitions.
11 *
12 * @since 1.42
13 * @unstable
14 */
15class JsonSchemaReferenceResolver {
16    /**
17     * Returns a URI relative to a JSON-schema document
18     * for a given definition name
19     *
20     * @param array $schema A $ref sub-schema
21     * @return string Definition relative URI
22     */
23    public static function normalizeRef( array $schema ): string {
24        return '#/$defs/' . self::getDefinitionName( $schema );
25    }
26
27    /**
28     * Destructures and validates a JSON-schema "$ref" definition of the
29     * form [ "class" => SomePHPClass:class, "field" => "someExistingPublicConsInTheClass" ]
30     *
31     * @param array $schema A $ref sub-schema
32     * @return array An array with the class name as the first element, and the field name as the second.
33     */
34    private static function unpackRef( array $schema ): array {
35        $className = $schema['class'];
36        $field = $schema['field'];
37        if ( !$className || !$field ) {
38            throw new InvalidArgumentException( 'The schema $ref must have "class" and "field" defined.' );
39        }
40        return [
41            $className,
42            $field
43        ];
44    }
45
46    /**
47     * Builds a definition name based on the specified "$ref" definition
48     *
49     * @param array $schema A $ref sub-schema
50     * @return string A definition name using both the name of the class and the field
51     */
52    public static function getDefinitionName( array $schema ): string {
53        [ $className, $field ] = self::unpackRef( $schema );
54        // Avoid using "\" or "/" in the definition name as they are interpreted as fragment
55        // separators in the URI and make consumers of the schema unable to resolve a document
56        // relative schema. Use "." as the word separator for the namespace, resulting in
57        // eg: SomeExtension.Namespace.MyClass::MyConstant
58        return str_replace( "\\", '.', $className ) . '::' . $field;
59    }
60
61    /**
62     * Returns the value of a given reference "$ref".
63     *
64     * @param array $schema A $ref sub-schema
65     * @return array The value of the class field referenced by the definition
66     */
67    public static function resolveRef( array $schema, string $rootClass ): array {
68        [ $className, $fieldName ] = self::unpackRef( $schema );
69        try {
70            $value = constant( "$className::$fieldName" );
71        } catch ( Error $e ) {
72            throw new RefNotFoundException(
73                "Failed resolving reference $fieldName in $className. Root schema location: $rootClass"
74            );
75        }
76        return $value;
77    }
78
79}