Removed obsolete trailing slashes from void elements
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / system / WCF.class.php
CommitLineData
d335fa16
AE
1<?php
2namespace wcf\system;
3use wcf\data\application\Application;
4use wcf\data\option\OptionEditor;
5use wcf\data\package\Package;
6use wcf\data\package\PackageCache;
7use wcf\data\package\PackageEditor;
12a2ff01
AE
8use wcf\data\page\Page;
9use wcf\data\page\PageCache;
5346b183 10use wcf\page\CmsPage;
d335fa16 11use wcf\system\application\ApplicationHandler;
f341086b 12use wcf\system\application\IApplication;
55b402a0 13use wcf\system\box\BoxHandler;
d335fa16
AE
14use wcf\system\cache\builder\CoreObjectCacheBuilder;
15use wcf\system\cache\builder\PackageUpdateCacheBuilder;
16use wcf\system\cronjob\CronjobScheduler;
157054c9 17use wcf\system\database\MySQLDatabase;
d335fa16
AE
18use wcf\system\event\EventHandler;
19use wcf\system\exception\AJAXException;
b25ad8f3 20use wcf\system\exception\ErrorException;
d335fa16
AE
21use wcf\system\exception\IPrintableException;
22use wcf\system\exception\NamedUserException;
7b9ff46b 23use wcf\system\exception\ParentClassException;
d335fa16
AE
24use wcf\system\exception\PermissionDeniedException;
25use wcf\system\exception\SystemException;
26use wcf\system\language\LanguageFactory;
27use wcf\system\package\PackageInstallationDispatcher;
12a2ff01 28use wcf\system\request\Request;
a80873d5 29use wcf\system\request\RequestHandler;
d335fa16
AE
30use wcf\system\request\RouteHandler;
31use wcf\system\session\SessionFactory;
32use wcf\system\session\SessionHandler;
33use wcf\system\style\StyleHandler;
15621401 34use wcf\system\template\EmailTemplateEngine;
d335fa16
AE
35use wcf\system\template\TemplateEngine;
36use wcf\system\user\storage\UserStorageHandler;
d335fa16
AE
37use wcf\util\FileUtil;
38use wcf\util\StringUtil;
dc0015ab 39use wcf\util\UserUtil;
d335fa16
AE
40
41// try to set a time-limit to infinite
42@set_time_limit(0);
43
44// fix timezone warning issue
45if (!@ini_get('date.timezone')) {
46 @date_default_timezone_set('Europe/London');
47}
48
49// define current wcf version
f558acf9 50define('WCF_VERSION', '2.2.0 Alpha 1 (Vortex)');
d335fa16
AE
51
52// define current unix timestamp
53define('TIME_NOW', time());
54
55// wcf imports
56if (!defined('NO_IMPORTS')) {
57 require_once(WCF_DIR.'lib/core.functions.php');
d11a8c9e 58 require_once(WCF_DIR.'lib/system/api/autoload.php');
d335fa16
AE
59}
60
61/**
62 * WCF is the central class for the community framework.
63 * It holds the database connection, access to template and language engine.
64 *
65 * @author Marcel Werk
a80873d5 66 * @copyright 2001-2016 WoltLab GmbH
d335fa16
AE
67 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
68 * @package com.woltlab.wcf
69 * @subpackage system
70 * @category Community Framework
71 */
72class WCF {
73 /**
74 * list of currently loaded applications
893aace3 75 * @var Application[]
d335fa16 76 */
058cbd6a 77 protected static $applications = [];
d335fa16
AE
78
79 /**
80 * list of currently loaded application objects
7a23a706 81 * @var IApplication[]
d335fa16 82 */
058cbd6a 83 protected static $applicationObjects = [];
d335fa16
AE
84
85 /**
86 * list of autoload directories
87 * @var array
88 */
058cbd6a 89 protected static $autoloadDirectories = [];
d335fa16
AE
90
91 /**
92 * list of unique instances of each core object
7a23a706 93 * @var SingletonFactory[]
d335fa16 94 */
058cbd6a 95 protected static $coreObject = [];
d335fa16
AE
96
97 /**
98 * list of cached core objects
7a23a706 99 * @var string[]
d335fa16 100 */
058cbd6a 101 protected static $coreObjectCache = [];
d335fa16
AE
102
103 /**
104 * database object
105 * @var \wcf\system\database\Database
106 */
107 protected static $dbObj = null;
108
109 /**
110 * language object
a59497a6 111 * @var \wcf\data\language\Language
d335fa16
AE
112 */
113 protected static $languageObj = null;
114
115 /**
116 * overrides disabled debug mode
117 * @var boolean
118 */
119 protected static $overrideDebugMode = false;
120
121 /**
122 * session object
123 * @var \wcf\system\session\SessionHandler
124 */
125 protected static $sessionObj = null;
126
127 /**
128 * template object
129 * @var \wcf\system\template\TemplateEngine
130 */
131 protected static $tplObj = null;
132
133 /**
134 * true if Zend Opcache is loaded and enabled
135 * @var boolean
136 */
137 protected static $zendOpcacheEnabled = null;
138
139 /**
140 * Calls all init functions of the WCF class.
141 */
142 public function __construct() {
143 // add autoload directory
144 self::$autoloadDirectories['wcf'] = WCF_DIR . 'lib/';
145
146 // define tmp directory
147 if (!defined('TMP_DIR')) define('TMP_DIR', FileUtil::getTempFolder());
148
149 // start initialization
d335fa16
AE
150 $this->initDB();
151 $this->loadOptions();
152 $this->initSession();
153 $this->initLanguage();
154 $this->initTPL();
155 $this->initCronjobs();
156 $this->initCoreObjects();
157 $this->initApplications();
158 $this->initBlacklist();
159
160 EventHandler::getInstance()->fireAction($this, 'initialized');
161 }
162
163 /**
3ba197fa
TD
164 * Flushes the output, closes the session, performs background tasks and more.
165 *
166 * You *must* not create output in here under normal circumstances, as it might get eaten
167 * when gzip is enabled.
d335fa16
AE
168 */
169 public static function destruct() {
170 try {
171 // database has to be initialized
172 if (!is_object(self::$dbObj)) return;
173
3ba197fa
TD
174 $debug = self::debugModeIsEnabled(true);
175 if (!$debug) {
176 // flush output
177 if (ob_get_level()) ob_end_flush();
d335fa16 178 flush();
3ba197fa
TD
179
180 // close connection if using FPM
181 if (function_exists('fastcgi_finish_request')) fastcgi_finish_request();
d335fa16
AE
182 }
183
184 // update session
185 if (is_object(self::getSession())) {
186 self::getSession()->update();
187 }
188
189 // execute shutdown actions of user storage handler
190 UserStorageHandler::getInstance()->shutdown();
191 }
192 catch (\Exception $exception) {
193 die("<pre>WCF::destruct() Unhandled exception: ".$exception->getMessage()."\n\n".$exception->getTraceAsString());
194 }
195 }
196
d335fa16
AE
197 /**
198 * Returns the database object.
199 *
200 * @return \wcf\system\database\Database
201 */
202 public static final function getDB() {
203 return self::$dbObj;
204 }
205
206 /**
207 * Returns the session object.
208 *
209 * @return \wcf\system\session\SessionHandler
210 */
211 public static final function getSession() {
212 return self::$sessionObj;
213 }
214
215 /**
216 * Returns the user object.
217 *
218 * @return \wcf\data\user\User
219 */
220 public static final function getUser() {
221 return self::getSession()->getUser();
222 }
223
224 /**
225 * Returns the language object.
226 *
227 * @return \wcf\data\language\Language
228 */
229 public static final function getLanguage() {
230 return self::$languageObj;
231 }
232
233 /**
234 * Returns the template object.
235 *
236 * @return \wcf\system\template\TemplateEngine
237 */
238 public static final function getTPL() {
239 return self::$tplObj;
240 }
241
242 /**
243 * Calls the show method on the given exception.
244 *
245 * @param \Exception $e
246 */
e09c7044 247 public static final function handleException($e) {
f8434d25
AE
248 if (ob_get_level()) {
249 // discard any output generated before the exception occured, prevents exception
250 // being hidden inside HTML elements and therefore not visible in browser output
251 ob_clean();
252 }
253
5bc5a7e6
TD
254 // backwards compatibility
255 if ($e instanceof IPrintableException) {
256 $e->show();
257 exit;
258 }
259
260 @header('HTTP/1.1 503 Service Unavailable');
d335fa16 261 try {
5bc5a7e6 262 \wcf\functions\exception\printThrowable($e);
d335fa16 263 }
5bc5a7e6
TD
264 catch (\Throwable $e2) {
265 echo "<pre>An Exception was thrown while handling an Exception:\n\n";
266 echo $e2;
267 echo "\n\nwas thrown while:\n\n";
268 echo $e;
269 echo "\n\nwas handled.</pre>";
270 exit;
e09c7044 271 }
5bc5a7e6
TD
272 catch (\Exception $e2) {
273 echo "<pre>An Exception was thrown while handling an Exception:\n\n";
274 echo $e2;
275 echo "\n\nwas thrown while:\n\n";
276 echo $e;
277 echo "\n\nwas handled.</pre>";
278 exit;
d335fa16
AE
279 }
280 }
281
282 /**
b25ad8f3 283 * Turns PHP errors into an ErrorException.
d335fa16 284 *
ac52543a 285 * @param integer $severity
d335fa16 286 * @param string $message
ac52543a
MS
287 * @param string $file
288 * @param integer $line
2b770bdd 289 * @throws ErrorException
d335fa16 290 */
b25ad8f3
TD
291 public static final function handleError($severity, $message, $file, $line) {
292 // this is neccessary for the shut-up operator
293 if (error_reporting() == 0) return;
294
295 throw new ErrorException($message, 0, $severity, $file, $line);
d335fa16
AE
296 }
297
298 /**
299 * Loads the database configuration and creates a new connection to the database.
300 */
301 protected function initDB() {
302 // get configuration
303 $dbHost = $dbUser = $dbPassword = $dbName = '';
304 $dbPort = 0;
d335fa16
AE
305 require(WCF_DIR.'config.inc.php');
306
307 // create database connection
a2d822ad 308 self::$dbObj = new MySQLDatabase($dbHost, $dbUser, $dbPassword, $dbName, $dbPort);
d335fa16
AE
309 }
310
311 /**
312 * Loads the options file, automatically created if not exists.
313 */
314 protected function loadOptions() {
315 $filename = WCF_DIR.'options.inc.php';
316
317 // create options file if doesn't exist
318 if (!file_exists($filename) || filemtime($filename) <= 1) {
319 OptionEditor::rebuild();
320 }
321 require_once($filename);
b842faa1
AE
322
323 // check if option file is complete and writable
324 if (PACKAGE_ID) {
325 if (!is_writable($filename)) {
326 FileUtil::makeWritable($filename);
327
328 if (!is_writable($filename)) {
329 throw new SystemException("The option file '" . $filename . "' is not writable.");
330 }
331 }
332
333 // check if a previous write operation was incomplete and force rebuilding
334 if (!defined('WCF_OPTION_INC_PHP_SUCCESS')) {
335 OptionEditor::rebuild();
336
337 require_once($filename);
338 }
339 }
d335fa16
AE
340 }
341
342 /**
343 * Starts the session system.
344 */
345 protected function initSession() {
f341086b 346 $factory = new SessionFactory();
d335fa16
AE
347 $factory->load();
348
f341086b 349 self::$sessionObj = SessionHandler::getInstance();
91082aee 350 self::$sessionObj->setHasValidCookie($factory->hasValidCookie());
d335fa16
AE
351 }
352
353 /**
354 * Initialises the language engine.
355 */
356 protected function initLanguage() {
357 if (isset($_GET['l']) && !self::getUser()->userID) {
358 self::getSession()->setLanguageID(intval($_GET['l']));
359 }
360
361 // set mb settings
362 mb_internal_encoding('UTF-8');
363 if (function_exists('mb_regex_encoding')) mb_regex_encoding('UTF-8');
364 mb_language('uni');
365
366 // get language
f341086b 367 self::$languageObj = LanguageFactory::getInstance()->getUserLanguage(self::getSession()->getLanguageID());
d335fa16
AE
368 }
369
370 /**
371 * Initialises the template engine.
372 */
373 protected function initTPL() {
f341086b 374 self::$tplObj = TemplateEngine::getInstance();
d335fa16
AE
375 self::getTPL()->setLanguageID(self::getLanguage()->languageID);
376 $this->assignDefaultTemplateVariables();
377
378 $this->initStyle();
379 }
380
381 /**
382 * Initializes the user's style.
383 */
384 protected function initStyle() {
385 if (isset($_REQUEST['styleID'])) {
386 self::getSession()->setStyleID(intval($_REQUEST['styleID']));
387 }
388
f341086b 389 $styleHandler = StyleHandler::getInstance();
d11a8c9e 390 $styleHandler->changeStyle(self::getSession()->getStyleID());
d335fa16
AE
391 }
392
393 /**
394 * Executes the blacklist.
395 */
396 protected function initBlacklist() {
fa75ccfb
AE
397 $isAjax = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest');
398
d335fa16 399 if (defined('BLACKLIST_IP_ADDRESSES') && BLACKLIST_IP_ADDRESSES != '') {
dc0015ab 400 if (!StringUtil::executeWordFilter(UserUtil::convertIPv6To4(self::getSession()->ipAddress), BLACKLIST_IP_ADDRESSES)) {
fa75ccfb 401 if ($isAjax) {
4a1aeea1 402 throw new AJAXException(self::getLanguage()->get('wcf.ajax.error.permissionDenied'), AJAXException::INSUFFICIENT_PERMISSIONS);
fa75ccfb
AE
403 }
404 else {
405 throw new PermissionDeniedException();
406 }
dc0015ab
AE
407 }
408 else if (!StringUtil::executeWordFilter(self::getSession()->ipAddress, BLACKLIST_IP_ADDRESSES)) {
fa75ccfb 409 if ($isAjax) {
4a1aeea1 410 throw new AJAXException(self::getLanguage()->get('wcf.ajax.error.permissionDenied'), AJAXException::INSUFFICIENT_PERMISSIONS);
fa75ccfb
AE
411 }
412 else {
413 throw new PermissionDeniedException();
414 }
d335fa16
AE
415 }
416 }
417 if (defined('BLACKLIST_USER_AGENTS') && BLACKLIST_USER_AGENTS != '') {
418 if (!StringUtil::executeWordFilter(self::getSession()->userAgent, BLACKLIST_USER_AGENTS)) {
fa75ccfb 419 if ($isAjax) {
4a1aeea1 420 throw new AJAXException(self::getLanguage()->get('wcf.ajax.error.permissionDenied'), AJAXException::INSUFFICIENT_PERMISSIONS);
fa75ccfb
AE
421 }
422 else {
423 throw new PermissionDeniedException();
424 }
d335fa16
AE
425 }
426 }
427 if (defined('BLACKLIST_HOSTNAMES') && BLACKLIST_HOSTNAMES != '') {
428 if (!StringUtil::executeWordFilter(@gethostbyaddr(self::getSession()->ipAddress), BLACKLIST_HOSTNAMES)) {
fa75ccfb 429 if ($isAjax) {
4a1aeea1 430 throw new AJAXException(self::getLanguage()->get('wcf.ajax.error.permissionDenied'), AJAXException::INSUFFICIENT_PERMISSIONS);
fa75ccfb
AE
431 }
432 else {
433 throw new PermissionDeniedException();
434 }
d335fa16
AE
435 }
436 }
437
438 // handle banned users
439 if (self::getUser()->userID && self::getUser()->banned) {
fa75ccfb 440 if ($isAjax) {
d335fa16
AE
441 throw new AJAXException(self::getLanguage()->getDynamicVariable('wcf.user.error.isBanned'), AJAXException::INSUFFICIENT_PERMISSIONS);
442 }
443 else {
444 throw new NamedUserException(self::getLanguage()->getDynamicVariable('wcf.user.error.isBanned'));
445 }
446 }
447 }
448
449 /**
450 * Initializes applications.
451 */
452 protected function initApplications() {
453 // step 1) load all applications
058cbd6a 454 $loadedApplications = [];
d335fa16
AE
455
456 // register WCF as application
39abe192 457 self::$applications['wcf'] = ApplicationHandler::getInstance()->getApplicationByID(1);
d335fa16 458
39abe192
AE
459 // TODO: what exactly should the base href represent and how should it be calculated, also because
460 // defining it here eventually breaks the ACP due to tpl initialization occurs first
461 if (!class_exists(WCFACP::class, false)) {
462 $this->getTPL()->assign('baseHref', self::$applications['wcf']->getPageURL());
463 }
464
465 // TODO: this is required for the uninstallation of applications, find a different solution!
d335fa16 466 if (PACKAGE_ID == 1) {
39abe192 467 //return;
d335fa16
AE
468 }
469
470 // start main application
471 $application = ApplicationHandler::getInstance()->getActiveApplication();
39abe192
AE
472 if ($application->packageID != 1) {
473 $loadedApplications[] = $this->loadApplication($application);
474
475 // register primary application
476 $abbreviation = ApplicationHandler::getInstance()->getAbbreviation($application->packageID);
477 self::$applications[$abbreviation] = $application;
478 }
d335fa16
AE
479
480 // start dependent applications
481 $applications = ApplicationHandler::getInstance()->getDependentApplications();
482 foreach ($applications as $application) {
39abe192
AE
483 if ($application->packageID == 1) {
484 // ignore WCF
485 continue;
486 }
487
d335fa16
AE
488 $loadedApplications[] = $this->loadApplication($application, true);
489 }
490
491 // step 2) run each application
492 if (!class_exists('wcf\system\WCFACP', false)) {
f341086b 493 /** @var IApplication $application */
d335fa16
AE
494 foreach ($loadedApplications as $application) {
495 $application->__run();
496 }
497
498 // refresh the session 1 minute before it expires
499 self::getTPL()->assign('__sessionKeepAlive', (SESSION_TIMEOUT - 60));
500 }
501 }
502
503 /**
504 * Loads an application.
505 *
2b770bdd
MS
506 * @param Application $application
507 * @param boolean $isDependentApplication
508 * @return IApplication
509 * @throws SystemException
d335fa16
AE
510 */
511 protected function loadApplication(Application $application, $isDependentApplication = false) {
512 $applicationObject = null;
513 $package = PackageCache::getInstance()->getPackage($application->packageID);
514 // package cache might be outdated
515 if ($package === null) {
516 $package = new Package($application->packageID);
517
518 // package cache is outdated, discard cache
519 if ($package->packageID) {
520 PackageEditor::resetCache();
521 }
522 else {
523 // package id is invalid
524 throw new SystemException("application identified by package id '".$application->packageID."' is unknown");
525 }
526 }
527
528 $abbreviation = ApplicationHandler::getInstance()->getAbbreviation($application->packageID);
529 $packageDir = FileUtil::getRealPath(WCF_DIR.$package->packageDir);
530 self::$autoloadDirectories[$abbreviation] = $packageDir . 'lib/';
531
532 $className = $abbreviation.'\system\\'.strtoupper($abbreviation).'Core';
157054c9 533 if (class_exists($className) && is_subclass_of($className, IApplication::class)) {
d335fa16
AE
534 // include config file
535 $configPath = $packageDir . PackageInstallationDispatcher::CONFIG_FILE;
b842faa1
AE
536
537 // TODO: this should be done during update instead, remove this before any public release
538 if (!file_exists($configPath)) {
539 Package::writeConfigFile($package->packageID);
540 }
541
d335fa16
AE
542 if (file_exists($configPath)) {
543 require_once($configPath);
544 }
545 else {
546 throw new SystemException('Unable to load configuration for '.$package->package);
547 }
548
549 // register template path if not within ACP
550 if (!class_exists('wcf\system\WCFACP', false)) {
551 // add template path and abbreviation
552 $this->getTPL()->addApplication($abbreviation, $packageDir . 'templates/');
553 }
15621401 554 EmailTemplateEngine::getInstance()->addApplication($abbreviation, $packageDir . 'templates/');
d335fa16
AE
555
556 // init application and assign it as template variable
058cbd6a 557 self::$applicationObjects[$application->packageID] = call_user_func([$className, 'getInstance']);
d335fa16 558 $this->getTPL()->assign('__'.$abbreviation, self::$applicationObjects[$application->packageID]);
15621401 559 EmailTemplateEngine::getInstance()->assign('__'.$abbreviation, self::$applicationObjects[$application->packageID]);
d335fa16
AE
560 }
561 else {
562 unset(self::$autoloadDirectories[$abbreviation]);
157054c9 563 throw new SystemException("Unable to run '".$package->package."', '".$className."' is missing or does not implement '".IApplication::class."'.");
d335fa16
AE
564 }
565
566 // register template path in ACP
567 if (class_exists('wcf\system\WCFACP', false)) {
568 $this->getTPL()->addApplication($abbreviation, $packageDir . 'acp/templates/');
569 }
570 else if (!$isDependentApplication) {
571 // assign base tag
572 $this->getTPL()->assign('baseHref', $application->getPageURL());
573 }
574
575 // register application
576 self::$applications[$abbreviation] = $application;
577
578 return self::$applicationObjects[$application->packageID];
579 }
580
581 /**
582 * Returns the corresponding application object. Does not support the 'wcf' pseudo application.
583 *
a59497a6 584 * @param \wcf\data\application\Application $application
d335fa16
AE
585 * @return \wcf\system\application\IApplication
586 */
587 public static function getApplicationObject(Application $application) {
588 if (isset(self::$applicationObjects[$application->packageID])) {
589 return self::$applicationObjects[$application->packageID];
590 }
591
592 return null;
593 }
594
595 /**
596 * Loads an application on runtime, do not use this outside the package installation.
597 *
598 * @param integer $packageID
599 */
600 public static function loadRuntimeApplication($packageID) {
601 $package = new Package($packageID);
602 $application = new Application($packageID);
603
604 $abbreviation = Package::getAbbreviation($package->package);
605 $packageDir = FileUtil::getRealPath(WCF_DIR.$package->packageDir);
606 self::$autoloadDirectories[$abbreviation] = $packageDir . 'lib/';
607 self::$applications[$abbreviation] = $application;
608 self::getTPL()->addApplication($abbreviation, $packageDir . 'acp/templates/');
609 }
610
611 /**
612 * Initializes core object cache.
613 */
614 protected function initCoreObjects() {
615 // ignore core objects if installing WCF
616 if (PACKAGE_ID == 0) {
617 return;
618 }
619
620 self::$coreObjectCache = CoreObjectCacheBuilder::getInstance()->getData();
621 }
622
623 /**
624 * Assigns some default variables to the template engine.
625 */
626 protected function assignDefaultTemplateVariables() {
058cbd6a
MS
627 self::getTPL()->registerPrefilter(['event', 'hascontent', 'lang']);
628 self::getTPL()->assign([
d335fa16
AE
629 '__wcf' => $this,
630 '__wcfVersion' => LAST_UPDATE_TIME // @deprecated since 2.1, use LAST_UPDATE_TIME directly
058cbd6a 631 ]);
15621401
TD
632 EmailTemplateEngine::getInstance()->registerPrefilter(['event', 'hascontent', 'lang']);
633 EmailTemplateEngine::getInstance()->assign([
634 '__wcf' => $this
635 ]);
d335fa16
AE
636 }
637
638 /**
639 * Wrapper for the getter methods of this class.
640 *
641 * @param string $name
642 * @return mixed value
2b770bdd 643 * @throws SystemException
d335fa16
AE
644 */
645 public function __get($name) {
646 $method = 'get'.ucfirst($name);
647 if (method_exists($this, $method)) {
648 return $this->$method();
649 }
650
651 throw new SystemException("method '".$method."' does not exist in class WCF");
652 }
653
487db634
MW
654 /**
655 * Returns true if current application (WCF) is treated as active and was invoked directly.
656 *
657 * @return boolean
658 */
659 public function isActiveApplication() {
660 return (ApplicationHandler::getInstance()->getActiveApplication()->packageID == 1);
661 }
662
d335fa16
AE
663 /**
664 * Changes the active language.
665 *
666 * @param integer $languageID
667 */
668 public static final function setLanguage($languageID) {
2d90d60f
TD
669 if (!$languageID) $languageID = LanguageFactory::getInstance()->getDefaultLanguageID();
670
d335fa16
AE
671 self::$languageObj = LanguageFactory::getInstance()->getLanguage($languageID);
672 self::getTPL()->setLanguageID(self::getLanguage()->languageID);
673 }
674
675 /**
676 * Includes the required util or exception classes automatically.
677 *
678 * @param string $className
679 * @see spl_autoload_register()
680 */
681 public static final function autoload($className) {
682 $namespaces = explode('\\', $className);
683 if (count($namespaces) > 1) {
684 $applicationPrefix = array_shift($namespaces);
685 if ($applicationPrefix === '') {
686 $applicationPrefix = array_shift($namespaces);
687 }
688 if (isset(self::$autoloadDirectories[$applicationPrefix])) {
689 $classPath = self::$autoloadDirectories[$applicationPrefix] . implode('/', $namespaces) . '.class.php';
690 if (file_exists($classPath)) {
691 require_once($classPath);
692 }
693 }
694 }
695 }
696
697 /**
0fcfe5f6 698 * @inheritDoc
d335fa16
AE
699 */
700 public final function __call($name, array $arguments) {
701 // bug fix to avoid php crash, see http://bugs.php.net/bug.php?id=55020
702 if (!method_exists($this, $name)) {
703 return self::__callStatic($name, $arguments);
704 }
705
706 return $this->$name($arguments);
707 }
708
709 /**
710 * Returns dynamically loaded core objects.
711 *
712 * @param string $name
713 * @param array $arguments
71952a87 714 * @return object
2b770bdd 715 * @throws SystemException
d335fa16
AE
716 */
717 public static final function __callStatic($name, array $arguments) {
718 $className = preg_replace('~^get~', '', $name);
719
720 if (isset(self::$coreObject[$className])) {
721 return self::$coreObject[$className];
722 }
723
724 $objectName = self::getCoreObject($className);
725 if ($objectName === null) {
726 throw new SystemException("Core object '".$className."' is unknown.");
727 }
728
729 if (class_exists($objectName)) {
157054c9 730 if (!(is_subclass_of($objectName, SingletonFactory::class))) {
7b9ff46b 731 throw new ParentClassException($objectName, SingletonFactory::class);
d335fa16
AE
732 }
733
058cbd6a 734 self::$coreObject[$className] = call_user_func([$objectName, 'getInstance']);
d335fa16
AE
735 return self::$coreObject[$className];
736 }
737 }
738
739 /**
740 * Searches for cached core object definition.
741 *
742 * @param string $className
743 * @return string
744 */
745 protected static final function getCoreObject($className) {
746 if (isset(self::$coreObjectCache[$className])) {
747 return self::$coreObjectCache[$className];
748 }
749
750 return null;
751 }
752
753 /**
754 * Returns true if the debug mode is enabled, otherwise false.
755 *
756 * @param boolean $ignoreACP
757 * @return boolean
758 */
759 public static function debugModeIsEnabled($ignoreACP = false) {
760 // ACP override
761 if (!$ignoreACP && self::$overrideDebugMode) {
762 return true;
763 }
764 else if (defined('ENABLE_DEBUG_MODE') && ENABLE_DEBUG_MODE) {
765 return true;
766 }
767
768 return false;
769 }
770
771 /**
772 * Returns true if benchmarking is enabled, otherwise false.
773 *
774 * @return boolean
775 */
776 public static function benchmarkIsEnabled() {
777 // benchmarking is enabled by default
778 if (!defined('ENABLE_BENCHMARK') || ENABLE_BENCHMARK) return true;
779 return false;
780 }
781
782 /**
783 * Returns domain path for given application.
784 *
785 * @param string $abbreviation
786 * @return string
787 */
788 public static function getPath($abbreviation = 'wcf') {
789 // workaround during WCFSetup
790 if (!PACKAGE_ID) {
791 return '../';
792 }
793
794 if (!isset(self::$applications[$abbreviation])) {
795 $abbreviation = 'wcf';
796 }
797
798 return self::$applications[$abbreviation]->getPageURL();
799 }
800
801 /**
802 * Returns a fully qualified anchor for current page.
803 *
804 * @param string $fragment
805 * @return string
806 */
807 public function getAnchor($fragment) {
808 return self::getRequestURI() . '#' . $fragment;
809 }
810
12a2ff01
AE
811 /**
812 * Returns the currently active page or null if unknown.
813 *
814 * @return Page|null
815 */
816 public static function getActivePage() {
5346b183
AE
817 if (self::getActiveRequest()->getClassName() === CmsPage::class) {
818 $metaData = self::getActiveRequest()->getMetaData();
819 return PageCache::getInstance()->getPage($metaData['cms']['pageID']);
820 }
821
12a2ff01
AE
822 return PageCache::getInstance()->getPageByController(self::getActiveRequest()->getClassName());
823 }
824
825 /**
826 * Returns the currently active request.
827 *
828 * @return Request
829 */
830 public static function getActiveRequest() {
831 return RequestHandler::getInstance()->getActiveRequest();
832 }
833
d335fa16
AE
834 /**
835 * Returns the URI of the current page.
836 *
837 * @return string
838 */
839 public static function getRequestURI() {
840 if (URL_LEGACY_MODE) {
841 // resolve path and query components
842 $scriptName = $_SERVER['SCRIPT_NAME'];
843 $pathInfo = RouteHandler::getPathInfo();
844 if (empty($pathInfo)) {
845 // bug fix if URL omits script name and path
846 $scriptName = substr($scriptName, 0, strrpos($scriptName, '/'));
847 }
848
849 $path = str_replace('/index.php', '', str_replace($scriptName, '', $_SERVER['REQUEST_URI']));
850 if (!StringUtil::isUTF8($path)) {
851 $path = StringUtil::convertEncoding('ISO-8859-1', 'UTF-8', $path);
852 }
853 $path = FileUtil::removeLeadingSlash($path);
854 $baseHref = self::getTPL()->get('baseHref');
855
856 if (!empty($path) && mb_strpos($path, '?') !== 0) {
857 $baseHref .= 'index.php/';
858 }
859
860 return $baseHref . $path;
861 }
862 else {
4f86656e
AE
863 $url = preg_replace('~^(https?://[^/]+)(?:/.*)?$~', '$1', self::getTPL()->get('baseHref'));
864 $url .= $_SERVER['REQUEST_URI'];
d335fa16 865
4f86656e 866 return $url;
d335fa16
AE
867 }
868 }
869
870 /**
871 * Resets Zend Opcache cache if installed and enabled.
872 *
873 * @param string $script
874 */
875 public static function resetZendOpcache($script = '') {
876 if (self::$zendOpcacheEnabled === null) {
877 self::$zendOpcacheEnabled = false;
878
879 if (extension_loaded('Zend Opcache') && @ini_get('opcache.enable')) {
880 self::$zendOpcacheEnabled = true;
881 }
882
883 }
884
885 if (self::$zendOpcacheEnabled) {
886 if (empty($script)) {
887 opcache_reset();
888 }
889 else {
890 opcache_invalidate($script, true);
891 }
892 }
893 }
894
895 /**
896 * Returns style handler.
897 *
898 * @return \wcf\system\style\StyleHandler
899 */
900 public function getStyleHandler() {
901 return StyleHandler::getInstance();
902 }
903
55b402a0
MW
904 /**
905 * Returns box handler.
906 *
907 * @return BoxHandler
2f53b086 908 * @since 2.2
55b402a0
MW
909 */
910 public function getBoxHandler() {
911 return BoxHandler::getInstance();
912 }
913
d335fa16
AE
914 /**
915 * Returns number of available updates.
916 *
917 * @return integer
918 */
919 public function getAvailableUpdates() {
920 $data = PackageUpdateCacheBuilder::getInstance()->getData();
921 return $data['updates'];
922 }
923
924 /**
925 * Returns a 8 character prefix for editor autosave.
926 *
927 * @return string
928 */
929 public function getAutosavePrefix() {
930 return substr(sha1(preg_replace('~^https~', 'http', self::getPath())), 0, 8);
931 }
932
933 /**
934 * Returns the favicon URL or a base64 encoded image.
935 *
936 * @return string
937 */
938 public function getFavicon() {
939 $activeApplication = ApplicationHandler::getInstance()->getActiveApplication();
39abe192 940 $wcf = ApplicationHandler::getInstance()->getWCF();
d335fa16 941
39abe192 942 if ($activeApplication->domainName !== $wcf->domainName) {
d335fa16
AE
943 if (file_exists(WCF_DIR.'images/favicon.ico')) {
944 $favicon = file_get_contents(WCF_DIR.'images/favicon.ico');
945
946 return 'data:image/x-icon;base64,' . base64_encode($favicon);
947 }
948 }
949
950 return self::getPath() . 'images/favicon.ico';
951 }
952
a80873d5
AE
953 /**
954 * Returns true if currently active request represents the landing page.
955 *
8ff2cd79 956 * @return boolean
a80873d5 957 */
0fdd7f96 958 public static function isLandingPage() {
a80873d5
AE
959 return RequestHandler::getInstance()->getActiveRequest()->isLandingPage();
960 }
961
d335fa16
AE
962 /**
963 * Initialises the cronjobs.
964 */
965 protected function initCronjobs() {
966 if (PACKAGE_ID) {
967 self::getTPL()->assign('executeCronjobs', (CronjobScheduler::getInstance()->getNextExec() < TIME_NOW && defined('OFFLINE') && !OFFLINE));
968 }
969 }
970}