Skip to content

HP-2496: Extend TariffTypeDefinition with findBehaviorByClass method#104

Merged
VadymHrechukha merged 11 commits intohiqdev:masterfrom
VadymHrechukha:HP-2496_extend_tarifftypedefinition_with_getbehavior_method
Aug 11, 2025
Merged

HP-2496: Extend TariffTypeDefinition with findBehaviorByClass method#104
VadymHrechukha merged 11 commits intohiqdev:masterfrom
VadymHrechukha:HP-2496_extend_tarifftypedefinition_with_getbehavior_method

Conversation

@VadymHrechukha
Copy link
Copy Markdown
Collaborator

@VadymHrechukha VadymHrechukha commented Aug 10, 2025

Summary by CodeRabbit

  • Refactor

    • Centralized and simplified behavior management by introducing registry components and unifying how behaviors are accessed.
    • Removed an obsolete price-type lookup API and adjusted behavior lookup signatures for clearer type handling.
  • Tests

    • Updated unit tests to match the new registry-based behavior access and renamed accessors.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Aug 10, 2025

Walkthrough

Introduces an abstract BehaviorRegistry and concrete PriceTypeBehaviorRegistry, refactors TariffTypeBehaviorRegistry to extend BehaviorRegistry and renames getBehaviors()→withBehaviors(), removes TariffTypeDefinition::findPricesByTypeName, adjusts findBehaviorByClass typing, updates usages and tests, and cleans imports/exceptions.

Changes

Cohort / File(s) Change Summary
Tariff type definition cleanup
src/product/TariffTypeDefinition.php, src/product/TariffTypeDefinitionInterface.php
Removed findPricesByTypeName; updated withBehaviors() to call registry->withBehaviors(); removed nullable return type from findBehaviorByClass(); cleaned imports and exception usage.
Behavior registry abstraction & interface
src/product/behavior/BehaviorRegistry.php, src/product/behavior/HasBehaviorsInterface.php
Added abstract BehaviorRegistry implementing lookup/lock helpers (hasBehavior, findBehaviorByClass, lock); updated HasBehaviorsInterface::findBehaviorByClass() to use PHPDoc generics and removed explicit return type.
TariffTypeBehaviorRegistry refactor + tests
src/product/behavior/TariffTypeBehaviorRegistry.php, tests/unit/product/behavior/TariffTypeBehaviorRegistryTest.php
TariffTypeBehaviorRegistry now extends BehaviorRegistry; renamed getBehaviors()withBehaviors(); removed public lookup/lock methods; added protected getBehaviorCollection(); tests updated to use withBehaviors().
Price type behavior registry introduction & usage
src/product/price/PriceTypeDefinition.php, src/product/behavior/PriceTypeBehaviorRegistry.php
Replaced inline behavior collection with PriceTypeBehaviorRegistry; delegated withBehaviors(), hasBehavior(), findBehaviorByClass(), and locking to the registry; added new PriceTypeBehaviorRegistry class.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant TariffTypeDefinition
    participant TariffTypeBehaviorRegistry
    participant BehaviorCollection

    Client->>TariffTypeDefinition: withBehaviors()
    TariffTypeDefinition->>TariffTypeBehaviorRegistry: withBehaviors()
    TariffTypeBehaviorRegistry->>BehaviorCollection: return collection
    TariffTypeBehaviorRegistry-->>TariffTypeDefinition: BehaviorCollection
    TariffTypeDefinition-->>Client: BehaviorCollection
Loading
sequenceDiagram
    participant Client
    participant PriceTypeDefinition
    participant PriceTypeBehaviorRegistry
    participant BehaviorRegistry

    Client->>PriceTypeDefinition: hasBehavior(className) / findBehaviorByClass(class)
    PriceTypeDefinition->>PriceTypeBehaviorRegistry: hasBehavior / findBehaviorByClass
    PriceTypeBehaviorRegistry->>BehaviorRegistry: withBehaviors() / getBehaviorCollection()
    BehaviorRegistry-->>PriceTypeBehaviorRegistry: result / behavior|null
    PriceTypeBehaviorRegistry-->>PriceTypeDefinition: result / behavior|null
    PriceTypeDefinition-->>Client: result / behavior|null
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • SilverFire

Poem

"I twitch my whiskers at methods old and worn,
Plant registries where tangled code was born.
Hops of logic, neat and spry,
New behaviors hop beneath the sky.
🐇🌱"


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c4cb9df and 9f10707.

