Elgg  Version 2.2
 All Classes Namespaces Files Functions Variables Pages
Application.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg;
4 
7 
20 class Application {
21 
22  const GET_PATH_KEY = '__elgg_uri';
23  const REWRITE_TEST_TOKEN = '__testing_rewrite';
24  const REWRITE_TEST_OUTPUT = 'success';
25 
29  private $services;
30 
34  private $engine_dir;
35 
44  private static $public_services = [
45  //'config' => true,
46  'menus' => true,
47  ];
48 
56  public static $_instance;
57 
65  public function __construct(ServiceProvider $services) {
66  $this->services = $services;
67 
73  if (!isset($GLOBALS['START_MICROTIME'])) {
74  $GLOBALS['START_MICROTIME'] = microtime(true);
75  }
76 
77  $services->timer->begin([]);
78 
86  if (!isset($GLOBALS['_ELGG'])) {
87  $GLOBALS['_ELGG'] = new \stdClass();
88  }
89 
90  $this->engine_dir = __DIR__ . '/../..';
91  }
92 
101  public function loadSettings() {
102  $this->services->config->loadSettingsFile();
103  }
104 
114  public function loadCore() {
115  if (function_exists('elgg')) {
116  return;
117  }
118 
119  $lib_dir = self::elggDir()->chroot("engine/lib");
120 
121  // load the rest of the library files from engine/lib/
122  // All on separate lines to make diffs easy to read + make it apparent how much
123  // we're actually loading on every page (Hint: it's too much).
124  $lib_files = array(
125  // Needs to be loaded first to correctly bootstrap
126  'autoloader.php',
127  'elgglib.php',
128 
129  // The order of these doesn't matter, so keep them alphabetical
130  'access.php',
131  'actions.php',
132  'admin.php',
133  'annotations.php',
134  'cache.php',
135  'comments.php',
136  'configuration.php',
137  'cron.php',
138  'database.php',
139  'entities.php',
140  'extender.php',
141  'filestore.php',
142  'friends.php',
143  'group.php',
144  'input.php',
145  'languages.php',
146  'mb_wrapper.php',
147  'memcache.php',
148  'metadata.php',
149  'metastrings.php',
150  'navigation.php',
151  'notification.php',
152  'objects.php',
153  'output.php',
154  'pagehandler.php',
155  'pageowner.php',
156  'pam.php',
157  'plugins.php',
158  'private_settings.php',
159  'relationships.php',
160  'river.php',
161  'sessions.php',
162  'sites.php',
163  'statistics.php',
164  'system_log.php',
165  'tags.php',
166  'user_settings.php',
167  'users.php',
168  'upgrade.php',
169  'views.php',
170  'widgets.php',
171 
172  // backward compatibility
173  'deprecated-1.9.php',
174  'deprecated-1.10.php',
175  'deprecated-1.11.php',
176  'deprecated-1.12.php',
177  'deprecated-2.1.php',
178  );
179 
180  // isolate global scope
181  call_user_func(function () use ($lib_dir, $lib_files) {
182 
183  $setups = array();
184 
185  // include library files, capturing setup functions
186  foreach ($lib_files as $file) {
187  $setup = (require_once $lib_dir->getPath($file));
188 
189  if ($setup instanceof \Closure) {
190  $setups[$file] = $setup;
191  }
192  }
193 
194  // store instance to be returned by elgg()
195  self::$_instance = $this;
196 
197  // set up autoloading and DIC
198  _elgg_services($this->services);
199 
200  $events = $this->services->events;
201  $hooks = $this->services->hooks;
202 
203  // run setups
204  foreach ($setups as $func) {
205  $func($events, $hooks);
206  }
207  });
208  }
209 
215  public static function start() {
216  $app = self::create();
217  $app->bootCore();
218  return $app;
219  }
220 
234  public function bootCore() {
235 
236  $config = $this->services->config;
237 
238  if ($config->getVolatile('Elgg\Application_phpunit')) {
239  throw new \RuntimeException('Unit tests should not call ' . __METHOD__);
240  }
241 
242  if ($config->getVolatile('boot_complete')) {
243  return;
244  }
245 
246  $this->loadSettings();
247 
248  $config->set('boot_complete', false);
249 
250  // This will be overridden by the DB value but may be needed before the upgrade script can be run.
251  $config->set('default_limit', 10);
252 
253  // in case not loaded already
254  $this->loadCore();
255 
256  $events = $this->services->events;
257 
258  // Connect to database, load language files, load configuration, init session
259  // Plugins can't use this event because they haven't been loaded yet.
260  $events->trigger('boot', 'system');
261 
262  // Load the plugins that are active
263  $this->services->plugins->load();
264 
265  $root = Directory\Local::root();
266  if ($root->getPath() != self::elggDir()->getPath()) {
267  // Elgg is installed as a composer dep, so try to treat the root directory
268  // as a custom plugin that is always loaded last and can't be disabled...
269  if (!elgg_get_config('system_cache_loaded')) {
270  // configure view locations for the custom plugin (not Elgg core)
271  $viewsFile = $root->getFile('views.php');
272  if ($viewsFile->exists()) {
273  $viewsSpec = $viewsFile->includeFile();
274  if (is_array($viewsSpec)) {
275  _elgg_services()->views->mergeViewsSpec($viewsSpec);
276  }
277  }
278 
279  // find views for the custom plugin (not Elgg core)
280  _elgg_services()->views->registerPluginViews($root->getPath());
281  }
282 
283  if (!elgg_get_config('i18n_loaded_from_cache')) {
284  _elgg_services()->translator->registerPluginTranslations($root->getPath());
285  }
286 
287  // This is root directory start.php, not elgg/engine/start.php
288  $root_start = $root->getPath("start.php");
289  if (is_file($root_start)) {
290  require $root_start;
291  }
292  }
293 
294 
295  // @todo move loading plugins into a single boot function that replaces 'boot', 'system' event
296  // and then move this code in there.
297  // This validates the view type - first opportunity to do it is after plugins load.
298  $viewtype = elgg_get_viewtype();
299  if (!elgg_is_registered_viewtype($viewtype)) {
300  elgg_set_viewtype('default');
301  }
302 
303  $this->allowPathRewrite();
304 
305  // Allows registering handlers strictly before all init, system handlers
306  $events->trigger('plugins_boot', 'system');
307 
308  // Complete the boot process for both engine and plugins
309  $events->trigger('init', 'system');
310 
311  $config->set('boot_complete', true);
312 
313  // System loaded and ready
314  $events->trigger('ready', 'system');
315  }
316 
329  public function getDb() {
330  $this->loadSettings();
331  return $this->services->publicDb;
332  }
333 
341  public function __get($name) {
342  if (isset(self::$public_services[$name])) {
343  return $this->services->{$name};
344  }
345  trigger_error("Undefined property: " . __CLASS__ . ":\${$name}");
346  }
347 
354  private static function create() {
355  if (self::$_instance === null) {
356  // we need to register for shutdown before Symfony registers the
357  // session_write_close() function. https://github.com/Elgg/Elgg/issues/9243
358  register_shutdown_function(function () {
359  // There are cases where we may exit before this function is defined
360  if (function_exists('_elgg_shutdown_hook')) {
362  }
363  });
364 
365  self::$_instance = new self(new Di\ServiceProvider(new Config()));
366  }
367 
368  return self::$_instance;
369  }
370 
376  public static function index() {
377  self::create()->run();
378  }
379 
385  public function run() {
386  $path = $this->setupPath();
387 
388  // allow testing from the upgrade page before the site is upgraded.
389  if (isset($_GET[self::REWRITE_TEST_TOKEN])) {
390  if (false !== strpos($path, self::REWRITE_TEST_TOKEN)) {
391  echo self::REWRITE_TEST_OUTPUT;
392  }
393  return true;
394  }
395 
396  if (php_sapi_name() === 'cli-server') {
397  $www_root = "http://{$_SERVER['SERVER_NAME']}:{$_SERVER['SERVER_PORT']}/";
398  $this->services->config->set('wwwroot', $www_root);
399  }
400 
401  if (0 === strpos($path, '/cache/')) {
402  (new Application\CacheHandler($this, $this->services->config, $_SERVER))->handleRequest($path);
403  return true;
404  }
405 
406  if (0 === strpos($path, '/serve-file/')) {
407  $this->services->serveFileHandler->getResponse($this->services->request)->send();
408  return true;
409  }
410 
411  if ($path === '/rewrite.php') {
412  require Directory\Local::root()->getPath("install.php");
413  return true;
414  }
415 
416  if (php_sapi_name() === 'cli-server') {
417  // The CLI server routes ALL requests here (even existing files), so we have to check for these.
418  if ($path !== '/' && Directory\Local::root()->isFile($path)) {
419  // serve the requested resource as-is.
420  return false;
421  }
422  }
423 
424  $this->bootCore();
425 
426  // TODO use formal Response object instead
427  header("Content-Type: text/html;charset=utf-8");
428 
429  if (!$this->services->router->route($this->services->request)) {
430  forward('', '404');
431  }
432  }
433 
442  public static function getDataPath() {
443  $app = self::create();
444  $app->services->config->loadSettingsFile();
445 
446  if ($GLOBALS['_ELGG']->dataroot_in_settings) {
447  return $app->services->config->getVolatile('dataroot');
448  }
449 
450  $dataroot = $app->services->datalist->get('dataroot');
451  if (!$dataroot) {
452  throw new \InstallationException('The datalists table lacks a value for "dataroot".');
453  }
454  $dataroot = rtrim($dataroot, '/\\') . DIRECTORY_SEPARATOR;
455  $app->services->config->set('dataroot', $dataroot);
456  return $dataroot;
457  }
458 
465  public static function elggDir() /*: Directory*/ {
466  return Directory\Local::fromPath(realpath(__DIR__ . '/../../..'));
467  }
468 
474  public static function install() {
475  ini_set('display_errors', 1);
476  $installer = new \ElggInstaller();
477  $step = get_input('step', 'welcome');
478  $installer->run($step);
479  }
480 
495  public static function upgrade() {
496  // we want to know if an error occurs
497  ini_set('display_errors', 1);
498 
499  define('UPGRADING', 'upgrading');
500 
501  self::start();
502 
503  $site_url = elgg_get_config('url');
504  $site_host = parse_url($site_url, PHP_URL_HOST) . '/';
505 
506  // turn any full in-site URLs into absolute paths
507  $forward_url = get_input('forward', '/admin', false);
508  $forward_url = str_replace(array($site_url, $site_host), '/', $forward_url);
509 
510  if (strpos($forward_url, '/') !== 0) {
511  $forward_url = '/' . $forward_url;
512  }
513 
514  if (get_input('upgrade') == 'upgrade') {
515 
516  $upgrader = _elgg_services()->upgrades;
517  $result = $upgrader->run();
518  if ($result['failure'] == true) {
519  register_error($result['reason']);
521  }
522  } else {
523  $rewriteTester = new \ElggRewriteTester();
524  $url = elgg_get_site_url() . "__testing_rewrite?__testing_rewrite=1";
525  if (!$rewriteTester->runRewriteTest($url)) {
526  // see if there is a problem accessing the site at all
527  // due to ip restrictions for example
528  if (!$rewriteTester->runLocalhostAccessTest()) {
529  // note: translation may not be available until after upgrade
530  $msg = elgg_echo("installation:htaccess:localhost:connectionfailed");
531  if ($msg === "installation:htaccess:localhost:connectionfailed") {
532  $msg = "Elgg cannot connect to itself to test rewrite rules properly. Check "
533  . "that curl is working and there are no IP restrictions preventing "
534  . "localhost connections.";
535  }
536  echo $msg;
537  exit;
538  }
539 
540  // note: translation may not be available until after upgrade
541  $msg = elgg_echo("installation:htaccess:needs_upgrade");
542  if ($msg === "installation:htaccess:needs_upgrade") {
543  $msg = "You must update your .htaccess file so that the path is injected "
544  . "into the GET parameter __elgg_uri (you can use install/config/htaccess.dist as a guide).";
545  }
546  echo $msg;
547  exit;
548  }
549 
550  $vars = array(
551  'forward' => $forward_url
552  );
553 
554  // reset cache to have latest translations available during upgrade
556 
557  echo elgg_view_page(elgg_echo('upgrading'), '', 'upgrade', $vars);
558  exit;
559  }
560 
562  }
563 
569  private function setupPath() {
570  if (!isset($_GET[self::GET_PATH_KEY]) || is_array($_GET[self::GET_PATH_KEY])) {
571  if (php_sapi_name() === 'cli-server') {
572  $_GET[self::GET_PATH_KEY] = (string)parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
573  } else {
574  $_GET[self::GET_PATH_KEY] = '/';
575  }
576  }
577 
578  // normalize
579  $_GET[self::GET_PATH_KEY] = '/' . trim($_GET[self::GET_PATH_KEY], '/');
580 
581  return $_GET[self::GET_PATH_KEY];
582  }
583 
589  private function allowPathRewrite() {
590  $request = $this->services->request;
591  $new = $this->services->router->allowRewrite($request);
592  if ($new === $request) {
593  return;
594  }
595 
596  $this->services->setValue('request', $new);
598  }
599 }
static index()
Elgg's front controller.
A simple directory abstraction.
Definition: Directory.php:13
elgg_get_config($name, $site_guid=0)
Get an Elgg configuration value.
run()
Routes the request, booting core if not yet booted.
elgg_reset_system_cache()
Reset the system cache by deleting the caches.
Definition: cache.php:29
loadSettings()
Load settings.php.
static install()
Renders a web UI for installing Elgg.
if(!$entity->delete()) $forward_url
Definition: delete.php:37
if(!array_key_exists($filename, $text_files)) $file
__get($name)
Get an undefined property.
if($guid==elgg_get_logged_in_user_guid()) $name
Definition: delete.php:21
elgg_echo($message_key, $args=array(), $language="")
Given a message key, returns an appropriately translated full-text string.
Definition: languages.php:21
$path
Definition: details.php:88
elgg_is_registered_viewtype($viewtype)
Checks if $viewtype is registered.
Definition: views.php:152
static getDataPath()
Determine the Elgg data directory with trailing slash, save it to config, and return it...
$url
Definition: exceptions.php:24
$vars['entity']
getDb()
Get a Database wrapper for performing queries without booting Elgg.
_elgg_shutdown_hook()
Emits a shutdown:system event upon PHP shutdown, but before database connections are dropped...
Definition: elgglib.php:1480
register_error($error)
Display an error on next page load.
Definition: elgglib.php:452
elgg_get_viewtype()
Return the current view type.
Definition: views.php:94
static start()
Replacement for loading engine/start.php.
loadCore()
Load all Elgg procedural code and wire up boot events, but don't boot.
bootCore()
Bootstrap the Elgg engine, loads plugins, and calls initial system events.
static static $_instance
Reference to the loaded Application returned by elgg()
Definition: Application.php:44
get_input($variable, $default=null, $filter_result=true)
Get some input from variables passed submitted through GET or POST.
Definition: input.php:27
static upgrade()
Elgg upgrade script.
$site_url
Definition: header_logo.php:8
if(is_array($custom_items)) $new
Definition: save.php:63
__construct(ServiceProvider $services)
Constructor.
Definition: Application.php:65
elgg_set_viewtype($viewtype="")
Manually set the viewtype.
Definition: views.php:73
$installer
$dataroot
elgg_get_site_url($site_guid=0)
Get the URL for the current (or specified) site.
_elgg_services(\Elgg\Di\ServiceProvider $services=null)
Get the global service provider.
Definition: autoloader.php:17
forward($location="", $reason= 'system')
Forward to $location.
Definition: elgglib.php:93
static elggDir()
Returns a directory that points to the root of Elgg, but not necessarily the install root...
exit
Definition: autoloader.php:34
elgg_view_page($title, $body, $page_shell= 'default', $vars=array())
Assembles and outputs a full page.
Definition: views.php:437
_elgg_set_initial_context(\Elgg\Http\Request $request)
Set an initial context if using index.php front controller.
Definition: pageowner.php:273