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