Skip to content

Comments

Restore File::unique() helper#186

Open
damsfx wants to merge 8 commits intowintercms:developfrom
damsfx:develop
Open

Restore File::unique() helper#186
damsfx wants to merge 8 commits intowintercms:developfrom
damsfx:develop

Conversation

@damsfx
Copy link
Contributor

@damsfx damsfx commented Aug 1, 2024

Restore :

Summary by CodeRabbit

  • New Features
    • Added a new filename uniqueness utility that automatically generates unique names when duplicates are detected. The method appends sequential numbers to conflicting filenames using fully customizable separators (underscore by default) and starting indexes, providing flexible naming conventions. This ensures reliable handling of file naming conflicts.

damsfx added a commit to damsfx/winter that referenced this pull request Aug 1, 2024
@mjauvin mjauvin added this to the 1.2.7 milestone Nov 21, 2024
@mjauvin mjauvin modified the milestones: 1.2.7, 1.2.8 Dec 4, 2024
@mjauvin
Copy link
Member

mjauvin commented Dec 4, 2024

@damsfx you also need to add the method signature to the File facade (winter/storm/src/Support/Facades/File.php)

@damsfx
Copy link
Contributor Author

damsfx commented Dec 5, 2024

@damsfx you also need to add the method signature to the File facade (winter/storm/src/Support/Facades/File.php)

@mjauvin Just done it!
However, having done this on the develop branch was an error ... I'm always afraid in this case of not being in line with the winter repo.

@github-actions
Copy link

github-actions bot commented Feb 4, 2025

This pull request will be closed and archived in 3 days, as there has been no activity in the last 60 days.
If this is still being worked on, please respond and we will re-open this pull request.
If this pull request is critical to your business, consider joining the Premium Support Program where a Service Level Agreement is offered.

Comment on lines +236 to +237
* winter.txt, [winter_1.txt, winter_2.txt] -> winter_3.txt
* winter.txt, [winter_1.txt, winter_3.txt] -> winter_4.txt
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
* winter.txt, [winter_1.txt, winter_2.txt] -> winter_3.txt
* winter.txt, [winter_1.txt, winter_3.txt] -> winter_4.txt
* winter.txt, [winter.txt] -> winter_1.txt
* winter.txt, [winter.txt, winter_1.txt, winter_2.txt] -> winter_3.txt
* winter.txt, [winter.txt, winter_1.txt, winter_3.txt] -> winter_4.txt
* winter.txt, [winter_1.txt, winter_3.txt] -> winter.txt

@damsfx it should probably allow the input to be returned unmodified if it doesn't exist within the array.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@LukeTowers If the entry doesn't exist in the array, it must be returned with another index anyway, right?

Copy link
Member

Choose a reason for hiding this comment

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

@damsfx I'm making the suggestion that if it doesn't exist in the array it should be returned unmodified. The method is intended to give you a unique filename provided the input of a desired filename and a list of already existing options. If the filename provided is already unique, then there's no need to modify it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@LukeTowers So in a such case, the same filename is needed in the array of references to get the next incremented name.

Copy link
Member

Choose a reason for hiding this comment

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

The suggested change above should list all the cases that I think the method should need to handle, I've already updated the tests so it should just require a minor change in the method itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@LukeTowers Your modifications in tests goes to a failure for me.

// File already unique, return original
'winter.cms' => ['winter.cms', ['winter_1.cms']],

FAILED !
The function return wintert_2.cms in that case.

What I don't understand is why you want to return the unmodified value if it's unique.
In any case, we should return the incremented value! 🤯

(new Filesystem())->unique('winter.cms', ['test.cms']);   // winter_1.cms
(new Filesystem())->unique('winter.cms', []);             // winter_1.cms

Copy link
Member

Choose a reason for hiding this comment

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

@damsfx The tests are failing because the method doesn't currently have the logic to allow the name to be returned unmodified. The method claims to return a unique filename given the input of a desired filename and a list of existing filenames; why does it have to always return an incremented value if the desired filename isn't present?

Copy link
Contributor Author

@damsfx damsfx Feb 17, 2025

Choose a reason for hiding this comment

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

@LukeTowers Apply a unique index to a filename from provided list ...
Maybe we should then say Apply a unique index to a filename from provided list ONLY if it is found in the list ... or change the name of the method which is perhaps the source of the confusion.

In any case, the current function respects the logic of the original one:
68bb7ac

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@LukeTowers Do we continue with this PR in draft?

