Skip to content

refactor: improve validation command structure and add utility classes#7

Merged
konradmichalik merged 4 commits intomainfrom
refactor-validation
Jun 27, 2025
Merged

refactor: improve validation command structure and add utility classes#7
konradmichalik merged 4 commits intomainfrom
refactor-validation

Conversation

@konradmichalik
Copy link
Copy Markdown
Contributor

@konradmichalik konradmichalik commented Jun 27, 2025

Summary by CodeRabbit

  • New Features

    • Introduced utility classes for class validation and path normalization.
    • Added registries for parsers and validators to centralize available implementations.
    • Enhanced validators with structured issue collection, detailed rendering, and explanatory descriptions.
    • Added new methods to support improved file and parser handling.
  • Refactor

    • Modularized translation validation command and validators for clearer separation of concerns.
    • Updated validator interfaces to separate file processing, issue rendering, and explanations.
    • Simplified file detection and translation set mapping logic.
  • Documentation

    • Corrected example command in README for running test scenarios.
  • Style

    • Improved folder path normalization for consistent output.
  • Tests

    • Added test file with duplicate translation keys to support validation scenarios.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jun 27, 2025

Warning

Rate limit exceeded

@jackd248 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 19 minutes and 4 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 89a93ce and 9d09f09.

📒 Files selected for processing (6)
  • src/Command/ValidateTranslationCommand.php (2 hunks)
  • src/FileDetector/Collector.php (1 hunks)
  • src/Validator/AbstractValidator.php (1 hunks)
  • src/Validator/DuplicatesValidator.php (1 hunks)
  • src/Validator/MismatchValidator.php (1 hunks)
  • src/Validator/SchemaValidator.php (2 hunks)

"""

Walkthrough

This update introduces a modular validation architecture for translation files. It adds utility classes for class and path validation, a collector for file gathering, and a registry-based system for parsers and validators. Validator interfaces and implementations are refactored for single-file processing and structured issue reporting, with rendering and explanation methods separated from core validation logic.

Changes

File(s) Change Summary
README.md Fixed test scenario command: corrected composer binary path and added working directory option.
src/Command/ValidateTranslationCommand.php Refactored command for modular validation: removed parser option, centralized class validation, used new Collector for file gathering, supported multiple validators via ValidatorRegistry, restructured validation loop, added renderIssues method, and improved error reporting and exit code handling.
src/FileDetector/Collector.php Added new Collector class with collectFiles method for gathering files by parser and path, supporting exclude patterns and logging errors for invalid paths.
src/FileDetector/PrefixFileDetector.php Simplified mapTranslationSet: now groups all files by stripped basename, removed findSourceFiles helper, and eliminated previous source/target file distinction.
src/Parser/ParserInterface.php Added getFileName() and getFilePath() methods to the interface.
src/Parser/ParserRegistry.php Introduced ParserRegistry class with static getAvailableParsers() method listing available parser classes.
src/Parser/ParserUtility.php Removed resolveValidators() method and its logic from ParserUtility.
src/Utility/ClassUtility.php Added ClassUtility with static validateClass method for class/interface validation and error logging.
src/Utility/PathUtility.php Added PathUtility with static normalizeFolderPath method for normalizing and relativizing folder paths.
src/Validator/AbstractValidator.php Added AbstractValidator base class: processes files one at a time, collects issues, supports post-processing, and requires subclasses to implement processFile.
src/Validator/DuplicatesValidator.php Refactored to extend AbstractValidator: moved logic to processFile, removed direct console output, added renderIssueSets and explain methods, and now returns structured duplicate key issues.
src/Validator/MismatchValidator.php Refactored to extend AbstractValidator: moved logic to processFile, added postProcess for key aggregation, renderIssueSets for reporting, and explain method; removed inline console output and now returns structured missing key issues.
src/Validator/SchemaValidator.php Refactored to extend AbstractValidator: moved schema checking to processFile, added renderIssueSets for error reporting, and explain method; removed direct console output and previous constructor.
src/Validator/ValidatorInterface.php Replaced validate method with processFile, renderIssueSets, and explain methods for single-file processing, issue rendering, and explanation, respectively.
src/Validator/ValidatorRegistry.php Added ValidatorRegistry class with static getAvailableValidators() method listing all validator classes.
tests/Build/app/Language/XLIFF-fail/de.locallang_be.xlf Added a duplicate translation unit with the same ID to the test XLIFF file for validation testing.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ValidateTranslationCommand
    participant ClassUtility
    participant Collector
    participant ValidatorRegistry
    participant Validator
    participant Output

    User->>ValidateTranslationCommand: execute()
    ValidateTranslationCommand->>ClassUtility: validateClass()
    ClassUtility-->>ValidateTranslationCommand: validation result
    ValidateTranslationCommand->>Collector: collectFiles(paths, detector, excludePatterns)
    Collector-->>ValidateTranslationCommand: filesByParserAndPath
    ValidateTranslationCommand->>ValidatorRegistry: getAvailableValidators()
    ValidatorRegistry-->>ValidateTranslationCommand: validatorClasses
    loop For each validator
        loop For each parser and path
            loop For each translation set
                ValidateTranslationCommand->>Validator: processFile(file)
                Validator-->>ValidateTranslationCommand: issues
            end
        end
        ValidateTranslationCommand->>Validator: renderIssueSets(input, output, issues)
        Validator-->>Output: rendered issues
    end
    ValidateTranslationCommand->>Output: summary and exit code
Loading

Possibly related PRs

Poem

A bunny hopped through fields of code,
Refactoring validators on its road.
With registries and collectors new,
Each translation file got its due.
Now issues render, neat and bright—
This modular warren feels just right!
🐇✨
"""

