Overview

Namespaces

  • Contrib
    • Bundle
      • CoverallsBundle
        • Console
        • Entity
      • CoverallsV1Bundle
        • Api
        • Collector
        • Command
        • Config
        • Entity
          • Git
    • Component
      • File
      • Log
      • System
        • Git
  • Guzzle
    • Batch
      • Exception
    • Cache
    • Common
      • Exception
    • Http
      • Curl
      • Exception
      • Message
      • QueryAggregator
    • Inflection
    • Iterator
    • Log
    • Parser
      • Cookie
      • Message
      • UriTemplate
      • Url
    • Plugin
      • Async
      • Backoff
      • Cache
      • Cookie
        • CookieJar
        • Exception
      • CurlAuth
      • ErrorResponse
        • Exception
      • History
      • Log
      • Md5
      • Mock
      • Oauth
    • Service
      • Builder
      • Command
        • Factory
        • LocationVisitor
          • Request
          • Response
      • Description
      • Exception
      • Resource
    • Stream
  • PHP
  • Psr
    • Log
  • Symfony
    • Component
      • Config
        • Definition
          • Builder
          • Exception
        • Exception
        • Loader
        • Resource
        • Util
      • Console
        • Command
        • Formatter
        • Helper
        • Input
        • Output
        • Tester
      • EventDispatcher
        • Debug
      • Finder
        • Adapter
        • Comparator
        • Exception
        • Expression
        • Iterator
        • Shell
      • Stopwatch
      • Yaml
        • Exception

Classes

  • Operation
  • Parameter
  • SchemaFormatter
  • SchemaValidator
  • ServiceDescription
  • ServiceDescriptionLoader

