diff --git a/Neos.Cache/Classes/Backend/RedisBackend.php b/Neos.Cache/Classes/Backend/RedisBackend.php index 237558d78b..58a4987c95 100644 --- a/Neos.Cache/Classes/Backend/RedisBackend.php +++ b/Neos.Cache/Classes/Backend/RedisBackend.php @@ -501,9 +501,17 @@ private function getRedisClient(): \Redis $this->port = null; } $redis = new \Redis(); - if (!$redis->connect($this->hostname, $this->port)) { - throw new CacheException('Could not connect to Redis.', 1391972021); + + try { + $connected = false; + // keep the above! the line below leave the variable undefined if an error occurs. + $connected = $redis->connect($this->hostname, $this->port); + } finally { + if ($connected === false) { + throw new CacheException('Could not connect to Redis.', 1391972021); + } } + if ($this->password !== '') { if (!$redis->auth($this->password)) { throw new CacheException('Redis authentication failed.', 1502366200); diff --git a/Neos.Flow/Classes/Core/Booting/Scripts.php b/Neos.Flow/Classes/Core/Booting/Scripts.php index 65671d0084..04e7e8d76d 100644 --- a/Neos.Flow/Classes/Core/Booting/Scripts.php +++ b/Neos.Flow/Classes/Core/Booting/Scripts.php @@ -25,6 +25,7 @@ use Neos\Flow\Error\Debugger; use Neos\Flow\Error\ErrorHandler; use Neos\Flow\Error\ProductionExceptionHandler; +use Neos\Flow\Http\HttpRequestHandlerInterface; use Neos\Flow\Log\Logger; use Neos\Flow\Log\LoggerBackendConfigurationHelper; use Neos\Flow\Log\LoggerFactory; @@ -240,8 +241,7 @@ public static function initializeSystemLogger(Bootstrap $bootstrap) $configurationManager = $bootstrap->getEarlyInstance(ConfigurationManager::class); $settings = $configurationManager->getConfiguration(ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, 'Neos.Flow'); - $throwableStorage = new FileStorage(); - $throwableStorage->injectStoragePath(FLOW_PATH_DATA . 'Logs/Exceptions'); + $throwableStorage = self::initializeExceptionStorage($bootstrap); $bootstrap->setEarlyInstance(ThrowableStorageInterface::class, $throwableStorage); /** @var PsrLoggerFactoryInterface $psrLoggerFactoryName */ @@ -254,7 +254,7 @@ public static function initializeSystemLogger(Bootstrap $bootstrap) $psrLogFactory = $psrLoggerFactoryName::create($psrLogConfigurations); // This is all deprecated and can be removed with the removal of respective interfaces and classes. - $loggerFactory = new LoggerFactory($psrLogFactory); + $loggerFactory = new LoggerFactory($psrLogFactory, $throwableStorage); $bootstrap->setEarlyInstance($psrLoggerFactoryName, $psrLogFactory); $bootstrap->setEarlyInstance(PsrLoggerFactoryInterface::class, $psrLogFactory); $bootstrap->setEarlyInstance(LoggerFactory::class, $loggerFactory); @@ -265,6 +265,62 @@ public static function initializeSystemLogger(Bootstrap $bootstrap) $bootstrap->setEarlyInstance(SystemLoggerInterface::class, $systemLogger); } + /** + * Initialize the exception storage + * + * @param Bootstrap $bootstrap + * @return ThrowableStorageInterface + * @throws FlowException + * @throws \Neos\Flow\Configuration\Exception\InvalidConfigurationTypeException + */ + protected static function initializeExceptionStorage(Bootstrap $bootstrap): ThrowableStorageInterface + { + $configurationManager = $bootstrap->getEarlyInstance(ConfigurationManager::class); + $settings = $configurationManager->getConfiguration(ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, 'Neos.Flow'); + + $storageClassName = $settings['log']['throwables']['storageClass'] ?? FileStorage::class; + $storageOptions = $settings['log']['throwables']['optionsByImplementation'][$storageClassName] ?? []; + + + if (!in_array(ThrowableStorageInterface::class, class_implements($storageClassName, true))) { + throw new \Exception( + sprintf('The class "%s" configured as throwable storage does not implement the ThrowableStorageInterface', $storageClassName), + 1540583485 + ); + } + + /** @var ThrowableStorageInterface $throwableStorage */ + $throwableStorage = $storageClassName::createWithOptions($storageOptions); + + $throwableStorage->setBacktraceRenderer(function ($backtrace) { + return Debugger::getBacktraceCode($backtrace, false, true); + }); + + $throwableStorage->setRequestInformationRenderer(function () { + $output = ''; + if (!(Bootstrap::$staticObjectManager instanceof ObjectManagerInterface)) { + return $output; + } + + $bootstrap = Bootstrap::$staticObjectManager->get(Bootstrap::class); + /* @var Bootstrap $bootstrap */ + $requestHandler = $bootstrap->getActiveRequestHandler(); + if (!$requestHandler instanceof HttpRequestHandlerInterface) { + return $output; + } + + $request = $requestHandler->getHttpRequest(); + $response = $requestHandler->getHttpResponse(); + $output .= PHP_EOL . 'HTTP REQUEST:' . PHP_EOL . ($request == '' ? '[request was empty]' : $request) . PHP_EOL; + $output .= PHP_EOL . 'HTTP RESPONSE:' . PHP_EOL . ($response == '' ? '[response was empty]' : $response) . PHP_EOL; + $output .= PHP_EOL . 'PHP PROCESS:' . PHP_EOL . 'Inode: ' . getmyinode() . PHP_EOL . 'PID: ' . getmypid() . PHP_EOL . 'UID: ' . getmyuid() . PHP_EOL . 'GID: ' . getmygid() . PHP_EOL . 'User: ' . get_current_user() . PHP_EOL; + + return $output; + }); + + return $throwableStorage; + } + /** * Initializes the error handling * diff --git a/Neos.Flow/Classes/Log/LoggerFactory.php b/Neos.Flow/Classes/Log/LoggerFactory.php index 5345718af1..2bb8162bac 100644 --- a/Neos.Flow/Classes/Log/LoggerFactory.php +++ b/Neos.Flow/Classes/Log/LoggerFactory.php @@ -23,6 +23,7 @@ * * @api * @Flow\Scope("singleton") + * @Flow\Autowiring(false) * @deprecated Instead a \Neos\Flow\Log\PsrLoggerFactoryInterface should be used. */ class LoggerFactory @@ -32,6 +33,11 @@ class LoggerFactory */ protected $psrLoggerFactory; + /** + * @var ThrowableStorageInterface + */ + protected $throwableStorage; + /** * @var array */ @@ -46,10 +52,12 @@ class LoggerFactory * LoggerFactory constructor. * * @param PsrLoggerFactoryInterface $psrLoggerFactory + * @param ThrowableStorageInterface $throwableStorage */ - public function __construct(PsrLoggerFactoryInterface $psrLoggerFactory) + public function __construct(PsrLoggerFactoryInterface $psrLoggerFactory, ThrowableStorageInterface $throwableStorage) { $this->psrLoggerFactory = $psrLoggerFactory; + $this->throwableStorage = $throwableStorage; $this->requestInfoCallback = function () { $output = ''; if (!(Bootstrap::$staticObjectManager instanceof ObjectManagerInterface)) { @@ -145,27 +153,9 @@ protected function instantiateLogger(string $loggerObjectName, $backendObjectNam protected function injectAdditionalDependencies(LoggerInterface $logger): LoggerInterface { if ($logger instanceof Logger) { - $logger->injectThrowableStorage($this->instantiateThrowableStorage()); + $logger->injectThrowableStorage($this->throwableStorage); } return $logger; } - - /** - * @return FileStorage|ThrowableStorageInterface - */ - protected function instantiateThrowableStorage(): ThrowableStorageInterface - { - // Fallback early throwable storage - $throwableStorage = new FileStorage(); - $throwableStorage->injectStoragePath(FLOW_PATH_DATA . 'Logs/Exceptions'); - if (Bootstrap::$staticObjectManager instanceof ObjectManagerInterface) { - $throwableStorage = Bootstrap::$staticObjectManager->get(ThrowableStorageInterface::class); - } - $throwableStorage->setBacktraceRenderer(function ($backtrace) { - return Debugger::getBacktraceCode($backtrace, false, true); - }); - $throwableStorage->setRequestInformationRenderer($this->requestInfoCallback); - return $throwableStorage; - } } diff --git a/Neos.Flow/Classes/Log/ThrowableStorage/FileStorage.php b/Neos.Flow/Classes/Log/ThrowableStorage/FileStorage.php index bda0a0def0..2e624cb90b 100644 --- a/Neos.Flow/Classes/Log/ThrowableStorage/FileStorage.php +++ b/Neos.Flow/Classes/Log/ThrowableStorage/FileStorage.php @@ -1,20 +1,23 @@ storagePath = $storagePath; + + $this->requestInformationRenderer = function () { + $output = ''; + if (!(Bootstrap::$staticObjectManager instanceof ObjectManagerInterface)) { + return $output; + } + + $bootstrap = Bootstrap::$staticObjectManager->get(Bootstrap::class); + /* @var Bootstrap $bootstrap */ + $requestHandler = $bootstrap->getActiveRequestHandler(); + if (!$requestHandler instanceof HttpRequestHandlerInterface) { + return $output; + } + + $request = $requestHandler->getHttpRequest(); + $response = $requestHandler->getHttpResponse(); + $output .= PHP_EOL . 'HTTP REQUEST:' . PHP_EOL . ($request == '' ? '[request was empty]' : $request) . PHP_EOL; + $output .= PHP_EOL . 'HTTP RESPONSE:' . PHP_EOL . ($response == '' ? '[response was empty]' : $response) . PHP_EOL; + $output .= PHP_EOL . 'PHP PROCESS:' . PHP_EOL . 'Inode: ' . getmyinode() . PHP_EOL . 'PID: ' . getmypid() . PHP_EOL . 'UID: ' . getmyuid() . PHP_EOL . 'GID: ' . getmygid() . PHP_EOL . 'User: ' . get_current_user() . PHP_EOL; + + return $output; + }; + + $this->backtraceRenderer = function ($backtrace) { + return Debugger::getBacktraceCode($backtrace, false, true); + }; } /** @@ -42,6 +91,7 @@ public function injectStoragePath(string $storagePath) public function setRequestInformationRenderer(\Closure $requestInformationRenderer) { $this->requestInformationRenderer = $requestInformationRenderer; + return $this; } @@ -52,6 +102,7 @@ public function setRequestInformationRenderer(\Closure $requestInformationRender public function setBacktraceRenderer(\Closure $backtraceRenderer) { $this->backtraceRenderer = $backtraceRenderer; + return $this; } @@ -62,22 +113,10 @@ public function setBacktraceRenderer(\Closure $backtraceRenderer) */ public function logThrowable(\Throwable $throwable, array $additionalData = []) { - return $this->logError($throwable, $additionalData); - } - - /** - * Writes information about the given exception into the log. - * - * @param \Throwable $error \Exception or \Throwable - * @param array $additionalData Additional data to log - * @return string The exception message - */ - protected function logError(\Throwable $error, array $additionalData = []) - { - $message = $this->getErrorLogMessage($error); + $message = $this->getErrorLogMessage($throwable); - if ($error->getPrevious() !== null) { - $additionalData['previousException'] = $this->getErrorLogMessage($error->getPrevious()); + if ($throwable->getPrevious() !== null) { + $additionalData['previousException'] = $this->getErrorLogMessage($throwable->getPrevious()); } if (!file_exists($this->storagePath)) { @@ -88,10 +127,13 @@ protected function logError(\Throwable $error, array $additionalData = []) } // FIXME: getReferenceCode should probably become an interface. - $referenceCode = (is_callable([$error, 'getReferenceCode']) ? $error->getReferenceCode() : $this->generateUniqueReferenceCode()); - $errorDumpPathAndFilename = Files::concatenatePaths([$this->storagePath, $referenceCode . '.txt']); - file_put_contents($errorDumpPathAndFilename, $this->renderErrorInfo($error, $additionalData)); - $message .= ' - See also: ' . basename($errorDumpPathAndFilename); + $referenceCode = (is_callable([ + $throwable, + 'getReferenceCode' + ]) ? $throwable->getReferenceCode() : $this->generateUniqueReferenceCode()); + $throwableDumpPathAndFilename = Files::concatenatePaths([$this->storagePath, $referenceCode . '.txt']); + file_put_contents($throwableDumpPathAndFilename, $this->renderErrorInfo($throwable, $additionalData)); + $message .= ' - See also: ' . basename($throwableDumpPathAndFilename); return $message; } @@ -135,7 +177,7 @@ protected function renderErrorInfo(\Throwable $error, array $additionalData = [] $postMortemInfo .= PHP_EOL . $this->renderRequestInfo(); $postMortemInfo .= PHP_EOL; - $postMortemInfo .= (new PlainTextFormatter($additionalData))->format(); + $postMortemInfo .= empty($additionalData) ? '' : (new PlainTextFormatter($additionalData))->format(); return $postMortemInfo; } diff --git a/Neos.Flow/Classes/Log/ThrowableStorageInterface.php b/Neos.Flow/Classes/Log/ThrowableStorageInterface.php index 8d11b28738..615f9a565d 100644 --- a/Neos.Flow/Classes/Log/ThrowableStorageInterface.php +++ b/Neos.Flow/Classes/Log/ThrowableStorageInterface.php @@ -8,6 +8,17 @@ */ interface ThrowableStorageInterface { + /** TODO: Factory method to create an instance, should officially become part of the interface in next major. + * + * A factory method to create an instance of the throwable storage. + * Note that throwable storages must work without proxy so all dependencies need to be resolved manually or via options. + * + * @param array $options + * @return ThrowableStorageInterface + */ +// public static function createWithOptions(array $options): ThrowableStorageInterface; + + /** * Writes information about the given exception into the log. * diff --git a/Neos.Flow/Classes/ObjectManagement/Proxy/ProxyMethod.php b/Neos.Flow/Classes/ObjectManagement/Proxy/ProxyMethod.php index 8260783bf8..a48f2e71d5 100644 --- a/Neos.Flow/Classes/ObjectManagement/Proxy/ProxyMethod.php +++ b/Neos.Flow/Classes/ObjectManagement/Proxy/ProxyMethod.php @@ -249,6 +249,7 @@ public function buildMethodParametersCode($fullClassName, $methodName, $addTypeA { $methodParametersCode = ''; $methodParameterTypeName = ''; + $nullableSign = ''; $defaultValue = ''; $byReferenceSign = ''; @@ -265,11 +266,16 @@ public function buildMethodParametersCode($fullClassName, $methodName, $addTypeA $methodParameterTypeName = 'array'; } elseif ($methodParameterInfo['scalarDeclaration']) { $methodParameterTypeName = $methodParameterInfo['type']; + } elseif ($methodParameterInfo['class'] !== null) { + $methodParameterTypeName = '\\' . $methodParameterInfo['class']; } else { - $methodParameterTypeName = ($methodParameterInfo['class'] === null) ? '' : '\\' . $methodParameterInfo['class']; + $methodParameterTypeName = ''; + } + if (\PHP_MAJOR_VERSION >= 7 && \PHP_MINOR_VERSION >= 1) { + $nullableSign = $methodParameterInfo['allowsNull'] ? '?' : ''; } if ($methodParameterInfo['optional'] === true) { - $rawDefaultValue = (isset($methodParameterInfo['defaultValue']) ? $methodParameterInfo['defaultValue'] : null); + $rawDefaultValue = $methodParameterInfo['defaultValue'] ?? null; if ($rawDefaultValue === null) { $defaultValue = ' = NULL'; } elseif (is_bool($rawDefaultValue)) { @@ -285,7 +291,13 @@ public function buildMethodParametersCode($fullClassName, $methodName, $addTypeA $byReferenceSign = ($methodParameterInfo['byReference'] ? '&' : ''); } - $methodParametersCode .= ($methodParametersCount > 0 ? ', ' : '') . ($methodParameterTypeName ? $methodParameterTypeName . ' ' : '') . $byReferenceSign . '$' . $methodParameterName . $defaultValue; + $methodParametersCode .= ($methodParametersCount > 0 ? ', ' : '') + . ($methodParameterTypeName ? $nullableSign . $methodParameterTypeName . ' ' : '') + . $byReferenceSign + . '$' + . $methodParameterName + . $defaultValue + ; $methodParametersCount++; } } diff --git a/Neos.Flow/Configuration/Objects.yaml b/Neos.Flow/Configuration/Objects.yaml index ca796ab4b8..e98ec2a98c 100644 --- a/Neos.Flow/Configuration/Objects.yaml +++ b/Neos.Flow/Configuration/Objects.yaml @@ -163,13 +163,9 @@ Neos\Flow\Log\SecurityLoggerInterface: Neos\Flow\Log\ThrowableStorageInterface: scope: singleton + # Note that this is a "fake" entry and doesn't get used. Please change the setting Neos.Flow.log.throwables.storageClass className: Neos\Flow\Log\ThrowableStorage\FileStorage -Neos\Flow\Log\ThrowableStorage\FileStorage: - properties: - storagePath: - setting: Neos.Flow.log.throwables.fileStorage.path - Neos\Flow\Log\PsrLoggerFactory: scope: singleton autowiring: off diff --git a/Neos.Flow/Configuration/Settings.Log.yaml b/Neos.Flow/Configuration/Settings.Log.yaml index 4bfedcad70..559dddd405 100644 --- a/Neos.Flow/Configuration/Settings.Log.yaml +++ b/Neos.Flow/Configuration/Settings.Log.yaml @@ -48,5 +48,7 @@ Neos: logFilesToKeep: 1 throwables: - fileStorage: - path: '%FLOW_PATH_DATA%Logs/Exceptions' + storageClass: Neos\Flow\Log\ThrowableStorage\FileStorage + optionsByImplementation: + 'Neos\Flow\Log\ThrowableStorage\FileStorage': + storagePath: '%FLOW_PATH_DATA%Logs/Exceptions' diff --git a/Neos.Flow/Tests/Unit/ObjectManagement/Proxy/ProxyMethodTest.php b/Neos.Flow/Tests/Unit/ObjectManagement/Proxy/ProxyMethodTest.php index 97619e1df4..c2f8ba4646 100644 --- a/Neos.Flow/Tests/Unit/ObjectManagement/Proxy/ProxyMethodTest.php +++ b/Neos.Flow/Tests/Unit/ObjectManagement/Proxy/ProxyMethodTest.php @@ -80,7 +80,7 @@ public function foo($arg1, array $arg2, \ArrayObject $arg3, $arg4= "foo", $arg5 'byReference' => false, 'array' => true, 'optional' => false, - 'allowsNull' => true, + 'allowsNull' => false, 'class' => null, 'scalarDeclaration' => false ], @@ -89,7 +89,7 @@ public function foo($arg1, array $arg2, \ArrayObject $arg3, $arg4= "foo", $arg5 'byReference' => false, 'array' => false, 'optional' => false, - 'allowsNull' => true, + 'allowsNull' => false, 'class' => 'ArrayObject', 'scalarDeclaration' => false ], @@ -118,7 +118,7 @@ public function foo($arg1, array $arg2, \ArrayObject $arg3, $arg4= "foo", $arg5 'byReference' => false, 'array' => true, 'optional' => true, - 'allowsNull' => true, + 'allowsNull' => false, 'class' => null, 'defaultValue' => [0 => true, 'foo' => 'bar', 1 => null, 3 => 1, 4 => 2.3], 'scalarDeclaration' => false diff --git a/Neos.FluidAdaptor/Classes/Core/Parser/Interceptor/ResourceInterceptor.php b/Neos.FluidAdaptor/Classes/Core/Parser/Interceptor/ResourceInterceptor.php index a512873bdf..98ac328408 100644 --- a/Neos.FluidAdaptor/Classes/Core/Parser/Interceptor/ResourceInterceptor.php +++ b/Neos.FluidAdaptor/Classes/Core/Parser/Interceptor/ResourceInterceptor.php @@ -54,7 +54,7 @@ class ResourceInterceptor implements InterceptorInterface * Is the text at hand a resource URI and what are path/package? * * @var string - * @see \Neos\Flow\Pckage\Package::PATTERN_MATCH_PACKAGEKEY + * @see \Neos\Flow\Package\Package::PATTERN_MATCH_PACKAGEKEY */ const PATTERN_MATCH_RESOURCE_URI = '!(?:../)*(?:(?P[A-Za-z0-9]+\.(?:[A-Za-z0-9][\.a-z0-9]*)+)/Resources/)?Public/(?P[^"]+)!';