Open Journal Systems  3.3.0
SchemaValidator.php
1 <?php
2 
4 
6 
11 {
13  protected static $instance;
14 
16  protected $castIntegerToStringType;
17 
19  protected $errors;
20 
25  public static function getInstance()
26  {
27  if (!self::$instance) {
28  self::$instance = new self();
29  }
30 
32  }
33 
38  public function __construct($castIntegerToStringType = true)
39  {
40  $this->castIntegerToStringType = $castIntegerToStringType;
41  }
42 
43  public function validate(Parameter $param, &$value)
44  {
45  $this->errors = array();
46  $this->recursiveProcess($param, $value);
47 
48  if (empty($this->errors)) {
49  return true;
50  } else {
51  sort($this->errors);
52  return false;
53  }
54  }
55 
61  public function getErrors()
62  {
63  return $this->errors ?: array();
64  }
65 
76  protected function recursiveProcess(Parameter $param, &$value, $path = '', $depth = 0)
77  {
78  // Update the value by adding default or static values
79  $value = $param->getValue($value);
80 
81  $required = $param->getRequired();
82  // if the value is null and the parameter is not required or is static, then skip any further recursion
83  if ((null === $value && !$required) || $param->getStatic()) {
84  return true;
85  }
86 
87  $type = $param->getType();
88  // Attempt to limit the number of times is_array is called by tracking if the value is an array
89  $valueIsArray = is_array($value);
90  // If a name is set then update the path so that validation messages are more helpful
91  if ($name = $param->getName()) {
92  $path .= "[{$name}]";
93  }
94 
95  if ($type == 'object') {
96 
97  // Objects are either associative arrays, ToArrayInterface, or some other object
98  if ($param->getInstanceOf()) {
99  $instance = $param->getInstanceOf();
100  if (!($value instanceof $instance)) {
101  $this->errors[] = "{$path} must be an instance of {$instance}";
102  return false;
103  }
104  }
105 
106  // Determine whether or not this "value" has properties and should be traversed
107  $traverse = $temporaryValue = false;
108 
109  // Convert the value to an array
110  if (!$valueIsArray && $value instanceof ToArrayInterface) {
111  $value = $value->toArray();
112  }
113 
114  if ($valueIsArray) {
115  // Ensure that the array is associative and not numerically indexed
116  if (isset($value[0])) {
117  $this->errors[] = "{$path} must be an array of properties. Got a numerically indexed array.";
118  return false;
119  }
120  $traverse = true;
121  } elseif ($value === null) {
122  // Attempt to let the contents be built up by default values if possible
123  $value = array();
124  $temporaryValue = $valueIsArray = $traverse = true;
125  }
126 
127  if ($traverse) {
128 
129  if ($properties = $param->getProperties()) {
130  // if properties were found, the validate each property of the value
131  foreach ($properties as $property) {
132  $name = $property->getName();
133  if (isset($value[$name])) {
134  $this->recursiveProcess($property, $value[$name], $path, $depth + 1);
135  } else {
136  $current = null;
137  $this->recursiveProcess($property, $current, $path, $depth + 1);
138  // Only set the value if it was populated with something
139  if (null !== $current) {
140  $value[$name] = $current;
141  }
142  }
143  }
144  }
145 
146  $additional = $param->getAdditionalProperties();
147  if ($additional !== true) {
148  // If additional properties were found, then validate each against the additionalProperties attr.
149  $keys = array_keys($value);
150  // Determine the keys that were specified that were not listed in the properties of the schema
151  $diff = array_diff($keys, array_keys($properties));
152  if (!empty($diff)) {
153  // Determine which keys are not in the properties
154  if ($additional instanceOf Parameter) {
155  foreach ($diff as $key) {
156  $this->recursiveProcess($additional, $value[$key], "{$path}[{$key}]", $depth);
157  }
158  } else {
159  // if additionalProperties is set to false and there are additionalProperties in the values, then fail
160  foreach ($diff as $prop) {
161  $this->errors[] = sprintf('%s[%s] is not an allowed property', $path, $prop);
162  }
163  }
164  }
165  }
166 
167  // A temporary value will be used to traverse elements that have no corresponding input value.
168  // This allows nested required parameters with default values to bubble up into the input.
169  // Here we check if we used a temp value and nothing bubbled up, then we need to remote the value.
170  if ($temporaryValue && empty($value)) {
171  $value = null;
172  $valueIsArray = false;
173  }
174  }
175 
176  } elseif ($type == 'array' && $valueIsArray && $param->getItems()) {
177  foreach ($value as $i => &$item) {
178  // Validate each item in an array against the items attribute of the schema
179  $this->recursiveProcess($param->getItems(), $item, $path . "[{$i}]", $depth + 1);
180  }
181  }
182 
183  // If the value is required and the type is not null, then there is an error if the value is not set
184  if ($required && $value === null && $type != 'null') {
185  $message = "{$path} is " . ($param->getType() ? ('a required ' . implode(' or ', (array) $param->getType())) : 'required');
186  if ($param->getDescription()) {
187  $message .= ': ' . $param->getDescription();
188  }
189  $this->errors[] = $message;
190  return false;
191  }
192 
193  // Validate that the type is correct. If the type is string but an integer was passed, the class can be
194  // instructed to cast the integer to a string to pass validation. This is the default behavior.
195  if ($type && (!$type = $this->determineType($type, $value))) {
196  if ($this->castIntegerToStringType && $param->getType() == 'string' && is_integer($value)) {
197  $value = (string) $value;
198  } else {
199  $this->errors[] = "{$path} must be of type " . implode(' or ', (array) $param->getType());
200  }
201  }
202 
203  // Perform type specific validation for strings, arrays, and integers
204  if ($type == 'string') {
205 
206  // Strings can have enums which are a list of predefined values
207  if (($enum = $param->getEnum()) && !in_array($value, $enum)) {
208  $this->errors[] = "{$path} must be one of " . implode(' or ', array_map(function ($s) {
209  return '"' . addslashes($s) . '"';
210  }, $enum));
211  }
212  // Strings can have a regex pattern that the value must match
213  if (($pattern = $param->getPattern()) && !preg_match($pattern, $value)) {
214  $this->errors[] = "{$path} must match the following regular expression: {$pattern}";
215  }
216 
217  $strLen = null;
218  if ($min = $param->getMinLength()) {
219  $strLen = strlen($value);
220  if ($strLen < $min) {
221  $this->errors[] = "{$path} length must be greater than or equal to {$min}";
222  }
223  }
224  if ($max = $param->getMaxLength()) {
225  if (($strLen ?: strlen($value)) > $max) {
226  $this->errors[] = "{$path} length must be less than or equal to {$max}";
227  }
228  }
229 
230  } elseif ($type == 'array') {
231 
232  $size = null;
233  if ($min = $param->getMinItems()) {
234  $size = count($value);
235  if ($size < $min) {
236  $this->errors[] = "{$path} must contain {$min} or more elements";
237  }
238  }
239  if ($max = $param->getMaxItems()) {
240  if (($size ?: count($value)) > $max) {
241  $this->errors[] = "{$path} must contain {$max} or fewer elements";
242  }
243  }
244 
245  } elseif ($type == 'integer' || $type == 'number' || $type == 'numeric') {
246  if (($min = $param->getMinimum()) && $value < $min) {
247  $this->errors[] = "{$path} must be greater than or equal to {$min}";
248  }
249  if (($max = $param->getMaximum()) && $value > $max) {
250  $this->errors[] = "{$path} must be less than or equal to {$max}";
251  }
252  }
253 
254  return empty($this->errors);
255  }
256 
265  protected function determineType($type, $value)
266  {
267  foreach ((array) $type as $t) {
268  if ($t == 'string' && (is_string($value) || (is_object($value) && method_exists($value, '__toString')))) {
269  return 'string';
270  } elseif ($t == 'object' && (is_array($value) || is_object($value))) {
271  return 'object';
272  } elseif ($t == 'array' && is_array($value)) {
273  return 'array';
274  } elseif ($t == 'integer' && is_integer($value)) {
275  return 'integer';
276  } elseif ($t == 'boolean' && is_bool($value)) {
277  return 'boolean';
278  } elseif ($t == 'number' && is_numeric($value)) {
279  return 'number';
280  } elseif ($t == 'numeric' && is_numeric($value)) {
281  return 'numeric';
282  } elseif ($t == 'null' && !$value) {
283  return 'null';
284  } elseif ($t == 'any') {
285  return 'any';
286  }
287  }
288 
289  return false;
290  }
291 }
Guzzle\Service\Description\Parameter\getItems
getItems()
Definition: Parameter.php:820
Guzzle\Service\Description\SchemaValidator\getErrors
getErrors()
Definition: SchemaValidator.php:67
Guzzle\Common\ToArrayInterface
Definition: paymethod/paypal/lib/vendor/guzzle/guzzle/src/Guzzle/Common/ToArrayInterface.php:8
Guzzle\Service\Description\SchemaValidator\recursiveProcess
recursiveProcess(Parameter $param, &$value, $path='', $depth=0)
Definition: SchemaValidator.php:82
Guzzle\Service\Description\SchemaValidator\$instance
static $instance
Definition: SchemaValidator.php:14
Guzzle\Service\Description\Parameter\getMaximum
getMaximum()
Definition: Parameter.php:394
Guzzle\Service\Description\Parameter\getMinimum
getMinimum()
Definition: Parameter.php:370
Guzzle\Service\Description\Parameter
Definition: Parameter.php:10
Guzzle\Service\Description\Parameter\getMinItems
getMinItems()
Definition: Parameter.php:490
Guzzle\Service\Description\Parameter\getValue
getValue($value)
Definition: Parameter.php:185
Guzzle\Service\Description\SchemaValidator\getInstance
static getInstance()
Definition: SchemaValidator.php:31
Guzzle\Service\Description\Parameter\getRequired
getRequired()
Definition: Parameter.php:298
Guzzle\Service\Description\Parameter\getMaxLength
getMaxLength()
Definition: Parameter.php:442
Guzzle\Service\Description\Parameter\getStatic
getStatic()
Definition: Parameter.php:605
Guzzle\Service\Description\Parameter\getPattern
getPattern()
Definition: Parameter.php:883
Guzzle\Service\Description
Definition: Operation.php:3
Seboettg\Collection\count
count()
Definition: ArrayListTrait.php:253
Guzzle\Service\Description\Parameter\getMaxItems
getMaxItems()
Definition: Parameter.php:466
Guzzle\Service\Description\Parameter\getProperties
getProperties()
Definition: Parameter.php:705
Guzzle\Service\Description\Parameter\getInstanceOf
getInstanceOf()
Definition: Parameter.php:835
Guzzle\Service\Description\Parameter\getName
getName()
Definition: Parameter.php:240
Guzzle\Service\Description\SchemaValidator\$castIntegerToStringType
$castIntegerToStringType
Definition: SchemaValidator.php:19
Guzzle\Service\Description\SchemaValidator\validate
validate(Parameter $param, &$value)
Definition: SchemaValidator.php:49
Guzzle\Service\Description\Parameter\getDescription
getDescription()
Definition: Parameter.php:346
Guzzle\Service\Description\SchemaValidator\determineType
determineType($type, $value)
Definition: SchemaValidator.php:271
Guzzle\Service\Description\ValidatorInterface
Definition: ValidatorInterface.php:8
Guzzle\Service\Description\Parameter\getAdditionalProperties
getAdditionalProperties()
Definition: Parameter.php:775
Guzzle\Service\Description\SchemaValidator
Definition: SchemaValidator.php:10
Guzzle\Service\Description\Parameter\getType
getType()
Definition: Parameter.php:274
Guzzle\Service\Description\SchemaValidator\$errors
$errors
Definition: SchemaValidator.php:25
Guzzle\Service\Description\Parameter\getMinLength
getMinLength()
Definition: Parameter.php:418
Guzzle\Service\Description\SchemaValidator\__construct
__construct($castIntegerToStringType=true)
Definition: SchemaValidator.php:44
Guzzle\Service\Description\Parameter\getEnum
getEnum()
Definition: Parameter.php:859