From 92453ab3ed6d8212e4e74a2dcef662f50968744d Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Mon, 13 Jul 2026 15:02:27 +0200 Subject: [PATCH 01/11] Require wp-cli/wp-cli v2.13 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index da4eba6..3321246 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ }, "require": { "php": ">=7.2.24", - "wp-cli/wp-cli": "^2.12" + "wp-cli/wp-cli": "^2.13" }, "require-dev": { "wp-cli/wp-cli-tests": "^5.0.0" From 5b758806be7a2c1ac2f8e19b7031641ef53b0b0a Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Mon, 13 Jul 2026 15:40:07 +0200 Subject: [PATCH 02/11] Prefix aliases for back compat --- src/Find_Command.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Find_Command.php b/src/Find_Command.php index 02ef100..1f9b840 100644 --- a/src/Find_Command.php +++ b/src/Find_Command.php @@ -258,7 +258,7 @@ private function recurse_directory( $path ) { && file_exists( $path . 'version.php' ) ) { $version_path = $path . 'version.php'; $wp_path = substr( $path, 0, -13 ); - $alias = isset( $this->resolved_aliases[ $wp_path ] ) ? $this->resolved_aliases[ $wp_path ] : ''; + $alias = isset( $this->resolved_aliases[ $wp_path ] ) ? '@' . $this->resolved_aliases[ $wp_path ] : ''; $wp_path = rtrim( $wp_path, '/' ) . '/'; $this->found_wp[ $version_path ] = [ From 96b98917347b92adf1de62ddb9b49bd4a30ad218 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Mon, 13 Jul 2026 17:30:05 +0200 Subject: [PATCH 03/11] Normalize paths throughout --- features/find.feature | 14 +++++++------- src/Find_Command.php | 20 +++++++++++++------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/features/find.feature b/features/find.feature index ae50d10..d9570f2 100644 --- a/features/find.feature +++ b/features/find.feature @@ -4,7 +4,7 @@ Feature: Find WordPress installs on the filesystem Given a WP install in 'subdir1' And a WP install in 'subdir2' - When I run `wp eval --skip-wordpress 'echo realpath( getenv( "RUN_DIR" ) );'` + When I run `wp eval --skip-wordpress "echo WP_CLI\Path::normalize( realpath( getenv( 'RUN_DIR' ) ) );"` Then save STDOUT as {TEST_DIR} When I run `wp find {TEST_DIR} --field=version_path | sort` @@ -35,7 +35,7 @@ Feature: Find WordPress installs on the filesystem And a WP install in 'cache' And a WP install in 'tmp' - When I run `wp eval --skip-wordpress 'echo realpath( getenv( "RUN_DIR" ) );'` + When I run `wp eval --skip-wordpress "echo WP_CLI\Path::normalize( realpath( getenv( 'RUN_DIR' ) ) );"` Then save STDOUT as {TEST_DIR} When I run `wp find {TEST_DIR} --field=version_path --verbose` @@ -74,7 +74,7 @@ Feature: Find WordPress installs on the filesystem And a WP install in 'sub/subdir2' And a WP install in 'sub/sub/subdir3' - When I run `wp eval --skip-wordpress 'echo realpath( getenv( "RUN_DIR" ) );'` + When I run `wp eval --skip-wordpress "echo WP_CLI\Path::normalize( realpath( getenv( 'RUN_DIR' ) ) );"` Then save STDOUT as {TEST_DIR} When I run `wp find {TEST_DIR} --verbose` @@ -166,7 +166,7 @@ Feature: Find WordPress installs on the filesystem Given a WP install in 'subdir1' And a WP install in 'subdir2' - When I run `wp eval --skip-wordpress 'echo realpath( getenv( "RUN_DIR" ) );'` + When I run `wp eval --skip-wordpress "echo WP_CLI\Path::normalize( realpath( getenv( 'RUN_DIR' ) ) );"` Then save STDOUT as {TEST_DIR} When I run `echo "@test1:\n path: {TEST_DIR}/subdir2" > wp-cli.yml` @@ -184,7 +184,7 @@ Feature: Find WordPress installs on the filesystem And I run `mkdir -p subdir2/.svn` And a WP install in 'subdir2/.svn/wp-install' - When I run `wp eval --skip-wordpress 'echo realpath( getenv( "RUN_DIR" ) );'` + When I run `wp eval --skip-wordpress "echo WP_CLI\Path::normalize( realpath( getenv( 'RUN_DIR' ) ) );"` Then save STDOUT as {TEST_DIR} When I run `wp find {TEST_DIR} --format=count` @@ -203,7 +203,7 @@ Feature: Find WordPress installs on the filesystem Given a WP install in 'subdir1' And a WP install in 'subdir2' - When I run `wp eval --skip-wordpress 'echo realpath( getenv( "RUN_DIR" ) );'` + When I run `wp eval --skip-wordpress "echo WP_CLI\Path::normalize( realpath( getenv( 'RUN_DIR' ) ) );"` Then save STDOUT as {TEST_DIR} When I run `wp find {TEST_DIR} --format=count` @@ -224,7 +224,7 @@ Feature: Find WordPress installs on the filesystem And a WP install in 'logs' And a WP install in 'js' - When I run `wp eval --skip-wordpress 'echo realpath( getenv( "RUN_DIR" ) );'` + When I run `wp eval --skip-wordpress "echo WP_CLI\Path::normalize( realpath( getenv( 'RUN_DIR' ) ) );"` Then save STDOUT as {TEST_DIR} When I run `wp find {TEST_DIR} --field=version_path --verbose` diff --git a/src/Find_Command.php b/src/Find_Command.php index 1f9b840..c2b3d70 100644 --- a/src/Find_Command.php +++ b/src/Find_Command.php @@ -2,6 +2,7 @@ use WP_CLI\Utils; use WP_CLI\Formatter; +use WP_CLI\Path; class Find_Command { @@ -189,14 +190,17 @@ class Find_Command { * @when before_wp_load */ public function __invoke( $args, $assoc_args ) { - list( $path ) = $args; - $this->base_path = (string) realpath( $path ); - if ( ! $this->base_path ) { + list( $path ) = $args; + $base_path = (string) realpath( $path ); + if ( ! $base_path ) { WP_CLI::error( 'Invalid path specified.' ); } + $this->base_path = Path::normalize( $base_path ); $this->skip_ignored_paths = Utils\get_flag_value( $assoc_args, 'skip-ignored-paths' ); if ( ! empty( $assoc_args['include_ignored_paths'] ) ) { - $this->ignored_paths = array_merge( $this->ignored_paths, explode( ',', $assoc_args['include_ignored_paths'] ) ); + $included_paths = explode( ',', $assoc_args['include_ignored_paths'] ); + $included_paths = array_map( [ 'WP_CLI\Path', 'normalize' ], $included_paths ); + $this->ignored_paths = array_merge( $this->ignored_paths, $included_paths ); } $this->max_depth = Utils\get_flag_value( $assoc_args, 'max_depth', false ); $this->verbose = Utils\get_flag_value( $assoc_args, 'verbose' ); @@ -206,7 +210,7 @@ public function __invoke( $args, $assoc_args ) { if ( empty( $target['path'] ) ) { continue; } - $this->resolved_aliases[ rtrim( $target['path'], '/' ) ] = $alias; + $this->resolved_aliases[ rtrim( Path::normalize( $target['path'] ), '/' ) ] = $alias; } $fields = [ 'version_path', 'version', 'depth', 'alias' ]; @@ -229,6 +233,8 @@ private function recurse_directory( $path ) { return; } + $path = Path::normalize( $path ); + // Provide consistent trailing slashes to all paths $path = rtrim( $path, '/' ) . '/'; @@ -254,7 +260,7 @@ private function recurse_directory( $path ) { // This looks like a wp-includes directory, so check if it has a // version.php file. - if ( DIRECTORY_SEPARATOR . 'wp-includes/' === substr( $path, -13 ) + if ( '/wp-includes/' === substr( $path, -13 ) && file_exists( $path . 'version.php' ) ) { $version_path = $path . 'version.php'; $wp_path = substr( $path, 0, -13 ); @@ -346,7 +352,7 @@ private static function get_wp_config_path( $installation_dir ) { } if ( $path ) { - $path = realpath( $path ); + $path = Path::normalize( (string) realpath( $path ) ); } return $path; } From b5943e45ec14d3d31a1d96deb2605bda3595aa28 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Mon, 13 Jul 2026 17:58:58 +0200 Subject: [PATCH 04/11] More Windows tweaks --- features/find.feature | 13 ++++++++----- src/Find_Command.php | 17 ++++++++++++++--- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/features/find.feature b/features/find.feature index d9570f2..eb8b57c 100644 --- a/features/find.feature +++ b/features/find.feature @@ -70,7 +70,7 @@ Feature: Find WordPress installs on the filesystem Scenario: Use --max_depth= to specify max recursion depth Given a WP install in 'subdir1' - And I run `mkdir -p sub/sub` + And an empty sub/sub directory And a WP install in 'sub/subdir2' And a WP install in 'sub/sub/subdir3' @@ -169,8 +169,11 @@ Feature: Find WordPress installs on the filesystem When I run `wp eval --skip-wordpress "echo WP_CLI\Path::normalize( realpath( getenv( 'RUN_DIR' ) ) );"` Then save STDOUT as {TEST_DIR} - When I run `echo "@test1:\n path: {TEST_DIR}/subdir2" > wp-cli.yml` - Then the return code should be 0 + Given a wp-cli.yml file: + """ + @test1: + path: {TEST_DIR}/subdir2 + """ When I run `wp find {TEST_DIR} --fields=version_path,alias` Then STDOUT should be a table containing rows: @@ -181,7 +184,7 @@ Feature: Find WordPress installs on the filesystem Scenario: Ignore hidden directories by default Given a WP install in 'subdir1' And a WP install in '.svn' - And I run `mkdir -p subdir2/.svn` + And an empty subdir2/.svn directory And a WP install in 'subdir2/.svn/wp-install' When I run `wp eval --skip-wordpress "echo WP_CLI\Path::normalize( realpath( getenv( 'RUN_DIR' ) ) );"` @@ -212,7 +215,7 @@ Feature: Find WordPress installs on the filesystem 2 """ - When I run `wp find {TEST_DIR} --include_ignored_paths='/subdir1/,/apple/' --format=count` + When I run `wp find {TEST_DIR} --include_ignored_paths=/subdir1/,/apple/ --format=count` Then STDOUT should be: """ 1 diff --git a/src/Find_Command.php b/src/Find_Command.php index c2b3d70..6aac2d3 100644 --- a/src/Find_Command.php +++ b/src/Find_Command.php @@ -198,8 +198,13 @@ public function __invoke( $args, $assoc_args ) { $this->base_path = Path::normalize( $base_path ); $this->skip_ignored_paths = Utils\get_flag_value( $assoc_args, 'skip-ignored-paths' ); if ( ! empty( $assoc_args['include_ignored_paths'] ) ) { - $included_paths = explode( ',', $assoc_args['include_ignored_paths'] ); - $included_paths = array_map( [ 'WP_CLI\Path', 'normalize' ], $included_paths ); + $included_paths = explode( ',', trim( $assoc_args['include_ignored_paths'], "\"' " ) ); + $included_paths = array_map( + static function ( $path ) { + return Path::normalize( trim( $path, "\"' " ) ); + }, + $included_paths + ); $this->ignored_paths = array_merge( $this->ignored_paths, $included_paths ); } $this->max_depth = Utils\get_flag_value( $assoc_args, 'max_depth', false ); @@ -215,7 +220,13 @@ public function __invoke( $args, $assoc_args ) { $fields = [ 'version_path', 'version', 'depth', 'alias' ]; if ( ! empty( $assoc_args['fields'] ) ) { - $fields = explode( ',', $assoc_args['fields'] ); + $fields = explode( ',', trim( $assoc_args['fields'], "\"' " ) ); + $fields = array_map( + static function ( $field ) { + return trim( $field, "\"' " ); + }, + $fields + ); } $this->start_time = microtime( true ); From dc788a4beb7304e28809b3a44209c0864aff8ad8 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Mon, 13 Jul 2026 18:43:34 +0200 Subject: [PATCH 05/11] Another one --- src/Find_Command.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Find_Command.php b/src/Find_Command.php index 6aac2d3..aabd7e4 100644 --- a/src/Find_Command.php +++ b/src/Find_Command.php @@ -201,7 +201,8 @@ public function __invoke( $args, $assoc_args ) { $included_paths = explode( ',', trim( $assoc_args['include_ignored_paths'], "\"' " ) ); $included_paths = array_map( static function ( $path ) { - return Path::normalize( trim( $path, "\"' " ) ); + $path = Path::normalize( trim( $path, "\"' " ) ); + return ltrim( rtrim( $path, '/' ) . '/', '/' ); }, $included_paths ); @@ -252,7 +253,7 @@ private function recurse_directory( $path ) { // Don't recurse directories that probably don't have a WordPress installation. if ( ! $this->skip_ignored_paths ) { // Assume base path doesn't need comparison - $compared_path = (string) preg_replace( '#^' . preg_quote( $this->base_path, '#' ) . '#', '', $path ); + $compared_path = (string) preg_replace( '#^' . preg_quote( $this->base_path, '#' ) . '#i', '', $path ); // Ignore all hidden system directories $bits = explode( '/', trim( $compared_path, '/' ) ); $current_dir = array_pop( $bits ); From bd721956572776018b67e5b39f63d713a16aa143 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Mon, 13 Jul 2026 23:12:05 +0200 Subject: [PATCH 06/11] Wrap in quotes --- features/find.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/find.feature b/features/find.feature index eb8b57c..45199ff 100644 --- a/features/find.feature +++ b/features/find.feature @@ -215,7 +215,7 @@ Feature: Find WordPress installs on the filesystem 2 """ - When I run `wp find {TEST_DIR} --include_ignored_paths=/subdir1/,/apple/ --format=count` + When I run `wp find {TEST_DIR} --include_ignored_paths="/subdir1/,/apple/" --format=count` Then STDOUT should be: """ 1 From 79339cd0971ae3287275a9cb3bde300e283e135c Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Tue, 14 Jul 2026 11:45:01 +0200 Subject: [PATCH 07/11] Wrap everything --- features/find.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/find.feature b/features/find.feature index 45199ff..f67550f 100644 --- a/features/find.feature +++ b/features/find.feature @@ -215,7 +215,7 @@ Feature: Find WordPress installs on the filesystem 2 """ - When I run `wp find {TEST_DIR} --include_ignored_paths="/subdir1/,/apple/" --format=count` + When I run `wp find {TEST_DIR} "--include_ignored_paths=/subdir1/,/apple/" --format=count` Then STDOUT should be: """ 1 From 6d89aeca300bef6e493ee8f430b1c7124afa85e6 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Tue, 14 Jul 2026 12:09:45 +0200 Subject: [PATCH 08/11] debug logs --- src/Find_Command.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Find_Command.php b/src/Find_Command.php index aabd7e4..7fb1edb 100644 --- a/src/Find_Command.php +++ b/src/Find_Command.php @@ -254,6 +254,10 @@ private function recurse_directory( $path ) { if ( ! $this->skip_ignored_paths ) { // Assume base path doesn't need comparison $compared_path = (string) preg_replace( '#^' . preg_quote( $this->base_path, '#' ) . '#i', '', $path ); + WP_CLI::log( "DEBUG: base_path='{$this->base_path}'" ); + WP_CLI::log( "DEBUG: path='{$path}'" ); + WP_CLI::log( "DEBUG: compared_path='{$compared_path}'" ); + WP_CLI::log( "DEBUG: ignored_paths='" . implode( ',', $this->ignored_paths ) . "'" ); // Ignore all hidden system directories $bits = explode( '/', trim( $compared_path, '/' ) ); $current_dir = array_pop( $bits ); From 23c3d8b86930613fb0104b09bacf72584fcfc5b1 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Tue, 14 Jul 2026 12:19:47 +0200 Subject: [PATCH 09/11] comment out --- features/find.feature | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/features/find.feature b/features/find.feature index f67550f..0cccbef 100644 --- a/features/find.feature +++ b/features/find.feature @@ -209,11 +209,11 @@ Feature: Find WordPress installs on the filesystem When I run `wp eval --skip-wordpress "echo WP_CLI\Path::normalize( realpath( getenv( 'RUN_DIR' ) ) );"` Then save STDOUT as {TEST_DIR} - When I run `wp find {TEST_DIR} --format=count` - Then STDOUT should be: - """ - 2 - """ +# When I run `wp find {TEST_DIR} --format=count` +# Then STDOUT should be: +# """ +# 2 +# """ When I run `wp find {TEST_DIR} "--include_ignored_paths=/subdir1/,/apple/" --format=count` Then STDOUT should be: From 8b0351311c9b271cca452bb9eb24dd9d78440d5b Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Tue, 14 Jul 2026 12:43:20 +0200 Subject: [PATCH 10/11] cleanup --- features/find.feature | 12 ++++++------ src/Find_Command.php | 7 ++----- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/features/find.feature b/features/find.feature index 0cccbef..eb27edd 100644 --- a/features/find.feature +++ b/features/find.feature @@ -209,13 +209,13 @@ Feature: Find WordPress installs on the filesystem When I run `wp eval --skip-wordpress "echo WP_CLI\Path::normalize( realpath( getenv( 'RUN_DIR' ) ) );"` Then save STDOUT as {TEST_DIR} -# When I run `wp find {TEST_DIR} --format=count` -# Then STDOUT should be: -# """ -# 2 -# """ + When I run `wp find {TEST_DIR} --format=count` + Then STDOUT should be: + """ + 2 + """ - When I run `wp find {TEST_DIR} "--include_ignored_paths=/subdir1/,/apple/" --format=count` + When I run `wp find {TEST_DIR} "--include_ignored_paths=subdir1,apple" --format=count` Then STDOUT should be: """ 1 diff --git a/src/Find_Command.php b/src/Find_Command.php index 7fb1edb..9b9fd6d 100644 --- a/src/Find_Command.php +++ b/src/Find_Command.php @@ -254,12 +254,9 @@ private function recurse_directory( $path ) { if ( ! $this->skip_ignored_paths ) { // Assume base path doesn't need comparison $compared_path = (string) preg_replace( '#^' . preg_quote( $this->base_path, '#' ) . '#i', '', $path ); - WP_CLI::log( "DEBUG: base_path='{$this->base_path}'" ); - WP_CLI::log( "DEBUG: path='{$path}'" ); - WP_CLI::log( "DEBUG: compared_path='{$compared_path}'" ); - WP_CLI::log( "DEBUG: ignored_paths='" . implode( ',', $this->ignored_paths ) . "'" ); // Ignore all hidden system directories - $bits = explode( '/', trim( $compared_path, '/' ) ); + $bits = explode( '/', trim( $compared_path, '/' ) ); + $current_dir = array_pop( $bits ); if ( $current_dir && '.' === $current_dir[0] ) { $this->log( "Matched ignored path. Skipping recursion into '{$path}'" ); From 60f73a269f0da08f1c23ed4fbf309926b07b33d7 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Tue, 14 Jul 2026 14:13:00 +0200 Subject: [PATCH 11/11] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/Find_Command.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Find_Command.php b/src/Find_Command.php index 9b9fd6d..2c1b6c6 100644 --- a/src/Find_Command.php +++ b/src/Find_Command.php @@ -253,7 +253,7 @@ private function recurse_directory( $path ) { // Don't recurse directories that probably don't have a WordPress installation. if ( ! $this->skip_ignored_paths ) { // Assume base path doesn't need comparison - $compared_path = (string) preg_replace( '#^' . preg_quote( $this->base_path, '#' ) . '#i', '', $path ); + $compared_path = (string) preg_replace( '#^' . preg_quote( $this->base_path, '#' ) . '#' . ( '\\' === DIRECTORY_SEPARATOR ? 'i' : '' ), '', $path ); // Ignore all hidden system directories $bits = explode( '/', trim( $compared_path, '/' ) );