xref: /webtrees/index.php (revision 310b7a5a19cf8cff7a1f909e219fcab37d8a8181)
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\BootModules;
23use Fisharebest\Webtrees\Http\Middleware\CheckCsrf;
24use Fisharebest\Webtrees\Http\Middleware\CheckForMaintenanceMode;
25use Fisharebest\Webtrees\Http\Middleware\DebugBarData;
26use Fisharebest\Webtrees\Http\Middleware\Housekeeping;
27use Fisharebest\Webtrees\Http\Middleware\MiddlewareInterface;
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 Illuminate\Support\Collection;
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
75try {
76    // No config file? Run the setup wizard
77    if (!file_exists(Webtrees::CONFIG_FILE)) {
78        define('WT_DATA_DIR', 'data/');
79
80        /** @var SetupController $controller */
81        $controller = app(SetupController::class);
82        $response   = $controller->setup($request);
83        $response->prepare($request)->send();
84
85        return;
86    }
87
88    $database_config = parse_ini_file(Webtrees::CONFIG_FILE);
89
90    if ($database_config === false) {
91        throw new Exception('Invalid config file: ' . Webtrees::CONFIG_FILE);
92    }
93
94    // Read the connection settings and create the database
95    Database::connect($database_config);
96
97    // Update the database schema, if necessary.
98    app(MigrationService::class)->updateSchema('\Fisharebest\Webtrees\Schema', 'WT_SCHEMA_VERSION', Webtrees::SCHEMA_VERSION);
99
100    // Middleware allows code to intercept the request before it reaches the controller, and to
101    // intercept the response afterwards.
102    //
103    //                   +----------------------------------+
104    //                   |           Middleware1            |
105    //                   | +------------------------------+ |
106    //                   | |         Middleware2          | |
107    //                   | | +--------------------------+ | |
108    //                   | | |                          | | |
109    //       Request ----|-|-|-> Controller::action() --|-|-|---> Response
110    //                   | | |                          | | |
111    //                   | | +--------------------------+ | |
112    //                   | |                              | |
113    //                   | +------------------------------+ |
114    //                   |                                  |
115    //                   +----------------------------------+
116
117    // Create the middleware, from the "inside" to the "outside".
118    /** @var Collection $middleware_stack */
119    $middleware_stack = app(ModuleService::class)
120        ->findByInterface(MiddlewareInterface::class);
121
122    // Core middleware.
123    $middleware_stack = $middleware_stack->merge([
124        CheckCsrf::class,
125        UseTransaction::class,
126        Housekeeping::class,
127        DebugBarData::class,
128        BootModules::class,
129        UseTheme::class,
130        UseLocale::class,
131        UseTree::class,
132        UseSession::class,
133        UseFilesystem::class,
134        CheckForMaintenanceMode::class,
135    ]);
136
137    // Construct the core middleware *after* loading the modules, to reduce dependencies.
138    $middleware_stack = $middleware_stack->map(function ($middleware): MiddlewareInterface {
139        return $middleware instanceof MiddlewareInterface ? $middleware : app($middleware);
140    });
141
142    // Create a pipeline, which applies the middleware as a nested function call.
143    $pipeline = $middleware_stack->reduce(function (Closure $next, MiddlewareInterface $middleware): Closure {
144        // Create a closure to apply the middleware.
145        return function (Request $request) use ($middleware, $next): Response {
146            return $middleware->handle($request, $next);
147        };
148    }, function (Request $request): Response {
149        // Load the route and routing table.
150        $route  = $request->get('route');
151        $routes = require 'routes/web.php';
152
153        // Find the controller and action for the selected route
154        $controller_action = $routes[$request->getMethod() . ':' . $route] ?? 'ErrorController@noRouteFound';
155        [$controller_name, $action] = explode('@', $controller_action);
156        $controller_class = '\\Fisharebest\\Webtrees\\Http\\Controllers\\' . $controller_name;
157
158        $controller = app($controller_class);
159
160        return app()->dispatch($controller, $action);
161    });
162
163    $response = $pipeline($request);
164} catch (Throwable $exception) {
165    $response = (new Handler())->render($request, $exception);
166}
167
168// Send response
169$response->prepare($request)->send();
170