/** * @test */ public function mergeShouldMergeTwoResults() { $notice1 = $this->getMockMessage('Notice'); $notice2 = $this->getMockMessage('Notice'); $notice3 = $this->getMockMessage('Notice'); $warning1 = $this->getMockMessage('Warning'); $warning2 = $this->getMockMessage('Warning'); $warning3 = $this->getMockMessage('Warning'); $error1 = $this->getMockMessage('Error'); $error2 = $this->getMockMessage('Error'); $error3 = $this->getMockMessage('Error'); $otherResult = new Result(); $otherResult->addNotice($notice1); $otherResult->forProperty('foo.bar')->addNotice($notice2); $this->result->forProperty('foo')->addNotice($notice3); $otherResult->addWarning($warning1); $this->result->addWarning($warning2); $this->result->addWarning($warning3); $otherResult->forProperty('foo')->addError($error1); $otherResult->forProperty('foo')->addError($error2); $otherResult->addError($error3); $this->result->merge($otherResult); $this->assertSame([$notice1], $this->result->getNotices(), 'Notices are not merged correctly without recursion'); $this->assertSame([$notice3], $this->result->forProperty('foo')->getNotices(), 'Original sub-notices are overridden.'); $this->assertSame([$notice2], $this->result->forProperty('foo')->forProperty('bar')->getNotices(), 'Sub-notices are not copied.'); $this->assertSame([$warning2, $warning3, $warning1], $this->result->getWarnings()); $this->assertSame([$error3], $this->result->getErrors()); $this->assertSame([$error1, $error2], $this->result->forProperty('foo')->getErrors()); }
/** * Checks if the given value is valid according to the property validators. * * @param mixed $object The value that should be validated * @return void * @api */ protected function isValid($object) { $messages = new ErrorResult(); foreach ($this->propertyValidators as $propertyName => $validators) { $propertyValue = $this->getPropertyValue($object, $propertyName); $result = $this->checkProperty($propertyValue, $validators); if ($result !== null) { $messages->forProperty($propertyName)->merge($result); } } $this->result = $messages; }
/** * Internal function which actually does the property mapping. * * @param mixed $source the source data to map. MUST be a simple type, NO object allowed! * @param string $targetType The type of the target; can be either a class name or a simple type. * @param PropertyMappingConfigurationInterface $configuration Configuration for the property mapping. * @param array $currentPropertyPath The property path currently being mapped; used for knowing the context in case an exception is thrown. * @return mixed an instance of $targetType * @throws Exception\TypeConverterException * @throws Exception\InvalidPropertyMappingConfigurationException */ protected function doMapping($source, $targetType, PropertyMappingConfigurationInterface $configuration, &$currentPropertyPath) { if (is_object($source)) { $targetClass = TypeHandling::truncateElementType($targetType); if ($source instanceof $targetClass) { return $source; } } if ($source === null) { $source = ''; } $typeConverter = $this->findTypeConverter($source, $targetType, $configuration); $targetType = $typeConverter->getTargetTypeForSource($source, $targetType, $configuration); if (!is_object($typeConverter) || !$typeConverter instanceof TypeConverterInterface) { throw new Exception\TypeConverterException('Type converter for "' . $source . '" -> "' . $targetType . '" not found.'); } $convertedChildProperties = []; foreach ($typeConverter->getSourceChildPropertiesToBeConverted($source) as $sourcePropertyName => $sourcePropertyValue) { $targetPropertyName = $configuration->getTargetPropertyName($sourcePropertyName); if ($configuration->shouldSkip($targetPropertyName)) { continue; } if (!$configuration->shouldMap($targetPropertyName)) { if ($configuration->shouldSkipUnknownProperties()) { continue; } throw new Exception\InvalidPropertyMappingConfigurationException('It is not allowed to map property "' . $targetPropertyName . '". You need to use $propertyMappingConfiguration->allowProperties(\'' . $targetPropertyName . '\') to enable mapping of this property.', 1335969887); } $targetPropertyType = $typeConverter->getTypeOfChildProperty($targetType, $targetPropertyName, $configuration); $subConfiguration = $configuration->getConfigurationFor($targetPropertyName); $currentPropertyPath[] = $targetPropertyName; $targetPropertyValue = $this->doMapping($sourcePropertyValue, $targetPropertyType, $subConfiguration, $currentPropertyPath); array_pop($currentPropertyPath); if (!$targetPropertyValue instanceof Error) { $convertedChildProperties[$targetPropertyName] = $targetPropertyValue; } } $result = $typeConverter->convertFrom($source, $targetType, $convertedChildProperties, $configuration); if ($result instanceof Error) { $this->messages->forProperty(implode('.', $currentPropertyPath))->addError($result); } return $result; }
/** * Get all property mapping / validation errors * * @return Result */ public function getValidationResults() { $results = new Result(); foreach ($this as $argument) { $argumentValidationResults = $argument->getValidationResults(); if ($argumentValidationResults === null) { continue; } $results->forProperty($argument->getName())->merge($argumentValidationResults); } return $results; }
/** * Validate a single configuration type * * @param string $configurationType the configuration typr to validate * @param string $path configuration path to validate, or NULL. * @param array $loadedSchemaFiles will be filled with a list of loaded schema files * @return \Neos\Error\Messages\Result * @throws Exception\SchemaValidationException */ protected function validateSingleType($configurationType, $path, &$loadedSchemaFiles) { $availableConfigurationTypes = $this->configurationManager->getAvailableConfigurationTypes(); if (in_array($configurationType, $availableConfigurationTypes) === false) { throw new Exception\SchemaValidationException('The configuration type "' . $configurationType . '" was not found. Only the following configuration types are supported: "' . implode('", "', $availableConfigurationTypes) . '"', 1364984886); } $configuration = $this->configurationManager->getConfiguration($configurationType); // find schema files for the given type and path $schemaFileInfos = []; $activePackages = $this->packageManager->getActivePackages(); foreach ($activePackages as $package) { $packageKey = $package->getPackageKey(); $packageSchemaPath = Files::concatenatePaths([$package->getResourcesPath(), 'Private/Schema']); if (is_dir($packageSchemaPath)) { foreach (Files::getRecursiveDirectoryGenerator($packageSchemaPath, '.schema.yaml') as $schemaFile) { $schemaName = substr($schemaFile, strlen($packageSchemaPath) + 1, -strlen('.schema.yaml')); $schemaNameParts = explode('.', str_replace('/', '.', $schemaName), 2); $schemaType = $schemaNameParts[0]; $schemaPath = isset($schemaNameParts[1]) ? $schemaNameParts[1] : null; if ($schemaType === $configurationType && ($path === null || strpos($schemaPath, $path) === 0)) { $schemaFileInfos[] = ['file' => $schemaFile, 'name' => $schemaName, 'path' => $schemaPath, 'packageKey' => $packageKey]; } } } } if (count($schemaFileInfos) === 0) { throw new Exception\SchemaValidationException('No schema files found for configuration type "' . $configurationType . '"' . ($path !== null ? ' and path "' . $path . '".' : '.'), 1364985056); } $result = new Result(); foreach ($schemaFileInfos as $schemaFileInfo) { $loadedSchemaFiles[] = $schemaFileInfo['file']; if ($schemaFileInfo['path'] !== null) { $data = Arrays::getValueByPath($configuration, $schemaFileInfo['path']); } else { $data = $configuration; } if (empty($data)) { $result->addNotice(new Notice('No configuration found, skipping schema "%s".', 1364985445, [substr($schemaFileInfo['file'], strlen(FLOW_PATH_ROOT))])); } else { $parsedSchema = Yaml::parse($schemaFileInfo['file']); $validationResultForSingleSchema = $this->schemaValidator->validate($data, $parsedSchema); if ($schemaFileInfo['path'] !== null) { $result->forProperty($schemaFileInfo['path'])->merge($validationResultForSingleSchema); } else { $result->merge($validationResultForSingleSchema); } } } return $result; }
/** * Validate a dictionary value with the given schema * * The following properties are handled in given $schema: * - properties : array of keys and schemas that have to validate * - formatProperties : dictionary of schemas, the schemas are used to validate all keys that match the string-format * - patternProperties : dictionary of schemas, the schemas are used to validate all keys that match the string-pattern * - additionalProperties : if FALSE is given all additionalProperties are forbidden, if a schema is given all additional properties have to match the schema * * @param mixed $value * @param array $schema * @return ErrorResult */ protected function validateDictionaryType($value, array $schema) { $result = new ErrorResult(); if (is_array($value) === false || $this->isDictionary($value) === false) { $result->addError($this->createError('type=dictionary', 'type=' . gettype($value))); return $result; } $propertyKeysToHandle = array_keys($value); if (isset($schema['properties'])) { foreach ($schema['properties'] as $propertyName => $propertySchema) { if (array_key_exists($propertyName, $value)) { $propertyValue = $value[$propertyName]; $subresult = $this->validate($propertyValue, $propertySchema); if ($subresult->hasErrors()) { $result->forProperty($propertyName)->merge($subresult); } $propertyKeysToHandle = array_diff($propertyKeysToHandle, [$propertyName]); } else { // is subproperty required if (is_array($propertySchema) && $this->isSchema($propertySchema) && isset($propertySchema['required']) && $propertySchema['required'] === true) { $result->addError($this->createError('Property ' . $propertyName . ' is required')); } } } } if (isset($schema['patternProperties']) && count($propertyKeysToHandle) > 0 && $this->isDictionary($schema['patternProperties'])) { foreach (array_values($propertyKeysToHandle) as $propertyKey) { foreach ($schema['patternProperties'] as $propertyPattern => $propertySchema) { $keyResult = $this->validateStringType($propertyKey, ['pattern' => $propertyPattern]); if ($keyResult->hasErrors() === false) { $subresult = $this->validate($value[$propertyKey], $propertySchema); if ($subresult->hasErrors()) { $result->forProperty($propertyKey)->merge($subresult); } $propertyKeysToHandle = array_diff($propertyKeysToHandle, [$propertyKey]); } } } } if (isset($schema['formatProperties']) && count($propertyKeysToHandle) > 0 && $this->isDictionary($schema['formatProperties'])) { foreach (array_values($propertyKeysToHandle) as $propertyKey) { foreach ($schema['formatProperties'] as $propertyPattern => $propertySchema) { $keyResult = $this->validateStringType($propertyKey, ['format' => $propertyPattern]); if ($keyResult->hasErrors() === false) { $subresult = $this->validate($value[$propertyKey], $propertySchema); if ($subresult->hasErrors()) { $result->forProperty($propertyKey)->merge($subresult); } $propertyKeysToHandle = array_diff($propertyKeysToHandle, [$propertyKey]); } } } } if (isset($schema['additionalProperties']) && $schema['additionalProperties'] !== true && count($propertyKeysToHandle) > 0) { if ($schema['additionalProperties'] === false) { foreach ($propertyKeysToHandle as $propertyKey) { $result->forProperty($propertyKey)->addError($this->createError('This property is not allowed here, check the spelling if you think it belongs here.')); } } else { foreach ($propertyKeysToHandle as $propertyKey) { $subresult = $this->validate($value[$propertyKey], $schema['additionalProperties']); if ($subresult->hasErrors()) { $result->forProperty($propertyKey)->merge($subresult); } } } } return $result; }