Completed
Push — develop ( 316159...00443b )
by Zack
20:22
created
vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/Constraint.php 1 patch
Indentation   +196 added lines, -196 removed lines patch added patch discarded remove patch
@@ -19,200 +19,200 @@
 block discarded – undo
19 19
  */
20 20
 abstract class Constraint extends BaseConstraint implements ConstraintInterface
21 21
 {
22
-    protected $inlineSchemaProperty = '$schema';
23
-
24
-    const CHECK_MODE_NONE =             0x00000000;
25
-    const CHECK_MODE_NORMAL =           0x00000001;
26
-    const CHECK_MODE_TYPE_CAST =        0x00000002;
27
-    const CHECK_MODE_COERCE_TYPES =     0x00000004;
28
-    const CHECK_MODE_APPLY_DEFAULTS =   0x00000008;
29
-    const CHECK_MODE_EXCEPTIONS =       0x00000010;
30
-    const CHECK_MODE_DISABLE_FORMAT =   0x00000020;
31
-    const CHECK_MODE_ONLY_REQUIRED_DEFAULTS   = 0x00000080;
32
-    const CHECK_MODE_VALIDATE_SCHEMA =  0x00000100;
33
-
34
-    /**
35
-     * Bubble down the path
36
-     *
37
-     * @param JsonPointer|null $path Current path
38
-     * @param mixed            $i    What to append to the path
39
-     *
40
-     * @return JsonPointer;
41
-     */
42
-    protected function incrementPath(JsonPointer $path = null, $i)
43
-    {
44
-        $path = $path ?: new JsonPointer('');
45
-
46
-        if ($i === null || $i === '') {
47
-            return $path;
48
-        }
49
-
50
-        $path = $path->withPropertyPaths(
51
-            array_merge(
52
-                $path->getPropertyPaths(),
53
-                array($i)
54
-            )
55
-        );
56
-
57
-        return $path;
58
-    }
59
-
60
-    /**
61
-     * Validates an array
62
-     *
63
-     * @param mixed            $value
64
-     * @param mixed            $schema
65
-     * @param JsonPointer|null $path
66
-     * @param mixed            $i
67
-     */
68
-    protected function checkArray(&$value, $schema = null, JsonPointer $path = null, $i = null)
69
-    {
70
-        $validator = $this->factory->createInstanceFor('collection');
71
-        $validator->check($value, $schema, $path, $i);
72
-
73
-        $this->addErrors($validator->getErrors());
74
-    }
75
-
76
-    /**
77
-     * Validates an object
78
-     *
79
-     * @param mixed            $value
80
-     * @param mixed            $schema
81
-     * @param JsonPointer|null $path
82
-     * @param mixed            $properties
83
-     * @param mixed            $additionalProperties
84
-     * @param mixed            $patternProperties
85
-     */
86
-    protected function checkObject(&$value, $schema = null, JsonPointer $path = null, $properties = null,
87
-        $additionalProperties = null, $patternProperties = null, $appliedDefaults = array())
88
-    {
89
-        $validator = $this->factory->createInstanceFor('object');
90
-        $validator->check($value, $schema, $path, $properties, $additionalProperties, $patternProperties, $appliedDefaults);
91
-
92
-        $this->addErrors($validator->getErrors());
93
-    }
94
-
95
-    /**
96
-     * Validates the type of a property
97
-     *
98
-     * @param mixed            $value
99
-     * @param mixed            $schema
100
-     * @param JsonPointer|null $path
101
-     * @param mixed            $i
102
-     */
103
-    protected function checkType(&$value, $schema = null, JsonPointer $path = null, $i = null)
104
-    {
105
-        $validator = $this->factory->createInstanceFor('type');
106
-        $validator->check($value, $schema, $path, $i);
107
-
108
-        $this->addErrors($validator->getErrors());
109
-    }
110
-
111
-    /**
112
-     * Checks a undefined element
113
-     *
114
-     * @param mixed            $value
115
-     * @param mixed            $schema
116
-     * @param JsonPointer|null $path
117
-     * @param mixed            $i
118
-     */
119
-    protected function checkUndefined(&$value, $schema = null, JsonPointer $path = null, $i = null, $fromDefault = false)
120
-    {
121
-        $validator = $this->factory->createInstanceFor('undefined');
122
-
123
-        $validator->check($value, $this->factory->getSchemaStorage()->resolveRefSchema($schema), $path, $i, $fromDefault);
124
-
125
-        $this->addErrors($validator->getErrors());
126
-    }
127
-
128
-    /**
129
-     * Checks a string element
130
-     *
131
-     * @param mixed            $value
132
-     * @param mixed            $schema
133
-     * @param JsonPointer|null $path
134
-     * @param mixed            $i
135
-     */
136
-    protected function checkString($value, $schema = null, JsonPointer $path = null, $i = null)
137
-    {
138
-        $validator = $this->factory->createInstanceFor('string');
139
-        $validator->check($value, $schema, $path, $i);
140
-
141
-        $this->addErrors($validator->getErrors());
142
-    }
143
-
144
-    /**
145
-     * Checks a number element
146
-     *
147
-     * @param mixed       $value
148
-     * @param mixed       $schema
149
-     * @param JsonPointer $path
150
-     * @param mixed       $i
151
-     */
152
-    protected function checkNumber($value, $schema = null, JsonPointer $path = null, $i = null)
153
-    {
154
-        $validator = $this->factory->createInstanceFor('number');
155
-        $validator->check($value, $schema, $path, $i);
156
-
157
-        $this->addErrors($validator->getErrors());
158
-    }
159
-
160
-    /**
161
-     * Checks a enum element
162
-     *
163
-     * @param mixed            $value
164
-     * @param mixed            $schema
165
-     * @param JsonPointer|null $path
166
-     * @param mixed            $i
167
-     */
168
-    protected function checkEnum($value, $schema = null, JsonPointer $path = null, $i = null)
169
-    {
170
-        $validator = $this->factory->createInstanceFor('enum');
171
-        $validator->check($value, $schema, $path, $i);
172
-
173
-        $this->addErrors($validator->getErrors());
174
-    }
175
-
176
-    /**
177
-     * Checks format of an element
178
-     *
179
-     * @param mixed            $value
180
-     * @param mixed            $schema
181
-     * @param JsonPointer|null $path
182
-     * @param mixed            $i
183
-     */
184
-    protected function checkFormat($value, $schema = null, JsonPointer $path = null, $i = null)
185
-    {
186
-        $validator = $this->factory->createInstanceFor('format');
187
-        $validator->check($value, $schema, $path, $i);
188
-
189
-        $this->addErrors($validator->getErrors());
190
-    }
191
-
192
-    /**
193
-     * Get the type check based on the set check mode.
194
-     *
195
-     * @return TypeCheck\TypeCheckInterface
196
-     */
197
-    protected function getTypeCheck()
198
-    {
199
-        return $this->factory->getTypeCheck();
200
-    }
201
-
202
-    /**
203
-     * @param JsonPointer $pointer
204
-     *
205
-     * @return string property path
206
-     */
207
-    protected function convertJsonPointerIntoPropertyPath(JsonPointer $pointer)
208
-    {
209
-        $result = array_map(
210
-            function ($path) {
211
-                return sprintf(is_numeric($path) ? '[%d]' : '.%s', $path);
212
-            },
213
-            $pointer->getPropertyPaths()
214
-        );
215
-
216
-        return trim(implode('', $result), '.');
217
-    }
22
+	protected $inlineSchemaProperty = '$schema';
23
+
24
+	const CHECK_MODE_NONE =             0x00000000;
25
+	const CHECK_MODE_NORMAL =           0x00000001;
26
+	const CHECK_MODE_TYPE_CAST =        0x00000002;
27
+	const CHECK_MODE_COERCE_TYPES =     0x00000004;
28
+	const CHECK_MODE_APPLY_DEFAULTS =   0x00000008;
29
+	const CHECK_MODE_EXCEPTIONS =       0x00000010;
30
+	const CHECK_MODE_DISABLE_FORMAT =   0x00000020;
31
+	const CHECK_MODE_ONLY_REQUIRED_DEFAULTS   = 0x00000080;
32
+	const CHECK_MODE_VALIDATE_SCHEMA =  0x00000100;
33
+
34
+	/**
35
+	 * Bubble down the path
36
+	 *
37
+	 * @param JsonPointer|null $path Current path
38
+	 * @param mixed            $i    What to append to the path
39
+	 *
40
+	 * @return JsonPointer;
41
+	 */
42
+	protected function incrementPath(JsonPointer $path = null, $i)
43
+	{
44
+		$path = $path ?: new JsonPointer('');
45
+
46
+		if ($i === null || $i === '') {
47
+			return $path;
48
+		}
49
+
50
+		$path = $path->withPropertyPaths(
51
+			array_merge(
52
+				$path->getPropertyPaths(),
53
+				array($i)
54
+			)
55
+		);
56
+
57
+		return $path;
58
+	}
59
+
60
+	/**
61
+	 * Validates an array
62
+	 *
63
+	 * @param mixed            $value
64
+	 * @param mixed            $schema
65
+	 * @param JsonPointer|null $path
66
+	 * @param mixed            $i
67
+	 */
68
+	protected function checkArray(&$value, $schema = null, JsonPointer $path = null, $i = null)
69
+	{
70
+		$validator = $this->factory->createInstanceFor('collection');
71
+		$validator->check($value, $schema, $path, $i);
72
+
73
+		$this->addErrors($validator->getErrors());
74
+	}
75
+
76
+	/**
77
+	 * Validates an object
78
+	 *
79
+	 * @param mixed            $value
80
+	 * @param mixed            $schema
81
+	 * @param JsonPointer|null $path
82
+	 * @param mixed            $properties
83
+	 * @param mixed            $additionalProperties
84
+	 * @param mixed            $patternProperties
85
+	 */
86
+	protected function checkObject(&$value, $schema = null, JsonPointer $path = null, $properties = null,
87
+		$additionalProperties = null, $patternProperties = null, $appliedDefaults = array())
88
+	{
89
+		$validator = $this->factory->createInstanceFor('object');
90
+		$validator->check($value, $schema, $path, $properties, $additionalProperties, $patternProperties, $appliedDefaults);
91
+
92
+		$this->addErrors($validator->getErrors());
93
+	}
94
+
95
+	/**
96
+	 * Validates the type of a property
97
+	 *
98
+	 * @param mixed            $value
99
+	 * @param mixed            $schema
100
+	 * @param JsonPointer|null $path
101
+	 * @param mixed            $i
102
+	 */
103
+	protected function checkType(&$value, $schema = null, JsonPointer $path = null, $i = null)
104
+	{
105
+		$validator = $this->factory->createInstanceFor('type');
106
+		$validator->check($value, $schema, $path, $i);
107
+
108
+		$this->addErrors($validator->getErrors());
109
+	}
110
+
111
+	/**
112
+	 * Checks a undefined element
113
+	 *
114
+	 * @param mixed            $value
115
+	 * @param mixed            $schema
116
+	 * @param JsonPointer|null $path
117
+	 * @param mixed            $i
118
+	 */
119
+	protected function checkUndefined(&$value, $schema = null, JsonPointer $path = null, $i = null, $fromDefault = false)
120
+	{
121
+		$validator = $this->factory->createInstanceFor('undefined');
122
+
123
+		$validator->check($value, $this->factory->getSchemaStorage()->resolveRefSchema($schema), $path, $i, $fromDefault);
124
+
125
+		$this->addErrors($validator->getErrors());
126
+	}
127
+
128
+	/**
129
+	 * Checks a string element
130
+	 *
131
+	 * @param mixed            $value
132
+	 * @param mixed            $schema
133
+	 * @param JsonPointer|null $path
134
+	 * @param mixed            $i
135
+	 */
136
+	protected function checkString($value, $schema = null, JsonPointer $path = null, $i = null)
137
+	{
138
+		$validator = $this->factory->createInstanceFor('string');
139
+		$validator->check($value, $schema, $path, $i);
140
+
141
+		$this->addErrors($validator->getErrors());
142
+	}
143
+
144
+	/**
145
+	 * Checks a number element
146
+	 *
147
+	 * @param mixed       $value
148
+	 * @param mixed       $schema
149
+	 * @param JsonPointer $path
150
+	 * @param mixed       $i
151
+	 */
152
+	protected function checkNumber($value, $schema = null, JsonPointer $path = null, $i = null)
153
+	{
154
+		$validator = $this->factory->createInstanceFor('number');
155
+		$validator->check($value, $schema, $path, $i);
156
+
157
+		$this->addErrors($validator->getErrors());
158
+	}
159
+
160
+	/**
161
+	 * Checks a enum element
162
+	 *
163
+	 * @param mixed            $value
164
+	 * @param mixed            $schema
165
+	 * @param JsonPointer|null $path
166
+	 * @param mixed            $i
167
+	 */
168
+	protected function checkEnum($value, $schema = null, JsonPointer $path = null, $i = null)
169
+	{
170
+		$validator = $this->factory->createInstanceFor('enum');
171
+		$validator->check($value, $schema, $path, $i);
172
+
173
+		$this->addErrors($validator->getErrors());
174
+	}
175
+
176
+	/**
177
+	 * Checks format of an element
178
+	 *
179
+	 * @param mixed            $value
180
+	 * @param mixed            $schema
181
+	 * @param JsonPointer|null $path
182
+	 * @param mixed            $i
183
+	 */
184
+	protected function checkFormat($value, $schema = null, JsonPointer $path = null, $i = null)
185
+	{
186
+		$validator = $this->factory->createInstanceFor('format');
187
+		$validator->check($value, $schema, $path, $i);
188
+
189
+		$this->addErrors($validator->getErrors());
190
+	}
191
+
192
+	/**
193
+	 * Get the type check based on the set check mode.
194
+	 *
195
+	 * @return TypeCheck\TypeCheckInterface
196
+	 */
197
+	protected function getTypeCheck()
198
+	{
199
+		return $this->factory->getTypeCheck();
200
+	}
201
+
202
+	/**
203
+	 * @param JsonPointer $pointer
204
+	 *
205
+	 * @return string property path
206
+	 */
207
+	protected function convertJsonPointerIntoPropertyPath(JsonPointer $pointer)
208
+	{
209
+		$result = array_map(
210
+			function ($path) {
211
+				return sprintf(is_numeric($path) ? '[%d]' : '.%s', $path);
212
+			},
213
+			$pointer->getPropertyPaths()
214
+		);
215
+
216
+		return trim(implode('', $result), '.');
217
+	}
218 218
 }
