Files
dev-epau.archi.fr/system/src/Grav/Common/Processors/InitializeProcessor.php
T
2021-03-19 16:07:04 +01:00

461 lines
13 KiB
PHP

<?php
/**
* @package Grav\Common\Processors
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common\Processors;
use Grav\Common\Config\Config;
use Grav\Common\Debugger;
use Grav\Common\Errors\Errors;
use Grav\Common\Grav;
use Grav\Common\Page\Pages;
use Grav\Common\Plugins;
use Grav\Common\Session;
use Grav\Common\Uri;
use Grav\Common\Utils;
use Grav\Framework\File\Formatter\YamlFormatter;
use Grav\Framework\File\YamlFile;
use Grav\Framework\Psr7\Response;
use Grav\Framework\Session\Exceptions\SessionException;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\SyslogHandler;
use Monolog\Logger;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use function defined;
use function in_array;
/**
* Class InitializeProcessor
* @package Grav\Common\Processors
*/
class InitializeProcessor extends ProcessorBase
{
/** @var string */
public $id = '_init';
/** @var string */
public $title = 'Initialize';
/** @var bool */
private static $cli_initialized = false;
/**
* @param Grav $grav
* @return void
*/
public static function initializeCli(Grav $grav)
{
if (!static::$cli_initialized) {
static::$cli_initialized = true;
$instance = new static($grav);
$instance->processCli();
}
}
/**
* @param ServerRequestInterface $request
* @param RequestHandlerInterface $handler
* @return ResponseInterface
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$this->startTimer('_init', 'Initialize');
// Load configuration.
$config = $this->initializeConfig();
// Initialize logger.
$this->initializeLogger($config);
// Initialize error handlers.
$this->initializeErrors();
// Initialize debugger.
$debugger = $this->initializeDebugger();
// Debugger can return response right away.
$response = $this->handleDebuggerRequest($debugger, $request);
if ($response) {
$this->stopTimer('_init');
return $response;
}
// Initialize output buffering.
$this->initializeOutputBuffering($config);
// Set timezone, locale.
$this->initializeLocale($config);
// Load plugins.
$this->initializePlugins();
// Load pages.
$this->initializePages($config);
// Initialize URI.
$this->initializeUri($config);
// Grav may return redirect response right away.
$redirectCode = (int)$config->get('system.pages.redirect_trailing_slash', 1);
if ($redirectCode) {
$response = $this->handleRedirectRequest($request, $redirectCode > 300 ? $redirectCode : null);
if ($response) {
$this->stopTimer('_init');
return $response;
}
}
// Load accounts (decides class to be used).
// TODO: remove in 2.0.
$this->container['accounts'];
// Initialize session.
$this->initializeSession($config);
$this->stopTimer('_init');
// Wrap call to next handler so that debugger can profile it.
/** @var Response $response */
$response = $debugger->profile(static function () use ($handler, $request) {
return $handler->handle($request);
});
// Log both request and response and return the response.
return $debugger->logRequest($request, $response);
}
public function processCli(): void
{
// Load configuration.
$config = $this->initializeConfig();
// Initialize logger.
$this->initializeLogger($config);
// Disable debugger.
$this->container['debugger']->enabled(false);
// Set timezone, locale.
$this->initializeLocale($config);
// Load plugins.
$this->initializePlugins();
// Load pages.
$this->initializePages($config);
// Initialize URI.
$this->initializeUri($config);
// Load accounts (decides class to be used).
// TODO: remove in 2.0.
$this->container['accounts'];
}
/**
* @return Config
*/
protected function initializeConfig(): Config
{
$this->startTimer('_init_config', 'Configuration');
// Initialize Configuration
$grav = $this->container;
/** @var Config $config */
$config = $grav['config'];
$config->init();
$grav['plugins']->setup();
if (defined('GRAV_SCHEMA') && $config->get('versions') === null) {
$filename = GRAV_ROOT . '/user/config/versions.yaml';
if (!is_file($filename)) {
$versions = [
'core' => [
'grav' => [
'version' => GRAV_VERSION,
'schema' => GRAV_SCHEMA
]
]
];
$config->set('versions', $versions);
$file = new YamlFile($filename, new YamlFormatter(['inline' => 4]));
$file->save($versions);
}
}
// Override configuration using the environment.
$prefix = 'GRAV_CONFIG';
$env = getenv($prefix);
if ($env) {
$cPrefix = $prefix . '__';
$aPrefix = $prefix . '_ALIAS__';
$cLen = strlen($cPrefix);
$aLen = strlen($aPrefix);
$keys = $aliases = [];
$env = $_ENV + $_SERVER;
foreach ($env as $key => $value) {
if (!str_starts_with($key, $prefix)) {
continue;
}
if (str_starts_with($key, $cPrefix)) {
$key = str_replace('__', '.', substr($key, $cLen));
$keys[$key] = $value;
} elseif (str_starts_with($key, $aPrefix)) {
$key = substr($key, $aLen);
$aliases[$key] = $value;
}
}
$list = [];
foreach ($keys as $key => $value) {
foreach ($aliases as $alias => $real) {
$key = str_replace($alias, $real, $key);
}
$list[$key] = $value;
$config->set($key, $value);
}
}
$this->stopTimer('_init_config');
return $config;
}
/**
* @param Config $config
* @return Logger
*/
protected function initializeLogger(Config $config): Logger
{
$this->startTimer('_init_logger', 'Logger');
$grav = $this->container;
// Initialize Logging
/** @var Logger $log */
$log = $grav['log'];
if ($config->get('system.log.handler', 'file') === 'syslog') {
$log->popHandler();
$facility = $config->get('system.log.syslog.facility', 'local6');
$logHandler = new SyslogHandler('grav', $facility);
$formatter = new LineFormatter("%channel%.%level_name%: %message% %extra%");
$logHandler->setFormatter($formatter);
$log->pushHandler($logHandler);
}
$this->stopTimer('_init_logger');
return $log;
}
/**
* @return Errors
*/
protected function initializeErrors(): Errors
{
$this->startTimer('_init_errors', 'Error Handlers Reset');
$grav = $this->container;
// Initialize Error Handlers
/** @var Errors $errors */
$errors = $grav['errors'];
$errors->resetHandlers();
$this->stopTimer('_init_errors');
return $errors;
}
/**
* @return Debugger
*/
protected function initializeDebugger(): Debugger
{
$this->startTimer('_init_debugger', 'Init Debugger');
$grav = $this->container;
/** @var Debugger $debugger */
$debugger = $grav['debugger'];
$debugger->init();
$this->stopTimer('_init_debugger');
return $debugger;
}
/**
* @param Debugger $debugger
* @param ServerRequestInterface $request
* @return ResponseInterface|null
*/
protected function handleDebuggerRequest(Debugger $debugger, ServerRequestInterface $request): ?ResponseInterface
{
// Clockwork integration.
$clockwork = $debugger->getClockwork();
if ($clockwork) {
$server = $request->getServerParams();
// $baseUri = str_replace('\\', '/', dirname(parse_url($server['SCRIPT_NAME'], PHP_URL_PATH)));
// if ($baseUri === '/') {
// $baseUri = '';
// }
$requestTime = $server['REQUEST_TIME_FLOAT'] ?? GRAV_REQUEST_TIME;
$request = $request->withAttribute('request_time', $requestTime);
// Handle clockwork API calls.
$uri = $request->getUri();
if (Utils::contains($uri->getPath(), '/__clockwork/')) {
return $debugger->debuggerRequest($request);
}
$this->container['clockwork'] = $clockwork;
}
return null;
}
/**
* @param Config $config
*/
protected function initializeOutputBuffering(Config $config): void
{
$this->startTimer('_init_ob', 'Initialize Output Buffering');
// Use output buffering to prevent headers from being sent too early.
ob_start();
if ($config->get('system.cache.gzip') && !@ob_start('ob_gzhandler')) {
// Enable zip/deflate with a fallback in case of if browser does not support compressing.
ob_start();
}
$this->stopTimer('_init_ob');
}
/**
* @param Config $config
*/
protected function initializeLocale(Config $config): void
{
$this->startTimer('_init_locale', 'Initialize Locale');
// Initialize the timezone.
$timezone = $config->get('system.timezone');
if ($timezone) {
date_default_timezone_set($timezone);
}
$grav = $this->container;
$grav->setLocale();
$this->stopTimer('_init_locale');
}
protected function initializePlugins(): Plugins
{
$this->startTimer('_init_plugins_load', 'Load Plugins');
$grav = $this->container;
/** @var Plugins $plugins */
$plugins = $grav['plugins'];
$plugins->init();
$this->stopTimer('_init_plugins_load');
return $plugins;
}
protected function initializePages(Config $config): Pages
{
$this->startTimer('_init_pages_register', 'Load Pages');
$grav = $this->container;
/** @var Pages $pages */
$pages = $grav['pages'];
// Upgrading from older Grav versions won't work without checking if the method exists.
if (method_exists($pages, 'register')) {
$pages->register();
}
$this->stopTimer('_init_pages_register');
return $pages;
}
protected function initializeUri(Config $config): void
{
$this->startTimer('_init_uri', 'Initialize URI');
$grav = $this->container;
/** @var Uri $uri */
$uri = $grav['uri'];
$uri->init();
$this->stopTimer('_init_uri');
}
protected function handleRedirectRequest(RequestInterface $request, int $code = null): ?ResponseInterface
{
if (!in_array($request->getMethod(), ['GET', 'HEAD'])) {
return null;
}
// Redirect pages with trailing slash if configured to do so.
$uri = $request->getUri();
$path = $uri->getPath() ?: '/';
$root = $this->container['uri']->rootUrl();
if ($path !== $root && $path !== $root . '/' && Utils::endsWith($path, '/')) {
// Use permanent redirect for SEO reasons.
return $this->container->getRedirectResponse((string)$uri->withPath(rtrim($path, '/')), $code);
}
return null;
}
/**
* @param Config $config
*/
protected function initializeSession(Config $config): void
{
// FIXME: Initialize session should happen later after plugins have been loaded. This is a workaround to fix session issues in AWS.
if (isset($this->container['session']) && $config->get('system.session.initialize', true)) {
$this->startTimer('_init_session', 'Start Session');
/** @var Session $session */
$session = $this->container['session'];
try {
$session->init();
} catch (SessionException $e) {
$session->init();
$message = 'Session corruption detected, restarting session...';
$this->addMessage($message);
$this->container['messages']->add($message, 'error');
}
$this->stopTimer('_init_session');
}
}
}