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