From 2ad6fd34967495dd9b5bba8c428f5e877c29085b Mon Sep 17 00:00:00 2001 From: SilverFire - Dmytro Naumenko Date: Wed, 4 Jun 2025 18:06:45 +0300 Subject: [PATCH 1/4] HP-2508 Working on typing for Psalm --- src/product/BillingRegistryInterface.php | 3 + src/product/TariffTypeDefinition.php | 9 ++- src/product/TariffTypeDefinitionInterface.php | 13 ++-- src/product/behavior/BehaviorCollection.php | 7 ++- .../behavior/BehaviorCollectionInterface.php | 8 +-- .../BehaviorPriceTypeDefinitionCollection.php | 26 +++++++- .../behavior/BehaviorTariffTypeCollection.php | 18 +++++- .../behavior/HasBehaviorsInterface.php | 9 ++- .../behavior/TariffTypeBehaviorRegistry.php | 11 ++++ .../invoice/RepresentationCollection.php | 21 +++++-- src/product/price/PriceTypeDefinition.php | 62 +++++++++++++------ .../price/PriceTypeDefinitionCollection.php | 4 +- ...PriceTypeDefinitionCollectionInterface.php | 7 +++ .../price/PriceTypeDefinitionInterface.php | 29 ++++++--- 14 files changed, 171 insertions(+), 56 deletions(-) diff --git a/src/product/BillingRegistryInterface.php b/src/product/BillingRegistryInterface.php index 38885f93..3c702405 100644 --- a/src/product/BillingRegistryInterface.php +++ b/src/product/BillingRegistryInterface.php @@ -13,6 +13,9 @@ interface BillingRegistryInterface extends HasLockInterface */ public function priceTypes(): Generator; + /** + * @return TariffTypeDefinitionInterface[] + */ public function getTariffTypeDefinitions(): array; public function addTariffType(TariffTypeDefinitionInterface $tariffTypeDefinition): void; diff --git a/src/product/TariffTypeDefinition.php b/src/product/TariffTypeDefinition.php index d4b8b33b..630160c0 100644 --- a/src/product/TariffTypeDefinition.php +++ b/src/product/TariffTypeDefinition.php @@ -39,7 +39,7 @@ public function belongToTariffType(TariffTypeInterface $tariffType): bool return $this->tariffType->equals($tariffType); } - public function ofProduct(ProductInterface $product): TariffTypeDefinitionInterface + public function ofProduct(ProductInterface $product): static { $this->ensureNotLocked(); $this->product = $product; @@ -61,7 +61,7 @@ private function ensureProductExists(): void } } - public function setPricesSuggester(string $suggesterClass): TariffTypeDefinitionInterface + public function setPricesSuggester(string $suggesterClass): static { $this->ensureNotLocked(); @@ -76,7 +76,10 @@ public function withPrices(): PriceTypeDefinitionCollection return $this->prices; } - public function withBehaviors(): BehaviorTariffTypeCollection + /** + * @return BehaviorTariffTypeCollection + */ + public function withBehaviors() { $this->ensureNotLocked(); diff --git a/src/product/TariffTypeDefinitionInterface.php b/src/product/TariffTypeDefinitionInterface.php index f52e978a..2acdd1d4 100644 --- a/src/product/TariffTypeDefinitionInterface.php +++ b/src/product/TariffTypeDefinitionInterface.php @@ -8,7 +8,7 @@ use hiqdev\php\billing\product\trait\HasLockInterface; /** - * @template T of PriceTypeDefinitionCollectionInterface + * @template-covariant T of PriceTypeDefinitionCollectionInterface */ interface TariffTypeDefinitionInterface extends HasBehaviorsInterface, HasLockInterface { @@ -22,17 +22,16 @@ public function tariffType(): TariffTypeInterface; */ public function belongToTariffType(TariffTypeInterface $tariffType): bool; - public function ofProduct(ProductInterface $product): self; + public function ofProduct(ProductInterface $product): static; public function getProduct(): ProductInterface; - public function setPricesSuggester(string $suggesterClass): self; + public function setPricesSuggester(string $suggesterClass): static; /** - * @return PriceTypeDefinitionCollectionInterface - * @psalm-return T + * @return T */ - public function withPrices(): PriceTypeDefinitionCollectionInterface; + public function withPrices(); - public function end(): self; + public function end(); } diff --git a/src/product/behavior/BehaviorCollection.php b/src/product/behavior/BehaviorCollection.php index 6c564a63..421b6ee1 100644 --- a/src/product/behavior/BehaviorCollection.php +++ b/src/product/behavior/BehaviorCollection.php @@ -5,6 +5,11 @@ use hiqdev\php\billing\product\Domain\Model\TariffTypeInterface; use hiqdev\php\billing\product\trait\HasLock; +/** + * @template TPriceDefinition + * @implements BehaviorCollectionInterface + * @psalm-consistent-templates + */ abstract class BehaviorCollection implements BehaviorCollectionInterface { use HasLock; @@ -21,7 +26,7 @@ public function getIterator(): \Traversable return new \ArrayIterator($this->behaviors); } - public function attach(BehaviorInterface $behavior): self + public function attach(BehaviorInterface $behavior): static { $this->ensureNotLocked(); diff --git a/src/product/behavior/BehaviorCollectionInterface.php b/src/product/behavior/BehaviorCollectionInterface.php index 14c9ca81..275f8812 100644 --- a/src/product/behavior/BehaviorCollectionInterface.php +++ b/src/product/behavior/BehaviorCollectionInterface.php @@ -10,6 +10,7 @@ /** * @extends IteratorAggregate + * @psalm-consistent-templates */ interface BehaviorCollectionInterface extends IteratorAggregate, HasLockInterface { @@ -18,10 +19,7 @@ interface BehaviorCollectionInterface extends IteratorAggregate, HasLockInterfac */ public function getIterator(): Traversable; - public function attach(BehaviorInterface $behavior): self; + public function attach(BehaviorInterface $behavior): static; - /** - * @return TariffTypeDefinitionInterface|PriceTypeDefinitionInterface - */ - public function end(): TariffTypeDefinitionInterface|PriceTypeDefinitionInterface; + public function end(); } diff --git a/src/product/behavior/BehaviorPriceTypeDefinitionCollection.php b/src/product/behavior/BehaviorPriceTypeDefinitionCollection.php index 9e650c3d..4745024b 100644 --- a/src/product/behavior/BehaviorPriceTypeDefinitionCollection.php +++ b/src/product/behavior/BehaviorPriceTypeDefinitionCollection.php @@ -5,14 +5,34 @@ use hiqdev\php\billing\product\Domain\Model\TariffTypeInterface; use hiqdev\php\billing\product\price\PriceTypeDefinitionInterface; +/** + * @template TPriceDefinition + * @extends BehaviorCollection + * @psalm-consistent-templates + */ class BehaviorPriceTypeDefinitionCollection extends BehaviorCollection { - public function __construct(private readonly PriceTypeDefinitionInterface $parent, TariffTypeInterface $tariffType) - { + /** + * @psalm-var TPriceDefinition + */ + private readonly PriceTypeDefinitionInterface $parent; + + /** + * @psalm-param TPriceDefinition $parent + */ + public function __construct( + PriceTypeDefinitionInterface $parent, + TariffTypeInterface $tariffType + ) { + $this->parent = $parent; + parent::__construct($tariffType); } - public function end(): PriceTypeDefinitionInterface + /** + * @return TPriceDefinition + */ + public function end() { return $this->parent; } diff --git a/src/product/behavior/BehaviorTariffTypeCollection.php b/src/product/behavior/BehaviorTariffTypeCollection.php index 905b3f06..3265cc54 100644 --- a/src/product/behavior/BehaviorTariffTypeCollection.php +++ b/src/product/behavior/BehaviorTariffTypeCollection.php @@ -5,13 +5,27 @@ use hiqdev\php\billing\product\Domain\Model\TariffTypeInterface; use hiqdev\php\billing\product\TariffTypeDefinitionInterface; +/** + * @template T + */ class BehaviorTariffTypeCollection extends BehaviorCollection { - public function __construct(private readonly TariffTypeDefinitionInterface $parent, TariffTypeInterface $tariffType) - { + /** + * @psalm-param T $parent + */ + public function __construct( + /** + * @var T + */ + private readonly TariffTypeDefinitionInterface $parent, + TariffTypeInterface $tariffType + ) { parent::__construct($tariffType); } + /** + * @psalm-return T + */ public function end(): TariffTypeDefinitionInterface { return $this->parent; diff --git a/src/product/behavior/HasBehaviorsInterface.php b/src/product/behavior/HasBehaviorsInterface.php index acf84be4..e76c057c 100644 --- a/src/product/behavior/HasBehaviorsInterface.php +++ b/src/product/behavior/HasBehaviorsInterface.php @@ -2,9 +2,16 @@ namespace hiqdev\php\billing\product\behavior; +/** + * @template TParentCollection + * @psalm-consistent-templates + */ interface HasBehaviorsInterface { - public function withBehaviors(): BehaviorCollectionInterface; +// /** +// * @return BehaviorCollectionInterface +// */ +// public function withBehaviors(); public function hasBehavior(string $behaviorClassName): bool; } diff --git a/src/product/behavior/TariffTypeBehaviorRegistry.php b/src/product/behavior/TariffTypeBehaviorRegistry.php index 8983d608..4e4edac0 100644 --- a/src/product/behavior/TariffTypeBehaviorRegistry.php +++ b/src/product/behavior/TariffTypeBehaviorRegistry.php @@ -23,16 +23,27 @@ * - To avoid code duplication of behavior-related methods in multiple classes. * - To separate concerns by handling behavior-related logic in a dedicated class. * - To improve maintainability and testability of tariff behavior handling. + * + * @template-covariant T of TariffTypeDefinitionInterface */ final class TariffTypeBehaviorRegistry implements HasLockInterface { + /** + * @var BehaviorTariffTypeCollection + */ private BehaviorTariffTypeCollection $behaviorCollection; + /** + * @psalm-param T $tariffTypeDefinition + */ public function __construct(TariffTypeDefinitionInterface $tariffTypeDefinition, TariffTypeInterface $tariffType) { $this->behaviorCollection = new BehaviorTariffTypeCollection($tariffTypeDefinition, $tariffType); } + /** + * @return BehaviorTariffTypeCollection + */ public function getBehaviors(): BehaviorTariffTypeCollection { return $this->behaviorCollection; diff --git a/src/product/invoice/RepresentationCollection.php b/src/product/invoice/RepresentationCollection.php index 81990d0c..ba90e2eb 100644 --- a/src/product/invoice/RepresentationCollection.php +++ b/src/product/invoice/RepresentationCollection.php @@ -9,8 +9,9 @@ use Traversable; /** - * @template T of PriceTypeDefinition + * @template T * @implements IteratorAggregate + * @psalm-consistent-templates */ class RepresentationCollection implements IteratorAggregate, HasLockInterface { @@ -20,8 +21,18 @@ class RepresentationCollection implements IteratorAggregate, HasLockInterface private RepresentationUniquenessGuard $uniquenessGuard; - public function __construct(private readonly PriceTypeDefinition $priceTypeDefinition) - { + /** + * @psalm-var T + */ + private readonly PriceTypeDefinition $priceTypeDefinition; + + /** + * @psalm-param T $priceTypeDefinition + */ + public function __construct( + PriceTypeDefinition $priceTypeDefinition, + ) { + $this->priceTypeDefinition = $priceTypeDefinition; $this->uniquenessGuard = new RepresentationUniquenessGuard(); } @@ -33,7 +44,7 @@ public function getIterator(): Traversable return new \ArrayIterator($this->representations); } - public function attach(RepresentationInterface $representation): self + public function attach(RepresentationInterface $representation): static { $this->ensureNotLocked(); @@ -49,7 +60,7 @@ public function attach(RepresentationInterface $representation): self /** * @psalm-return T */ - public function end(): PriceTypeDefinition + public function end() { return $this->priceTypeDefinition; } diff --git a/src/product/price/PriceTypeDefinition.php b/src/product/price/PriceTypeDefinition.php index ed6b97ac..0a662f51 100644 --- a/src/product/price/PriceTypeDefinition.php +++ b/src/product/price/PriceTypeDefinition.php @@ -1,8 +1,11 @@ - + * @implements HasBehaviorsInterface * @psalm-consistent-templates */ class PriceTypeDefinition implements PriceTypeDefinitionInterface @@ -31,22 +36,32 @@ class PriceTypeDefinition implements PriceTypeDefinitionInterface private string $description; - private QuantityFormatterDefinition $quantityFormatterDefinition; + private ?QuantityFormatterDefinition $quantityFormatterDefinition = null; + /** + * @var RepresentationCollection + */ private RepresentationCollection $representationCollection; + /** + * @var BehaviorPriceTypeDefinitionCollection + */ private BehaviorPriceTypeDefinitionCollection $behaviorCollection; private ?AggregateInterface $aggregate = null; + /** @psalm-var TParentCollection */ + private readonly PriceTypeDefinitionCollectionInterface $parent; + + /** + * @param TParentCollection $parent + */ public function __construct( - /** - * @psalm-var T - */ - private readonly PriceTypeDefinitionCollectionInterface $parent, + PriceTypeDefinitionCollectionInterface $parent, private readonly TypeInterface $type, TariffTypeInterface $tariffType, ) { + $this->parent = $parent; $this->representationCollection = new RepresentationCollection($this); $this->behaviorCollection = new BehaviorPriceTypeDefinitionCollection($this, $tariffType); @@ -58,7 +73,7 @@ protected function init(): void // Hook } - public function unit(UnitInterface $unit): self + public function unit(UnitInterface $unit): static { $this->ensureNotLocked(); @@ -67,7 +82,7 @@ public function unit(UnitInterface $unit): self return $this; } - public function description(string $description): self + public function description(string $description): static { $this->ensureNotLocked(); @@ -82,12 +97,10 @@ public function getDescription(): string } /** - * @param string $formatterClass * @param null|FractionUnitInterface|string $fractionUnit - * @return $this * @throws InvalidQuantityFormatterException */ - public function quantityFormatter(string $formatterClass, $fractionUnit = null): self + public function quantityFormatter(string $formatterClass, $fractionUnit = null): static { $this->ensureNotLocked(); @@ -112,25 +125,25 @@ public function createQuantityFormatter(FractionQuantityData $data): QuantityFor } /** - * @psalm-return T + * @return TParentCollection */ - public function end(): PriceTypeDefinitionCollectionInterface + public function end() { // Validate the PriceType return $this->parent; } /** - * @psalm-return RepresentationCollection + * @return RepresentationCollection */ - public function documentRepresentation(): RepresentationCollection + public function documentRepresentation() { $this->ensureNotLocked(); return $this->representationCollection; } - public function measuredWith(TrafCollectorInterface $collector): self + public function measuredWith(TrafCollectorInterface $collector): static { $this->ensureNotLocked(); @@ -154,7 +167,10 @@ public function getUnit(): UnitInterface return $this->unit; } - public function withBehaviors(): BehaviorPriceTypeDefinitionCollection + /** + * @return BehaviorPriceTypeDefinitionCollection + */ + public function withBehaviors() { $this->ensureNotLocked(); @@ -175,7 +191,7 @@ public function hasBehavior(string $behaviorClassName): bool /** * @inerhitDoc */ - public function aggregation(AggregateInterface $aggregate): self + public function aggregation(AggregateInterface $aggregate): static { $this->ensureNotLocked(); @@ -197,6 +213,14 @@ public function getAggregate(): AggregateInterface return $this->aggregate; } + /** + * @internal + */ + public function getQuantityFormatterDefinition(): ?QuantityFormatterDefinition + { + return $this->quantityFormatterDefinition; + } + protected function afterLock(): void { $this->representationCollection->lock(); diff --git a/src/product/price/PriceTypeDefinitionCollection.php b/src/product/price/PriceTypeDefinitionCollection.php index a978c2a9..a7e8db83 100644 --- a/src/product/price/PriceTypeDefinitionCollection.php +++ b/src/product/price/PriceTypeDefinitionCollection.php @@ -7,8 +7,8 @@ use hiqdev\php\billing\type\TypeInterface; /** - * @template T of PriceTypeDefinitionCollectionInterface - * @template M of TariffTypeDefinitionInterface + * @template-covariant T of PriceTypeDefinitionCollectionInterface + * @template-covariant M of TariffTypeDefinitionInterface * @mixin T */ class PriceTypeDefinitionCollection implements PriceTypeDefinitionCollectionInterface diff --git a/src/product/price/PriceTypeDefinitionCollectionInterface.php b/src/product/price/PriceTypeDefinitionCollectionInterface.php index 637fbfc8..846a65f1 100644 --- a/src/product/price/PriceTypeDefinitionCollectionInterface.php +++ b/src/product/price/PriceTypeDefinitionCollectionInterface.php @@ -2,6 +2,7 @@ namespace hiqdev\php\billing\product\price; +use hiqdev\php\billing\product\behavior\HasBehaviorsInterface; use hiqdev\php\billing\product\TariffTypeDefinitionInterface; use hiqdev\php\billing\product\trait\HasLockInterface; use hiqdev\php\billing\type\TypeInterface; @@ -10,6 +11,9 @@ /** * @extends IteratorAggregate + * @template-covariant TPriceTypeDefinition of PriceTypeDefinitionInterface + * @implements HasBehaviorsInterface + * @psalm-consistent-templates */ interface PriceTypeDefinitionCollectionInterface extends IteratorAggregate, \Countable, HasLockInterface { @@ -18,6 +22,9 @@ interface PriceTypeDefinitionCollectionInterface extends IteratorAggregate, \Cou */ public function getIterator(): Traversable; + /** + * @psalm-return PriceTypeDefinitionInterface + */ public function priceType(TypeInterface $type): PriceTypeDefinitionInterface; /** diff --git a/src/product/price/PriceTypeDefinitionInterface.php b/src/product/price/PriceTypeDefinitionInterface.php index 860031ce..5af86bcf 100644 --- a/src/product/price/PriceTypeDefinitionInterface.php +++ b/src/product/price/PriceTypeDefinitionInterface.php @@ -8,28 +8,39 @@ use hiqdev\php\billing\product\invoice\RepresentationCollection; use hiqdev\php\billing\product\measure\TrafCollectorInterface; use hiqdev\php\billing\product\quantity\FractionQuantityData; +use hiqdev\php\billing\product\quantity\QuantityFormatterDefinition; use hiqdev\php\billing\product\quantity\QuantityFormatterInterface; use hiqdev\php\billing\product\TariffTypeDefinitionInterface; use hiqdev\php\billing\product\trait\HasLockInterface; use hiqdev\php\billing\type\TypeInterface; +/** + * @template TParentCollection + * @psalm-consistent-templates + */ interface PriceTypeDefinitionInterface extends HasBehaviorsInterface, HasLockInterface { - public function unit(UnitInterface $unit): self; + public function unit(UnitInterface $unit): static; - public function description(string $description): self; + public function description(string $description): static; public function getDescription(): string; - public function quantityFormatter(string $formatterClass, $fractionUnit = null): self; + public function quantityFormatter(string $formatterClass, $fractionUnit = null): static; public function createQuantityFormatter(FractionQuantityData $data): QuantityFormatterInterface; - public function end(): PriceTypeDefinitionCollectionInterface; + /** + * @psalm-return TParentCollection + */ + public function end(); - public function documentRepresentation(): RepresentationCollection; + /** + * @return RepresentationCollection + */ + public function documentRepresentation(); - public function measuredWith(TrafCollectorInterface $collector): self; + public function measuredWith(TrafCollectorInterface $collector): static; public function type(): TypeInterface; @@ -42,9 +53,9 @@ public function getUnit(): UnitInterface; * місячне споживання за яке потрібно пробілити клієнта * * @param AggregateInterface $aggregate - * @return self + * @return static */ - public function aggregation(AggregateInterface $aggregate): self; + public function aggregation(AggregateInterface $aggregate): static; public function getAggregate(): AggregateInterface; @@ -56,4 +67,6 @@ public function getAggregate(): AggregateInterface; public function getTariffTypeDefinition(): TariffTypeDefinitionInterface; public function belongsToTariffType(string $tariffTypeName): bool; + + public function getQuantityFormatterDefinition(): ?QuantityFormatterDefinition; } From 51a92244def8463327fff9d9b76f114d2c5851d5 Mon Sep 17 00:00:00 2001 From: SilverFire - Dmytro Naumenko Date: Thu, 5 Jun 2025 15:09:22 +0300 Subject: [PATCH 2/4] Fix Psalm where possible, suppress else --- src/product/TariffTypeDefinition.php | 20 ++++++++++++++++--- src/product/TariffTypeDefinitionInterface.php | 7 +++++-- src/product/behavior/BehaviorCollection.php | 6 ++++-- .../behavior/BehaviorCollectionInterface.php | 4 ++++ .../BehaviorPriceTypeDefinitionCollection.php | 1 + .../behavior/BehaviorTariffTypeCollection.php | 3 +++ .../behavior/HasBehaviorsInterface.php | 14 ++++++++----- src/product/price/PriceTypeDefinition.php | 15 ++++++++++++-- .../price/PriceTypeDefinitionCollection.php | 19 +++++++++++++++--- ...PriceTypeDefinitionCollectionInterface.php | 9 ++++----- .../price/PriceTypeDefinitionInterface.php | 8 ++++++-- src/product/price/PriceTypeStorage.php | 7 ++++++- .../quantity/QuantityFormatterDefinition.php | 8 ++++++++ 13 files changed, 96 insertions(+), 25 deletions(-) diff --git a/src/product/TariffTypeDefinition.php b/src/product/TariffTypeDefinition.php index 630160c0..01e48cb1 100644 --- a/src/product/TariffTypeDefinition.php +++ b/src/product/TariffTypeDefinition.php @@ -2,16 +2,22 @@ namespace hiqdev\php\billing\product; +use Google\Service\TPU; +use hiqdev\php\billing\product\behavior\BehaviorCollectionInterface; use hiqdev\php\billing\product\behavior\BehaviorTariffTypeCollection; use hiqdev\php\billing\product\behavior\TariffTypeBehaviorRegistry; use hiqdev\php\billing\product\Domain\Model\TariffTypeInterface; use hiqdev\php\billing\product\Exception\ProductNotDefinedException; +use hiqdev\php\billing\product\price\PriceTypeDefinition; use hiqdev\php\billing\product\price\PriceTypeDefinitionCollection; +use hiqdev\php\billing\product\price\PriceTypeDefinitionCollectionInterface; use hiqdev\php\billing\product\price\PriceTypeDefinitionFactory; use hiqdev\php\billing\product\trait\HasLock; /** - * @implements TariffTypeDefinitionInterface + * @template TPriceTypeDefinitionCollection of PriceTypeDefinitionCollection + * @implements TariffTypeDefinitionInterface + * @psalm-suppress InvalidTemplateParam */ class TariffTypeDefinition implements TariffTypeDefinitionInterface { @@ -51,6 +57,7 @@ public function getProduct(): ProductInterface { $this->ensureProductExists(); + /** @psalm-suppress NullableReturnStatement */ return $this->product; } @@ -69,7 +76,11 @@ public function setPricesSuggester(string $suggesterClass): static return $this; } - public function withPrices(): PriceTypeDefinitionCollection + /** + * @psalm-suppress InvalidReturnType + * @psalm-suppress InvalidReturnStatement + */ + public function withPrices() { $this->ensureNotLocked(); @@ -77,7 +88,10 @@ public function withPrices(): PriceTypeDefinitionCollection } /** - * @return BehaviorTariffTypeCollection + * @return BehaviorCollectionInterface + * @psalm-suppress ImplementedReturnTypeMismatch + * @psalm-suppress InvalidReturnType + * @psalm-suppress InvalidReturnStatement */ public function withBehaviors() { diff --git a/src/product/TariffTypeDefinitionInterface.php b/src/product/TariffTypeDefinitionInterface.php index 2acdd1d4..f6e7a847 100644 --- a/src/product/TariffTypeDefinitionInterface.php +++ b/src/product/TariffTypeDefinitionInterface.php @@ -8,7 +8,10 @@ use hiqdev\php\billing\product\trait\HasLockInterface; /** - * @template-covariant T of PriceTypeDefinitionCollectionInterface + * @template-covariant TPriceTypeDefinitionCollection of PriceTypeDefinitionCollectionInterface + * @extends HasBehaviorsInterface + * @psalm-consistent-templates + * @psalm-suppress InvalidTemplateParam */ interface TariffTypeDefinitionInterface extends HasBehaviorsInterface, HasLockInterface { @@ -29,7 +32,7 @@ public function getProduct(): ProductInterface; public function setPricesSuggester(string $suggesterClass): static; /** - * @return T + * @return TPriceTypeDefinitionCollection */ public function withPrices(); diff --git a/src/product/behavior/BehaviorCollection.php b/src/product/behavior/BehaviorCollection.php index 421b6ee1..1fe50919 100644 --- a/src/product/behavior/BehaviorCollection.php +++ b/src/product/behavior/BehaviorCollection.php @@ -3,11 +3,13 @@ namespace hiqdev\php\billing\product\behavior; use hiqdev\php\billing\product\Domain\Model\TariffTypeInterface; +use hiqdev\php\billing\product\price\PriceTypeDefinitionInterface; +use hiqdev\php\billing\product\TariffTypeDefinitionInterface; use hiqdev\php\billing\product\trait\HasLock; /** - * @template TPriceDefinition - * @implements BehaviorCollectionInterface + * @template-covariant TParentContext of TariffTypeDefinitionInterface|PriceTypeDefinitionInterface + * @implements BehaviorCollectionInterface * @psalm-consistent-templates */ abstract class BehaviorCollection implements BehaviorCollectionInterface diff --git a/src/product/behavior/BehaviorCollectionInterface.php b/src/product/behavior/BehaviorCollectionInterface.php index 275f8812..9e822ee7 100644 --- a/src/product/behavior/BehaviorCollectionInterface.php +++ b/src/product/behavior/BehaviorCollectionInterface.php @@ -9,6 +9,7 @@ use Traversable; /** + * @template-covariant TParentContext of TariffTypeDefinitionInterface|PriceTypeDefinitionInterface * @extends IteratorAggregate * @psalm-consistent-templates */ @@ -21,5 +22,8 @@ public function getIterator(): Traversable; public function attach(BehaviorInterface $behavior): static; + /** + * @return TParentContext + */ public function end(); } diff --git a/src/product/behavior/BehaviorPriceTypeDefinitionCollection.php b/src/product/behavior/BehaviorPriceTypeDefinitionCollection.php index 4745024b..780b2a6c 100644 --- a/src/product/behavior/BehaviorPriceTypeDefinitionCollection.php +++ b/src/product/behavior/BehaviorPriceTypeDefinitionCollection.php @@ -9,6 +9,7 @@ * @template TPriceDefinition * @extends BehaviorCollection * @psalm-consistent-templates + * @psalm-suppress InvalidTemplateParam */ class BehaviorPriceTypeDefinitionCollection extends BehaviorCollection { diff --git a/src/product/behavior/BehaviorTariffTypeCollection.php b/src/product/behavior/BehaviorTariffTypeCollection.php index 3265cc54..b56da3c9 100644 --- a/src/product/behavior/BehaviorTariffTypeCollection.php +++ b/src/product/behavior/BehaviorTariffTypeCollection.php @@ -7,6 +7,8 @@ /** * @template T + * @psalm-suppress MissingTemplateParam + * @psalm-suppress InvalidTemplateParam */ class BehaviorTariffTypeCollection extends BehaviorCollection { @@ -25,6 +27,7 @@ public function __construct( /** * @psalm-return T + * @psalm-suppress LessSpecificImplementedReturnType */ public function end(): TariffTypeDefinitionInterface { diff --git a/src/product/behavior/HasBehaviorsInterface.php b/src/product/behavior/HasBehaviorsInterface.php index e76c057c..3b9ddd08 100644 --- a/src/product/behavior/HasBehaviorsInterface.php +++ b/src/product/behavior/HasBehaviorsInterface.php @@ -2,16 +2,20 @@ namespace hiqdev\php\billing\product\behavior; +use hiqdev\php\billing\product\invoice\RepresentationCollection; +use hiqdev\php\billing\product\price\PriceTypeDefinitionInterface; +use hiqdev\php\billing\product\TariffTypeDefinitionInterface; + /** - * @template TParentCollection + * @template TParentCollection of PriceTypeDefinitionInterface|TariffTypeDefinitionInterface * @psalm-consistent-templates */ interface HasBehaviorsInterface { -// /** -// * @return BehaviorCollectionInterface -// */ -// public function withBehaviors(); + /** + * @return BehaviorCollectionInterface + */ + public function withBehaviors(); public function hasBehavior(string $behaviorClassName): bool; } diff --git a/src/product/price/PriceTypeDefinition.php b/src/product/price/PriceTypeDefinition.php index 0a662f51..bd2c9598 100644 --- a/src/product/price/PriceTypeDefinition.php +++ b/src/product/price/PriceTypeDefinition.php @@ -24,9 +24,11 @@ /** * @template TParentCollection - * @implements PriceTypeDefinitionInterface - * @implements HasBehaviorsInterface + * @implements PriceTypeDefinitionInterface + * @implements HasBehaviorsInterface * @psalm-consistent-templates + * @psalm-suppress InvalidTemplateParam + * @psalm-suppress MissingTemplateParam */ class PriceTypeDefinition implements PriceTypeDefinitionInterface { @@ -113,6 +115,9 @@ public function quantityFormatter(string $formatterClass, $fractionUnit = null): return $this; } + /** + * @psalm-suppress PossiblyNullArgument + */ public function createQuantityFormatter(FractionQuantityData $data): QuantityFormatterInterface { $this->ensureNotLocked(); @@ -135,6 +140,9 @@ public function end() /** * @return RepresentationCollection + * @psalm-suppress MoreSpecificReturnType + * @psalm-suppress LessSpecificReturnStatement + * @psalm-suppress LessSpecificImplementedReturnType */ public function documentRepresentation() { @@ -169,6 +177,9 @@ public function getUnit(): UnitInterface /** * @return BehaviorPriceTypeDefinitionCollection + * @psalm-suppress ImplementedReturnTypeMismatch + * @psalm-suppress MoreSpecificReturnType + * @psalm-suppress LessSpecificReturnStatement */ public function withBehaviors() { diff --git a/src/product/price/PriceTypeDefinitionCollection.php b/src/product/price/PriceTypeDefinitionCollection.php index a7e8db83..b0a45693 100644 --- a/src/product/price/PriceTypeDefinitionCollection.php +++ b/src/product/price/PriceTypeDefinitionCollection.php @@ -7,9 +7,12 @@ use hiqdev\php\billing\type\TypeInterface; /** - * @template-covariant T of PriceTypeDefinitionCollectionInterface - * @template-covariant M of TariffTypeDefinitionInterface - * @mixin T + * @template TTariffTypeDefinition of TariffTypeDefinitionInterface + * @template TPriceTypeDefinition of PriceTypeDefinitionCollectionInterface + * @implements PriceTypeDefinitionCollectionInterface + * @mixin TPriceTypeDefinition + * @psalm-suppress TooManyTemplateParams + * @psalm-suppress InvalidTemplateParam */ class PriceTypeDefinitionCollection implements PriceTypeDefinitionCollectionInterface { @@ -19,7 +22,13 @@ class PriceTypeDefinitionCollection implements PriceTypeDefinitionCollectionInte private PriceTypeDefinitionCollectionInterface $collectionInstance; + /** + * @psalm-param TTariffTypeDefinition $parent + */ public function __construct( + /** + * @psalm-var TTariffTypeDefinition + */ private readonly TariffTypeDefinitionInterface $parent, private readonly PriceTypeDefinitionFactoryInterface $factory, PriceTypeDefinitionCollectionInterface $collectionInstance = null, @@ -36,6 +45,10 @@ public function getIterator(): \Traversable return new \ArrayIterator($this->storage->getAll()); } + /** + * @psalm-suppress InvalidReturnType + * @psalm-suppress InvalidReturnStatement + */ public function priceType(TypeInterface $type): PriceTypeDefinitionInterface { $this->ensureNotLocked(); diff --git a/src/product/price/PriceTypeDefinitionCollectionInterface.php b/src/product/price/PriceTypeDefinitionCollectionInterface.php index 846a65f1..d800a15e 100644 --- a/src/product/price/PriceTypeDefinitionCollectionInterface.php +++ b/src/product/price/PriceTypeDefinitionCollectionInterface.php @@ -2,7 +2,7 @@ namespace hiqdev\php\billing\product\price; -use hiqdev\php\billing\product\behavior\HasBehaviorsInterface; +use Countable; use hiqdev\php\billing\product\TariffTypeDefinitionInterface; use hiqdev\php\billing\product\trait\HasLockInterface; use hiqdev\php\billing\type\TypeInterface; @@ -10,12 +10,11 @@ use Traversable; /** + * @template TPriceTypeDefinition of PriceTypeDefinitionInterface * @extends IteratorAggregate - * @template-covariant TPriceTypeDefinition of PriceTypeDefinitionInterface - * @implements HasBehaviorsInterface * @psalm-consistent-templates */ -interface PriceTypeDefinitionCollectionInterface extends IteratorAggregate, \Countable, HasLockInterface +interface PriceTypeDefinitionCollectionInterface extends IteratorAggregate, Countable, HasLockInterface { /** * @return Traversable @@ -23,7 +22,7 @@ interface PriceTypeDefinitionCollectionInterface extends IteratorAggregate, \Cou public function getIterator(): Traversable; /** - * @psalm-return PriceTypeDefinitionInterface + * @psalm-return PriceTypeDefinitionInterface */ public function priceType(TypeInterface $type): PriceTypeDefinitionInterface; diff --git a/src/product/price/PriceTypeDefinitionInterface.php b/src/product/price/PriceTypeDefinitionInterface.php index 5af86bcf..2098f833 100644 --- a/src/product/price/PriceTypeDefinitionInterface.php +++ b/src/product/price/PriceTypeDefinitionInterface.php @@ -15,8 +15,12 @@ use hiqdev\php\billing\type\TypeInterface; /** - * @template TParentCollection + * @template TParentCollection of PriceTypeDefinitionCollectionInterface + * @template TPriceTypeDefinition of PriceTypeDefinitionInterface + * @extends HasBehaviorsInterface * @psalm-consistent-templates + * @psalm-suppress TooManyTemplateParams + * @psalm-suppress InvalidTemplateParam */ interface PriceTypeDefinitionInterface extends HasBehaviorsInterface, HasLockInterface { @@ -36,7 +40,7 @@ public function createQuantityFormatter(FractionQuantityData $data): QuantityFor public function end(); /** - * @return RepresentationCollection + * @return RepresentationCollection */ public function documentRepresentation(); diff --git a/src/product/price/PriceTypeStorage.php b/src/product/price/PriceTypeStorage.php index 02e95ebe..db03921d 100644 --- a/src/product/price/PriceTypeStorage.php +++ b/src/product/price/PriceTypeStorage.php @@ -3,6 +3,7 @@ namespace hiqdev\php\billing\product\price; use hiqdev\php\billing\type\TypeInterface; +use InvalidArgumentException; class PriceTypeStorage implements \Countable { @@ -12,8 +13,12 @@ class PriceTypeStorage implements \Countable public function add(TypeInterface $type, PriceTypeDefinitionInterface $priceTypeDefinition): void { - $this->pricesGroupedByPriceType[$type->getName()][] = $priceTypeDefinition; + $typeName = $type->getName(); + if ($typeName === null) { + throw new InvalidArgumentException('Price type name must not be null'); + } + $this->pricesGroupedByPriceType[$typeName][] = $priceTypeDefinition; $this->i++; } diff --git a/src/product/quantity/QuantityFormatterDefinition.php b/src/product/quantity/QuantityFormatterDefinition.php index 45c6fca2..15da673f 100644 --- a/src/product/quantity/QuantityFormatterDefinition.php +++ b/src/product/quantity/QuantityFormatterDefinition.php @@ -6,17 +6,25 @@ class QuantityFormatterDefinition { + /** @var class-string */ private string $formatterClass; /** @var FractionUnitInterface|null|string */ private $fractionUnit; + /** + * @param class-string $formatterClass + * @param FractionUnitInterface|string|null $fractionUnit + */ public function __construct(string $formatterClass, $fractionUnit = null) { $this->formatterClass = $formatterClass; $this->fractionUnit = $fractionUnit; } + /** + * @return class-string + */ public function formatterClass(): string { return $this->formatterClass; From 86d0ded2014d514e8dd2e5f007bda09ea958ed03 Mon Sep 17 00:00:00 2001 From: SilverFire - Dmytro Naumenko Date: Thu, 5 Jun 2025 15:22:42 +0300 Subject: [PATCH 3/4] Remove unused suppress statements --- src/product/price/PriceTypeDefinition.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/product/price/PriceTypeDefinition.php b/src/product/price/PriceTypeDefinition.php index bd2c9598..0767d2ef 100644 --- a/src/product/price/PriceTypeDefinition.php +++ b/src/product/price/PriceTypeDefinition.php @@ -140,8 +140,6 @@ public function end() /** * @return RepresentationCollection - * @psalm-suppress MoreSpecificReturnType - * @psalm-suppress LessSpecificReturnStatement * @psalm-suppress LessSpecificImplementedReturnType */ public function documentRepresentation() @@ -177,9 +175,6 @@ public function getUnit(): UnitInterface /** * @return BehaviorPriceTypeDefinitionCollection - * @psalm-suppress ImplementedReturnTypeMismatch - * @psalm-suppress MoreSpecificReturnType - * @psalm-suppress LessSpecificReturnStatement */ public function withBehaviors() { From b9689e4e6236807f25ffb03dd9a9ebb2c78ee83e Mon Sep 17 00:00:00 2001 From: SilverFire - Dmytro Naumenko Date: Thu, 5 Jun 2025 15:54:45 +0300 Subject: [PATCH 4/4] Fix PHPUnit test case --- tests/unit/price/ProgressivePriceTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/price/ProgressivePriceTest.php b/tests/unit/price/ProgressivePriceTest.php index a7ee9250..3a3da635 100644 --- a/tests/unit/price/ProgressivePriceTest.php +++ b/tests/unit/price/ProgressivePriceTest.php @@ -232,7 +232,7 @@ public function testProgressivePriceSmallUsage( ); $this->usage = Quantity::bps(6043); $usage = $price->calculateUsage($this->usage); - $this->assertSame($this->usage->getQuantity(), $usage->getQuantity()); + $this->assertTrue($this->usage->equals($usage)); $amount = $price->calculateSum($this->usage); $this->assertEquals($expectedAmount, $amount->getAmount());