Please login to merge, or discard this patch.
justinrainbow/json-schema/src/JsonSchema/Constraints/EnumConstraint.php 1 patch
Indentation   +29 added lines, -29 removed lines patch added patch discarded remove patch
@@ -19,36 +19,36 @@
 block discarded – undo
19 19
  */
20 20
 class EnumConstraint extends Constraint
21 21
 {
22
-    /**
23
-     * {@inheritdoc}
24
-     */
25
-    public function check(&$element, $schema = null, JsonPointer $path = null, $i = null)
26
-    {
27
-        // Only validate enum if the attribute exists
28
-        if ($element instanceof UndefinedConstraint && (!isset($schema->required) || !$schema->required)) {
29
-            return;
30
-        }
31
-        $type = gettype($element);
22
+	/**
23
+	 * {@inheritdoc}
24
+	 */
25
+	public function check(&$element, $schema = null, JsonPointer $path = null, $i = null)
26
+	{
27
+		// Only validate enum if the attribute exists
28
+		if ($element instanceof UndefinedConstraint && (!isset($schema->required) || !$schema->required)) {
29
+			return;
30
+		}
31
+		$type = gettype($element);
32 32
 
33
-        foreach ($schema->enum as $enum) {
34
-            $enumType = gettype($enum);
35
-            if ($this->factory->getConfig(self::CHECK_MODE_TYPE_CAST) && $type == 'array' && $enumType == 'object') {
36
-                if ((object) $element == $enum) {
37
-                    return;
38
-                }
39
-            }
33
+		foreach ($schema->enum as $enum) {
34
+			$enumType = gettype($enum);
35
+			if ($this->factory->getConfig(self::CHECK_MODE_TYPE_CAST) && $type == 'array' && $enumType == 'object') {
36
+				if ((object) $element == $enum) {
37
+					return;
38
+				}
39
+			}
40 40
 
41
-            if ($type === gettype($enum)) {
42
-                if ($type == 'object') {
43
-                    if ($element == $enum) {
44
-                        return;
45
-                    }
46
-                } elseif ($element === $enum) {
47
-                    return;
48
-                }
49
-            }
50
-        }
41
+			if ($type === gettype($enum)) {
42
+				if ($type == 'object') {
43
+					if ($element == $enum) {
44
+						return;
45
+					}
46
+				} elseif ($element === $enum) {
47
+					return;
48
+				}
49
+			}
50
+		}
51 51
 
52
-        $this->addError($path, 'Does not have a value in the enumeration ' . json_encode($schema->enum), 'enum', array('enum' => $schema->enum));
53
-    }
52
+		$this->addError($path, 'Does not have a value in the enumeration ' . json_encode($schema->enum), 'enum', array('enum' => $schema->enum));
53
+	}
54 54
 }
Please login to merge, or discard this patch.
justinrainbow/json-schema/src/JsonSchema/Constraints/TypeConstraint.php 1 patch
Indentation   +237 added lines, -237 removed lines patch added patch discarded remove patch
@@ -21,241 +21,241 @@
 block discarded – undo
21 21
  */
22 22
 class TypeConstraint extends Constraint
23 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
-    }
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 261
 }
Please login to merge, or discard this patch.
json-schema/src/JsonSchema/Constraints/UndefinedConstraint.php 1 patch
Indentation   +394 added lines, -394 removed lines patch added patch discarded remove patch
@@ -22,398 +22,398 @@
 block discarded – undo
22 22
  */
23 23
 class UndefinedConstraint extends Constraint
