These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace Elgg; |
||
4 | |||
5 | use Elgg\Database\SiteSecret; |
||
6 | use Elgg\Di\ServiceProvider; |
||
7 | use Elgg\Filesystem\Directory; |
||
8 | use Elgg\Http\Request; |
||
9 | use Elgg\Filesystem\Directory\Local; |
||
10 | use ConfigurationException; |
||
11 | use Elgg\Project\Paths; |
||
12 | |||
13 | /** |
||
14 | * Load, boot, and implement a front controller for an Elgg application |
||
15 | * |
||
16 | * To run as PHP CLI server: |
||
17 | * <code>php -S localhost:8888 /full/path/to/elgg/index.php</code> |
||
18 | * |
||
19 | * The full path is necessary to work around this: https://bugs.php.net/bug.php?id=55726 |
||
20 | * |
||
21 | * @since 2.0.0 |
||
22 | * |
||
23 | * @property-read \Elgg\Menu\Service $menus |
||
24 | * @property-read \Elgg\Views\TableColumn\ColumnFactory $table_columns |
||
25 | */ |
||
26 | class Application { |
||
27 | |||
28 | const DEFAULT_LANG = 'en'; |
||
29 | const DEFAULT_LIMIT = 10; |
||
30 | |||
31 | /** |
||
32 | * @var ServiceProvider |
||
33 | */ |
||
34 | private $services; |
||
35 | |||
36 | /** |
||
37 | * @var bool |
||
38 | */ |
||
39 | private static $core_loaded = false; |
||
40 | |||
41 | /** |
||
42 | * @var bool |
||
43 | */ |
||
44 | private static $testing_app; |
||
45 | |||
46 | /** |
||
47 | * Property names of the service provider to be exposed via __get() |
||
48 | * |
||
49 | * E.g. the presence of `'foo' => true` in the list would allow _elgg_services()->foo to |
||
50 | * be accessed via elgg()->foo. |
||
51 | * |
||
52 | * @var string[] |
||
53 | */ |
||
54 | private static $public_services = [ |
||
55 | //'config' => true, |
||
56 | 'menus' => true, |
||
57 | 'table_columns' => true, |
||
58 | ]; |
||
59 | |||
60 | /** |
||
61 | * Reference to the loaded Application returned by elgg() |
||
62 | * |
||
63 | * @internal Do not use this. use elgg() to access the application |
||
64 | * @access private |
||
65 | * @var Application |
||
66 | */ |
||
67 | public static $_instance; |
||
68 | |||
69 | /** |
||
70 | * Constructor |
||
71 | 198 | * |
|
72 | 198 | * Upon construction, no actions are taken to load or boot Elgg. |
|
73 | * |
||
74 | * @param ServiceProvider $services Elgg services provider |
||
75 | * @throws ConfigurationException |
||
76 | */ |
||
77 | public function __construct(ServiceProvider $services) { |
||
78 | $this->services = $services; |
||
79 | 198 | $services->setValue('app', $this); |
|
80 | 1 | ||
81 | $this->initConfig(); |
||
82 | } |
||
83 | 198 | ||
84 | /** |
||
85 | * Validate, normalize, fill in missing values, and lock some |
||
86 | * |
||
87 | * @return void |
||
88 | * @throws ConfigurationException |
||
89 | */ |
||
90 | private function initConfig() { |
||
91 | $config = $this->services->config; |
||
92 | 198 | ||
93 | if ($config->elgg_config_locks === null) { |
||
94 | $config->elgg_config_locks = true; |
||
95 | } |
||
96 | 198 | ||
97 | 198 | if ($config->elgg_config_locks) { |
|
98 | $lock = function ($name) use ($config) { |
||
99 | $config->lock($name); |
||
100 | }; |
||
101 | } else { |
||
102 | // the installer needs to build an application with defaults then update |
||
103 | // them after they're validated, so we don't want to lock them. |
||
104 | $lock = function () { |
||
105 | }; |
||
106 | } |
||
107 | |||
108 | $this->services->timer->begin([]); |
||
109 | |||
110 | // Until DB loads, let's log problems |
||
111 | if ($config->debug === null) { |
||
112 | $config->debug = 'NOTICE'; |
||
113 | } |
||
114 | |||
115 | if ($config->dataroot) { |
||
116 | $config->dataroot = rtrim($config->dataroot, '\\/') . DIRECTORY_SEPARATOR; |
||
117 | } else { |
||
118 | if (!$config->installer_running) { |
||
119 | throw new ConfigurationException('Config value "dataroot" is required.'); |
||
120 | 196 | } |
|
121 | 196 | } |
|
122 | 196 | $lock('dataroot'); |
|
123 | |||
124 | if ($config->cacheroot) { |
||
125 | $config->cacheroot = rtrim($config->cacheroot, '\\/') . DIRECTORY_SEPARATOR; |
||
126 | } else { |
||
127 | $config->cacheroot = $config->dataroot; |
||
128 | } |
||
129 | $lock('cacheroot'); |
||
130 | |||
131 | if ($config->wwwroot) { |
||
132 | $config->wwwroot = rtrim($config->wwwroot, '/') . '/'; |
||
133 | } else { |
||
134 | $config->wwwroot = $this->services->request->sniffElggUrl(); |
||
135 | } |
||
136 | $lock('wwwroot'); |
||
137 | |||
138 | if (!$config->language) { |
||
139 | $config->language = self::DEFAULT_LANG; |
||
140 | } |
||
141 | |||
142 | if ($config->default_limit) { |
||
143 | $lock('default_limit'); |
||
144 | } else { |
||
145 | $config->default_limit = self::DEFAULT_LIMIT; |
||
146 | } |
||
147 | |||
148 | $locked_props = [ |
||
149 | 'site_guid' => 1, |
||
150 | 'path' => Paths::project(), |
||
151 | 'plugins_path' => Paths::project() . "mod/", |
||
152 | 'pluginspath' => Paths::project() . "mod/", |
||
153 | 'url' => $config->wwwroot, |
||
154 | ]; |
||
155 | foreach ($locked_props as $name => $value) { |
||
156 | $config->$name = $value; |
||
157 | $lock($name); |
||
158 | } |
||
159 | |||
160 | // move sensitive credentials into isolated services |
||
161 | $this->services->dbConfig; |
||
162 | |||
163 | // If the site secret is in the settings file, let's move it to that component |
||
164 | // right away to keep this value out of config. |
||
165 | $secret = SiteSecret::fromConfig($config); |
||
166 | if ($secret) { |
||
167 | $this->services->setValue('siteSecret', $secret); |
||
168 | $config->elgg_config_set_secret = true; |
||
169 | } |
||
170 | |||
171 | $config->boot_complete = false; |
||
172 | } |
||
173 | |||
174 | /** |
||
175 | * Get the DB credentials. |
||
176 | * |
||
177 | * We no longer leave DB credentials in the config in case it gets accidentally dumped. |
||
178 | * |
||
179 | * @return \Elgg\Database\DbConfig |
||
180 | */ |
||
181 | public function getDbConfig() { |
||
182 | return $this->services->dbConfig; |
||
183 | } |
||
184 | |||
185 | /** |
||
186 | * Define all Elgg global functions and constants, wire up boot events, but don't boot |
||
187 | * |
||
188 | * This includes all the .php files in engine/lib (not upgrades). If a script returns a function, |
||
189 | * it is queued and executed at the end. |
||
190 | * |
||
191 | * @return void |
||
192 | * @access private |
||
193 | * @internal |
||
194 | * @throws \InstallationException |
||
195 | */ |
||
196 | public function loadCore() { |
||
197 | if (self::$core_loaded) { |
||
198 | return; |
||
199 | } |
||
200 | |||
201 | $setups = []; |
||
202 | $path = Paths::elgg() . 'engine/lib'; |
||
203 | |||
204 | // include library files, capturing setup functions |
||
205 | foreach (self::getEngineLibs() as $file) { |
||
206 | $return = Includer::includeFile("$path/$file"); |
||
207 | if (!$return) { |
||
208 | throw new \InstallationException("Elgg lib file failed include: engine/lib/$file"); |
||
209 | } |
||
210 | if ($return instanceof \Closure) { |
||
211 | $setups[$file] = $return; |
||
212 | } |
||
213 | } |
||
214 | |||
215 | // store instance to be returned by elgg() |
||
216 | self::$_instance = $this; |
||
217 | |||
218 | // allow global services access. :( |
||
219 | _elgg_services($this->services); |
||
220 | |||
221 | // setup logger and inject into config |
||
222 | //$this->services->config->setLogger($this->services->logger); |
||
223 | |||
224 | $hooks = $this->services->hooks; |
||
225 | $events = $hooks->getEvents(); |
||
226 | |||
227 | // run setups |
||
228 | foreach ($setups as $func) { |
||
229 | $func($events, $hooks); |
||
230 | } |
||
231 | |||
232 | self::$core_loaded = true; |
||
233 | } |
||
234 | |||
235 | /** |
||
236 | * Start and boot the core |
||
237 | * |
||
238 | * @return self |
||
239 | */ |
||
240 | public static function start() { |
||
241 | $app = self::factory(); |
||
242 | $app->bootCore(); |
||
243 | return $app; |
||
244 | } |
||
245 | |||
246 | /** |
||
247 | * Bootstrap the Elgg engine, loads plugins, and calls initial system events |
||
248 | * |
||
249 | * This method loads the full Elgg engine, checks the installation |
||
250 | * state, and triggers a series of events to finish booting Elgg: |
||
251 | * - {@elgg_event boot system} |
||
252 | * - {@elgg_event init system} |
||
253 | * - {@elgg_event ready system} |
||
254 | * |
||
255 | * If Elgg is not fully installed, the browser will be redirected to an installation page. |
||
256 | * |
||
257 | * @return void |
||
258 | */ |
||
259 | public function bootCore() { |
||
260 | $config = $this->services->config; |
||
261 | |||
262 | if ($this->isTestingApplication()) { |
||
263 | throw new \RuntimeException('Unit tests should not call ' . __METHOD__); |
||
264 | } |
||
265 | |||
266 | if ($config->boot_complete) { |
||
267 | return; |
||
268 | } |
||
269 | |||
270 | // in case not loaded already |
||
271 | $this->loadCore(); |
||
272 | |||
273 | if (!$this->services->db) { |
||
274 | // no database boot! |
||
275 | elgg_views_boot(); |
||
276 | $this->services->session->start(); |
||
277 | $this->services->translator->loadTranslations(); |
||
278 | |||
279 | actions_init(); |
||
280 | _elgg_init(); |
||
281 | _elgg_input_init(); |
||
282 | _elgg_nav_init(); |
||
283 | |||
284 | $config->boot_complete = true; |
||
285 | $config->lock('boot_complete'); |
||
286 | return; |
||
287 | } |
||
288 | |||
289 | // Connect to database, load language files, load configuration, init session |
||
290 | $this->services->boot->boot($this->services); |
||
291 | |||
292 | elgg_views_boot(); |
||
293 | |||
294 | // Load the plugins that are active |
||
295 | $this->services->plugins->load(); |
||
296 | |||
297 | if (Paths::project() != Paths::elgg()) { |
||
298 | // Elgg is installed as a composer dep, so try to treat the root directory |
||
299 | // as a custom plugin that is always loaded last and can't be disabled... |
||
300 | View Code Duplication | if (!$config->system_cache_loaded) { |
|
1 ignored issue
–
show
|
|||
301 | // configure view locations for the custom plugin (not Elgg core) |
||
302 | $viewsFile = Paths::project() . 'views.php'; |
||
303 | if (is_file($viewsFile)) { |
||
304 | $viewsSpec = Includer::includeFile($viewsFile); |
||
305 | if (is_array($viewsSpec)) { |
||
306 | $this->services->views->mergeViewsSpec($viewsSpec); |
||
307 | } |
||
308 | } |
||
309 | |||
310 | // find views for the custom plugin (not Elgg core) |
||
311 | $this->services->views->registerPluginViews(Paths::project()); |
||
312 | } |
||
313 | |||
314 | if (!$config->i18n_loaded_from_cache) { |
||
315 | $this->services->translator->registerPluginTranslations(Paths::project()); |
||
316 | } |
||
317 | |||
318 | // This is root directory start.php |
||
319 | $root_start = Paths::project() . "start.php"; |
||
320 | if (is_file($root_start)) { |
||
321 | require $root_start; |
||
322 | } |
||
323 | } |
||
324 | |||
325 | // after plugins are started we know which viewtypes are populated |
||
326 | $this->services->views->clampViewtypeToPopulatedViews(); |
||
327 | |||
328 | $this->allowPathRewrite(); |
||
329 | |||
330 | $events = $this->services->hooks->getEvents(); |
||
331 | |||
332 | // Allows registering handlers strictly before all init, system handlers |
||
333 | $events->trigger('plugins_boot', 'system'); |
||
334 | |||
335 | // Complete the boot process for both engine and plugins |
||
336 | $events->trigger('init', 'system'); |
||
337 | |||
338 | $config->boot_complete = true; |
||
339 | $config->lock('boot_complete'); |
||
340 | |||
341 | // System loaded and ready |
||
342 | $events->trigger('ready', 'system'); |
||
343 | } |
||
344 | |||
345 | /** |
||
346 | * Get a Database wrapper for performing queries without booting Elgg |
||
347 | * |
||
348 | * If settings has not been loaded, it will be loaded to configure the DB connection. |
||
349 | * |
||
350 | * @note Before boot, the Database instance will not yet be bound to a Logger. |
||
351 | * |
||
352 | * @return \Elgg\Application\Database |
||
353 | */ |
||
354 | public function getDb() { |
||
355 | return $this->services->publicDb; |
||
356 | } |
||
357 | |||
358 | /** |
||
359 | * Get an undefined property |
||
360 | * |
||
361 | * @param string $name The property name accessed |
||
362 | * |
||
363 | * @return mixed |
||
364 | */ |
||
365 | public function __get($name) { |
||
366 | if (isset(self::$public_services[$name])) { |
||
367 | return $this->services->{$name}; |
||
368 | } |
||
369 | trigger_error("Undefined property: " . __CLASS__ . ":\${$name}"); |
||
370 | } |
||
371 | |||
372 | /** |
||
373 | * Creates a new, trivial instance of Elgg\Application and set it as the singleton instance. |
||
374 | * If the singleton is already set, it's returned. |
||
375 | * |
||
376 | * @param array $spec Specification for initial call. |
||
377 | * @return self |
||
378 | * @throws ConfigurationException |
||
379 | */ |
||
380 | public static function factory(array $spec = []) { |
||
0 ignored issues
–
show
factory uses the super-global variable $GLOBALS which is generally not recommended.
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: // Bad
class Router
{
public function generate($path)
{
return $_SERVER['HOST'].$path;
}
}
// Better
class Router
{
private $host;
public function __construct($host)
{
$this->host = $host;
}
public function generate($path)
{
return $this->host.$path;
}
}
class Controller
{
public function myAction(Request $request)
{
// Instead of
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
// Better (assuming you use the Symfony2 request)
$page = $request->query->get('page', 1);
}
}
Loading history...
|
|||
381 | if (self::$_instance !== null) { |
||
382 | return self::$_instance; |
||
383 | } |
||
384 | |||
385 | $defaults = [ |
||
386 | 'service_provider' => null, |
||
387 | 'config' => null, |
||
388 | 'settings_path' => null, |
||
389 | 'handle_exceptions' => true, |
||
390 | 'handle_shutdown' => true, |
||
391 | 'overwrite_global_config' => true, |
||
392 | 'set_start_time' => true, |
||
393 | 'request' => null, |
||
394 | ]; |
||
395 | $spec = array_merge($defaults, $spec); |
||
396 | |||
397 | if ($spec['set_start_time']) { |
||
398 | /** |
||
399 | * The time with microseconds when the Elgg engine was started. |
||
400 | * |
||
401 | * @global float |
||
402 | */ |
||
403 | if (!isset($GLOBALS['START_MICROTIME'])) { |
||
404 | $GLOBALS['START_MICROTIME'] = microtime(true); |
||
405 | } |
||
406 | } |
||
407 | |||
408 | if (!$spec['service_provider']) { |
||
409 | if (!$spec['config']) { |
||
410 | $spec['config'] = Config::factory($spec['settings_path']); |
||
411 | } |
||
412 | $spec['service_provider'] = new ServiceProvider($spec['config']); |
||
413 | } |
||
414 | |||
415 | if ($spec['request']) { |
||
416 | if ($spec['request'] instanceof Request) { |
||
417 | $spec['service_provider']->setValue('request', $spec['request']); |
||
418 | } else { |
||
419 | throw new \InvalidArgumentException("Given request is not a " . Request::class); |
||
420 | } |
||
421 | } |
||
422 | |||
423 | self::$_instance = new self($spec['service_provider']); |
||
424 | |||
425 | if ($spec['handle_exceptions']) { |
||
426 | set_error_handler([self::$_instance, 'handleErrors']); |
||
427 | set_exception_handler([self::$_instance, 'handleExceptions']); |
||
428 | } |
||
429 | |||
430 | if ($spec['handle_shutdown']) { |
||
431 | // we need to register for shutdown before Symfony registers the |
||
432 | // session_write_close() function. https://github.com/Elgg/Elgg/issues/9243 |
||
433 | register_shutdown_function(function () { |
||
434 | // There are cases where we may exit before this function is defined |
||
435 | if (function_exists('_elgg_shutdown_hook')) { |
||
436 | _elgg_shutdown_hook(); |
||
437 | } |
||
438 | }); |
||
439 | } |
||
440 | |||
441 | if ($spec['overwrite_global_config']) { |
||
442 | global $CONFIG; |
||
443 | |||
444 | // this will be buggy be at least PHP will log failures |
||
445 | $CONFIG = $spec['service_provider']->config; |
||
446 | } |
||
447 | |||
448 | return self::$_instance; |
||
449 | } |
||
450 | |||
451 | /** |
||
452 | * Elgg's front controller. Handles basically all incoming URL requests. |
||
453 | * |
||
454 | * @return bool True if Elgg will handle the request, false if the server should (PHP-CLI server) |
||
455 | */ |
||
456 | public static function index() { |
||
457 | $req = Request::createFromGlobals(); |
||
458 | /** @var Request $req */ |
||
459 | |||
460 | if ($req->isRewriteCheck()) { |
||
461 | echo Request::REWRITE_TEST_OUTPUT; |
||
462 | return true; |
||
463 | } |
||
464 | |||
465 | return self::factory(['request' => $req])->run(); |
||
466 | } |
||
467 | |||
468 | /** |
||
469 | * Routes the request, booting core if not yet booted |
||
470 | * |
||
471 | * @return bool False if Elgg wants the PHP CLI server to handle the request |
||
472 | */ |
||
473 | public function run() { |
||
474 | $config = $this->services->config; |
||
475 | $request = $this->services->request; |
||
476 | |||
477 | if ($request->isCliServer()) { |
||
478 | if ($request->isCliServable(Paths::project())) { |
||
479 | return false; |
||
480 | } |
||
481 | |||
482 | // overwrite value from settings |
||
483 | $www_root = rtrim($request->getSchemeAndHttpHost() . $request->getBaseUrl(), '/') . '/'; |
||
484 | $config->wwwroot = $www_root; |
||
485 | $config->wwwroot_cli_server = $www_root; |
||
1 ignored issue
–
show
The property
wwwroot_cli_server does not seem to exist. Did you mean wwwroot ?
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name. If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.
Loading history...
|
|||
486 | } |
||
487 | |||
488 | if (0 === strpos($request->getElggPath(), '/cache/')) { |
||
489 | $this->services->cacheHandler->handleRequest($request)->prepare($request)->send(); |
||
490 | return true; |
||
491 | } |
||
492 | |||
493 | if (0 === strpos($request->getElggPath(), '/serve-file/')) { |
||
494 | $this->services->serveFileHandler->getResponse($request)->send(); |
||
495 | return true; |
||
496 | } |
||
497 | |||
498 | $this->bootCore(); |
||
499 | |||
500 | // TODO use formal Response object instead |
||
501 | // This is to set the charset to UTF-8. |
||
502 | header("Content-Type: text/html;charset=utf-8", true); |
||
503 | |||
504 | // re-fetch new request from services in case it was replaced by route:rewrite |
||
505 | $request = $this->services->request; |
||
506 | |||
507 | if (!$this->services->router->route($request)) { |
||
508 | forward('', '404'); |
||
509 | } |
||
510 | } |
||
511 | |||
512 | /** |
||
513 | * Returns a directory that points to the root of Elgg, but not necessarily |
||
514 | * the install root. See `self::root()` for that. |
||
515 | * |
||
516 | * @return Directory |
||
517 | */ |
||
518 | public static function elggDir() { |
||
519 | return Local::elggRoot(); |
||
520 | } |
||
521 | |||
522 | /** |
||
523 | * Returns a directory that points to the project root, where composer is installed. |
||
524 | * |
||
525 | * @return Directory |
||
526 | */ |
||
527 | public static function projectDir() { |
||
528 | return Local::projectRoot(); |
||
529 | } |
||
530 | |||
531 | /** |
||
532 | * Renders a web UI for installing Elgg. |
||
533 | * |
||
534 | * @return void |
||
535 | */ |
||
536 | public static function install() { |
||
537 | ini_set('display_errors', 1); |
||
538 | $installer = new \ElggInstaller(); |
||
539 | $installer->run(); |
||
540 | } |
||
541 | |||
542 | /** |
||
543 | * Elgg upgrade script. |
||
544 | * |
||
545 | * This script triggers any necessary upgrades. If the site has been upgraded |
||
546 | * to the most recent version of the code, no upgrades are run but the caches |
||
547 | * are flushed. |
||
548 | * |
||
549 | * Upgrades use a table {db_prefix}upgrade_lock as a mutex to prevent concurrent upgrades. |
||
550 | * |
||
551 | * The URL to forward to after upgrades are complete can be specified by setting $_GET['forward'] |
||
552 | * to a relative URL. |
||
553 | * |
||
554 | * @return void |
||
555 | */ |
||
556 | public static function upgrade() { |
||
557 | // we want to know if an error occurs |
||
558 | ini_set('display_errors', 1); |
||
559 | $is_cli = (php_sapi_name() === 'cli'); |
||
560 | |||
561 | $forward = function ($url) use ($is_cli) { |
||
562 | if ($is_cli) { |
||
563 | echo "Open $url in your browser to continue."; |
||
564 | exit; |
||
565 | } |
||
566 | |||
567 | forward($url); |
||
568 | }; |
||
569 | |||
570 | define('UPGRADING', 'upgrading'); |
||
571 | |||
572 | self::start(); |
||
573 | |||
574 | // check security settings |
||
575 | if (!$is_cli && _elgg_config()->security_protect_upgrade && !elgg_is_admin_logged_in()) { |
||
576 | // only admin's or users with a valid token can run upgrade.php |
||
577 | elgg_signed_request_gatekeeper(); |
||
578 | } |
||
579 | |||
580 | $site_url = _elgg_config()->url; |
||
581 | $site_host = parse_url($site_url, PHP_URL_HOST) . '/'; |
||
582 | |||
583 | // turn any full in-site URLs into absolute paths |
||
584 | $forward_url = get_input('forward', '/admin', false); |
||
585 | $forward_url = str_replace([$site_url, $site_host], '/', $forward_url); |
||
586 | |||
587 | if (strpos($forward_url, '/') !== 0) { |
||
588 | $forward_url = '/' . $forward_url; |
||
589 | } |
||
590 | |||
591 | if ($is_cli || (get_input('upgrade') == 'upgrade')) { |
||
592 | $upgrader = _elgg_services()->upgrades; |
||
593 | $result = $upgrader->run(); |
||
594 | |||
595 | if ($result['failure'] == true) { |
||
596 | register_error($result['reason']); |
||
597 | $forward($forward_url); |
||
598 | } |
||
599 | |||
600 | // Find unprocessed batch upgrade classes and save them as ElggUpgrade objects |
||
601 | $core_upgrades = (require self::elggDir()->getPath('engine/lib/upgrades/async-upgrades.php')); |
||
602 | 196 | $has_pending_upgrades = _elgg_services()->upgradeLocator->run($core_upgrades); |
|
603 | 196 | ||
604 | 196 | if ($has_pending_upgrades) { |
|
605 | // Forward to the list of pending upgrades |
||
606 | $forward_url = '/admin/upgrades'; |
||
607 | } |
||
608 | } else { |
||
609 | $rewriteTester = new \ElggRewriteTester(); |
||
610 | 1 | $url = elgg_get_site_url() . "__testing_rewrite?__testing_rewrite=1"; |
|
611 | 1 | if (!$rewriteTester->runRewriteTest($url)) { |
|
612 | // see if there is a problem accessing the site at all |
||
613 | // due to ip restrictions for example |
||
614 | if (!$rewriteTester->runLocalhostAccessTest()) { |
||
615 | // note: translation may not be available until after upgrade |
||
616 | $msg = elgg_echo("installation:htaccess:localhost:connectionfailed"); |
||
617 | if ($msg === "installation:htaccess:localhost:connectionfailed") { |
||
618 | $msg = "Elgg cannot connect to itself to test rewrite rules properly. Check " |
||
619 | . "that curl is working and there are no IP restrictions preventing " |
||
620 | . "localhost connections."; |
||
621 | } |
||
622 | echo $msg; |
||
623 | exit; |
||
624 | } |
||
625 | |||
626 | // note: translation may not be available until after upgrade |
||
627 | $msg = elgg_echo("installation:htaccess:needs_upgrade"); |
||
628 | if ($msg === "installation:htaccess:needs_upgrade") { |
||
629 | $msg = "You must update your .htaccess file (use install/config/htaccess.dist as a guide)."; |
||
630 | } |
||
631 | echo $msg; |
||
632 | exit; |
||
633 | } |
||
634 | |||
635 | $vars = [ |
||
636 | 'forward' => $forward_url |
||
637 | ]; |
||
638 | |||
639 | // reset cache to have latest translations available during upgrade |
||
640 | elgg_reset_system_cache(); |
||
641 | |||
642 | echo elgg_view_page(elgg_echo('upgrading'), '', 'upgrade', $vars); |
||
643 | exit; |
||
644 | } |
||
645 | |||
646 | $forward($forward_url); |
||
647 | } |
||
648 | |||
649 | /** |
||
650 | * Allow plugins to rewrite the path. |
||
651 | * |
||
652 | * @return void |
||
653 | */ |
||
654 | private function allowPathRewrite() { |
||
655 | $request = $this->services->request; |
||
656 | $new = $this->services->router->allowRewrite($request); |
||
657 | if ($new === $request) { |
||
658 | return; |
||
659 | } |
||
660 | |||
661 | $this->services->setValue('request', $new); |
||
662 | $this->services->context->initialize($new); |
||
663 | } |
||
664 | |||
665 | /** |
||
666 | * Flag this application as running for testing (PHPUnit) |
||
667 | * |
||
668 | * @param bool $testing Is testing application |
||
669 | * @return void |
||
670 | */ |
||
671 | public static function setTestingApplication($testing = true) { |
||
672 | self::$testing_app = $testing; |
||
673 | } |
||
674 | |||
675 | /** |
||
676 | * Checks if the application is running in PHPUnit |
||
677 | * @return bool |
||
678 | */ |
||
679 | public static function isTestingApplication() { |
||
680 | return (bool) self::$testing_app; |
||
681 | } |
||
682 | |||
683 | /** |
||
684 | * Intercepts, logs, and displays uncaught exceptions. |
||
685 | * |
||
686 | * To use a viewtype other than failsafe, create the views: |
||
687 | * <viewtype>/messages/exceptions/admin_exception |
||
688 | * <viewtype>/messages/exceptions/exception |
||
689 | * See the json viewtype for an example. |
||
690 | * |
||
691 | * @warning This function should never be called directly. |
||
692 | * |
||
693 | * @see http://www.php.net/set-exception-handler |
||
694 | * |
||
695 | * @param \Exception|\Error $exception The exception/error being handled |
||
696 | * |
||
697 | * @return void |
||
698 | * @access private |
||
699 | */ |
||
700 | public function handleExceptions($exception) { |
||
701 | $timestamp = time(); |
||
702 | error_log("Exception at time $timestamp: $exception"); |
||
703 | |||
704 | // Wipe any existing output buffer |
||
705 | ob_end_clean(); |
||
706 | |||
707 | // make sure the error isn't cached |
||
708 | header("Cache-Control: no-cache, must-revalidate", true); |
||
709 | header('Expires: Fri, 05 Feb 1982 00:00:00 -0500', true); |
||
710 | |||
711 | if ($exception instanceof \InstallationException) { |
||
712 | forward('/install.php'); |
||
713 | } |
||
714 | |||
715 | if (!self::$core_loaded) { |
||
716 | http_response_code(500); |
||
717 | echo "Exception loading Elgg core. Check log at time $timestamp"; |
||
718 | return; |
||
719 | } |
||
720 | |||
721 | try { |
||
722 | // allow custom scripts to trigger on exception |
||
723 | // value in settings.php should be a system path to a file to include |
||
724 | $exception_include = $this->services->config->exception_include; |
||
725 | |||
726 | if ($exception_include && is_file($exception_include)) { |
||
727 | ob_start(); |
||
728 | |||
729 | // don't isolate, these scripts may use the local $exception var. |
||
730 | include $exception_include; |
||
731 | |||
732 | $exception_output = ob_get_clean(); |
||
733 | |||
734 | // if content is returned from the custom handler we will output |
||
735 | // that instead of our default failsafe view |
||
736 | if (!empty($exception_output)) { |
||
737 | echo $exception_output; |
||
738 | exit; |
||
739 | } |
||
740 | } |
||
741 | |||
742 | if (elgg_is_xhr()) { |
||
743 | elgg_set_viewtype('json'); |
||
744 | $response = new \Symfony\Component\HttpFoundation\JsonResponse(null, 500); |
||
745 | } else { |
||
746 | elgg_set_viewtype('failsafe'); |
||
747 | $response = new \Symfony\Component\HttpFoundation\Response('', 500); |
||
748 | } |
||
749 | |||
750 | if (elgg_is_admin_logged_in()) { |
||
751 | $body = elgg_view("messages/exceptions/admin_exception", [ |
||
752 | 'object' => $exception, |
||
753 | 'ts' => $timestamp |
||
754 | ]); |
||
755 | } else { |
||
756 | $body = elgg_view("messages/exceptions/exception", [ |
||
757 | 'object' => $exception, |
||
758 | 'ts' => $timestamp |
||
759 | ]); |
||
760 | } |
||
761 | |||
762 | $response->setContent(elgg_view_page(elgg_echo('exception:title'), $body)); |
||
763 | $response->send(); |
||
764 | } catch (\Exception $e) { |
||
765 | $timestamp = time(); |
||
766 | $message = $e->getMessage(); |
||
767 | http_response_code(500); |
||
768 | echo "Fatal error in exception handler. Check log for Exception at time $timestamp"; |
||
769 | error_log("Exception at time $timestamp : fatal error in exception handler : $message"); |
||
770 | } |
||
771 | } |
||
772 | |||
773 | /** |
||
774 | * Intercepts catchable PHP errors. |
||
775 | * |
||
776 | * @warning This function should never be called directly. |
||
777 | * |
||
778 | * @internal |
||
779 | * For catchable fatal errors, throws an Exception with the error. |
||
780 | * |
||
781 | * For non-fatal errors, depending upon the debug settings, either |
||
782 | * log the error or ignore it. |
||
783 | * |
||
784 | * @see http://www.php.net/set-error-handler |
||
785 | * |
||
786 | * @param int $errno The level of the error raised |
||
787 | * @param string $errmsg The error message |
||
788 | * @param string $filename The filename the error was raised in |
||
789 | * @param int $linenum The line number the error was raised at |
||
790 | * @param array $vars An array that points to the active symbol table where error occurred |
||
791 | * |
||
792 | * @return true |
||
793 | * @throws \Exception |
||
794 | * @access private |
||
795 | */ |
||
796 | public function handleErrors($errno, $errmsg, $filename, $linenum, $vars) { |
||
0 ignored issues
–
show
handleErrors uses the super-global variable $GLOBALS which is generally not recommended.
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: // Bad
class Router
{
public function generate($path)
{
return $_SERVER['HOST'].$path;
}
}
// Better
class Router
{
private $host;
public function __construct($host)
{
$this->host = $host;
}
public function generate($path)
{
return $this->host.$path;
}
}
class Controller
{
public function myAction(Request $request)
{
// Instead of
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
// Better (assuming you use the Symfony2 request)
$page = $request->query->get('page', 1);
}
}
Loading history...
|
|||
797 | $error = date("Y-m-d H:i:s (T)") . ": \"$errmsg\" in file $filename (line $linenum)"; |
||
798 | |||
799 | $log = function ($message, $level) { |
||
800 | if (self::$core_loaded) { |
||
801 | return elgg_log($message, $level); |
||
802 | } |
||
803 | |||
804 | return false; |
||
805 | }; |
||
806 | |||
807 | switch ($errno) { |
||
808 | case E_USER_ERROR: |
||
809 | if (!$log("PHP: $error", 'ERROR')) { |
||
810 | error_log("PHP ERROR: $error"); |
||
811 | } |
||
812 | if (self::$core_loaded) { |
||
813 | register_error("ERROR: $error"); |
||
814 | } |
||
815 | |||
816 | // Since this is a fatal error, we want to stop any further execution but do so gracefully. |
||
817 | throw new \Exception($error); |
||
818 | break; |
||
819 | |||
820 | case E_WARNING : |
||
821 | case E_USER_WARNING : |
||
822 | case E_RECOVERABLE_ERROR: // (e.g. type hint violation) |
||
823 | |||
824 | // check if the error wasn't suppressed by the error control operator (@) |
||
825 | if (error_reporting() && !$log("PHP: $error", 'WARNING')) { |
||
826 | error_log("PHP WARNING: $error"); |
||
827 | } |
||
828 | break; |
||
829 | |||
830 | default: |
||
831 | if (function_exists('_elgg_config')) { |
||
832 | $debug = _elgg_config()->debug; |
||
833 | } else { |
||
834 | $debug = isset($GLOBALS['CONFIG']->debug) ? $GLOBALS['CONFIG']->debug : null; |
||
835 | } |
||
836 | if ($debug !== 'NOTICE') { |
||
837 | return true; |
||
838 | } |
||
839 | |||
840 | if (!$log("PHP (errno $errno): $error", 'NOTICE')) { |
||
841 | error_log("PHP NOTICE: $error"); |
||
842 | } |
||
843 | } |
||
844 | |||
845 | return true; |
||
846 | } |
||
847 | |||
848 | /** |
||
849 | * Does nothing. |
||
850 | * |
||
851 | * @return void |
||
852 | * @deprecated |
||
853 | */ |
||
854 | public function loadSettings() { |
||
855 | trigger_error(__METHOD__ . ' is no longer needed and will be removed.'); |
||
856 | } |
||
857 | |||
858 | /** |
||
859 | * Get all engine/lib library filenames |
||
860 | * |
||
861 | * @note We can't just pull in all directory files because some users leave old files in place. |
||
862 | * |
||
863 | * @return string[] |
||
864 | */ |
||
865 | private static function getEngineLibs() { |
||
866 | return [ |
||
867 | 'access.php', |
||
868 | 'actions.php', |
||
869 | 'admin.php', |
||
870 | 'annotations.php', |
||
871 | 'autoloader.php', |
||
872 | 'cache.php', |
||
873 | 'comments.php', |
||
874 | 'configuration.php', |
||
875 | 'constants.php', |
||
876 | 'cron.php', |
||
877 | 'database.php', |
||
878 | 'deprecated-3.0.php', |
||
879 | 'elgglib.php', |
||
880 | 'entities.php', |
||
881 | 'filestore.php', |
||
882 | 'group.php', |
||
883 | 'input.php', |
||
884 | 'languages.php', |
||
885 | 'mb_wrapper.php', |
||
886 | 'memcache.php', |
||
887 | 'metadata.php', |
||
888 | 'metastrings.php', |
||
889 | 'navigation.php', |
||
890 | 'notification.php', |
||
891 | 'output.php', |
||
892 | 'pagehandler.php', |
||
893 | 'pageowner.php', |
||
894 | 'pam.php', |
||
895 | 'plugins.php', |
||
896 | 'private_settings.php', |
||
897 | 'relationships.php', |
||
898 | 'river.php', |
||
899 | 'sessions.php', |
||
900 | 'statistics.php', |
||
901 | 'system_log.php', |
||
902 | 'tags.php', |
||
903 | 'upgrade.php', |
||
904 | 'user_settings.php', |
||
905 | 'users.php', |
||
906 | 'views.php', |
||
907 | 'widgets.php', |
||
908 | ]; |
||
909 | } |
||
910 | } |
||
911 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.