Skip to content

Use sebastian/file-filter in SourceFilter::includes() for issue trigger identification#6535

Draft
sebastianbergmann wants to merge 5 commits intomainfrom
issue-6519/file-filter
Draft

Use sebastian/file-filter in SourceFilter::includes() for issue trigger identification#6535
sebastianbergmann wants to merge 5 commits intomainfrom
issue-6519/file-filter

Conversation

@sebastianbergmann
Copy link
Owner

@sebastianbergmann sebastianbergmann commented Mar 8, 2026

FileFilterMapper::map() maps a PHPUnit\TextUI\Configuration\Source object to a SebastianBergmann\FileFilter\Filter object. SebastianBergmann\FileFilter\Filter\SourceFilter now uses this SebastianBergmann\FileFilter\Filter object.

Backward Compatibility

The changes proposed here mostly preserve backward compatibility. The following sections describe backward compatibility breaks in detail.

  • File and directory inclusion/exclusion semantics are preserved
  • Prefix and suffix filtering works identically
  • Explicit file includes are still overridden by directory excludes (same precedence as old implementation)
  • The includes() method signature and return type are unchanged for all callers
  • Code coverage filtering is unaffected (still uses SourceMapper::mapForCodeCoverage())
  • All tests for SourceFilter pass, including the additional tests that were implemented on main while working on these changes

I think the backward compatibility breaks discussed below are acceptable, even in a minor version such as PHPUnit 13.x, considering the performance improvement.

The only way to improve performance of issue trigger identification I see is implemented by these changes. We could keep the old implementation, introduce the new implementation as an alternative that can be opted-in using the XML configuration file. In the future, we could then deprecate the old implementation and later remove it. However, I want to avoid having to maintain two implementations.

Symlinks

The old implementation resolved symlinks and relative paths via realpath(). The new implementation does string-based matching only.

Hidden directories

The old implementation excluded hidden directories as a side effect of SebastianBergmann\FileIterator\Facade skipping hidden directories during filesystem traversal. This only happened on non-Windows platforms.

The new implementation excludes hidden directories on all platforms, including Windows. This is a minor behavioral change that only affects Windows users who have source code in hidden directories. The new behavior is arguably more consistent across platforms.

@sebastianbergmann sebastianbergmann self-assigned this Mar 8, 2026
@sebastianbergmann sebastianbergmann added type/backward-compatibility Something will be/is intentionally broken type/performance Issues related to resource consumption (time and memory) feature/issues Issues related to handling of `E_*` emitted by the PHP runtime and `E_USER_*` emitted in PHP code labels Mar 8, 2026
@sebastianbergmann sebastianbergmann changed the title Replace SourceFilter::includes() and SourceMapper::map() with sebastian/file-filter for issue trigger identification Use sebastian/file-filter in SourceFilter::includes() for issue trigger identification Mar 8, 2026
@codecov
Copy link

codecov bot commented Mar 8, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.96%. Comparing base (7d26bdd) to head (c9821a4).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff            @@
##               main    #6535   +/-   ##
=========================================
  Coverage     95.95%   95.96%           
- Complexity     7513     7520    +7     
=========================================
  Files           815      816    +1     
  Lines         23202    23225   +23     
=========================================
+ Hits          22264    22287   +23     
  Misses          938      938           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

public function map(Source $source): FileFilter
{
return (new FilterBuilder)->build(
$this->directories($source->includeDirectories()),
Copy link
Contributor

@dantleech dantleech Mar 8, 2026

Choose a reason for hiding this comment

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

it seems the include directories are not "normalized" - when the config is in a sub-drectory (e.g. config/phpunit.xml):

    <source ignoreSelfDeprecations="true">
        <include>
            <directory suffix=".php">../src/</directory>
        </include>
    </source>

Results in the regex:

#^/home/daniel/www/dantleech/phpunit\-6111/config/\.\./src/(?:/|$)#

But the path needs to be normalized from phpunit-6111/config/../src tophpunit-6111/src in order for it to match the source paths.

(see Symfony's Path component for an example)

Copy link
Owner Author

Choose a reason for hiding this comment

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

Good catch, should be handled by c9821a4.

When phpunit.xml is in a subdirectory and uses relative paths with ".." (e.g. ../src/), the path was passed to the file filter without canonicalization, causing the generated regex to include literal ".." segments that never match resolved file paths.
Apply realpath() in FileFilterMapper to normalize directory and file paths before building the filter.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature/issues Issues related to handling of `E_*` emitted by the PHP runtime and `E_USER_*` emitted in PHP code type/backward-compatibility Something will be/is intentionally broken type/performance Issues related to resource consumption (time and memory)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants