Completed
Push — develop ( 8b0b98...d9b800 )
by Greg
26:49 queued 16:13
created

Webtrees::dispatch()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * webtrees: online genealogy
5
 * Copyright (C) 2021 webtrees development team
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16
 */
17
18
declare(strict_types=1);
19
20
namespace Fisharebest\Webtrees;
21
22
use Closure;
23
use ErrorException;
24
use Fisharebest\Webtrees\Factories\CacheFactory;
25
use Fisharebest\Webtrees\Factories\ElementFactory;
26
use Fisharebest\Webtrees\Factories\FamilyFactory;
27
use Fisharebest\Webtrees\Factories\FilesystemFactory;
28
use Fisharebest\Webtrees\Factories\GedcomRecordFactory;
29
use Fisharebest\Webtrees\Factories\HeaderFactory;
30
use Fisharebest\Webtrees\Factories\ImageFactory;
31
use Fisharebest\Webtrees\Factories\IndividualFactory;
32
use Fisharebest\Webtrees\Factories\LocationFactory;
33
use Fisharebest\Webtrees\Factories\MediaFactory;
34
use Fisharebest\Webtrees\Factories\NoteFactory;
35
use Fisharebest\Webtrees\Factories\RepositoryFactory;
36
use Fisharebest\Webtrees\Factories\SlugFactory;
37
use Fisharebest\Webtrees\Factories\SourceFactory;
38
use Fisharebest\Webtrees\Factories\SubmissionFactory;
39
use Fisharebest\Webtrees\Factories\SubmitterFactory;
40
use Fisharebest\Webtrees\Factories\XrefFactory;
41
use Fisharebest\Webtrees\Http\Middleware\BadBotBlocker;
42
use Fisharebest\Webtrees\Http\Middleware\BootModules;
43
use Fisharebest\Webtrees\Http\Middleware\CheckForMaintenanceMode;
44
use Fisharebest\Webtrees\Http\Middleware\ClientIp;
45
use Fisharebest\Webtrees\Http\Middleware\CompressResponse;
46
use Fisharebest\Webtrees\Http\Middleware\DoHousekeeping;
47
use Fisharebest\Webtrees\Http\Middleware\EmitResponse;
48
use Fisharebest\Webtrees\Http\Middleware\HandleExceptions;
49
use Fisharebest\Webtrees\Http\Middleware\LoadRoutes;
50
use Fisharebest\Webtrees\Http\Middleware\NoRouteFound;
51
use Fisharebest\Webtrees\Http\Middleware\ReadConfigIni;
52
use Fisharebest\Webtrees\Http\Middleware\Router;
53
use Fisharebest\Webtrees\Http\Middleware\SecurityHeaders;
54
use Fisharebest\Webtrees\Http\Middleware\UpdateDatabaseSchema;
55
use Fisharebest\Webtrees\Http\Middleware\UseDatabase;
56
use Fisharebest\Webtrees\Http\Middleware\UseDebugbar;
57
use Fisharebest\Webtrees\Http\Middleware\UseLanguage;
58
use Fisharebest\Webtrees\Http\Middleware\UseSession;
59
use Fisharebest\Webtrees\Http\Middleware\UseTheme;
60
use Fisharebest\Webtrees\Http\Middleware\UseTransaction;
61
use Fisharebest\Webtrees\Http\Middleware\BaseUrl;
62
use Illuminate\Container\Container;
63
use Middleland\Dispatcher;
64
use Nyholm\Psr7\Factory\Psr17Factory;
65
use Nyholm\Psr7Server\ServerRequestCreator;
66
use Psr\Container\ContainerInterface;
67
use Psr\Http\Message\ResponseFactoryInterface;
68
use Psr\Http\Message\ResponseInterface;
69
use Psr\Http\Message\ServerRequestFactoryInterface;
70
use Psr\Http\Message\ServerRequestInterface;
71
use Psr\Http\Message\StreamFactoryInterface;
72
use Psr\Http\Message\UploadedFileFactoryInterface;
73
use Psr\Http\Message\UriFactoryInterface;
74
use Psr\Http\Server\MiddlewareInterface;
75
76
use function date_default_timezone_set;
77
use function error_reporting;
78
use function is_string;
79
use function mb_internal_encoding;
80
use function set_error_handler;
81
82
use const E_ALL;
83
use const E_DEPRECATED;
84
use const E_USER_DEPRECATED;
85
86
/**
87
 * Definitions for the webtrees application.
88
 */
