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