/** * Flushes entries tagged by the specified tag of all registered * caches. * * @param string $tag Tag to search for * @return void * @author Robert Lemke <*****@*****.**> * @api */ public function flushCachesByTag($tag) { $this->systemLogger->log(sprintf('Flushing caches by tag "%s".', $tag), LOG_DEBUG); foreach ($this->caches as $cache) { $cache->flushByTag($tag); } }
/** * Detects changes of the files and directories to be monitored and emits signals * accordingly. * * @return void * @author Robert Lemke <*****@*****.**> * @api */ public function detectChanges() { $changedFiles = array(); $changedDirectories = array(); $changedFiles = $this->detectChangedFiles($this->monitoredFiles); foreach ($this->monitoredDirectories as $path) { if (!isset($this->directoriesAndFiles[$path])) { $this->directoriesAndFiles[$path] = \F3\FLOW3\Utility\Files::readDirectoryRecursively($path); $this->directoriesChanged = TRUE; $changedDirectories[$path] = \F3\FLOW3\Monitor\ChangeDetectionStrategy\ChangeDetectionStrategyInterface::STATUS_CREATED; } } foreach ($this->directoriesAndFiles as $path => $pathAndFilenames) { if (!is_dir($path)) { unset($this->directoriesAndFiles[$path]); $this->directoriesChanged = TRUE; $changedDirectories[$path] = \F3\FLOW3\Monitor\ChangeDetectionStrategy\ChangeDetectionStrategyInterface::STATUS_DELETED; } else { $currentSubDirectoriesAndFiles = \F3\FLOW3\Utility\Files::readDirectoryRecursively($path); if ($currentSubDirectoriesAndFiles != $pathAndFilenames) { $pathAndFilenames = array_unique(array_merge($currentSubDirectoriesAndFiles, $pathAndFilenames)); } $changedFiles = array_merge($changedFiles, $this->detectChangedFiles($pathAndFilenames)); } } if (count($changedFiles) > 0) { $this->emitFilesHaveChanged($this->identifier, $changedFiles); } if (count($changedDirectories) > 0) { $this->emitDirectoriesHaveChanged($this->identifier, $changedDirectories); } if (count($changedFiles) > 0 || count($changedDirectories) > 0) { $this->systemLogger->log(sprintf('File Monitor detected %s changed files and %s changed directories.', count($changedFiles), count($changedDirectories)), LOG_INFO); } }
/** * 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); } }
/** * Does garbage collection * * @return void * @author Karsten Dambekalns <*****@*****.**> * @api */ public function collectGarbage() { $statementHandle = $this->databaseHandle->prepare('DELETE FROM "tags" WHERE "scope"=? AND "cache"=? AND "identifier" IN (SELECT "identifier" FROM "cache" WHERE "scope"=? AND "cache"=? AND "lifetime" > 0 AND "created" + "lifetime" < ' . time() . ')'); $statementHandle->execute(array($this->scope, $this->cacheIdentifier, $this->scope, $this->cacheIdentifier)); $statementHandle = $this->databaseHandle->prepare('DELETE FROM "cache" WHERE "scope"=? AND "cache"=? AND "lifetime" > 0 AND "created" + "lifetime" < ' . time()); $statementHandle->execute(array($this->scope, $this->cacheIdentifier)); $this->systemLogger->log(sprintf('Cache %s: removed %s entries during garbage collection', $this->cacheIdentifier, $statementHandle->rowCount()), LOG_INFO); }
/** * Handles the given exception * * @param \Exception $exception The exception object * @return void */ public function handleException(\Exception $exception) { if (is_object($this->systemLogger)) { $exceptionCodeNumber = $exception->getCode() > 0 ? ' #' . $exception->getCode() : ''; $backTrace = $exception->getTrace(); $className = isset($backTrace[0]['class']) ? $backTrace[0]['class'] : '?'; $methodName = isset($backTrace[0]['function']) ? $backTrace[0]['function'] : '?'; $line = isset($backTrace['line']) ? ' in line ' . $backTrace['line'] : ''; $message = 'Uncaught exception' . $exceptionCodeNumber . '. ' . $exception->getMessage() . $line . '.'; $explodedClassName = explode('\\', $className); $packageKey = isset($explodedClassName[1]) ? $explodedClassName[1] : NULL; $this->systemLogger->log($message, LOG_CRIT, array(), $packageKey, $className, $methodName); } if (is_object($this->lockManager)) { $this->lockManager->unlockSite(); } }
/** * Builds the corresponding uri (excluding protocol and host) by iterating * through all configured routes and calling their respective resolves() * method. If no matching route is found, an empty string is returned. * Note: calls of this message are cached by RouterCachingAspect * * @param array $routeValues Key/value pairs to be resolved. E.g. array('@package' => 'MyPackage', '@controller' => 'MyController'); * @return string * @author Bastian Waidelich <*****@*****.**> */ public function resolve(array $routeValues) { $this->createRoutesFromConfiguration(); foreach ($this->routes as $route) { if ($route->resolves($routeValues)) { return $route->getMatchingUri(); } } $this->systemLogger->log('Router resolve(): No route matched ' . str_replace(chr(10), '', var_export($routeValues, TRUE)) . '.', LOG_NOTICE); return ''; }
/** * Unlocks the site if this request has locked it. * * @return void * @author Robert Lemke <*****@*****.**> * @api */ public function unlockSite() { if ($this->siteLocked === TRUE) { if (file_exists($this->lockPathAndFilename)) { unlink($this->lockPathAndFilename); } else { $this->systemLogger->log('Site is locked but no lockfile could be found.', LOG_WARNING); } $this->siteLocked = FALSE; $this->systemLogger->log('Unlocked site.', LOG_NOTICE); } }
/** * Does garbage collection * * @return void * @author Karsten Dambekalns <*****@*****.**> * @api */ public function collectGarbage() { if (!$this->cache instanceof \F3\FLOW3\Cache\Frontend\FrontendInterface) { throw new \F3\FLOW3\Cache\Exception('Yet no cache frontend has been set via setCache().', 1222686150); } $pattern = $this->cacheDirectory . 'Data/' . $this->cacheIdentifier . '/*/*/*'; $filesFound = glob($pattern); foreach ($filesFound as $cacheFilename) { if ($this->isCacheFileExpired($cacheFilename)) { $this->remove(basename($cacheFilename)); } } $this->systemLogger->log(sprintf('Cache %s: removed %s files during garbage collection', $this->cacheIdentifier, count($filesFound)), LOG_INFO); }
/** * Logs calls and results of the authenticate() method of the PersistedUsernamePasswordProvider Authentication Provider * * @afterreturning method(F3\FLOW3\Security\Authentication\Provider\PersistedUsernamePasswordProvider->authenticate()) * @param \F3\FLOW3\AOP\JoinPointInterface $joinPoint The current joinpoint * @return mixed The result of the target method if it has not been intercepted * @author Robert Lemke <*****@*****.**> */ public function logPersistedUsernamePasswordProviderAuthenticate(\F3\FLOW3\AOP\JoinPointInterface $joinPoint) { $token = $joinPoint->getMethodArgument('authenticationToken'); $credentials = $token->getCredentials(); switch ($token->getAuthenticationStatus()) { case \F3\FLOW3\Security\Authentication\TokenInterface::AUTHENTICATION_SUCCESSFUL: $this->systemLogger->log('Successfully authenticated user "' . $credentials['username'] . '".', LOG_INFO, array(), 'FLOW3', 'F3\\FLOW3\\Security\\Authentication\\Provider\\PersistedUsernamePasswordProvider', 'authenticate'); break; case \F3\FLOW3\Security\Authentication\TokenInterface::WRONG_CREDENTIALS: $this->systemLogger->log('Wrong password given for user "' . $credentials['username'] . '".', LOG_WARNING, array(), 'FLOW3', 'F3\\FLOW3\\Security\\Authentication\\Provider\\PersistedUsernamePasswordProvider', 'authenticate'); break; case \F3\FLOW3\Security\Authentication\TokenInterface::NO_CREDENTIALS_GIVEN: $this->systemLogger->log('No credentials given or no account found with username "' . $credentials['username'] . '".', LOG_WARNING, array(), 'FLOW3', 'F3\\FLOW3\\Security\\Authentication\\Provider\\PersistedUsernamePasswordProvider', 'authenticate'); break; } }
/** * Creates FLOW3's temporary directory - or at least asserts that it exists and is * writable. * * @param string $temporaryDirectoryBase Full path to the base for the temporary directory * @return string The full path to the temporary directory * @throws \F3\FLOW3\Utility\Exception if the temporary directory could not be created or is not writable * @author Robert Lemke <*****@*****.**> * @author Bastian Waidelich <*****@*****.**> */ protected function createTemporaryDirectory($temporaryDirectoryBase) { $temporaryDirectoryBase = \F3\FLOW3\Utility\Files::getUnixStylePath($temporaryDirectoryBase); if (substr($temporaryDirectoryBase, -1, 1) !== '/') { $temporaryDirectoryBase .= '/'; } $maximumPathLength = $this->getMaximumPathLength(); if (strlen($temporaryDirectoryBase) > $maximumPathLength - 230) { $this->systemLogger->log('The path to your temporary directory is ' . strlen($temporaryDirectoryBase) . ' characters long. The maximum path length of your system is only ' . $maximumPathLength . '. Please consider setting the temporaryDirectoryBase option to a shorter path.', LOG_WARNING); } $processUser = extension_loaded('posix') ? posix_getpwuid(posix_geteuid()) : array('name' => 'default'); $pathHash = substr(md5(FLOW3_PATH_WEB . $this->getSAPIName() . $processUser['name'] . $this->context), 0, 12); $temporaryDirectory = $temporaryDirectoryBase . $pathHash . '/'; if (!is_dir($temporaryDirectory)) { try { \F3\FLOW3\Utility\Files::createDirectoryRecursively($temporaryDirectory); } catch (\F3\FLOW3\Error\Exception $exception) { } } if (!is_writable($temporaryDirectory)) { throw new \F3\FLOW3\Utility\Exception('The temporary directory "' . $temporaryDirectory . '" could not be created or is not writable for the current user "' . $processUser['name'] . '". Please make this directory writable or define another temporary directory by setting the respective system environment variable (eg. TMPDIR) or defining it in the FLOW3 settings.', 1216287176); } return $temporaryDirectory; }
/** * Runs the the FLOW3 Framework by resolving an appropriate Request Handler and passing control to it. * If the Framework is not initialized yet, it will be initialized. * * @return void * @author Robert Lemke <*****@*****.**> * @api */ public function run() { if (!$this->siteLocked) { $requestHandlerResolver = $this->objectManager->getObject('F3\\FLOW3\\MVC\\RequestHandlerResolver'); $requestHandler = $requestHandlerResolver->resolveRequestHandler(); $requestHandler->handleRequest(); if ($this->settings['persistence']['enable'] === TRUE) { $this->objectManager->getObject('F3\\FLOW3\\Persistence\\ManagerInterface')->persistAll(); } $this->emitFinishedNormalRun(); $this->systemLogger->log('Shutting down ...', LOG_INFO); $this->configurationManager->shutdown(); $this->objectManager->shutdown(); $this->reflectionService->shutdown(); $this->objectManager->getObject('F3\\FLOW3\\Object\\SessionRegistry')->writeDataToSession(); $this->objectManager->getObject('F3\\FLOW3\\Session\\SessionInterface')->close(); } else { header('HTTP/1.1 503 Service Temporarily Unavailable'); readfile('package://FLOW3/Private/Core/LockHoldingStackPage.html'); $this->systemLogger->log('Site is locked, exiting.', LOG_NOTICE); } }
/** * Writes the given message along with the additional information into the log. * * @param string $message The message to log * @param integer $severity An integer value, one of the SEVERITY_* constants * @param mixed $additionalData A variable containing more information about the event to be logged * @return void * @author Robert Lemke <*****@*****.**> */ protected function log($message, $severity = 6, $additionalData = NULL) { if (is_object($this->systemLogger)) { $this->systemLogger->log($message, $severity, $additionalData); } }
/** * 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); } } } }
/** * Does nothing, as memcached does GC itself * * @return void * @api */ public function collectGarbage() { $this->systemLogger->log(sprintf('Cache %s: garbage collection is done by memcached', $this->cacheIdentifier), LOG_INFO); }