hydrate($data); $this->required = (bool)$this->required; $this->stockLocation($data); $this->stockItemSchema($data); $this->stockProperties($data); } private function stockLocation(array $data) { $this->location = isset($data['location']) ? $data['location'] : self::DEFAULT_LOCATION; if (!AbstractParams::isSupportedLocation($this->location)) { throw new \RuntimeException(sprintf("%s is not a permitted location", $this->location)); } } private function stockItemSchema(array $data) { if (isset($data['items'])) { $this->itemSchema = new Parameter($data['items']); } } private function stockProperties(array $data) { if (isset($data['properties'])) { if (stripos($this->name, 'metadata') !== false) { $this->properties = new Parameter($data['properties']); } else { foreach ($data['properties'] as $name => $property) { $this->properties[$name] = new Parameter($property + ['name' => $name]); } } } } /** * Retrieve the name that will be used over the wire. * * @return string */ public function getName() { return $this->sentAs ?: $this->name; } /** * Indicates whether the user must provide a value for this parameter. * * @return bool */ public function isRequired() { return $this->required === true; } /** * Validates a given user value and checks whether it passes basic sanity checking, such as types. * * @param $userValues The value provided by the user * * @return bool TRUE if the validation passes * @throws \Exception If validation fails */ public function validate($userValues) { $this->validateEnums($userValues); $this->validateType($userValues); if ($this->isArray()) { $this->validateArray($userValues); } elseif ($this->isObject()) { $this->validateObject($userValues); } return true; } private function validateEnums($userValues) { if (!empty($this->enum) && $this->type == 'string' && !in_array($userValues, $this->enum)) { throw new \Exception(sprintf( 'The only permitted values are %s. You provided %s', implode(', ', $this->enum), print_r($userValues, true) )); } } private function validateType($userValues) { if (!$this->hasCorrectType($userValues)) { throw new \Exception(sprintf( 'The key provided "%s" has the wrong value type. You provided %s (%s) but was expecting %s', $this->name, print_r($userValues, true), gettype($userValues), $this->type )); } } private function validateArray($userValues) { foreach ($userValues as $userValue) { $this->itemSchema->validate($userValue); } } private function validateObject($userValues) { foreach ($userValues as $key => $userValue) { $property = $this->getNestedProperty($key); $property->validate($userValue); } } /** * Internal method which retrieves a nested property for object parameters. * * @param $key The name of the child parameter * * @returns Parameter * @throws \Exception */ private function getNestedProperty($key) { if (stripos($this->name, 'metadata') !== false && $this->properties instanceof Parameter) { return $this->properties; } elseif (isset($this->properties[$key])) { return $this->properties[$key]; } else { throw new \Exception(sprintf('The key provided "%s" is not defined', $key)); } } /** * Internal method which indicates whether the user value is of the same type as the one expected * by this parameter. * * @param $userValue The value being checked * * @return bool */ private function hasCorrectType($userValue) { // Helper fn to see whether an array is associative (i.e. a JSON object) $isAssociative = function ($value) { return is_array($value) && array_keys($value) !== range(0, count($value) - 1); }; // For params defined as objects, we'll let the user get away with // passing in an associative array - since it's effectively a hash if ($this->type == 'object' && $isAssociative($userValue)) { return true; } if (class_exists($this->type) || interface_exists($this->type)) { return is_a($userValue, $this->type); } if (!$this->type) { return true; } return gettype($userValue) == $this->type; } /** * Indicates whether this parameter represents an array type * * @return bool */ public function isArray() { return $this->type == 'array' && $this->itemSchema instanceof Parameter; } /** * Indicates whether this parameter represents an object type * * @return bool */ public function isObject() { return $this->type == 'object' && !empty($this->properties); } public function getLocation() { return $this->location; } /** * Verifies whether the given location matches the parameter's location. * * @param $value * * @return bool */ public function hasLocation($value) { return $this->location == $value; } /** * Retrieves the parameter's path. * * @return string|null */ public function getPath() { return $this->path; } /** * Retrieves the common schema that an array parameter applies to all its child elements. * * @return Parameter */ public function getItemSchema() { return $this->itemSchema; } /** * Sets the name of the parameter to a new value * * @param string $name */ public function setName($name) { $this->name = $name; } /** * Retrieves the child parameter for an object parameter. * * @param string $name The name of the child property * * @return null|Parameter */ public function getProperty($name) { if ($this->properties instanceof Parameter) { $this->properties->setName($name); return $this->properties; } return isset($this->properties[$name]) ? $this->properties[$name] : null; } /** * Retrieves the prefix for a parameter, if any. * * @return string|null */ public function getPrefix() { return $this->prefix; } public function getPrefixedName() { return $this->prefix . $this->getName(); } }