diff --git a/wcfsetup/install/files/lib/acp/action/UninstallPackageAction.class.php b/wcfsetup/install/files/lib/acp/action/UninstallPackageAction.class.php index f530cba3a1c..132a7999d12 100644 --- a/wcfsetup/install/files/lib/acp/action/UninstallPackageAction.class.php +++ b/wcfsetup/install/files/lib/acp/action/UninstallPackageAction.class.php @@ -6,10 +6,11 @@ use Psr\Http\Message\ResponseInterface; use wcf\acp\page\PackageListPage; use wcf\action\AbstractSecureAction; -use wcf\data\application\ApplicationAction; +use wcf\command\application\MarkApplicationAsTainted; use wcf\data\package\installation\queue\PackageInstallationQueue; use wcf\data\package\installation\queue\PackageInstallationQueueEditor; use wcf\data\package\Package; +use wcf\system\application\ApplicationHandler; use wcf\system\exception\IllegalLinkException; use wcf\system\package\PackageUninstallationDispatcher; use wcf\system\request\LinkHandler; @@ -116,8 +117,10 @@ protected function stepPrepare(): ResponseInterface // mark package as tainted if it is an app if ($package->isApplication) { - $applicationAction = new ApplicationAction([$package->packageID], 'markAsTainted'); - $applicationAction->executeAction(); + $application = ApplicationHandler::getInstance()->getApplicationByID($package->packageID); + \assert($application !== null); + + (new MarkApplicationAsTainted($application))(); } $this->installation->nodeBuilder->purgeNodes(); diff --git a/wcfsetup/install/files/lib/acp/form/ApplicationManagementForm.class.php b/wcfsetup/install/files/lib/acp/form/ApplicationManagementForm.class.php index bb7f611f376..56ffbb43080 100644 --- a/wcfsetup/install/files/lib/acp/form/ApplicationManagementForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/ApplicationManagementForm.class.php @@ -9,9 +9,9 @@ use wcf\data\page\PageNodeTree; use wcf\form\AbstractForm; use wcf\system\application\ApplicationHandler; -use wcf\system\cache\builder\ApplicationCacheBuilder; use wcf\system\cache\builder\PageCacheBuilder; use wcf\system\cache\builder\RoutingCacheBuilder; +use wcf\system\cache\eager\ApplicationCache; use wcf\system\exception\UserInputException; use wcf\system\Regex; use wcf\system\style\StyleHandler; @@ -188,7 +188,7 @@ public function save() ApplicationHandler::rebuild(); // Reset caches to reflect the new landing pages. - ApplicationCacheBuilder::getInstance()->reset(); + (new ApplicationCache())->rebuild(); PageCacheBuilder::getInstance()->reset(); RoutingCacheBuilder::getInstance()->reset(); StyleHandler::resetStylesheets(); diff --git a/wcfsetup/install/files/lib/acp/form/RescueModeForm.class.php b/wcfsetup/install/files/lib/acp/form/RescueModeForm.class.php index 774c601a7b5..9d09a82dda8 100644 --- a/wcfsetup/install/files/lib/acp/form/RescueModeForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/RescueModeForm.class.php @@ -3,8 +3,8 @@ namespace wcf\acp\form; use Laminas\Diactoros\Response\RedirectResponse; +use wcf\command\application\SynchronizeCookieDomain; use wcf\data\application\Application; -use wcf\data\application\ApplicationAction; use wcf\data\application\ApplicationEditor; use wcf\data\application\ApplicationList; use wcf\data\user\authentication\failure\UserAuthenticationFailure; @@ -333,9 +333,7 @@ public function save() ]); } - // rebuild cookie domain and paths - $applicationAction = new ApplicationAction([], 'rebuild'); - $applicationAction->executeAction(); + (new SynchronizeCookieDomain())(); // reload currently active application to avoid outdated cache data $application = ApplicationHandler::getInstance()->getActiveApplication(); diff --git a/wcfsetup/install/files/lib/command/application/MarkApplicationAsTainted.class.php b/wcfsetup/install/files/lib/command/application/MarkApplicationAsTainted.class.php new file mode 100644 index 00000000000..e2c2f5cd255 --- /dev/null +++ b/wcfsetup/install/files/lib/command/application/MarkApplicationAsTainted.class.php @@ -0,0 +1,31 @@ + + * @since 6.3 + */ +final class MarkApplicationAsTainted +{ + public function __construct( + public readonly Application $application + ) {} + + public function __invoke(): void + { + $applicationEditor = new ApplicationEditor($this->application); + $applicationEditor->update(['isTainted' => 1]); + + (new ApplicationCache())->rebuild(); + } +} diff --git a/wcfsetup/install/files/lib/command/application/SynchronizeCookieDomain.class.php b/wcfsetup/install/files/lib/command/application/SynchronizeCookieDomain.class.php new file mode 100644 index 00000000000..9dfe6c61b02 --- /dev/null +++ b/wcfsetup/install/files/lib/command/application/SynchronizeCookieDomain.class.php @@ -0,0 +1,64 @@ + + * @since 6.3 + */ +final class SynchronizeCookieDomain +{ + public function __invoke(): void + { + $sql = "UPDATE wcf1_application + SET cookieDomain = ? + WHERE packageID = ?"; + $statement = WCF::getDB()->prepare($sql); + + $regex = new Regex(':[0-9]+'); + + WCF::getDB()->beginTransaction(); + foreach ($this->getApplications() as $application) { + $domainName = $application->domainName; + if (\str_ends_with($regex->replace($domainName, ''), $application->cookieDomain)) { + $domainName = $application->cookieDomain; + } + + $statement->execute([ + $domainName, + $application->packageID, + ]); + } + WCF::getDB()->commitTransaction(); + + $this->resetCache(); + } + + /** + * @return Application[] + */ + private function getApplications(): array + { + $applicationList = new ApplicationList(); + $applicationList->readObjects(); + + return $applicationList->getObjects(); + } + + private function resetCache(): void + { + LanguageFactory::getInstance()->deleteLanguageCache(); + (new ApplicationCache())->rebuild(); + } +} diff --git a/wcfsetup/install/files/lib/data/application/ApplicationAction.class.php b/wcfsetup/install/files/lib/data/application/ApplicationAction.class.php index ba2e1636105..2d61f0a36cb 100644 --- a/wcfsetup/install/files/lib/data/application/ApplicationAction.class.php +++ b/wcfsetup/install/files/lib/data/application/ApplicationAction.class.php @@ -2,11 +2,9 @@ namespace wcf\data\application; +use wcf\command\application\MarkApplicationAsTainted; +use wcf\command\application\SynchronizeCookieDomain; use wcf\data\AbstractDatabaseObjectAction; -use wcf\system\cache\builder\ApplicationCacheBuilder; -use wcf\system\language\LanguageFactory; -use wcf\system\Regex; -use wcf\system\WCF; /** * Executes application-related actions. @@ -24,61 +22,27 @@ class ApplicationAction extends AbstractDatabaseObjectAction */ protected $className = ApplicationEditor::class; - /** - * application editor object - * @var ApplicationEditor - */ - public $applicationEditor; - /** * Assigns a list of applications to a group and computes cookie domain. * * @return void + * + * @deprecated 6.3 use `SynchronizeCookieDomain` */ public function rebuild() { - if (empty($this->objects)) { - $this->readObjects(); - } - - $sql = "UPDATE wcf1_application - SET cookieDomain = ? - WHERE packageID = ?"; - $statement = WCF::getDB()->prepare($sql); - - // calculate cookie domain - $regex = new Regex(':[0-9]+'); - WCF::getDB()->beginTransaction(); - foreach ($this->getObjects() as $application) { - $domainName = $application->domainName; - if (\str_ends_with($regex->replace($domainName, ''), $application->cookieDomain)) { - $domainName = $application->cookieDomain; - } - - $statement->execute([ - $domainName, - $application->packageID, - ]); - } - WCF::getDB()->commitTransaction(); - - // rebuild templates - LanguageFactory::getInstance()->deleteLanguageCache(); - - // reset application cache - ApplicationCacheBuilder::getInstance()->reset(); + (new SynchronizeCookieDomain())(); } /** * Marks an application as tainted, prevents loading it during uninstallation. * * @return void + * + * @deprecated 6.3 use `MarkApplicationAsTainted` */ public function markAsTainted() { - $applicationEditor = $this->getSingleObject(); - $applicationEditor->update(['isTainted' => 1]); - - ApplicationCacheBuilder::getInstance()->reset(); + (new MarkApplicationAsTainted($this->getSingleObject()->getDecoratedObject()))(); } } diff --git a/wcfsetup/install/files/lib/data/application/ApplicationEditor.class.php b/wcfsetup/install/files/lib/data/application/ApplicationEditor.class.php index 034faea2ee2..8f59cf61dc2 100644 --- a/wcfsetup/install/files/lib/data/application/ApplicationEditor.class.php +++ b/wcfsetup/install/files/lib/data/application/ApplicationEditor.class.php @@ -4,7 +4,7 @@ use wcf\data\DatabaseObjectEditor; use wcf\data\IEditableCachedObject; -use wcf\system\cache\builder\ApplicationCacheBuilder; +use wcf\system\cache\eager\ApplicationCache; /** * Provides functions to edit applications. @@ -29,6 +29,6 @@ class ApplicationEditor extends DatabaseObjectEditor implements IEditableCachedO */ public static function resetCache() { - ApplicationCacheBuilder::getInstance()->reset(); + (new ApplicationCache())->rebuild(); } } diff --git a/wcfsetup/install/files/lib/data/page/Page.class.php b/wcfsetup/install/files/lib/data/page/Page.class.php index e67e475c187..1e275c33611 100644 --- a/wcfsetup/install/files/lib/data/page/Page.class.php +++ b/wcfsetup/install/files/lib/data/page/Page.class.php @@ -12,8 +12,8 @@ use wcf\data\user\User; use wcf\system\acl\simple\SimpleAclResolver; use wcf\system\application\ApplicationHandler; -use wcf\system\cache\builder\ApplicationCacheBuilder; use wcf\system\cache\builder\RoutingCacheBuilder; +use wcf\system\cache\eager\ApplicationCache; use wcf\system\database\util\PreparedStatementConditionBuilder; use wcf\system\exception\SystemException; use wcf\system\language\LanguageFactory; @@ -297,7 +297,7 @@ public function setAsLandingPage() WCF::getDB()->commitTransaction(); // reset caches to reflect new landing page - ApplicationCacheBuilder::getInstance()->reset(); + (new ApplicationCache())->rebuild(); RoutingCacheBuilder::getInstance()->reset(); } diff --git a/wcfsetup/install/files/lib/system/application/ApplicationHandler.class.php b/wcfsetup/install/files/lib/system/application/ApplicationHandler.class.php index 8a599987bde..d9200e12254 100644 --- a/wcfsetup/install/files/lib/system/application/ApplicationHandler.class.php +++ b/wcfsetup/install/files/lib/system/application/ApplicationHandler.class.php @@ -2,11 +2,11 @@ namespace wcf\system\application; +use wcf\command\application\SynchronizeCookieDomain; use wcf\data\application\Application; -use wcf\data\application\ApplicationAction; -use wcf\data\application\ApplicationList; use wcf\data\package\Package; -use wcf\system\cache\builder\ApplicationCacheBuilder; +use wcf\system\cache\eager\ApplicationCache; +use wcf\system\cache\eager\data\ApplicationCacheData; use wcf\system\request\RequestHandler; use wcf\system\request\RouteHandler; use wcf\system\SingletonFactory; @@ -19,30 +19,25 @@ /** * Handles multi-application environments. * - * @author Alexander Ebert - * @copyright 2001-2019 WoltLab GmbH + * @author Alexander Ebert + * @copyright 2001-2019 WoltLab GmbH * @license GNU Lesser General Public License */ final class ApplicationHandler extends SingletonFactory { - /** - * application cache - * @var mixed[][] - */ - protected $cache; + private ApplicationCacheData $cache; /** - * list of page URLs * @var string[] */ - protected array $pageURLs = []; + private array $pageURLs = []; /** * Initializes cache. */ protected function init() { - $this->cache = ApplicationCacheBuilder::getInstance()->getData(); + $this->cache = (new ApplicationCache())->getCache(); } /** @@ -54,26 +49,16 @@ protected function init() */ public function getApplication(string $abbreviation): ?Application { - if (isset($this->cache['abbreviation'][$abbreviation])) { - $packageID = $this->cache['abbreviation'][$abbreviation]; - - if (isset($this->cache['application'][$packageID])) { - return $this->cache['application'][$packageID]; - } - } - - return null; + return $this->cache->getApplicationByAbbreviation($abbreviation); } /** * Returns an application delivered by the package with the given id or `null` * if no such application exists. - * - * @since 3.0 */ public function getApplicationByID(int $packageID): ?Application { - return $this->cache['application'][$packageID] ?? null; + return $this->cache->getApplication($packageID); } /** @@ -120,17 +105,13 @@ public function getActiveApplication(): Application return $this->getApplication($abbreviation); } - if (isset($this->cache['application'][PACKAGE_ID])) { - return $this->cache['application'][PACKAGE_ID]; - } - - return $this->getWCF(); + return $this->cache->getApplication(PACKAGE_ID) ?? $this->getWCF(); } /** * Returns a list of dependent applications. * - * @return Application[] + * @return Application[] */ public function getDependentApplications(): array { @@ -148,11 +129,11 @@ public function getDependentApplications(): array /** * Returns a list of all active applications. * - * @return Application[] + * @return Application[] */ public function getApplications(): array { - return $this->cache['application']; + return $this->cache->applications; } /** @@ -160,24 +141,17 @@ public function getApplications(): array */ public function getAbbreviation(int $packageID): ?string { - foreach ($this->cache['abbreviation'] as $abbreviation => $applicationID) { - if ($packageID == $applicationID) { - return $abbreviation; - } - } - - return null; + return $this->cache->getAbbreviationByPackageID($packageID); } /** * Returns the list of application abbreviations. * - * @return string[] - * @since 3.1 + * @return string[] */ public function getAbbreviations(): array { - return \array_keys($this->cache['abbreviation']); + return \array_keys($this->cache->abbreviations); } /** @@ -207,7 +181,6 @@ public function isInternalURL(string $url): bool /** * Always returns false. * - * @since 3.1 * @deprecated 5.4 */ public function isMultiDomainSetup(): bool @@ -216,12 +189,9 @@ public function isMultiDomainSetup(): bool } /** - * @since 5.2 * @deprecated 5.5 - This function is a noop. The 'active' status is determined live. */ - public function rebuildActiveApplication(): void - { - } + public function rebuildActiveApplication(): void {} /** * @since 6.0 @@ -236,11 +206,7 @@ public function getDomainName(): string */ public static function rebuild(): void { - $applicationList = new ApplicationList(); - $applicationList->readObjects(); - - $applicationAction = new ApplicationAction($applicationList->getObjects(), 'rebuild'); - $applicationAction->executeAction(); + (new SynchronizeCookieDomain())(); } /** @@ -251,7 +217,6 @@ public static function rebuild(): void * queries, for example. * * @param $skipCache if `true`, no caches will be used and relevant application packages will be read from database directly - * @since 5.2 */ public static function insertRealDatabaseTableNames(string $string, bool $skipCache = false): string { @@ -263,7 +228,7 @@ public static function insertRealDatabaseTableNames(string $string, bool $skipCa } if ($skipCache) { - $sql = "SELECT package + $sql = "SELECT package FROM wcf" . WCF_N . "_package WHERE isApplication = ?"; $statement = WCF::getDB()->prepareUnmanaged($sql); diff --git a/wcfsetup/install/files/lib/system/cache/builder/ApplicationCacheBuilder.class.php b/wcfsetup/install/files/lib/system/cache/builder/ApplicationCacheBuilder.class.php index 1333b423db0..24094931674 100644 --- a/wcfsetup/install/files/lib/system/cache/builder/ApplicationCacheBuilder.class.php +++ b/wcfsetup/install/files/lib/system/cache/builder/ApplicationCacheBuilder.class.php @@ -2,9 +2,7 @@ namespace wcf\system\cache\builder; -use wcf\data\application\Application; -use wcf\data\package\Package; -use wcf\system\WCF; +use wcf\system\cache\eager\ApplicationCache; /** * Caches applications. @@ -12,41 +10,25 @@ * @author Alexander Ebert * @copyright 2001-2019 WoltLab GmbH * @license GNU Lesser General Public License + * + * @deprecated 6.3 Use `ApplicationCache` instead. */ -class ApplicationCacheBuilder extends AbstractCacheBuilder +class ApplicationCacheBuilder extends AbstractLegacyCacheBuilder { - /** - * @inheritDoc - */ - public function rebuild(array $parameters) + #[\Override] + protected function rebuild(array $parameters): array { - $data = [ - 'abbreviation' => [], - 'application' => [], - ]; - - // fetch applications - $sql = "SELECT * - FROM wcf" . WCF_N . "_application"; - $statement = WCF::getDB()->prepareUnmanaged($sql); - $statement->execute(); - $applications = $statement->fetchObjects(Application::class); - - foreach ($applications as $application) { - $data['application'][$application->packageID] = $application; - } + $cache = (new ApplicationCache())->getCache(); - // fetch abbreviations - $sql = "SELECT packageID, package - FROM wcf" . WCF_N . "_package - WHERE isApplication = ?"; - $statement = WCF::getDB()->prepareUnmanaged($sql); - $statement->execute([1]); - $packages = $statement->fetchMap('packageID', 'package'); - foreach ($packages as $packageID => $package) { - $data['abbreviation'][Package::getAbbreviation($package)] = $packageID; - } + return [ + 'application' => $cache->applications, + 'abbreviation' => $cache->abbreviations, + ]; + } - return $data; + #[\Override] + public function reset(array $parameters = []) + { + (new ApplicationCache())->rebuild(); } } diff --git a/wcfsetup/install/files/lib/system/cache/eager/ApplicationCache.class.php b/wcfsetup/install/files/lib/system/cache/eager/ApplicationCache.class.php new file mode 100644 index 00000000000..2e86feb8964 --- /dev/null +++ b/wcfsetup/install/files/lib/system/cache/eager/ApplicationCache.class.php @@ -0,0 +1,49 @@ + + * @since 6.3 + * + * @extends AbstractEagerCache + */ +final class ApplicationCache extends AbstractEagerCache +{ + #[\Override] + protected function getCacheData(): ApplicationCacheData + { + $sql = "SELECT * + FROM wcf" . WCF_N . "_application"; + $statement = WCF::getDB()->prepareUnmanaged($sql); + $statement->execute(); + $applications = $statement->fetchObjects(Application::class, 'packageID'); + + + $sql = "SELECT packageID, package + FROM wcf" . WCF_N . "_package + WHERE isApplication = ?"; + $statement = WCF::getDB()->prepareUnmanaged($sql); + $statement->execute([1]); + $packages = $statement->fetchMap('packageID', 'package'); + + $abbreviation = []; + foreach ($packages as $packageID => $package) { + $abbreviation[Package::getAbbreviation($package)] = $packageID; + } + + return new ApplicationCacheData( + $applications, + $abbreviation, + ); + } +} diff --git a/wcfsetup/install/files/lib/system/cache/eager/data/ApplicationCacheData.class.php b/wcfsetup/install/files/lib/system/cache/eager/data/ApplicationCacheData.class.php new file mode 100644 index 00000000000..da980e5fe0a --- /dev/null +++ b/wcfsetup/install/files/lib/system/cache/eager/data/ApplicationCacheData.class.php @@ -0,0 +1,42 @@ + + * @since 6.3 + */ +final class ApplicationCacheData +{ + public function __construct( + /** @var array */ + public readonly array $applications, + /** @var array */ + public readonly array $abbreviations, + ) { + } + + public function getApplication(int $packageID): ?Application + { + return $this->applications[$packageID] ?? null; + } + + public function getApplicationByAbbreviation(string $abbreviation): ?Application + { + $packageID = $this->abbreviations[$abbreviation] ?? null; + if ($packageID === null) { + return null; + } + + return $this->getApplication($packageID); + } + + public function getAbbreviationByPackageID(int $packageID): ?string + { + return \array_search($packageID, $this->abbreviations) ?: null; + } +}