@bennothommo bennothommo added maintenance PRs that fix bugs, are translation changes or make only minor changes and removed Type: Enhancement labels Mar 28, 2025
@wintercms wintercms deleted a comment from github-actions bot Jul 29, 2025
@wintercms wintercms deleted a comment from github-actions bot Jul 29, 2025
@github-actions
Copy link

This pull request will be closed and archived in 3 days, as there has been no activity in the last 60 days.
If this is still being worked on, please respond and we will re-open this pull request.
If this pull request is critical to your business, consider joining the Premium Support Program where a Service Level Agreement is offered.

@github-actions
Copy link

This pull request will be closed and archived in 3 days, as there has been no activity in the last 60 days.
If this is still being worked on, please respond and we will re-open this pull request.
If this pull request is critical to your business, consider joining the Premium Support Program where a Service Level Agreement is offered.

@github-actions
Copy link

This pull request will be closed and archived in 3 days, as there has been no activity in the last 60 days.
If this is still being worked on, please respond and we will re-open this pull request.
If this pull request is critical to your business, consider joining the Premium Support Program where a Service Level Agreement is offered.

@coderabbitai
Copy link

coderabbitai bot commented Feb 19, 2026

Walkthrough

A new unique() method was added to the Filesystem class that generates unique filenames by appending incremented numeric suffixes. The method validates input, scans existing files for name conflicts, and returns the next available filename. The method is exposed via the File facade and includes comprehensive test coverage.

Changes

Cohort / File(s) Summary
Unique Filename Generation
src/Filesystem/Filesystem.php, src/Support/Facades/File.php, tests/Filesystem/FilesystemTest.php
Implemented unique() method to generate unique filenames with configurable separators and numeric increments. Method parses filenames, scans provided lists for conflicts, determines next available index, and returns modified filename. Facade PHPDoc declaration added for IDE support. Test suite covers existing conflicts, custom separators, and multiple sequential conflicts scenarios.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately describes the main change: restoring a previously removed File::unique() helper method across the Filesystem class, facade, and tests.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


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

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

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

249-254: Unanchored regex and missing preg_quote can produce silent false matches.

The pattern is assembled from raw user-supplied strings without anchors or escaping:

'/' . $info['filename'] . $separator . '(\d*)\.' . $info['extension'] . '/'

Two concrete problems:

  1. No anchors/winter_(\d*)\.cms/ matches superwinter_1.cms (substring match), silently adding a false index to the pool and inflating the returned index.
  2. No preg_quote — if $info['filename'] contains a dot (e.g., my.archive.tar.gzfilename = my.archive.tar) or $separator is a regex metacharacter (., +, etc.), the generated pattern is malformed and matches unintended strings.
  3. \d* vs \d+ — zero-or-more matches the empty group in winter_.cms, yielding index 0. \d+ is a tighter and more intentional constraint.
🔧 Proposed fix
-        if (!preg_match('/' . $info['filename'] . $separator . '(\d*)\.' . $info['extension'] . '/', $item, $matches)) {
+        if (!preg_match('/^' . preg_quote($info['filename'], '/') . preg_quote($separator, '/') . '(\d+)\.' . preg_quote($info['extension'], '/') . '$/', $item, $matches)) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Filesystem/Filesystem.php` around lines 249 - 254, The regex built in the
foreach loop that checks $list items uses unescaped user strings and lacks
anchors and a proper digit quantifier, causing false matches; update the pattern
used around the block that references $info['filename'], $separator, and
$info['extension'] to preg_quote the filename, separator and extension, anchor
the pattern with ^ and $ (or use start/end delimiters), and change (\d*) to
(\d+) so only one-or-more digits are captured before casting to int and
appending to $indexes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/Filesystem/FilesystemTest.php`:
- Around line 18-29: The test expectation for the case keyed 'winter.cms' is
inconsistent with the unique() implementation: update the expected result from
'winter.cms' to 'winter_2.cms' in the test row "'winter.cms' => ['winter.cms',
['winter_1.cms']]" (and update its inline comment) so it matches the unique()
behavior (which parses indexes like winter_1.cms and returns the next indexed
name); alternatively, if you intend unique() to return the original when not
present, change the unique() implementation to early-return $str in the unique()
function instead—but the quick fix is to change the test expectation and comment
to 'winter_2.cms'.
- Line 16: The line defining the $cases array is indented with 6 spaces and
fails the code-quality rule expecting at least 8 spaces; update the indentation
of the $cases declaration to match the rest of the method body (use 8 spaces) so
it aligns with surrounding statements in the same function (look for the $cases
variable in the test method in FilesystemTest and adjust its leading spaces
accordingly).

