Elgg  Version 4.x
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 
52  const DEFAULT_LANG = 'en';
53  const DEFAULT_LIMIT = 10;
54 
61 
68 
75  public static $_instance;
76 
82  public static function getInstance() {
83  if (self::$_instance === null) {
84  self::$_instance = self::factory();
85  self::setGlobalConfig(self::$_instance);
86  }
87 
88  return self::$_instance;
89  }
90 
98  public static function setInstance(Application $application = null) {
99  self::$_instance = $application;
100  }
101 
109  public function __construct(InternalContainer $internal_services) {
110  $this->internal_services = $internal_services;
111  $this->public_services = PublicContainer::factory();
112  }
113 
125  public static function loadCore() {
126  $path = Paths::elgg() . 'engine/lib';
127 
128  // include library files, capturing setup functions
129  foreach (self::getEngineLibs() as $file) {
130  try {
131  Includer::requireFileOnce("$path/$file");
132  } catch (\Error $e) {
133  throw new InstallationException("Elgg lib file failed include: $path/$file");
134  }
135  }
136  }
137 
143  public static function start() {
144  $app = self::getInstance();
145  $app->bootCore();
146 
147  return $app;
148  }
149 
155  public static function isCoreLoaded() {
156  return function_exists('elgg');
157  }
158 
178  public function bootCore() {
179  $boot = new BootHandler($this);
180  $boot();
181  }
182 
192  public function getDb() {
193  return $this->internal_services->publicDb;
194  }
195 
203  public static function setGlobalConfig(Application $application) {
204  global $CONFIG;
205  $CONFIG = $application->internal_services->config;
206  }
207 
221  public static function factory(array $spec = []) {
222 
223  $defaults = [
224  'config' => null,
225  'handle_exceptions' => true,
226  'handle_shutdown' => true,
227  'request' => null,
228  'internal_services' => null,
229  'set_start_time' => true,
230  'settings_path' => null,
231  ];
232  $spec = array_merge($defaults, $spec);
233 
234  if ($spec['set_start_time']) {
240  if (!isset($GLOBALS['START_MICROTIME'])) {
241  $GLOBALS['START_MICROTIME'] = microtime(true);
242  }
243  }
244 
245  if ($spec['handle_exceptions']) {
246  set_error_handler(new ErrorHandler());
247  set_exception_handler(new ExceptionHandler());
248  }
249 
250  self::loadCore();
251 
252  if (!$spec['internal_services']) {
253  if (!$spec['config']) {
254  $spec['config'] = Config::factory($spec['settings_path']);
255  }
256  $spec['internal_services'] = InternalContainer::factory(['config' => $spec['config']]);
257  }
258 
259  if ($spec['request']) {
260  if ($spec['request'] instanceof HttpRequest) {
261  $spec['request']->initializeTrustedProxyConfiguration($spec['internal_services']->config);
262  $spec['internal_services']->set('request', $spec['request']);
263  } else {
264  throw new InvalidArgumentException("Given request is not a " . HttpRequest::class);
265  }
266  }
267 
268  $app = new self($spec['internal_services']);
269 
270  if ($spec['handle_shutdown']) {
271  register_shutdown_function(new ShutdownHandler($app));
272  }
273 
274  return $app;
275  }
276 
284  public static function route(HttpRequest $request) {
285  self::loadCore();
286 
287  if ($request->isRewriteCheck()) {
288  $response = new OkResponse(HttpRequest::REWRITE_TEST_OUTPUT);
289  return self::respond($response);
290  }
291 
292  if (self::$_instance) {
293  $app = self::$_instance;
294  $app->internal_services->set('request', $request);
295  } else {
296  try {
297  $app = self::factory([
298  'request' => $request,
299  ]);
300 
301  self::setGlobalConfig($app);
302  self::setInstance($app);
303  } catch (ConfigurationException $ex) {
304  return self::install();
305  }
306  }
307 
308  return $app->run();
309  }
310 
321  public static function respond(ResponseBuilder $builder) {
322  if (self::$_instance) {
323  self::$_instance->internal_services->responseFactory->respond($builder);
324 
325  return self::$_instance->internal_services->responseFactory->getSentResponse();
326  }
327 
328  try {
329  $content = $builder->getContent();
330  $status = $builder->getStatusCode();
331  $headers = $builder->getHeaders();
332 
333  if ($builder->isRedirection()) {
334  $forward_url = $builder->getForwardURL();
335  $response = new SymfonyRedirect($forward_url, $status, $headers);
336  } else {
337  $response = new Response($content, $status, $headers);
338  }
339  } catch (\Exception $ex) {
340  $response = new Response($ex->getMessage(), 500);
341  }
342 
343  $response->headers->set('Pragma', 'public');
344  $response->headers->set('Cache-Control', 'no-store, must-revalidate');
345  $response->headers->set('Expires', 'Fri, 05 Feb 1982 00:00:00 -0500');
346 
347  self::getResponseTransport()->send($response);
348 
349  return $response;
350  }
351 
357  public static function index() {
358  return self::route(self::getRequest());
359  }
360 
367  public function run() {
368  $config = $this->internal_services->config;
369  $request = $this->internal_services->request;
370 
371  try {
372  if ($request->isCliServer()) {
373  if ($request->isCliServable(Paths::project())) {
374  return false;
375  }
376 
377  // overwrite value from settings
378  $www_root = rtrim($request->getSchemeAndHttpHost() . $request->getBaseUrl(), '/') . '/';
379  $config->wwwroot = $www_root;
380  $config->wwwroot_cli_server = $www_root;
381  }
382 
383  if (0 === strpos($request->getElggPath(), '/cache/')) {
384  $config->_disable_session_save = true;
385  $response = $this->internal_services->cacheHandler->handleRequest($request, $this)->prepare($request);
386  self::getResponseTransport()->send($response);
387 
388  return $response;
389  }
390 
391  if ($request->getElggPath() === '/refresh_token') {
392  $config->_disable_session_save = true;
393  $token = new \Elgg\Controllers\RefreshCsrfToken();
394  $response = $token($request);
395  self::getResponseTransport()->send($response);
396 
397  return $response;
398  }
399 
400  if (0 === strpos($request->getElggPath(), '/serve-file/')) {
401  $config->_disable_session_save = true;
402  $response = $this->internal_services->serveFileHandler->getResponse($request);
403  self::getResponseTransport()->send($response);
404 
405  return $response;
406  }
407 
408  if ($this->isCli()) {
409  $config->_disable_session_save = true;
410  }
411 
412  $this->bootCore();
413 
414  // re-fetch new request from services in case it was replaced by route:rewrite
415  $request = $this->internal_services->request;
416 
417  if (!$this->internal_services->router->route($request)) {
418  throw new PageNotFoundException();
419  }
420  } catch (HttpException $ex) {
421  $forward_url = $ex->getRedirectUrl();
422  if (!$forward_url) {
423  if ($ex instanceof GatekeeperException) {
425  } else if ($request->getFirstUrlSegment() == 'action') {
427  }
428  }
429 
430  $hook_params = [
431  'exception' => $ex,
432  ];
433 
434  $forward_url = $this->internal_services->hooks->trigger('forward', $ex->getCode(), $hook_params, $forward_url);
435 
436  if ($forward_url && !$request->isXmlHttpRequest()) {
437  if ($ex->getMessage()) {
438  $this->internal_services->system_messages->addErrorMessage($ex->getMessage());
439  }
440  $response = new RedirectResponse($forward_url);
441  } else {
442  $response = new ErrorResponse($ex->getMessage(), $ex->getCode(), $forward_url);
443  }
444 
445  $response->setException($ex);
446 
447  self::respond($response);
448  }
449 
450  return $this->internal_services->responseFactory->getSentResponse();
451  }
452 
459  public static function elggDir() {
460  return Local::elggRoot();
461  }
462 
468  public static function projectDir() {
469  return Local::projectRoot();
470  }
471 
477  public static function install() {
478  ini_set('display_errors', 1);
479 
480  try {
481  $installer = new \ElggInstaller();
482  $response = $installer->run();
483  } catch (\Exception $ex) {
484  $response = new ErrorResponse($ex->getMessage(), 500);
485  }
486 
487  return self::respond($response);
488  }
489 
504  public static function upgrade() {
505 
506  try {
507  self::migrate();
508  self::start();
509 
510  $request = self::$_instance->internal_services->request;
511  $signer = self::$_instance->internal_services->urlSigner;
512 
513  $url = $request->getCurrentURL();
514  $query = $request->getParams();
515 
516  // We need to resign the URL because the path is different
517  $mac = elgg_extract(UrlSigner::KEY_MAC, $query);
518  if (isset($mac) && !$signer->isValid($url)) {
519  throw new HttpException(elgg_echo('invalid_request_signature'), ELGG_HTTP_FORBIDDEN);
520  }
521 
522  unset($query[UrlSigner::KEY_MAC]);
523 
524  $base_url = elgg_normalize_site_url('upgrade/init');
526 
527  if (isset($mac)) {
528  $url = self::$_instance->internal_services->urlSigner->sign($url);
529  }
530 
532  } catch (\Exception $ex) {
533  $response = new ErrorResponse($ex->getMessage(), $ex->getCode() ? : ELGG_HTTP_INTERNAL_SERVER_ERROR);
534  }
535 
536  return self::respond($response);
537  }
538 
545  public static function migrate() {
546 
547  $constants = self::elggDir()->getPath('engine/lib/constants.php');
548  Includer::requireFileOnce($constants);
549 
550  $conf = self::elggDir()->getPath('engine/conf/migrations.php');
551  if (!$conf) {
552  throw new InstallationException('Settings file is required to run database migrations.');
553  }
554 
555  // setting timeout because some database migrations can take a long time
556  set_time_limit(0);
557 
558  $app = new \Phinx\Console\PhinxApplication();
559  $wrapper = new \Phinx\Wrapper\TextWrapper($app, [
560  'configuration' => $conf,
561  ]);
562  $log = $wrapper->getMigrate();
563 
564  if (!empty($_SERVER['argv']) && in_array('--verbose', $_SERVER['argv'])) {
565  error_log($log);
566  }
567 
568  return true;
569  }
570 
575  public static function getMigrationSettings() {
576 
577  $config = Config::factory();
578  $db_config = DbConfig::fromElggConfig($config);
579 
580  if ($db_config->isDatabaseSplit()) {
581  $conn = $db_config->getConnectionConfig(DbConfig::WRITE);
582  } else {
583  $conn = $db_config->getConnectionConfig();
584  }
585 
586  return [
587  "paths" => [
588  "migrations" => Paths::elgg() . 'engine/schema/migrations/',
589  ],
590  "environments" => [
591  "default_migration_table" => "{$conn['prefix']}migrations",
592  "default_database" => "prod",
593  "prod" => [
594  "adapter" => "mysql",
595  "host" => $conn['host'],
596  "port" => $conn['port'],
597  "name" => $conn['database'],
598  "user" => $conn['user'],
599  "pass" => $conn['password'],
600  "charset" => $conn['encoding'],
601  "table_prefix" => $conn['prefix'],
602  ],
603  ],
604  ];
605  }
606 
614  public function allowPathRewrite() {
615  $request = $this->internal_services->request;
616  $new = $this->internal_services->router->allowRewrite($request);
617  if ($new === $request) {
618  return;
619  }
620 
621  $this->internal_services->set('request', $new);
622  }
623 
628  public static function isCli() {
629  switch (php_sapi_name()) {
630  case 'cli' :
631  case 'phpdbg' :
632  return true;
633 
634  default:
635  return false;
636  }
637  }
638 
643  public static function getRequest() {
644  if (self::$_instance) {
645  return self::$_instance->internal_services->request;
646  }
647 
648  return HttpRequest::createFromGlobals();
649  }
650 
655  public static function getStdIn() {
656  if (self::isCli()) {
657  $request = self::getRequest();
658  $argv = $request->server->get('argv') ? : [];
659  return new ArgvInput($argv);
660  }
661 
662  return new ArrayInput([]);
663  }
664 
669  public static function getStdOut() {
670  if (self::isCli()) {
671  return new ConsoleOutput();
672  } else {
673  return new NullOutput();
674  }
675  }
676 
681  public static function getStdErr() {
682  $std_out = self::getStdOut();
683  if (is_callable([$std_out, 'getErrorOutput'])) {
684  return $std_out->getErrorOutput();
685  }
686 
687  return $std_out;
688  }
689 
694  public static function getResponseTransport() {
695  if (self::isCli()) {
696  return new \Elgg\Http\OutputBufferTransport();
697  }
698 
699  return new \Elgg\Http\HttpProtocolTransport();
700  }
701 
709  private static function getEngineLibs() {
710  return [
711  'elgglib.php',
712  'access.php',
713  'actions.php',
714  'admin.php',
715  'annotations.php',
716  'breadcrumbs.php',
717  'cache.php',
718  'configuration.php',
719  'constants.php',
720  'context.php',
721  'deprecated-4.0.php',
722  'deprecated-4.1.php',
723  'deprecated-4.2.php',
724  'deprecated-4.3.php',
725  'deprecation.php',
726  'entities.php',
727  'external_files.php',
728  'filestore.php',
729  'gatekeepers.php',
730  'input.php',
731  'languages.php',
732  'mb_wrapper.php',
733  'metadata.php',
734  'navigation.php',
735  'notification.php',
736  'output.php',
737  'pagehandler.php',
738  'pageowner.php',
739  'pam.php',
740  'plugins.php',
741  'relationships.php',
742  'river.php',
743  'sessions.php',
744  'users.php',
745  'views.php',
746  'widgets.php',
747  ];
748  }
749 }
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:515
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:45
const ELGG_HTTP_FORBIDDEN
Definition: constants.php:82
static install()
Renders a web UI for installing Elgg.
if(!$entity->delete()) $forward_url
Definition: delete.php:30
A generic parent class for Configuration exceptions.
getStatusCode()
Returns status code.
Base exception of invalid argument exceptions in the Elgg system.
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:75
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:208
static getStdOut()
Load console output interface.
static getStdErr()
Load console error output interface.
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.
$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:686
Redirect response builder.
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
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.
Definition: Application.php:98
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:223
static migrate()
Runs database migrations.
getForwardURL()
Returns redirect URL.
getRedirectUrl()
Get preferred redirect URL.
static getInstance()
Get the global Application instance.
Definition: Application.php:82