protected function generateType(PropertyInterface $type, $data) { if ($type instanceof Property\ComplexType) { $fields = array(); $properties = $type->getProperties(); foreach ($properties as $name => $property) { if (isset($data[$name])) { $fields[$name] = $this->generateType($property, $data[$name]); } elseif ($property->isRequired()) { throw new RuntimeException('Missing sample data of required property ' . $name); } } return new Record($type->getName(), $fields); } elseif ($type instanceof Property\ArrayType) { if (is_array($data)) { $values = array(); foreach ($data as $value) { $values[] = $this->generateType($type->getPrototype(), $value); } return $values; } elseif ($type->isRequired()) { throw new RuntimeException('Missing sample data of required property ' . $type->getName()); } } elseif ($type instanceof Property\BooleanType) { return (bool) $data; } elseif ($type instanceof Property\IntegerType) { return (int) $data; } elseif ($type instanceof Property\FloatType) { return (double) $data; } else { return (string) $data; } }
/** * @param \PSX\Data\Schema\PropertyInterface $property * @return $this */ public function add(PropertyInterface $property) { $this->properties[$property->getName()] = $property; return $this; }
protected function getPropertyTypeName(PropertyInterface $type) { switch ($type->getTypeName()) { case 'float': return 'number'; case 'integer': return 'integer'; case 'boolean': return 'boolean'; default: return 'string'; } }
protected function setParameterType(PropertyInterface $parameter, &$raml, $indent) { $indent = str_repeat(' ', $indent); switch (true) { case $parameter instanceof Property\IntegerType: $raml .= $indent . 'type: integer' . "\n"; break; case $parameter instanceof Property\FloatType: $raml .= $indent . 'type: number' . "\n"; break; case $parameter instanceof Property\BooleanType: $raml .= $indent . 'type: boolean' . "\n"; break; case $parameter instanceof Property\DateType: case $parameter instanceof Property\DateTimeType: $raml .= $indent . 'type: date' . "\n"; break; default: $raml .= $indent . 'type: string' . "\n"; break; } $description = $parameter->getDescription(); if (!empty($description)) { $raml .= $indent . 'description: ' . Inline::dump($parameter->getDescription()) . "\n"; } $raml .= $indent . 'required: ' . ($parameter->isRequired() ? 'true' : 'false') . "\n"; if ($parameter instanceof Property\DecimalType) { $min = $parameter->getMin(); $max = $parameter->getMax(); if ($min !== null) { $raml .= $indent . 'minimum: ' . $min . "\n"; } if ($max !== null) { $raml .= $indent . 'maximum: ' . $max . "\n"; } } elseif ($parameter instanceof Property\StringType) { $minLength = $parameter->getMinLength(); $maxLength = $parameter->getMaxLength(); $enumeration = $parameter->getEnumeration(); $pattern = $parameter->getPattern(); if ($minLength !== null) { $raml .= $indent . 'minLength: ' . $minLength . "\n"; } if ($maxLength !== null) { $raml .= $indent . 'maxLength: ' . $maxLength . "\n"; } if (!empty($enumeration)) { $raml .= $indent . 'enum: ' . Inline::dump($enumeration) . "\n"; } if (!empty($pattern)) { $raml .= $indent . 'pattern: ' . Inline::dump($pattern) . "\n"; } } }
protected function getValueDescription(PropertyInterface $type) { if ($type instanceof Property\ComplexType) { $span = '<span class="psx-property-type psx-property-type-complex"><a href="#psx-type-' . $type->getId() . '">' . $type->getName() . '</a></span>'; return [$span, null]; } elseif ($type instanceof Property\ArrayType) { $constraints = array(); $min = $type->getMinLength(); if ($min !== null) { $constraints['minimum'] = '<span class="psx-constraint-minimum">' . $min . '</span>'; } $max = $type->getMaxLength(); if ($max !== null) { $constraints['maximum'] = '<span class="psx-constraint-maximum">' . $max . '</span>'; } $constraint = $this->constraintToString($constraints); $property = $this->getValueDescription($type->getPrototype()); $span = '<span class="psx-property-type psx-property-type-array">Array<' . $property[0] . '></span>'; return [$span, $constraint]; } elseif ($type instanceof Property\ChoiceType) { $choice = array(); $properties = $type->getProperties(); foreach ($properties as $prop) { $property = $this->getValueDescription($prop); $choice[] = $property[0]; } $span = '<span class="psx-property-type psx-property-type-choice">' . implode('|', $choice) . '</span>'; return [$span, null]; } elseif ($type instanceof PropertySimpleAbstract) { $typeName = ucfirst($type->getTypeName()); $constraints = array(); if ($type->getPattern() !== null) { $constraints['pattern'] = '<span class="psx-constraint-pattern">' . $type->getPattern() . '</span>'; } if ($type->getEnumeration() !== null) { $enumeration = '<ul class="psx-property-enumeration">'; foreach ($type->getEnumeration() as $enum) { $enumeration .= '<li><span class="psx-constraint-enumeration-value">' . $enum . '</span></li>'; } $enumeration .= '</ul>'; $constraints['enumeration'] = '<span class="psx-constraint-enumeration">' . $enumeration . '</span>'; } if ($type instanceof Property\DecimalType) { $min = $type->getMin(); if ($min !== null) { $constraints['minimum'] = '<span class="psx-constraint-minimum">' . $min . '</span>'; } $max = $type->getMax(); if ($max !== null) { $constraints['maximum'] = '<span class="psx-constraint-maximum">' . $max . '</span>'; } } elseif ($type instanceof Property\StringType) { $min = $type->getMinLength(); if ($min !== null) { $constraints['minimum'] = '<span class="psx-constraint-minimum">' . $min . '</span>'; } $max = $type->getMaxLength(); if ($max !== null) { $constraints['maximum'] = '<span class="psx-constraint-maximum">' . $max . '</span>'; } } $constraint = $this->constraintToString($constraints); $cssClass = 'psx-property-type-' . strtolower($typeName); if ($type instanceof Property\DateType) { $typeName = '<a href="http://tools.ietf.org/html/rfc3339#section-5.6" title="RFC3339">Date</a>'; } elseif ($type instanceof Property\DateTimeType) { $typeName = '<a href="http://tools.ietf.org/html/rfc3339#section-5.6" title="RFC3339">DateTime</a>'; } elseif ($type instanceof Property\TimeType) { $typeName = '<a href="http://tools.ietf.org/html/rfc3339#section-5.6" title="RFC3339">Time</a>'; } elseif ($type instanceof Property\DurationType) { $typeName = '<span title="ISO 8601">Duration</span>'; } $span = '<span class="psx-property-type ' . $cssClass . '">' . $typeName . '</span>'; return [$span, $constraint]; } }
protected function createSimpleProperty($data, PropertyInterface $property) { $reference = $property->getReference(); if (!empty($reference)) { try { $class = new ReflectionClass($reference); $constructor = $class->getConstructor(); if ($constructor instanceof ReflectionMethod && $constructor->getNumberOfRequiredParameters() == 1) { return $class->newInstance($data); } } catch (ReflectionException $e) { // class does not exist } } return $data; }
protected function hasConstraints(PropertyInterface $type) { if ($type instanceof Property\CompositeTypeAbstract) { return true; } elseif ($type instanceof PropertySimpleAbstract) { if ($type instanceof Property\DecimalType) { if ($type->getMin() !== null || $type->getMax() !== null) { return true; } } elseif ($type instanceof Property\StringType) { if ($type->getMinLength() !== null || $type->getMaxLength() !== null) { return true; } } if ($type->getPattern() !== null || $type->getEnumeration() !== null) { return true; } } return false; }
protected function recTraverse($data, PropertyInterface $property, VisitorInterface $visitor, $type) { if ($property instanceof Property\ArrayType) { if (!is_array($data) && !$data instanceof Traversable) { throw new ValidationException($this->getCurrentPath() . ' must be an array'); } $result = array(); $index = 0; foreach ($data as $value) { array_push($this->pathStack, $index); $result[] = $this->recTraverse($value, $property->getPrototype(), $visitor, $type); array_pop($this->pathStack); $index++; } return $visitor->visitArray($result, $property, $this->getCurrentPath()); } elseif ($property instanceof Property\BooleanType) { return $visitor->visitBoolean($data, $property, $this->getCurrentPath()); } elseif ($property instanceof Property\ChoiceType) { $properties = $property->getProperties(); $matches = array(); foreach ($property as $index => $prop) { $value = $this->match($data, $prop); if ($value > 0) { $matches[$index] = $value; } } if (empty($matches)) { throw new ValidationException($this->getCurrentPath() . ' must be one of the following objects [' . implode(', ', array_keys($properties)) . ']'); } arsort($matches); return $this->recTraverse($data, $properties[key($matches)], $visitor, $type); } elseif ($property instanceof Property\ComplexType) { if ($type == self::TYPE_INCOMING) { if ($data instanceof \stdClass) { $data = (array) $data; } else { throw new ValidationException($this->getCurrentPath() . ' must be an object'); } } else { $data = $this->normalizeToArray($data); if (!is_array($data)) { throw new ValidationException($this->getCurrentPath() . ' must be an object'); } } $result = new \stdClass(); foreach ($property as $key => $prop) { array_push($this->pathStack, $key); if (isset($data[$key])) { $result->{$key} = $this->recTraverse($data[$key], $prop, $visitor, $type); } elseif ($prop->isRequired()) { throw new ValidationException($this->getCurrentPath() . ' is required'); } array_pop($this->pathStack); } if ($type == self::TYPE_INCOMING) { // check whether there are fields which not exist in the schema foreach ($data as $key => $value) { if (!$property->has($key)) { throw new ValidationException($this->getCurrentPath() . ' property "' . $key . '" does not exist'); } } } return $visitor->visitComplex($result, $property, $this->getCurrentPath()); } elseif ($property instanceof Property\DateTimeType) { return $visitor->visitDateTime($data, $property, $this->getCurrentPath()); } elseif ($property instanceof Property\DateType) { return $visitor->visitDate($data, $property, $this->getCurrentPath()); } elseif ($property instanceof Property\DurationType) { return $visitor->visitDuration($data, $property, $this->getCurrentPath()); } elseif ($property instanceof Property\FloatType) { return $visitor->visitFloat($data, $property, $this->getCurrentPath()); } elseif ($property instanceof Property\IntegerType) { return $visitor->visitInteger($data, $property, $this->getCurrentPath()); } elseif ($property instanceof Property\TimeType) { return $visitor->visitTime($data, $property, $this->getCurrentPath()); } elseif ($property instanceof Property\StringType) { return $visitor->visitString($data, $property, $this->getCurrentPath()); } }
protected function setParameterType(PropertyInterface $parameter, Parameter $param) { switch (true) { case $parameter instanceof Property\IntegerType: $param->setType('integer'); break; case $parameter instanceof Property\FloatType: $param->setType('number'); break; case $parameter instanceof Property\BooleanType: $param->setType('boolean'); break; case $parameter instanceof Property\DateType: $param->setType('string'); $param->setFormat('date'); break; case $parameter instanceof Property\DateTimeType: $param->setType('string'); $param->setFormat('date-time'); break; default: $param->setType('string'); break; } $param->setDescription($parameter->getDescription()); $param->setRequired($parameter->isRequired()); if ($parameter instanceof Property\DecimalType) { $param->setMinimum($parameter->getMin()); $param->setMaximum($parameter->getMax()); } elseif ($parameter instanceof Property\StringType) { $param->setMinimum($parameter->getMinLength()); $param->setMaximum($parameter->getMaxLength()); $param->setEnum($parameter->getEnumeration()); } }
protected function assertRequired($data, PropertyInterface $property, $path) { if ($property->isRequired() && $data === null) { throw new ValidationException('Property ' . $path . ' is required'); } }