24 24
 {
25
-    /**
26
-     * @var array List of properties to which a default value has been applied
27
-     */
28
-    protected $appliedDefaults = array();
29
-
30
-    /**
31
-     * {@inheritdoc}
32
-     */
33
-    public function check(&$value, $schema = null, JsonPointer $path = null, $i = null, $fromDefault = false)
34
-    {
35
-        if (is_null($schema) || !is_object($schema)) {
36
-            return;
37
-        }
38
-
39
-        $path = $this->incrementPath($path ?: new JsonPointer(''), $i);
40
-        if ($fromDefault) {
41
-            $path->setFromDefault();
42
-        }
43
-
44
-        // check special properties
45
-        $this->validateCommonProperties($value, $schema, $path, $i);
46
-
47
-        // check allOf, anyOf, and oneOf properties
48
-        $this->validateOfProperties($value, $schema, $path, '');
49
-
50
-        // check known types
51
-        $this->validateTypes($value, $schema, $path, $i);
52
-    }
53
-
54
-    /**
55
-     * Validates the value against the types
56
-     *
57
-     * @param mixed       $value
58
-     * @param mixed       $schema
59
-     * @param JsonPointer $path
60
-     * @param string      $i
61
-     */
62
-    public function validateTypes(&$value, $schema, JsonPointer $path, $i = null)
63
-    {
64
-        // check array
65
-        if ($this->getTypeCheck()->isArray($value)) {
66
-            $this->checkArray($value, $schema, $path, $i);
67
-        }
68
-
69
-        // check object
70
-        if (LooseTypeCheck::isObject($value)) { // object processing should always be run on assoc arrays,
71
-                                                // so use LooseTypeCheck here even if CHECK_MODE_TYPE_CAST
72
-                                                // is not set (i.e. don't use $this->getTypeCheck() here).
73
-            $this->checkObject(
74
-                $value,
75
-                $schema,
76
-                $path,
77
-                isset($schema->properties) ? $schema->properties : null,
78
-                isset($schema->additionalProperties) ? $schema->additionalProperties : null,
79
-                isset($schema->patternProperties) ? $schema->patternProperties : null,
80
-                $this->appliedDefaults
81
-            );
82
-        }
83
-
84
-        // check string
85
-        if (is_string($value)) {
86
-            $this->checkString($value, $schema, $path, $i);
87
-        }
88
-
89
-        // check numeric
90
-        if (is_numeric($value)) {
91
-            $this->checkNumber($value, $schema, $path, $i);
92
-        }
93
-
94
-        // check enum
95
-        if (isset($schema->enum)) {
96
-            $this->checkEnum($value, $schema, $path, $i);
97
-        }
98
-    }
99
-
100
-    /**
101
-     * Validates common properties
102
-     *
103
-     * @param mixed       $value
104
-     * @param mixed       $schema
105
-     * @param JsonPointer $path
106
-     * @param string      $i
107
-     */
108
-    protected function validateCommonProperties(&$value, $schema, JsonPointer $path, $i = '')
109
-    {
110
-        // if it extends another schema, it must pass that schema as well
111
-        if (isset($schema->extends)) {
112
-            if (is_string($schema->extends)) {
113
-                $schema->extends = $this->validateUri($schema, $schema->extends);
114
-            }
115
-            if (is_array($schema->extends)) {
116
-                foreach ($schema->extends as $extends) {
117
-                    $this->checkUndefined($value, $extends, $path, $i);
118
-                }
119
-            } else {
120
-                $this->checkUndefined($value, $schema->extends, $path, $i);
121
-            }
122
-        }
123
-
124
-        // Apply default values from schema
125
-        if (!$path->fromDefault()) {
126
-            $this->applyDefaultValues($value, $schema, $path);
127
-        }
128
-
129
-        // Verify required values
130
-        if ($this->getTypeCheck()->isObject($value)) {
131
-            if (!($value instanceof self) && isset($schema->required) && is_array($schema->required)) {
132
-                // Draft 4 - Required is an array of strings - e.g. "required": ["foo", ...]
133
-                foreach ($schema->required as $required) {
134
-                    if (!$this->getTypeCheck()->propertyExists($value, $required)) {
135
-                        $this->addError(
136
-                            $this->incrementPath($path ?: new JsonPointer(''), $required),
137
-                            'The property ' . $required . ' is required',
138
-                            'required'
139
-                        );
140
-                    }
141
-                }
142
-            } elseif (isset($schema->required) && !is_array($schema->required)) {
143
-                // Draft 3 - Required attribute - e.g. "foo": {"type": "string", "required": true}
144
-                if ($schema->required && $value instanceof self) {
145
-                    $propertyPaths = $path->getPropertyPaths();
146
-                    $propertyName = end($propertyPaths);
147
-                    $this->addError(
148
-                        $path,
149
-                        'The property ' . $propertyName . ' is required',
150
-                        'required'
151
-                    );
152
-                }
153
-            } else {
154
-                // if the value is both undefined and not required, skip remaining checks
155
-                // in this method which assume an actual, defined instance when validating.
156
-                if ($value instanceof self) {
157
-                    return;
158
-                }
159
-            }
160
-        }
161
-
162
-        // Verify type
163
-        if (!($value instanceof self)) {
164
-            $this->checkType($value, $schema, $path, $i);
165
-        }
166
-
167
-        // Verify disallowed items
168
-        if (isset($schema->disallow)) {
169
-            $initErrors = $this->getErrors();
170
-
171
-            $typeSchema = new \stdClass();
172
-            $typeSchema->type = $schema->disallow;
173
-            $this->checkType($value, $typeSchema, $path);
174
-
175
-            // if no new errors were raised it must be a disallowed value
176
-            if (count($this->getErrors()) == count($initErrors)) {
177
-                $this->addError($path, 'Disallowed value was matched', 'disallow');
178
-            } else {
179
-                $this->errors = $initErrors;
180
-            }
181
-        }
182
-
183
-        if (isset($schema->not)) {
184
-            $initErrors = $this->getErrors();
185
-            $this->checkUndefined($value, $schema->not, $path, $i);
186
-
187
-            // if no new errors were raised then the instance validated against the "not" schema
188
-            if (count($this->getErrors()) == count($initErrors)) {
189
-                $this->addError($path, 'Matched a schema which it should not', 'not');
190
-            } else {
191
-                $this->errors = $initErrors;
192
-            }
193
-        }
194
-
195
-        // Verify that dependencies are met
196
-        if (isset($schema->dependencies) && $this->getTypeCheck()->isObject($value)) {
197
-            $this->validateDependencies($value, $schema->dependencies, $path);
198
-        }
199
-    }
200
-
201
-    /**
202
-     * Check whether a default should be applied for this value
203
-     *
204
-     * @param mixed $schema
205
-     * @param mixed $parentSchema
206
-     * @param bool  $requiredOnly
207
-     *
208
-     * @return bool
209
-     */
210
-    private function shouldApplyDefaultValue($requiredOnly, $schema, $name = null, $parentSchema = null)
211
-    {
212
-        // required-only mode is off
213
-        if (!$requiredOnly) {
214
-            return true;
215
-        }
216
-        // draft-04 required is set
217
-        if (
218
-            $name !== null
219
-            && isset($parentSchema->required)
220
-            && is_array($parentSchema->required)
221
-            && in_array($name, $parentSchema->required)
222
-        ) {
223
-            return true;
224
-        }
225
-        // draft-03 required is set
226
-        if (isset($schema->required) && !is_array($schema->required) && $schema->required) {
227
-            return true;
228
-        }
229
-        // default case
230
-        return false;
231
-    }
232
-
233
-    /**
234
-     * Apply default values
235
-     *
236
-     * @param mixed       $value
237
-     * @param mixed       $schema
238
-     * @param JsonPointer $path
239
-     */
240
-    protected function applyDefaultValues(&$value, $schema, $path)
241
-    {
242
-        // only apply defaults if feature is enabled
243
-        if (!$this->factory->getConfig(self::CHECK_MODE_APPLY_DEFAULTS)) {
244
-            return;
245
-        }
246
-
247
-        // apply defaults if appropriate
248
-        $requiredOnly = $this->factory->getConfig(self::CHECK_MODE_ONLY_REQUIRED_DEFAULTS);
249
-        if (isset($schema->properties) && LooseTypeCheck::isObject($value)) {
250
-            // $value is an object or assoc array, and properties are defined - treat as an object
251
-            foreach ($schema->properties as $currentProperty => $propertyDefinition) {
252
-                $propertyDefinition = $this->factory->getSchemaStorage()->resolveRefSchema($propertyDefinition);
253
-                if (
254
-                    !LooseTypeCheck::propertyExists($value, $currentProperty)
255
-                    && property_exists($propertyDefinition, 'default')
256
-                    && $this->shouldApplyDefaultValue($requiredOnly, $propertyDefinition, $currentProperty, $schema)
257
-                ) {
258
-                    // assign default value
259
-                    if (is_object($propertyDefinition->default)) {
260
-                        LooseTypeCheck::propertySet($value, $currentProperty, clone $propertyDefinition->default);
261
-                    } else {
262
-                        LooseTypeCheck::propertySet($value, $currentProperty, $propertyDefinition->default);
263
-                    }
264
-                    $this->appliedDefaults[] = $currentProperty;
265
-                }
266
-            }
267
-        } elseif (isset($schema->items) && LooseTypeCheck::isArray($value)) {
268
-            $items = array();
269
-            if (LooseTypeCheck::isArray($schema->items)) {
270
-                $items = $schema->items;
271
-            } elseif (isset($schema->minItems) && count($value) < $schema->minItems) {
272
-                $items = array_fill(count($value), $schema->minItems - count($value), $schema->items);
273
-            }
274
-            // $value is an array, and items are defined - treat as plain array
275
-            foreach ($items as $currentItem => $itemDefinition) {
276
-                $itemDefinition = $this->factory->getSchemaStorage()->resolveRefSchema($itemDefinition);
277
-                if (
278
-                    !array_key_exists($currentItem, $value)
279
-                    && property_exists($itemDefinition, 'default')
280
-                    && $this->shouldApplyDefaultValue($requiredOnly, $itemDefinition)) {
281
-                    if (is_object($itemDefinition->default)) {
282
-                        $value[$currentItem] = clone $itemDefinition->default;
283
-                    } else {
284
-                        $value[$currentItem] = $itemDefinition->default;
285
-                    }
286
-                }
287
-                $path->setFromDefault();
288
-            }
289
-        } elseif (
290
-            $value instanceof self
291
-            && property_exists($schema, 'default')
292
-            && $this->shouldApplyDefaultValue($requiredOnly, $schema)) {
293
-            // $value is a leaf, not a container - apply the default directly
294
-            $value = is_object($schema->default) ? clone $schema->default : $schema->default;
295
-            $path->setFromDefault();
296
-        }
297
-    }
298
-
299
-    /**
300
-     * Validate allOf, anyOf, and oneOf properties
301
-     *
302
-     * @param mixed       $value
303
-     * @param mixed       $schema
304
-     * @param JsonPointer $path
305
-     * @param string      $i
306
-     */
307
-    protected function validateOfProperties(&$value, $schema, JsonPointer $path, $i = '')
308
-    {
309
-        // Verify type
310
-        if ($value instanceof self) {
311
-            return;
312
-        }
313
-
314
-        if (isset($schema->allOf)) {
315
-            $isValid = true;
316
-            foreach ($schema->allOf as $allOf) {
317
-                $initErrors = $this->getErrors();
318
-                $this->checkUndefined($value, $allOf, $path, $i);
319
-                $isValid = $isValid && (count($this->getErrors()) == count($initErrors));
320
-            }
321
-            if (!$isValid) {
322
-                $this->addError($path, 'Failed to match all schemas', 'allOf');
323
-            }
324
-        }
325
-
326
-        if (isset($schema->anyOf)) {
327
-            $isValid = false;
328
-            $startErrors = $this->getErrors();
329
-            $caughtException = null;
330
-            foreach ($schema->anyOf as $anyOf) {
331
-                $initErrors = $this->getErrors();
332
-                try {
333
-                    $this->checkUndefined($value, $anyOf, $path, $i);
334
-                    if ($isValid = (count($this->getErrors()) == count($initErrors))) {
335
-                        break;
336
-                    }
337
-                } catch (ValidationException $e) {
338
-                    $isValid = false;
339
-                }
340
-            }
341
-            if (!$isValid) {
342
-                $this->addError($path, 'Failed to match at least one schema', 'anyOf');
343
-            } else {
344
-                $this->errors = $startErrors;
345
-            }
346
-        }
347
-
348
-        if (isset($schema->oneOf)) {
349
-            $allErrors = array();
350
-            $matchedSchemas = 0;
351
-            $startErrors = $this->getErrors();
352
-            foreach ($schema->oneOf as $oneOf) {
353
-                try {
354
-                    $this->errors = array();
355
-                    $this->checkUndefined($value, $oneOf, $path, $i);
356
-                    if (count($this->getErrors()) == 0) {
357
-                        $matchedSchemas++;
358
-                    }
359
-                    $allErrors = array_merge($allErrors, array_values($this->getErrors()));
360
-                } catch (ValidationException $e) {
361
-                    // deliberately do nothing here - validation failed, but we want to check
362
-                    // other schema options in the OneOf field.
363
-                }
364
-            }
365
-            if ($matchedSchemas !== 1) {
366
-                $this->addErrors(array_merge($allErrors, $startErrors));
367
-                $this->addError($path, 'Failed to match exactly one schema', 'oneOf');
368
-            } else {
369
-                $this->errors = $startErrors;
370
-            }
371
-        }
372
-    }
373
-
374
-    /**
375
-     * Validate dependencies
376
-     *
377
-     * @param mixed       $value
378
-     * @param mixed       $dependencies
379
-     * @param JsonPointer $path
380
-     * @param string      $i
381
-     */
382
-    protected function validateDependencies($value, $dependencies, JsonPointer $path, $i = '')
383
-    {
384
-        foreach ($dependencies as $key => $dependency) {
385
-            if ($this->getTypeCheck()->propertyExists($value, $key)) {
386
-                if (is_string($dependency)) {
387
-                    // Draft 3 string is allowed - e.g. "dependencies": {"bar": "foo"}
388
-                    if (!$this->getTypeCheck()->propertyExists($value, $dependency)) {
389
-                        $this->addError($path, "$key depends on $dependency and $dependency is missing", 'dependencies');
390
-                    }
391
-                } elseif (is_array($dependency)) {
392
-                    // Draft 4 must be an array - e.g. "dependencies": {"bar": ["foo"]}
393
-                    foreach ($dependency as $d) {
394
-                        if (!$this->getTypeCheck()->propertyExists($value, $d)) {
395
-                            $this->addError($path, "$key depends on $d and $d is missing", 'dependencies');
396
-                        }
397
-                    }
398
-                } elseif (is_object($dependency)) {
399
-                    // Schema - e.g. "dependencies": {"bar": {"properties": {"foo": {...}}}}
400
-                    $this->checkUndefined($value, $dependency, $path, $i);
401
-                }
402
-            }
403
-        }
404
-    }
405
-
406
-    protected function validateUri($schema, $schemaUri = null)
407
-    {
408
-        $resolver = new UriResolver();
409
-        $retriever = $this->factory->getUriRetriever();
410
-
411
-        $jsonSchema = null;
412
-        if ($resolver->isValid($schemaUri)) {
413
-            $schemaId = property_exists($schema, 'id') ? $schema->id : null;
414
-            $jsonSchema = $retriever->retrieve($schemaId, $schemaUri);
415
-        }
416
-
417
-        return $jsonSchema;
418
-    }
25
+	/**
26
+	 * @var array List of properties to which a default value has been applied
27
+	 */
28
+	protected $appliedDefaults = array();
29
+
30
+	/**
31
+	 * {@inheritdoc}
32
+	 */
33
+	public function check(&$value, $schema = null, JsonPointer $path = null, $i = null, $fromDefault = false)
34
+	{
35
+		if (is_null($schema) || !is_object($schema)) {
36
+			return;
37
+		}
38
+
39
+		$path = $this->incrementPath($path ?: new JsonPointer(''), $i);
40
+		if ($fromDefault) {
41
+			$path->setFromDefault();
42
+		}
43
+
44
+		// check special properties
45
+		$this->validateCommonProperties($value, $schema, $path, $i);
46
+
47
+		// check allOf, anyOf, and oneOf properties
48
+		$this->validateOfProperties($value, $schema, $path, '');
49
+
50
+		// check known types
51
+		$this->validateTypes($value, $schema, $path, $i);
52
+	}
53
+
54
+	/**
55
+	 * Validates the value against the types
56
+	 *
57
+	 * @param mixed       $value
58
+	 * @param mixed       $schema
59
+	 * @param JsonPointer $path
60
+	 * @param string      $i
61
+	 */
62
+	public function validateTypes(&$value, $schema, JsonPointer $path, $i = null)
63
+	{
64
+		// check array
65
+		if ($this->getTypeCheck()->isArray($value)) {
66
+			$this->checkArray($value, $schema, $path, $i);
67
+		}
68
+
69
+		// check object
70
+		if (LooseTypeCheck::isObject($value)) { // object processing should always be run on assoc arrays,
71
+												// so use LooseTypeCheck here even if CHECK_MODE_TYPE_CAST
72
+												// is not set (i.e. don't use $this->getTypeCheck() here).
73
+			$this->checkObject(
74
+				$value,
75
+				$schema,
76
+				$path,
77
+				isset($schema->properties) ? $schema->properties : null,
78
+				isset($schema->additionalProperties) ? $schema->additionalProperties : null,
79
+				isset($schema->patternProperties) ? $schema->patternProperties : null,
80
+				$this->appliedDefaults
81
+			);
82
+		}
83
+
84
+		// check string
85
+		if (is_string($value)) {
86
+			$this->checkString($value, $schema, $path, $i);
87
+		}
88
+
89
+		// check numeric
90
+		if (is_numeric($value)) {
91
+			$this->checkNumber($value, $schema, $path, $i);
92
+		}
93
+
94
+		// check enum
95
+		if (isset($schema->enum)) {
96
+			$this->checkEnum($value, $schema, $path, $i);
97
+		}
98
+	}
99
+
100
+	/**
101
+	 * Validates common properties
102
+	 *
103
+	 * @param mixed       $value
104
+	 * @param mixed       $schema
105
+	 * @param JsonPointer $path
106
+	 * @param string      $i
107
+	 */
108
+	protected function validateCommonProperties(&$value, $schema, JsonPointer $path, $i = '')
109
+	{
110
+		// if it extends another schema, it must pass that schema as well
111
+		if (isset($schema->extends)) {
112
+			if (is_string($schema->extends)) {
113
+				$schema->extends = $this->validateUri($schema, $schema->extends);
114
+			}
115
+			if (is_array($schema->extends)) {
116
+				foreach ($schema->extends as $extends) {
117
+					$this->checkUndefined($value, $extends, $path, $i);
118
+				}
119
+			} else {
120
+				$this->checkUndefined($value, $schema->extends, $path, $i);
121
+			}
122
+		}
123
+
124
+		// Apply default values from schema
125
+		if (!$path->fromDefault()) {
126
+			$this->applyDefaultValues($value, $schema, $path);
127
+		}
128
+
129
+		// Verify required values
130
+		if ($this->getTypeCheck()->isObject($value)) {
131
+			if (!($value instanceof self) && isset($schema->required) && is_array($schema->required)) {
132
+				// Draft 4 - Required is an array of strings - e.g. "required": ["foo", ...]
133
+				foreach ($schema->required as $required) {
134
+					if (!$this->getTypeCheck()->propertyExists($value, $required)) {
135
+						$this->addError(
136
+							$this->incrementPath($path ?: new JsonPointer(''), $required),
137
+							'The property ' . $required . ' is required',
138
+							'required'
139
+						);
140
+					}
141
+				}
142
+			} elseif (isset($schema->required) && !is_array($schema->required)) {
143
+				// Draft 3 - Required attribute - e.g. "foo": {"type": "string", "required": true}
144
+				if ($schema->required && $value instanceof self) {
145
+					$propertyPaths = $path->getPropertyPaths();
146
+					$propertyName = end($propertyPaths);
147
+					$this->addError(
148
+						$path,
149
+						'The property ' . $propertyName . ' is required',
150
+						'required'
151
+					);
152
+				}
153
+			} else {
154
+				// if the value is both undefined and not required, skip remaining checks
155
+				// in this method which assume an actual, defined instance when validating.
156
+				if ($value instanceof self) {
157
+					return;
158
+				}
159
+			}
160
+		}
161
+
162
+		// Verify type
163
+		if (!($value instanceof self)) {
164
+			$this->checkType($value, $schema, $path, $i);
165
+		}
166
+
167
+		// Verify disallowed items
168
+		if (isset($schema->disallow)) {
169
+			$initErrors = $this->getErrors();
170
+
171
+			$typeSchema = new \stdClass();
172
+			$typeSchema->type = $schema->disallow;
173
+			$this->checkType($value, $typeSchema, $path);
174
+
175
+			// if no new errors were raised it must be a disallowed value
176
+			if (count($this->getErrors()) == count($initErrors)) {
177
+				$this->addError($path, 'Disallowed value was matched', 'disallow');
178
+			} else {
179
+				$this->errors = $initErrors;
180
+			}
181
+		}
182
+
183
+		if (isset($schema->not)) {
184
+			$initErrors = $this->getErrors();
185
+			$this->checkUndefined($value, $schema->not, $path, $i);
186
+
187
+			// if no new errors were raised then the instance validated against the "not" schema
188
+			if (count($this->getErrors()) == count($initErrors)) {
189
+				$this->addError($path, 'Matched a schema which it should not', 'not');
190
+			} else {
191
+				$this->errors = $initErrors;
192
+			}
193
+		}
194
+
195
+		// Verify that dependencies are met
196
+		if (isset($schema->dependencies) && $this->getTypeCheck()->isObject($value)) {
197
+			$this->validateDependencies($value, $schema->dependencies, $path);
198
+		}
199
+	}
200
+
201
+	/**
202
+	 * Check whether a default should be applied for this value
203
+	 *
204
+	 * @param mixed $schema
205
+	 * @param mixed $parentSchema
206
+	 * @param bool  $requiredOnly
207
+	 *
208
+	 * @return bool
209
+	 */
210
+	private function shouldApplyDefaultValue($requiredOnly, $schema, $name = null, $parentSchema = null)
211
+	{
212
+		// required-only mode is off
213
+		if (!$requiredOnly) {
214
+			return true;
215
+		}
216
+		// draft-04 required is set
217
+		if (
218
+			$name !== null
219
+			&& isset($parentSchema->required)
220
+			&& is_array($parentSchema->required)
221
+			&& in_array($name, $parentSchema->required)
222
+		) {
223
+			return true;
224
+		}
225
+		// draft-03 required is set
226
+		if (isset($schema->required) && !is_array($schema->required) && $schema->required) {
227
+			return true;
228
+		}
229
+		// default case
230
+		return false;
231
+	}
232
+
233
+	/**
234
+	 * Apply default values
235
+	 *
236
+	 * @param mixed       $value
237
+	 * @param mixed       $schema
238
+	 * @param JsonPointer $path
239
+	 */
240
+	protected function applyDefaultValues(&$value, $schema, $path)
241
+	{
242
+		// only apply defaults if feature is enabled
243
+		if (!$this->factory->getConfig(self::CHECK_MODE_APPLY_DEFAULTS)) {
244
+			return;
245
+		}
246
+
247
+		// apply defaults if appropriate
248
+		$requiredOnly = $this->factory->getConfig(self::CHECK_MODE_ONLY_REQUIRED_DEFAULTS);
249
+		if (isset($schema->properties) && LooseTypeCheck::isObject($value)) {
250
+			// $value is an object or assoc array, and properties are defined - treat as an object
251
+			foreach ($schema->properties as $currentProperty => $propertyDefinition) {
252
+				$propertyDefinition = $this->factory->getSchemaStorage()->resolveRefSchema($propertyDefinition);
253
+				if (
254
+					!LooseTypeCheck::propertyExists($value, $currentProperty)
255
+					&& property_exists($propertyDefinition, 'default')
256
+					&& $this->shouldApplyDefaultValue($requiredOnly, $propertyDefinition, $currentProperty, $schema)
257
+				) {
258
+					// assign default value
259
+					if (is_object($propertyDefinition->default)) {
260
+						LooseTypeCheck::propertySet($value, $currentProperty, clone $propertyDefinition->default);
261
+					} else {
262
+						LooseTypeCheck::propertySet($value, $currentProperty, $propertyDefinition->default);
263
+					}
264
+					$this->appliedDefaults[] = $currentProperty;
265
+				}
266
+			}
267
+		} elseif (isset($schema->items) && LooseTypeCheck::isArray($value)) {
268
+			$items = array();
269
+			if (LooseTypeCheck::isArray($schema->items)) {
270
+				$items = $schema->items;
271
+			} elseif (isset($schema->minItems) && count($value) < $schema->minItems) {
272
+				$items = array_fill(count($value), $schema->minItems - count($value), $schema->items);
273
+			}
274
+			// $value is an array, and items are defined - treat as plain array
275
+			foreach ($items as $currentItem => $itemDefinition) {
276
+				$itemDefinition = $this->factory->getSchemaStorage()->resolveRefSchema($itemDefinition);
277
+				if (
278
+					!array_key_exists($currentItem, $value)
279
+					&& property_exists($itemDefinition, 'default')
280
+					&& $this->shouldApplyDefaultValue($requiredOnly, $itemDefinition)) {
281
+					if (is_object($itemDefinition->default)) {
282
+						$value[$currentItem] = clone $itemDefinition->default;
283
+					} else {
284
+						$value[$currentItem] = $itemDefinition->default;
285
+					}
286
+				}
287
+				$path->setFromDefault();
288
+			}
289
+		} elseif (
290
+			$value instanceof self
291
+			&& property_exists($schema, 'default')
292
+			&& $this->shouldApplyDefaultValue($requiredOnly, $schema)) {
293
+			// $value is a leaf, not a container - apply the default directly
294
+			$value = is_object($schema->default) ? clone $schema->default : $schema->default;
295
+			$path->setFromDefault();
296
+		}
297
+	}
298
+
299
+	/**
300
+	 * Validate allOf, anyOf, and oneOf properties
301
+	 *
302
+	 * @param mixed       $value
303
+	 * @param mixed       $schema
304
+	 * @param JsonPointer $path
305
+	 * @param string      $i
306
+	 */
307
+	protected function validateOfProperties(&$value, $schema, JsonPointer $path, $i = '')
308
+	{
309
+		// Verify type
310
+		if ($value instanceof self) {
311
+			return;
312
+		}
313
+
314
+		if (isset($schema->allOf)) {
315
+			$isValid = true;
316
+			foreach ($schema->allOf as $allOf) {
317
+				$initErrors = $this->getErrors();
318
+				$this->checkUndefined($value, $allOf, $path, $i);
319
+				$isValid = $isValid && (count($this->getErrors()) == count($initErrors));
320
+			}
321
+			if (!$isValid) {
322
+				$this->addError($path, 'Failed to match all schemas', 'allOf');
323
+			}
324
+		}
325
+
326
+		if (isset($schema->anyOf)) {
327
+			$isValid = false;
328
+			$startErrors = $this->getErrors();
329
+			$caughtException = null;
330
+			foreach ($schema->anyOf as $anyOf) {
331
+				$initErrors = $this->getErrors();
332
+				try {
333
+					$this->checkUndefined($value, $anyOf, $path, $i);
334
+					if ($isValid = (count($this->getErrors()) == count($initErrors))) {
335
+						break;
336
+					}
337
+				} catch (ValidationException $e) {
338
+					$isValid = false;
339
+				}
340
+			}
341
+			if (!$isValid) {
342
+				$this->addError($path, 'Failed to match at least one schema', 'anyOf');
343
+			} else {
344
+				$this->errors = $startErrors;
345
+			}
346
+		}
347
+
348
+		if (isset($schema->oneOf)) {
349
+			$allErrors = array();
350
+			$matchedSchemas = 0;
351
+			$startErrors = $this->getErrors();
352
+			foreach ($schema->oneOf as $oneOf) {
353
+				try {
354
+					$this->errors = array();
355
+					$this->checkUndefined($value, $oneOf, $path, $i);
356
+					if (count($this->getErrors()) == 0) {
357
+						$matchedSchemas++;
358
+					}
359
+					$allErrors = array_merge($allErrors, array_values($this->getErrors()));
360
+				} catch (ValidationException $e) {
361
+					// deliberately do nothing here - validation failed, but we want to check
362
+					// other schema options in the OneOf field.
363
+				}
364
+			}
365
+			if ($matchedSchemas !== 1) {
366
+				$this->addErrors(array_merge($allErrors, $startErrors));
367
+				$this->addError($path, 'Failed to match exactly one schema', 'oneOf');
368
+			} else {
369
+				$this->errors = $startErrors;
370
+			}
371
+		}
372
+	}
373
+
374
+	/**
375
+	 * Validate dependencies
376
+	 *
377
+	 * @param mixed       $value
378
+	 * @param mixed       $dependencies
379
+	 * @param JsonPointer $path
380
+	 * @param string      $i
381
+	 */
382
+	protected function validateDependencies($value, $dependencies, JsonPointer $path, $i = '')
383
+	{
384
+		foreach ($dependencies as $key => $dependency) {
385
+			if ($this->getTypeCheck()->propertyExists($value, $key)) {
386
+				if (is_string($dependency)) {
387
+					// Draft 3 string is allowed - e.g. "dependencies": {"bar": "foo"}
388
+					if (!$this->getTypeCheck()->propertyExists($value, $dependency)) {
389
+						$this->addError($path, "$key depends on $dependency and $dependency is missing", 'dependencies');
390
+					}
391
+				} elseif (is_array($dependency)) {
392
+					// Draft 4 must be an array - e.g. "dependencies": {"bar": ["foo"]}
393
+					foreach ($dependency as $d) {
394
+						if (!$this->getTypeCheck()->propertyExists($value, $d)) {
395
+							$this->addError($path, "$key depends on $d and $d is missing", 'dependencies');
396
+						}
397
+					}
398
+				} elseif (is_object($dependency)) {
399
+					// Schema - e.g. "dependencies": {"bar": {"properties": {"foo": {...}}}}
400
+					$this->checkUndefined($value, $dependency, $path, $i);
401
+				}
402
+			}
403
+		}
404
+	}
405
+
406
+	protected function validateUri($schema, $schemaUri = null)
407
+	{
408
+		$resolver = new UriResolver();
409
+		$retriever = $this->factory->getUriRetriever();
410
+
411
+		$jsonSchema = null;
412
+		if ($resolver->isValid($schemaUri)) {
413
+			$schemaId = property_exists($schema, 'id') ? $schema->id : null;
414
+			$jsonSchema = $retriever->retrieve($schemaId, $schemaUri);
415
+		}
416
+
417
+		return $jsonSchema;
418
+	}
419 419
 }
