/** * {@inheritdoc} */ public function apply($instance, stdClass $schema, Context $context, Walker $walker) { if (is_object($schema->items)) { // 8.2.3.1. If items is a schema, then the child instance must be // valid against this schema, regardless of its index, and // regardless of the value of "additionalItems". foreach ($instance as $index => $item) { $context->enterNode($index); $walker->applyConstraints($item, $schema->items, $context); $context->leaveNode(); } } else { // "items" is an array $itemSize = count($schema->items); foreach ($instance as $index => $item) { $context->enterNode($index); // 8.2.3.2. If the index is less than, or equal to, the size of // "items", the child instance must be valid against the // corresponding schema in the "items" array; otherwise, it must // be valid against the schema defined by "additionalItems". // // NOTE: this is adapted for 0-based indexation. if ($index < $itemSize) { $walker->applyConstraints($item, $schema->items[$index], $context); } elseif ($schema->additionalItems === false) { $context->addViolation('additional items are not allowed'); } else { $walker->applyConstraints($item, $schema->additionalItems, $context); } $context->leaveNode(); } } }
/** * {@inheritdoc} */ public function apply($instance, stdClass $schema, Context $context, Walker $walker) { $accumulatingContext = $context->duplicate(false); $hasMatch = false; $hasDoubleMatch = false; foreach ($schema->oneOf as $subSchema) { $subContext = $context->duplicate(false); $walker->applyConstraints($instance, $subSchema, $subContext); if ($subContext->countViolations() === 0) { if (!$hasMatch) { $hasMatch = true; } else { $hasDoubleMatch = true; break; } } else { $accumulatingContext->mergeViolations($subContext); } } if (!$hasMatch) { $context->mergeViolations($accumulatingContext); } if (!$hasMatch || $hasDoubleMatch) { $context->addViolation('instance must match exactly one of the schemas listed in oneOf'); } }
/** * {@inheritdoc} */ public function apply($instance, stdClass $schema, Context $context, Walker $walker) { $length = extension_loaded('mbstring') ? mb_strlen($instance, mb_detect_encoding($instance)) : strlen($instance); if ($length > $schema->maxLength) { $context->addViolation('should be lesser than or equal to %s characters', [$schema->maxLength]); } }
/** * {@inheritdoc} */ public function normalize(stdClass $schema, Context $context, Walker $walker) { $context->enterNode('dependencies'); if (!is_object($schema->dependencies)) { throw new InvalidTypeException($context, Types::TYPE_OBJECT); } foreach ($schema->dependencies as $property => $value) { $context->enterNode($property); if (is_object($value)) { $walker->parseSchema($value, $context); } elseif (is_array($value)) { if (0 === ($propertyCount = count($value))) { throw new EmptyArrayException($context); } foreach ($value as $index => $subProperty) { if (!is_string($subProperty)) { $context->enterNode($index); throw new InvalidTypeException($context, Types::TYPE_STRING); } } if (count(array_unique($value)) !== $propertyCount) { throw new NotUniqueException($context); } } else { throw new InvalidTypeException($context, [Types::TYPE_OBJECT, Types::TYPE_ARRAY]); } $context->leaveNode(); } $context->leaveNode(); }
/** * {@inheritdoc} */ public function apply($instance, stdClass $schema, Context $context, Walker $walker) { if (!is_string($instance)) { $context->addViolation('should be a string'); } elseif ($schema->format === 'date-time') { // PHP support for RFC3339 doesn't include fractional time // (milliseconds) so we must add another check if needed if (!$this->isDateTimeValid($instance, DATE_RFC3339) && !$this->isDateTimeValid($instance, 'Y-m-d\\TH:i:s.uP')) { $context->addViolation('should be a valid date-time (RFC3339)'); } } elseif ($schema->format === 'email') { if (!filter_var($instance, FILTER_VALIDATE_EMAIL)) { $context->addViolation('should be a valid email'); } } elseif ($schema->format === 'hostname') { if (!preg_match(self::HOSTNAME_REGEX, $instance)) { $context->addViolation('should be a valid hostname'); } } elseif ($schema->format === 'ipv4') { if (!filter_var($instance, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { $context->addViolation('should be a valid IPv4 address'); } } elseif ($schema->format === 'ipv6') { if (!filter_var($instance, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { $context->addViolation('should be a valid IPv6 address'); } } elseif ($schema->format === 'uri') { if (!preg_match(self::URI_REGEX, $instance)) { $context->addViolation('should be a valid URI (RFC3986)'); } } }
/** * {@inheritdoc} */ public function apply($instance, stdClass $schema, Context $context, Walker $walker) { $altContext = $context->duplicate(); $walker->applyConstraints($instance, $schema->not, $altContext); if ($altContext->countViolations() === $context->countViolations()) { $context->addViolation('should not match schema in "not"'); } }
/** * {@inheritdoc} */ public function apply($instance, stdClass $schema, Context $context, Walker $walker) { foreach ($schema->required as $property) { if (!property_exists($instance, $property)) { $context->addViolation('property "%s" is missing', [$property]); } } }
/** * {@inheritdoc} */ public function apply($instance, stdClass $schema, Context $context, Walker $walker) { if ($schema->exclusiveMaximum === false) { if ($instance > $schema->maximum) { $context->addViolation('should be lesser than or equal to %s', [$schema->maximum]); } } elseif ($instance >= $schema->maximum) { $context->addViolation('should be lesser than %s', [$schema->maximum]); } }
/** * {@inheritdoc} */ public function apply($instance, stdClass $schema, Context $context, Walker $walker) { $originalCount = $context->countViolations(); foreach ($schema->allOf as $subSchema) { $walker->applyConstraints($instance, $subSchema, $context); } if ($context->countViolations() > $originalCount) { $context->addViolation('instance must match all the schemas listed in allOf'); } }
/** * {@inheritdoc} */ public function apply($instance, stdClass $schema, Context $context, Walker $walker) { if (is_string($schema->type)) { if (!Types::isA($instance, $schema->type)) { $context->addViolation('instance must be of type %s', [$schema->type]); } } else { if (!Types::isOneOf($instance, $schema->type)) { $context->addViolation('instance does not match any of the expected types'); } } }
/** * {@inheritdoc} */ public function normalize(stdClass $schema, Context $context, Walker $walker) { $keyword = $this->keywords()[0]; $context->enterNode($keyword); if (!is_int($schema->{$keyword})) { throw new InvalidTypeException($context, Types::TYPE_INTEGER); } if ($schema->{$keyword} < 0) { throw new LessThanZeroException($context); } $context->leaveNode(); }
/** * {@inheritdoc} */ public function apply($instance, stdClass $schema, Context $context, Walker $walker) { if ($schema->uniqueItems === true) { foreach ($instance as $i => $aItem) { foreach ($instance as $j => $bItem) { if ($i !== $j && Utils::areEqual($aItem, $bItem)) { $context->addViolation('elements must be unique'); break 2; } } } } }
/** * @dataProvider fileDataProvider * * @param string $file * @param string $title * @param mixed $instance * @param \stdClass $schema * @param bool $isInstanceValid * @param array $expectedErrors */ public function testApply($file, $title, $instance, \stdClass $schema, $isInstanceValid, array $expectedErrors) { $constraint = $this->getConstraint(); $schemaContext = new Context(); $validationContext = new Context(); $walker = new Walker(new Registry(), new Resolver()); $pathBefore = $schemaContext->getCurrentPath(); $constraint->normalize($schema, $schemaContext, $walker); $this->assertSame($pathBefore, $schemaContext->getCurrentPath()); $constraint->apply($instance, $schema, $validationContext, $walker); $actualErrors = $validationContext->getViolations(); $this->assertValidationResult($file, $title, $instance, $schema, $isInstanceValid, $expectedErrors, $actualErrors); }
/** * {@inheritdoc} */ public function apply($instance, stdClass $schema, Context $context, Walker $walker) { $hasMatch = false; foreach ($schema->enum as $value) { if (Utils::areEqual($instance, $value)) { $hasMatch = true; break; } } if (!$hasMatch) { $context->addViolation('should match one element in enum'); } }
/** * {@inheritdoc} */ public function apply($instance, stdClass $schema, Context $context, Walker $walker) { $divider = $schema->multipleOf; $modulus = fmod($instance, $divider); $precision = abs(1.0E-10); $diff = (double) ($modulus - $divider); if (-$precision < $diff && $diff < $precision) { $fMod = 0.0; } else { $decimals1 = mb_strpos($instance, '.') ? mb_strlen($instance) - mb_strpos($instance, '.') - 1 : 0; $decimals2 = mb_strpos($divider, '.') ? mb_strlen($divider) - mb_strpos($divider, '.') - 1 : 0; $fMod = (double) round($modulus, max($decimals1, $decimals2)); } if ($fMod != 0) { $context->addViolation('should be a multiple of %s', [$divider]); } }
/** * {@inheritdoc} */ public function apply($instance, stdClass $schema, Context $context, Walker $walker) { $accumulatingContext = $context->duplicate(); $hasMatch = false; foreach ($schema->anyOf as $subSchema) { $originalCount = $accumulatingContext->countViolations(); $walker->applyConstraints($instance, $subSchema, $accumulatingContext); if ($accumulatingContext->countViolations() === $originalCount) { $hasMatch = true; break; } } if (!$hasMatch) { $context->mergeViolations($accumulatingContext); $context->addViolation('instance must match at least one of the schemas listed in anyOf'); } }
/** * {@inheritdoc} */ public function normalize(stdClass $schema, Context $context, Walker $walker) { $property = $this->keywords()[0]; $secondaryProperty = $this->keywords()[1]; if (!property_exists($schema, $property)) { throw new MissingKeywordException($context, $property); } if (!property_exists($schema, $secondaryProperty)) { $schema->{$secondaryProperty} = false; } if (!Types::isA($schema->{$property}, Types::TYPE_NUMBER)) { $context->enterNode($property); throw new InvalidTypeException($context, Types::TYPE_NUMBER); } if (!is_bool($schema->{$secondaryProperty})) { $context->enterNode($secondaryProperty); throw new InvalidTypeException($context, Types::TYPE_BOOLEAN); } }
/** * {@inheritdoc} */ public function normalize(stdClass $schema, Context $context, Walker $walker) { $keyword = $this->keywords()[0]; $context->enterNode($keyword); if (!is_array($schema->{$keyword})) { throw new InvalidTypeException($context, Types::TYPE_ARRAY); } if (count($schema->{$keyword}) === 0) { throw new EmptyArrayException($context); } foreach ($schema->{$keyword} as $index => $subSchema) { $context->enterNode($index); if (!is_object($subSchema)) { throw new InvalidTypeException($context, Types::TYPE_OBJECT); } $walker->parseSchema($subSchema, $context); $context->leaveNode(); } $context->leaveNode(); }
/** * @return string */ public function getPath() { return $this->context->getCurrentPath(); }
private function parsePatternPropertiesProperty(stdClass $schema, Context $context, Walker $walker) { if (!is_object($schema->patternProperties)) { throw new InvalidTypeException($context, Types::TYPE_OBJECT); } foreach ($schema->patternProperties as $regex => $value) { $context->enterNode($regex); if (!Utils::isValidRegex($regex)) { throw new InvalidRegexException($context); } if (!is_object($value)) { throw new InvalidTypeException($context, Types::TYPE_OBJECT); } $walker->parseSchema($value, $context); $context->leaveNode(); } }
/** * {@inheritdoc} */ public function apply($instance, stdClass $schema, Context $context, Walker $walker) { if (!Utils::matchesRegex($instance, $schema->pattern)) { $context->addViolation('should match regex "%s"', [$schema->pattern]); } }
/** * {@inheritdoc} */ public function apply($instance, stdClass $schema, Context $context, Walker $walker) { if (count(get_object_vars($instance)) < $schema->minProperties) { $context->addViolation('number of properties should be greater than or equal to %s', [$schema->minProperties]); } }
/** * {@inheritdoc} */ public function apply($instance, stdClass $schema, Context $context, Walker $walker) { if (count($instance) < $schema->minItems) { $context->addViolation('number of items should be greater than or equal to %s', [$schema->minItems]); } }