Application   F
last analyzed

Complexity

Total Complexity 81

Size/Duplication

Total Lines 730
Duplicated Lines 0 %

Test Coverage

Coverage 85.42%

Importance

Changes 0
Metric Value
eloc 274
dl 0
loc 730
ccs 246
cts 288
cp 0.8542
rs 2
c 0
b 0
f 0
wmc 81

30 Methods

Rating   Name   Duplication   Size   Complexity  
A isCoreLoaded() 0 2 1
A setInstance() 0 2 1
A loadCore() 0 9 3
A getInstance() 0 7 2
A bootCore() 0 3 1
A __construct() 0 3 1
A start() 0 5 1
A getStdErr() 0 7 2
A setBootStatus() 0 2 1
A getDb() 0 2 1
A getBootStatus() 0 2 1
A upgrade() 0 33 6
A getResponseTransport() 0 6 2
A install() 0 11 2
A allowPathRewrite() 0 8 2
A projectDir() 0 2 1
A isCli() 0 8 3
A getRequest() 0 6 2
A getStdIn() 0 8 3
F run() 0 81 16
A getStdOut() 0 5 2
A elggDir() 0 2 1
A index() 0 2 1
A respond() 0 29 4
A setGlobalConfig() 0 3 1
B factory() 0 52 9
A migrate() 0 24 4
A getEngineLibs() 0 35 1
A route() 0 25 4
A getMigrationSettings() 0 27 2

How to fix   Complexity   

Complex Class

Complex classes like Application often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Application, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Elgg;
4
5
use Elgg\Application\BootHandler;
6
use Elgg\Application\ErrorHandler;
7
use Elgg\Application\ExceptionHandler;
8
use Elgg\Application\ShutdownHandler;
9
use Elgg\Database\DbConfig;
10
use Elgg\Di\InternalContainer;
11
use Elgg\Di\PublicContainer;
12
use Elgg\Exceptions\ConfigurationException;
13
use Elgg\Exceptions\Configuration\InstallationException;
14
use Elgg\Exceptions\HttpException;
15
use Elgg\Exceptions\Http\GatekeeperException;
16
use Elgg\Exceptions\Http\PageNotFoundException;
17
use Elgg\Exceptions\InvalidArgumentException;
18
use Elgg\Filesystem\Directory;
19
use Elgg\Filesystem\Directory\Local;
20
use Elgg\Http\ErrorResponse;
21
use Elgg\Http\OkResponse;
22
use Elgg\Http\RedirectResponse;
23
use Elgg\Http\Request as HttpRequest;
24
use Elgg\Http\ResponseBuilder;
25
use Elgg\Http\ResponseTransport;
26
use Elgg\Project\Paths;
27
use Elgg\Security\UrlSigner;
28
use Elgg\Traits\Loggable;
29
use Symfony\Component\Console\Input\ArgvInput;
30
use Symfony\Component\Console\Input\ArrayInput;
31
use Symfony\Component\Console\Input\InputInterface;
32
use Symfony\Component\Console\Output\ConsoleOutput;
33
use Symfony\Component\Console\Output\NullOutput;
34
use Symfony\Component\Console\Output\OutputInterface;
35
use Symfony\Component\HttpFoundation\RedirectResponse as SymfonyRedirect;
36
use Symfony\Component\HttpFoundation\Response;
37
38
/**
39
 * Load, boot, and implement a front controller for an Elgg application
40
 *
41
 * To run as PHP CLI server:
42
 * <code>php -S localhost:8888 /full/path/to/elgg/index.php</code>
43
 *
44
 * The full path is necessary to work around this: https://bugs.php.net/bug.php?id=55726
45
 *
46
 * @since 2.0.0
47
 */