---

Nitpick comments:
In `@src/Filesystem/Filesystem.php`:
- Around line 249-254: The regex built in the foreach loop that checks $list
items uses unescaped user strings and lacks anchors and a proper digit
quantifier, causing false matches; update the pattern used around the block that
references $info['filename'], $separator, and $info['extension'] to preg_quote
the filename, separator and extension, anchor the pattern with ^ and $ (or use
start/end delimiters), and change (\d*) to (\d+) so only one-or-more digits are
captured before casting to int and appending to $indexes.


public function testUnique()
{
$cases = [
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix indentation — confirmed code-quality pipeline failure.

Line 16 uses 6 spaces; the pipeline reports expected at least 8 spaces. Align with the rest of the method body.

🔧 Proposed fix
-      $cases = [
+        $cases = [
📝 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
$cases = [
$cases = [
🧰 Tools
🪛 GitHub Actions: Code Quality

[error] 16-16: ERR: Line indented incorrectly; expected at least 8 spaces

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/Filesystem/FilesystemTest.php` at line 16, The line defining the $cases
array is indented with 6 spaces and fails the code-quality rule expecting at
least 8 spaces; update the indentation of the $cases declaration to match the
rest of the method body (use 8 spaces) so it aligns with surrounding statements
in the same function (look for the $cases variable in the test method in
FilesystemTest and adjust its leading spaces accordingly).

Comment on lines +18 to +29
'winter_1.cms' => ['winter.cms', ['winter.cms', 'test_5']],

// File already unique, return original
'winter.cms' => ['winter.cms', ['winter_1.cms']],

// Last index available is incremented
'winter_4.cms' => ['winter.cms', ['winter_1.cms', 'test_5', 'winter_3.cms']],
'winter_98.cms' => ['winter.cms', ['winter_97.cms', 'test_5', 'winter_1.cms']],

// Separator as space
'winter 1.cms' => ['winter.cms', ['winter_1.cms', 'test_5', 'winter_3.cms'], ' '],
];
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Test case B's expected value contradicts the implementation and causes the confirmed pipeline failure (line 31).

The failing assertion is case B:

'winter.cms' => ['winter.cms', ['winter_1.cms']],  // expected: 'winter.cms'

which calls unique('winter.cms', ['winter_1.cms']).

Tracing through the implementation:

  • Pattern /winter_(\d*)\.cms/ matches winter_1.cms$indexes = [1]
  • max([1]) + 1 = 2 → returns winter_2.cms
  • Test asserts winter.cmsFAIL ✓ (confirmed by pipeline)

Cases B and C have the same precondition (winter.cms absent from the list) but opposite expectations:

Case $list Expected
B ['winter_1.cms'] winter.cms
C ['winter_1.cms', 'test_5', 'winter_3.cms'] winter_4.cms

There is no single consistent rule that produces both results. The docblock examples (winter.txt, [winter_1.txt, winter_2.txt] → winter_3.txt) match the current implementation (always return an indexed name when indexed variants exist), not case B's expectation.

Most likely fix: case B's expected value should be winter_2.cms and its comment should be revised:

🔧 Proposed fix
-            // File already unique, return original
-            'winter.cms' => ['winter.cms', ['winter_1.cms']],
+            // Only one indexed variant exists; next index is returned
+            'winter_2.cms' => ['winter.cms', ['winter_1.cms']],

If instead the intent is that unique() returns $str unchanged when $str is not in $list, then the implementation needs an early-return guard and cases C and D (where winter.cms is also absent from the list yet an indexed name is expected) would need to be revised or replaced. Please cross-check against the original commits referenced in the PR description (68bb7ac and 76a4302) to confirm the intended algorithm.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/Filesystem/FilesystemTest.php` around lines 18 - 29, The test
expectation for the case keyed 'winter.cms' is inconsistent with the unique()
implementation: update the expected result from 'winter.cms' to 'winter_2.cms'
in the test row "'winter.cms' => ['winter.cms', ['winter_1.cms']]" (and update
its inline comment) so it matches the unique() behavior (which parses indexes
like winter_1.cms and returns the next indexed name); alternatively, if you
intend unique() to return the original when not present, change the unique()
implementation to early-return $str in the unique() function instead—but the
quick fix is to change the test expectation and comment to 'winter_2.cms'.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

maintenance PRs that fix bugs, are translation changes or make only minor changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants