Skip to content

Commit e74a8e7

Browse files
committed
Rewrite ignore patterns to support globs
1 parent 1d30e2f commit e74a8e7

7 files changed

Lines changed: 247 additions & 60 deletions

File tree

composer.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@
3030
"ext-simplexml": "*",
3131
"lib-libxml": "*",
3232
"yahnis-elsts/plugin-update-checker": "^5.6",
33-
"masterminds/html5": "^2.9"
33+
"masterminds/html5": "^2.9",
34+
"phlak/splat": "^5.0",
35+
"symfony/finder": "=6.0.3"
3436
},
3537
"require-dev": {
3638
"thecodingmachine/phpstan-strict-rules": "*",

composer.lock

Lines changed: 121 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

flake.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
let rel = baseNameOf path;
2323
in rel == "composer.json" || rel == "composer.lock";
2424
};
25-
vendorHash = "sha256-4DTiW/rykJGZAnWCRSqlAaBtDthXKkZshPNe3yYCfA4=";
25+
vendorHash = "sha256-dIGRVRFG7Rc1HhN0gCiYm1DgHsZAmc1KP490A26Gj2A=";
2626
});
2727
wp2static = runCommand "wp2static" {} ''
2828
export PLUGIN_DIR="$TMPDIR/${name}"

src/CoreOptions.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ public static function optionSpecs() : array {
297297
'filenamesToIgnore',
298298
'1',
299299
'Directory and File Names to Ignore',
300-
'Directories and files with these names will be ignored.',
300+
'Directories and files with these names will be ignored. Glob syntax is supported via <a href="https://github.com/PHLAK/Splat">Splat</a>.',
301301
implode(
302302
"\n",
303303
[

src/FileFiltering.php

Lines changed: 30 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -2,41 +2,50 @@
22

33
namespace WP2Static;
44

5+
use PHLAK\Splat\Anchors;
6+
use PHLAK\Splat\Pattern;
57
use WP2Static\CoreOptions;
8+
use WP2Static\FileIgnorePattern;
69
use WP2Static\SiteInfo;
710

811
class FileFiltering {
912

1013
/**
11-
* @var array<string>
12-
* File and directory names to ignore
14+
* @var array<FileIgnorePattern>
15+
* Files and directories to ignore
1316
*/
14-
private $filenames_to_ignore;
15-
16-
/**
17-
* @var array<string>
18-
* File extensions to ignore
19-
*/
20-
private $file_extensions_to_ignore;
17+
private $patterns_to_ignore;
2118

2219
public function __construct() {
20+
$this->patterns_to_ignore = [];
21+
2322
$filenames_to_ignore = CoreOptions::getLineDelimitedBlobValue( 'filenamesToIgnore' );
2423

25-
$this->filenames_to_ignore =
24+
$filenames_to_ignore =
2625
apply_filters(
2726
'wp2static_filenames_to_ignore',
2827
$filenames_to_ignore
2928
);
3029

30+
foreach ( $filenames_to_ignore as $filename ) {
31+
$this->patterns_to_ignore[] = new FileIgnorePattern( $filename );
32+
}
33+
3134
$file_extensions_to_ignore = CoreOptions::getLineDelimitedBlobValue(
3235
'fileExtensionsToIgnore'
3336
);
3437

35-
$this->file_extensions_to_ignore =
38+
$file_extensions_to_ignore =
3639
apply_filters(
3740
'wp2static_file_extensions_to_ignore',
3841
$file_extensions_to_ignore
3942
);
43+
44+
foreach ( $file_extensions_to_ignore as $extension ) {
45+
$this->patterns_to_ignore[] = new FileIgnorePattern(
46+
"**$extension"
47+
);
48+
}
4049
}
4150

4251
/**
@@ -49,6 +58,8 @@ public function __construct() {
4958
public function crawlableFiles(
5059
string $directory,
5160
) : \Iterator {
61+
$abs_base_dir = ( new \SplFileInfo( $directory ) )->getPathname();
62+
5263
$dir_iter = new \RecursiveDirectoryIterator(
5364
$directory,
5465
\RecursiveDirectoryIterator::SKIP_DOTS,
@@ -59,32 +70,14 @@ public function crawlableFiles(
5970
// blocked directories.
6071
$filter_iter = new \RecursiveCallbackFilterIterator(
6172
$dir_iter,
62-
function ( $current, $key, $iterator ) {
63-
$filename = $current->getFilename();
64-
73+
function ( $current, $key, $iterator ) use ( $abs_base_dir ) {
6574
// Filter out both directories and files
66-
foreach ( $this->filenames_to_ignore as $filename_to_ignore ) {
67-
if ( $filename === $filename_to_ignore ) {
75+
foreach ( $this->patterns_to_ignore as $pattern ) {
76+
if ( $pattern->matches( $abs_base_dir, $current ) ) {
6877
return false;
6978
}
7079
}
7180

72-
// Filter only files
73-
if ( $current->isFile() ) {
74-
/*
75-
Prepare the file extension list for regex:
76-
- Add prepending (escaped) \ for a literal . at the start of
77-
the file extension
78-
- Add $ at the end to match end of string
79-
- Add i modifier for case insensitivity
80-
*/
81-
foreach ( $this->file_extensions_to_ignore as $extension ) {
82-
if ( preg_match( "/\\{$extension}$/i", $filename ) ) {
83-
return false;
84-
}
85-
}
86-
}
87-
8881
return true;
8982
}
9083
);
@@ -118,33 +111,14 @@ public function getListOfLocalFilesByDir(
118111
}
119112

120113
/**
121-
* Ensure a given filepath has an allowed filename and extension.
122-
*
123-
* @param string $file_name
124-
* @return bool True if the given file does not have a disallowed filename
125-
* or extension.
114+
* @param string $path
115+
* @return bool True if the given path does not match an ignore pattern
126116
*/
127117
public function pathLooksCrawlable(
128-
string $file_name,
118+
string $path,
129119
) : bool {
130-
$filename_matches = 0;
131-
132-
str_ireplace( $this->filenames_to_ignore, '', $file_name, $filename_matches );
133-
134-
// If we found matches we don't need to go any further
135-
if ( $filename_matches ) {
136-
return false;
137-
}
138-
139-
/*
140-
Prepare the file extension list for regex:
141-
- Add prepending (escaped) \ for a literal . at the start of
142-
the file extension
143-
- Add $ at the end to match end of string
144-
- Add i modifier for case insensitivity
145-
*/
146-
foreach ( $this->file_extensions_to_ignore as $extension ) {
147-
if ( preg_match( "/\\{$extension}$/i", $file_name ) ) {
120+
foreach ( $this->patterns_to_ignore as $pattern ) {
121+
if ( $pattern->matchesPath( $path ) ) {
148122
return false;
149123
}
150124
}

src/FileIgnorePattern.php

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
namespace WP2Static;
4+
5+
use PHLAK\Splat\Pattern;
6+
use WP2Static\Utils;
7+
use WP2Static\WsLog;
8+
9+
class FileIgnorePattern {
10+
/**
11+
* @var bool
12+
* Match only directories and not files.
13+
*/
14+
private $only_directories;
15+
16+
/**
17+
* @var string
18+
* Regex tested against file and directory paths.
19+
*/
20+
private $regex;
21+
22+
/**
23+
* Following gitignore rules:
24+
* - Patterns ending in / match only directories.
25+
* - Patterns with a / at the start or middle match
26+
* from the site root only.
27+
* - Other patterns can match starting from any subdirectory.
28+
*
29+
* Splat handles the rest of the logic of turning glob
30+
* patterns into regexes.
31+
*/
32+
public function __construct(
33+
string $pattern,
34+
) {
35+
if ( substr( $pattern, -1 ) === '/' ) {
36+
$this->only_directories = true;
37+
$pattern = substr( $pattern, 0, -1 );
38+
} else {
39+
$this->only_directories = false;
40+
}
41+
42+
if ( strpos( $pattern, '/' ) === false ) {
43+
$pattern = '**/' . $pattern;
44+
} else if ( substr( $pattern, 0, 1 ) !== '/' ) {
45+
$pattern = '/' . $pattern;
46+
}
47+
48+
$regex = Pattern::make( $pattern )->toRegex( Pattern::BOTH_ANCHORS );
49+
// Make it case-insensitive
50+
$this->regex = $regex . 'i';
51+
52+
//WsLog::l("Created regex $this->regex for pattern $pattern");
53+
}
54+
55+
public function matches(
56+
string $abs_base_dir,
57+
\SplFileInfo $file,
58+
) : bool {
59+
if ( $this->only_directories && !$file->isDir() ) {
60+
return false;
61+
}
62+
63+
$path = $file->getPathname();
64+
$path = Utils::str_replace_first( $abs_base_dir, '', $path );
65+
66+
return self::matchesPath( $path );
67+
}
68+
69+
public function matchesPath(
70+
string $path,
71+
) : bool {
72+
if ( preg_match( $this->regex, $path ) ) {
73+
//WsLog::l(
74+
// "Ignoring $path with regex $this->regex"
75+
//);
76+
return true;
77+
}
78+
79+
return false;
80+
}
81+
}

0 commit comments

Comments
 (0)