Please login to merge, or discard this patch.
justinrainbow/json-schema/src/JsonSchema/Constraints/NumberConstraint.php 1 patch
Indentation   +52 added lines, -52 removed lines patch added patch discarded remove patch
@@ -19,63 +19,63 @@
 block discarded – undo
19 19
  */
20 20
 class NumberConstraint extends Constraint
21 21
 {
22
-    /**
23
-     * {@inheritdoc}
24
-     */
25
-    public function check(&$element, $schema = null, JsonPointer $path = null, $i = null)
26
-    {
27
-        // Verify minimum
28
-        if (isset($schema->exclusiveMinimum)) {
29
-            if (isset($schema->minimum)) {
30
-                if ($schema->exclusiveMinimum && $element <= $schema->minimum) {
31
-                    $this->addError($path, 'Must have a minimum value of ' . $schema->minimum, 'exclusiveMinimum', array('minimum' => $schema->minimum));
32
-                } elseif ($element < $schema->minimum) {
33
-                    $this->addError($path, 'Must have a minimum value of ' . $schema->minimum, 'minimum', array('minimum' => $schema->minimum));
34
-                }
35
-            } else {
36
-                $this->addError($path, 'Use of exclusiveMinimum requires presence of minimum', 'missingMinimum');
37
-            }
38
-        } elseif (isset($schema->minimum) && $element < $schema->minimum) {
39
-            $this->addError($path, 'Must have a minimum value of ' . $schema->minimum, 'minimum', array('minimum' => $schema->minimum));
40
-        }
22
+	/**
23
+	 * {@inheritdoc}
24
+	 */
25
+	public function check(&$element, $schema = null, JsonPointer $path = null, $i = null)
26
+	{
27
+		// Verify minimum
28
+		if (isset($schema->exclusiveMinimum)) {
29
+			if (isset($schema->minimum)) {
30
+				if ($schema->exclusiveMinimum && $element <= $schema->minimum) {
31
+					$this->addError($path, 'Must have a minimum value of ' . $schema->minimum, 'exclusiveMinimum', array('minimum' => $schema->minimum));
32
+				} elseif ($element < $schema->minimum) {
33
+					$this->addError($path, 'Must have a minimum value of ' . $schema->minimum, 'minimum', array('minimum' => $schema->minimum));
34
+				}
35
+			} else {
36
+				$this->addError($path, 'Use of exclusiveMinimum requires presence of minimum', 'missingMinimum');
37
+			}
38
+		} elseif (isset($schema->minimum) && $element < $schema->minimum) {
39
+			$this->addError($path, 'Must have a minimum value of ' . $schema->minimum, 'minimum', array('minimum' => $schema->minimum));
40
+		}
41 41
 
42
-        // Verify maximum
43
-        if (isset($schema->exclusiveMaximum)) {
44
-            if (isset($schema->maximum)) {
45
-                if ($schema->exclusiveMaximum && $element >= $schema->maximum) {
46
-                    $this->addError($path, 'Must have a maximum value of ' . $schema->maximum, 'exclusiveMaximum', array('maximum' => $schema->maximum));
47
-                } elseif ($element > $schema->maximum) {
48
-                    $this->addError($path, 'Must have a maximum value of ' . $schema->maximum, 'maximum', array('maximum' => $schema->maximum));
49
-                }
50
-            } else {
51
-                $this->addError($path, 'Use of exclusiveMaximum requires presence of maximum', 'missingMaximum');
52
-            }
53
-        } elseif (isset($schema->maximum) && $element > $schema->maximum) {
54
-            $this->addError($path, 'Must have a maximum value of ' . $schema->maximum, 'maximum', array('maximum' => $schema->maximum));
55
-        }
42
+		// Verify maximum
43
+		if (isset($schema->exclusiveMaximum)) {
44
+			if (isset($schema->maximum)) {
45
+				if ($schema->exclusiveMaximum && $element >= $schema->maximum) {
46
+					$this->addError($path, 'Must have a maximum value of ' . $schema->maximum, 'exclusiveMaximum', array('maximum' => $schema->maximum));
47
+				} elseif ($element > $schema->maximum) {
48
+					$this->addError($path, 'Must have a maximum value of ' . $schema->maximum, 'maximum', array('maximum' => $schema->maximum));
49
+				}
50
+			} else {
51
+				$this->addError($path, 'Use of exclusiveMaximum requires presence of maximum', 'missingMaximum');
52
+			}
53
+		} elseif (isset($schema->maximum) && $element > $schema->maximum) {
54
+			$this->addError($path, 'Must have a maximum value of ' . $schema->maximum, 'maximum', array('maximum' => $schema->maximum));
55
+		}
56 56
 
57
-        // Verify divisibleBy - Draft v3
58
-        if (isset($schema->divisibleBy) && $this->fmod($element, $schema->divisibleBy) != 0) {
59
-            $this->addError($path, 'Is not divisible by ' . $schema->divisibleBy, 'divisibleBy', array('divisibleBy' => $schema->divisibleBy));
60
-        }
57
+		// Verify divisibleBy - Draft v3
58
+		if (isset($schema->divisibleBy) && $this->fmod($element, $schema->divisibleBy) != 0) {
59
+			$this->addError($path, 'Is not divisible by ' . $schema->divisibleBy, 'divisibleBy', array('divisibleBy' => $schema->divisibleBy));
60
+		}
61 61
 
62
-        // Verify multipleOf - Draft v4
63
-        if (isset($schema->multipleOf) && $this->fmod($element, $schema->multipleOf) != 0) {
64
-            $this->addError($path, 'Must be a multiple of ' . $schema->multipleOf, 'multipleOf', array('multipleOf' => $schema->multipleOf));
65
-        }
62
+		// Verify multipleOf - Draft v4
63
+		if (isset($schema->multipleOf) && $this->fmod($element, $schema->multipleOf) != 0) {
64
+			$this->addError($path, 'Must be a multiple of ' . $schema->multipleOf, 'multipleOf', array('multipleOf' => $schema->multipleOf));
65
+		}
66 66
 
67
-        $this->checkFormat($element, $schema, $path, $i);
68
-    }
67
+		$this->checkFormat($element, $schema, $path, $i);
68
+	}
69 69
 
70
-    private function fmod($number1, $number2)
71
-    {
72
-        $modulus = ($number1 - round($number1 / $number2) * $number2);
73
-        $precision = 0.0000000001;
70
+	private function fmod($number1, $number2)
71
+	{
72
+		$modulus = ($number1 - round($number1 / $number2) * $number2);
73
+		$precision = 0.0000000001;
74 74
 
75
-        if (-$precision < $modulus && $modulus < $precision) {
76
-            return 0.0;
77
-        }
75
+		if (-$precision < $modulus && $modulus < $precision) {
76
+			return 0.0;
77
+		}
78 78
 
79
-        return $modulus;
80
-    }
79
+		return $modulus;
80
+	}
81 81
 }