✨ Finishing Touches
  • 📝 Generate Docstrings

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.
    • @coderabbitai modularize this function.
  • 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.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

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

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

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 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

@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: 5

🧹 Nitpick comments (8)
src/Parser/ParserRegistry.php (1)

1-18: LGTM! Consider adding documentation for better maintainability.

The registry pattern implementation is clean and serves its purpose well in the modular architecture. However, the static analysis correctly identifies missing documentation.

Consider adding class and method documentation:

+/**
+ * Registry for managing available parser implementations.
+ */
 class ParserRegistry
 {
+    /**
+     * Get all available parser class names.
+     * 
+     * @return array<int, class-string<ParserInterface>>
+     */
     public static function getAvailableParsers(): array
src/Utility/PathUtility.php (1)

9-25: Solid path normalization logic with minor documentation need.

The implementation correctly handles the main path normalization scenarios. The logic flow is clear and uses modern PHP string functions appropriately.

Consider adding method documentation as suggested by static analysis:

+    /**
+     * Normalize a folder path to be relative with trailing slash.
+     * 
+     * @param string $path The path to normalize
+     * @return string The normalized relative path with trailing slash
+     */
     public static function normalizeFolderPath(string $path): string

Also consider handling the edge case where getcwd() might return false:

-        $cwd = getcwd();
+        $cwd = getcwd() ?: '';
src/Utility/ClassUtility.php (1)

11-30: Well-structured class validation with proper error handling.

The validation logic correctly checks class existence before interface compliance, and the PSR-3 logging integration provides good error reporting. The null handling is appropriate for optional class validation.

Add documentation as suggested by static analysis:

+    /**
+     * Validate that a class exists and implements the required interface.
+     * 
+     * @param string $interface The required interface class name
+     * @param LoggerInterface $logger Logger for error reporting
+     * @param string|null $class The class to validate (null is considered valid)
+     * @return bool True if validation passes, false otherwise
+     */
     public static function validateClass(string $interface, LoggerInterface $logger, ?string $class): bool
src/Validator/ValidatorRegistry.php (1)

7-20: LGTM! Well-implemented registry pattern.

The ValidatorRegistry effectively centralizes validator management with proper type annotations. Consider adding documentation to address the static analysis hints:

+/**
+ * Registry for available translation validators.
+ */
 class ValidatorRegistry
 {
     /**
+     * Get all available validator classes.
+     *
      * @return array<int, class-string<ValidatorInterface>>
      */
     public static function getAvailableValidators(): array
src/FileDetector/Collector.php (1)

34-34: Remove unnecessary method_exists check.

Since ParserRegistry::getAvailableParsers() returns class-string<ParserInterface> types and getSupportedFileExtensions() is defined in the interface, the method_exists() check is redundant.

-                if (method_exists($parserClass, 'getSupportedFileExtensions')) {
                    $files = array_filter(
                        glob($path.'/*'),
                        static fn ($file) => in_array(pathinfo($file, PATHINFO_EXTENSION), $parserClass::getSupportedFileExtensions(), true)
                    );
-                }
src/Validator/ValidatorInterface.php (1)

11-24: LGTM! Excellent interface refactoring with separation of concerns.

The interface refactoring successfully separates file processing, issue rendering, and explanation into distinct methods, improving testability and maintainability. Consider adding comprehensive documentation:

+/**
+ * Interface for translation file validators.
+ */
 interface ValidatorInterface
 {
     /**
+     * Process a single parsed file and return validation issues.
+     *
+     * @param ParserInterface $file The parsed file to validate
      * @return array<string, mixed>
      */
     public function processFile(ParserInterface $file): array;

     /**
+     * Render collected validation issues to console output.
+     *
+     * @param InputInterface $input Console input interface
+     * @param OutputInterface $output Console output interface
      * @param array<string, mixed> $issueSets
+     * @return void
      */
     public function renderIssueSets(InputInterface $input, OutputInterface $output, array $issueSets): void;

+    /**
+     * Provide a textual explanation of what this validator checks.
+     *
+     * @return string Description of the validator's purpose
+     */
     public function explain(): string;
src/Validator/MismatchValidator.php (1)

49-51: Simplify redundant array search operation.

The ternary operation on line 50 searches for a key and returns the value at that position, which is the same as the key itself. This can be simplified.

-                    $this->issues[$key][$file] = in_array($key, $keys, true)
-                        ? $keys[array_search($key, $keys, true)]
-                        : null;
+                    $this->issues[$key][$file] = in_array($key, $keys, true) ? $key : null;
src/Command/ValidateTranslationCommand.php (1)

108-120: Address the TODO comment for nested loop simplification.

The 4-level nested loop structure is indeed complex and should be simplified for better maintainability.

The nested structure processes: parsers → paths → translation sets → validators. Would you like me to help refactor this into a more maintainable structure, perhaps using a method extraction or a different data flow approach?

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between cb98d17 and 73e47b3.

📒 Files selected for processing (16)
  • README.md (1 hunks)
  • src/Command/ValidateTranslationCommand.php (2 hunks)
  • src/FileDetector/Collector.php (1 hunks)
  • src/FileDetector/PrefixFileDetector.php (1 hunks)
  • src/Parser/ParserInterface.php (1 hunks)
  • src/Parser/ParserRegistry.php (1 hunks)
  • src/Parser/ParserUtility.php (0 hunks)
  • src/Utility/ClassUtility.php (1 hunks)
  • src/Utility/PathUtility.php (1 hunks)
  • src/Validator/AbstractValidator.php (1 hunks)
  • src/Validator/DuplicatesValidator.php (1 hunks)
  • src/Validator/MismatchValidator.php (1 hunks)
  • src/Validator/SchemaValidator.php (2 hunks)
  • src/Validator/ValidatorInterface.php (1 hunks)
  • src/Validator/ValidatorRegistry.php (1 hunks)
  • tests/Build/app/Language/XLIFF-fail/de.locallang_be.xlf (1 hunks)
💤 Files with no reviewable changes (1)
  • src/Parser/ParserUtility.php
🧰 Additional context used
🧬 Code Graph Analysis (6)
src/Parser/ParserRegistry.php (1)
src/Parser/XliffParser.php (1)
  • XliffParser (11-98)
src/Parser/ParserInterface.php (1)
src/Parser/XliffParser.php (2)
  • getFileName (73-76)
  • getFilePath (83-86)
src/FileDetector/Collector.php (4)
src/Parser/ParserRegistry.php (2)
  • ParserRegistry (7-18)
  • getAvailableParsers (12-17)
src/Parser/ParserInterface.php (2)
  • __construct (9-9)
  • getSupportedFileExtensions (14-14)
src/Validator/AbstractValidator.php (1)
  • __construct (16-18)
src/FileDetector/PrefixFileDetector.php (1)
  • mapTranslationSet (14-25)
src/Validator/AbstractValidator.php (8)
src/Parser/ParserUtility.php (2)
  • ParserUtility (7-65)
  • resolveParserClass (51-64)
src/Parser/ParserInterface.php (2)
  • __construct (9-9)
  • getFileName (23-23)
src/FileDetector/Collector.php (1)
  • __construct (13-15)
src/Parser/XliffParser.php (1)
  • getFileDirectory (78-81)
src/Validator/DuplicatesValidator.php (1)
  • processFile (17-33)
src/Validator/ValidatorInterface.php (1)
  • processFile (16-16)
src/Validator/SchemaValidator.php (1)
  • processFile (16-32)
src/Validator/MismatchValidator.php (2)
  • processFile (17-29)
  • postProcess (31-55)
src/Validator/SchemaValidator.php (4)
src/Validator/AbstractValidator.php (2)
  • AbstractValidator (11-56)
  • processFile (50-50)
src/Validator/ValidatorInterface.php (3)
  • processFile (16-16)
  • renderIssueSets (21-21)
  • explain (23-23)
src/Parser/ParserInterface.php (1)
  • getFilePath (25-25)
src/Parser/XliffParser.php (1)
  • getFilePath (83-86)
src/Command/ValidateTranslationCommand.php (9)
src/FileDetector/Collector.php (2)
  • Collector (11-56)
  • collectFiles (23-55)
src/FileDetector/PrefixFileDetector.php (1)
  • PrefixFileDetector (7-26)
src/Utility/ClassUtility.php (2)
  • ClassUtility (9-31)
  • validateClass (11-30)
src/Utility/PathUtility.php (2)
  • PathUtility (7-26)
  • normalizeFolderPath (9-25)
src/Validator/ValidatorRegistry.php (2)
  • ValidatorRegistry (7-20)
  • getAvailableValidators (12-19)
src/Validator/DuplicatesValidator.php (2)
  • explain (56-60)
  • renderIssueSets (38-54)
src/Validator/ValidatorInterface.php (2)
  • explain (23-23)
  • renderIssueSets (21-21)
src/Validator/SchemaValidator.php (2)
  • explain (65-69)
  • renderIssueSets (37-63)
src/Validator/MismatchValidator.php (2)
  • explain (84-88)
  • renderIssueSets (60-82)
🪛 phpcs (3.7.2)
src/Validator/ValidatorRegistry.php

[ERROR] 2-2: Missing file doc comment

(PEAR.Commenting.FileComment.Missing)


[ERROR] 7-7: Missing doc comment for class ValidatorRegistry

(PEAR.Commenting.ClassComment.Missing)


[ERROR] 9-9: Missing short description in doc comment

(Generic.Commenting.DocComment.MissingShort)

src/Utility/PathUtility.php

[ERROR] 2-2: Missing file doc comment

(PEAR.Commenting.FileComment.Missing)


[ERROR] 7-7: Missing doc comment for class PathUtility

(PEAR.Commenting.ClassComment.Missing)


[ERROR] 9-9: Missing doc comment for function normalizeFolderPath()

(PEAR.Commenting.FunctionComment.Missing)

src/Utility/ClassUtility.php

[ERROR] 2-2: Missing file doc comment

(PEAR.Commenting.FileComment.Missing)


[ERROR] 9-9: Missing doc comment for class ClassUtility

(PEAR.Commenting.ClassComment.Missing)


[ERROR] 11-11: Missing doc comment for function validateClass()

(PEAR.Commenting.FunctionComment.Missing)

src/Validator/ValidatorInterface.php

[ERROR] 11-11: Missing doc comment for interface ValidatorInterface

(PEAR.Commenting.ClassComment.Missing)


[ERROR] 13-13: Missing short description in doc comment

(Generic.Commenting.DocComment.MissingShort)


[ERROR] 13-13: Doc comment for parameter "$file" missing

(PEAR.Commenting.FunctionComment.MissingParamTag)


[ERROR] 18-18: Missing short description in doc comment

(Generic.Commenting.DocComment.MissingShort)


[ERROR] 18-18: Doc comment for parameter "$input" missing

(PEAR.Commenting.FunctionComment.MissingParamTag)


[ERROR] 18-18: Doc comment for parameter "$output" missing

(PEAR.Commenting.FunctionComment.MissingParamTag)


[ERROR] 19-19: Missing parameter comment

(PEAR.Commenting.FunctionComment.MissingParamComment)


[ERROR] 19-19: Doc comment for parameter $issueSets does not match actual variable name $input

(PEAR.Commenting.FunctionComment.ParamNameNoMatch)


[ERROR] 20-20: Missing @return tag in function comment

(PEAR.Commenting.FunctionComment.MissingReturn)


[ERROR] 23-23: Missing doc comment for function explain()

(PEAR.Commenting.FunctionComment.Missing)

🪛 PHPStan (2.1.15)
src/FileDetector/Collector.php

34-34: Call to function method_exists() with class-string<MoveElevator\ComposerTranslationValidator\Parser\ParserInterface> and 'getSupportedFileExt…' will always evaluate to true.
Because the type is coming from a PHPDoc, you can turn off this check by setting treatPhpDocTypesAsCertain: false in your %configurationFile%.

(function.alreadyNarrowedType)

🔇 Additional comments (6)
src/FileDetector/PrefixFileDetector.php (1)

16-24: Excellent simplification that improves readability and maintainability.

The refactored approach is much cleaner than the previous two-step process. The direct grouping by stripped basename is more straightforward and easier to understand.

The regex pattern correctly identifies language prefixes (e.g., "de.", "en.") and the grouping logic will properly associate translation files with their variants.

tests/Build/app/Language/XLIFF-fail/de.locallang_be.xlf (1)

14-17: Appropriate test case for duplicate validation.

The duplicate trans-unit with id="key2" correctly creates a test scenario for the DuplicatesValidator to detect and report duplicate translation keys.

This test file will help ensure the refactored validation architecture properly identifies duplicate entries in XLIFF files.

README.md (1)

37-37: LGTM! Documentation path correction.

The path correction from test/ to tests/ and the addition of the -d flag properly align the documentation with the project structure and ensure the command runs in the correct working directory.

src/Parser/ParserInterface.php (1)

23-25: LGTM! Interface additions support modular architecture.

The new getFileName() and getFilePath() methods provide necessary file metadata access for the refactored validation system. The methods are properly typed and align with the existing implementation in XliffParser.php.

src/FileDetector/Collector.php (1)

40-42: LGTM! Efficient exclude pattern filtering.

The double array_filter approach for exclude patterns is efficient and handles the logic correctly.

src/Validator/SchemaValidator.php (1)

16-32: Well-implemented schema validation logic.

The method properly handles exceptions, logs errors appropriately, and returns the expected array structure. Good use of Symfony's XML utilities for schema validation.

Comment on lines 61 to 74
if (!ClassUtility::validateClass(
DetectorInterface::class,
$this->logger,
$input->getOption('file-detector'))
) {
$this->io->error(
sprintf('The file detector class "%s" must implement %s.',
$input->getOption('file-detector'),
DetectorInterface::class
)
);

return Command::FAILURE;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Extract duplicate class validation logic.

The class validation logic is duplicated for both file detector and validator. Consider extracting this into a helper method to follow DRY principles.

Add a private helper method:

private function validateAndInstantiate(string $interface, string $className, string $type): ?object
{
    if (!ClassUtility::validateClass($interface, $this->logger, $className)) {
        $this->io->error(
            sprintf('The %s class "%s" must implement %s.', $type, $className, $interface)
        );
        return null;
    }
    return new $className();
}

Then use it:

-        if (!ClassUtility::validateClass(
-            DetectorInterface::class,
-            $this->logger,
-            $input->getOption('file-detector'))
-        ) {
-            $this->io->error(
-                sprintf('The file detector class "%s" must implement %s.',
-                    $input->getOption('file-detector'),
-                    DetectorInterface::class
-                )
-            );
-
-            return Command::FAILURE;
-        }
-        $fileDetector = new ($input->getOption('file-detector'))();
+        $fileDetector = $this->validateAndInstantiate(
+            DetectorInterface::class,
+            $input->getOption('file-detector'),
+            'file detector'
+        );
+        if (!$fileDetector) {
+            return Command::FAILURE;
+        }

Also applies to: 90-103

🤖 Prompt for AI Agents
In src/Command/ValidateTranslationCommand.php around lines 61 to 74 and 90 to
103, the class validation logic for file detector and validator is duplicated.
Extract this logic into a private helper method named validateAndInstantiate
that takes the interface, class name, and type as parameters, performs the
validation, logs an error if invalid, and returns an instance of the class if
valid or null otherwise. Replace the existing validation code blocks with calls
to this new helper method to adhere to DRY principles.

Copy link
Copy Markdown

@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/FileDetector/Collector.php (1)

28-33: Fix early return logic for invalid paths.

The current implementation returns an empty array when encountering the first invalid path, preventing processing of subsequent valid paths. This could cause the entire validation to fail unnecessarily.

         foreach ($paths as $path) {
             if (!(new Filesystem())->exists($path)) {
-                $this->logger->error('The provided path "'.$path.'" is not a valid directory.');
+                $this->logger?->error('The provided path "'.$path.'" is not a valid directory.');
-
-                return [];
+                continue;
             }
🧹 Nitpick comments (3)
src/FileDetector/Collector.php (3)

36-36: Remove redundant method existence check.

The static analysis tool correctly identifies that this check is always true since ParserInterface guarantees the getSupportedFileExtensions method exists.

-                if (method_exists($parserClass, 'getSupportedFileExtensions')) {
                    $files = array_filter(
                        glob($path.'/*'),
                        static fn ($file) => in_array(pathinfo($file, PATHINFO_EXTENSION), $parserClass::getSupportedFileExtensions(), true)
                    );
-                }

37-40: Improve file discovery efficiency and safety.

The current glob approach has several limitations: it doesn't handle recursive directories, may not scale well for large directories, and doesn't validate that results are actually files.

Consider using Symfony's Finder component for more robust file discovery:

+use Symfony\Component\Finder\Finder;

-                    $files = array_filter(
-                        glob($path.'/*'),
-                        static fn ($file) => in_array(pathinfo($file, PATHINFO_EXTENSION), $parserClass::getSupportedFileExtensions(), true)
-                    );
+                    $finder = new Finder();
+                    $finder->files()->in($path)->name('/\.('.implode('|', $parserClass::getSupportedFileExtensions()).')$/');
+                    $files = array_map(fn ($file) => $file->getPathname(), iterator_to_array($finder));

47-48: Apply consistent null-safe logging.

-                        $this->logger->debug('No files found for parser class "'.$parserClass.'" in path "'.$path.'".');
+                        $this->logger?->debug('No files found for parser class "'.$parserClass.'" in path "'.$path.'".');
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 73e47b3 and 89a93ce.

⛔ Files ignored due to path filters (1)
  • composer.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • composer.json (1 hunks)
  • src/FileDetector/Collector.php (1 hunks)
  • src/Validator/AbstractValidator.php (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • composer.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Validator/AbstractValidator.php
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/FileDetector/Collector.php (4)
src/Parser/ParserRegistry.php (2)
  • ParserRegistry (7-18)
  • getAvailableParsers (12-17)
src/Validator/AbstractValidator.php (1)
  • __construct (16-18)
src/Parser/ParserInterface.php (2)
  • __construct (9-9)
  • getSupportedFileExtensions (14-14)
src/FileDetector/PrefixFileDetector.php (1)
  • mapTranslationSet (14-25)
🪛 PHPStan (2.1.15)
src/FileDetector/Collector.php

36-36: Call to function method_exists() with class-string<MoveElevator\ComposerTranslationValidator\Parser\ParserInterface> and 'getSupportedFileExt…' will always evaluate to true.
Because the type is coming from a PHPDoc, you can turn off this check by setting treatPhpDocTypesAsCertain: false in your %configurationFile%.

(function.alreadyNarrowedType)

🔇 Additional comments (1)
src/FileDetector/Collector.php (1)

23-23: Clarify or remove ReflectionException from documentation.

The method documentation indicates it can throw ReflectionException, but the current implementation doesn't use reflection directly. This might be left over from a previous implementation or anticipate future changes.

Please verify if ReflectionException is still relevant to this method's implementation or if it should be removed from the documentation.

Comment on lines +13 to +15
public function __construct(protected ?LoggerInterface $logger = null)
{
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add null check for logger usage.

The constructor accepts an optional logger but the class uses $this->logger without null checks, which could cause fatal errors.

Apply this pattern throughout the class:

-                $this->logger->error('The provided path "'.$path.'" is not a valid directory.');
+                $this->logger?->error('The provided path "'.$path.'" is not a valid directory.');

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/FileDetector/Collector.php around lines 13 to 15, the constructor accepts
an optional LoggerInterface which can be null, but the class uses $this->logger
without checking if it is null. To fix this, add null checks before using
$this->logger throughout the class to ensure it is not null before calling any
methods on it, preventing potential fatal errors.

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