Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
ExportIgnoreFilter
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
2 / 2
6
100.00% covered (success)
100.00%
1 / 1
 filter
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
3
 normalizePath
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3declare(strict_types=1);
4
5/**
6 * Fast Forward Development Tools for PHP projects.
7 *
8 * This file is part of fast-forward/dev-tools project.
9 *
10 * @author   Felipe SayĆ£o Lobato Abreu <github@mentordosnerds.com>
11 * @license  https://opensource.org/licenses/MIT MIT License
12 *
13 * @see      https://github.com/php-fast-forward/
14 * @see      https://github.com/php-fast-forward/dev-tools
15 * @see      https://github.com/php-fast-forward/dev-tools/issues
16 * @see      https://php-fast-forward.github.io/dev-tools/
17 * @see      https://datatracker.ietf.org/doc/html/rfc2119
18 */
19
20namespace FastForward\DevTools\GitAttributes;
21
22use function Safe\preg_replace;
23
24/**
25 * Filters export-ignore candidates using normalized path comparisons.
26 *
27 * This filter SHALL compare configured keep-in-export paths against canonical
28 * candidates while ignoring leading and trailing slash differences. It MUST
29 * preserve the original candidate ordering in the filtered result.
30 */
31 class ExportIgnoreFilter implements ExportIgnoreFilterInterface
32{
33    /**
34     * Filters export-ignore candidates using the configured keep-in-export paths.
35     *
36     * @param list<string> $candidates the canonical candidate paths
37     * @param list<string> $keepInExportPaths the paths that MUST remain exportable
38     *
39     * @return list<string> the filtered export-ignore candidates
40     */
41    public function filter(array $candidates, array $keepInExportPaths): array
42    {
43        $keptPathLookup = [];
44
45        foreach ($keepInExportPaths as $path) {
46            $normalizedPath = $this->normalizePath($path);
47
48            if ('' === $normalizedPath) {
49                continue;
50            }
51
52            $keptPathLookup[$normalizedPath] = true;
53        }
54
55        return array_values(array_filter(
56            $candidates,
57            fn(string $candidate): bool => ! isset($keptPathLookup[$this->normalizePath($candidate)])
58        ));
59    }
60
61    /**
62     * Normalizes a configured path for stable matching.
63     *
64     * @param string $path the raw path from candidates or Composer extra config
65     *
66     * @return string the normalized path used for comparisons
67     */
68    private function normalizePath(string $path): string
69    {
70        $trimmedPath = trim($path);
71
72        if ('' === $trimmedPath) {
73            return '';
74        }
75
76        $normalizedPath = preg_replace('#/+#', '/', '/' . ltrim($trimmedPath, '/')) ?? $trimmedPath;
77
78        return '/' === $normalizedPath ? $normalizedPath : rtrim($normalizedPath, '/');
79    }
80}