📒 Files selected for processing (1)
  • src/product/behavior/BehaviorRegistry.php (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/product/behavior/BehaviorRegistry.php
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (10)
tests/unit/product/behavior/TariffTypeBehaviorRegistryTest.php (1)

35-36: Augment test coverage for findBehaviorByClass

After attaching the behavior, also assert the registry can retrieve it by class. This exercises the new delegation path.

You can extend the test like this:

 $behavior = $this->createMock(TestBehavior::class);
 $behaviorCollection = $this->manager->withBehaviors();
 $behaviorCollection->attach($behavior);

 $this->assertTrue($this->manager->hasBehavior(TestBehavior::class));
+ $this->assertSame($behavior, $this->manager->findBehaviorByClass(TestBehavior::class));
src/product/behavior/HasBehaviorsInterface.php (1)

21-21: Specify intent and improve static analysis on findBehaviorByClass

Removing the return type is acceptable, but we should document the expected types and keep parameter naming consistent with hasBehavior.

Apply:

-    public function findBehaviorByClass(string $class);
+    /**
+     * @psalm-param class-string<BehaviorInterface> $behaviorClassName
+     * @psalm-return BehaviorInterface|null
+     */
+    public function findBehaviorByClass(string $behaviorClassName);

This preserves runtime flexibility while giving Psalm/IDE precise info.

src/product/TariffTypeDefinition.php (1)

105-108: Add docblock and align parameter name for findBehaviorByClass

Mirror HasBehaviorsInterface docs and naming for consistency and better tooling.

-    public function findBehaviorByClass(string $class)
+    /**
+     * @psalm-param class-string<\hiqdev\php\billing\product\behavior\BehaviorInterface> $behaviorClassName
+     * @psalm-return \hiqdev\php\billing\product\behavior\BehaviorInterface|null
+     */
+    public function findBehaviorByClass(string $behaviorClassName)
     {
-        return $this->tariffTypeBehaviorRegistry->findBehaviorByClass($class);
+        return $this->tariffTypeBehaviorRegistry->findBehaviorByClass($behaviorClassName);
     }
src/product/behavior/PriceTypeBehaviourRegistry.php (2)

21-26: Parameter naming clarity: this is a PriceTypeDefinition, not TariffTypeDefinition

The parameter is misnamed; fix the name and psalm annotation to avoid confusion.

-    /**
-     * @psalm-param T $tariffTypeDefinition
-     */
-    public function __construct(PriceTypeDefinitionInterface $tariffTypeDefinition, TariffTypeInterface $tariffType)
+    /**
+     * @psalm-param T $priceTypeDefinition
+     */
+    public function __construct(PriceTypeDefinitionInterface $priceTypeDefinition, TariffTypeInterface $tariffType)
     {
-        $this->behaviorCollection = new BehaviorPriceTypeDefinitionCollection($tariffTypeDefinition, $tariffType);
+        $this->behaviorCollection = new BehaviorPriceTypeDefinitionCollection($priceTypeDefinition, $tariffType);
     }

18-18: Consider marking the collection property readonly

The collection reference itself is immutable after construction. Declaring it readonly communicates intent and prevents accidental reassignment.

-    private BehaviorPriceTypeDefinitionCollection $behaviorCollection;
+    private readonly BehaviorPriceTypeDefinitionCollection $behaviorCollection;
src/product/behavior/BehaviorRegistry.php (2)

13-22: Document expected input types for better static guarantees

Clarify that we expect a behavior class-string and that the return is a BehaviorInterface|null.

-    public function hasBehavior(string $behaviorClassName): bool
+    /**
+     * @psalm-param class-string<BehaviorInterface> $behaviorClassName
+     */
+    public function hasBehavior(string $behaviorClassName): bool

24-33: Use a single accessor consistently and document types

Use getBehaviorCollection() here as well for consistency with hasBehavior() and lock(). Add psalm annotations to aid static analysis.

-    public function findBehaviorByClass(string $class)
+    /**
+     * @psalm-param class-string<BehaviorInterface> $class
+     * @psalm-return BehaviorInterface|null
+     */
+    public function findBehaviorByClass(string $class)
     {
-        foreach ($this->withBehaviors() as $behavior) {
+        foreach ($this->getBehaviorCollection() as $behavior) {
             if ($behavior instanceof $class) {
                 return $behavior;
             }
         }
 
         return null;
     }
src/product/behavior/TariffTypeBehaviorRegistry.php (1)

17-21: Docblock references stale method name (getBehaviors -> withBehaviors)

Update the documentation to reflect the new API and that hasBehavior()/findBehaviorByClass() are inherited from BehaviorRegistry.

- *  - Provides access to the behavior collection via getBehaviors().
- *  - Allows checking for the existence of a specific behavior using hasBehavior().
+ *  - Provides access to the behavior collection via withBehaviors().
+ *  - Allows behavior lookup via hasBehavior()/findBehaviorByClass() (inherited from BehaviorRegistry).
src/product/price/PriceTypeDefinition.php (2)

195-195: Typo in annotation: use @inheritdoc

Fix minor typo to ensure tooling recognizes inheritance documentation.

-     * @inerhitDoc
+     * @inheritdoc

184-192: Strengthen type hints via PHPDoc (class-string) for behavior lookups

Document parameters as class-string to aid static analysis and improve API clarity.

-    public function hasBehavior(string $behaviorClassName): bool
+    /**
+     * @param class-string $behaviorClassName
+     */
+    public function hasBehavior(string $behaviorClassName): bool
     {
         return $this->behaviorRegistry->hasBehavior($behaviorClassName);
     }

-    public function findBehaviorByClass(string $class)
+    /**
+     * @param class-string $class
+     * @return mixed|null
+     */
+    public function findBehaviorByClass(string $class)
     {
         return $this->behaviorRegistry->findBehaviorByClass($class);
     }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c650bfe and 0aff5ab.

📒 Files selected for processing (8)
  • src/product/TariffTypeDefinition.php (3 hunks)
  • src/product/TariffTypeDefinitionInterface.php (0 hunks)
  • src/product/behavior/BehaviorRegistry.php (1 hunks)
  • src/product/behavior/HasBehaviorsInterface.php (1 hunks)
  • src/product/behavior/PriceTypeBehaviourRegistry.php (1 hunks)
  • src/product/behavior/TariffTypeBehaviorRegistry.php (2 hunks)
  • src/product/price/PriceTypeDefinition.php (7 hunks)
  • tests/unit/product/behavior/TariffTypeBehaviorRegistryTest.php (2 hunks)
💤 Files with no reviewable changes (1)
  • src/product/TariffTypeDefinitionInterface.php
🧰 Additional context used
🧬 Code Graph Analysis (6)
tests/unit/product/behavior/TariffTypeBehaviorRegistryTest.php (3)
src/product/behavior/BehaviorTariffTypeCollection.php (1)
  • BehaviorTariffTypeCollection (13-36)
src/product/behavior/HasBehaviorsInterface.php (1)
  • withBehaviors (17-17)
src/product/behavior/TariffTypeBehaviorRegistry.php (1)
  • withBehaviors (47-50)
src/product/behavior/HasBehaviorsInterface.php (3)
src/product/behavior/BehaviorRegistry.php (1)
  • findBehaviorByClass (24-33)
src/product/TariffTypeDefinition.php (1)
  • findBehaviorByClass (105-108)
src/product/price/PriceTypeDefinition.php (1)
  • findBehaviorByClass (189-192)
src/product/TariffTypeDefinition.php (4)
src/Exception/LogicException.php (1)
  • LogicException (5-8)
src/product/behavior/HasBehaviorsInterface.php (3)
  • withBehaviors (17-17)
  • hasBehavior (19-19)
  • findBehaviorByClass (21-21)
src/product/behavior/TariffTypeBehaviorRegistry.php (1)
  • withBehaviors (47-50)
src/product/behavior/BehaviorRegistry.php (2)
  • hasBehavior (13-22)
  • findBehaviorByClass (24-33)
src/product/behavior/BehaviorRegistry.php (5)
src/product/behavior/PriceTypeBehaviourRegistry.php (2)
  • getBehaviorCollection (36-39)
  • withBehaviors (31-34)
src/product/behavior/TariffTypeBehaviorRegistry.php (2)
  • getBehaviorCollection (52-55)
  • withBehaviors (47-50)
src/product/behavior/HasBehaviorsInterface.php (3)
  • hasBehavior (19-19)
  • findBehaviorByClass (21-21)
  • withBehaviors (17-17)
src/product/TariffTypeDefinition.php (3)
  • hasBehavior (100-103)
  • findBehaviorByClass (105-108)
  • withBehaviors (93-98)
src/product/price/PriceTypeDefinition.php (3)
  • hasBehavior (184-187)
  • findBehaviorByClass (189-192)
  • withBehaviors (177-182)
src/product/behavior/PriceTypeBehaviourRegistry.php (7)
src/product/behavior/BehaviorRegistry.php (2)
  • BehaviorRegistry (9-39)
  • getBehaviorCollection (11-11)
src/product/behavior/BehaviorPriceTypeDefinitionCollection.php (1)
  • BehaviorPriceTypeDefinitionCollection (14-40)
src/product/TariffTypeDefinition.php (3)
  • __construct (29-33)
  • tariffType (35-38)
  • withBehaviors (93-98)
src/product/behavior/TariffTypeBehaviorRegistry.php (3)
  • __construct (39-42)
  • withBehaviors (47-50)
  • getBehaviorCollection (52-55)
src/product/price/PriceTypeDefinition.php (2)
  • __construct (59-69)
  • withBehaviors (177-182)
src/product/TariffTypeDefinitionInterface.php (1)
  • tariffType (18-18)
src/product/behavior/HasBehaviorsInterface.php (1)
  • withBehaviors (17-17)
src/product/behavior/TariffTypeBehaviorRegistry.php (6)
src/product/behavior/BehaviorRegistry.php (2)
  • BehaviorRegistry (9-39)
  • getBehaviorCollection (11-11)
src/product/behavior/HasBehaviorsInterface.php (1)
  • withBehaviors (17-17)
src/product/TariffTypeDefinition.php (1)
  • withBehaviors (93-98)
src/product/behavior/PriceTypeBehaviourRegistry.php (2)
  • withBehaviors (31-34)
  • getBehaviorCollection (36-39)
src/product/price/PriceTypeDefinition.php (1)
  • withBehaviors (177-182)
src/product/behavior/BehaviorTariffTypeCollection.php (1)
  • BehaviorTariffTypeCollection (13-36)
🔇 Additional comments (7)
tests/unit/product/behavior/TariffTypeBehaviorRegistryTest.php (1)

24-24: Rename usage looks correct

Switch to withBehaviors() aligns with the new API. No issues here.

src/product/TariffTypeDefinition.php (2)

97-98: Correct delegation to registry

Switching to $this->tariffTypeBehaviorRegistry->withBehaviors() is consistent with the new registry abstraction.


12-12: Confirm which LogicException to use (global vs project-specific)

You currently import use LogicException; which maps to \LogicException. The project also defines hiqdev\php\billing\Exception\LogicException. If the intent is to throw a domain-specific exception (easier to catch library errors), import that instead.

If desired, switch to the project exception:

- use LogicException;
+ use hiqdev\php\billing\Exception\LogicException;

Then Line 116 remains throw new LogicException(...);

Also applies to: 116-116

src/product/behavior/TariffTypeBehaviorRegistry.php (3)

29-29: Good move: centralize behavior logic via BehaviorRegistry

Extending BehaviorRegistry removes duplication and aligns with the new registry pattern.


47-50: withBehaviors() impl is correct and type-safe

Returning the concrete BehaviorTariffTypeCollection is appropriate and compatible with HasBehaviorsInterface expectations.


52-55: Correct fulfillment of abstract contract

getBehaviorCollection() cleanly exposes the underlying collection for the base class.

src/product/price/PriceTypeDefinition.php (1)

54-55: Confirm TariffTypeDefinition exposes findBehaviorByClass and delegation is wired correctly

All delegation methods are in place and TariffTypeDefinition now directly exposes findBehaviorByClass:

  • src/product/TariffTypeDefinition.php lines 105–107:

    public function findBehaviorByClass(string $class)
    {
        return $this->tariffTypeBehaviorRegistry->findBehaviorByClass($class);
    }
  • Delegation for withBehaviors(), hasBehavior(), lock() and others remain correctly routed through TariffTypeBehaviorRegistry.

Note: the registry class names use inconsistent spelling—Price uses PriceTypeBehaviourRegistry (UK) while Tariff uses TariffTypeBehaviorRegistry (US). If this wasn’t intentional, consider unifying the naming across both registries.

/**
* @template T as PriceTypeDefinitionInterface
*/
final class PriceTypeBehaviourRegistry extends BehaviorRegistry
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Naming consistency: Behaviour ➜ Behavior

The codebase predominantly uses “Behavior”. Introduce PriceTypeBehaviorRegistry to match TariffTypeBehaviorRegistry and other classes. Otherwise, you’ll have mixed spellings across APIs and types.

-final class PriceTypeBehaviourRegistry extends BehaviorRegistry
+final class PriceTypeBehaviorRegistry extends BehaviorRegistry

Follow-up: rename the file to PriceTypeBehaviorRegistry.php and update all references.


🏁 Script executed:

#!/bin/bash
# Find all British spelling occurrences to update along with the class/file rename
rg -n "PriceTypeBehaviourRegistry|BehaviourRegistry|Behaviour" --glob '!vendor'

Length of output: 555


Rename PriceTypeBehaviourRegistry to PriceTypeBehaviorRegistry for naming consistency

To align with the rest of the codebase (which uses the American spelling “Behavior”), rename the class, file, and all references:

• src/product/behavior/PriceTypeBehaviourRegistry.php

-final class PriceTypeBehaviourRegistry extends BehaviorRegistry
+final class PriceTypeBehaviorRegistry extends BehaviorRegistry

• Rename file → src/product/behavior/PriceTypeBehaviorRegistry.php

• src/product/price/PriceTypeDefinition.php

-use hiqdev\php\billing\product\behavior\PriceTypeBehaviourRegistry;
+use hiqdev\php\billing\product\behavior\PriceTypeBehaviorRegistry;

-private readonly PriceTypeBehaviourRegistry $behaviorRegistry;
+private readonly PriceTypeBehaviorRegistry $behaviorRegistry;

-$this->behaviorRegistry = new PriceTypeBehaviourRegistry($this, $tariffType);
+$this->behaviorRegistry = new PriceTypeBehaviorRegistry($this, $tariffType);

After renaming, run a global search for “BehaviourRegistry” to catch any remaining imports or references.

🤖 Prompt for AI Agents
In src/product/behavior/PriceTypeBehaviourRegistry.php at line 13, rename the
class from PriceTypeBehaviourRegistry to PriceTypeBehaviorRegistry to maintain
naming consistency with the American spelling used elsewhere. Rename the file
accordingly to PriceTypeBehaviorRegistry.php and update all references and
imports in the codebase, including in src/product/price/PriceTypeDefinition.php.
After these changes, perform a global search for "BehaviourRegistry" to ensure
no remaining references exist.

use hiqdev\php\billing\product\behavior\BehaviorCollectionInterface;
use hiqdev\php\billing\product\behavior\BehaviorInterface;
use hiqdev\php\billing\product\behavior\HasBehaviorsInterface;
use hiqdev\php\billing\product\behavior\PriceTypeBehaviourRegistry;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Naming consistency: Behaviour vs Behavior

The codebase mixes “Behaviour” and “Behavior”. If not intentional, consider standardizing to a single variant to reduce confusion and accidental mis-imports.

To assess the scope, run:

Also applies to: 54-55


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n "Behaviour|Behavior" src | sed -E 's/:.*//' | sort -u

Length of output: 953


Standardize “Behavior” vs “Behaviour” Spelling

The codebase currently mixes British and American spellings. Almost all behavior-related classes use Behavior (US), except:

  • src/product/behavior/PriceTypeBehaviourRegistry.php (class, file name, and references)
  • import in src/product/price/PriceTypeDefinition.php (line 8)

To avoid confusion and accidental mis-imports, pick one variant—ideally Behavior to match the directory and existing classes—and update:

  • Rename PriceTypeBehaviourRegistry.phpPriceTypeBehaviorRegistry.php
  • Update its class name and namespace accordingly
  • Adjust all imports and references (e.g., in PriceTypeDefinition.php)
  • Ensure tests and any related docs use the chosen spelling
🤖 Prompt for AI Agents
In src/product/price/PriceTypeDefinition.php at line 8, the import uses the
British spelling "Behaviour" which is inconsistent with the rest of the codebase
that uses the American spelling "Behavior". Rename the file
src/product/behavior/PriceTypeBehaviourRegistry.php to
PriceTypeBehaviorRegistry.php, update the class name and namespace inside that
file to use "Behavior", and change all imports and references accordingly,
including the import in PriceTypeDefinition.php at line 8. Also, update any
tests and documentation to use the standardized "Behavior" spelling.

use hiqdev\php\billing\product\TariffTypeDefinitionInterface;
use hiqdev\php\billing\product\trait\HasLock;
use hiqdev\php\billing\type\TypeInterface;
use function class_exists;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Validate formatter contract, not just existence

class_exists prevents fatals, but validating the class implements QuantityFormatterInterface will fail earlier and with a clearer message.

-        if (!class_exists($formatterClass)) {
+        if (!class_exists($formatterClass)) {
             throw new InvalidQuantityFormatterException("Formatter class $formatterClass does not exist");
         }
+        if (!is_subclass_of($formatterClass, QuantityFormatterInterface::class)) {
+            throw new InvalidQuantityFormatterException(sprintf(
+                'Formatter class %s must implement %s',
+                $formatterClass,
+                QuantityFormatterInterface::class
+            ));
+        }

Also applies to: 107-112

🤖 Prompt for AI Agents
In src/product/price/PriceTypeDefinition.php at line 24 and also lines 107 to
112, the current code uses class_exists to check for the formatter class
existence, which prevents fatal errors but does not ensure the class implements
QuantityFormatterInterface. Update the validation to check that the class both
exists and implements QuantityFormatterInterface, throwing an early and clear
exception if it does not. This will improve error clarity and contract
enforcement.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
src/product/behavior/BehaviorRegistry.php (1)

20-29: Minor: reduce duplication by delegating to findBehaviorByClass.

Same traversal twice. Reuse findBehaviorByClass for clarity and maintainability.

-    public function hasBehavior(string $behaviorClassName): bool
-    {
-        foreach ($this->getBehaviorCollection() as $behavior) {
-            if ($behavior instanceof $behaviorClassName) {
-                return true;
-            }
-        }
-
-        return false;
-    }
+    /**
+     * @param class-string $behaviorClassName
+     */
+    public function hasBehavior(string $behaviorClassName): bool
+    {
+        return $this->findBehaviorByClass($behaviorClassName) !== null;
+    }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0aff5ab and ca6bc90.

📒 Files selected for processing (1)
  • src/product/behavior/BehaviorRegistry.php (1 hunks)
🧰 Additional context used
🪛 GitHub Check: PHP 8.3
src/product/behavior/BehaviorRegistry.php

[failure] 16-16: InvalidTemplateParam
src/product/behavior/BehaviorRegistry.php:16:16: InvalidTemplateParam: Extended template param TParentContext of hiqdev\php\billing\product\behavior\BehaviorCollectionInterface<TParentCollection:hiqdev\php\billing\product\behavior\BehaviorRegistry as mixed> expects type hiqdev\php\billing\product\TariffTypeDefinitionInterface|hiqdev\php\billing\product\price\PriceTypeDefinitionInterface, type TParentCollection:hiqdev\php\billing\product\behavior\BehaviorRegistry as mixed given (see https://psalm.dev/183)


[failure] 13-13: InvalidTemplateParam
src/product/behavior/BehaviorRegistry.php:13:62: InvalidTemplateParam: Extended template param TParentCollection expects type hiqdev\php\billing\product\TariffTypeDefinitionInterface|hiqdev\php\billing\product\price\PriceTypeDefinitionInterface, type TParentCollection:hiqdev\php\billing\product\behavior\BehaviorRegistry as mixed given (see https://psalm.dev/183)


[failure] 13-13: InvalidTemplateParam
src/product/behavior/BehaviorRegistry.php:13:62: InvalidTemplateParam: Cannot extend a strictly-enforced parent template param TParentCollection with constraint hiqdev\php\billing\product\TariffTypeDefinitionInterface|hiqdev\php\billing\product\price\PriceTypeDefinitionInterface with a child template param TParentCollection with different constraint mixed (see https://psalm.dev/183)

🪛 GitHub Actions: Psalm Static Analysis
src/product/behavior/BehaviorRegistry.php

[error] 13-13: Psalm InvalidTemplateParam: Cannot extend a strictly-enforced parent template param TParentCollection with constraint hiqdev\php\billing\product\TariffTypeDefinitionInterface|hiqdev\php\billing\product\price\PriceTypeDefinitionInterface with a child template param TParentCollection with different constraint mixed.

🔇 Additional comments (1)
src/product/behavior/BehaviorRegistry.php (1)

7-7: Import path verified

HasLockInterface is declared in src/product/trait/HasLockInterface.php under
namespace hiqdev\php\billing\product\trait; the use statement is correct and will autoload as expected.

Comment on lines +31 to +40
public function findBehaviorByClass(string $class)
{
foreach ($this->getBehaviorCollection() as $behavior) {
if ($behavior instanceof $class) {
return $behavior;
}
}

return null;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Strengthen typing for findBehaviorByClass with class-string and generic return.

Improve static analysis by annotating class-string input and a templated return type.

-    public function findBehaviorByClass(string $class)
+    /**
+     * @template TBehavior of object
+     * @param class-string<TBehavior> $class
+     * @return TBehavior|null
+     */
+    public function findBehaviorByClass(string $class)
     {
         foreach ($this->getBehaviorCollection() as $behavior) {
             if ($behavior instanceof $class) {
                 return $behavior;
             }
         }
 
         return null;
     }
🤖 Prompt for AI Agents
In src/product/behavior/BehaviorRegistry.php around lines 31 to 40, the method
findBehaviorByClass lacks precise typing for its input and return value. Update
the PHPDoc to specify the parameter as a class-string of a generic type T and
the return type as either an instance of T or null. This will enhance static
analysis and type safety by clearly indicating the expected input and output
types.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/product/behavior/PriceTypeBehaviorRegistry.php (1)

19-19: Optional: Make the collection property readonly (PHP ≥ 8.1)

The field is initialized once in the constructor and never reassigned. Marking it readonly improves immutability guarantees.

-    private BehaviorPriceTypeDefinitionCollection $behaviorCollection;
+    private readonly BehaviorPriceTypeDefinitionCollection $behaviorCollection;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ca6bc90 and 8064c4a.

📒 Files selected for processing (4)
  • src/product/behavior/BehaviorRegistry.php (1 hunks)
  • src/product/behavior/PriceTypeBehaviorRegistry.php (1 hunks)
  • src/product/behavior/TariffTypeBehaviorRegistry.php (2 hunks)
  • src/product/price/PriceTypeDefinition.php (7 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/product/behavior/TariffTypeBehaviorRegistry.php
  • src/product/behavior/BehaviorRegistry.php
  • src/product/price/PriceTypeDefinition.php
🔇 Additional comments (1)
src/product/behavior/PriceTypeBehaviorRegistry.php (1)

37-40: LGTM: correct protected override returning the interface

Clean override; returning BehaviorCollectionInterface keeps the base contract intact while encapsulating the concrete collection.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/product/behavior/TariffTypeBehaviorRegistry.php (2)

53-56: Add precise generics to return type doc for getBehaviorCollection()

To help Psalm/IDE inference, annotate the generic on the interface type.

+    /**
+     * @return BehaviorCollectionInterface<T>
+     */
     protected function getBehaviorCollection(): BehaviorCollectionInterface
     {
         return $this->behaviorCollection;
     }

17-25: Docblock is stale: reference withBehaviors() and findBehaviorByClass()

The docs still mention getBehaviors(). Also, consider noting findBehaviorByClass() availability via the base class.

- *  - Provides access to the behavior collection via getBehaviors().
- *  - Allows checking for the existence of a specific behavior using hasBehavior().
+ *  - Provides access to the behavior collection via withBehaviors().
+ *  - Exposes behavior lookup helpers (e.g., hasBehavior(), findBehaviorByClass()) via the parent BehaviorRegistry.
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8064c4a and 1a99cc4.

📒 Files selected for processing (3)
  • src/product/behavior/HasBehaviorsInterface.php (1 hunks)
  • src/product/behavior/PriceTypeBehaviorRegistry.php (1 hunks)
  • src/product/behavior/TariffTypeBehaviorRegistry.php (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/product/behavior/PriceTypeBehaviorRegistry.php
  • src/product/behavior/HasBehaviorsInterface.php
🧰 Additional context used
🪛 GitHub Check: PHP 8.3
src/product/behavior/TariffTypeBehaviorRegistry.php

[failure] 30-30: InvalidTemplateParam
src/product/behavior/TariffTypeBehaviorRegistry.php:30:13: InvalidTemplateParam: Cannot extend an invariant template param TParentCollection into a covariant context (see https://psalm.dev/183)

🪛 GitHub Actions: Psalm Static Analysis
src/product/behavior/TariffTypeBehaviorRegistry.php

[error] 30-30: Psalm: InvalidTemplateParam: Cannot extend an invariant template param TParentCollection into a covariant context (see https://psalm.dev/183)

Comment on lines +48 to 51
public function withBehaviors(): BehaviorTariffTypeCollection
{
return $this->behaviorCollection;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

withBehaviors() rename: verify all call sites migrated from getBehaviors()

Rename looks good. Please ensure no usages of getBehaviors() remain.

Run this script to find leftovers and confirm new usages:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Searching for old API getBehaviors("
rg -n --hidden --no-ignore -S '\bgetBehaviors\(' || true

echo
echo "Searching for new API withBehaviors("
rg -n --hidden --no-ignore -S '\bwithBehaviors\(' || true

Length of output: 2746


withBehaviors() rename verification

All call-sites of the old getBehaviors() API have been updated to withBehaviors(). The only remaining reference is in the docblock of the registry class:

• src/product/behavior/TariffTypeBehaviorRegistry.php:19
Change “Provides access to the behavior collection via getBehaviors().” to “via withBehaviors().”

🤖 Prompt for AI Agents
In src/product/behavior/TariffTypeBehaviorRegistry.php around lines 19, update
the class docblock to replace the phrase "Provides access to the behavior
collection via getBehaviors()." with "Provides access to the behavior collection
via withBehaviors()." to reflect the method rename accurately.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/product/behavior/BehaviorRegistry.php (1)

33-42: Strengthen typing for findBehaviorByClass (class-string + generic return).

Annotate with method-level template and class-string param to improve static analysis.

-    public function findBehaviorByClass(string $class)
+    /**
+     * @template TBehavior of object
+     * @param class-string<TBehavior> $class
+     * @return TBehavior|null
+     */
+    public function findBehaviorByClass(string $class)
🧹 Nitpick comments (1)
src/product/behavior/BehaviorRegistry.php (1)

22-31: Tighten input typing for hasBehavior with class-string.

Clarify that the argument is a class-string for better static analysis.

-    public function hasBehavior(string $behaviorClassName): bool
+    /**
+     * @param class-string $behaviorClassName
+     */
+    public function hasBehavior(string $behaviorClassName): bool
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1a99cc4 and 5b5cf26.

📒 Files selected for processing (1)
  • src/product/behavior/BehaviorRegistry.php (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-01-13T12:20:09.570Z
Learnt from: SilverFire
PR: hiqdev/php-billing#93
File: src/product/BillingRegistry.php:5-5
Timestamp: 2025-01-13T12:20:09.570Z
Learning: The `hiqdev/php-billing` repository should not depend on `advancedhosters/billing-registry`. Use internal interfaces and implementations instead.

Applied to files:

  • src/product/behavior/BehaviorRegistry.php
🧬 Code Graph Analysis (1)
src/product/behavior/BehaviorRegistry.php (4)
src/product/behavior/TariffTypeBehaviorRegistry.php (1)
  • getBehaviorCollection (53-56)
src/product/behavior/HasBehaviorsInterface.php (2)
  • hasBehavior (19-19)
  • findBehaviorByClass (26-26)
src/product/TariffTypeDefinition.php (2)
  • hasBehavior (100-103)
  • findBehaviorByClass (105-108)
src/product/price/PriceTypeDefinition.php (2)
  • hasBehavior (184-187)
  • findBehaviorByClass (189-192)
🪛 GitHub Check: PHP 8.3
src/product/behavior/BehaviorRegistry.php

[failure] 15-15: InvalidTemplateParam
src/product/behavior/BehaviorRegistry.php:15:62: InvalidTemplateParam: Cannot extend an invariant template param TParentCollection into a covariant context (see https://psalm.dev/183)

🪛 GitHub Actions: Psalm Static Analysis
src/product/behavior/BehaviorRegistry.php

[error] 15-15: Psalm: InvalidTemplateParam: Cannot extend an invariant template param TParentCollection into a covariant context (see https://psalm.dev/183)

🔇 Additional comments (1)
src/product/behavior/BehaviorRegistry.php (1)

44-47: Lock delegation LGTM.

Delegates to underlying collection; matches HasLockInterface intent.

Comment on lines +11 to +16
/**
* @template-covariant TParentCollection of PriceTypeDefinitionInterface|TariffTypeDefinitionInterface
* @implements HasBehaviorsInterface<TParentCollection>
*/
abstract class BehaviorRegistry implements HasLockInterface, HasBehaviorsInterface
{
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix template variance to satisfy Psalm (blocks CI).

TParentCollection is declared covariant, but HasBehaviorsInterface expects an invariant template. Make it invariant.

 /**
- * @template-covariant TParentCollection of PriceTypeDefinitionInterface|TariffTypeDefinitionInterface
+ * @template TParentCollection of PriceTypeDefinitionInterface|TariffTypeDefinitionInterface
  * @implements HasBehaviorsInterface<TParentCollection>
  */
 abstract class BehaviorRegistry implements HasLockInterface, HasBehaviorsInterface
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* @template-covariant TParentCollection of PriceTypeDefinitionInterface|TariffTypeDefinitionInterface
* @implements HasBehaviorsInterface<TParentCollection>
*/
abstract class BehaviorRegistry implements HasLockInterface, HasBehaviorsInterface
{
/**
- * @template-covariant TParentCollection of PriceTypeDefinitionInterface|TariffTypeDefinitionInterface
+ * @template TParentCollection of PriceTypeDefinitionInterface|TariffTypeDefinitionInterface
* @implements HasBehaviorsInterface<TParentCollection>
*/
abstract class BehaviorRegistry implements HasLockInterface, HasBehaviorsInterface
{
// ...
}
🧰 Tools
🪛 GitHub Check: PHP 8.3

[failure] 15-15: InvalidTemplateParam
src/product/behavior/BehaviorRegistry.php:15:62: InvalidTemplateParam: Cannot extend an invariant template param TParentCollection into a covariant context (see https://psalm.dev/183)

🪛 GitHub Actions: Psalm Static Analysis

[error] 15-15: Psalm: InvalidTemplateParam: Cannot extend an invariant template param TParentCollection into a covariant context (see https://psalm.dev/183)

🤖 Prompt for AI Agents
In src/product/behavior/BehaviorRegistry.php around lines 11 to 16, the template
parameter TParentCollection is declared as covariant, but the
HasBehaviorsInterface requires it to be invariant. Change the template
annotation for TParentCollection from covariant to invariant to satisfy Psalm
and unblock CI.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/product/behavior/PriceTypeBehaviorRegistry.php (1)

10-13: Fix Psalm generics: declare template T and use it in @extends

T is referenced in property/param/return docblocks but never declared, causing the Psalm UndefinedDocblockClass errors. Also, per prior feedback, propagate the subclass template to the parent.

-/**
- * @extends BehaviorRegistry<PriceTypeDefinitionInterface>
- */
+/**
+ * @template T of PriceTypeDefinitionInterface
+ * @extends BehaviorRegistry<T>
+ */

This will resolve the reported issues at Lines 18, 21, and 29 and properly wire generics across the hierarchy.

🧹 Nitpick comments (1)
src/product/behavior/PriceTypeBehaviorRegistry.php (1)

18-18: Make the collection property readonly (immutability by construction)

The registry is immutable post-construction. Mark the property readonly to encode the invariant and aid static analysis.

-    private BehaviorPriceTypeDefinitionCollection $behaviorCollection;
+    private readonly BehaviorPriceTypeDefinitionCollection $behaviorCollection;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5b5cf26 and da60a93.

📒 Files selected for processing (2)
  • src/product/behavior/PriceTypeBehaviorRegistry.php (1 hunks)
  • src/product/behavior/TariffTypeBehaviorRegistry.php (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/product/behavior/TariffTypeBehaviorRegistry.php
🧰 Additional context used
🪛 GitHub Check: PHP 8.3
src/product/behavior/PriceTypeBehaviorRegistry.php

[failure] 29-29: UndefinedDocblockClass
src/product/behavior/PriceTypeBehaviorRegistry.php:29:16: UndefinedDocblockClass: Docblock-defined class, interface or enum named hiqdev\php\billing\product\behavior\T does not exist (see https://psalm.dev/200)


[failure] 21-21: UndefinedDocblockClass
src/product/behavior/PriceTypeBehaviorRegistry.php:21:21: UndefinedDocblockClass: Docblock-defined class, interface or enum named hiqdev\php\billing\product\behavior\T does not exist (see https://psalm.dev/200)


[failure] 18-18: UndefinedDocblockClass
src/product/behavior/PriceTypeBehaviorRegistry.php:18:13: UndefinedDocblockClass: Docblock-defined class, interface or enum named hiqdev\php\billing\product\behavior\T does not exist (see https://psalm.dev/200)

🔇 Additional comments (2)
src/product/behavior/PriceTypeBehaviorRegistry.php (2)

31-34: withBehaviors() is clear and correctly typed

Return type and variance are appropriate; exposing the specialized collection aligns with usage.


36-39: Correct override of abstract accessor

Returns the expected BehaviorCollectionInterface; this enables base class features (hasBehavior/findBehaviorByClass/locking).

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🔭 Outside diff range comments (1)
src/product/behavior/TariffTypeBehaviorRegistry.php (1)

17-21: Consistently apply API rename getBehaviors() → withBehaviors()

The API rename hasn’t been applied everywhere—besides updating the docblock in TariffTypeBehaviorRegistry.php, you also need to rename the method and its interface.

Files to update:

  • src/product/behavior/TariffTypeBehaviorRegistry.php (docblock)
  • src/product/Application/BillingRegistryService.php (method signature)
  • src/product/Application/BillingRegistryServiceInterface.php (interface signature)

Proposed diffs:

--- a/src/product/behavior/TariffTypeBehaviorRegistry.php
+++ b/src/product/behavior/TariffTypeBehaviorRegistry.php
@@ -17,7 +17,7 @@
  *  Purpose:
  *  - Stores a collection of behaviors associated with a TariffType.
- *  - Provides access to the behavior collection via getBehaviors().
+ *  - Provides access to the behavior collection via withBehaviors().
  *  - Allows checking for the existence of a specific behavior using hasBehavior().
  *
--- a/src/product/Application/BillingRegistryService.php
+++ b/src/product/Application/BillingRegistryService.php
@@ -111,7 +111,7 @@
     /**
      * Retrieve behaviors registered for a given wrapper.
-     * @return \Generator
-     */
-    public function getBehaviors(string $behaviorClassWrapper): \Generator
+     * @return \Generator
+     */
+    public function withBehaviors(string $behaviorClassWrapper): \Generator
     {
         // …
     }
--- a/src/product/Application/BillingRegistryServiceInterface.php
+++ b/src/product/Application/BillingRegistryServiceInterface.php
@@ -58,7 +58,7 @@
      * @param string $behaviorClassWrapper
      * @return Generator
      */
-    public function getBehaviors(string $behaviorClassWrapper): Generator;
+    public function withBehaviors(string $behaviorClassWrapper): Generator;
♻️ Duplicate comments (3)
src/product/behavior/BehaviorRegistry.php (2)

22-31: Strengthen typing of findBehaviorByClass with class-string and generic return.

Improves static analysis and callers’ DX; keeps signature compatible with the interface.

-    public function findBehaviorByClass(string $class)
+    /**
+     * @template TBehavior of object
+     * @param class-string<TBehavior> $class
+     * @return TBehavior|null
+     */
+    public function findBehaviorByClass(string $class)
     {
         foreach ($this->withBehaviors() as $behavior) {
             if ($behavior instanceof $class) {
                 return $behavior;
             }
         }
 
         return null;
     }

5-9: Add constrained template + @implements and required imports to satisfy Psalm generics (blocks CI).

BehaviorRegistry implements a generic HasBehaviorsInterface but declares no template. Psalm errors: MissingTemplateParam/InvalidTemplateParam. Constrain and wire the template via @implements and import the constrained types.

Apply:

 namespace hiqdev\php\billing\product\behavior;

 use hiqdev\php\billing\product\trait\HasLockInterface;
+use hiqdev\php\billing\product\TariffTypeDefinitionInterface;
+use hiqdev\php\billing\product\price\PriceTypeDefinitionInterface;
 
-abstract class BehaviorRegistry implements HasLockInterface, HasBehaviorsInterface
+/**
+ * @template TParentCollection of TariffTypeDefinitionInterface|PriceTypeDefinitionInterface
+ * @implements HasBehaviorsInterface<TParentCollection>
+ */
+abstract class BehaviorRegistry implements HasLockInterface, HasBehaviorsInterface
src/product/behavior/TariffTypeBehaviorRegistry.php (1)

27-31: Fix template variance and map child template to parent with @extends.

Parent BehaviorRegistry’s template is invariant; child declares covariant T — Psalm flags InvalidTemplateParam. Make T invariant and explicitly extend the parent with T.

- * @template-covariant T of TariffTypeDefinitionInterface
+ * @template T of TariffTypeDefinitionInterface
+ * @extends BehaviorRegistry<T>
🧹 Nitpick comments (2)
src/product/behavior/BehaviorRegistry.php (1)

11-20: Annotate hasBehavior arg as class-string for better static analysis.

Small DX win; no runtime changes.

-    public function hasBehavior(string $behaviorClassName): bool
+    /**
+     * @param class-string $behaviorClassName
+     */
+    public function hasBehavior(string $behaviorClassName): bool
     {
         foreach ($this->withBehaviors() as $behavior) {
             if ($behavior instanceof $behaviorClassName) {
                 return true;
             }
         }
 
         return false;
     }
src/product/behavior/TariffTypeBehaviorRegistry.php (1)

52-55: Add templated return annotation for getBehaviorCollection().

Helps Psalm keep T flowing through the registry.

-    protected function getBehaviorCollection(): BehaviorCollectionInterface
+    /**
+     * @return BehaviorCollectionInterface<T>
+     */
+    protected function getBehaviorCollection(): BehaviorCollectionInterface
     {
         return $this->behaviorCollection;
     }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e6fe816 and c4cb9df.

📒 Files selected for processing (3)
  • src/product/behavior/BehaviorRegistry.php (1 hunks)
  • src/product/behavior/PriceTypeBehaviorRegistry.php (1 hunks)
  • src/product/behavior/TariffTypeBehaviorRegistry.php (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/product/behavior/PriceTypeBehaviorRegistry.php
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-01-13T12:20:09.570Z
Learnt from: SilverFire
PR: hiqdev/php-billing#93
File: src/product/BillingRegistry.php:5-5
Timestamp: 2025-01-13T12:20:09.570Z
Learning: The `hiqdev/php-billing` repository should not depend on `advancedhosters/billing-registry`. Use internal interfaces and implementations instead.

Applied to files:

  • src/product/behavior/BehaviorRegistry.php
🧬 Code Graph Analysis (2)
src/product/behavior/BehaviorRegistry.php (4)
src/product/behavior/HasBehaviorsInterface.php (3)
  • hasBehavior (19-19)
  • withBehaviors (17-17)
  • findBehaviorByClass (26-26)
src/product/price/PriceTypeDefinition.php (3)
  • hasBehavior (184-187)
  • withBehaviors (177-182)
  • findBehaviorByClass (189-192)
src/product/behavior/PriceTypeBehaviorRegistry.php (1)
  • withBehaviors (31-34)
src/product/behavior/TariffTypeBehaviorRegistry.php (1)
  • withBehaviors (47-50)
src/product/behavior/TariffTypeBehaviorRegistry.php (6)
src/product/behavior/BehaviorRegistry.php (1)
  • BehaviorRegistry (9-37)
src/product/behavior/PriceTypeBehaviorRegistry.php (2)
  • withBehaviors (31-34)
  • getBehaviorCollection (36-39)
src/product/behavior/HasBehaviorsInterface.php (1)
  • withBehaviors (17-17)
src/product/TariffTypeDefinition.php (1)
  • withBehaviors (93-98)
src/product/price/PriceTypeDefinition.php (1)
  • withBehaviors (177-182)
src/product/behavior/BehaviorTariffTypeCollection.php (1)
  • BehaviorTariffTypeCollection (13-36)
🪛 GitHub Check: PHP 8.3
src/product/behavior/BehaviorRegistry.php

[failure] 9-9: InvalidTemplateParam
src/product/behavior/BehaviorRegistry.php:9:62: InvalidTemplateParam: Cannot extend a strictly-enforced parent template param TParentCollection with a non-template type (see https://psalm.dev/183)


[failure] 9-9: MissingTemplateParam
src/product/behavior/BehaviorRegistry.php:9:62: MissingTemplateParam: hiqdev\php\billing\product\behavior\BehaviorRegistry requires the same number of template params as hiqdev\php\billing\product\behavior\HasBehaviorsInterface but saw 0 (see https://psalm.dev/182)


[failure] 9-9: MissingTemplateParam
src/product/behavior/BehaviorRegistry.php:9:62: MissingTemplateParam: hiqdev\php\billing\product\behavior\BehaviorRegistry has missing template params when extending hiqdev\php\billing\product\behavior\HasBehaviorsInterface, expecting 1 (see https://psalm.dev/182)

🪛 GitHub Actions: Psalm Static Analysis
src/product/behavior/BehaviorRegistry.php

[error] 9-9: MissingTemplateParam: BehaviorRegistry has missing template params when extending HasBehaviorsInterface, expecting 1 (see https://psalm.dev/182)

🔇 Additional comments (1)
src/product/behavior/TariffTypeBehaviorRegistry.php (1)

47-50: withBehaviors() return typing looks good.

Signature and docblock align with the collection type and template.

@VadymHrechukha VadymHrechukha merged commit 79b6e2c into hiqdev:master Aug 11, 2025
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant