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