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