Please login to merge, or discard this patch.
justinrainbow/json-schema/src/JsonSchema/Constraints/FormatConstraint.php 1 patch
Indentation   +190 added lines, -190 removed lines patch added patch discarded remove patch
@@ -21,194 +21,194 @@
 block discarded – undo
21 21
  */
22 22
 class FormatConstraint extends Constraint
23 23
 {
24
-    /**
25
-     * {@inheritdoc}
26
-     */
27
-    public function check(&$element, $schema = null, JsonPointer $path = null, $i = null)
28
-    {
29
-        if (!isset($schema->format) || $this->factory->getConfig(self::CHECK_MODE_DISABLE_FORMAT)) {
30
-            return;
31
-        }
32
-
33
-        switch ($schema->format) {
34
-            case 'date':
35
-                if (!$date = $this->validateDateTime($element, 'Y-m-d')) {
36
-                    $this->addError($path, sprintf('Invalid date %s, expected format YYYY-MM-DD', json_encode($element)), 'format', array('format' => $schema->format));
37
-                }
38
-                break;
39
-
40
-            case 'time':
41
-                if (!$this->validateDateTime($element, 'H:i:s')) {
42
-                    $this->addError($path, sprintf('Invalid time %s, expected format hh:mm:ss', json_encode($element)), 'format', array('format' => $schema->format));
43
-                }
44
-                break;
45
-
46
-            case 'date-time':
47
-                if (null === Rfc3339::createFromString($element)) {
48
-                    $this->addError($path, sprintf('Invalid date-time %s, expected format YYYY-MM-DDThh:mm:ssZ or YYYY-MM-DDThh:mm:ss+hh:mm', json_encode($element)), 'format', array('format' => $schema->format));
49
-                }
50
-                break;
51
-
52
-            case 'utc-millisec':
53
-                if (!$this->validateDateTime($element, 'U')) {
54
-                    $this->addError($path, sprintf('Invalid time %s, expected integer of milliseconds since Epoch', json_encode($element)), 'format', array('format' => $schema->format));
55
-                }
56
-                break;
57
-
58
-            case 'regex':
59
-                if (!$this->validateRegex($element)) {
60
-                    $this->addError($path, 'Invalid regex format ' . $element, 'format', array('format' => $schema->format));
61
-                }
62
-                break;
63
-
64
-            case 'color':
65
-                if (!$this->validateColor($element)) {
66
-                    $this->addError($path, 'Invalid color', 'format', array('format' => $schema->format));
67
-                }
68
-                break;
69
-
70
-            case 'style':
71
-                if (!$this->validateStyle($element)) {
72
-                    $this->addError($path, 'Invalid style', 'format', array('format' => $schema->format));
73
-                }
74
-                break;
75
-
76
-            case 'phone':
77
-                if (!$this->validatePhone($element)) {
78
-                    $this->addError($path, 'Invalid phone number', 'format', array('format' => $schema->format));
79
-                }
80
-                break;
81
-
82
-            case 'uri':
83
-                if (null === filter_var($element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE)) {
84
-                    $this->addError($path, 'Invalid URL format', 'format', array('format' => $schema->format));
85
-                }
86
-                break;
87
-
88
-            case 'uriref':
89
-            case 'uri-reference':
90
-                if (null === filter_var($element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE)) {
91
-                    // FILTER_VALIDATE_URL does not conform to RFC-3986, and cannot handle relative URLs, but
92
-                    // the json-schema spec uses RFC-3986, so need a bit of hackery to properly validate them.
93
-                    // See https://tools.ietf.org/html/rfc3986#section-4.2 for additional information.
94
-                    if (substr($element, 0, 2) === '//') { // network-path reference
95
-                        $validURL = filter_var('scheme:' . $element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE);
96
-                    } elseif (substr($element, 0, 1) === '/') { // absolute-path reference
97
-                        $validURL = filter_var('scheme://host' . $element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE);
98
-                    } elseif (strlen($element)) { // relative-path reference
99
-                        $pathParts = explode('/', $element, 2);
100
-                        if (strpos($pathParts[0], ':') !== false) {
101
-                            $validURL = null;
102
-                        } else {
103
-                            $validURL = filter_var('scheme://host/' . $element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE);
104
-                        }
105
-                    } else {
106
-                        $validURL = null;
107
-                    }
108
-                    if ($validURL === null) {
109
-                        $this->addError($path, 'Invalid URL format', 'format', array('format' => $schema->format));
110
-                    }
111
-                }
112
-                break;
113
-
114
-            case 'email':
115
-                $filterFlags = FILTER_NULL_ON_FAILURE;
116
-                if (defined('FILTER_FLAG_EMAIL_UNICODE')) {
117
-                    // Only available from PHP >= 7.1.0, so ignore it for coverage checks
118
-                    $filterFlags |= constant('FILTER_FLAG_EMAIL_UNICODE'); // @codeCoverageIgnore
119
-                }
120
-                if (null === filter_var($element, FILTER_VALIDATE_EMAIL, $filterFlags)) {
121
-                    $this->addError($path, 'Invalid email', 'format', array('format' => $schema->format));
122
-                }
123
-                break;
124
-
125
-            case 'ip-address':
126
-            case 'ipv4':
127
-                if (null === filter_var($element, FILTER_VALIDATE_IP, FILTER_NULL_ON_FAILURE | FILTER_FLAG_IPV4)) {
128
-                    $this->addError($path, 'Invalid IP address', 'format', array('format' => $schema->format));
129
-                }
130
-                break;
131
-
132
-            case 'ipv6':
133
-                if (null === filter_var($element, FILTER_VALIDATE_IP, FILTER_NULL_ON_FAILURE | FILTER_FLAG_IPV6)) {
134
-                    $this->addError($path, 'Invalid IP address', 'format', array('format' => $schema->format));
135
-                }
136
-                break;
137
-
138
-            case 'host-name':
139
-            case 'hostname':
140
-                if (!$this->validateHostname($element)) {
141
-                    $this->addError($path, 'Invalid hostname', 'format', array('format' => $schema->format));
142
-                }
143
-                break;
144
-
145
-            default:
146
-                // Empty as it should be:
147
-                // The value of this keyword is called a format attribute. It MUST be a string.
148
-                // A format attribute can generally only validate a given set of instance types.
149
-                // If the type of the instance to validate is not in this set, validation for
150
-                // this format attribute and instance SHOULD succeed.
151
-                // http://json-schema.org/latest/json-schema-validation.html#anchor105
152
-                break;
153
-        }
154
-    }
155
-
156
-    protected function validateDateTime($datetime, $format)
157
-    {
158
-        $dt = \DateTime::createFromFormat($format, $datetime);
159
-
160
-        if (!$dt) {
161
-            return false;
162
-        }
163
-
164
-        if ($datetime === $dt->format($format)) {
165
-            return true;
166
-        }
167
-
168
-        // handles the case where a non-6 digit microsecond datetime is passed
169
-        // which will fail the above string comparison because the passed
170
-        // $datetime may be '2000-05-01T12:12:12.123Z' but format() will return
171
-        // '2000-05-01T12:12:12.123000Z'
172
-        if ((strpos('u', $format) !== -1) && (preg_match('/\.\d+Z$/', $datetime))) {
173
-            return true;
174
-        }
175
-
176
-        return false;
177
-    }
178
-
179
-    protected function validateRegex($regex)
180
-    {
181
-        return false !== @preg_match('/' . $regex . '/u', '');
182
-    }
183
-
184
-    protected function validateColor($color)
185
-    {
186
-        if (in_array(strtolower($color), array('aqua', 'black', 'blue', 'fuchsia',
187
-            'gray', 'green', 'lime', 'maroon', 'navy', 'olive', 'orange', 'purple',
188
-            'red', 'silver', 'teal', 'white', 'yellow'))) {
189
-            return true;
190
-        }
191
-
192
-        return preg_match('/^#([a-f0-9]{3}|[a-f0-9]{6})$/i', $color);
193
-    }
194
-
195
-    protected function validateStyle($style)
196
-    {
197
-        $properties     = explode(';', rtrim($style, ';'));
198
-        $invalidEntries = preg_grep('/^\s*[-a-z]+\s*:\s*.+$/i', $properties, PREG_GREP_INVERT);
199
-
200
-        return empty($invalidEntries);
201
-    }
202
-
203
-    protected function validatePhone($phone)
204
-    {
205
-        return preg_match('/^\+?(\(\d{3}\)|\d{3}) \d{3} \d{4}$/', $phone);
206
-    }
207
-
208
-    protected function validateHostname($host)
209
-    {
210
-        $hostnameRegex = '/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/i';
211
-
212
-        return preg_match($hostnameRegex, $host);
213
-    }
24
+	/**
25
+	 * {@inheritdoc}
26
+	 */
27
+	public function check(&$element, $schema = null, JsonPointer $path = null, $i = null)
28
+	{
29
+		if (!isset($schema->format) || $this->factory->getConfig(self::CHECK_MODE_DISABLE_FORMAT)) {
30
+			return;
31
+		}
32
+
33
+		switch ($schema->format) {
34
+			case 'date':
35
+				if (!$date = $this->validateDateTime($element, 'Y-m-d')) {
36
+					$this->addError($path, sprintf('Invalid date %s, expected format YYYY-MM-DD', json_encode($element)), 'format', array('format' => $schema->format));
37
+				}
38
+				break;
39
+
40
+			case 'time':
41
+				if (!$this->validateDateTime($element, 'H:i:s')) {
42
+					$this->addError($path, sprintf('Invalid time %s, expected format hh:mm:ss', json_encode($element)), 'format', array('format' => $schema->format));
43
+				}
44
+				break;
45
+
46
+			case 'date-time':
47
+				if (null === Rfc3339::createFromString($element)) {
48
+					$this->addError($path, sprintf('Invalid date-time %s, expected format YYYY-MM-DDThh:mm:ssZ or YYYY-MM-DDThh:mm:ss+hh:mm', json_encode($element)), 'format', array('format' => $schema->format));
49
+				}
50
+				break;
51
+
52
+			case 'utc-millisec':
53
+				if (!$this->validateDateTime($element, 'U')) {
54
+					$this->addError($path, sprintf('Invalid time %s, expected integer of milliseconds since Epoch', json_encode($element)), 'format', array('format' => $schema->format));
55
+				}
56
+				break;
57
+
58
+			case 'regex':
59
+				if (!$this->validateRegex($element)) {
60
+					$this->addError($path, 'Invalid regex format ' . $element, 'format', array('format' => $schema->format));
61
+				}
62
+				break;
63
+
64
+			case 'color':
65
+				if (!$this->validateColor($element)) {
66
+					$this->addError($path, 'Invalid color', 'format', array('format' => $schema->format));
67
+				}
68
+				break;
69
+
70
+			case 'style':
71
+				if (!$this->validateStyle($element)) {
72
+					$this->addError($path, 'Invalid style', 'format', array('format' => $schema->format));
73
+				}
74
+				break;
75
+
76
+			case 'phone':
77
+				if (!$this->validatePhone($element)) {
78
+					$this->addError($path, 'Invalid phone number', 'format', array('format' => $schema->format));
79
+				}
80
+				break;
81
+
82
+			case 'uri':
83
+				if (null === filter_var($element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE)) {
84
+					$this->addError($path, 'Invalid URL format', 'format', array('format' => $schema->format));
85
+				}
86
+				break;
87
+
88
+			case 'uriref':
89
+			case 'uri-reference':
90
+				if (null === filter_var($element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE)) {
91
+					// FILTER_VALIDATE_URL does not conform to RFC-3986, and cannot handle relative URLs, but
92
+					// the json-schema spec uses RFC-3986, so need a bit of hackery to properly validate them.
93
+					// See https://tools.ietf.org/html/rfc3986#section-4.2 for additional information.
94
+					if (substr($element, 0, 2) === '//') { // network-path reference
95
+						$validURL = filter_var('scheme:' . $element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE);
96
+					} elseif (substr($element, 0, 1) === '/') { // absolute-path reference
97
+						$validURL = filter_var('scheme://host' . $element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE);
98
+					} elseif (strlen($element)) { // relative-path reference
99
+						$pathParts = explode('/', $element, 2);
100
+						if (strpos($pathParts[0], ':') !== false) {
101
+							$validURL = null;
102
+						} else {
103
+							$validURL = filter_var('scheme://host/' . $element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE);
104
+						}
105
+					} else {
106
+						$validURL = null;
107
+					}
108
+					if ($validURL === null) {
109
+						$this->addError($path, 'Invalid URL format', 'format', array('format' => $schema->format));
110
+					}
111
+				}
112
+				break;
113
+
114
+			case 'email':
115
+				$filterFlags = FILTER_NULL_ON_FAILURE;
116
+				if (defined('FILTER_FLAG_EMAIL_UNICODE')) {
117
+					// Only available from PHP >= 7.1.0, so ignore it for coverage checks
118
+					$filterFlags |= constant('FILTER_FLAG_EMAIL_UNICODE'); // @codeCoverageIgnore
119
+				}
120
+				if (null === filter_var($element, FILTER_VALIDATE_EMAIL, $filterFlags)) {
121
+					$this->addError($path, 'Invalid email', 'format', array('format' => $schema->format));
122
+				}
123
+				break;
124
+
125
+			case 'ip-address':
126
+			case 'ipv4':
127
+				if (null === filter_var($element, FILTER_VALIDATE_IP, FILTER_NULL_ON_FAILURE | FILTER_FLAG_IPV4)) {
128
+					$this->addError($path, 'Invalid IP address', 'format', array('format' => $schema->format));
129
+				}
130
+				break;
131
+
132
+			case 'ipv6':
133
+				if (null === filter_var($element, FILTER_VALIDATE_IP, FILTER_NULL_ON_FAILURE | FILTER_FLAG_IPV6)) {
134
+					$this->addError($path, 'Invalid IP address', 'format', array('format' => $schema->format));
135
+				}
136
+				break;
137
+
138
+			case 'host-name':
139
+			case 'hostname':
140
+				if (!$this->validateHostname($element)) {
141
+					$this->addError($path, 'Invalid hostname', 'format', array('format' => $schema->format));
142
+				}
143
+				break;
144
+
145
+			default:
146
+				// Empty as it should be:
147
+				// The value of this keyword is called a format attribute. It MUST be a string.
148
+				// A format attribute can generally only validate a given set of instance types.
149
+				// If the type of the instance to validate is not in this set, validation for
150
+				// this format attribute and instance SHOULD succeed.
151
+				// http://json-schema.org/latest/json-schema-validation.html#anchor105
152
+				break;
153
+		}
154
+	}
155
+
156
+	protected function validateDateTime($datetime, $format)
157
+	{
158
+		$dt = \DateTime::createFromFormat($format, $datetime);
159
+
160
+		if (!$dt) {
161
+			return false;
162
+		}
163
+
164
+		if ($datetime === $dt->format($format)) {
165
+			return true;
166
+		}
167
+
168
+		// handles the case where a non-6 digit microsecond datetime is passed
169
+		// which will fail the above string comparison because the passed
170
+		// $datetime may be '2000-05-01T12:12:12.123Z' but format() will return
171
+		// '2000-05-01T12:12:12.123000Z'
172
+		if ((strpos('u', $format) !== -1) && (preg_match('/\.\d+Z$/', $datetime))) {
173
+			return true;
174
+		}
175
+
176
+		return false;
177
+	}
178
+
179
+	protected function validateRegex($regex)
180
+	{
181
+		return false !== @preg_match('/' . $regex . '/u', '');
182
+	}
183
+
184
+	protected function validateColor($color)
185
+	{
186
+		if (in_array(strtolower($color), array('aqua', 'black', 'blue', 'fuchsia',
187
+			'gray', 'green', 'lime', 'maroon', 'navy', 'olive', 'orange', 'purple',
188
+			'red', 'silver', 'teal', 'white', 'yellow'))) {
189
+			return true;
190
+		}
191
+
192
+		return preg_match('/^#([a-f0-9]{3}|[a-f0-9]{6})$/i', $color);
193
+	}
194
+
195
+	protected function validateStyle($style)
196
+	{
197
+		$properties     = explode(';', rtrim($style, ';'));
198
+		$invalidEntries = preg_grep('/^\s*[-a-z]+\s*:\s*.+$/i', $properties, PREG_GREP_INVERT);
199
+
200
+		return empty($invalidEntries);
201
+	}
202
+
203
+	protected function validatePhone($phone)
204
+	{
205
+		return preg_match('/^\+?(\(\d{3}\)|\d{3}) \d{3} \d{4}$/', $phone);
206
+	}
207
+
208
+	protected function validateHostname($host)
209
+	{
210
+		$hostnameRegex = '/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/i';
211
+
212
+		return preg_match($hostnameRegex, $host);
213
+	}
214 214
 }
