Skip to content

Improved exclude file options #1208

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

Merged
merged 2 commits into from
Feb 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/filesystem/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
},
"minimum-stability": "stable",
"require": {
"php": "^8.1"
"php": "^8.1",
"webmozart/assert": "^1.3"
},
"extra": {
"branch-alias": {
Expand Down
72 changes: 72 additions & 0 deletions packages/filesystem/src/Finder/Exclude.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

declare(strict_types=1);

/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/

namespace phpDocumentor\FileSystem\Finder;

use phpDocumentor\FileSystem\Path;

use function array_map;
use function array_values;
use function str_starts_with;

final class Exclude
{
/** @var list<string> */
private readonly array $paths;

/** @param list<string|Path> $paths */
public function __construct(
array $paths = [],
private readonly bool $hidden = false,
private readonly bool $symlinks = false,
) {
$this->paths = array_values(
array_map(
static function (string|Path $path): string {
if (str_starts_with((string) $path, '/')) {
return (string) $path;
}

return '/' . $path;
},
$paths,
),
);
}

/** @return list<string> */
public function getPaths(): array
{
return $this->paths;
}

public function excludeHidden(): bool
{
return $this->hidden;
}

public function followSymlinks(): bool
{
return $this->symlinks;
}

/** @param list<string|path> $excludePaths */
public function withPaths(array $excludePaths): self
{
return new self(
$excludePaths,
$this->hidden,
$this->symlinks,
);
}
}
83 changes: 83 additions & 0 deletions packages/filesystem/src/Finder/SpecificationFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

declare(strict_types=1);

/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/

namespace phpDocumentor\FileSystem\Finder;

use Flyfinder\Path as FlyFinderPath;
use Flyfinder\Specification\Glob;
use Flyfinder\Specification\HasExtension;
use Flyfinder\Specification\InPath;
use Flyfinder\Specification\IsHidden;
use Flyfinder\Specification\NotSpecification;
use Flyfinder\Specification\SpecificationInterface;
use phpDocumentor\FileSystem\Path;

/**
* Factory class to build Specification used by FlyFinder when reading files to process.
*/
final class SpecificationFactory implements SpecificationFactoryInterface
{
/**
* Creates a SpecificationInterface object based on the ignore and extension parameters.
*
* @param list<string|Path> $paths
* @param list<string> $extensions
*/
public function create(array $paths, Exclude $ignore, array $extensions): SpecificationInterface
{
/** @var ?Glob $pathSpec */
$pathSpec = null;
foreach ($paths as $path) {
if ($path instanceof Path) {
$condition = new InPath(new FlyFinderPath((string) $path));
} else {
$condition = new Glob($path);
}

if ($pathSpec === null) {
$pathSpec = $condition;
continue;
}

$pathSpec = $pathSpec->orSpecification($condition);
}

/** @var ?Glob $ignoreSpec */
$ignoreSpec = null;
foreach ($ignore->getPaths() as $path) {
if ($ignoreSpec === null) {
$ignoreSpec = new Glob($path);
continue;
}

$ignoreSpec = $ignoreSpec->orSpecification(new Glob($path));
}

if ($ignore->excludeHidden()) {
$ignoreSpec = $ignoreSpec === null
? new IsHidden()
: $ignoreSpec->orSpecification(new IsHidden());
}

$result = new HasExtension($extensions);
if ($ignoreSpec !== null) {
$result = $result->andSpecification(new NotSpecification($ignoreSpec));
}

if ($pathSpec !== null) {
$result = $result->andSpecification($pathSpec);
}

return $result;
}
}
31 changes: 31 additions & 0 deletions packages/filesystem/src/Finder/SpecificationFactoryInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/

namespace phpDocumentor\FileSystem\Finder;

use Flyfinder\Specification\SpecificationInterface;
use phpDocumentor\FileSystem\Path;

/**
* Interface for Specifications used to filter the FileSystem.
*/
interface SpecificationFactoryInterface
{
/**
* Creates a SpecificationInterface object based on the ignore and extension parameters.
*
* @param list<string|Path> $paths
* @param list<string> $extensions
*/
public function create(array $paths, Exclude $ignore, array $extensions): SpecificationInterface;
}
90 changes: 90 additions & 0 deletions packages/filesystem/src/Path.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?php

declare(strict_types=1);

/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/

namespace phpDocumentor\FileSystem;

use Stringable;
use Webmozart\Assert\Assert;

use function array_pop;
use function ctype_alpha;
use function explode;
use function implode;
use function parse_url;
use function sprintf;
use function strlen;
use function strspn;

use const PHP_URL_SCHEME;

/**
* Value Object for paths.
* This can be absolute or relative.
*/
final class Path implements Stringable
{
/**
* Initializes the path.
*/
public function __construct(private readonly string $path)
{
Assert::notEmpty(
$path,
sprintf('"%s" is not a valid path', $path),
);
}

/**
* Verifies if another Path object has the same identity as this one.
*/
public function equals(self $otherPath): bool
{
return $this->path === (string) $otherPath;
}

/**
* returns a string representation of the path.
*/
public function __toString(): string
{
return $this->path;
}

/**
* Returns whether the file path is an absolute path.
*
* @param string $file A file path
*/
public static function isAbsolutePath(string $file): bool
{
return strspn($file, '/\\', 0, 1)
|| (strlen($file) > 3 && ctype_alpha($file[0])
&& $file[1] === ':'
&& strspn($file, '/\\', 2, 1)
)
|| parse_url($file, PHP_URL_SCHEME) !== null;
}

public static function dirname(Path $input): self
{
$parts = explode('/', (string) $input);
array_pop($parts);

$path = implode('/', $parts);
if ($path === '') {
return new self('/');
}

return new self($path);
}
}
Loading
Loading