89
class Webtrees
90
{
91
    // The root folder of this installation
92
    public const ROOT_DIR = __DIR__ . '/../';
93
94
    // This is the location of system data, such as temporary and cache files.
95
    // The system files are always in this location.
96
    // It is also the default location of user data, such as media and GEDCOM files.
97
    // The user files could be anywhere supported by Flysystem.
98
    public const DATA_DIR  = self::ROOT_DIR . 'data/';
99
100
    // Location of the file containing the database connection details.
101
    public const CONFIG_FILE = self::DATA_DIR . 'config.ini.php';
102
103
    // Location of the file that triggers maintenance mode.
104
    public const OFFLINE_FILE = self::DATA_DIR . 'offline.txt';
105
106
    // Location of our modules.
107
    public const MODULES_PATH = 'modules_v4/';
108
    public const MODULES_DIR  = self::ROOT_DIR . self::MODULES_PATH;
109
110
    // Enable debugging on development builds.
111
    public const DEBUG = self::STABILITY !== '';
112
113
    // We want to know about all PHP errors during development, and fewer in production.
114
    public const ERROR_REPORTING = self::DEBUG ? E_ALL : E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED;
115
116
    // The name of the application.
117
    public const NAME = 'webtrees';
118
119
    // Required version of database tables/columns/indexes/etc.
120
    public const SCHEMA_VERSION = 45;
121
122
    // e.g. "-dev", "-alpha", "-beta", etc.
123
    public const STABILITY = '-dev';
124
125
    // Version number
126
    public const VERSION = '2.1.0' . self::STABILITY;
127
128
    // Project website.
129
    public const URL = 'https://webtrees.net/';
130
131
    // FAQ links
132
    public const URL_FAQ_EMAIL = 'https://webtrees.net/faq/email';
133
134
    // Project website.
135
    public const GEDCOM_PDF = 'https://webtrees.net/downloads/gedcom-551.pdf';
136
137
    private const MIDDLEWARE = [
138
        EmitResponse::class,
139
        SecurityHeaders::class,
140
        ReadConfigIni::class,
141
        BaseUrl::class,
142
        HandleExceptions::class,
143
        ClientIp::class,
144
        CompressResponse::class,
145
        BadBotBlocker::class,
146
        UseDatabase::class,
147
        UseDebugbar::class,
148
        UpdateDatabaseSchema::class,
149
        UseSession::class,
150
        UseLanguage::class,
151
        CheckForMaintenanceMode::class,
152
        UseTheme::class,
153
        DoHousekeeping::class,
154
        UseTransaction::class,
155
        LoadRoutes::class,
156
        BootModules::class,
157
        Router::class,
158
        NoRouteFound::class,
159
    ];
160
161
    /**
162
     * Initialise the application.
163
     *
164
     * @return void
165
     */
166
    public function bootstrap(): void
167
    {
168
        // Show all errors and warnings in development, fewer in production.
169
        error_reporting(self::ERROR_REPORTING);
170
        set_error_handler($this->phpErrorHandler());
171
172
        // All modern software uses UTF-8 encoding.
173
        mb_internal_encoding('UTF-8');
174
175
        // Use UTC internally and convert to local time when displaying datetimes.
176
        date_default_timezone_set('UTC');
177
178
        // Factory objects
179
        Registry::cache(new CacheFactory());
180
        Registry::familyFactory(new FamilyFactory());
181
        Registry::filesystem(new FilesystemFactory());
182
        Registry::elementFactory(new ElementFactory());
183
        Registry::gedcomRecordFactory(new GedcomRecordFactory());
184
        Registry::headerFactory(new HeaderFactory());
185
        Registry::imageFactory(new ImageFactory());
186
        Registry::individualFactory(new IndividualFactory());
187
        Registry::locationFactory(new LocationFactory());
188
        Registry::mediaFactory(new MediaFactory());
189
        Registry::noteFactory(new NoteFactory());
190
        Registry::repositoryFactory(new RepositoryFactory());
191
        Registry::slugFactory(new SlugFactory());
192
        Registry::sourceFactory(new SourceFactory());
193
        Registry::submissionFactory(new SubmissionFactory());
194
        Registry::submitterFactory(new SubmitterFactory());
195
        Registry::xrefFactory(new XrefFactory());
196
    }
197
198
    /**
199
     * Respond to a CLI request.
200
     *
201
     * @return void
202
     */
203
    public function cliRequest(): void
204
    {
205
        // CLI handler will go here.
206
    }
207
208
    /**
209
     * Response to an HTTP request.
210
     *
211
     * @return ResponseInterface
212
     */
213
    public function httpRequest(): ResponseInterface
214
    {
215
        // PSR7 messages and PSR17 message-factories
216
        self::set(ResponseFactoryInterface::class, Psr17Factory::class);
217
        self::set(ServerRequestFactoryInterface::class, Psr17Factory::class);
218
        self::set(StreamFactoryInterface::class, Psr17Factory::class);
219
        self::set(UploadedFileFactoryInterface::class, Psr17Factory::class);
220
        self::set(UriFactoryInterface::class, Psr17Factory::class);
221
222
        $request = $this->captureRequest();
223
224
        return self::dispatch($request, self::MIDDLEWARE);
225
    }
226
227
    /**
228
     * @param ServerRequestInterface            $request
229
     * @param array<string|MiddlewareInterface> $middleware
230
     *
231
     * @return ResponseInterface
232
     */
233
    public static function dispatch(ServerRequestInterface $request, array $middleware): ResponseInterface
234
    {
235
        $dispatcher = new Dispatcher($middleware, self::container());
236
237
        return $dispatcher->dispatch($request);
238
    }
239
240
    /**
241
     * An error handler that can be passed to set_error_handler().
242
     *
243
     * @return Closure
244
     */
245
    private function phpErrorHandler(): Closure
246
    {
247
        return static function (int $errno, string $errstr, string $errfile, int $errline): bool {
248
            // Ignore errors that are silenced with '@'
249
            if (error_reporting() & $errno) {
250
                throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
251
            }
252
253
            return true;
254
        };
255
    }
256
257
    /**
258
     * Build the request from the PHP super-globals.
259
     *
260
     * @return ServerRequestInterface
261
     */
262
    private function captureRequest(): ServerRequestInterface
263
    {
264
        return self::make(ServerRequestCreator::class)->fromGlobals();
265
    }
266
267
    /**
268
     * @return ContainerInterface
269
     */
270
    public static function container(): ContainerInterface
271
    {
272
        return Container::getInstance();
273
    }
274
275
    /**
276
     * Make an object, using dependency injection.
277
     *
278
     * @param string $class
279
     *
280
     * @return mixed
281
     */
282
    public static function make(string $class)
283
    {
284
        return Container::getInstance()->make($class);
285
    }
286
287
    /**
288
     * Write a value into the container.
289
     *
290
     * @param string        $abstract
291
     * @param string|object $concrete
292
     */
293
    public static function set(string $abstract, $concrete): void
294
    {
295
        if (is_string($concrete)) {
296
            Container::getInstance()->bind($abstract, $concrete);
297
        } else {
298
            Container::getInstance()->instance($abstract, $concrete);
299
        }
300
    }
301
}
302