xref: /webtrees/index.php (revision c5e5c1cea5f7d35d6fd56eb77c9a68e91d21befe)
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\Services\MigrationService;
35use Fisharebest\Webtrees\Services\ModuleService;
36use Fisharebest\Webtrees\Services\TimeoutService;
37use Fisharebest\Webtrees\Webtrees;
38use Illuminate\Cache\ArrayStore;
39use Illuminate\Cache\Repository;
40use Symfony\Component\HttpFoundation\Request;
41use Symfony\Component\HttpFoundation\Response;
42
43require __DIR__ . '/vendor/autoload.php';
44
45const WT_ROOT = __DIR__ . DIRECTORY_SEPARATOR;
46
47Webtrees::init();
48
49// Initialise the DebugBar for development.
50// Use `composer install --dev` on a development build to enable.
51// Note that you may need to increase the size of the fcgi buffers on nginx.
52// e.g. add these lines to your fastcgi_params file:
53// fastcgi_buffers 16 16m;
54// fastcgi_buffer_size 32m;
55DebugBar::init(class_exists('\\DebugBar\\StandardDebugBar'));
56
57// Use an array cache for database calls, etc.
58app()->instance('cache.array', new Repository(new ArrayStore()));
59
60// Start the timer.
61app()->instance(TimeoutService::class, new TimeoutService(microtime(true)));
62
63// Extract the request parameters.
64$request = Request::createFromGlobals();
65app()->instance(Request::class, $request);
66
67// Calculate the base URL, so we can generate absolute URLs.
68$request_uri = $request->getSchemeAndHttpHost() . $request->getRequestUri();
69
70// Remove any PHP script name and parameters.
71$base_uri = preg_replace('/[^\/]+\.php(\?.*)?$/', '', $request_uri);
72define('WT_BASE_URL', $base_uri);
73
74try {
75    // No config file? Run the setup wizard
76    if (!file_exists(Webtrees::CONFIG_FILE)) {
77        define('WT_DATA_DIR', 'data/');
78
79        /** @var SetupController $controller */
80        $controller = app()->make(SetupController::class);
81        $response   = $controller->setup($request);
82        $response->prepare($request)->send();
83
84        return;
85    }
86
87    $database_config = parse_ini_file(Webtrees::CONFIG_FILE);
88
89    if ($database_config === false) {
90        throw new Exception('Invalid config file: ' . Webtrees::CONFIG_FILE);
91    }
92
93    // Read the connection settings and create the database
94    Database::connect($database_config);
95
96    // Update the database schema, if necessary.
97    app()->make(MigrationService::class)->updateSchema('\Fisharebest\Webtrees\Schema', 'WT_SCHEMA_VERSION', Webtrees::SCHEMA_VERSION);
98
99    $middleware_stack = [
100        CheckForMaintenanceMode::class,
101        SetPhpLimits::class,
102        UseFilesystem::class,
103        UseSession::class,
104        UseTree::class,
105        UseLocale::class,
106    ];
107
108    if (class_exists(DebugBar::class)) {
109        $middleware_stack[] = DebugBarData::class;
110    }
111
112    if ($request->getMethod() === Request::METHOD_GET) {
113        $middleware_stack[] = Housekeeping::class;
114        $middleware_stack[] = UseTheme::class;
115    }
116
117    if ($request->getMethod() === Request::METHOD_POST) {
118        $middleware_stack[] = UseTransaction::class;
119        $middleware_stack[] = CheckCsrf::class;
120    }
121
122    // Allow modules to provide middleware.
123    foreach (app()->make(ModuleService::class)->findByInterface(MiddlewareInterface::class) as $middleware) {
124        $middleware_stack[] = $middleware;
125    }
126
127    // We build the "onion" from the inside outwards, and some middlewares are dependant on others.
128    $middleware_stack = array_reverse($middleware_stack);
129
130    // Create the middleware *after* loading the modules, to give modules the opportunity to replace middleware.
131    $middleware_stack = array_map(function ($middleware): MiddlewareInterface {
132        return $middleware instanceof MiddlewareInterface ? $middleware : app()->make($middleware);
133    }, $middleware_stack);
134
135    // Apply the middleware using the "onion" pattern.
136    $pipeline = array_reduce($middleware_stack, function (Closure $next, MiddlewareInterface $middleware): Closure {
137        // Create a closure to apply the middleware.
138        return function (Request $request) use ($middleware, $next): Response {
139            return $middleware->handle($request, $next);
140        };
141    }, function (Request $request): Response {
142        // Load the route and routing table.
143        $route  = $request->get('route');
144        $routes = require 'routes/web.php';
145
146        // Find the controller and action for the selected route
147        $controller_action = $routes[$request->getMethod() . ':' . $route] ?? 'ErrorController@noRouteFound';
148        [$controller_name, $action] = explode('@', $controller_action);
149        $controller_class = '\\Fisharebest\\Webtrees\\Http\\Controllers\\' . $controller_name;
150
151        $controller = app()->make($controller_class);
152
153        return app()->dispatch($controller, $action);
154    });
155
156    $response = call_user_func($pipeline, $request);
157} catch (Exception $exception) {
158    $response = (new Handler())->render($request, $exception);
159}
160
161// Send response
162$response->prepare($request)->send();
163