/** * Dispatches a signal by calling the registered Slot methods * * @param string $signalClassName Name of the class containing the signal * @param string $signalMethodName Method name of the signal * @param array $signalArguments arguments passed to the signal method * @return void * @throws \F3\FLOW3\SignalSlot\Exception\InvalidSlotException if the slot is not valid * @author Robert Lemke <*****@*****.**> * @api */ public function dispatch($signalClassName, $signalMethodName, array $signalArguments) { if (!isset($this->slots[$signalClassName][$signalMethodName])) { return; } $this->systemLogger->log(sprintf('Dispatching signal %s::%s ...', $signalClassName, $signalMethodName), LOG_DEBUG); foreach ($this->slots[$signalClassName][$signalMethodName] as $slotInformation) { if (isset($slotInformation['object'])) { $object = $slotInformation['object']; } else { if (!$this->objectManager->isObjectRegistered($slotInformation['class'])) { throw new \F3\FLOW3\SignalSlot\Exception\InvalidSlotException('The given class "' . $slotInformation['class'] . '" is not a registered object.', 1245673367); } $object = $this->objectManager->getObject($slotInformation['class']); } $slotArguments = $signalArguments; if ($slotInformation['omitSignalInformation'] !== TRUE) { array_unshift($slotArguments, $signalClassName . '::' . $signalMethodName); } $this->systemLogger->log(sprintf(' to slot %s::%s.', get_class($object), $slotInformation['method']), LOG_DEBUG); if (!method_exists($object, $slotInformation['method'])) { throw new \F3\FLOW3\SignalSlot\Exception\InvalidSlotException('The slot method ' . get_class($object) . '->' . $slotInformation['method'] . '() does not exist.', 1245673368); } call_user_func_array(array($object, $slotInformation['method']), $slotArguments); } }
/** * Commits new objects and changes to objects in the current persistence * session into the backend * * @return void * @author Robert Lemke <*****@*****.**> * @author Karsten Dambekalns <*****@*****.**> * @api */ public function persistAll() { $aggregateRootObjects = new \SplObjectStorage(); $deletedEntities = new \SplObjectStorage(); // fetch and inspect objects from all known repositories $repositoryClassNames = $this->reflectionService->getAllImplementationClassNamesForInterface('F3\\FLOW3\\Persistence\\RepositoryInterface'); foreach ($repositoryClassNames as $repositoryClassName) { $repository = $this->objectManager->getObject($repositoryClassName); $aggregateRootObjects->addAll($repository->getAddedObjects()); $deletedEntities->addAll($repository->getRemovedObjects()); } $aggregateRootObjects->addAll($this->persistenceSession->getReconstitutedObjects()); // hand in only aggregate roots, leaving handling of subobjects to // the underlying storage layer $this->backend->setAggregateRootObjects($aggregateRootObjects); $this->backend->setDeletedEntities($deletedEntities); $this->backend->commit(); // this needs to unregister more than just those, as at least some of // the subobjects are supposed to go away as well... // OTOH those do no harm, changes to the unused ones should not happen, // so all they do is eat some memory. foreach ($deletedEntities as $deletedEntity) { $this->persistenceSession->unregisterReconstitutedObject($deletedEntity); } }
/** * Adds a custom filter to the poincut filter composite * * @param string $operator The operator * @param string $filterObjectName Object Name of the custom filter (value of the designator) * @param \F3\FLOW3\AOP\Pointcut\PointcutFilterComposite $pointcutFilterComposite An instance of the pointcut filter composite. The result (ie. the custom filter) will be added to this composite object. * @return void * @author Robert Lemke <*****@*****.**> */ protected function parseDesignatorFilter($operator, $filterObjectName, \F3\FLOW3\AOP\Pointcut\PointcutFilterComposite $pointcutFilterComposite) { $customFilter = $this->objectManager->getObject($filterObjectName); if (!$customFilter instanceof \F3\FLOW3\AOP\Pointcut\PointcutFilterInterface) { throw new \F3\FLOW3\AOP\Exception\InvalidPointcutExpressionException('Invalid custom filter: "' . $filterObjectName . '" does not implement the required PoincutFilterInterface.', 1231871755); } $pointcutFilterComposite->addFilter($operator, $customFilter); }
/** * Creates and sets the configured access decision voters * * @param array $voterClassNames Array of access decision voter class names * @return void * @author Andreas Förthner <*****@*****.**> */ protected function createAccessDecisionVoters(array $voterClassNames) { foreach ($voterClassNames as $voterClassName) { if (!$this->objectManager->isObjectRegistered($voterClassName)) { throw new \F3\FLOW3\Security\Exception\VoterNotFoundException('No voter of type ' . $voterClassName . ' found!', 1222267934); } $voter = $this->objectManager->getObject($voterClassName); if (!$voter instanceof \F3\FLOW3\Security\Authorization\AccessDecisionVoterInterface) { throw new \F3\FLOW3\Security\Exception\VoterNotFoundException('The found voter class did not implement \\F3\\FLOW3\\Security\\Authorization\\AccessDecisionVoterInterface', 1222268008); } $this->accessDecisionVoters[] = $voter; } }
/** * Maps arguments delivered by the request object to the local controller arguments. * * @return void * @author Robert Lemke <*****@*****.**> */ protected function mapRequestArgumentsToControllerArguments() { $optionalArgumentNames = array(); $allArgumentNames = $this->arguments->getArgumentNames(); foreach ($allArgumentNames as $argumentName) { if ($this->arguments[$argumentName]->isRequired() === FALSE) { $optionalArgumentNames[] = $argumentName; } } $validator = $this->objectManager->getObject('F3\\FLOW3\\MVC\\Controller\\ArgumentsValidator'); $this->propertyMapper->mapAndValidate($allArgumentNames, $this->request->getArguments(), $this->arguments, $optionalArgumentNames, $validator); $this->argumentsMappingResults = $this->propertyMapper->getMappingResults(); }
/** * Analyzes the raw request and tries to find a request handler which can handle * it. If none is found, an exception is thrown. * * @return \F3\FLOW3\MVC\RequestHandler A request handler * @throws \F3\FLOW3\MVC\Exception * @author Robert Lemke <*****@*****.**> */ public function resolveRequestHandler() { $availableRequestHandlerClassNames = $this->reflectionService->getAllImplementationClassNamesForInterface('F3\\FLOW3\\MVC\\RequestHandlerInterface'); $suitableRequestHandlers = array(); foreach ($availableRequestHandlerClassNames as $requestHandlerClassName) { if (!$this->objectManager->isObjectRegistered($requestHandlerClassName)) { continue; } $requestHandler = $this->objectManager->getObject($requestHandlerClassName); if ($requestHandler->canHandleRequest()) { $priority = $requestHandler->getPriority(); if (isset($suitableRequestHandlers[$priority])) { throw new \F3\FLOW3\MVC\Exception('More than one request handler with the same priority can handle the request, but only one handler may be active at a time!', 1176475350); } $suitableRequestHandlers[$priority] = $requestHandler; } } if (count($suitableRequestHandlers) === 0) { throw new \F3\FLOW3\MVC\Exception('No suitable request handler found.', 1205414233); } ksort($suitableRequestHandlers); return array_pop($suitableRequestHandlers); }
/** * Checks, resolves and injects dependencies as properties through calling the setter methods or setting * properties directly through reflection. * * @param array $properties Array of \F3\FLOW3\Object\Configuration\ConfigurationProperty for the current object * @param object $object The recently created instance of the current object. Dependencies will be injected to it. * @return void * @author Robert Lemke <*****@*****.**> */ protected function injectProperties($properties, $object) { foreach ($properties as $propertyName => $property) { $propertyValue = $property->getValue(); switch ($property->getType()) { case \F3\FLOW3\Object\Configuration\ConfigurationProperty::PROPERTY_TYPES_OBJECT: if ($propertyValue instanceof \F3\FLOW3\Object\Configuration\Configuration) { $preparedValue = $this->createObject($propertyValue->getObjectName(), $propertyValue); } else { if (strpos($propertyValue, '.') !== FALSE) { $settingPath = explode('.', $propertyValue); $settings = $this->configurationManager->getConfiguration(\F3\FLOW3\Configuration\ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, array_shift($settingPath)); $propertyValue = \F3\FLOW3\Utility\Arrays::getValueByPath($settings, $settingPath); } $preparedValue = $this->objectManager->getObject($propertyValue); } break; case \F3\FLOW3\Object\Configuration\ConfigurationProperty::PROPERTY_TYPES_STRAIGHTVALUE: $preparedValue = $propertyValue; break; case \F3\FLOW3\Object\Configuration\ConfigurationProperty::PROPERTY_TYPES_SETTING: if (strpos($propertyValue, '.') !== FALSE) { $settingPath = explode('.', $propertyValue); $settings = $this->configurationManager->getConfiguration(\F3\FLOW3\Configuration\ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, array_shift($settingPath)); $preparedValue = \F3\FLOW3\Utility\Arrays::getValueByPath($settings, $settingPath); } else { $preparedValue = $this->configurationManager->getConfiguration(\F3\FLOW3\Configuration\ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, $propertyValue); } break; } $setterMethodName = 'inject' . ucfirst($propertyName); if (method_exists($object, $setterMethodName)) { $object->{$setterMethodName}($preparedValue); continue; } $setterMethodName = 'set' . ucfirst($propertyName); if (method_exists($object, $setterMethodName)) { $object->{$setterMethodName}($preparedValue); continue; } if (property_exists($object, $propertyName)) { $propertyReflection = new \ReflectionProperty($object, $propertyName); $propertyReflection->setAccessible(TRUE); $propertyReflection->setValue($object, $preparedValue); } } }
/** * Transforms strings with UUIDs or arrays with UUIDs/identity properties * into the requested type, if possible. * * @param mixed $propertyValue The value to transform, string or array * @param string $targetType The type to transform to * @param string $propertyName In case of an error we add this to the error message * @return object The object, when no transformation was possible this may return NULL as well */ protected function transformToObject($propertyValue, $targetType, $propertyName) { if (is_string($propertyValue) && preg_match(self::PATTERN_MATCH_UUID, $propertyValue) === 1) { $object = $this->persistenceManager->getObjectByIdentifier($propertyValue); if ($object === FALSE) { $this->mappingResults->addError($this->objectManager->getObject('F3\\FLOW3\\Error\\Error', 'Querying the repository for the specified object with UUID ' . $propertyValue . ' was not successful.', 1249379517), $propertyName); } } elseif (is_array($propertyValue)) { if (isset($propertyValue['__identity'])) { $existingObject = is_array($propertyValue['__identity']) ? $this->findObjectByIdentityProperties($propertyValue['__identity'], $targetType) : $this->persistenceManager->getObjectByIdentifier($propertyValue['__identity']); if ($existingObject === FALSE) { throw new \F3\FLOW3\Property\Exception\TargetNotFoundException('Querying the repository for the specified object was not successful.', 1237305720); } unset($propertyValue['__identity']); if (count($propertyValue) === 0) { $object = $existingObject; } elseif ($existingObject !== NULL) { $newObject = clone $existingObject; if ($this->map(array_keys($propertyValue), $propertyValue, $newObject)) { $object = $newObject; } } } else { if (isset($this->objectConverters[$targetType])) { $conversionResult = $this->objectConverters[$targetType]->convertFrom($propertyValue); if ($conversionResult instanceof \F3\FLOW3\Error\Error) { $this->mappingResults->addError($conversionResult, $propertyName); return NULL; } elseif (is_object($conversionResult) || $conversionResult === NULL) { return $conversionResult; } } $newObject = $this->objectManager->getObject($targetType); if ($this->map(array_keys($propertyValue), $propertyValue, $newObject)) { return $newObject; } throw new \F3\FLOW3\Property\Exception\InvalidTargetException('Values could not be mapped to new object of type ' . $targetType . ' for property "' . $propertyName . '". (Map errors: ' . implode(' - ', $this->mappingResults->getErrors()) . ')', 1259770027); } } else { throw new \InvalidArgumentException('transformToObject() accepts only strings and arrays.', 1251814355); } return $object; }
/** * Traverses the Tests directory of the given package and returns an * array of filenames (including path) of all files ending with "Test.php". * * @return array Filenames of all found testcase files * @author Robert Lemke <*****@*****.**> * @author Karsten Dambekalns <*****@*****.**> */ protected function getTestcaseFilenames() { $packageManager = $this->objectManager->getObject('F3\\FLOW3\\Package\\PackageManagerInterface'); $packages = array(); $testcaseFilenames = array(self::TYPE_UNIT => array(), self::TYPE_INTEGRATION => array(), self::TYPE_SYSTEM => array()); $testcaseClassNameMatches = array(); preg_match('/F3\\\\([^\\\\]*)\\\\(.*)/', $this->testcaseClassName, $testcaseClassNameMatches); if (count($testcaseClassNameMatches) === 3) { $this->testcaseClassName = $testcaseClassNameMatches[2]; if ($testcaseClassNameMatches[1] === '.*') { $packages = $packageManager->getActivePackages(); } elseif ($packageManager->isPackageActive($testcaseClassNameMatches[1])) { $packages = array($packageManager->getPackage($testcaseClassNameMatches[1])); } } elseif ($this->packageKey == '*') { $packages = $packageManager->getActivePackages(); $this->testcaseClassName = '.*Test'; } elseif ($packageManager->isPackageActive($this->packageKey)) { $packages = array($packageManager->getPackage($this->packageKey)); $this->testcaseClassName = '.*Test'; } shuffle($packages); foreach ($packages as $package) { if (in_array($package->getPackageKey(), $this->testBlacklist)) { continue; } foreach (array(self::TYPE_UNIT => \F3\FLOW3\Package\Package::DIRECTORY_TESTS_UNIT, self::TYPE_INTEGRATION => \F3\FLOW3\Package\Package::DIRECTORY_TESTS_INTEGRATION, self::TYPE_SYSTEM => \F3\FLOW3\Package\Package::DIRECTORY_TESTS_SYSTEM) as $type => $directory) { $testPath = $package->getPackagePath() . $directory; if (is_dir($testPath)) { try { $testsDirectoryIterator = new \RecursiveDirectoryIterator($testPath); $testcaseFilenames[$type] = $this->readDirectories($testcaseFilenames[$type], $testsDirectoryIterator); \PHPUnit_Util_Filter::removeDirectoryFromFilter($package->getPackagePath() . 'Classes'); } catch (\Exception $exception) { throw new \RuntimeException($exception->getMessage(), 1170236926); } } shuffle($testcaseFilenames[$type]); } } return $testcaseFilenames; }
/** * Builds the provider and token objects based on the given configuration * * @param array $providerConfigurations The configured provider settings * @return void * @author Andreas Förthner <*****@*****.**> */ protected function buildProvidersAndTokensFromConfiguration(array $providerConfigurations) { foreach ($providerConfigurations as $providerName => $providerConfiguration) { if (!is_array($providerConfiguration) || !isset($providerConfiguration['providerClass'])) { throw new \F3\FLOW3\Security\Exception\InvalidAuthenticationProviderException('The configured authentication provider "' . $providerConfiguration['providerClass'] . '" could not be found!', 1248209521); } $providerObjectName = $this->providerResolver->resolveProviderClass((string) $providerConfiguration['providerClass']); if ($providerObjectName === NULL) { throw new \F3\FLOW3\Security\Exception\InvalidAuthenticationProviderException('The configured authentication provider "' . $providerConfiguration['providerClass'] . '" could not be found!', 1237330453); } $providerOptions = array(); if (isset($providerConfiguration['options']) && is_array($providerConfiguration['options'])) { $providerOptions = $providerConfiguration['options']; } $providerInstance = $this->objectManager->getObject($providerObjectName, $providerName, $providerOptions); $this->providers[] = $providerInstance; foreach ($providerInstance->getTokenClassNames() as $tokenClassName) { $tokenInstance = $this->objectManager->getObject($tokenClassName); $tokenInstance->setAuthenticationProviderName($providerName); $this->tokens[] = $tokenInstance; } if (isset($providerConfiguration['requestPatterns']) && is_array($providerConfiguration['requestPatterns'])) { $requestPatterns = array(); foreach ($providerConfiguration['requestPatterns'] as $patternType => $patternConfiguration) { $requestPattern = $this->objectManager->getObject($this->requestPatternResolver->resolveRequestPatternClass($patternType)); $requestPattern->setPattern($patternConfiguration); $requestPatterns[] = $requestPattern; } $tokenInstance->setRequestPatterns($requestPatterns); } if (isset($providerConfiguration['entryPoint']) && is_array($providerConfiguration['entryPoint'])) { reset($providerConfiguration['entryPoint']); $entryPointObjectName = key($providerConfiguration['entryPoint']); $entryPoint = $this->objectManager->getObject($this->entryPointResolver->resolveEntryPointClass($entryPointObjectName)); $entryPoint->setOptions($providerConfiguration['entryPoint'][$entryPointObjectName]); $tokenInstance->setAuthenticationEntryPoint($entryPoint); } } }
/** * (For now) evaluates the package configuration * * @param \F3\FLOW3\Package\PackageInterface $package The package * @param array $packageConfiguration The configuration to evaluate * @return void * @author Robert Lemke <*****@*****.**> * @see initializePackages() * @todo needs refactoring and be moved to elsewhere (package manager) */ protected function evaluatePackageConfiguration(\F3\FLOW3\Package\PackageInterface $package, array $packageConfiguration) { if (isset($packageConfiguration['classLoader'])) { if (isset($packageConfiguration['classLoader']['specialClassNameAndPaths'])) { $classLoader = $this->objectManager->getObject('F3\\FLOW3\\Resource\\ClassLoader'); foreach ($packageConfiguration['classLoader']['specialClassNameAndPaths'] as $className => $classFilePathAndName) { $classFilePathAndName = str_replace('%PATH_PACKAGE%', $package->getPackagePath(), $classFilePathAndName); $classFilePathAndName = str_replace('%PATH_PACKAGE_CLASSES%', $package->getClassesPath(), $classFilePathAndName); $classFilePathAndName = str_replace('%PATH_PACKAGE_RESOURCES%', $package->getResourcesPath(), $classFilePathAndName); $classLoader->setSpecialClassNameAndPath($className, $classFilePathAndName); } } if (isset($packageConfiguration['classLoader']['includePaths'])) { foreach ($packageConfiguration['classLoader']['includePaths'] as $includePath) { $includePath = str_replace('%PATH_PACKAGE%', $package->getPackagePath(), $includePath); $includePath = str_replace('%PATH_PACKAGE_CLASSES%', $package->getClassesPath(), $includePath); $includePath = str_replace('%PATH_PACKAGE_RESOURCES%', $package->getResourcesPath(), $includePath); $includePath = str_replace('/', DIRECTORY_SEPARATOR, $includePath); set_include_path($includePath . PATH_SEPARATOR . get_include_path()); } } } }
/** * Builds a base validator conjunction for the given data type. * * The base validation rules are those which were declared directly in a class (typically * a model) through some @validate annotations on properties. * * Additionally, if a custom validator was defined for the class in question, it will be added * to the end of the conjunction. A custom validator is found if it follows the naming convention * "Replace '\Model\' by '\Validator\' and append "Validator". * * Example: $dataType is F3\Foo\Domain\Model\Quux, then the Validator will be found if it has the * name F3\Foo\Domain\Validator\QuuxValidator * * @param string $dataType The data type to build the validation conjunction for. Needs to be the fully qualified object name. * @return F3\FLOW3\Validation\Validator\ConjunctionValidator The validator conjunction or NULL * @author Robert Lemke <*****@*****.**> * @author Sebastian Kurfürst <*****@*****.**> */ protected function buildBaseValidatorConjunction($dataType) { $validatorConjunction = $this->objectManager->getObject('F3\\FLOW3\\Validation\\Validator\\ConjunctionValidator'); // Model based validator if (class_exists($dataType)) { $validatorCount = 0; $objectValidator = $this->createValidator('F3\\FLOW3\\Validation\\Validator\\GenericObjectValidator'); foreach ($this->reflectionService->getClassPropertyNames($dataType) as $classPropertyName) { $classPropertyTagsValues = $this->reflectionService->getPropertyTagsValues($dataType, $classPropertyName); if (!isset($classPropertyTagsValues['validate'])) { continue; } foreach ($classPropertyTagsValues['validate'] as $validateValue) { $parsedAnnotation = $this->parseValidatorAnnotation($validateValue); foreach ($parsedAnnotation['validators'] as $validatorConfiguration) { $newValidator = $this->createValidator($validatorConfiguration['validatorName'], $validatorConfiguration['validatorOptions']); if ($newValidator === NULL) { throw new \F3\FLOW3\Validation\Exception\NoSuchValidatorException('Invalid validate annotation in ' . $dataType . '::' . $classPropertyName . ': Could not resolve class name for validator "' . $validatorConfiguration['validatorName'] . '".', 1241098027); } $objectValidator->addPropertyValidator($classPropertyName, $newValidator); $validatorCount++; } } } if ($validatorCount > 0) { $validatorConjunction->addValidator($objectValidator); } } // Custom validator for the class $possibleValidatorClassName = str_replace('\\Model\\', '\\Validator\\', $dataType) . 'Validator'; $customValidator = $this->createValidator($possibleValidatorClassName); if ($customValidator !== NULL) { $validatorConjunction->addValidator($customValidator); } return $validatorConjunction; }
/** * Set a filter * * @param mixed $filter Object name of a filter or the actual filter object * @return \F3\FLOW3\MVC\Controller\Argument Returns $this (used for fluent interface) * @author Andreas Förthner <*****@*****.**> */ public function setFilter($filter) { $this->filter = $filter instanceof \F3\FLOW3\Validation\Filter\FilterInterface ? $filter : $this->objectManager->getObject($filter); return $this; }
/** * Iterates through all segments in $this->uriPattern and creates * appropriate RoutePart instances. * * @return void * @author Bastian Waidelich <*****@*****.**> */ public function parse() { if ($this->isParsed || $this->uriPattern === NULL || $this->uriPattern === '') { return; } $this->routeParts = array(); $currentRoutePartIsOptional = FALSE; if (substr($this->uriPattern, -1) === '/') { throw new \F3\FLOW3\MVC\Exception\InvalidUriPatternException('The URI pattern "' . $this->uriPattern . '" of route "' . $this->getName() . '" ends with a slash, which is not allowed. You can put the trailing slash in brackets to make it optional.', 1234782997); } if ($this->uriPattern[0] === '/') { throw new \F3\FLOW3\MVC\Exception\InvalidUriPatternException('The URI pattern "' . $this->uriPattern . '" of route "' . $this->getName() . '" starts with a slash, which is not allowed.', 1234782983); } $matches = array(); preg_match_all(self::PATTERN_EXTRACTROUTEPARTS, $this->uriPattern, $matches, PREG_SET_ORDER); $lastRoutePart = NULL; foreach ($matches as $match) { $routePartType = empty($match['dynamic']) ? self::ROUTEPART_TYPE_STATIC : self::ROUTEPART_TYPE_DYNAMIC; $routePartName = $match['content']; if (!empty($match['optionalStart'])) { if ($lastRoutePart !== NULL && $lastRoutePart->isOptional()) { throw new \F3\FLOW3\MVC\Exception\InvalidUriPatternException('the URI pattern "' . $this->uriPattern . '" of route "' . $this->getName() . '" contains succesive optional Route sections, which is not allowed.', 1234562050); } $currentRoutePartIsOptional = TRUE; } $routePart = NULL; switch ($routePartType) { case self::ROUTEPART_TYPE_DYNAMIC: if ($lastRoutePart instanceof \F3\FLOW3\MVC\Web\Routing\DynamicRoutePartInterface) { throw new \F3\FLOW3\MVC\Exception\InvalidUriPatternException('the URI pattern "' . $this->uriPattern . '" of route "' . $this->getName() . '" contains succesive Dynamic Route Parts, which is not allowed.', 1218446975); } if (isset($this->routePartsConfiguration[$routePartName]['handler'])) { $routePart = $this->objectManager->getObject($this->routePartsConfiguration[$routePartName]['handler']); if (!$routePart instanceof \F3\FLOW3\MVC\Web\Routing\DynamicRoutePartInterface) { throw new \F3\FLOW3\MVC\Exception\InvalidRoutePartHandlerException('routePart handlers must implement "\\F3\\FLOW3\\MVC\\Web\\Routing\\DynamicRoutePartInterface" in route "' . $this->getName() . '"', 1218480972); } } else { $routePart = $this->objectFactory->create('F3\\FLOW3\\MVC\\Web\\Routing\\DynamicRoutePart'); } if (isset($this->defaults[$routePartName])) { $routePart->setDefaultValue($this->defaults[$routePartName]); } break; case self::ROUTEPART_TYPE_STATIC: $routePart = $this->objectFactory->create('F3\\FLOW3\\MVC\\Web\\Routing\\StaticRoutePart'); if ($lastRoutePart !== NULL && $lastRoutePart instanceof \F3\FLOW3\MVC\Web\Routing\DynamicRoutePartInterface) { $lastRoutePart->setSplitString($routePartName); } } $routePart->setName($routePartName); $routePart->setOptional($currentRoutePartIsOptional); if ($this->lowerCase) { $routePart->setLowerCase(TRUE); } if (isset($this->routePartsConfiguration[$routePartName]['options'])) { $routePart->setOptions($this->routePartsConfiguration[$routePartName]['options']); } if (isset($this->routePartsConfiguration[$routePartName]['toLowerCase'])) { $routePart->setLowerCase($this->routePartsConfiguration[$routePartName]['toLowerCase']); } $this->routeParts[] = $routePart; if (!empty($match['optionalEnd'])) { if (!$currentRoutePartIsOptional) { throw new \F3\FLOW3\MVC\Exception\InvalidUriPatternException('The URI pattern "' . $this->uriPattern . '" of route "' . $this->getName() . '" contains an unopened optional section.', 1234564495); } $currentRoutePartIsOptional = FALSE; } $lastRoutePart = $routePart; } if ($currentRoutePartIsOptional) { throw new \F3\FLOW3\MVC\Exception\InvalidUriPatternException('The URI pattern "' . $this->uriPattern . '" of route "' . $this->getName() . '" contains an unterminated optional section.', 1234563922); } $this->isParsed = TRUE; }