xref: /webtrees/index.php (revision bc241c54273892142cd7cd386c7e97823e1415df)
1<?php
2/**
3 * webtrees: online genealogy
4 * Copyright (C) 2018 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
18namespace Fisharebest\Webtrees;
19
20use Closure;
21use Fisharebest\Webtrees\Http\Controllers\ErrorController;
22use Fisharebest\Webtrees\Http\Middleware\CheckCsrf;
23use Fisharebest\Webtrees\Http\Middleware\CheckForMaintenanceMode;
24use Fisharebest\Webtrees\Http\Middleware\UseTransaction;
25use Symfony\Component\HttpFoundation\JsonResponse;
26use Symfony\Component\HttpFoundation\RedirectResponse;
27use Symfony\Component\HttpFoundation\Request;
28use Symfony\Component\HttpFoundation\Response;
29use Symfony\Component\HttpKernel\Exception\HttpException;
30use Throwable;
31use Whoops\Handler\PrettyPageHandler;
32use Whoops\Run;
33
34// Bootstrap the application
35require 'includes/session.php';
36
37DebugBar::startMeasure('routing');
38
39// The HTTP request.
40$request = Request::createFromGlobals();
41$method  = $request->getMethod();
42$route   = $request->get('route');
43
44try {
45	// Most requests will need the current tree and user.
46	$all_trees = Tree::getAll();
47
48	$tree = $all_trees[$request->get('ged')] ?? null;
49
50	// No tree specified/available?  Choose one.
51	if ($tree === null && $method === 'GET') {
52		$tree = $all_trees[Site::getPreference('DEFAULT_GEDCOM')] ?? array_values($all_trees)[0] ?? null;
53	}
54
55	$request->attributes->set('tree', $tree);
56	$request->attributes->set('user', Auth::user());
57
58	// Most layouts will require a tree for the page header/footer
59	View::share('tree', $tree);
60
61	// Load the routing table.
62	$routes = require 'routes/web.php';
63
64	// Find the action for the selected route
65	$controller_action = $routes[$method . ':' . $route] ?? 'ErrorController@noRouteFound';
66
67
68	// Create the controller
69	list($controller_name, $action) = explode('@', $controller_action);
70	$controller_class = __NAMESPACE__ . '\\Http\\Controllers\\' . $controller_name;
71	$controller       = new $controller_class;
72
73	DebugBar::stopMeasure('routing');
74
75	// Note that we can't stop this timer, as running the action will
76	// generate the response - which includes (and stops) the timer
77	DebugBar::startMeasure('controller_action', $controller_action);
78
79	$middleware_stack = [
80		new CheckForMaintenanceMode,
81	];
82
83	if ($method === 'POST') {
84		$middleware_stack[] = new UseTransaction;
85		$middleware_stack[] = new CheckCsrf;
86	}
87
88	// Apply the middleware using the "onion" pattern.
89	$pipeline = array_reduce($middleware_stack, function (Closure $next, $middleware): Closure {
90		// Create a closure to apply the middleware.
91		return function (Request $request) use ($middleware, $next): Response {
92			return $middleware->handle($request, $next);
93		};
94	}, function (Request $request) use ($controller, $action): Response {
95		// Create a closure to generate the response.
96		return call_user_func([$controller, $action], $request);
97	});
98
99	$response = call_user_func($pipeline, $request);
100} catch (Throwable $ex) {
101	DebugBar::addThrowable($ex);
102
103	// Clear any buffered output.
104	while (ob_get_level() > 0) {
105		ob_end_clean();
106	}
107
108	if ($ex instanceof HttpException) {
109		// Show a friendly page for expected exceptions.
110		if ($request->isXmlHttpRequest()) {
111			$response = new Response($ex->getMessage(), $ex->getStatusCode());
112		} else {
113			$controller = new ErrorController;
114			$response   = $controller->errorResponse($ex);
115		}
116	} else {
117		// Show an error page for unexpected exceptions.
118		if (getenv('DEBUG')) {
119			// Local dev environment?  Show full debug.
120			$whoops = new Run;
121			$whoops->pushHandler(new PrettyPageHandler);
122			$whoops->handleException($ex);
123		} else {
124			// Running remotely?  Show a friendly error page.
125			$controller = new ErrorController;
126			$response   = $controller->unhandledExceptionResponse($request, $ex);
127		}
128	}
129}
130
131// Send response
132if ($response instanceof RedirectResponse) {
133	// Show the debug data on the next page
134	DebugBar::stackData();
135} elseif ($response instanceof JsonResponse) {
136	// Use HTTP headers and some jQuery to add debug to the current page.
137	DebugBar::sendDataInHeaders();
138}
139
140return $response->prepare($request)->send();
141