summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/Gadgets/includes/content/GadgetDefinitionValidator.php
blob: 2be757834870a2b2b95279eaa711484fb689d8ae (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
<?php

/**
 * Class responsible for validating Gadget definition contents
 *
 * @todo maybe this should use a formal JSON schema validator or something
 */
class GadgetDefinitionValidator {
	/**
	 * Validation metadata.
	 * 'foo.bar.baz' => [ 'type check callback',
	 *   'type name' [, 'member type check callback', 'member type name'] ]
	 */
	protected static $propertyValidation = [
		'settings' => [ 'is_array', 'array' ],
		'settings.rights' => [ 'is_array', 'array' , 'is_string', 'string' ],
		'settings.default' => [ 'is_bool', 'boolean' ],
		'settings.hidden' => [ 'is_bool', 'boolean' ],
		'settings.skins' => [ [ __CLASS__, 'isArrayOrTrue' ], 'array or true', 'is_string', 'string' ],
		'settings.category' => [ 'is_string', 'string' ],
		'module' => [ 'is_array', 'array' ],
		'module.scripts' => [ 'is_array', 'array', 'is_string', 'string' ],
		'module.styles' => [ 'is_array', 'array', 'is_string', 'string' ],
		'module.dependencies' => [ 'is_array', 'array', 'is_string', 'string' ],
		'module.peers' => [ 'is_array', 'array', 'is_string', 'string' ],
		'module.messages' => [ 'is_array', 'array', 'is_string', 'string' ],
		'module.type' => [ 'is_string', 'string' ],
	];

	/**
	 * @param mixed $value
	 * @return bool
	 */
	public static function isArrayOrTrue( $value ) {
		return is_array( $value ) || $value === true;
	}

	/**
	 * Check the validity of the given properties array
	 * @param array $properties Return value of FormatJson::decode( $blob, true )
	 * @param bool $tolerateMissing If true, don't complain about missing keys
	 * @return Status object with error message if applicable
	 */
	public function validate( array $properties, $tolerateMissing = false ) {
		foreach ( self::$propertyValidation as $property => $validation ) {
			$path = explode( '.', $property );
			$val = $properties;

			// Walk down and verify that the path from the root to this property exists
			foreach ( $path as $p ) {
				if ( !array_key_exists( $p, $val ) ) {
					if ( $tolerateMissing ) {
						// Skip validation of this property altogether
						continue 2;
					} else {
						return Status::newFatal( 'gadgets-validate-notset', $property );
					}
				}
				$val = $val[$p];
			}

			// Do the actual validation of this property
			$func = $validation[0];
			if ( !call_user_func( $func, $val ) ) {
				return Status::newFatal(
						'gadgets-validate-wrongtype',
						$property,
						$validation[1],
						gettype( $val )
				);
			}

			if ( isset( $validation[2] ) && is_array( $val ) ) {
				// Descend into the array and check the type of each element
				$func = $validation[2];
				foreach ( $val as $i => $v ) {
					if ( !call_user_func( $func, $v ) ) {
						return Status::newFatal(
								'gadgets-validate-wrongtype',
								"{$property}[{$i}]",
								$validation[3],
								gettype( $v )
						);
					}
				}
			}
		}

		return Status::newGood();
	}
}