/** * Handles the web request. The response will automatically be sent to the client. * * @return void * @author Robert Lemke <*****@*****.**> */ public function handleRequest() { $request = $this->requestBuilder->build(); $response = $this->objectManager->create('F3\\FLOW3\\MVC\\Web\\Response'); $this->dispatcher->dispatch($request, $response); $response->send(); }
public function addTransaction($action, $method, $data, $tid, $packageKey, $subpackageKey) { $transaction = $this->objectManager->create('F3\\ExtJS\\ExtDirect\\Transaction', $this); $transaction->setAction($action); $transaction->setMethod($method); $transaction->setData($data); $transaction->setTid($tid); $transaction->setPackageKey($packageKey); $transaction->setSubpackageKey($subpackageKey); $this->transactions[] = $transaction; }
/** * 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); } }
/** * 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); } }
/** * 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); }
/** * @test * @expectedException \F3\FLOW3\AOP\Exception\InvalidPointcutExpressionException * @author Robert Lemke <*****@*****.**> */ public function parseDesignatorFilterThrowsAnExceptionIfACustomFilterDoesNotImplementThePointcutFilterInterface() { $mockFilter = new \ArrayObject(); $mockPointcutFilterComposite = $this->getMock('F3\\FLOW3\\AOP\\Pointcut\\PointcutFilterComposite', array(), array(), '', FALSE); $this->mockObjectManager->expects($this->once())->method('getObject')->with('F3\\Foo\\Custom\\Filter')->will($this->returnValue($mockFilter)); $parser = $this->getMock($this->buildAccessibleProxy('F3\\FLOW3\\AOP\\Pointcut\\PointcutExpressionParser'), array('dummy'), array(), '', FALSE); $parser->injectObjectManager($this->mockObjectManager); $parser->_call('parseDesignatorFilter', '&&', 'F3\\Foo\\Custom\\Filter', $mockPointcutFilterComposite); }
/** * @test * @author Robert Lemke <*****@*****.**> * @expectedException \F3\FLOW3\Object\Exception */ public function autoWiringThrowsExceptionForUnmatchedDependenciesOfRequiredSetterInjectedDependencies() { $this->mockObjectManager->expects($this->once())->method('getObject')->with('stdClass')->will($this->throwException(new \F3\FLOW3\Object\Exception())); $objectName = 'F3\\FLOW3\\Tests\\Object\\Fixture\\ClassWithUnmatchedRequiredSetterDependency'; $setterParameters = array(array('position' => 0, 'byReference' => FALSE, 'array' => FALSE, 'optional' => FALSE, 'allowsNull' => FALSE, 'class' => 'stdClass')); $this->mockReflectionService->expects($this->at(1))->method('getMethodParameters')->with($objectName, 'injectRequiredSetterArgument')->will($this->returnValue($setterParameters)); $objectConfiguration = new \F3\FLOW3\Object\Configuration\Configuration($objectName); $this->objectBuilder->createObject($objectName, $objectConfiguration); }
/** * @test * @author Bastian Waidelich <*****@*****.**> */ public function registeredRoutePartHandlerIsInvokedWhenCallingResolve() { $this->route->setUriPattern('{key1}/{key2}'); $this->route->setRoutePartsConfiguration(array('key1' => array('handler' => 'F3\\FLOW3\\MVC\\Fixture\\Web\\Routing\\MockRoutePartHandler'))); $this->routeValues = array('key2' => 'value2'); $mockRoutePartHandler = new \F3\FLOW3\MVC\Fixture\Web\Routing\MockRoutePartHandler(); $this->mockObjectManager->expects($this->once())->method('getObject')->with('F3\\FLOW3\\MVC\\Fixture\\Web\\Routing\\MockRoutePartHandler')->will($this->returnValue($mockRoutePartHandler)); $this->route->resolves($this->routeValues); $this->assertEquals('_resolve_invoked_/value2', $this->route->getMatchingUri()); }
/** * Returns the object name of the controller defined by the package key and * controller name * * @return string The controller's Object Name * @author Robert Lemke <*****@*****.**> * @author Bastian Waidelich <*****@*****.**> * @api */ public function getControllerObjectName() { $possibleObjectName = $this->controllerObjectNamePattern; $possibleObjectName = str_replace('@package', $this->controllerPackageKey, $possibleObjectName); $possibleObjectName = str_replace('@subpackage', $this->controllerSubpackageKey, $possibleObjectName); $possibleObjectName = str_replace('@controller', $this->controllerName, $possibleObjectName); $possibleObjectName = str_replace('\\\\', '\\', $possibleObjectName); $lowercaseObjectName = strtolower($possibleObjectName); $objectName = $this->objectManager->getCaseSensitiveObjectName($lowercaseObjectName); return $objectName !== FALSE ? $objectName : ''; }
/** * Returns an object of an appropriate validator for the given type. If no * validator is available NULL is returned * * @param string $validatorType Either the fully qualified class name of the validator or the short name of a built-in validator * @return string Name of the validator object or FALSE * @author Robert Lemke <*****@*****.**> */ protected function resolveValidatorObjectName($validatorType) { if ($this->objectManager->isObjectRegistered($validatorType)) { return $validatorType; } $possibleClassName = 'F3\\FLOW3\\Validation\\Validator\\' . $this->getValidatorType($validatorType) . 'Validator'; if ($this->objectManager->isObjectRegistered($possibleClassName)) { return $possibleClassName; } return FALSE; }
/** * * @param \F3\ExtJS\ExtDirect\Transaction $transaction * @return F3\FLOW3\MVC\Web\Request A request for dispatching the transaction */ protected function buildDispatchRequest(\F3\ExtJS\ExtDirect\Transaction $transaction) { $dispatchRequest = $this->objectManager->create('F3\\FLOW3\\MVC\\Web\\Request'); $dispatchRequest->injectEnvironment($this->environment); $dispatchRequest->setControllerPackageKey($transaction->getPackageKey()); $dispatchRequest->setControllerSubpackageKey($transaction->getSubpackageKey()); $dispatchRequest->setControllerName($transaction->getAction()); $dispatchRequest->setControllerActionName($transaction->getMethod()); $dispatchRequest->setFormat('extdirect'); $arguments = $this->getArgumentsFromTransaction($dispatchRequest, $transaction); $dispatchRequest->setArguments($arguments); return $dispatchRequest; }
/** * 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(); }
/** * @test * @author Robert Lemke <*****@*****.**> */ public function createRegistersShutdownObjectsAtTheComponentManager() { $className = 'SomeClass' . uniqid(); eval('class ' . $className . ' {}'); $object = new $className(); $objectName = 'F3\\Virtual\\BasicClass'; $objectConfiguration = new \F3\FLOW3\Object\Configuration\Configuration($objectName); $objectConfiguration->setScope('prototype'); $this->mockObjectManager->expects($this->once())->method('isObjectRegistered')->with($objectName)->will($this->returnValue(TRUE)); $this->mockObjectManager->expects($this->once())->method('getObjectConfiguration')->with($objectName)->will($this->returnValue($objectConfiguration)); $this->mockObjectManager->expects($this->once())->method('registerShutdownObject')->with($object, 'shutdownObject'); $this->mockObjectBuilder->expects($this->once())->method('createObject')->will($this->returnValue($object)); $this->objectFactory->create($objectName); }
/** * Factory method which creates the specified cache along with the specified kind of backend. * After creating the cache, it will be registered at the cache manager. * * @param string $cacheIdentifier The name / identifier of the cache to create * @param string $cacheObjectName Object name of the cache frontend * @param string $backendObjectName Object name of the cache backend * @param array $backendOptions (optional) Array of backend options * @return \F3\FLOW3\Cache\Frontend\FrontendInterface The created cache frontend * @author Robert Lemke <*****@*****.**> * @api */ public function create($cacheIdentifier, $cacheObjectName, $backendObjectName, array $backendOptions = array()) { $context = $this->objectManager->getContext(); $backend = $this->objectFactory->create($backendObjectName, $context, $backendOptions); if (!$backend instanceof \F3\FLOW3\Cache\Backend\BackendInterface) { throw new \F3\FLOW3\Cache\Exception\InvalidBackendException('"' . $backendObjectName . '" is not a valid cache backend object.', 1216304301); } $cache = $this->objectFactory->create($cacheObjectName, $cacheIdentifier, $backend); if (!$cache instanceof \F3\FLOW3\Cache\Frontend\FrontendInterface) { throw new \F3\FLOW3\Cache\Exception\InvalidCacheException('"' . $cacheObjectName . '" is not a valid cache frontend object.', 1216304300); } $this->cacheManager->registerCache($cache); return $cache; }
/** * 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); }
/** * @test * @author Robert Lemke <*****@*****.**> */ public function setNewValidatorConjunctionCanHandleShortValidatorNames() { $mockValidator1 = $this->getMock('F3\\FLOW3\\Validation\\Validator\\ValidatorInterface'); $mockValidator2 = $this->getMock('F3\\FLOW3\\Validation\\Validator\\ValidatorInterface'); $mockValidatorChain = $this->getMock('F3\\FLOW3\\Validation\\Validator\\ConjunctionValidator', array(), array(), '', FALSE); $mockValidatorChain->expects($this->at(0))->method('addValidator')->with($mockValidator1); $mockValidatorChain->expects($this->at(1))->method('addValidator')->with($mockValidator2); $this->mockObjectFactory->expects($this->once())->method('create')->with('F3\\FLOW3\\Validation\\Validator\\ConjunctionValidator')->will($this->returnValue($mockValidatorChain)); $this->mockObjectManager->expects($this->any())->method('isObjectRegistered')->will($this->returnValue(FALSE)); $this->mockObjectManager->expects($this->exactly(2))->method('getObject')->will($this->onConsecutiveCalls($mockValidator1, $mockValidator2)); $argument = $this->getMock($this->buildAccessibleProxy('F3\\FLOW3\\MVC\\Controller\\Argument'), array('dummy'), array(), '', FALSE); $argument->_set('objectManager', $this->mockObjectManager); $argument->_set('objectFactory', $this->mockObjectFactory); $argument->setNewValidatorConjunction(array('Validator1', 'Validator2')); }
/** * 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); } } }
/** * Creates a fresh instance of the object specified by $objectName. * * This factory method can only create objects of the scope prototype. * Singleton objects must be either injected by some type of Dependency Injection or * if that is not possible, be retrieved by the getObject() method of the * Object Manager * * You must use either Dependency Injection or this factory method for instantiation * of your objects if you need FLOW3's object management capabilities (including * AOP, Security and Persistence). It is absolutely okay and often advisable to * use the "new" operator for instantiation in your automated tests. * * @param string $objectName The name of the object to create * @return object The new object instance * @throws \InvalidArgumentException if the object name starts with a backslash * @throws \F3\FLOW3\Object\Exception\UnknownObjectException if an object with the given name does not exist * @throws \F3\FLOW3\Object\Exception\WrongScopeException if the specified object is not configured as Prototype * @author Robert Lemke <*****@*****.**> * @api */ public function create($objectName) { if ($objectName[0] === '\\') { throw new \InvalidArgumentException('The object name must not start with a backslash, "' . $objectName . '" given.', 1243272770); } if (!$this->objectManager->isObjectRegistered($objectName)) { throw new \F3\FLOW3\Object\Exception\UnknownObjectException('Object "' . $objectName . '" is not registered.', 1166550023); } $objectConfiguration = $this->objectManager->getObjectConfiguration($objectName); if ($objectConfiguration->getScope() != 'prototype') { throw new \F3\FLOW3\Object\Exception\WrongScopeException('Object "' . $objectName . '" is of scope ' . $objectConfiguration->getScope() . ' but only prototype is supported by create()', 1225385285); } $overridingArguments = self::convertArgumentValuesToArgumentObjects(array_slice(func_get_args(), 1)); $object = $this->objectBuilder->createObject($objectName, $objectConfiguration, $overridingArguments); $this->objectManager->registerShutdownObject($object, $objectConfiguration->getLifecycleShutdownMethodName()); return $object; }
/** * Maps a single record into the object it represents and registers it as * reconstituted with the session. * * @param array $objectData * @return object * @author Karsten Dambekalns <*****@*****.**> */ public function mapToObject(array $objectData) { if ($this->persistenceSession->hasIdentifier($objectData['identifier'])) { return $this->persistenceSession->getObjectByIdentifier($objectData['identifier']); } else { $className = $objectData['classname']; $classSchema = $this->reflectionService->getClassSchema($className); $objectConfiguration = $this->objectManager->getObjectConfiguration($className); $object = $this->objectBuilder->createEmptyObject($className, $objectConfiguration); $this->persistenceSession->registerObject($object, $objectData['identifier']); $this->objectBuilder->reinjectDependencies($object, $objectConfiguration); $this->thawProperties($object, $objectData['identifier'], $objectData, $classSchema); $object->FLOW3_Persistence_memorizeCleanState(); $this->persistenceSession->registerReconstitutedObject($object); return $object; } }
/** * 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()); } } } }
/** * Parses the tokens, starting at the current index and replaces the "new" * operator with a call to the object manager if the class to be instantiated * is registered as an object. * * @param array $tokens Tokens to parse * @param integer &$index Token index to start at * @param string &$targetCode Target source code for replacement * @return boolean Returns TRUE if the new operator really has been replaced, otherwise FALSE * @author Robert Lemke <*****@*****.**> */ protected function replaceNewOperator(array $tokens, &$index, &$targetCode) { $index++; $newOperatorHasBeenReplaced = FALSE; $whitespace = ''; while ($tokens[$index][0] === T_WHITESPACE) { $whitespace .= $tokens[$index][1]; $index++; } switch ($tokens[$index][0]) { case T_STRING: $className = $tokens[$index][1]; if ($tokens[$index + 1][0] === '(') { $index++; $constructorArguments = $this->parseConstructorArguments($tokens, $index); if ($this->objectManager->isObjectRegistered($className)) { $targetCode .= '$GLOBALS[\'TYPO3\']->getObjectManager()->getObject' . '(\'' . $className . '\', ' . $constructorArguments . ')' . $whitespace; $newOperatorHasBeenReplaced = TRUE; } else { $targetCode .= 'new' . $whitespace . $className . '(' . $constructorArguments . ')'; } } else { if ($this->objectManager->isObjectRegistered($className)) { $targetCode .= '$GLOBALS[\'TYPO3\']->getObjectManager()->getObject' . '(\'' . $className . '\')' . $whitespace; $newOperatorHasBeenReplaced = TRUE; } else { $targetCode .= 'new' . $whitespace . $className; } } break; case T_VARIABLE: default: $targetCode .= 'new' . $whitespace; $targetCode .= $tokens[$index][1]; } return $newOperatorHasBeenReplaced; }
/** * Initializes the AOP framework. * * During initialization the specified configuration of objects is searched for possible * aspect annotations. If an aspect class is found, the poincut expressions are parsed and * a new aspect with one or more advisors is added to the aspect registry of the AOP framework. * Finally all advices are woven into their target classes by generating proxy classes. * * The class names of all proxied classes is stored back in the $objectConfigurations array. * * @param array &$objectConfigurations * @return void * @author Robert Lemke <*****@*****.**> */ public function initialize(array &$objectConfigurations) { if ($this->isInitialized) { throw new \F3\FLOW3\AOP\Exception('The AOP framework has already been initialized!', 1169550994); } $this->isInitialized = TRUE; if ($this->proxyBuildInformationCache->has('targetAndProxyClassNames')) { $this->targetAndProxyClassNames = $this->proxyBuildInformationCache->get('targetAndProxyClassNames'); } if (!$this->proxyBuildInformationCache->has('allProxyClassesUpToDate')) { $allAvailableClassNames = $this->getAllImplementationClassesFromObjectConfigurations($objectConfigurations); $cachedTargetClassNames = $this->proxyBuildInformationCache->has('targetClassNames') ? $this->proxyBuildInformationCache->get('targetClassNames') : array(); $cachedAspectClassNames = $this->proxyBuildInformationCache->has('aspectClassNames') ? $this->proxyBuildInformationCache->get('aspectClassNames') : array(); $actualTargetClassNames = $this->getProxyableClasses($allAvailableClassNames); $actualAspectClassNames = $this->reflectionService->getClassNamesByTag('aspect'); sort($actualTargetClassNames); sort($actualAspectClassNames); $this->aspectContainers = $this->buildAspectContainers($allAvailableClassNames); $dirtyTargetClassNames = $actualTargetClassNames; if ($cachedAspectClassNames === $actualAspectClassNames) { $validProxyClassesCount = 0; $outdatedProxyClassesCount = 0; foreach ($this->targetAndProxyClassNames as $targetClassName => $proxyClassName) { if ($this->proxyClassesCache->has(str_replace('\\', '_', $proxyClassName))) { $validProxyClassesCount++; $dirtyTargetClassNames = array_diff($dirtyTargetClassNames, array($targetClassName)); } else { $outdatedProxyClassesCount++; unset($this->targetAndProxyClassNames[$targetClassName]); } } $this->systemLogger->log(sprintf('At least one target class changed, aspects unchanged. Found %s valid and %s outdated proxy classes.', $validProxyClassesCount, $outdatedProxyClassesCount), LOG_INFO); } else { $this->systemLogger->log(sprintf('At least one aspect changed, rebuilding proxy classes for %s target classes.', count($actualTargetClassNames)), LOG_INFO); $this->proxyClassesCache->flush(); $this->targetAndProxyClassNames = array(); } foreach ($dirtyTargetClassNames as $targetClassName) { $proxyBuildResult = $this->proxyClassBuilder->buildProxyClass($targetClassName, $this->aspectContainers, $this->objectManager->getContext()); if ($proxyBuildResult !== FALSE) { $this->targetAndProxyClassNames[$targetClassName] = $proxyBuildResult['proxyClassName']; $this->systemLogger->log(sprintf('Built proxy class "%s" for target class "%s" (length: %s).', $proxyBuildResult['proxyClassName'], $targetClassName, strlen($proxyBuildResult['proxyClassCode'])), LOG_DEBUG); $this->proxyClassesCache->set(str_replace('\\', '_', $proxyBuildResult['proxyClassName']), $proxyBuildResult['proxyClassCode'], array($this->proxyClassesCache->getClassTag($targetClassName))); } else { unset($this->targetAndProxyClassNames[$targetClassName]); } } $aspectClassesTags = array(); foreach ($actualAspectClassNames as $aspectClassName) { $aspectClassesTags[] = $this->proxyBuildInformationCache->getClassTag($aspectClassName); } $this->proxyBuildInformationCache->set('targetAndProxyClassNames', $this->targetAndProxyClassNames); $this->proxyBuildInformationCache->set('aspectClassNames', $actualAspectClassNames, $aspectClassesTags); $this->proxyBuildInformationCache->set('targetClassNames', $actualTargetClassNames); $this->proxyBuildInformationCache->set('allProxyClassesUpToDate', '', array($this->proxyClassesCache->getClassTag())); } foreach ($this->targetAndProxyClassNames as $targetClassName => $proxyClassName) { if (class_exists($proxyClassName, FALSE)) { throw new \F3\FLOW3\AOP\Exception('Class ' . $proxyClassName . ' already exists.', 1229361833); } if (!$this->proxyClassesCache->has(str_replace('\\', '_', $proxyClassName))) { throw new \F3\FLOW3\AOP\Exception('No proxy class code for class "' . $proxyClassName . '" found in cache.', 1229362833); } $this->proxyClassesCache->requireOnce(str_replace('\\', '_', $proxyClassName)); foreach ($objectConfigurations as $objectName => $objectConfiguration) { if ($objectConfiguration->getClassName() === $targetClassName) { $objectConfigurations[$objectName]->setClassName($proxyClassName); } } } }
/** * 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; }