Interfaces

  • OperationInterface
  • ServiceDescriptionInterface
  • ValidatorInterface
  • Overview
  • Namespace
  • Class
  • Tree
  • Todo
  1: <?php
  2: 
  3: namespace Guzzle\Service\Description;
  4: 
  5: use Guzzle\Common\ToArrayInterface;
  6: 
  7: /**
  8:  * Default parameter validator
  9:  */
 10: class SchemaValidator implements ValidatorInterface
 11: {
 12:     /**
 13:      * @var self Cache instance of the object
 14:      */
 15:     protected static $instance;
 16: 
 17:     /**
 18:      * @var bool Whether or not integers are converted to strings when an integer is received for a string input
 19:      */
 20:     protected $castIntegerToStringType;
 21: 
 22:     /**
 23:      * @var array Errors encountered while validating
 24:      */
 25:     protected $errors;
 26: 
 27:     /**
 28:      * Get a cached instance
 29:      *
 30:      * @return self
 31:      * @codeCoverageIgnore
 32:      */
 33:     public static function getInstance()
 34:     {
 35:         if (!self::$instance) {
 36:             self::$instance = new self();
 37:         }
 38: 
 39:         return self::$instance;
 40:     }
 41: 
 42:     /**
 43:      * @param bool $castIntegerToStringType Set to true to convert integers into strings when a required type is a
 44:      *                                      string and the input value is an integer. Defaults to true.
 45:      */
 46:     public function __construct($castIntegerToStringType = true)
 47:     {
 48:         $this->castIntegerToStringType = $castIntegerToStringType;
 49:     }
 50: 
 51:     /**
 52:      * {@inheritdoc}
 53:      */
 54:     public function validate(Parameter $param, &$value)
 55:     {
 56:         $this->errors = array();
 57:         $this->recursiveProcess($param, $value);
 58: 
 59:         if (empty($this->errors)) {
 60:             return true;
 61:         } else {
 62:             sort($this->errors);
 63:             return false;
 64:         }
 65:     }
 66: 
 67:     /**
 68:      * Get the errors encountered while validating
 69:      *
 70:      * @return array
 71:      */
 72:     public function getErrors()
 73:     {
 74:         return $this->errors ?: array();
 75:     }
 76: 
 77:     /**
 78:      * Recursively validate a parameter
 79:      *
 80:      * @param Parameter $param  API parameter being validated
 81:      * @param mixed     $value  Value to validate and validate. The value may change during this validate.
 82:      * @param string    $path   Current validation path (used for error reporting)
 83:      * @param int       $depth  Current depth in the validation validate
 84:      *
 85:      * @return bool Returns true if valid, or false if invalid
 86:      */
 87:     protected function recursiveProcess(Parameter $param, &$value, $path = '', $depth = 0)
 88:     {
 89:         // Update the value by adding default or static values
 90:         $value = $param->getValue($value);
 91: 
 92:         $required = $param->getRequired();
 93:         // if the value is null and the parameter is not required or is static, then skip any further recursion
 94:         if ((null === $value && !$required) || $param->getStatic()) {
 95:             return true;
 96:         }
 97: 
 98:         $type = $param->getType();
 99:         // Attempt to limit the number of times is_array is called by tracking if the value is an array
100:         $valueIsArray = is_array($value);
101:         // If a name is set then update the path so that validation messages are more helpful
102:         if ($name = $param->getName()) {
103:             $path .= "[{$name}]";
104:         }
105: 
106:         if ($type == 'object') {
107: 
108:             // Objects are either associative arrays, ToArrayInterface, or some other object
109:             if ($param->getInstanceOf()) {
110:                 $instance = $param->getInstanceOf();
111:                 if (!($value instanceof $instance)) {
112:                     $this->errors[] = "{$path} must be an instance of {$instance}";
113:                     return false;
114:                 }
115:             }
116: 
117:             // Determine whether or not this "value" has properties and should be traversed
118:             $traverse = $temporaryValue = false;
119: 
120:             // Convert the value to an array
121:             if (!$valueIsArray && $value instanceof ToArrayInterface) {
122:                 $value = $value->toArray();
123:             }
124: 
125:             if ($valueIsArray) {
126:                 // Ensure that the array is associative and not numerically indexed
127:                 if (isset($value[0])) {
128:                     $this->errors[] = "{$path} must be an array of properties. Got a numerically indexed array.";
129:                     return false;
130:                 }
131:                 $traverse = true;
132:             } elseif ($value === null) {
133:                 // Attempt to let the contents be built up by default values if possible
134:                 $value = array();
135:                 $temporaryValue = $valueIsArray = $traverse = true;
136:             }
137: 
138:             if ($traverse) {
139: 
140:                 if ($properties = $param->getProperties()) {
141:                     // if properties were found, the validate each property of the value
142:                     foreach ($properties as $property) {
143:                         $name = $property->getName();
144:                         if (isset($value[$name])) {
145:                             $this->recursiveProcess($property, $value[$name], $path, $depth + 1);
146:                         } else {
147:                             $current = null;
148:                             $this->recursiveProcess($property, $current, $path, $depth + 1);
149:                             // Only set the value if it was populated with something
150:                             if ($current) {
151:                                 $value[$name] = $current;
152:                             }
153:                         }
154:                     }
155:                 }
156: 
157:                 $additional = $param->getAdditionalProperties();
158:                 if ($additional !== true) {
159:                     // If additional properties were found, then validate each against the additionalProperties attr.
160:                     $keys = array_keys($value);
161:                     // Determine the keys that were specified that were not listed in the properties of the schema
162:                     $diff = array_diff($keys, array_keys($properties));
163:                     if (!empty($diff)) {
164:                         // Determine which keys are not in the properties
165:                         if ($additional instanceOf Parameter) {
166:                             foreach ($diff as $key) {
167:                                 $this->recursiveProcess($additional, $value[$key], "{$path}[{$key}]", $depth);
168:                             }
169:                         } else {
170:                             // if additionalProperties is set to false and there are additionalProperties in the values, then fail
171:                             $keys = array_keys($value);
172:                             $this->errors[] = sprintf('%s[%s] is not an allowed property', $path, reset($keys));
173:                         }
174:                     }
175:                 }
176: 
177:                 // A temporary value will be used to traverse elements that have no corresponding input value.
178:                 // This allows nested required parameters with default values to bubble up into the input.
179:                 // Here we check if we used a temp value and nothing bubbled up, then we need to remote the value.
180:                 if ($temporaryValue && empty($value)) {
181:                     $value = null;
182:                     $valueIsArray = false;
183:                 }
184:             }
185: 
186:         } elseif ($type == 'array' && $valueIsArray && $param->getItems()) {
187:             foreach ($value as $i => &$item) {
188:                 // Validate each item in an array against the items attribute of the schema
189:                 $this->recursiveProcess($param->getItems(), $item, $path . "[{$i}]", $depth + 1);
190:             }
191:         }
192: 
193:         // If the value is required and the type is not null, then there is an error if the value is not set
194:         if ($required && $value === null && $type != 'null') {
195:             $message = "{$path} is " . ($param->getType() ? ('a required ' . implode(' or ', (array) $param->getType())) : 'required');
196:             if ($param->getDescription()) {
197:                 $message .= ': ' . $param->getDescription();
198:             }
199:             $this->errors[] = $message;
200:             return false;
201:         }
202: 
203:         // Validate that the type is correct. If the type is string but an integer was passed, the class can be
204:         // instructed to cast the integer to a string to pass validation. This is the default behavior.
205:         if ($type && (!$type = $this->determineType($type, $value))) {
206:             if ($this->castIntegerToStringType && $param->getType() == 'string' && is_integer($value)) {
207:                 $value = (string) $value;
208:             } else {
209:                 $this->errors[] = "{$path} must be of type " . implode(' or ', (array) $param->getType());
210:             }
211:         }
212: 
213:         // Perform type specific validation for strings, arrays, and integers
214:         if ($type == 'string') {
215: 
216:             // Strings can have enums which are a list of predefined values
217:             if (($enum = $param->getEnum()) && !in_array($value, $enum)) {
218:                 $this->errors[] = "{$path} must be one of " . implode(' or ', array_map(function ($s) {
219:                     return '"' . addslashes($s) . '"';
220:                 }, $enum));
221:             }
222:             // Strings can have a regex pattern that the value must match
223:             if (($pattern  = $param->getPattern()) && !preg_match($pattern, $value)) {
224:                 $this->errors[] = "{$path} must match the following regular expression: {$pattern}";
225:             }
226: 
227:             $strLen = null;
228:             if ($min = $param->getMinLength()) {
229:                 $strLen = strlen($value);
230:                 if ($strLen < $min) {
231:                     $this->errors[] = "{$path} length must be greater than or equal to {$min}";
232:                 }
233:             }
234:             if ($max = $param->getMaxLength()) {
235:                 if (($strLen ?: strlen($value)) > $max) {
236:                     $this->errors[] = "{$path} length must be less than or equal to {$max}";
237:                 }
238:             }
239: 
240:         } elseif ($type == 'array') {
241: 
242:             $size = null;
243:             if ($min = $param->getMinItems()) {
244:                 $size = count($value);
245:                 if ($size < $min) {
246:                     $this->errors[] = "{$path} must contain {$min} or more elements";
247:                 }
248:             }
249:             if ($max = $param->getMaxItems()) {
250:                 if (($size ?: count($value)) > $max) {
251:                     $this->errors[] = "{$path} must contain {$max} or fewer elements";
252:                 }
253:             }
254: 
255:         } elseif ($type == 'integer' || $type == 'number' || $type == 'numeric') {
256:             if (($min = $param->getMinimum()) && $value < $min) {
257:                 $this->errors[] = "{$path} must be greater than or equal to {$min}";
258:             }
259:             if (($max = $param->getMaximum()) && $value > $max) {
260:                 $this->errors[] = "{$path} must be less than or equal to {$max}";
261:             }
262:         }
263: 
264:         return empty($this->errors);
265:     }
266: 
267:     /**
268:      * From the allowable types, determine the type that the variable matches
269:      *
270:      * @param string $type  Parameter type
271:      * @param mixed  $value Value to determine the type
272:      *
273:      * @return string|bool Returns the matching type on
274:      */
275:     protected function determineType($type, $value)
276:     {
277:         foreach ((array) $type as $t) {
278:             if ($t == 'string' && (is_string($value) || (is_object($value) && method_exists($value, '__toString')))) {
279:                 return 'string';
280:             } elseif ($t == 'object' && (is_array($value) || is_object($value))) {
281:                 return 'object';
282:             } elseif ($t == 'array' && is_array($value)) {
283:                 return 'array';
284:             } elseif ($t == 'integer' && is_integer($value)) {
285:                 return 'integer';
286:             } elseif ($t == 'boolean' && is_bool($value)) {
287:                 return 'boolean';
288:             } elseif ($t == 'number' && is_numeric($value)) {
289:                 return 'number';
290:             } elseif ($t == 'numeric' && is_numeric($value)) {
291:                 return 'numeric';
292:             } elseif ($t == 'null' && !$value) {
293:                 return 'null';
294:             } elseif ($t == 'any') {
295:                 return 'any';
296:             }
297:         }
298: 
299:         return false;
300:     }
301: }
302: 
php-coveralls API documentation generated by ApiGen 2.8.0