]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeConstraint.php
MediaWiki 1.30.2
[autoinstalls/mediawiki.git] / vendor / justinrainbow / json-schema / src / JsonSchema / Constraints / TypeConstraint.php
1 <?php
2
3 /*
4  * This file is part of the JsonSchema package.
5  *
6  * For the full copyright and license information, please view the LICENSE
7  * file that was distributed with this source code.
8  */
9
10 namespace JsonSchema\Constraints;
11
12 use JsonSchema\Entity\JsonPointer;
13 use JsonSchema\Exception\InvalidArgumentException;
14 use UnexpectedValueException as StandardUnexpectedValueException;
15
16 /**
17  * The TypeConstraint Constraints, validates an element against a given type
18  *
19  * @author Robert Schönthal <seroscho@googlemail.com>
20  * @author Bruno Prieto Reis <bruno.p.reis@gmail.com>
21  */
22 class TypeConstraint extends Constraint
23 {
24     /**
25      * @var array|string[] type wordings for validation error messages
26      */
27     public static $wording = array(
28         'integer' => 'an integer',
29         'number'  => 'a number',
30         'boolean' => 'a boolean',
31         'object'  => 'an object',
32         'array'   => 'an array',
33         'string'  => 'a string',
34         'null'    => 'a null',
35         'any'     => null, // validation of 'any' is always true so is not needed in message wording
36         0         => null, // validation of a false-y value is always true, so not needed as well
37     );
38
39     /**
40      * {@inheritdoc}
41      */
42     public function check(&$value = null, $schema = null, JsonPointer $path = null, $i = null)
43     {
44         $type = isset($schema->type) ? $schema->type : null;
45         $isValid = false;
46         $wording = array();
47
48         if (is_array($type)) {
49             $this->validateTypesArray($value, $type, $wording, $isValid, $path);
50         } elseif (is_object($type)) {
51             $this->checkUndefined($value, $type, $path);
52
53             return;
54         } else {
55             $isValid = $this->validateType($value, $type);
56         }
57
58         if ($isValid === false) {
59             if (!is_array($type)) {
60                 $this->validateTypeNameWording($type);
61                 $wording[] = self::$wording[$type];
62             }
63             $this->addError($path, ucwords(gettype($value)) . ' value found, but ' .
64                 $this->implodeWith($wording, ', ', 'or') . ' is required', 'type');
65         }
66     }
67
68     /**
69      * Validates the given $value against the array of types in $type. Sets the value
70      * of $isValid to true, if at least one $type mateches the type of $value or the value
71      * passed as $isValid is already true.
72      *
73      * @param mixed $value             Value to validate
74      * @param array $type              TypeConstraints to check agains
75      * @param array $validTypesWording An array of wordings of the valid types of the array $type
76      * @param bool  $isValid           The current validation value
77      * @param $path
78      */
79     protected function validateTypesArray(&$value, array $type, &$validTypesWording, &$isValid, $path)
80     {
81         foreach ($type as $tp) {
82             // $tp can be an object, if it's a schema instead of a simple type, validate it
83             // with a new type constraint
84             if (is_object($tp)) {
85                 if (!$isValid) {
86                     $validator = $this->factory->createInstanceFor('type');
87                     $subSchema = new \stdClass();
88                     $subSchema->type = $tp;
89                     $validator->check($value, $subSchema, $path, null);
90                     $error = $validator->getErrors();
91                     $isValid = !(bool) $error;
92                     $validTypesWording[] = self::$wording['object'];
93                 }
94             } else {
95                 $this->validateTypeNameWording($tp);
96                 $validTypesWording[] = self::$wording[$tp];
97                 if (!$isValid) {
98                     $isValid = $this->validateType($value, $tp);
99                 }
100             }
101         }
102     }
103
104     /**
105      * Implodes the given array like implode() with turned around parameters and with the
106      * difference, that, if $listEnd isn't false, the last element delimiter is $listEnd instead of
107      * $delimiter.
108      *
109      * @param array  $elements  The elements to implode
110      * @param string $delimiter The delimiter to use
111      * @param bool   $listEnd   The last delimiter to use (defaults to $delimiter)
112      *
113      * @return string
114      */
115     protected function implodeWith(array $elements, $delimiter = ', ', $listEnd = false)
116     {
117         if ($listEnd === false || !isset($elements[1])) {
118             return implode($delimiter, $elements);
119         }
120         $lastElement  = array_slice($elements, -1);
121         $firsElements = join($delimiter, array_slice($elements, 0, -1));
122         $implodedElements = array_merge(array($firsElements), $lastElement);
123
124         return join(" $listEnd ", $implodedElements);
125     }
126
127     /**
128      * Validates the given $type, if there's an associated self::$wording. If not, throws an
129      * exception.
130      *
131      * @param string $type The type to validate
132      *
133      * @throws StandardUnexpectedValueException
134      */
135     protected function validateTypeNameWording($type)
136     {
137         if (!isset(self::$wording[$type])) {
138             throw new StandardUnexpectedValueException(
139                 sprintf(
140                     'No wording for %s available, expected wordings are: [%s]',
141                     var_export($type, true),
142                     implode(', ', array_filter(self::$wording)))
143             );
144         }
145     }
146
147     /**
148      * Verifies that a given value is of a certain type
149      *
150      * @param mixed  $value Value to validate
151      * @param string $type  TypeConstraint to check against
152      *
153      * @throws InvalidArgumentException
154      *
155      * @return bool
156      */
157     protected function validateType(&$value, $type)
158     {
159         //mostly the case for inline schema
160         if (!$type) {
161             return true;
162         }
163
164         if ('any' === $type) {
165             return true;
166         }
167
168         if ('object' === $type) {
169             return $this->getTypeCheck()->isObject($value);
170         }
171
172         if ('array' === $type) {
173             return $this->getTypeCheck()->isArray($value);
174         }
175
176         $coerce = $this->factory->getConfig(Constraint::CHECK_MODE_COERCE_TYPES);
177
178         if ('integer' === $type) {
179             if ($coerce) {
180                 $value = $this->toInteger($value);
181             }
182
183             return is_int($value);
184         }
185
186         if ('number' === $type) {
187             if ($coerce) {
188                 $value = $this->toNumber($value);
189             }
190
191             return is_numeric($value) && !is_string($value);
192         }
193
194         if ('boolean' === $type) {
195             if ($coerce) {
196                 $value = $this->toBoolean($value);
197             }
198
199             return is_bool($value);
200         }
201
202         if ('string' === $type) {
203             return is_string($value);
204         }
205
206         if ('email' === $type) {
207             return is_string($value);
208         }
209
210         if ('null' === $type) {
211             return is_null($value);
212         }
213
214         throw new InvalidArgumentException((is_object($value) ? 'object' : $value) . ' is an invalid type for ' . $type);
215     }
216
217     /**
218      * Converts a value to boolean. For example, "true" becomes true.
219      *
220      * @param $value The value to convert to boolean
221      *
222      * @return bool|mixed
223      */
224     protected function toBoolean($value)
225     {
226         if ($value === 'true') {
227             return true;
228         }
229
230         if ($value === 'false') {
231             return false;
232         }
233
234         return $value;
235     }
236
237     /**
238      * Converts a numeric string to a number. For example, "4" becomes 4.
239      *
240      * @param mixed $value the value to convert to a number
241      *
242      * @return int|float|mixed
243      */
244     protected function toNumber($value)
245     {
246         if (is_numeric($value)) {
247             return $value + 0; // cast to number
248         }
249
250         return $value;
251     }
252
253     protected function toInteger($value)
254     {
255         if (is_numeric($value) && (int) $value == $value) {
256             return (int) $value; // cast to number
257         }
258
259         return $value;
260     }
261 }