48
class Application {
49
50
	use Loggable;
51
52
	/**
53
	 * @var InternalContainer
54
	 *
55
	 * @internal DO NOT USE
56
	 */
57
	public $internal_services;
58
59
	/**
60
	 * @var PublicContainer
61
	 *
62
	 * @internal DO NOT USE
63
	 */
64
	public $public_services;
65
66
	/**
67
	 * Reference to the loaded Application
68
	 *
69
	 * @internal Do not use this
70
	 * @var Application
71
	 */
72
	public static $_instance;
73
	
74
	/**
75
	 * Stores status about the boot process
76
	 *
77
	 * @var array
78
	 */
79
	protected $boot_status = [
80
		'application_boot_completed' => false,
81
		'full_boot_completed' => false,
82
		'plugins_boot_completed' => false,
83
		'service_boot_completed' => false,
84
	];
85
86
	/**
87
	 * Get the global Application instance. If not set, it's auto-created and wired to $CONFIG.
88
	 *
89
	 * @return Application|null
90
	 */
91 4587
	public static function getInstance() {
92 4587
		if (self::$_instance === null) {
93 1
			self::$_instance = self::factory();
94 1
			self::setGlobalConfig(self::$_instance);
95
		}
96
97 4587
		return self::$_instance;
98
	}
99
100
	/**
101
	 * Set the global Application instance
102
	 *
103
	 * @param Application $application Global application
104
	 *
105
	 * @return void
106
	 */
107 8369
	public static function setInstance(Application $application = null) {
108 8369
		self::$_instance = $application;
109
	}
110
111
	/**
112
	 * Constructor
113
	 *
114
	 * Upon construction, no actions are taken to load or boot Elgg.
115
	 *
116
	 * @param InternalContainer $internal_services Elgg internal services
117
	 */
118 6920
	public function __construct(InternalContainer $internal_services) {
119 6920
		$this->internal_services = $internal_services;
120 6920
		$this->public_services = PublicContainer::factory();
121
	}
122
123
	/**
124
	 * Define all Elgg global functions and constants, wire up boot events, but don't boot
125
	 *
126
	 * This includes all the .php files in engine/lib (not upgrades). If a script returns a function,
127
	 * it is queued and executed at the end.
128
	 *
129
	 * @return void
130
	 *
131
	 * @internal
132
	 * @throws InstallationException
133
	 */
134 6920
	public static function loadCore() {
135 6920
		$path = Paths::elgg() . 'engine/lib';
136
137
		// include library files, capturing setup functions
138 6920
		foreach (self::getEngineLibs() as $file) {
139
			try {
140 6920
				Includer::requireFileOnce("$path/$file");
141
			} catch (\Error $e) {
142
				throw new InstallationException("Elgg lib file failed include: $path/$file");
143
			}
144
		}
145
	}
146
147
	/**
148
	 * Start and boot the core
149
	 *
150
	 * @return self
151
	 */
152 4
	public static function start() {
153 4
		$app = self::getInstance();
154 4
		$app->bootCore();
155
156 4
		return $app;
157
	}
158
159
	/**
160
	 * Are Elgg's global functions loaded?
161
	 *
162
	 * @return bool
163
	 */
164 1
	public static function isCoreLoaded() {
165 1
		return function_exists('elgg');
166
	}
167
168
	/**
169
	 * Bootstrap the Elgg engine, loads plugins, and calls initial system events
170
	 *
171
	 * This method loads the full Elgg engine, checks the installation
172
	 * state, and triggers a series of events to finish booting Elgg:
173
	 *    - plugins_load system
174
	 *    - plugins_boot system
175
	 *    - init system
176
	 *    - ready system
177
	 *
178
	 * Please note that the Elgg session is started after all plugins are loader, there will therefore
179
	 * be no information about a logged user available until plugins_load,system event is complete.
180
	 *
181
	 * If Elgg is not fully installed, the browser will be redirected to an installation page.
182
	 *
183
	 * @return void
184
	 *
185
	 * @internal
186
	 */
187 2364
	public function bootCore() {
188 2364
		$boot = new BootHandler($this);
189 2364
		$boot();
190
	}
191
	
192
	/**
193
	 * Retrieve the boot status of the application
194
	 *
195
	 * @param string $type status to check
196
	 *
197
	 * @return bool
198
	 * @since 4.3
199
	 */
200 2370
	public function getBootStatus(string $type): bool {
201 2370
		return $this->boot_status[$type] ?? false;
202
	}
203
204
	/**
205
	 * Sets the boot status
206
	 *
207
	 * @param string $type   type of status to set
208
	 * @param bool   $status value of the status
209
	 *
210
	 * @return void
211
	 * @since 4.3
212
	 */
213 2366
	public function setBootStatus(string $type, bool $status): void {
214 2366
		$this->boot_status[$type] = $status;
215
	}
216
217
	/**
218
	 * Get a Database wrapper for performing queries without booting Elgg
219
	 *
220
	 * If settings has not been loaded, it will be loaded to configure the DB connection.
221
	 *
222
	 * @note Before boot, the Database instance will not yet be bound to a Logger.
223
	 *
224
	 * @return \Elgg\Application\Database
225
	 */
226 1
	public function getDb() {
227 1
		return $this->internal_services->publicDb;
228
	}
229
230
	/**
231
	 * Make the global $CONFIG a reference to this application's config service
232
	 *
233
	 * @param Application $application The Application
234
	 *
235
	 * @return void
236
	 */
237 6920
	public static function setGlobalConfig(Application $application) {
238 6920
		global $CONFIG;
239 6920
		$CONFIG = $application->internal_services->config;
240
	}
241
242
	/**
243
	 * Create a new application.
244
	 *
245
	 * @warning You generally want to use getInstance().
246
	 *
247
	 * For normal operation, you must use setInstance() and optionally setGlobalConfig() to wire the
248
	 * application to Elgg's global API.
249
	 *
250
	 * @param array $spec Specification for initial call.
251
	 *
252
	 * @return self
253
	 * @throws InvalidArgumentException
254
	 */
255 6920
	public static function factory(array $spec = []) {
256
257 6920
		$defaults = [
258 6920
			'config' => null,
259 6920
			'handle_exceptions' => true,
260 6920
			'handle_shutdown' => true,
261 6920
			'request' => null,
262 6920
			'internal_services' => null,
263 6920
			'set_start_time' => true,
264 6920
			'settings_path' => '',
265 6920
		];
266 6920
		$spec = array_merge($defaults, $spec);
267
268 6920
		if ($spec['set_start_time']) {
269
			// The time with microseconds when the Elgg engine was started.
270 13
			if (!isset($GLOBALS['START_MICROTIME'])) {
271 2
				$GLOBALS['START_MICROTIME'] = microtime(true);
272
			}
273
		}
274
275 6920
		if ($spec['handle_exceptions']) {
276 1
			set_error_handler(new ErrorHandler());
277 1
			set_exception_handler(new ExceptionHandler());
278
		}
279
280 6920
		self::loadCore();
281
282 6920
		if (!$spec['internal_services']) {
283 2339
			if (!$spec['config']) {
284 1
				$spec['config'] = Config::factory($spec['settings_path']);
285
			}
286
			
287 2339
			$spec['internal_services'] = InternalContainer::factory(['config' => $spec['config']]);
288
		}
289
290 6920
		if ($spec['request']) {
291 129
			if (!$spec['request'] instanceof HttpRequest) {
292
				throw new InvalidArgumentException('Given request is not a ' . HttpRequest::class);
293
			}
294
295 129
			$spec['request']->initializeTrustedProxyConfiguration($spec['internal_services']->config);
296 129
			$spec['request']->correctBaseURL($spec['internal_services']->config);
297 129
			$spec['internal_services']->set('request', $spec['request']);
298
		}
299
300 6920
		$app = new self($spec['internal_services']);
301
302 6920
		if ($spec['handle_shutdown']) {
303 1
			register_shutdown_function(new ShutdownHandler($app));
304
		}
305
306 6920
		return $app;
307
	}
308
309
	/**
310
	 * Route a request
311
	 *
312
	 * @param HttpRequest $request Request
313
	 *
314
	 * @return Response|false
315
	 */
316 12
	public static function route(HttpRequest $request) {
317 12
		self::loadCore();
318
319 12
		if ($request->isRewriteCheck()) {
320
			$response = new OkResponse(HttpRequest::REWRITE_TEST_OUTPUT);
321
			return self::respond($response);
322
		}
323
324 12
		if (self::$_instance) {
325 12
			$app = self::$_instance;
326 12
			$app->internal_services->set('request', $request);
327
		} else {
328
			try {
329
				$app = self::factory([
330
					'request' => $request,
331
				]);
332
333
				self::setGlobalConfig($app);
334
				self::setInstance($app);
335
			} catch (ConfigurationException $ex) {
336
				return self::install();
337
			}
338
		}
339
340 12
		return $app->run();
341
	}
342
343
	/**
344
	 * Build and send a response
345
	 *
346
	 * If application is booted, we will use the response factory service,
347
	 * otherwise we will prepare a non-cacheable response
348
	 *
349
	 * @param ResponseBuilder $builder Response builder
350
	 *
351
	 * @return Response|null Sent response
352
	 */
353 12
	public static function respond(ResponseBuilder $builder): ?Response {
354 12
		if (self::$_instance) {
355 11
			self::$_instance->internal_services->responseFactory->respond($builder);
356
357 11
			return self::$_instance->internal_services->responseFactory->getSentResponse();
358
		}
359
360
		try {
361 1
			$content = $builder->getContent();
362 1
			$status = $builder->getStatusCode();
363 1
			$headers = $builder->getHeaders();
364
365 1
			if ($builder->isRedirection()) {
366
				$forward_url = $builder->getForwardURL();
367
				$response = new SymfonyRedirect($forward_url, $status, $headers);
368
			} else {
369 1
				$response = new Response($content, $status, $headers);
370
			}
371
		} catch (\Exception $ex) {
372
			$response = new Response($ex->getMessage(), 500);
373
		}
374
375 1
		$response->headers->set('Pragma', 'public');
376 1
		$response->headers->set('Cache-Control', 'no-store, must-revalidate');
377 1
		$response->headers->set('Expires', 'Fri, 05 Feb 1982 00:00:00 -0500');
378
379 1
		self::getResponseTransport()->send($response);
380
381 1
		return $response;
382
	}
383
384
	/**
385
	 * Elgg's front controller. Handles basically all incoming URL requests.
386
	 *
387
	 * @return Response|false True if Elgg will handle the request, false if the server should (PHP-CLI server)
388
	 */
389 11
	public static function index() {
390 11
		return self::route(self::getRequest());
391
	}
392
393
	/**
394
	 * Routes the request, booting core if not yet booted
395
	 *
396
	 * @return Response|null|false False if Elgg wants the PHP CLI server to handle the request
397
	 */
398 12
	public function run() {
399 12
		$config = $this->internal_services->config;
400 12
		$request = $this->internal_services->request;
401
402
		try {
403 12
			if ($request->isCliServer()) {
404
				if ($request->isCliServable(Paths::project())) {
405
					return false;
406
				}
407
408
				// overwrite value from settings
409
				$www_root = rtrim($request->getSchemeAndHttpHost() . $request->getBaseUrl(), '/') . '/';
410
				$config->wwwroot = $www_root;
411
				$config->wwwroot_cli_server = $www_root;
412
			}
413
414 12
			if (str_starts_with($request->getElggPath(), '/cache/')) {
415 1
				$config->_disable_session_save = true;
416 1
				$response = $this->internal_services->cacheHandler->handleRequest($request, $this)->prepare($request);
417 1
				self::getResponseTransport()->send($response);
418
419 1
				return $response;
420
			}
421
			
422 11
			if ($request->getElggPath() === '/refresh_token') {
423 1
				$config->_disable_session_save = true;
424 1
				$token = new \Elgg\Controllers\RefreshCsrfToken();
425 1
				$response = $token($request);
426 1
				self::getResponseTransport()->send($response);
427
428 1
				return $response;
429
			}
430
431 10
			if (str_starts_with($request->getElggPath(), '/serve-file/')) {
432 1
				$config->_disable_session_save = true;
433 1
				$response = $this->internal_services->serveFileHandler->getResponse($request);
434 1
				self::getResponseTransport()->send($response);
435
436 1
				return $response;
437
			}
438
			
439 9
			if ($this->isCli()) {
440 9
				$config->_disable_session_save = true;
441
			}
442
443 9
			$this->bootCore();
444
445
			// re-fetch new request from services in case it was replaced by route:rewrite
446 9
			$request = $this->internal_services->request;
447
448 9
			if (!$this->internal_services->router->route($request)) {
449 2
				throw new PageNotFoundException();
450
			}
451 7
		} catch (HttpException $ex) {
452 6
			$forward_url = $ex->getRedirectUrl();
453 6
			if (!$forward_url) {
454 5
				if ($ex instanceof GatekeeperException) {
455 2
					$forward_url = elgg_is_logged_in() ? null : elgg_get_login_url();
456 3
				} else if ($request->getFirstUrlSegment() == 'action') {
457
					$forward_url = REFERRER;
458
				}
459
			}
460
461 6
			$forward_url = (string) $this->internal_services->events->triggerResults('forward', $ex->getCode(), ['exception' => $ex], $forward_url);
462
463 6
			if ($forward_url && !$request->isXmlHttpRequest()) {
464 2
				if ($ex->getMessage()) {
465 2
					$this->internal_services->system_messages->addErrorMessage($ex->getMessage());
466
				}
467
				
468 2
				$response = new RedirectResponse($forward_url);
469
			} else {
470 4
				$response = new ErrorResponse($ex->getMessage(), $ex->getCode(), $forward_url);
471
			}
472
			
473 6
			$response->setException($ex);
474
475 6
			self::respond($response);
476
		}
477
478 8
		return $this->internal_services->responseFactory->getSentResponse();
479
	}
480
481
	/**
482
	 * Returns a directory that points to the root of Elgg, but not necessarily
483
	 * the install root. See `self::root()` for that.
484
	 *
485
	 * @return Directory
486
	 */
487 21
	public static function elggDir() {
488 21
		return Local::elggRoot();
489
	}
490
491
	/**
492
	 * Returns a directory that points to the project root, where composer is installed.
493
	 *
494
	 * @return Directory
495
	 */
496
	public static function projectDir() {
497
		return Local::projectRoot();
498
	}
499
500
	/**
501
	 * Renders a web UI for installing Elgg.
502
	 *
503
	 * @return Response|false
504
	 */
505
	public static function install() {
506
		ini_set('display_errors', 1);
507
508
		try {
509
			$installer = new \ElggInstaller();
510
			$response = $installer->run();
511
		} catch (\Exception $ex) {
512
			$response = new ErrorResponse($ex->getMessage(), 500);
513
		}
514
515
		return self::respond($response);
516
	}
517
518
	/**
519
	 * Elgg upgrade script.
520
	 *
521
	 * This script triggers any necessary upgrades. If the site has been upgraded
522
	 * to the most recent version of the code, no upgrades are run but the caches
523
	 * are flushed.
524
	 *
525
	 * Upgrades use a table {db_prefix}upgrade_lock as a mutex to prevent concurrent upgrades.
526
	 *
527
	 * The URL to forward to after upgrades are complete can be specified by setting $_GET['forward']
528
	 * to a relative URL.
529
	 *
530
	 * @return Response|false
531
	 */
532 2
	public static function upgrade() {
533
534
		try {
535 2
			self::migrate();
536 2
			self::start();
537
538 2
			$request = self::$_instance->internal_services->request;
539 2
			$signer = self::$_instance->internal_services->urlSigner;
540
541 2
			$url = $request->getCurrentURL();
542 2
			$query = $request->getParams();
543
544
			// We need to resign the URL because the path is different
545 2
			$mac = elgg_extract(UrlSigner::KEY_MAC, $query);
546 2
			if (isset($mac) && !$signer->isValid($url)) {
547 1
				throw new HttpException(elgg_echo('invalid_request_signature'), ELGG_HTTP_FORBIDDEN);
548
			}
549
550 1
			unset($query[UrlSigner::KEY_MAC]);
551
552 1
			$base_url = elgg_normalize_site_url('upgrade/init');
553 1
			$url = elgg_http_add_url_query_elements($base_url, $query);
554
555 1
			if (isset($mac)) {
556
				$url = self::$_instance->internal_services->urlSigner->sign($url);
557
			}
558
559 1
			$response = new RedirectResponse($url, ELGG_HTTP_PERMANENTLY_REDIRECT);
560 1
		} catch (\Exception $ex) {
561 1
			$response = new ErrorResponse($ex->getMessage(), $ex->getCode() ?: ELGG_HTTP_INTERNAL_SERVER_ERROR);
562
		}
563
564 2
		return self::respond($response);
565
	}
566
567
	/**
568
	 * Runs database migrations
569
	 *
570
	 * @throws InstallationException
571
	 * @return bool
572
	 */
573 10
	public static function migrate() {
574
		
575 10
		$constants = self::elggDir()->getPath('engine/lib/constants.php');
576 10
		Includer::requireFileOnce($constants);
577
		
578 10
		$conf = self::elggDir()->getPath('engine/schema/migrations.php');
579 10
		if (!$conf) {
580
			throw new InstallationException('Settings file is required to run database migrations.');
581
		}
582
583
		// setting timeout because some database migrations can take a long time
584 10
		set_time_limit(0);
585
586 10
		$app = new \Phinx\Console\PhinxApplication();
587 10
		$wrapper = new \Phinx\Wrapper\TextWrapper($app, [
588 10
			'configuration' => $conf,
589 10
		]);
590 10
		$log = $wrapper->getMigrate();
591
592 10
		if (!empty($_SERVER['argv']) && in_array('--verbose', $_SERVER['argv'])) {
593
			error_log($log);
594
		}
595
596 10
		return true;
597
	}
598
599
	/**
600
	 * Returns configuration array for database migrations
601
	 *
602
	 * @return array
603
	 */
604 10
	public static function getMigrationSettings() {
605
606 10
		$config = Config::factory();
607 10
		$db_config = DbConfig::fromElggConfig($config);
608
609 10
		if ($db_config->isDatabaseSplit()) {
610
			$conn = $db_config->getConnectionConfig(DbConfig::WRITE);
611
		} else {
612 10
			$conn = $db_config->getConnectionConfig();
613
		}
614
615 10
		return [
616 10
			'paths' => [
617 10
				'migrations' => Paths::elgg() . 'engine/schema/migrations/',
618 10
			],
619 10
			'environments' => [
620 10
				'default_migration_table' => "{$conn['prefix']}migrations",
621 10
				'default_database' => 'prod',
622 10
				'prod' => [
623 10
					'adapter' => 'mysql',
624 10
					'host' => $conn['host'],
625 10
					'port' => $conn['port'],
626 10
					'name' => $conn['database'],
627 10
					'user' => $conn['user'],
628 10
					'pass' => $conn['password'],
629 10
					'charset' => $conn['encoding'],
630 10
					'table_prefix' => $conn['prefix'],
631 10
				],
632 10
			],
633 10
		];
634
	}
635
636
	/**
637
	 * Allow plugins to rewrite the path.
638
	 *
639
	 * @return void
640
	 * @internal
641
	 */
642 2364
	public function allowPathRewrite() {
643 2364
		$request = $this->internal_services->request;
644 2364
		$new = $this->internal_services->router->allowRewrite($request);
645 2364
		if ($new === $request) {
646 2364
			return;
647
		}
648
649
		$this->internal_services->set('request', $new);
650
	}
651
652
	/**
653
	 * Is application running in CLI
654
	 *
655
	 * @return bool
656
	 */
657 7289
	public static function isCli() {
658
		switch (PHP_SAPI) {
659 7289
			case 'cli':
660
			case 'phpdbg':
661 7289
				return true;
662
663
			default:
664
				return false;
665
		}
666
	}
667
668
	/**
669
	 * Build request object
670
	 *
671
	 * @return \Elgg\Http\Request
672
	 */
673 6921
	public static function getRequest() {
674 6921
		if (self::$_instance) {
675 6921
			return self::$_instance->internal_services->request;
676
		}
677
678 2338
		return HttpRequest::createFromGlobals();
679
	}
680
681
	/**
682
	 * Load console input interface
683
	 *
684
	 * @return InputInterface
685
	 */
686 6921
	public static function getStdIn() {
687 6921
		if (self::isCli()) {
688 6921
			$request = self::getRequest();
689 6921
			$argv = $request->server->get('argv') ?: [];
690 6921
			return new ArgvInput($argv);
691
		}
692
693
		return new ArrayInput([]);
694
	}
695
696
	/**
697
	 * Load console output interface
698
	 *
699
	 * @return OutputInterface
700
	 */
701 6920
	public static function getStdOut() {
702 6920
		if (self::isCli()) {
703 6920
			return new ConsoleOutput();
704
		} else {
705
			return new NullOutput();
706
		}
707
	}
708
709
	/**
710
	 * Load console error output interface
711
	 *
712
	 * @return OutputInterface
713
	 */
714 6920
	public static function getStdErr() {
715 6920
		$std_out = self::getStdOut();
716 6920
		if (is_callable([$std_out, 'getErrorOutput'])) {
717 6920
			return $std_out->getErrorOutput();
718
		}
719
720
		return $std_out;
721
	}
722
723
	/**
724
	 * Build a transport for sending responses
725
	 *
726
	 * @return ResponseTransport
727
	 */
728 2528
	public static function getResponseTransport() {
729 2528
		if (self::isCli()) {
730 2528
			return new \Elgg\Http\OutputBufferTransport();
731
		}
732
733
		return new \Elgg\Http\HttpProtocolTransport();
734
	}
735
736
	/**
737
	 * Get all engine/lib library filenames
738
	 *
739
	 * @note We can't just pull in all directory files because some users leave old files in place.
740
	 *
741
	 * @return string[]
742
	 */
743 6920
	private static function getEngineLibs() {
744 6920
		return [
745 6920
			'elgglib.php',
746 6920
			'events.php',
747 6920
			'access.php',
748 6920
			'actions.php',
749 6920
			'admin.php',
750 6920
			'annotations.php',
751 6920
			'breadcrumbs.php',
752 6920
			'cache.php',
753 6920
			'configuration.php',
754 6920
			'constants.php',
755 6920
			'context.php',
756 6920
			'deprecated-5.0.php',
757 6920
			'entities.php',
758 6920
			'external_files.php',
759 6920
			'filestore.php',
760 6920
			'gatekeepers.php',
761 6920
			'input.php',
762 6920
			'languages.php',
763 6920
			'mb_wrapper.php',
764 6920
			'metadata.php',
765 6920
			'navigation.php',
766 6920
			'notification.php',
767 6920
			'output.php',
768 6920
			'pagehandler.php',
769 6920
			'pageowner.php',
770 6920
			'pam.php',
771 6920
			'plugins.php',
772 6920
			'relationships.php',
773 6920
			'river.php',
774 6920
			'sessions.php',
775 6920
			'users.php',
776 6920
			'views.php',
777 6920
			'widgets.php',
778 6920
		];
779
	}
780
}
781