Please login to merge, or discard this patch.
justinrainbow/json-schema/src/JsonSchema/Constraints/StringConstraint.php 1 patch
Indentation   +38 added lines, -38 removed lines patch added patch discarded remove patch
@@ -19,42 +19,42 @@
 block discarded – undo
19 19
  */
20 20
 class StringConstraint extends Constraint
21 21
 {
22
-    /**
23
-     * {@inheritdoc}
24
-     */
25
-    public function check(&$element, $schema = null, JsonPointer $path = null, $i = null)
26
-    {
27
-        // Verify maxLength
28
-        if (isset($schema->maxLength) && $this->strlen($element) > $schema->maxLength) {
29
-            $this->addError($path, 'Must be at most ' . $schema->maxLength . ' characters long', 'maxLength', array(
30
-                'maxLength' => $schema->maxLength,
31
-            ));
32
-        }
33
-
34
-        //verify minLength
35
-        if (isset($schema->minLength) && $this->strlen($element) < $schema->minLength) {
36
-            $this->addError($path, 'Must be at least ' . $schema->minLength . ' characters long', 'minLength', array(
37
-                'minLength' => $schema->minLength,
38
-            ));
39
-        }
40
-
41
-        // Verify a regex pattern
42
-        if (isset($schema->pattern) && !preg_match('#' . str_replace('#', '\\#', $schema->pattern) . '#u', $element)) {
43
-            $this->addError($path, 'Does not match the regex pattern ' . $schema->pattern, 'pattern', array(
44
-                'pattern' => $schema->pattern,
45
-            ));
46
-        }
47
-
48
-        $this->checkFormat($element, $schema, $path, $i);
49
-    }
50
-
51
-    private function strlen($string)
52
-    {
53
-        if (extension_loaded('mbstring')) {
54
-            return mb_strlen($string, mb_detect_encoding($string));
55
-        }
56
-
57
-        // mbstring is present on all test platforms, so strlen() can be ignored for coverage
58
-        return strlen($string); // @codeCoverageIgnore
59
-    }
22
+	/**
23
+	 * {@inheritdoc}
24
+	 */
25
+	public function check(&$element, $schema = null, JsonPointer $path = null, $i = null)
26
+	{
27
+		// Verify maxLength
28
+		if (isset($schema->maxLength) && $this->strlen($element) > $schema->maxLength) {
29
+			$this->addError($path, 'Must be at most ' . $schema->maxLength . ' characters long', 'maxLength', array(
30
+				'maxLength' => $schema->maxLength,
31
+			));
32
+		}
33
+
34
+		//verify minLength
35
+		if (isset($schema->minLength) && $this->strlen($element) < $schema->minLength) {
36
+			$this->addError($path, 'Must be at least ' . $schema->minLength . ' characters long', 'minLength', array(
37
+				'minLength' => $schema->minLength,
38
+			));
39
+		}
40
+
41
+		// Verify a regex pattern
42
+		if (isset($schema->pattern) && !preg_match('#' . str_replace('#', '\\#', $schema->pattern) . '#u', $element)) {
43
+			$this->addError($path, 'Does not match the regex pattern ' . $schema->pattern, 'pattern', array(
44
+				'pattern' => $schema->pattern,
45
+			));
46
+		}
47
+
48
+		$this->checkFormat($element, $schema, $path, $i);
49
+	}
50
+
51
+	private function strlen($string)
52
+	{
53
+		if (extension_loaded('mbstring')) {
54
+			return mb_strlen($string, mb_detect_encoding($string));
55
+		}
56
+
57
+		// mbstring is present on all test platforms, so strlen() can be ignored for coverage
58
+		return strlen($string); // @codeCoverageIgnore
59
+	}
60 60
 }
