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