Elgg  Version 4.3
Application.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg;
4 
37 
48 class Application {
49 
50  use Loggable;
51 
58 
65 
72  public static $_instance;
73 
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 
91  public static function getInstance() {
92  if (self::$_instance === null) {
93  self::$_instance = self::factory();
94  self::setGlobalConfig(self::$_instance);
95  }
96 
97  return self::$_instance;
98  }
99 
107  public static function setInstance(Application $application = null) {
108  self::$_instance = $application;
109  }
110 
118  public function __construct(InternalContainer $internal_services) {
119  $this->internal_services = $internal_services;
120  $this->public_services = PublicContainer::factory();
121  }
122 
134  public static function loadCore() {
135  $path = Paths::elgg() . 'engine/lib';
136 
137  // include library files, capturing setup functions
138  foreach (self::getEngineLibs() as $file) {
139  try {
140  Includer::requireFileOnce("$path/$file");
141  } catch (\Error $e) {
142  throw new InstallationException("Elgg lib file failed include: $path/$file");
143  }
144  }
145  }
146 
152  public static function start() {
153  $app = self::getInstance();
154  $app->bootCore();
155 
156  return $app;
157  }
158 
164  public static function isCoreLoaded() {
165  return function_exists('elgg');
166  }
167 
187  public function bootCore() {
188  $boot = new BootHandler($this);
189  $boot();
190  }
191 
201  public function getBootStatus(string $type): bool {
202  return $this->boot_status[$type] ?? false;
203  }
204 
215  public function setBootStatus(string $type, bool $status): void {
216  $this->boot_status[$type] = $status;
217  }
218 
228  public function getDb() {
229  return $this->internal_services->publicDb;
230  }
231 
239  public static function setGlobalConfig(Application $application) {
240  global $CONFIG;
241  $CONFIG = $application->internal_services->config;
242  }
243 
257  public static function factory(array $spec = []) {
258 
259  $defaults = [
260  'config' => null,
261  'handle_exceptions' => true,
262  'handle_shutdown' => true,
263  'request' => null,
264  'internal_services' => null,
265  'set_start_time' => true,
266  'settings_path' => null,
267  ];
268  $spec = array_merge($defaults, $spec);
269 
270  if ($spec['set_start_time']) {
276  if (!isset($GLOBALS['START_MICROTIME'])) {
277  $GLOBALS['START_MICROTIME'] = microtime(true);
278  }
279  }
280 
281  if ($spec['handle_exceptions']) {
282  set_error_handler(new ErrorHandler());
283  set_exception_handler(new ExceptionHandler());
284  }
285 
286  self::loadCore();
287 
288  if (!$spec['internal_services']) {
289  if (!$spec['config']) {
290  $spec['config'] = Config::factory($spec['settings_path']);
291  }
292  $spec['internal_services'] = InternalContainer::factory(['config' => $spec['config']]);
293  }
294 
295  if ($spec['request']) {
296  if ($spec['request'] instanceof HttpRequest) {
297  $spec['request']->initializeTrustedProxyConfiguration($spec['internal_services']->config);
298  $spec['request']->correctBaseURL($spec['internal_services']->config);
299  $spec['internal_services']->set('request', $spec['request']);
300  } else {
301  throw new InvalidArgumentException("Given request is not a " . HttpRequest::class);
302  }
303  }
304 
305  $app = new self($spec['internal_services']);
306 
307  if ($spec['handle_shutdown']) {
308  register_shutdown_function(new ShutdownHandler($app));
309  }
310 
311  return $app;
312  }
313 
321  public static function route(HttpRequest $request) {
322  self::loadCore();
323 
324  if ($request->isRewriteCheck()) {
325  $response = new OkResponse(HttpRequest::REWRITE_TEST_OUTPUT);
326  return self::respond($response);
327  }
328 
329  if (self::$_instance) {
330  $app = self::$_instance;
331  $app->internal_services->set('request', $request);
332  } else {
333  try {
334  $app = self::factory([
335  'request' => $request,
336  ]);
337 
338  self::setGlobalConfig($app);
339  self::setInstance($app);
340  } catch (ConfigurationException $ex) {
341  return self::install();
342  }
343  }
344 
345  return $app->run();
346  }
347 
358  public static function respond(ResponseBuilder $builder) {
359  if (self::$_instance) {
360  self::$_instance->internal_services->responseFactory->respond($builder);
361 
362  return self::$_instance->internal_services->responseFactory->getSentResponse();
363  }
364 
365  try {
366  $content = $builder->getContent();
367  $status = $builder->getStatusCode();
368  $headers = $builder->getHeaders();
369 
370  if ($builder->isRedirection()) {
371  $forward_url = $builder->getForwardURL();
372  $response = new SymfonyRedirect($forward_url, $status, $headers);
373  } else {
374  $response = new Response($content, $status, $headers);
375  }
376  } catch (\Exception $ex) {
377  $response = new Response($ex->getMessage(), 500);
378  }
379 
380  $response->headers->set('Pragma', 'public');
381  $response->headers->set('Cache-Control', 'no-store, must-revalidate');
382  $response->headers->set('Expires', 'Fri, 05 Feb 1982 00:00:00 -0500');
383 
384  self::getResponseTransport()->send($response);
385 
386  return $response;
387  }
388 
394  public static function index() {
395  return self::route(self::getRequest());
396  }
397 
404  public function run() {
405  $config = $this->internal_services->config;
406  $request = $this->internal_services->request;
407 
408  try {
409  if ($request->isCliServer()) {
410  if ($request->isCliServable(Paths::project())) {
411  return false;
412  }
413 
414  // overwrite value from settings
415  $www_root = rtrim($request->getSchemeAndHttpHost() . $request->getBaseUrl(), '/') . '/';
416  $config->wwwroot = $www_root;
417  $config->wwwroot_cli_server = $www_root;
418  }
419 
420  if (0 === strpos($request->getElggPath(), '/cache/')) {
421  $config->_disable_session_save = true;
422  $response = $this->internal_services->cacheHandler->handleRequest($request, $this)->prepare($request);
423  self::getResponseTransport()->send($response);
424 
425  return $response;
426  }
427 
428  if ($request->getElggPath() === '/refresh_token') {
429  $config->_disable_session_save = true;
430  $token = new \Elgg\Controllers\RefreshCsrfToken();
431  $response = $token($request);
432  self::getResponseTransport()->send($response);
433 
434  return $response;
435  }
436 
437  if (0 === strpos($request->getElggPath(), '/serve-file/')) {
438  $config->_disable_session_save = true;
439  $response = $this->internal_services->serveFileHandler->getResponse($request);
440  self::getResponseTransport()->send($response);
441 
442  return $response;
443  }
444 
445  if ($this->isCli()) {
446  $config->_disable_session_save = true;
447  }
448 
449  $this->bootCore();
450 
451  // re-fetch new request from services in case it was replaced by route:rewrite
452  $request = $this->internal_services->request;
453 
454  if (!$this->internal_services->router->route($request)) {
455  throw new PageNotFoundException();
456  }
457  } catch (HttpException $ex) {
458  $forward_url = $ex->getRedirectUrl();
459  if (!$forward_url) {
460  if ($ex instanceof GatekeeperException) {
462  } else if ($request->getFirstUrlSegment() == 'action') {
464  }
465  }
466 
467  $hook_params = [
468  'exception' => $ex,
469  ];
470 
471  $forward_url = $this->internal_services->hooks->trigger('forward', $ex->getCode(), $hook_params, $forward_url);
472 
473  if ($forward_url && !$request->isXmlHttpRequest()) {
474  if ($ex->getMessage()) {
475  $this->internal_services->system_messages->addErrorMessage($ex->getMessage());
476  }
477  $response = new RedirectResponse($forward_url);
478  } else {
479  $response = new ErrorResponse($ex->getMessage(), $ex->getCode(), $forward_url);
480  }
481 
482  $response->setException($ex);
483 
484  self::respond($response);
485  }
486 
487  return $this->internal_services->responseFactory->getSentResponse();
488  }
489 
496  public static function elggDir() {
497  return Local::elggRoot();
498  }
499 
505  public static function projectDir() {
506  return Local::projectRoot();
507  }
508 
514  public static function install() {
515  ini_set('display_errors', 1);
516 
517  try {
518  $installer = new \ElggInstaller();
519  $response = $installer->run();
520  } catch (\Exception $ex) {
521  $response = new ErrorResponse($ex->getMessage(), 500);
522  }
523 
524  return self::respond($response);
525  }
526 
541  public static function upgrade() {
542 
543  try {
544  self::migrate();
545  self::start();
546 
547  $request = self::$_instance->internal_services->request;
548  $signer = self::$_instance->internal_services->urlSigner;
549 
550  $url = $request->getCurrentURL();
551  $query = $request->getParams();
552 
553  // We need to resign the URL because the path is different
554  $mac = elgg_extract(UrlSigner::KEY_MAC, $query);
555  if (isset($mac) && !$signer->isValid($url)) {
556  throw new HttpException(elgg_echo('invalid_request_signature'), ELGG_HTTP_FORBIDDEN);
557  }
558 
559  unset($query[UrlSigner::KEY_MAC]);
560 
561  $base_url = elgg_normalize_site_url('upgrade/init');
563 
564  if (isset($mac)) {
565  $url = self::$_instance->internal_services->urlSigner->sign($url);
566  }
567 
569  } catch (\Exception $ex) {
570  $response = new ErrorResponse($ex->getMessage(), $ex->getCode() ? : ELGG_HTTP_INTERNAL_SERVER_ERROR);
571  }
572 
573  return self::respond($response);
574  }
575 
582  public static function migrate() {
583 
584  $constants = self::elggDir()->getPath('engine/lib/constants.php');
585  Includer::requireFileOnce($constants);
586 
587  $conf = self::elggDir()->getPath('engine/conf/migrations.php');
588  if (!$conf) {
589  throw new InstallationException('Settings file is required to run database migrations.');
590  }
591 
592  // setting timeout because some database migrations can take a long time
593  set_time_limit(0);
594 
595  $app = new \Phinx\Console\PhinxApplication();
596  $wrapper = new \Phinx\Wrapper\TextWrapper($app, [
597  'configuration' => $conf,
598  ]);
599  $log = $wrapper->getMigrate();
600 
601  if (!empty($_SERVER['argv']) && in_array('--verbose', $_SERVER['argv'])) {
602  error_log($log);
603  }
604 
605  return true;
606  }
607 
612  public static function getMigrationSettings() {
613 
614  $config = Config::factory();
615  $db_config = DbConfig::fromElggConfig($config);
616 
617  if ($db_config->isDatabaseSplit()) {
618  $conn = $db_config->getConnectionConfig(DbConfig::WRITE);
619  } else {
620  $conn = $db_config->getConnectionConfig();
621  }
622 
623  return [
624  "paths" => [
625  "migrations" => Paths::elgg() . 'engine/schema/migrations/',
626  ],
627  "environments" => [
628  "default_migration_table" => "{$conn['prefix']}migrations",
629  "default_database" => "prod",
630  "prod" => [
631  "adapter" => "mysql",
632  "host" => $conn['host'],
633  "port" => $conn['port'],
634  "name" => $conn['database'],
635  "user" => $conn['user'],
636  "pass" => $conn['password'],
637  "charset" => $conn['encoding'],
638  "table_prefix" => $conn['prefix'],
639  ],
640  ],
641  ];
642  }
643 
651  public function allowPathRewrite() {
652  $request = $this->internal_services->request;
653  $new = $this->internal_services->router->allowRewrite($request);
654  if ($new === $request) {
655  return;
656  }
657 
658  $this->internal_services->set('request', $new);
659  }
660 
665  public static function isCli() {
666  switch (php_sapi_name()) {
667  case 'cli' :
668  case 'phpdbg' :
669  return true;
670 
671  default:
672  return false;
673  }
674  }
675 
680  public static function getRequest() {
681  if (self::$_instance) {
682  return self::$_instance->internal_services->request;
683  }
684 
685  return HttpRequest::createFromGlobals();
686  }
687 
692  public static function getStdIn() {
693  if (self::isCli()) {
694  $request = self::getRequest();
695  $argv = $request->server->get('argv') ? : [];
696  return new ArgvInput($argv);
697  }
698 
699  return new ArrayInput([]);
700  }
701 
706  public static function getStdOut() {
707  if (self::isCli()) {
708  return new ConsoleOutput();
709  } else {
710  return new NullOutput();
711  }
712  }
713 
718  public static function getStdErr() {
719  $std_out = self::getStdOut();
720  if (is_callable([$std_out, 'getErrorOutput'])) {
721  return $std_out->getErrorOutput();
722  }
723 
724  return $std_out;
725  }
726 
731  public static function getResponseTransport() {
732  if (self::isCli()) {
733  return new \Elgg\Http\OutputBufferTransport();
734  }
735 
736  return new \Elgg\Http\HttpProtocolTransport();
737  }
738 
746  private static function getEngineLibs() {
747  return [
748  'elgglib.php',
749  'access.php',
750  'actions.php',
751  'admin.php',
752  'annotations.php',
753  'breadcrumbs.php',
754  'cache.php',
755  'configuration.php',
756  'constants.php',
757  'context.php',
758  'deprecated-4.0.php',
759  'deprecated-4.1.php',
760  'deprecated-4.2.php',
761  'deprecated-4.3.php',
762  'deprecation.php',
763  'entities.php',
764  'external_files.php',
765  'filestore.php',
766  'gatekeepers.php',
767  'input.php',
768  'languages.php',
769  'mb_wrapper.php',
770  'metadata.php',
771  'navigation.php',
772  'notification.php',
773  'output.php',
774  'pagehandler.php',
775  'pageowner.php',
776  'pam.php',
777  'plugins.php',
778  'relationships.php',
779  'river.php',
780  'sessions.php',
781  'users.php',
782  'views.php',
783  'widgets.php',
784  ];
785  }
786 }
static index()
Elgg&#39;s front controller.
static factory(array $spec=[])
Create a new application.
elgg_http_add_url_query_elements($url, array $elements)
Sets elements in a URL&#39;s query string.
Definition: elgglib.php:481
allowPathRewrite()
Allow plugins to rewrite the path.
run()
Routes the request, booting core if not yet booted.
Handler for uncaught exceptions.
HTTP response builder interface.
elgg_is_logged_in()
Returns whether or not the user is currently logged in.
Definition: sessions.php:43
const ELGG_HTTP_FORBIDDEN
Definition: constants.php:82
static install()
Renders a web UI for installing Elgg.
A generic parent class for Configuration exceptions.
getStatusCode()
Returns status code.
Exception thrown if an argument is not of the expected type.
isRedirection()
Check if response is redirection.
static projectDir()
Returns a directory that points to the project root, where composer is installed. ...
__construct(InternalContainer $internal_services)
Constructor.
getContent()
Returns response body.
static $_instance
Reference to the loaded Application.
Definition: Application.php:72
static loadCore()
Define all Elgg global functions and constants, wire up boot events, but don&#39;t boot.
$defaults
$request
Definition: livesearch.php:11
Error response builder.
const ELGG_HTTP_PERMANENTLY_REDIRECT
Definition: constants.php:78
static isCoreLoaded()
Are Elgg&#39;s global functions loaded?
elgg_normalize_site_url($unsafe_url)
From untrusted input, get a site URL safe for forwarding.
Definition: output.php:166
c Accompany it with the information you received as to the offer to distribute corresponding source complete source code means all the source code for all modules it plus any associated interface definition plus the scripts used to control compilation and installation of the executable as a special the source code distributed need not include anything that is normally and so on of the operating system on which the executable unless that component itself accompanies the executable If distribution of executable or object code is made by offering access to copy from a designated then offering equivalent access to copy the source code from the same place counts as distribution of the source even though third parties are not compelled to copy the source along with the object code You may not or distribute the Program except as expressly provided under this License Any attempt otherwise to sublicense or distribute the Program is void
Definition: LICENSE.txt:215
static getStdOut()
Load console output interface.
static getStdErr()
Load console error output interface.
$type
Definition: delete.php:21
elgg_echo($message_key, array $args=[], $language="")
Elgg language module Functions to manage language and translations.
Definition: languages.php:18
getDb()
Get a Database wrapper for performing queries without booting Elgg.
$config
Advanced site settings, debugging section.
Definition: debugging.php:6
string project
Definition: conf.py:48
Updates the basic settings for the primary site object.
static start()
Start and boot the core.
if(!$pagination &&$limit!==false &&!empty($items)&&count($items) >=$limit) $base_url
Definition: list.php:113
$path
Definition: details.php:68
trait Loggable
Enables adding a logger.
Definition: Loggable.php:14
Thrown when page is not accessible.
const REFERRER
Definition: constants.php:42
bootCore()
Bootstrap the Elgg engine, loads plugins, and calls initial system events.
static getResponseTransport()
Build a transport for sending responses.
static getMigrationSettings()
Returns configuration array for database migrations.
static upgrade()
Elgg upgrade script.
static isCli()
Is application running in CLI.
global $CONFIG
Thrown when there is a major problem with the installation.
static respond(ResponseBuilder $builder)
Build and send a response.
$installer
static route(HttpRequest $request)
Route a request.
Handle system and PHP errors.
static getRequest()
Build request object.
if(!$entity->delete()) $forward_url
Definition: delete.php:30
$token
elgg_extract($key, $array, $default=null, $strict=true)
Checks for $array[$key] and returns its value if it exists, else returns $default.
Definition: elgglib.php:547
Redirect response builder.
getBootStatus(string $type)
Retrieve the boot status of the application.
static getStdIn()
Load console input interface.
Load, boot, and implement a front controller for an Elgg application.
Definition: Application.php:48
Generic HTTP exception.
$query
$content
Set robots.txt action.
Definition: set_robots.php:6
const ELGG_HTTP_INTERNAL_SERVER_ERROR
Definition: constants.php:106
foreach($plugin_guids as $guid) if(empty($deactivated_plugins)) $url
Definition: deactivate.php:39
static elggDir()
Returns a directory that points to the root of Elgg, but not necessarily the install root...
setException(\Exception $e)
Set an exception for this response.the exception for this responseself
Definition: Response.php:61
setBootStatus(string $type, bool $status)
Sets the boot status.
getHeaders()
Returns additional response headers.
elgg global
Pointer to the global context.
Definition: deprecated.js:9
static setGlobalConfig(Application $application)
Make the global $CONFIG a reference to this application&#39;s config service.
Thrown when one of the gatekeepers prevents access.
Handles application boot sequence.
Definition: BootHandler.php:12
OK response builder.
Definition: OkResponse.php:8
var elgg
Definition: elgglib.js:4
static setInstance(Application $application=null)
Set the global Application instance.
elgg_get_login_url(array $query=[], $fragment= '')
Returns site&#39;s login URL Triggers a &#39;login_url&#39;, &#39;site&#39; plugin hook that can be used by plugins to al...
Definition: users.php:185
static migrate()
Runs database migrations.
getForwardURL()
Returns redirect URL.
getRedirectUrl()
Get preferred redirect URL.
static getInstance()
Get the global Application instance.
Definition: Application.php:91