xref: /webtrees/app/Webtrees.php (revision 0b4092ed8e31e3588eb143d0ade6c4a411367da8)
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
18namespace Fisharebest\Webtrees;
19
20use Closure;
21use ErrorException;
22use Fisharebest\Webtrees\Http\Middleware\BootModules;
23use Fisharebest\Webtrees\Http\Middleware\CheckCsrf;
24use Fisharebest\Webtrees\Http\Middleware\CheckForMaintenanceMode;
25use Fisharebest\Webtrees\Http\Middleware\DoHousekeeping;
26use Fisharebest\Webtrees\Http\Middleware\EmitResponse;
27use Fisharebest\Webtrees\Http\Middleware\HandleExceptions;
28use Fisharebest\Webtrees\Http\Middleware\ModuleMiddleware;
29use Fisharebest\Webtrees\Http\Middleware\NoRouteFound;
30use Fisharebest\Webtrees\Http\Middleware\PhpEnvironment;
31use Fisharebest\Webtrees\Http\Middleware\ReadConfigIni;
32use Fisharebest\Webtrees\Http\Middleware\UpdateDatabaseSchema;
33use Fisharebest\Webtrees\Http\Middleware\UseCache;
34use Fisharebest\Webtrees\Http\Middleware\UseDatabase;
35use Fisharebest\Webtrees\Http\Middleware\UseDebugbar;
36use Fisharebest\Webtrees\Http\Middleware\UseFilesystem;
37use Fisharebest\Webtrees\Http\Middleware\UseLocale;
38use Fisharebest\Webtrees\Http\Middleware\UseSession;
39use Fisharebest\Webtrees\Http\Middleware\UseTheme;
40use Fisharebest\Webtrees\Http\Middleware\UseTransaction;
41use Fisharebest\Webtrees\Http\Middleware\UseTree;
42use Fisharebest\Webtrees\Http\Middleware\WebEnvironment;
43use Nyholm\Psr7\Factory\Psr17Factory;
44use Psr\Http\Message\ResponseFactoryInterface;
45use Psr\Http\Message\ServerRequestFactoryInterface;
46use Psr\Http\Message\StreamFactoryInterface;
47use Psr\Http\Message\UploadedFileFactoryInterface;
48use Psr\Http\Message\UriFactoryInterface;
49use Throwable;
50use function app;
51use function dirname;
52use function error_reporting;
53use function ob_end_clean;
54use function ob_get_level;
55use function set_error_handler;
56use function set_exception_handler;
57use function str_replace;
58use const PHP_EOL;
59
60/**
61 * Definitions for the webtrees application.
62 */
63class Webtrees
64{
65    // The root folder of this installation
66    public const ROOT_DIR = __DIR__ . '/../';
67
68    // Location of the file containing the database connection details.
69    public const CONFIG_FILE = self::ROOT_DIR . 'data/config.ini.php';
70
71    // Location of the file that triggers maintenance mode.
72    public const OFFLINE_FILE = self::ROOT_DIR . 'data/offline.txt';
73
74    // Location of our modules.
75    public const MODULES_PATH = 'modules_v4/';
76    public const MODULES_DIR  = self::ROOT_DIR . self::MODULES_PATH;
77
78    // Enable debugging on development builds.
79    public const DEBUG = self::STABILITY !== '';
80
81    // We want to know about all PHP errors during development, and fewer in production.
82    public const ERROR_REPORTING = self::DEBUG ? E_ALL | E_STRICT | E_NOTICE | E_DEPRECATED : E_ALL;
83
84    // The name of the application.
85    public const NAME = 'webtrees';
86
87    // Required version of database tables/columns/indexes/etc.
88    public const SCHEMA_VERSION = 43;
89
90    // e.g. "dev", "alpha", "beta", etc.
91    public const STABILITY = 'beta.4';
92
93    // Version number
94    public const VERSION = '2.0.0' . (self::STABILITY === '' ? '' : '-') . self::STABILITY;
95
96    // Project website.
97    public const URL = 'https://www.webtrees.net/';
98
99    /**
100     * Initialise the application.
101     *
102     * @return void
103     */
104    public function bootstrap(): void
105    {
106        // Show all errors and warnings in development, fewer in production.
107        error_reporting(self::ERROR_REPORTING);
108
109        set_error_handler($this->phpErrorHandler());
110        set_exception_handler($this->phpExceptionHandler());
111    }
112
113    /**
114     * An error handler that can be passed to set_error_handler().
115     *
116     * @return Closure
117     */
118    private function phpErrorHandler(): Closure
119    {
120        return static function (int $errno, string $errstr, string $errfile, int $errline): bool {
121            // Ignore errors that are silenced with '@'
122            if (error_reporting() & $errno) {
123                throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
124            }
125
126            return true;
127        };
128    }
129
130    /**
131     * An exception handler that can be passed to set_exception_handler().
132     * Display any exception that are not caught by the middleware exception handler.
133     *
134     * @return Closure
135     */
136    private function phpExceptionHandler(): Closure
137    {
138        return static function (Throwable $ex): void {
139            $base_path = dirname(__DIR__);
140            $trace     = $ex->getMessage() . ' ' . $ex->getFile() . ':' . $ex->getLine() . PHP_EOL . $ex->getTraceAsString();
141            $trace     = str_replace($base_path, '', $trace);
142
143            while (ob_get_level() > 0) {
144                ob_end_clean();
145            }
146
147            echo '<html lang="en"><head><title>Error</title><meta charset="UTF-8"></head><body><pre>' . $trace . '</pre></body></html>';
148        };
149    }
150
151    /**
152     * We can use any PSR-7 / PSR-17 compatible message factory.
153     *
154     * @return void
155     */
156    public function selectMessageFactory(): void
157    {
158        app()->bind(ResponseFactoryInterface::class, Psr17Factory::class);
159        app()->bind(ServerRequestFactoryInterface::class, Psr17Factory::class);
160        app()->bind(StreamFactoryInterface::class, Psr17Factory::class);
161        app()->bind(UploadedFileFactoryInterface::class, Psr17Factory::class);
162        app()->bind(UriFactoryInterface::class, Psr17Factory::class);
163    }
164
165    /**
166     * The webtrees application is built from middleware.
167     *
168     * @return string[]
169     */
170    public function middleware(): array
171    {
172        return [
173            PhpEnvironment::class,
174            EmitResponse::class,
175            HandleExceptions::class,
176            ReadConfigIni::class,
177            WebEnvironment::class,
178            UseDatabase::class,
179            UseDebugbar::class,
180            UpdateDatabaseSchema::class,
181            UseCache::class,
182            UseFilesystem::class,
183            UseSession::class,
184            UseTree::class,
185            UseLocale::class,
186            CheckForMaintenanceMode::class,
187            UseTheme::class,
188            DoHousekeeping::class,
189            CheckCsrf::class,
190            UseTransaction::class,
191            BootModules::class,
192            ModuleMiddleware::class,
193            Router::class,
194            NoRouteFound::class,
195        ];
196    }
197}
198