<?php declare(strict_types=1);
namespace Shopware\Core\Framework;
use Shopware\Core\Checkout\Cart\Price\Struct\CartPrice;
use Shopware\Core\Defaults;
use Shopware\Core\Framework\Api\Context\AdminApiSource;
use Shopware\Core\Framework\Api\Context\ContextSource;
use Shopware\Core\Framework\Api\Context\SystemSource;
use Shopware\Core\Framework\DataAbstractionLayer\Pricing\CashRoundingConfig;
use Shopware\Core\Framework\Log\Package;
use Shopware\Core\Framework\Struct\StateAwareTrait;
use Shopware\Core\Framework\Struct\Struct;
use Shopware\Core\System\SalesChannel\Exception\ContextRulesLockedException;
use Symfony\Component\Serializer\Annotation\Ignore;
#[Package('core')]
class Context extends Struct
{
const SYSTEM_SCOPE = 'system';
const USER_SCOPE = 'user';
const CRUD_API_SCOPE = 'crud';
/**
* @deprecated tag:v6.5.0 - Use `\Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria::STATE_ELASTICSEARCH_AWARE` on Criteria instead
*/
const STATE_ELASTICSEARCH_AWARE = 'elasticsearchAware';
const SKIP_TRIGGER_FLOW = 'skipTriggerFlow';
/**
* @var non-empty-array<string>
*/
protected $languageIdChain;
protected $scope = self::USER_SCOPE;
protected $rulesLocked = false;
/**
* @var array<string>
*/
#[Ignore]
protected $extensions = [];
/**
* @var ContextSource
*/
protected $source;
/**
* @var non-empty-array<string>
*/
protected $ruleIds = [];
/**
* @var string
*/
protected $currencyId = Defaults::CURRENCY;
/**
* @var string
*/
protected $versionId = Defaults::LIVE_VERSION;
/**
* @var float
*/
protected $currencyFactor = 1.0;
/**
* @var bool
*/
protected $considerInheritance = false;
/**
* @var string
*/
protected $taxState = CartPrice::TAX_STATE_GROSS;
/**
* @var CashRoundingConfig
*/
protected $rounding;
/**
* @var array<string>
*/
private $states = [];
/**
* @param array<string> $languageIdChain
* @param array<string> $ruleIds
*/
public function __construct(
ContextSource $source,
array $ruleIds = [],
string $currencyId = Defaults::CURRENCY,
array $languageIdChain = [Defaults::LANGUAGE_SYSTEM],
string $versionId = Defaults::LIVE_VERSION,
float $currencyFactor = 1.0,
bool $considerInheritance = false,
/**
* @see CartPrice::TAX_STATE_GROSS, CartPrice::TAX_STATE_NET, CartPrice::TAX_STATE_FREE
*/
string $taxState = CartPrice::TAX_STATE_GROSS,
CashRoundingConfig $rounding = null
) {
$this->rounding = $rounding ?? new CashRoundingConfig(2, 0.01, true);
$this->taxState = $taxState;
$this->considerInheritance = $considerInheritance;
$this->currencyFactor = $currencyFactor;
$this->versionId = $versionId;
$this->currencyId = $currencyId;
$this->ruleIds = $ruleIds;
$this->source = $source;
if ($source instanceof SystemSource) {
$this->scope = self::SYSTEM_SCOPE;
}
if (empty($languageIdChain)) {
throw new \InvalidArgumentException('Argument languageIdChain must not be empty');
}
/** @var non-empty-array<string> $chain */
$chain = array_keys(array_flip(array_filter($languageIdChain)));
$this->languageIdChain = $chain;
}
/**
* @internal
*/
public static function createDefaultContext(ContextSource $source = null): self
{
return new self($source ?? new SystemSource());
}
public static function createCLIContext(ContextSource $source = null): self
{
return self::createDefaultContext($source);
}
public function getSource(): ContextSource
{
return $this->source;
}
public function getVersionId(): string
{
return $this->versionId;
}
public function getLanguageId(): string
{
return $this->languageIdChain[0];
}
public function getCurrencyId(): string
{
return $this->currencyId;
}
public function getCurrencyFactor(): float
{
return $this->currencyFactor;
}
/**
* @return array<string>
*/
public function getRuleIds(): array
{
return $this->ruleIds;
}
/**
* @return non-empty-array<string>
*/
public function getLanguageIdChain(): array
{
return $this->languageIdChain;
}
public function createWithVersionId(string $versionId): self
{
$context = new self(
$this->source,
$this->ruleIds,
$this->currencyId,
$this->languageIdChain,
$versionId,
$this->currencyFactor,
$this->considerInheritance,
$this->taxState,
$this->rounding
);
$context->scope = $this->scope;
foreach ($this->getExtensions() as $key => $extension) {
$context->addExtension($key, $extension);
}
return $context;
}
/**
* @template TReturn of mixed
*
* @param \Closure(Context): TReturn $callback
*
* @return TReturn the return value of the provided callback function
*/
public function scope(string $scope, \Closure $callback)
{
$currentScope = $this->getScope();
$this->scope = $scope;
try {
$result = $callback($this);
} finally {
$this->scope = $currentScope;
}
return $result;
}
public function getScope(): string
{
return $this->scope;
}
public function considerInheritance(): bool
{
return $this->considerInheritance;
}
/**
* @return void
*/
public function setConsiderInheritance(bool $considerInheritance)
{
$this->considerInheritance = $considerInheritance;
}
public function getTaxState(): string
{
return $this->taxState;
}
/**
* @return void
*/
public function setTaxState(string $taxState)
{
$this->taxState = $taxState;
}
/**
* @return bool
*/
public function isAllowed(string $privilege)
{
if ($this->source instanceof AdminApiSource) {
return $this->source->isAllowed($privilege);
}
return true;
}
/**
* @param array<string> $ruleIds
*/
public function setRuleIds(array $ruleIds)
{
if ($this->rulesLocked) {
throw new ContextRulesLockedException();
}
$this->ruleIds = array_filter(array_values($ruleIds));
}
/**
* @template TReturn of mixed
*
* @param \Closure(Context): TReturn $function
*
* @return TReturn
*/
public function enableInheritance(\Closure $function)
{
$previous = $this->considerInheritance;
$this->considerInheritance = true;
$result = $function($this);
$this->considerInheritance = $previous;
return $result;
}
/**
* @template TReturn of mixed
*
* @param \Closure(Context): TReturn $function
*
* @return TReturn
*/
public function disableInheritance(\Closure $function)
{
$previous = $this->considerInheritance;
$this->considerInheritance = false;
$result = $function($this);
$this->considerInheritance = $previous;
return $result;
}
public function getApiAlias(): string
{
return 'context';
}
public function getRounding(): CashRoundingConfig
{
return $this->rounding;
}
/**
* @return void
*/
public function setRounding(CashRoundingConfig $rounding)
{
$this->rounding = $rounding;
}
/**
* @return void
*/
public function lockRules()
{
$this->rulesLocked = true;
}
/**
* @return void
*/
public function addState(string ...$states)
{
foreach ($states as $state) {
$this->states[$state] = $state;
}
}
/**
* @return void
*/
public function removeState(string $state)
{
unset($this->states[$state]);
}
public function hasState(string ...$states): bool
{
foreach ($states as $state) {
if (isset($this->states[$state])) {
return true;
}
}
return false;
}
/**
* @return array<string>
*/
public function getStates(): array
{
return array_keys($this->states);
}
}