Please login to merge, or discard this patch.
justinrainbow/json-schema/src/JsonSchema/Constraints/SchemaConstraint.php 1 patch
Indentation   +60 added lines, -60 removed lines patch added patch discarded remove patch
@@ -23,72 +23,72 @@
 block discarded – undo
23 23
  */
24 24
 class SchemaConstraint extends Constraint
25 25
 {
26
-    const DEFAULT_SCHEMA_SPEC = 'http://json-schema.org/draft-04/schema#';
26
+	const DEFAULT_SCHEMA_SPEC = 'http://json-schema.org/draft-04/schema#';
27 27
 
28
-    /**
29
-     * {@inheritdoc}
30
-     */
31
-    public function check(&$element, $schema = null, JsonPointer $path = null, $i = null)
32
-    {
33
-        if ($schema !== null) {
34
-            // passed schema
35
-            $validationSchema = $schema;
36
-        } elseif ($this->getTypeCheck()->propertyExists($element, $this->inlineSchemaProperty)) {
37
-            // inline schema
38
-            $validationSchema = $this->getTypeCheck()->propertyGet($element, $this->inlineSchemaProperty);
39
-        } else {
40
-            throw new InvalidArgumentException('no schema found to verify against');
41
-        }
28
+	/**
29
+	 * {@inheritdoc}
30
+	 */
31
+	public function check(&$element, $schema = null, JsonPointer $path = null, $i = null)
32
+	{
33
+		if ($schema !== null) {
34
+			// passed schema
35
+			$validationSchema = $schema;
36
+		} elseif ($this->getTypeCheck()->propertyExists($element, $this->inlineSchemaProperty)) {
37
+			// inline schema
38
+			$validationSchema = $this->getTypeCheck()->propertyGet($element, $this->inlineSchemaProperty);
39
+		} else {
40
+			throw new InvalidArgumentException('no schema found to verify against');
41
+		}
42 42
 
43
-        // cast array schemas to object
44
-        if (is_array($validationSchema)) {
45
-            $validationSchema = BaseConstraint::arrayToObjectRecursive($validationSchema);
46
-        }
43
+		// cast array schemas to object
44
+		if (is_array($validationSchema)) {
45
+			$validationSchema = BaseConstraint::arrayToObjectRecursive($validationSchema);
46
+		}
47 47
 
48
-        // validate schema against whatever is defined in $validationSchema->$schema. If no
49
-        // schema is defined, assume self::DEFAULT_SCHEMA_SPEC (currently draft-04).
50
-        if ($this->factory->getConfig(self::CHECK_MODE_VALIDATE_SCHEMA)) {
51
-            if (!$this->getTypeCheck()->isObject($validationSchema)) {
52
-                throw new RuntimeException('Cannot validate the schema of a non-object');
53
-            }
54
-            if ($this->getTypeCheck()->propertyExists($validationSchema, '$schema')) {
55
-                $schemaSpec = $this->getTypeCheck()->propertyGet($validationSchema, '$schema');
56
-            } else {
57
-                $schemaSpec = self::DEFAULT_SCHEMA_SPEC;
58
-            }
48
+		// validate schema against whatever is defined in $validationSchema->$schema. If no
49
+		// schema is defined, assume self::DEFAULT_SCHEMA_SPEC (currently draft-04).
50
+		if ($this->factory->getConfig(self::CHECK_MODE_VALIDATE_SCHEMA)) {
51
+			if (!$this->getTypeCheck()->isObject($validationSchema)) {
52
+				throw new RuntimeException('Cannot validate the schema of a non-object');
53
+			}
54
+			if ($this->getTypeCheck()->propertyExists($validationSchema, '$schema')) {
55
+				$schemaSpec = $this->getTypeCheck()->propertyGet($validationSchema, '$schema');
56
+			} else {
57
+				$schemaSpec = self::DEFAULT_SCHEMA_SPEC;
58
+			}
59 59
 
60
-            // get the spec schema
61
-            $schemaStorage = $this->factory->getSchemaStorage();
62
-            if (!$this->getTypeCheck()->isObject($schemaSpec)) {
63
-                $schemaSpec = $schemaStorage->getSchema($schemaSpec);
64
-            }
60
+			// get the spec schema
61
+			$schemaStorage = $this->factory->getSchemaStorage();
62
+			if (!$this->getTypeCheck()->isObject($schemaSpec)) {
63
+				$schemaSpec = $schemaStorage->getSchema($schemaSpec);
64
+			}
65 65
 
66
-            // save error count, config & subtract CHECK_MODE_VALIDATE_SCHEMA
67
-            $initialErrorCount = $this->numErrors();
68
-            $initialConfig = $this->factory->getConfig();
69
-            $initialContext = $this->factory->getErrorContext();
70
-            $this->factory->removeConfig(self::CHECK_MODE_VALIDATE_SCHEMA | self::CHECK_MODE_APPLY_DEFAULTS);
71
-            $this->factory->addConfig(self::CHECK_MODE_TYPE_CAST);
72
-            $this->factory->setErrorContext(Validator::ERROR_SCHEMA_VALIDATION);
66
+			// save error count, config & subtract CHECK_MODE_VALIDATE_SCHEMA
67
+			$initialErrorCount = $this->numErrors();
68
+			$initialConfig = $this->factory->getConfig();
69
+			$initialContext = $this->factory->getErrorContext();
70
+			$this->factory->removeConfig(self::CHECK_MODE_VALIDATE_SCHEMA | self::CHECK_MODE_APPLY_DEFAULTS);
71
+			$this->factory->addConfig(self::CHECK_MODE_TYPE_CAST);
72
+			$this->factory->setErrorContext(Validator::ERROR_SCHEMA_VALIDATION);
73 73
 
74
-            // validate schema
75
-            try {
76
-                $this->check($validationSchema, $schemaSpec);
77
-            } catch (\Exception $e) {
78
-                if ($this->factory->getConfig(self::CHECK_MODE_EXCEPTIONS)) {
79
-                    throw new InvalidSchemaException('Schema did not pass validation', 0, $e);
80
-                }
81
-            }
82
-            if ($this->numErrors() > $initialErrorCount) {
83
-                $this->addError($path, 'Schema is not valid', 'schema');
84
-            }
74
+			// validate schema
75
+			try {
76
+				$this->check($validationSchema, $schemaSpec);
77
+			} catch (\Exception $e) {
78
+				if ($this->factory->getConfig(self::CHECK_MODE_EXCEPTIONS)) {
79
+					throw new InvalidSchemaException('Schema did not pass validation', 0, $e);
80
+				}
81
+			}
82
+			if ($this->numErrors() > $initialErrorCount) {
83
+				$this->addError($path, 'Schema is not valid', 'schema');
84
+			}
85 85
 
86
-            // restore the initial config
87
-            $this->factory->setConfig($initialConfig);
88
-            $this->factory->setErrorContext($initialContext);
89
-        }
86
+			// restore the initial config
87
+			$this->factory->setConfig($initialConfig);
88
+			$this->factory->setErrorContext($initialContext);
89
+		}
90 90
 
91
-        // validate element against $validationSchema
92
-        $this->checkUndefined($element, $validationSchema, $path, $i);
93
-    }
91
+		// validate element against $validationSchema
92
+		$this->checkUndefined($element, $validationSchema, $path, $i);
93
+	}
94 94
 }
Please login to merge, or discard this patch.
justinrainbow/json-schema/src/JsonSchema/Constraints/ObjectConstraint.php 1 patch
Indentation   +170 added lines, -170 removed lines patch added patch discarded remove patch
@@ -19,174 +19,174 @@
 block discarded – undo
19 19
  */
20 20
 class ObjectConstraint extends Constraint
