Files
kupshop/bundles/KupShop/KupShopBundle/Context/ContextManager.php
2025-08-02 16:30:27 +02:00

274 lines
7.2 KiB
PHP

<?php
namespace KupShop\KupShopBundle\Context;
use KupShop\KupShopBundle\Exception\InvalidArgumentException;
use KupShop\KupShopBundle\Util\Contexts;
use KupShop\PricelistBundle\Context\PricelistContext;
use KupShop\SellerBundle\Context\SellerContext;
class ContextManager
{
public const CLEAR_CACHE = null;
public const FORCE_EMPTY = 'FORCE_EMPTY';
public function __construct()
{
}
/**
* @template T
*
* @param callable(): T $callback
*
* @return T
*
* @throws InvalidArgumentException
*/
public function activateSite(int $siteId, callable $callback): mixed
{
return $this->activateContexts($this->getActivateSiteContexts($siteId), $callback);
}
/**
* @template T
*
* @param callable(): T $callback
*
* @return T
*
* @throws InvalidArgumentException
*/
public function activateOrder(\OrderBase $order, callable $callback)
{
return $this->activateContexts($this->getActivateOrderContexts($order), $callback);
}
/**
* @template T
*
* @param callable(): T $callback
*
* @return T
*
* @throws InvalidArgumentException
*/
public function activateUser(\User $user, callable $callback): mixed
{
return $this->activateContexts($this->getActivateUserContexts($user), $callback);
}
/**
* @template T
*
* @param callable(): T $callback
*
* @return T
*
* @throws InvalidArgumentException
*/
public function deactivateUser(callable $callback): mixed
{
return $this->activateContexts($this->getDeactivateUserContexts(), $callback);
}
/**
* @template T
*
* @param callable(): T $callback
*
* @return T
*
* @throws InvalidArgumentException
*/
public function activateContexts(array $contexts, callable $callback)
{
$originalValues = [];
foreach ($contexts as $class => $id) {
$context = null;
// e.g. if context is in a bundle that is not activated
try {
$context = Contexts::get($class);
} catch (\Throwable $e) {
if (isDevelopment()) {
throw new InvalidArgumentException("Tohle by se nemělo stát, {$class} neexistuje!", previous: $e);
}
}
if ($context === null) {
continue;
}
if (!($context instanceof ContextInterface)) {
throw new InvalidArgumentException("Invalid context class: {$class} is not a ContextInterface.");
}
$originalValues[$class] = $this->getOriginalContextValue($context);
switch ($id) {
case self::CLEAR_CACHE:
$context->clearCache();
break;
case self::FORCE_EMPTY:
if (!($context instanceof EmptiableContext)) {
throw new InvalidArgumentException("{$context} is not Emptiable, but FORCE_EMPTY was passed in!");
}
$context->forceEmpty();
break;
default:
$context->activate($id);
}
}
$result = $callback();
// restore original values
foreach ($originalValues as $class => $value) {
$context = Contexts::get($class);
switch ($value) {
case self::CLEAR_CACHE:
$context->clearCache();
break;
case self::FORCE_EMPTY:
$context->forceEmpty();
break;
default:
$context->activate($value);
}
}
return $result;
}
protected function getOriginalContextValue(ContextInterface $context): mixed
{
$id = $context instanceof UserContext ? $context->getActive() : $context->getActiveId();
if ($id === null && $context instanceof EmptiableContext && $context->isEmpty()) {
$id = self::FORCE_EMPTY;
}
return $id;
}
public function getDomainFromLanguage($language)
{
return null;
}
protected function getActivateUserContexts(\User $user): array
{
$contexts = [
UserContext::class => $user,
VatContext::class => self::CLEAR_CACHE,
];
if (findModule(\Modules::PRICE_LEVELS)) {
$contexts[PriceLevelContext::class] = self::CLEAR_CACHE;
}
if (findModule(\Modules::PRICELISTS)) {
$contexts[PricelistContext::class] = self::CLEAR_CACHE;
}
return $contexts;
}
protected function getActivateOrderContexts(\OrderBase $order): array
{
$contexts = [
UserContext::class => $order->getUser() ?? self::FORCE_EMPTY,
VatContext::class => self::CLEAR_CACHE,
];
if (findModule(\Modules::PRICE_LEVELS)) {
$contexts[PriceLevelContext::class] = self::CLEAR_CACHE;
}
if (findModule(\Modules::PRICELISTS)) {
$contexts[PricelistContext::class] = self::CLEAR_CACHE;
}
return $contexts;
}
protected function getActivateSiteContexts(int $siteId): array
{
$site = Contexts::get(SiteContext::class)->getSupported()[$siteId] ?? null;
return [
SiteContext::class => $siteId,
DomainContext::class => $site?->getDomain(),
LanguageContext::class => $site?->getLanguage(),
CurrencyContext::class => self::CLEAR_CACHE,
CountryContext::class => self::CLEAR_CACHE,
VatContext::class => self::CLEAR_CACHE,
];
}
protected function getDeactivateUserContexts(): array
{
$contexts = [
UserContext::class => self::FORCE_EMPTY,
];
if (findModule(\Modules::PRICE_LEVELS)) {
$contexts[PriceLevelContext::class] = self::CLEAR_CACHE;
}
if (findModule(\Modules::PRICELISTS)) {
$contexts[PricelistContext::class] = self::CLEAR_CACHE;
}
if (findModule(\Modules::SELLERS)) {
$contexts[SellerContext::class] = self::FORCE_EMPTY;
}
return $contexts;
}
protected function getEmptiableContexts(): array
{
$contexts = [UserContext::class];
if (findModule(\Modules::PRICE_LEVELS)) {
$contexts[] = PriceLevelContext::class;
}
if (findModule(\Modules::PRICELISTS)) {
$contexts[] = PricelistContext::class;
}
if (findModule(\Modules::SELLERS)) {
$contexts[] = SellerContext::class;
}
return $contexts;
}
public function forceEmptyContexts(): void
{
foreach ($this->getEmptiableContexts() as $context) {
Contexts::get($context)->forceEmpty();
}
}
public function checkEmptyContextsLeak(): array
{
$leaks = [];
foreach ($this->getEmptiableContexts() as $context) {
if (!Contexts::get($context)->isEmpty()) {
$leaks[$context] = Contexts::get($context);
}
}
return $leaks;
}
}