/** * Checks the cache for the route path given in the Request and returns the result * * @param Request $httpRequest * @return array|boolean the cached route values or FALSE if no cache entry was found */ public function getCachedMatchResults(Request $httpRequest) { $cachedResult = $this->routeCache->get($this->buildRouteCacheIdentifier($httpRequest)); if ($cachedResult !== false) { $this->systemLogger->log(sprintf('Router route(): A cached Route with the cache identifier "%s" matched the path "%s".', $this->buildRouteCacheIdentifier($httpRequest), $httpRequest->getRelativePath()), LOG_DEBUG); } return $cachedResult; }
/** * Before advice for all methods annotated with "@Flow\Session(autoStart=true)". * Those methods will trigger a session initialization if a session does not exist * yet. * * @param JoinPointInterface $joinPoint The current join point * @return void * @fixme The pointcut expression below does not consider the options of the session annotation – needs adjustments in the AOP framework * @Flow\Before("methodAnnotatedWith(Neos\Flow\Annotations\Session)") */ public function initializeSession(JoinPointInterface $joinPoint) { if ($this->session->isStarted() === true) { return; } $objectName = $this->objectManager->getObjectNameByClassName(get_class($joinPoint->getProxy())); $methodName = $joinPoint->getMethodName(); $this->systemLogger->log(sprintf('Session initialization triggered by %s->%s.', $objectName, $methodName), LOG_DEBUG); $this->session->start(); }
/** * @param array $redirects * @return void * @throws Exception */ public function emitRedirectCreated(array $redirects) { foreach ($redirects as $redirect) { if (!$redirect instanceof RedirectInterface) { throw new Exception('Redirect should implement RedirectInterface', 1460139669); } $this->_redirectService->emitRedirectCreated($redirect); $this->_logger->log(sprintf('Redirect from %s %s -> %s (%d) added', $redirect->getHost(), $redirect->getSourceUriPath(), $redirect->getTargetUriPath(), $redirect->getStatusCode()), LOG_DEBUG); } }
/** * Iterates over all existing sessions and removes their data if the inactivity * timeout was reached. * * @return integer The number of outdated entries removed * @api */ public function collectGarbage() { if ($this->inactivityTimeout === 0) { return 0; } if ($this->metaDataCache->has('_garbage-collection-running')) { return false; } $sessionRemovalCount = 0; $this->metaDataCache->set('_garbage-collection-running', true, [], 120); foreach ($this->metaDataCache->getIterator() as $sessionIdentifier => $sessionInfo) { if ($sessionIdentifier === '_garbage-collection-running') { continue; } $lastActivitySecondsAgo = $this->now - $sessionInfo['lastActivityTimestamp']; if ($lastActivitySecondsAgo > $this->inactivityTimeout) { if ($sessionInfo['storageIdentifier'] === null) { $this->systemLogger->log('SESSION INFO INVALID: ' . $sessionIdentifier, LOG_WARNING, $sessionInfo); } else { $this->storageCache->flushByTag($sessionInfo['storageIdentifier']); $sessionRemovalCount++; } $this->metaDataCache->remove($sessionIdentifier); } if ($sessionRemovalCount >= $this->garbageCollectionMaximumPerRun) { break; } } $this->metaDataCache->remove('_garbage-collection-running'); return $sessionRemovalCount; }
/** * Detects changes of the files and directories to be monitored and emits signals * accordingly. * * @return void * @api */ public function detectChanges() { if ($this->changedFiles === null || $this->changedPaths === null) { $this->loadDetectedDirectoriesAndFiles(); $changesDetected = false; $this->changedPaths = $this->changedFiles = []; $this->changedFiles = $this->detectChangedFiles($this->monitoredFiles); foreach ($this->monitoredDirectories as $path => $filenamePattern) { $changesDetected = $this->detectChangesOnPath($path, $filenamePattern) ? true : $changesDetected; } if ($changesDetected) { $this->saveDetectedDirectoriesAndFiles(); } $this->directoriesAndFiles = null; } $changedFileCount = count($this->changedFiles); $changedPathCount = count($this->changedPaths); if ($changedFileCount > 0) { $this->emitFilesHaveChanged($this->identifier, $this->changedFiles); } if ($changedPathCount > 0) { $this->emitDirectoriesHaveChanged($this->identifier, $this->changedPaths); } if ($changedFileCount > 0 || $changedPathCount) { $this->systemLogger->log(sprintf('File Monitor "%s" detected %s changed files and %s changed directories.', $this->identifier, $changedFileCount, $changedPathCount), LOG_INFO); } }
/** * Log a deprecation message once * * @return void */ protected function logDeprecation() { if (!static::$loggedDeprecation) { static::$loggedDeprecation = true; $this->logger->log('Neos.Media is configured to simulate the deprecated Neos 1.2 behaviour. Please check the setting "Neos.Media.behaviourFlag".', LOG_DEBUG); } }
/** * Checks if the specified method matches with the method annotation filter pattern * * @param string $className Name of the class to check against - not used here * @param string $methodName Name of the method * @param string $methodDeclaringClassName Name of the class the method was originally declared in * @param mixed $pointcutQueryIdentifier Some identifier for this query - must at least differ from a previous identifier. Used for circular reference detection - not used here * @return boolean TRUE if the class matches, otherwise FALSE */ public function matches($className, $methodName, $methodDeclaringClassName, $pointcutQueryIdentifier) { if ($methodDeclaringClassName === null || !method_exists($methodDeclaringClassName, $methodName)) { return false; } $designatedAnnotations = $this->reflectionService->getMethodAnnotations($methodDeclaringClassName, $methodName, $this->annotation); if ($designatedAnnotations !== [] || $this->annotationValueConstraints === []) { $matches = $designatedAnnotations !== []; } else { // It makes no sense to check property values for an annotation that is used multiple times, we shortcut and check the value against the first annotation found. $firstFoundAnnotation = $designatedAnnotations; $annotationProperties = $this->reflectionService->getClassPropertyNames($this->annotation); foreach ($this->annotationValueConstraints as $propertyName => $expectedValue) { if (!array_key_exists($propertyName, $annotationProperties)) { $this->systemLogger->log('The property "' . $propertyName . '" declared in pointcut does not exist in annotation ' . $this->annotation, LOG_NOTICE); return false; } if ($firstFoundAnnotation->{$propertyName} === $expectedValue) { $matches = true; } else { return false; } } } return $matches; }
/** * Checks if the specified method matches against the method name * expression. * * Returns TRUE if method name, visibility and arguments constraints match and the target * method is not final. * * @param string $className Ignored in this pointcut filter * @param string $methodName Name of the method to match against * @param string $methodDeclaringClassName Name of the class the method was originally declared in * @param mixed $pointcutQueryIdentifier Some identifier for this query - must at least differ from a previous identifier. Used for circular reference detection. * @return boolean TRUE if the class matches, otherwise FALSE * @throws Exception */ public function matches($className, $methodName, $methodDeclaringClassName, $pointcutQueryIdentifier) { $matchResult = preg_match('/^' . $this->methodNameFilterExpression . '$/', $methodName); if ($matchResult === false) { throw new Exception('Error in regular expression', 1168876915); } elseif ($matchResult !== 1) { return false; } switch ($this->methodVisibility) { case 'public': if (!($methodDeclaringClassName !== null && $this->reflectionService->isMethodPublic($methodDeclaringClassName, $methodName))) { return false; } break; case 'protected': if (!($methodDeclaringClassName !== null && $this->reflectionService->isMethodProtected($methodDeclaringClassName, $methodName))) { return false; } break; } if ($methodDeclaringClassName !== null && $this->reflectionService->isMethodFinal($methodDeclaringClassName, $methodName)) { return false; } $methodArguments = $methodDeclaringClassName === null ? [] : $this->reflectionService->getMethodParameters($methodDeclaringClassName, $methodName); foreach (array_keys($this->methodArgumentConstraints) as $argumentName) { $objectAccess = explode('.', $argumentName, 2); $argumentName = $objectAccess[0]; if (!array_key_exists($argumentName, $methodArguments)) { $this->systemLogger->log('The argument "' . $argumentName . '" declared in pointcut does not exist in method ' . $methodDeclaringClassName . '->' . $methodName, LOG_NOTICE); return false; } } return true; }
/** * Filters the classnames available for object management by filter expressions that either include or exclude classes. * * @param array $classNames All classnames per package * @param array $filterConfiguration The filter configuration to apply * @param string $includeOrExclude if this is an "include" or "exclude" filter * @return array the remaining class * @throws InvalidConfigurationTypeException */ protected function applyClassFilterConfiguration($classNames, $filterConfiguration, $includeOrExclude = 'include') { if (!in_array($includeOrExclude, ['include', 'exclude'])) { throw new \InvalidArgumentException('The argument $includeOrExclude must be one of "include" or "exclude", the given value was not allowed.', 1423726253); } foreach ($filterConfiguration as $packageKey => $filterExpressions) { if (!array_key_exists($packageKey, $classNames)) { $this->systemLogger->log('The package "' . $packageKey . '" specified in the setting "Neos.Flow.object.' . $includeOrExclude . 'Classes" was either excluded or is not loaded.', LOG_DEBUG); continue; } if (!is_array($filterExpressions)) { throw new InvalidConfigurationTypeException('The value given for setting "Neos.Flow.object.' . $includeOrExclude . 'Classes.\'' . $packageKey . '\'" is invalid. It should be an array of expressions. Check the syntax in the YAML file.', 1422357272); } $classesForPackageUnderInspection = $classNames[$packageKey]; $classNames[$packageKey] = []; foreach ($filterExpressions as $filterExpression) { $classesForPackageUnderInspection = array_filter($classesForPackageUnderInspection, function ($className) use($filterExpression, $includeOrExclude) { $match = preg_match('/' . $filterExpression . '/', $className); return $includeOrExclude === 'include' ? $match === 1 : $match !== 1; }); if ($includeOrExclude === 'include') { $classNames[$packageKey] = array_merge($classNames[$packageKey], $classesForPackageUnderInspection); $classesForPackageUnderInspection = $classNames[$packageKey]; } else { $classNames[$packageKey] = $classesForPackageUnderInspection; } } if ($classNames[$packageKey] === []) { unset($classNames[$packageKey]); } } return $classNames; }
/** * @param array $nodes */ public function assignNodes(array $nodes) { $data = array(); foreach ($nodes as $node) { if ($node->getPath() !== '/') { $q = new FlowQuery(array($node)); $closestDocumentNode = $q->closest('[instanceof Neos.Neos:Document]')->get(0); if ($closestDocumentNode !== null) { $data[] = array('nodeContextPath' => $node->getContextPath(), 'documentNodeContextPath' => $closestDocumentNode->getContextPath()); } else { $this->systemLogger->log('You have a node that is no longer connected to a parent. Path: ' . $node->getPath() . ' (Identifier: ' . $node->getIdentifier() . ')'); } } } $this->assign('value', array('data' => $data, 'success' => true)); }
/** * Matches a \Neos\Flow\Mvc\RequestInterface against the configured CSRF pattern rules and * searches for invalid csrf tokens. If this returns TRUE, the request is invalid! * * @param RequestInterface $request The request that should be matched * @return boolean TRUE if the pattern matched, FALSE otherwise * @throws AuthenticationRequiredException */ public function matchRequest(RequestInterface $request) { if (!$request instanceof ActionRequest || $request->getHttpRequest()->isMethodSafe()) { $this->systemLogger->log('CSRF: No token required, safe request', LOG_DEBUG); return false; } if ($this->authenticationManager->isAuthenticated() === false) { $this->systemLogger->log('CSRF: No token required, not authenticated', LOG_DEBUG); return false; } if ($this->securityContext->areAuthorizationChecksDisabled() === true) { $this->systemLogger->log('CSRF: No token required, authorization checks are disabled', LOG_DEBUG); return false; } $controllerClassName = $this->objectManager->getClassNameByObjectName($request->getControllerObjectName()); $actionMethodName = $request->getControllerActionName() . 'Action'; if (!$this->hasPolicyEntryForMethod($controllerClassName, $actionMethodName)) { $this->systemLogger->log(sprintf('CSRF: No token required, method %s::%s() is not restricted by a policy.', $controllerClassName, $actionMethodName), LOG_DEBUG); return false; } if ($this->reflectionService->isMethodTaggedWith($controllerClassName, $actionMethodName, 'skipcsrfprotection')) { $this->systemLogger->log(sprintf('CSRF: No token required, method %s::%s() is tagged with a "skipcsrfprotection" annotation', $controllerClassName, $actionMethodName), LOG_DEBUG); return false; } $httpRequest = $request->getHttpRequest(); if ($httpRequest->hasHeader('X-Flow-Csrftoken')) { $csrfToken = $httpRequest->getHeader('X-Flow-Csrftoken'); } else { $internalArguments = $request->getMainRequest()->getInternalArguments(); $csrfToken = isset($internalArguments['__csrfToken']) ? $internalArguments['__csrfToken'] : null; } if (empty($csrfToken)) { $this->systemLogger->log(sprintf('CSRF: token was empty but a valid token is required for %s::%s()', $controllerClassName, $actionMethodName), LOG_DEBUG); return true; } if (!$this->securityContext->hasCsrfProtectionTokens()) { throw new AuthenticationRequiredException(sprintf('CSRF: No CSRF tokens in security context, possible session timeout. A valid token is required for %s::%s()', $controllerClassName, $actionMethodName), 1317309673); } if ($this->securityContext->isCsrfProtectionTokenValid($csrfToken) === false) { $this->systemLogger->log(sprintf('CSRF: token was invalid but a valid token is required for %s::%s()', $controllerClassName, $actionMethodName), LOG_DEBUG); return true; } $this->systemLogger->log(sprintf('CSRF: Successfully verified token for %s::%s()', $controllerClassName, $actionMethodName), LOG_DEBUG); return false; }
/** * Flushes I18n caches if translation files have changed * * @param array $changedFiles A list of full paths to changed files * @return void * @see flushSystemCachesByChangedFiles() */ protected function flushTranslationCachesByChangedFiles(array $changedFiles) { foreach ($changedFiles as $pathAndFilename => $status) { if (preg_match('/\\/Translations\\/.+\\.xlf/', $pathAndFilename) === 1) { $this->systemLogger->log('The localization files have changed, thus flushing the I18n XML model cache.', LOG_INFO); $this->getCache('Flow_I18n_XmlModelCache')->flush(); break; } } }
/** * Flush caches according to the previously registered node changes. * * @return void */ public function shutdownObject() { if ($this->tagsToFlush !== array()) { foreach ($this->tagsToFlush as $tag => $logMessage) { $affectedEntries = $this->contentCache->flushByTag($tag); if ($affectedEntries > 0) { $this->systemLogger->log(sprintf('Content cache: Removed %s entries %s', $affectedEntries, $logMessage), LOG_DEBUG); } } } }
/** * Refreshes this asset after the Resource or any other parameters affecting thumbnails have been modified * * @return void */ public function refresh() { $assetClassType = str_replace('Neos\\Media\\Domain\\Model\\', '', get_class($this)); $this->systemLogger->log(sprintf('%s: refresh() called, clearing all thumbnails. Filename: %s. PersistentResource SHA1: %s', $assetClassType, $this->getResource()->getFilename(), $this->getResource()->getSha1()), LOG_DEBUG); // whitelist objects so they can be deleted (even during safe requests) $this->persistenceManager->whitelistObject($this); foreach ($this->thumbnails as $thumbnail) { $this->persistenceManager->whitelistObject($thumbnail); } $this->thumbnails->clear(); }
/** * Logs calls of collectGarbage() * * @Flow\AfterReturning("within(Neos\Flow\Session\SessionInterface) && method(.*->collectGarbage())") * @param JoinPointInterface $joinPoint The current joinpoint * @return void */ public function logCollectGarbage(JoinPointInterface $joinPoint) { $sessionRemovalCount = $joinPoint->getResult(); if ($sessionRemovalCount > 0) { $this->systemLogger->log(sprintf('%s: Triggered garbage collection and removed %s expired sessions.', $this->getClassName($joinPoint), $sessionRemovalCount), LOG_INFO); } elseif ($sessionRemovalCount === 0) { $this->systemLogger->log(sprintf('%s: Triggered garbage collection but no sessions needed to be removed.', $this->getClassName($joinPoint)), LOG_INFO); } elseif ($sessionRemovalCount === false) { $this->systemLogger->log(sprintf('%s: Ommitting garbage collection because another process is already running. Consider lowering the GC propability if these messages appear a lot.', $this->getClassName($joinPoint)), LOG_WARNING); } }
/** * @param callable $callback a callback function to process every notification * @return void * @api */ public function flush(callable $callback = null) { foreach ($this->messages as $message) { /** @var Message $message */ $this->messages->detach($message); $this->systemLogger->log('ResourcePublishingMessage: ' . $message->getMessage(), $message->getSeverity()); if ($callback !== null) { $callback($message); } } }
/** * Analyzes the Object Configuration provided by the compiler and builds the necessary PHP code for the proxy classes * to realize dependency injection. * * @return void */ public function build() { $this->objectConfigurations = $this->objectManager->getObjectConfigurations(); foreach ($this->objectConfigurations as $objectName => $objectConfiguration) { $className = $objectConfiguration->getClassName(); if ($className === '' || $this->compiler->hasCacheEntryForClass($className) === true) { continue; } if ($objectName !== $className || $this->reflectionService->isClassAbstract($className)) { continue; } $proxyClass = $this->compiler->getProxyClass($className); if ($proxyClass === false) { continue; } $this->systemLogger->log('Building DI proxy for "' . $className . '".', LOG_DEBUG); $constructorPreCode = ''; $constructorPostCode = ''; $constructorPreCode .= $this->buildSetInstanceCode($objectConfiguration); $constructorPreCode .= $this->buildConstructorInjectionCode($objectConfiguration); $setRelatedEntitiesCode = ''; if (!$this->reflectionService->hasMethod($className, '__sleep')) { $proxyClass->addTraits(['\\' . ObjectSerializationTrait::class]); $sleepMethod = $proxyClass->getMethod('__sleep'); $sleepMethod->addPostParentCallCode($this->buildSerializeRelatedEntitiesCode($objectConfiguration)); $setRelatedEntitiesCode = "\n " . '$this->Flow_setRelatedEntities();' . "\n"; } $wakeupMethod = $proxyClass->getMethod('__wakeup'); $wakeupMethod->addPreParentCallCode($this->buildSetInstanceCode($objectConfiguration)); $wakeupMethod->addPreParentCallCode($setRelatedEntitiesCode); $wakeupMethod->addPostParentCallCode($this->buildLifecycleInitializationCode($objectConfiguration, ObjectManagerInterface::INITIALIZATIONCAUSE_RECREATED)); $wakeupMethod->addPostParentCallCode($this->buildLifecycleShutdownCode($objectConfiguration)); $injectPropertiesCode = $this->buildPropertyInjectionCode($objectConfiguration); if ($injectPropertiesCode !== '') { $proxyClass->addTraits(['\\' . PropertyInjectionTrait::class]); $proxyClass->getMethod('Flow_Proxy_injectProperties')->addPreParentCallCode($injectPropertiesCode); $proxyClass->getMethod('Flow_Proxy_injectProperties')->overrideMethodVisibility('private'); $wakeupMethod->addPreParentCallCode(" \$this->Flow_Proxy_injectProperties();\n"); $constructorPostCode .= ' if (\'' . $className . '\' === get_class($this)) {' . "\n"; $constructorPostCode .= ' $this->Flow_Proxy_injectProperties();' . "\n"; $constructorPostCode .= ' }' . "\n"; } $constructorPostCode .= $this->buildLifecycleInitializationCode($objectConfiguration, ObjectManagerInterface::INITIALIZATIONCAUSE_CREATED); $constructorPostCode .= $this->buildLifecycleShutdownCode($objectConfiguration); $constructor = $proxyClass->getConstructor(); $constructor->addPreParentCallCode($constructorPreCode); $constructor->addPostParentCallCode($constructorPostCode); if ($this->objectManager->getContext()->isProduction()) { $this->compileStaticMethods($className, $proxyClass); } } }
/** * Called after a functional test in Flow, dumps everything in the database. * * @return void */ public function tearDown() { // "driver" is used only for Doctrine, thus we (mis-)use it here // additionally, when no path is set, skip this step, assuming no DB is needed if ($this->settings['backendOptions']['driver'] !== null && $this->settings['backendOptions']['path'] !== null) { $this->entityManager->clear(); $schemaTool = new SchemaTool($this->entityManager); $schemaTool->dropDatabase(); $this->systemLogger->log('Doctrine 2 schema destroyed.', LOG_NOTICE); } else { $this->systemLogger->log('Doctrine 2 destroy skipped, driver and path backend options not set!', LOG_NOTICE); } }
/** * Returns the current workspace. * * @param boolean $createWorkspaceIfNecessary DEPRECATED: If enabled, creates a workspace with the configured name if it doesn't exist already. This option is DEPRECATED, create workspace explicitly instead. * @return Workspace The workspace or NULL * @api */ public function getWorkspace($createWorkspaceIfNecessary = true) { if ($this->workspace !== null) { return $this->workspace; } $this->workspace = $this->workspaceRepository->findByIdentifier($this->workspaceName); if ($this->workspace === null && $createWorkspaceIfNecessary) { $liveWorkspace = $this->workspaceRepository->findByIdentifier('live'); $this->workspace = new Workspace($this->workspaceName, $liveWorkspace); $this->workspaceRepository->add($this->workspace); $this->systemLogger->log(sprintf('Notice: %s::getWorkspace() implicitly created the new workspace "%s". This behaviour is discouraged and will be removed in future versions. Make sure to create workspaces explicitly by adding a new workspace to the Workspace Repository.', __CLASS__, $this->workspaceName), LOG_NOTICE); } if ($this->workspace !== null) { $this->validateWorkspace($this->workspace); } return $this->workspace; }
/** * Builds proxy class code which weaves advices into the respective target classes. * * The object configurations provided by the Compiler are searched for possible aspect * annotations. If an aspect class is found, the pointcut 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. * * In general, the command neos.flow:core:compile is responsible for compilation * and calls this method to do so. * * In order to distinguish between an emerged / changed possible target class and * a class which has been matched previously but just didn't have to be proxied, * the latter are kept track of by an "unproxiedClass-*" cache entry. * * @return void */ public function build() { $allAvailableClassNamesByPackage = $this->objectManager->getRegisteredClassNames(); $possibleTargetClassNames = $this->getProxyableClasses($allAvailableClassNamesByPackage); $actualAspectClassNames = $this->reflectionService->getClassNamesByAnnotation(Flow\Aspect::class); sort($possibleTargetClassNames); sort($actualAspectClassNames); $this->aspectContainers = $this->buildAspectContainers($actualAspectClassNames); $rebuildEverything = false; if ($this->objectConfigurationCache->has('allAspectClassesUpToDate') === false) { $rebuildEverything = true; $this->systemLogger->log('Aspects have been modified, therefore rebuilding all target classes.', LOG_INFO); $this->objectConfigurationCache->set('allAspectClassesUpToDate', true); } $possibleTargetClassNameIndex = new ClassNameIndex(); $possibleTargetClassNameIndex->setClassNames($possibleTargetClassNames); $targetClassNameCandidates = new ClassNameIndex(); foreach ($this->aspectContainers as $aspectContainer) { $targetClassNameCandidates->applyUnion($aspectContainer->reduceTargetClassNames($possibleTargetClassNameIndex)); } $targetClassNameCandidates->sort(); $treatedSubClasses = new ClassNameIndex(); foreach ($targetClassNameCandidates->getClassNames() as $targetClassName) { $isUnproxied = $this->objectConfigurationCache->has('unproxiedClass-' . str_replace('\\', '_', $targetClassName)); $hasCacheEntry = $this->compiler->hasCacheEntryForClass($targetClassName) || $isUnproxied; if ($rebuildEverything === true || $hasCacheEntry === false) { $proxyBuildResult = $this->buildProxyClass($targetClassName, $this->aspectContainers); if ($proxyBuildResult === false) { // In case the proxy was not build because there was nothing adviced, // it might be an advice in the parent and so we need to try to treat this class. $treatedSubClasses = $this->addBuildMethodsAndAdvicesCodeToClass($targetClassName, $treatedSubClasses); } $treatedSubClasses = $this->proxySubClassesOfClassToEnsureAdvices($targetClassName, $targetClassNameCandidates, $treatedSubClasses); if ($proxyBuildResult !== false) { if ($isUnproxied) { $this->objectConfigurationCache->remove('unproxiedClass-' . str_replace('\\', '_', $targetClassName)); } $this->systemLogger->log(sprintf('Built AOP proxy for class "%s".', $targetClassName), LOG_DEBUG); } else { $this->objectConfigurationCache->set('unproxiedClass-' . str_replace('\\', '_', $targetClassName), true); } } } }
/** * Call the render() method and handle errors. * * @return string the rendered ViewHelper * @throws Exception */ protected function callRenderMethod() { $renderMethodParameters = []; foreach ($this->argumentDefinitions as $argumentName => $argumentDefinition) { if ($argumentDefinition instanceof ArgumentDefinition && $argumentDefinition->isMethodParameter()) { $renderMethodParameters[$argumentName] = $this->arguments[$argumentName]; } } try { return call_user_func_array([$this, 'render'], $renderMethodParameters); } catch (Exception $exception) { if ($this->objectManager->getContext()->isProduction()) { $this->systemLogger->log('A Fluid ViewHelper Exception was captured: ' . $exception->getMessage() . ' (' . $exception->getCode() . ')', LOG_ERR, ['exception' => $exception]); return ''; } else { throw $exception; } } }
/** * 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 * @throws NoMatchingRouteException */ public function resolve(array $routeValues) { $cachedResolvedUriPath = $this->routerCachingService->getCachedResolvedUriPath($routeValues); if ($cachedResolvedUriPath !== false) { return $cachedResolvedUriPath; } $this->lastResolvedRoute = null; $this->createRoutesFromConfiguration(); /** @var $route Route */ foreach ($this->routes as $route) { if ($route->resolves($routeValues)) { $this->lastResolvedRoute = $route; $resolvedUriPath = $route->getResolvedUriPath(); if ($resolvedUriPath !== null) { $this->routerCachingService->storeResolvedUriPath($resolvedUriPath, $routeValues); } return $resolvedUriPath; } } $this->systemLogger->log('Router resolve(): Could not resolve a route for building an URI for the given route values.', LOG_WARNING, $routeValues); throw new NoMatchingRouteException('Could not resolve a route and its corresponding URI for the given parameters. This may be due to referring to a not existing package / controller / action while building a link or URI. Refer to log and check the backtrace for more details.', 1301610453); }
/** * Make room in the sortindex-index space of a given path in preparation to inserting a node. * All indices that are greater or equal to the given referenceIndex are incremented by 100 * * @param string $parentPath * @param integer $referenceIndex * @throws Exception\NodeException */ protected function openIndexSpace($parentPath, $referenceIndex) { $this->systemLogger->log(sprintf('Opening sortindex space after index %s at path %s.', $referenceIndex, $parentPath), LOG_INFO); /** @var Query $query */ $query = $this->entityManager->createQuery('SELECT n.Persistence_Object_Identifier identifier, n.index, n.path FROM Neos\\ContentRepository\\Domain\\Model\\NodeData n WHERE n.parentPathHash = :parentPathHash ORDER BY n.index ASC'); $query->setParameter('parentPathHash', md5($parentPath)); $nodesOnLevel = []; /** @var $node NodeData */ foreach ($query->getArrayResult() as $node) { $nodesOnLevel[] = ['identifier' => $node['identifier'], 'path' => $node['path'], 'index' => $node['index']]; } /** @var $node NodeData */ foreach ($this->addedNodes as $node) { if ($node->getParentPath() === $parentPath) { $nodesOnLevel[] = ['addedNode' => $node, 'path' => $node->getPath(), 'index' => $node->getIndex()]; } } $query = $this->entityManager->createQuery('UPDATE Neos\\ContentRepository\\Domain\\Model\\NodeData n SET n.index = :index WHERE n.Persistence_Object_Identifier = :identifier'); foreach ($nodesOnLevel as $node) { if ($node['index'] < $referenceIndex) { continue; } $newIndex = $node['index'] + 100; if ($newIndex > self::INDEX_MAXIMUM) { throw new Exception\NodeException(sprintf('Reached maximum node index of %s while setting index of node %s.', $newIndex, $node['path']), 1317140402); } if (isset($node['addedNode'])) { $node['addedNode']->setIndex($newIndex); } else { if ($entity = $this->entityManager->getUnitOfWork()->tryGetById($node['identifier'], NodeData::class)) { $entity->setIndex($newIndex); } $query->setParameter('index', $newIndex); $query->setParameter('identifier', $node['identifier']); $query->execute(); } } }
/** * Matches a frontend URI pointing to a node (for example a page). * * This function tries to find a matching node by the given request path. If one was found, its * absolute context node path is set in $this->value and true is returned. * * Note that this matcher does not check if access to the resolved workspace or node is allowed because at the point * in time the route part handler is invoked, the security framework is not yet fully initialized. * * @param string $requestPath The request path (without leading "/", relative to the current Site Node) * @return boolean true if the $requestPath could be matched, otherwise false * @throws \Exception * @throws Exception\NoHomepageException if no node could be found on the homepage (empty $requestPath) */ protected function matchValue($requestPath) { try { /** @var NodeInterface $node */ $node = null; // Build context explicitly without authorization checks because the security context isn't available yet // anyway and any Entity Privilege targeted on Workspace would fail at this point: $this->securityContext->withoutAuthorizationChecks(function () use(&$node, $requestPath) { $node = $this->convertRequestPathToNode($requestPath); }); } catch (Exception $exception) { $this->systemLogger->log('FrontendNodeRoutePartHandler matchValue(): ' . $exception->getMessage(), LOG_DEBUG); if ($requestPath === '') { throw new Exception\NoHomepageException('Homepage could not be loaded. Probably you haven\'t imported a site yet', 1346950755, $exception); } return false; } if ($this->onlyMatchSiteNodes() && $node !== $node->getContext()->getCurrentSiteNode()) { return false; } $this->value = $node->getContextPath(); return true; }
/** * Deletes the given PersistentResource from the ResourceRepository and, if the storage data is no longer used in another * PersistentResource object, also deletes the data from the storage. * * This method will also remove the PersistentResource object from the (internal) ResourceRepository. * * @param PersistentResource $resource The resource to delete * @param boolean $unpublishResource If the resource should be unpublished before deleting it from the storage * @return boolean true if the resource was deleted, otherwise FALSE * @api */ public function deleteResource(PersistentResource $resource, $unpublishResource = true) { $this->initialize(); $collectionName = $resource->getCollectionName(); $result = $this->resourceRepository->findBySha1($resource->getSha1()); if (count($result) > 1) { $this->systemLogger->log(sprintf('Not removing storage data of resource %s (%s) because it is still in use by %s other PersistentResource object(s).', $resource->getFilename(), $resource->getSha1(), count($result) - 1), LOG_DEBUG); } else { if (!isset($this->collections[$collectionName])) { $this->systemLogger->log(sprintf('Could not remove storage data of resource %s (%s) because it refers to the unknown collection "%s".', $resource->getFilename(), $resource->getSha1(), $collectionName), LOG_WARNING); return false; } $storage = $this->collections[$collectionName]->getStorage(); if (!$storage instanceof WritableStorageInterface) { $this->systemLogger->log(sprintf('Could not remove storage data of resource %s (%s) because it its collection "%s" is read-only.', $resource->getFilename(), $resource->getSha1(), $collectionName), LOG_WARNING); return false; } try { $storage->deleteResource($resource); } catch (\Exception $exception) { $this->systemLogger->log(sprintf('Could not remove storage data of resource %s (%s): %s.', $resource->getFilename(), $resource->getSha1(), $exception->getMessage()), LOG_WARNING); return false; } if ($unpublishResource) { /** @var TargetInterface $target */ $target = $this->collections[$collectionName]->getTarget(); $target->unpublishResource($resource); $this->systemLogger->log(sprintf('Removed storage data and unpublished resource %s (%s) because it not used by any other PersistentResource object.', $resource->getFilename(), $resource->getSha1()), LOG_DEBUG); } else { $this->systemLogger->log(sprintf('Removed storage data of resource %s (%s) because it not used by any other PersistentResource object.', $resource->getFilename(), $resource->getSha1()), LOG_DEBUG); } } $resource->setDeleted(); $this->resourceRepository->remove($resource); return true; }
/** * "Send" the given Message. This transport will add it to a stored collection of sent messages * for testing purposes and log the message to the system logger. * * @param \Swift_Mime_Message $message The message to send * @param array &$failedRecipients Failed recipients * @return integer */ public function send(\Swift_Mime_Message $message, &$failedRecipients = null) { self::$deliveredMessages[] = $message; $this->systemLogger->log('Sent email to ' . $this->buildStringFromEmailAndNameArray($message->getTo()), LOG_DEBUG, array('message' => $message->toString())); return count((array) $message->getTo()) + count((array) $message->getCc()) + count((array) $message->getBcc()); }
/** * Returns an empty string * * @param string $typoScriptPath path causing the exception * @param \Exception $exception exception to handle * @param integer $referenceCode * @return string */ protected function handle($typoScriptPath, \Exception $exception, $referenceCode) { $this->systemLogger->log('Absorbed Exception: ' . $exception->getMessage(), LOG_DEBUG, array('typoScriptPath' => $typoScriptPath, 'referenceCode' => $referenceCode), 'Neos.Fusion', \Neos\Fusion\Core\ExceptionHandlers\AbsorbingHandler::class); return ''; }
/** * This function tries to find yet unmatched dependencies which need to be injected via "inject*" setter methods. * * @param array &$objectConfigurations * @return void * @throws ObjectException if an injected property is private */ protected function autowireProperties(array &$objectConfigurations) { /** @var Configuration $objectConfiguration */ foreach ($objectConfigurations as $objectConfiguration) { $className = $objectConfiguration->getClassName(); $properties = $objectConfiguration->getProperties(); if ($className === '') { continue; } $classMethodNames = get_class_methods($className); if (!is_array($classMethodNames)) { if (!class_exists($className)) { throw new UnknownClassException(sprintf('The class "%s" defined in the object configuration for object "%s", defined in package: %s, does not exist.', $className, $objectConfiguration->getObjectName(), $objectConfiguration->getPackageKey()), 1352371371); } else { throw new UnknownClassException(sprintf('Could not autowire properties of class "%s" because names of methods contained in that class could not be retrieved using get_class_methods().', $className), 1352386418); } } foreach ($classMethodNames as $methodName) { if (isset($methodName[6]) && strpos($methodName, 'inject') === 0 && $methodName[6] === strtoupper($methodName[6])) { $propertyName = lcfirst(substr($methodName, 6)); $autowiringAnnotation = $this->reflectionService->getMethodAnnotation($className, $methodName, Flow\Autowiring::class); if ($autowiringAnnotation !== null && $autowiringAnnotation->enabled === false) { continue; } if ($methodName === 'injectSettings') { $packageKey = $objectConfiguration->getPackageKey(); if ($packageKey !== null) { $properties[$propertyName] = new ConfigurationProperty($propertyName, ['type' => ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, 'path' => $packageKey], ConfigurationProperty::PROPERTY_TYPES_CONFIGURATION); } } else { if (array_key_exists($propertyName, $properties)) { continue; } $methodParameters = $this->reflectionService->getMethodParameters($className, $methodName); if (count($methodParameters) !== 1) { $this->systemLogger->log(sprintf('Could not autowire property %s because %s() expects %s instead of exactly 1 parameter.', $className . '::' . $propertyName, $methodName, count($methodParameters) ?: 'none'), LOG_DEBUG); continue; } $methodParameter = array_pop($methodParameters); if ($methodParameter['class'] === null) { $this->systemLogger->log(sprintf('Could not autowire property %s because the method parameter in %s() contained no class type hint.', $className . '::' . $propertyName, $methodName), LOG_DEBUG); continue; } $properties[$propertyName] = new ConfigurationProperty($propertyName, $methodParameter['class'], ConfigurationProperty::PROPERTY_TYPES_OBJECT); } } } foreach ($this->reflectionService->getPropertyNamesByAnnotation($className, Inject::class) as $propertyName) { if ($this->reflectionService->isPropertyPrivate($className, $propertyName)) { throw new ObjectException(sprintf('The property "%%s" in class "%s" must not be private when annotated for injection.', $propertyName, $className), 1328109641); } if (!array_key_exists($propertyName, $properties)) { /** @var Inject $injectAnnotation */ $injectAnnotation = $this->reflectionService->getPropertyAnnotation($className, $propertyName, Inject::class); $objectName = trim(implode('', $this->reflectionService->getPropertyTagValues($className, $propertyName, 'var')), ' \\'); $configurationProperty = new ConfigurationProperty($propertyName, $objectName, ConfigurationProperty::PROPERTY_TYPES_OBJECT, null, $injectAnnotation->lazy); $properties[$propertyName] = $configurationProperty; } } foreach ($this->reflectionService->getPropertyNamesByAnnotation($className, InjectConfiguration::class) as $propertyName) { if ($this->reflectionService->isPropertyPrivate($className, $propertyName)) { throw new ObjectException(sprintf('The property "%s" in class "%s" must not be private when annotated for configuration injection.', $propertyName, $className), 1416765599); } if (array_key_exists($propertyName, $properties)) { continue; } /** @var InjectConfiguration $injectConfigurationAnnotation */ $injectConfigurationAnnotation = $this->reflectionService->getPropertyAnnotation($className, $propertyName, InjectConfiguration::class); if ($injectConfigurationAnnotation->type === ConfigurationManager::CONFIGURATION_TYPE_SETTINGS) { $packageKey = $injectConfigurationAnnotation->package !== null ? $injectConfigurationAnnotation->package : $objectConfiguration->getPackageKey(); $configurationPath = rtrim($packageKey . '.' . $injectConfigurationAnnotation->path, '.'); } else { if ($injectConfigurationAnnotation->package !== null) { throw new ObjectException(sprintf('The InjectConfiguration annotation for property "%s" in class "%s" specifies a "package" key for configuration type "%s", but this is only supported for injection of "Settings".', $propertyName, $className, $injectConfigurationAnnotation->type), 1420811958); } $configurationPath = $injectConfigurationAnnotation->path; } $properties[$propertyName] = new ConfigurationProperty($propertyName, ['type' => $injectConfigurationAnnotation->type, 'path' => $configurationPath], ConfigurationProperty::PROPERTY_TYPES_CONFIGURATION); } $objectConfiguration->setProperties($properties); } }
/** * Publishes the given source stream to this target, with the given relative path. * * @param resource $sourceStream Stream of the source to publish * @param string $relativeTargetPathAndFilename relative path and filename in the target directory * @return void * @throws TargetException */ protected function publishFile($sourceStream, $relativeTargetPathAndFilename) { $pathInfo = UnicodeFunctions::pathinfo($relativeTargetPathAndFilename); if (isset($pathInfo['extension']) && array_key_exists(strtolower($pathInfo['extension']), $this->extensionBlacklist) && $this->extensionBlacklist[strtolower($pathInfo['extension'])] === true) { throw new TargetException(sprintf('Could not publish "%s" into resource publishing target "%s" because the filename extension "%s" is blacklisted.', $sourceStream, $this->name, strtolower($pathInfo['extension'])), 1447148472); } $targetPathAndFilename = $this->path . $relativeTargetPathAndFilename; if (@fstat($sourceStream) === false) { throw new TargetException(sprintf('Could not publish "%s" into resource publishing target "%s" because the source file is not accessible (file stat failed).', $sourceStream, $this->name), 1375258499); } if (!file_exists(dirname($targetPathAndFilename))) { Files::createDirectoryRecursively(dirname($targetPathAndFilename)); } if (!is_writable(dirname($targetPathAndFilename))) { throw new Exception(sprintf('Could not publish "%s" into resource publishing target "%s" because the target file "%s" is not writable.', $sourceStream, $this->name, $targetPathAndFilename), 1428917322, isset($exception) ? $exception : null); } try { $targetFileHandle = fopen($targetPathAndFilename, 'w'); $result = stream_copy_to_stream($sourceStream, $targetFileHandle); fclose($targetFileHandle); } catch (\Exception $exception) { $result = false; } if ($result === false) { throw new TargetException(sprintf('Could not publish "%s" into resource publishing target "%s" because the source file could not be copied to the target location.', $sourceStream, $this->name), 1375258399, isset($exception) ? $exception : null); } $this->systemLogger->log(sprintf('FileSystemTarget: Published file. (target: %s, file: %s)', $this->name, $relativeTargetPathAndFilename), LOG_DEBUG); }
/** * Returns a string containing all validation errors separated by PHP_EOL. * * @return string */ protected function getFlattenedValidationErrorMessage() { $outputMessage = 'Validation failed while trying to call ' . get_class($this) . '->' . $this->actionMethodName . '().' . PHP_EOL; $logMessage = $outputMessage; foreach ($this->arguments->getValidationResults()->getFlattenedErrors() as $propertyPath => $errors) { foreach ($errors as $error) { $logMessage .= 'Error for ' . $propertyPath . ': ' . $error->render() . PHP_EOL; } } $this->systemLogger->log($logMessage, LOG_ERR); return $outputMessage; }