1<?php 2/** 3 * webtrees: online genealogy 4 * Copyright (C) 2019 webtrees development team 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 */ 16declare(strict_types=1); 17 18use Fisharebest\Webtrees\Database; 19use Fisharebest\Webtrees\DebugBar; 20use Fisharebest\Webtrees\Exceptions\Handler; 21use Fisharebest\Webtrees\Http\Controllers\SetupController; 22use Fisharebest\Webtrees\Http\Middleware\CheckCsrf; 23use Fisharebest\Webtrees\Http\Middleware\CheckForMaintenanceMode; 24use Fisharebest\Webtrees\Http\Middleware\DebugBarData; 25use Fisharebest\Webtrees\Http\Middleware\Housekeeping; 26use Fisharebest\Webtrees\Http\Middleware\MiddlewareInterface; 27use Fisharebest\Webtrees\Http\Middleware\SetPhpLimits; 28use Fisharebest\Webtrees\Http\Middleware\UseFilesystem; 29use Fisharebest\Webtrees\Http\Middleware\UseLocale; 30use Fisharebest\Webtrees\Http\Middleware\UseSession; 31use Fisharebest\Webtrees\Http\Middleware\UseTheme; 32use Fisharebest\Webtrees\Http\Middleware\UseTransaction; 33use Fisharebest\Webtrees\Http\Middleware\UseTree; 34use Fisharebest\Webtrees\I18N; 35use Fisharebest\Webtrees\Services\MigrationService; 36use Fisharebest\Webtrees\Services\ModuleService; 37use Fisharebest\Webtrees\Services\TimeoutService; 38use Fisharebest\Webtrees\Webtrees; 39use Illuminate\Cache\ArrayStore; 40use Illuminate\Cache\Repository; 41use Symfony\Component\HttpFoundation\Request; 42use Symfony\Component\HttpFoundation\Response; 43 44require __DIR__ . '/vendor/autoload.php'; 45 46const WT_ROOT = __DIR__ . DIRECTORY_SEPARATOR; 47 48Webtrees::init(); 49 50// Initialise the DebugBar for development. 51// Use `composer install --dev` on a development build to enable. 52// Note that you may need to increase the size of the fcgi buffers on nginx. 53// e.g. add these lines to your fastcgi_params file: 54// fastcgi_buffers 16 16m; 55// fastcgi_buffer_size 32m; 56DebugBar::init(class_exists('\\DebugBar\\StandardDebugBar')); 57 58// Use an array cache for database calls, etc. 59app()->instance('cache.array', new Repository(new ArrayStore())); 60 61// Start the timer. 62app()->instance(TimeoutService::class, new TimeoutService(microtime(true))); 63 64// Extract the request parameters. 65$request = Request::createFromGlobals(); 66app()->instance(Request::class, $request); 67 68// Calculate the base URL, so we can generate absolute URLs. 69$request_uri = $request->getSchemeAndHttpHost() . $request->getRequestUri(); 70 71// Remove any PHP script name and parameters. 72$base_uri = preg_replace('/[^\/]+\.php(\?.*)?$/', '', $request_uri); 73define('WT_BASE_URL', $base_uri); 74 75// Connect to the database 76try { 77 // No config file? Run the setup wizard 78 if (!file_exists(Webtrees::CONFIG_FILE)) { 79 define('WT_DATA_DIR', 'data/'); 80 /** @var SetupController $controller */ 81 $controller = app()->make(SetupController::class); 82 $response = $controller->setup($request); 83 $response->prepare($request)->send(); 84 85 return; 86 } 87} catch (PDOException $exception) { 88 defined('WT_DATA_DIR') || define('WT_DATA_DIR', 'data/'); 89 I18N::init(); 90 if ($exception->getCode() === 1045) { 91 // Error during connection? 92 $content = view('errors/database-connection', ['error' => $exception->getMessage()]); 93 } else { 94 // Error in a migration script? 95 $content = view('errors/database-error', ['error' => $exception->getMessage()]); 96 } 97 $html = view('layouts/error', ['content' => $content]); 98 $response = new Response($html, Response::HTTP_SERVICE_UNAVAILABLE); 99 $response->prepare($request)->send(); 100 101 return; 102} catch (Throwable $exception) { 103 defined('WT_DATA_DIR') || define('WT_DATA_DIR', 'data/'); 104 I18N::init(); 105 $content = view('errors/database-connection', ['error' => $exception->getMessage()]); 106 $html = view('layouts/error', ['content' => $content]); 107 $response = new Response($html, Response::HTTP_SERVICE_UNAVAILABLE); 108 $response->prepare($request)->send(); 109 110 return; 111} 112 113try { 114 $database_config = parse_ini_file(Webtrees::CONFIG_FILE); 115 116 if ($database_config === false) { 117 throw new Exception('Invalid config file: ' . Webtrees::CONFIG_FILE); 118 } 119 120 // Read the connection settings and create the database 121 Database::connect($database_config); 122 123 // Update the database schema, if necessary. 124 app()->make(MigrationService::class)->updateSchema('\Fisharebest\Webtrees\Schema', 'WT_SCHEMA_VERSION', Webtrees::SCHEMA_VERSION); 125 126 $middleware_stack = [ 127 CheckForMaintenanceMode::class, 128 SetPhpLimits::class, 129 UseFilesystem::class, 130 UseSession::class, 131 UseTree::class, 132 UseLocale::class, 133 ]; 134 135 if (class_exists(DebugBar::class)) { 136 $middleware_stack[] = DebugBarData::class; 137 } 138 139 if ($request->getMethod() === Request::METHOD_GET) { 140 $middleware_stack[] = Housekeeping::class; 141 $middleware_stack[] = UseTheme::class; 142 } 143 144 if ($request->getMethod() === Request::METHOD_POST) { 145 $middleware_stack[] = UseTransaction::class; 146 $middleware_stack[] = CheckCsrf::class; 147 } 148 149 // Allow modules to provide middleware. 150 foreach (app()->make(ModuleService::class)->findByInterface(MiddlewareInterface::class) as $middleware) { 151 $middleware_stack[] = $middleware; 152 } 153 154 // We build the "onion" from the inside outwards, and some middlewares are dependant on others. 155 $middleware_stack = array_reverse($middleware_stack); 156 157 // Create the middleware *after* loading the modules, to give modules the opportunity to replace middleware. 158 $middleware_stack = array_map(function ($middleware): MiddlewareInterface { 159 return $middleware instanceof MiddlewareInterface ? $middleware : app()->make($middleware); 160 }, $middleware_stack); 161 162 // Apply the middleware using the "onion" pattern. 163 $pipeline = array_reduce($middleware_stack, function (Closure $next, MiddlewareInterface $middleware): Closure { 164 // Create a closure to apply the middleware. 165 return function (Request $request) use ($middleware, $next): Response { 166 return $middleware->handle($request, $next); 167 }; 168 }, function (Request $request): Response { 169 // Load the route and routing table. 170 $route = $request->get('route'); 171 $routes = require 'routes/web.php'; 172 173 // Find the controller and action for the selected route 174 $controller_action = $routes[$request->getMethod() . ':' . $route] ?? 'ErrorController@noRouteFound'; 175 [$controller_name, $action] = explode('@', $controller_action); 176 $controller_class = '\\Fisharebest\\Webtrees\\Http\\Controllers\\' . $controller_name; 177 178 $controller = app()->make($controller_class); 179 180 return app()->dispatch($controller, $action); 181 }); 182 183 $response = call_user_func($pipeline, $request); 184} catch (Exception $exception) { 185 $response = (new Handler())->render($request, $exception); 186} 187 188// Send response 189$response->prepare($request)->send(); 190