-
-
Notifications
You must be signed in to change notification settings - Fork 77
Files/FileList: adding the same file twice should not increment FileList::$numFiles
#1150
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Files/FileList: adding the same file twice should not increment FileList::$numFiles
#1150
Conversation
0b0430a
to
befb62b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @rodrigoprimo, nice work!
I've left some small comments and questions in various places, nothing really blocking.
Only real question I have is with regards to the solution applied in the third commit versus the suggestion I left in #1113 (comment)
I've now started wondering if
$numFiles
shouldn't be set just once at the end of the__construct()
method via acount($this->files)
instead of doing the incrementing whenever a new file is being added... ? I'd expect that would solve the issue too.
While I can see that, as the addFile()
method is public
, this wouldn't solve anything for addFile()
when not called from the constructor, I do imagine the above suggestion could simplify the code change for the constructor itself.
Was there a particular reason why you went with a different solution ?
/** | ||
* Tests for the \PHP_CodeSniffer\Files\FileList::addFile method. | ||
* | ||
* @copyright 2019-2025 PHPCSStandards Contributors |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where does the "2019-2025" come from ? (here and elsewhere)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I copied the line @copyright 2019-2024 PHPCSStandards Contributors
from another file and, without thinking too much about it, updated it to 2025 since this is the current year.
It is not clear to me if the years apply to just this file or to the whole repository. If it is just this file, should it be?
@copyright 2025 PHPCSStandards Contributors
I can see that phpDocumentor leaves the format of the description of the @copyright
tag to each individual project (https://docs.phpdoc.org/guide/references/phpdoc/tags/copyright.html). Maybe this is documented somewhere in this repo, and I missed it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is not clear to me if the years apply to just this file or to the whole repository. If it is just this file, should it be?
Well, it's a bit messy in this repository, so generally speaking, you can assume it applies to the specific file. I still intend to look into streamlining this at some point, but there may be legal implications of doing that, so something I want to be careful with.
@copyright 2025 PHPCSStandards Contributors
is what I would have expected for this new file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. I opted to make this change in two separate commits (one for ConstructTest.php and FilterDouble.php, and another for AddFileTest.php) to hopefully make it easier when squashing these changes into the original commits.
|
||
$this->assertCount(count($expectedFiles), $fileList, 'File count mismatch'); | ||
|
||
$i = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the $i
have value in the error messages ? I mean, the array is being sorted by key anyway and the $expectedFiles
does not have explicit keys.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My idea is that it would help identify which file caused the assertion to fail by providing the index of the corresponding value in the $expectedFiles
array. But I can see that this is not very straightforward. Perhaps it would be better to modify the error message to include the $filePath
instead of $i
??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like that idea. Sounds more descriptive to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
/** | ||
* Setup objects needed for the tests. | ||
* | ||
* @before |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@before
or could/should this be @beforeClass
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a vague memory of us discussing that creating the Ruleset
class in the tests has a performance impact. That is why I went with the mock of this class. I don't think I'm able to create a mock of Ruleset
if I use @beforeClass
.
That being said, I'm not sure what is better: creating an instance of ConfigDouble
and a mock of Ruleset
for every single test, or creating an instance of ConfigDouble
and an instance of Ruleset
once for all the tests.
Is this more or less what you had in mind with this question?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Considering you need both for the data provider anyway.... I imagine there is some optimization which can now be applied....
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not entirely sure if I understand what you're hinting at, but I gave it a try and added two new commits (one for ConstructTest.php and another for AddFileTest.php), making both test classes use an initializeConfigAndRuleset()
method that uses the @beforeClass
tag.
For AddFileTest
, the Ruleset
class is still instantiated twice, as initializeConfigAndRuleset()
needs to be called directly inside dataAddFile()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I was hinting at was that both the a beforeClass
method and the data provider need to be static methods - and need to write to static properties, so the beforeClass
method could check if the properties had been initialized and if not, could create the objects and the data provider could call the beforeClass
method to do the same and then in both the tests as well as the data provider, the statically stored objects can be re-used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the additional details, Juliette! I have now implemented this change.
'STDIN' => ['fileName' => 'STDIN'], | ||
'Regular file with file object' => [ | ||
'fileName' => 'test1.php', | ||
'fileObject' => new stdClass(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should create a File
object, not a stdClass
object. As things are, this will break in PHPCS 4.0 when the type declarations are added (addFile(string $path, ?File $file=null)
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! I added a commit creating an instance of the File
class. To do that, I had to instantiate both ConfigDouble
and Ruleset
. Let me know if you had a different approach in mind.
4b3b6ec
to
78ab8ac
Compare
Thanks for your review, @jrfnl!
I did consider your suggestion, but noticed that it did not apply to Besides that, I addressed all the other comments. Could you please take another look when you get a chance? Also, before merging this PR, let me know if you prefer to squash the commits yourself or if you would like me to do it. |
78ab8ac
to
d958137
Compare
Rebased without changes to fix the failing |
@rodrigoprimo As I'm going to need to look at the complete thing again before merging this, feel free to reorganize the commits already when you make your final changes. |
This is just an initial set of tests. It does not fully cover the FileList::__construct() method.
This is just an initial set of tests. It does not fully cover the FileList::addFile() method.
…List::$numFiles` This commit fixes the `FileList::__construct()` and `FileList::addFile()` methods to ensure that, when there is an attempt to add the same file twice, the `FileList::$numFiles` variable is not incremented. The code was already handling duplicates correctly in the sense that duplicated files were not added to `FileList::$files`. However, `FileList::$numFiles` was being incorrectly incremented. There is some duplicated logic in `FileList::__construct()` and `FileList::addFile()`. I considered refactoring the duplicated code to a private method before adding the check that is added in this commit. I decided not to do it as there are some subtle differences between the logic in the two methods. `FileList::__construct()` always sets the value of an entry in the `FileList::$files` to `null` and the key to whatever is returned by `SplFileInfo::getPathname()`. While `FileList::addFile()` sets the value of an entry in the `FileList::$files` to `null` or an object passed to the method and the key to the path passed to the method. I decided to leave this refactor to remove the duplication to the future and focus this commit on fixing the issue with handling duplicated files.
185e384
to
5165510
Compare
Done, this PR is now ready for a final review. Thanks. |
Description
This PR fixes the
FileList::__construct()
andFileList::addFile()
methods to ensure that, when there is an attempt to add the same file twice, theFileList::$numFiles
variable is not incremented. This behavior was causing a fatal error when using the--parallel
option and passing the same file twice (see #1113).The code was already handling duplicates correctly in the sense that duplicated files were not added to
FileList::$files
. However,FileList::$numFiles
was being incorrectly incremented.This PR also includes some basic tests for
FileList::__construct()
andFileList::addFile()
in separate commits.Suggested changelog entry
Fixed: fatal error when using the
--parallel
option and passing the same file twiceRelated issues/external references
Fixes #1113
Additional notes
There is some duplicated logic in
FileList::__construct()
andFileList::addFile()
. I considered refactoring the duplicated code to a private method before adding the check that is added in this commit. I decided not to do it as there are some subtle differences between the logic in the two methods that make the refactor not straightforward.FileList::__construct()
always sets the value of an entry in theFileList::$files
tonull
and the key to whatever is returned bySplFileInfo::getPathname()
. WhileFileList::addFile()
sets the value of an entry in theFileList::$files
tonull
or an object passed to the method, and the key to the path passed to the method.I decided to leave this refactor to remove the duplication to the future and focus this commit on fixing the issue with handling duplicated files. I believe it will be necessary to write additional tests for this class and also incorporate more defensive code.
Types of changes
PR checklist