Skip to content

Commit 2f44a2c

Browse files
committed
Add support for smart URL rewriting
1 parent 20e7c5a commit 2f44a2c

File tree

8 files changed

+128
-21
lines changed

8 files changed

+128
-21
lines changed

wcfsetup/install/files/global.php

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
11
<?php
22

33
/**
4-
* @author Marcel Werk
4+
* @author Marcel Werk
55
* @copyright 2001-2019 WoltLab GmbH
6-
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
6+
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
77
*/
88

9-
// include config
109
require_once(__DIR__ . '/app.config.inc.php');
1110

12-
// Make the frontend inaccessible until WCFSetup completes.
13-
if (!PACKAGE_ID) {
11+
// Deny access to the frontend until the WCFSetup has completed.
12+
if (defined('PACKAGE_ID') && PACKAGE_ID === 0) {
1413
\http_response_code(500);
1514

1615
exit;
1716
}
1817

19-
// initiate wcf core
2018
require_once(WCF_DIR . 'lib/system/WCF.class.php');
2119
new wcf\system\WCF();

wcfsetup/install/files/lib/data/package/Package.class.php

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -433,11 +433,8 @@ private static function formatVersionForCompare(string $version): string
433433

434434
/**
435435
* Writes the config.inc.php for an application.
436-
*
437-
* @param int $packageID
438-
* @return void
439436
*/
440-
public static function writeConfigFile($packageID)
437+
public static function writeConfigFile(int $packageID): void
441438
{
442439
$package = new self($packageID);
443440
$packageDir = FileUtil::addTrailingSlash(FileUtil::getRealPath(WCF_DIR . $package->packageDir));
@@ -447,9 +444,9 @@ public static function writeConfigFile($packageID)
447444
$content = "<?php\n";
448445
$content .= "// {$package->package} (packageID {$packageID})\n";
449446
$content .= "if (!defined('{$prefix}_DIR')) define('{$prefix}_DIR', __DIR__.'/');\n";
450-
$content .= "if (!defined('PACKAGE_ID')) define('PACKAGE_ID', {$packageID});\n";
451447

452-
if ($packageID != 1) {
448+
if ($packageID !== 1) {
449+
$content .= "if (!defined('PACKAGE_ID')) define('PACKAGE_ID', {$packageID});\n";
453450
$content .= "\n";
454451
$content .= "// helper constants for applications\n";
455452
$content .= "if (!defined('RELATIVE_{$prefix}_DIR')) define('RELATIVE_{$prefix}_DIR', '');\n";

wcfsetup/install/files/lib/system/WCF.class.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ public function __construct()
194194
// start initialization
195195
$this->initDB();
196196
$this->loadOptions();
197+
$this->resolveActiveApplication();
197198
$this->initSession();
198199
$this->initLanguage();
199200
$this->initTPL();
@@ -418,7 +419,7 @@ protected function loadOptions(): void
418419
require($filename);
419420

420421
// check if option file is complete and writable
421-
if (PACKAGE_ID) {
422+
if (!defined('PACKAGE_ID') || \PACKAGE_ID !== 0) {
422423
if (!\is_writable($filename)) {
423424
FileUtil::makeWritable($filename);
424425

@@ -519,6 +520,26 @@ protected function defineLegacyOptions(): void
519520
\define('ATTACHMENT_IMAGE_AUTOSCALE_QUALITY', 80);
520521
}
521522

523+
/**
524+
* Resolve the active application and the path when using smart URL rewriting.
525+
*
526+
* @since 6.2
527+
*/
528+
protected function resolveActiveApplication(): void
529+
{
530+
if (!isset($_GET['__rewrittenPath'])) {
531+
if (!\defined('PACKAGE_ID')) {
532+
\define('PACKAGE_ID', 1);
533+
}
534+
535+
return;
536+
}
537+
538+
ApplicationHandler::getInstance()->resolveActiveApplication($_GET['__rewrittenPath']);
539+
540+
unset($_GET['__rewrittenPath']);
541+
}
542+
522543
/**
523544
* Starts the session system.
524545
*/

wcfsetup/install/files/lib/system/WCFACP.class.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public function __construct()
4747
// start initialization
4848
$this->initDB();
4949
$this->loadOptions();
50+
$this->resolveActiveApplication();
5051
$this->initSession();
5152
$this->initLanguage();
5253
$this->initTPL();

wcfsetup/install/files/lib/system/application/ApplicationHandler.class.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ final class ApplicationHandler extends SingletonFactory
2929
* @var array{
3030
* abbreviation: array<string, int>,
3131
* application: array<int, Application>,
32+
* rootApplication: ?int,
33+
* sortedPaths: array<int, string>
3234
* }
3335
*/
3436
private array $cache;
@@ -230,6 +232,38 @@ public function getDomainName(): string
230232
return $this->getApplicationByID(1)->domainName;
231233
}
232234

235+
/**
236+
* Resolve the active package id based on the rewritten URL.
237+
*
238+
* @since 6.2
239+
*/
240+
public function resolveActiveApplication(string $path): void
241+
{
242+
$rootApplication = $this->cache['rootApplication'];
243+
\assert($rootApplication !== null);
244+
245+
$path = FileUtil::removeLeadingSlash($path);
246+
$packageID = \array_find_key(
247+
$this->cache['sortedPaths'],
248+
static fn($prefix) => \str_starts_with($path, $prefix),
249+
);
250+
251+
if ($packageID === null) {
252+
\assert($this->cache['rootApplication'] !== null);
253+
$packageID = $this->cache['rootApplication'];
254+
} else {
255+
$prefix = $this->cache['sortedPaths'][$packageID];
256+
$path = \mb_substr($path, \mb_strlen($prefix));
257+
}
258+
259+
260+
RouteHandler::overridePathInfo($path);
261+
262+
if (!\defined('PACKAGE_ID')) {
263+
\define('PACKAGE_ID', $packageID);
264+
}
265+
}
266+
233267
/**
234268
* Rebuilds cookie domain/path for all applications.
235269
*/

wcfsetup/install/files/lib/system/cache/builder/ApplicationCacheBuilder.class.php

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,22 @@
99
/**
1010
* Caches applications.
1111
*
12-
* @author Alexander Ebert
12+
* @author Alexander Ebert
1313
* @copyright 2001-2019 WoltLab GmbH
14-
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
14+
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
1515
*/
16-
class ApplicationCacheBuilder extends AbstractCacheBuilder
16+
final class ApplicationCacheBuilder extends AbstractCacheBuilder
1717
{
18-
/**
19-
* @inheritDoc
20-
*/
18+
#[\Override]
2119
public function rebuild(array $parameters)
2220
{
2321
$data = [
2422
'abbreviation' => [],
2523
'application' => [],
24+
'rootApplication' => null,
25+
'sortedPaths' => [],
2626
];
2727

28-
// fetch applications
2928
$sql = "SELECT *
3029
FROM wcf" . WCF_N . "_application";
3130
$statement = WCF::getDB()->prepareUnmanaged($sql);
@@ -34,9 +33,16 @@ public function rebuild(array $parameters)
3433

3534
foreach ($applications as $application) {
3635
$data['application'][$application->packageID] = $application;
36+
$data['sortedPaths'][$application->packageID] = $application->domainPath;
37+
}
38+
39+
\uasort($data['sortedPaths'], static fn($a, $b) => \mb_strlen($b) - \mb_strlen($a));
40+
$data['rootApplication'] = $this->getRootApplication($data['sortedPaths']);
41+
42+
if ($data['rootApplication'] !== null) {
43+
$data['sortedPaths'] = $this->stripCommonPath($data['sortedPaths'], $data['rootApplication']);
3744
}
3845

39-
// fetch abbreviations
4046
$sql = "SELECT packageID, package
4147
FROM wcf" . WCF_N . "_package
4248
WHERE isApplication = ?";
@@ -49,4 +55,37 @@ public function rebuild(array $parameters)
4955

5056
return $data;
5157
}
58+
59+
/**
60+
* @param array<int, string> $sortedPaths
61+
* @return array<int, string>
62+
* @since 6.2
63+
*/
64+
private function stripCommonPath(array $sortedPaths, int $rootApplication): array
65+
{
66+
$length = \mb_strlen($sortedPaths[$rootApplication]);
67+
68+
return \array_map(
69+
static fn($path) => \mb_substr($path, $length),
70+
$sortedPaths
71+
);
72+
}
73+
74+
/**
75+
* @param array<int, string> $sortedPaths
76+
* @since 6.2
77+
*/
78+
private function getRootApplication(array $sortedPaths): ?int
79+
{
80+
$candidate = \array_key_last($sortedPaths);
81+
$shortestPath = $sortedPaths[$candidate];
82+
83+
foreach ($sortedPaths as $path) {
84+
if (!\str_starts_with($path, $shortestPath)) {
85+
return null;
86+
}
87+
}
88+
89+
return $candidate;
90+
}
5291
}

wcfsetup/install/files/lib/system/request/RequestHandler.class.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,13 @@ protected function init()
8989
*/
9090
public function handle(string $application = 'wcf', bool $isACPRequest = false): void
9191
{
92+
// Override the application when using smart URL rewriting.
93+
if ($application === 'wcf' && \PACKAGE_ID > 1) {
94+
$app = ApplicationHandler::getInstance()->getApplicationByID(\PACKAGE_ID);
95+
\assert($app !== null);
96+
$application = $app->getAbbreviation();
97+
}
98+
9299
try {
93100
$this->isACPRequest = $isACPRequest;
94101

wcfsetup/install/files/lib/system/request/RouteHandler.class.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,4 +384,14 @@ public static function getPathInfo(): string
384384

385385
return self::$pathInfo;
386386
}
387+
388+
/**
389+
* Overrides the path info as part of the smart URL rewriting feature.
390+
*
391+
* @since 6.2
392+
*/
393+
public static function overridePathInfo(string $pathInfo): void
394+
{
395+
self::$pathInfo = $pathInfo;
396+
}
387397
}

0 commit comments

Comments
 (0)