21 21
 {
22
-    /**
23
-     * @var array List of properties to which a default value has been applied
24
-     */
25
-    protected $appliedDefaults = array();
26
-
27
-    /**
28
-     * {@inheritdoc}
29
-     */
30
-    public function check(&$element, $schema = null, JsonPointer $path = null, $properties = null,
31
-        $additionalProp = null, $patternProperties = null, $appliedDefaults = array())
32
-    {
33
-        if ($element instanceof UndefinedConstraint) {
34
-            return;
35
-        }
36
-
37
-        $this->appliedDefaults = $appliedDefaults;
38
-
39
-        $matches = array();
40
-        if ($patternProperties) {
41
-            // validate the element pattern properties
42
-            $matches = $this->validatePatternProperties($element, $path, $patternProperties);
43
-        }
44
-
45
-        if ($properties) {
46
-            // validate the element properties
47
-            $this->validateProperties($element, $properties, $path);
48
-        }
49
-
50
-        // validate additional element properties & constraints
51
-        $this->validateElement($element, $matches, $schema, $path, $properties, $additionalProp);
52
-    }
53
-
54
-    public function validatePatternProperties($element, JsonPointer $path = null, $patternProperties)
55
-    {
56
-        $try = array('/', '#', '+', '~', '%');
57
-        $matches = array();
58
-        foreach ($patternProperties as $pregex => $schema) {
59
-            $delimiter = '/';
60
-            // Choose delimiter. Necessary for patterns like ^/ , otherwise you get error
61
-            foreach ($try as $delimiter) {
62
-                if (strpos($pregex, $delimiter) === false) { // safe to use
63
-                    break;
64
-                }
65
-            }
66
-
67
-            // Validate the pattern before using it to test for matches
68
-            if (@preg_match($delimiter . $pregex . $delimiter . 'u', '') === false) {
69
-                $this->addError($path, 'The pattern "' . $pregex . '" is invalid', 'pregex', array('pregex' => $pregex));
70
-                continue;
71
-            }
72
-            foreach ($element as $i => $value) {
73
-                if (preg_match($delimiter . $pregex . $delimiter . 'u', $i)) {
74
-                    $matches[] = $i;
75
-                    $this->checkUndefined($value, $schema ?: new \stdClass(), $path, $i, in_array($i, $this->appliedDefaults));
76
-                }
77
-            }
78
-        }
79
-
80
-        return $matches;
81
-    }
82
-
83
-    /**
84
-     * Validates the element properties
85
-     *
86
-     * @param \StdClass        $element        Element to validate
87
-     * @param array            $matches        Matches from patternProperties (if any)
88
-     * @param \StdClass        $schema         ObjectConstraint definition
89
-     * @param JsonPointer|null $path           Current test path
90
-     * @param \StdClass        $properties     Properties
91
-     * @param mixed            $additionalProp Additional properties
92
-     */
93
-    public function validateElement($element, $matches, $schema = null, JsonPointer $path = null,
94
-        $properties = null, $additionalProp = null)
95
-    {
96
-        $this->validateMinMaxConstraint($element, $schema, $path);
97
-
98
-        foreach ($element as $i => $value) {
99
-            $definition = $this->getProperty($properties, $i);
100
-
101
-            // no additional properties allowed
102
-            if (!in_array($i, $matches) && $additionalProp === false && $this->inlineSchemaProperty !== $i && !$definition) {
103
-                $this->addError($path, 'The property ' . $i . ' is not defined and the definition does not allow additional properties', 'additionalProp');
104
-            }
105
-
106
-            // additional properties defined
107
-            if (!in_array($i, $matches) && $additionalProp && !$definition) {
108
-                if ($additionalProp === true) {
109
-                    $this->checkUndefined($value, null, $path, $i, in_array($i, $this->appliedDefaults));
110
-                } else {
111
-                    $this->checkUndefined($value, $additionalProp, $path, $i, in_array($i, $this->appliedDefaults));
112
-                }
113
-            }
114
-
115
-            // property requires presence of another
116
-            $require = $this->getProperty($definition, 'requires');
117
-            if ($require && !$this->getProperty($element, $require)) {
118
-                $this->addError($path, 'The presence of the property ' . $i . ' requires that ' . $require . ' also be present', 'requires');
119
-            }
120
-
121
-            $property = $this->getProperty($element, $i, $this->factory->createInstanceFor('undefined'));
122
-            if (is_object($property)) {
123
-                $this->validateMinMaxConstraint(!($property instanceof UndefinedConstraint) ? $property : $element, $definition, $path);
124
-            }
125
-        }
126
-    }
127
-
128
-    /**
129
-     * Validates the definition properties
130
-     *
131
-     * @param \stdClass        $element    Element to validate
132
-     * @param \stdClass        $properties Property definitions
133
-     * @param JsonPointer|null $path       Path?
134
-     */
135
-    public function validateProperties(&$element, $properties = null, JsonPointer $path = null)
136
-    {
137
-        $undefinedConstraint = $this->factory->createInstanceFor('undefined');
138
-
139
-        foreach ($properties as $i => $value) {
140
-            $property = &$this->getProperty($element, $i, $undefinedConstraint);
141
-            $definition = $this->getProperty($properties, $i);
142
-
143
-            if (is_object($definition)) {
144
-                // Undefined constraint will check for is_object() and quit if is not - so why pass it?
145
-                $this->checkUndefined($property, $definition, $path, $i, in_array($i, $this->appliedDefaults));
146
-            }
147
-        }
148
-    }
149
-
150
-    /**
151
-     * retrieves a property from an object or array
152
-     *
153
-     * @param mixed  $element  Element to validate
154
-     * @param string $property Property to retrieve
155
-     * @param mixed  $fallback Default value if property is not found
156
-     *
157
-     * @return mixed
158
-     */
159
-    protected function &getProperty(&$element, $property, $fallback = null)
160
-    {
161
-        if (is_array($element) && (isset($element[$property]) || array_key_exists($property, $element)) /*$this->checkMode == self::CHECK_MODE_TYPE_CAST*/) {
162
-            return $element[$property];
163
-        } elseif (is_object($element) && property_exists($element, $property)) {
164
-            return $element->$property;
165
-        }
166
-
167
-        return $fallback;
168
-    }
169
-
170
-    /**
171
-     * validating minimum and maximum property constraints (if present) against an element
172
-     *
173
-     * @param \stdClass        $element          Element to validate
174
-     * @param \stdClass        $objectDefinition ObjectConstraint definition
175
-     * @param JsonPointer|null $path             Path to test?
176
-     */
177
-    protected function validateMinMaxConstraint($element, $objectDefinition, JsonPointer $path = null)
178
-    {
179
-        // Verify minimum number of properties
180
-        if (isset($objectDefinition->minProperties) && !is_object($objectDefinition->minProperties)) {
181
-            if ($this->getTypeCheck()->propertyCount($element) < $objectDefinition->minProperties) {
182
-                $this->addError($path, 'Must contain a minimum of ' . $objectDefinition->minProperties . ' properties', 'minProperties', array('minProperties' => $objectDefinition->minProperties));
183
-            }
184
-        }
185
-        // Verify maximum number of properties
186
-        if (isset($objectDefinition->maxProperties) && !is_object($objectDefinition->maxProperties)) {
187
-            if ($this->getTypeCheck()->propertyCount($element) > $objectDefinition->maxProperties) {
188
-                $this->addError($path, 'Must contain no more than ' . $objectDefinition->maxProperties . ' properties', 'maxProperties', array('maxProperties' => $objectDefinition->maxProperties));
189
-            }
190
-        }
191
-    }
22
+	/**
23
+	 * @var array List of properties to which a default value has been applied
24
+	 */
25
+	protected $appliedDefaults = array();
26
+
27
+	/**
28
+	 * {@inheritdoc}
29
+	 */
30
+	public function check(&$element, $schema = null, JsonPointer $path = null, $properties = null,
31
+		$additionalProp = null, $patternProperties = null, $appliedDefaults = array())
32
+	{
33
+		if ($element instanceof UndefinedConstraint) {
34
+			return;
35
+		}
36
+
37
+		$this->appliedDefaults = $appliedDefaults;
38
+
39
+		$matches = array();
40
+		if ($patternProperties) {
41
+			// validate the element pattern properties
42
+			$matches = $this->validatePatternProperties($element, $path, $patternProperties);
43
+		}
44
+
45
+		if ($properties) {
46
+			// validate the element properties
47
+			$this->validateProperties($element, $properties, $path);
48
+		}
49
+
50
+		// validate additional element properties & constraints
51
+		$this->validateElement($element, $matches, $schema, $path, $properties, $additionalProp);
52
+	}
53
+
54
+	public function validatePatternProperties($element, JsonPointer $path = null, $patternProperties)
55
+	{
56
+		$try = array('/', '#', '+', '~', '%');
57
+		$matches = array();
58
+		foreach ($patternProperties as $pregex => $schema) {
59
+			$delimiter = '/';
60
+			// Choose delimiter. Necessary for patterns like ^/ , otherwise you get error
61
+			foreach ($try as $delimiter) {
62
+				if (strpos($pregex, $delimiter) === false) { // safe to use
63
+					break;
64
+				}
65
+			}
66
+
67
+			// Validate the pattern before using it to test for matches
68
+			if (@preg_match($delimiter . $pregex . $delimiter . 'u', '') === false) {
69
+				$this->addError($path, 'The pattern "' . $pregex . '" is invalid', 'pregex', array('pregex' => $pregex));
70
+				continue;
71
+			}
72
+			foreach ($element as $i => $value) {
73
+				if (preg_match($delimiter . $pregex . $delimiter . 'u', $i)) {
74
+					$matches[] = $i;
75
+					$this->checkUndefined($value, $schema ?: new \stdClass(), $path, $i, in_array($i, $this->appliedDefaults));
76
+				}
77
+			}
78
+		}
79
+
80
+		return $matches;
81
+	}
82
+
83
+	/**
84
+	 * Validates the element properties
85
+	 *
86
+	 * @param \StdClass        $element        Element to validate
87
+	 * @param array            $matches        Matches from patternProperties (if any)
88
+	 * @param \StdClass        $schema         ObjectConstraint definition
89
+	 * @param JsonPointer|null $path           Current test path
90
+	 * @param \StdClass        $properties     Properties
91
+	 * @param mixed            $additionalProp Additional properties
92
+	 */
93
+	public function validateElement($element, $matches, $schema = null, JsonPointer $path = null,
94
+		$properties = null, $additionalProp = null)
95
+	{
96
+		$this->validateMinMaxConstraint($element, $schema, $path);
97
+
98
+		foreach ($element as $i => $value) {
99
+			$definition = $this->getProperty($properties, $i);
100
+
101
+			// no additional properties allowed
102
+			if (!in_array($i, $matches) && $additionalProp === false && $this->inlineSchemaProperty !== $i && !$definition) {
103
+				$this->addError($path, 'The property ' . $i . ' is not defined and the definition does not allow additional properties', 'additionalProp');
104
+			}
105
+
106
+			// additional properties defined
107
+			if (!in_array($i, $matches) && $additionalProp && !$definition) {
108
+				if ($additionalProp === true) {
109
+					$this->checkUndefined($value, null, $path, $i, in_array($i, $this->appliedDefaults));
110
+				} else {
111
+					$this->checkUndefined($value, $additionalProp, $path, $i, in_array($i, $this->appliedDefaults));
112
+				}
113
+			}
114
+
115
+			// property requires presence of another
116
+			$require = $this->getProperty($definition, 'requires');
117
+			if ($require && !$this->getProperty($element, $require)) {
118
+				$this->addError($path, 'The presence of the property ' . $i . ' requires that ' . $require . ' also be present', 'requires');
119
+			}
120
+
121
+			$property = $this->getProperty($element, $i, $this->factory->createInstanceFor('undefined'));
122
+			if (is_object($property)) {
123
+				$this->validateMinMaxConstraint(!($property instanceof UndefinedConstraint) ? $property : $element, $definition, $path);
124
+			}
125
+		}
126
+	}
127
+
128
+	/**
129
+	 * Validates the definition properties
130
+	 *
131
+	 * @param \stdClass        $element    Element to validate
132
+	 * @param \stdClass        $properties Property definitions
133
+	 * @param JsonPointer|null $path       Path?
134
+	 */
135
+	public function validateProperties(&$element, $properties = null, JsonPointer $path = null)
136
+	{
137
+		$undefinedConstraint = $this->factory->createInstanceFor('undefined');
138
+
139
+		foreach ($properties as $i => $value) {
140
+			$property = &$this->getProperty($element, $i, $undefinedConstraint);
141
+			$definition = $this->getProperty($properties, $i);
142
+
143
+			if (is_object($definition)) {
144
+				// Undefined constraint will check for is_object() and quit if is not - so why pass it?
145
+				$this->checkUndefined($property, $definition, $path, $i, in_array($i, $this->appliedDefaults));
146
+			}
147
+		}
148
+	}
149
+
150
+	/**
151
+	 * retrieves a property from an object or array
152
+	 *
153
+	 * @param mixed  $element  Element to validate
154
+	 * @param string $property Property to retrieve
155
+	 * @param mixed  $fallback Default value if property is not found
156
+	 *
157
+	 * @return mixed
158
+	 */
159
+	protected function &getProperty(&$element, $property, $fallback = null)
160
+	{
161
+		if (is_array($element) && (isset($element[$property]) || array_key_exists($property, $element)) /*$this->checkMode == self::CHECK_MODE_TYPE_CAST*/) {
162
+			return $element[$property];
163
+		} elseif (is_object($element) && property_exists($element, $property)) {
164
+			return $element->$property;
165
+		}
166
+
167
+		return $fallback;
168
+	}
169
+
170
+	/**
171
+	 * validating minimum and maximum property constraints (if present) against an element
172
+	 *
173
+	 * @param \stdClass        $element          Element to validate
174
+	 * @param \stdClass        $objectDefinition ObjectConstraint definition
175
+	 * @param JsonPointer|null $path             Path to test?
176
+	 */
177
+	protected function validateMinMaxConstraint($element, $objectDefinition, JsonPointer $path = null)
178
+	{
179
+		// Verify minimum number of properties
180
+		if (isset($objectDefinition->minProperties) && !is_object($objectDefinition->minProperties)) {
181
+			if ($this->getTypeCheck()->propertyCount($element) < $objectDefinition->minProperties) {
182
+				$this->addError($path, 'Must contain a minimum of ' . $objectDefinition->minProperties . ' properties', 'minProperties', array('minProperties' => $objectDefinition->minProperties));
183
+			}
184
+		}
185
+		// Verify maximum number of properties
186
+		if (isset($objectDefinition->maxProperties) && !is_object($objectDefinition->maxProperties)) {
187
+			if ($this->getTypeCheck()->propertyCount($element) > $objectDefinition->maxProperties) {
188
+				$this->addError($path, 'Must contain no more than ' . $objectDefinition->maxProperties . ' properties', 'maxProperties', array('maxProperties' => $objectDefinition->maxProperties));
189
+			}
190
+		}
191
+	}
192 192
 }
Please login to merge, or discard this patch.