3 namespace wcf\system\application
;
5 use wcf\data\application\Application
;
6 use wcf\data\application\ApplicationAction
;
7 use wcf\data\application\ApplicationList
;
8 use wcf\data\package\Package
;
9 use wcf\data\package\PackageList
;
10 use wcf\system\cache\builder\ApplicationCacheBuilder
;
11 use wcf\system\request\RequestHandler
;
12 use wcf\system\request\RouteHandler
;
13 use wcf\system\SingletonFactory
;
15 use wcf\util\ArrayUtil
;
16 use wcf\util\FileUtil
;
17 use wcf\util\StringUtil
;
21 * Handles multi-application environments.
23 * @author Alexander Ebert
24 * @copyright 2001-2019 WoltLab GmbH
25 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
26 * @package WoltLabSuite\Core\System\Application
28 class ApplicationHandler
extends SingletonFactory
37 * true for multi-domain setups
40 protected $isMultiDomain;
46 protected $pageURLs = [];
51 protected function init()
53 $this->cache
= ApplicationCacheBuilder
::getInstance()->getData();
57 * Returns an application based upon it's abbreviation. Will return the
58 * primary application if the abbreviation is `wcf` or `null` if no such
61 * @param string $abbreviation package abbreviation, e.g. `wbb` for `com.woltlab.wbb`
62 * @return Application|null
64 public function getApplication($abbreviation)
66 if (isset($this->cache
['abbreviation'][$abbreviation])) {
67 $packageID = $this->cache
['abbreviation'][$abbreviation];
69 if (isset($this->cache
['application'][$packageID])) {
70 return $this->cache
['application'][$packageID];
76 * Returns an application delivered by the package with the given id or `null`
77 * if no such application exists.
79 * @param int $packageID package id
80 * @return Application|null application object
83 public function getApplicationByID($packageID)
85 // work-around for update from 2.1 (out-dated cache)
86 if ($packageID == 1 && !isset($this->cache
['application'][1])) {
87 $this->cache
['application'][1] = new Application(1);
89 if (isset($this->cache
['application'][$packageID])) {
90 return $this->cache
['application'][$packageID];
95 * Returns pseudo-application representing WCF used for special cases,
96 * e.g. cross-domain files requestable through the webserver.
99 * @deprecated 3.0 please use `getApplication()` instead
101 public function getWCF()
103 return $this->getApplicationByID(1);
107 * Returns the currently active application.
109 * @return Application
111 public function getActiveApplication()
113 // work-around during WCFSetup
115 $host = \
str_replace(RouteHandler
::getProtocol(), '', RouteHandler
::getHost());
116 $documentRoot = FileUtil
::addTrailingSlash(FileUtil
::unifyDirSeparator(\realpath
($_SERVER['DOCUMENT_ROOT'])));
118 // always use the core directory
119 if (empty($_POST['directories']) ||
empty($_POST['directories']['wcf'])) {
121 $_POST['directories'] = ['wcf' => $documentRoot . FileUtil
::removeLeadingSlash(RouteHandler
::getPath(['acp']))];
124 $path = FileUtil
::addLeadingSlash(FileUtil
::addTrailingSlash(FileUtil
::unifyDirSeparator(FileUtil
::getRelativePath(
126 $_POST['directories']['wcf']
129 return new Application(null, [
130 'domainName' => $host,
131 'domainPath' => $path,
132 'cookieDomain' => $host,
136 $request = RequestHandler
::getInstance()->getActiveRequest();
137 if ($request !== null) {
138 $abbreviation = \
substr($request->getClassName(), 0, \
mb_strpos($request->getClassName(), '\\'));
140 return $this->getApplication($abbreviation);
143 if (isset($this->cache
['application'][PACKAGE_ID
])) {
144 return $this->cache
['application'][PACKAGE_ID
];
147 return $this->getWCF();
151 * Returns a list of dependent applications.
153 * @return Application[]
155 public function getDependentApplications()
157 $applications = $this->getApplications();
158 foreach ($applications as $key => $application) {
159 if ($application->packageID
== $this->getActiveApplication()->packageID
) {
160 unset($applications[$key]);
165 return $applications;
169 * Returns a list of all active applications.
171 * @return Application[]
173 public function getApplications()
175 return $this->cache
['application'];
179 * Returns abbreviation for a given package id or `null` if application is unknown.
181 * @param int $packageID unique package id
182 * @return string|null
184 public function getAbbreviation($packageID)
186 foreach ($this->cache
['abbreviation'] as $abbreviation => $applicationID) {
187 if ($packageID == $applicationID) {
188 return $abbreviation;
194 * Returns the list of application abbreviations.
199 public function getAbbreviations()
201 return \array_keys
($this->cache
['abbreviation']);
205 * Returns true if given $url is an internal URL.
210 public function isInternalURL($url)
212 if (empty($this->pageURLs
)) {
213 foreach ($this->getApplications() as $application) {
214 $this->pageURLs
[] = $application->domainName
;
217 $this->pageURLs
= \array_unique
(\array_merge
(
219 ArrayUtil
::trim(\
explode("\n", StringUtil
::unifyNewlines(\INTERNAL_HOSTNAMES
)))
223 $host = Url
::parse($url)['host'];
225 // Relative URLs are internal.
230 return Url
::getHostnameMatcher($this->pageURLs
)($host);
234 * Returns true if this is a multi-domain setup.
240 public function isMultiDomainSetup()
242 if ($this->isMultiDomain
=== null) {
243 $this->isMultiDomain
= false;
245 $domainName = $this->getApplicationByID(1)->domainName
;
246 foreach ($this->getApplications() as $application) {
247 if ($application->domainName
!== $domainName) {
248 $this->isMultiDomain
= true;
254 return $this->isMultiDomain
;
260 public function rebuildActiveApplication()
262 /** @var AbstractApplication $application */
263 foreach ($this->cache
['application'] as $application) {
264 if ($application->getPackage()->package
=== 'com.woltlab.wcf') {
268 $appObject = WCF
::getApplicationObject($application);
269 if ($appObject instanceof AbstractApplication
) {
270 $appObject->rebuildActiveApplication();
276 * Rebuilds cookie domain/path for all applications.
278 public static function rebuild()
280 $applicationList = new ApplicationList();
281 $applicationList->readObjects();
283 $applicationAction = new ApplicationAction($applicationList->getObjects(), 'rebuild');
284 $applicationAction->executeAction();
288 * Replaces `app1_` in the given string with the correct installation number:
291 * This method can either be used for database table names directly or for
292 * queries, for example.
294 * @param string $string string to be processed
295 * @param bool $skipCache if `true`, no caches will be used and relevant application packages will be read from database directly
296 * @return string processed string
299 public static function insertRealDatabaseTableNames($string, $skipCache = false)
302 $packageList = new PackageList();
303 $packageList->getConditionBuilder()->add('package.isApplication = ?', [1]);
304 $packageList->readObjects();
306 foreach ($packageList as $package) {
307 $abbreviation = Package
::getAbbreviation($package->package
);
309 $string = \
str_replace($abbreviation . '1_', $abbreviation . WCF_N
. '_', $string);
312 foreach (static::getInstance()->getAbbreviations() as $abbreviation) {
313 $string = \
str_replace($abbreviation . '1_', $abbreviation . WCF_N
. '_', $string);