66use Gettext \Translations ;
77use Gettext \Utils \ParsedComment ;
88use WP_CLI ;
9+ use WP_CLI \Path ;
910use WP_CLI \Utils ;
1011
12+
1113/**
1214 * Audit strings in a WordPress project.
1315 *
@@ -88,33 +90,46 @@ class AuditCommand extends MakePotCommand {
8890 * # Audit a plugin with GitHub Actions annotations format.
8991 * $ wp i18n audit wp-content/plugins/hello-world --format=github-actions
9092 *
91- * @when before_wp_load
92- *
93+ * @param array<string> $args Positional arguments.
94+ * @param array<mixed> $assoc_args Associative arguments.
95+ * @return void
9396 * @throws WP_CLI\ExitException
9497 */
9598 public function __invoke ( $ args , $ assoc_args ) {
96- $ this -> source = realpath ( $ args [0 ] );
97- if ( ! $ this -> source || ! is_dir ( $ this -> source ) ) {
99+ $ source = realpath ( $ args [0 ] );
100+ if ( ! $ source || ! is_dir ( $ source ) ) {
98101 WP_CLI ::error ( 'Not a valid source directory. ' );
99102 }
100103
101- $ this ->slug = Utils \get_flag_value ( $ assoc_args , 'slug ' , Utils \basename ( $ this ->source ) );
102- $ this ->domain = Utils \get_flag_value ( $ assoc_args , 'domain ' , null );
103- $ this ->skip_js = Utils \get_flag_value ( $ assoc_args , 'skip-js ' , $ this ->skip_js );
104- $ this ->skip_php = Utils \get_flag_value ( $ assoc_args , 'skip-php ' , $ this ->skip_php );
105- $ this ->skip_blade = Utils \get_flag_value ( $ assoc_args , 'skip-blade ' , $ this ->skip_blade );
106- $ this ->skip_block_json = Utils \get_flag_value ( $ assoc_args , 'skip-block-json ' , $ this ->skip_block_json );
107- $ this ->skip_theme_json = Utils \get_flag_value ( $ assoc_args , 'skip-theme-json ' , $ this ->skip_theme_json );
108- $ this ->format = Utils \get_flag_value ( $ assoc_args , 'format ' , $ this ->format );
109- $ ignore_domain = Utils \get_flag_value ( $ assoc_args , 'ignore-domain ' , false );
110-
111- $ include = Utils \get_flag_value ( $ assoc_args , 'include ' , [] );
112- if ( ! empty ( $ include ) ) {
104+ $ this ->source = $ source ;
105+
106+ /** @var array<string, bool|string> $assoc_args_simple */
107+ $ assoc_args_simple = $ assoc_args ;
108+
109+ $ slug = Utils \get_flag_value ( $ assoc_args_simple , 'slug ' , Path::basename ( $ this ->source ) );
110+ $ this ->slug = is_string ( $ slug ) ? $ slug : Path::basename ( $ this ->source );
111+
112+ $ domain = Utils \get_flag_value ( $ assoc_args_simple , 'domain ' , null );
113+ $ this ->domain = is_string ( $ domain ) ? $ domain : null ;
114+
115+ $ this ->skip_js = (bool ) Utils \get_flag_value ( $ assoc_args_simple , 'skip-js ' , $ this ->skip_js );
116+ $ this ->skip_php = (bool ) Utils \get_flag_value ( $ assoc_args_simple , 'skip-php ' , $ this ->skip_php );
117+ $ this ->skip_blade = (bool ) Utils \get_flag_value ( $ assoc_args_simple , 'skip-blade ' , $ this ->skip_blade );
118+ $ this ->skip_block_json = (bool ) Utils \get_flag_value ( $ assoc_args_simple , 'skip-block-json ' , $ this ->skip_block_json );
119+ $ this ->skip_theme_json = (bool ) Utils \get_flag_value ( $ assoc_args_simple , 'skip-theme-json ' , $ this ->skip_theme_json );
120+
121+ $ format = Utils \get_flag_value ( $ assoc_args_simple , 'format ' , $ this ->format );
122+ $ this ->format = is_string ( $ format ) ? $ format : 'plaintext ' ;
123+
124+ $ ignore_domain = (bool ) Utils \get_flag_value ( $ assoc_args_simple , 'ignore-domain ' , false );
125+
126+ $ include = Utils \get_flag_value ( $ assoc_args_simple , 'include ' , null );
127+ if ( is_string ( $ include ) && '' !== $ include ) {
113128 $ this ->include = array_map ( 'trim ' , explode ( ', ' , $ include ) );
114129 }
115130
116- $ exclude = Utils \get_flag_value ( $ assoc_args , 'exclude ' , [] );
117- if ( ! empty ( $ exclude ) ) {
131+ $ exclude = Utils \get_flag_value ( $ assoc_args_simple , 'exclude ' , null );
132+ if ( is_string ( $ exclude ) && '' !== $ exclude ) {
118133 $ this ->exclude = array_map ( 'trim ' , explode ( ', ' , $ exclude ) );
119134 }
120135
@@ -128,7 +143,7 @@ public function __invoke( $args, $assoc_args ) {
128143 if ( null === $ this ->domain ) {
129144 $ this ->domain = $ this ->slug ;
130145
131- if ( ! empty ( $ this ->main_file_data ['Text Domain ' ]['value ' ] ) ) {
146+ if ( ! empty ( $ this ->main_file_data ['Text Domain ' ]['value ' ] ) && is_string ( $ this -> main_file_data [ ' Text Domain ' ][ ' value ' ] ) ) {
132147 $ this ->domain = $ this ->main_file_data ['Text Domain ' ]['value ' ];
133148 }
134149 }
@@ -166,24 +181,28 @@ public function __invoke( $args, $assoc_args ) {
166181 *
167182 * Overrides parent method to suppress log messages when using non-plaintext formats.
168183 *
169- * @return array
184+ * @return array<string, array<string, mixed>>
170185 */
171186 protected function get_main_file_data () {
172187 $ files = new \IteratorIterator ( new \DirectoryIterator ( $ this ->source ) );
173188
174189 /** @var \DirectoryIterator $file */
175190 foreach ( $ files as $ file ) {
191+ $ real_path = $ file ->getRealPath ();
192+ if ( false === $ real_path ) {
193+ continue ;
194+ }
176195 $ stylesheet_path = null ;
177196 $ project_path = null ;
178197
179198 // wp-content/themes/my-theme/style.css
180199 if ( $ file ->isFile () && 'style ' === $ file ->getBasename ( '.css ' ) && $ file ->isReadable () ) {
181- $ stylesheet_path = $ file -> getRealPath () ;
182- $ project_path = $ file -> getRealPath () ;
183- } elseif ( $ file ->isDir () && ! $ file ->isDot () && is_readable ( $ file -> getRealPath () . '/style.css ' ) ) {
200+ $ stylesheet_path = $ real_path ;
201+ $ project_path = $ real_path ;
202+ } elseif ( $ file ->isDir () && ! $ file ->isDot () && is_readable ( $ real_path . '/style.css ' ) ) {
184203 // wp-content/themes/my-themes/theme-a/style.css
185- $ stylesheet_path = $ file -> getRealPath () . '/style.css ' ;
186- $ project_path = $ file -> getRealPath () ;
204+ $ stylesheet_path = $ real_path . '/style.css ' ;
205+ $ project_path = $ real_path ;
187206 }
188207
189208 if ( $ stylesheet_path ) {
@@ -202,8 +221,10 @@ protected function get_main_file_data() {
202221 }
203222 WP_CLI ::debug ( sprintf ( 'Theme stylesheet: %s ' , $ stylesheet_path ), 'audit ' );
204223
205- $ this ->project_type = 'theme ' ;
206- $ this ->main_file_path = $ project_path ;
224+ $ this ->project_type = 'theme ' ;
225+ if ( is_string ( $ project_path ) ) {
226+ $ this ->main_file_path = $ project_path ;
227+ }
207228
208229 return $ theme_data ;
209230 }
@@ -212,7 +233,7 @@ protected function get_main_file_data() {
212233 // wp-content/plugins/my-plugin/my-plugin.php
213234 if ( $ file ->isFile () && $ file ->isReadable () && 'php ' === $ file ->getExtension () ) {
214235 $ plugin_data = FileDataExtractor::get_file_data (
215- $ file -> getRealPath () ,
236+ $ real_path ,
216237 array_combine (
217238 $ this ->get_file_headers ( 'plugin ' ),
218239 $ this ->get_file_headers ( 'plugin ' )
@@ -224,10 +245,10 @@ protected function get_main_file_data() {
224245 if ( 'plaintext ' === $ this ->format ) {
225246 WP_CLI ::log ( 'Plugin file detected. ' );
226247 }
227- WP_CLI ::debug ( sprintf ( 'Plugin file: %s ' , $ file -> getRealPath () ), 'audit ' );
248+ WP_CLI ::debug ( sprintf ( 'Plugin file: %s ' , $ real_path ), 'audit ' );
228249
229250 $ this ->project_type = 'plugin ' ;
230- $ this ->main_file_path = $ file -> getRealPath () ;
251+ $ this ->main_file_path = $ real_path ;
231252
232253 return $ plugin_data ;
233254 }
@@ -378,7 +399,7 @@ protected function get_comment_text( $comment ) {
378399 * Goes through all extracted strings to find possible mistakes.
379400 *
380401 * @param Translations $translations Translations object.
381- * @return array Array of issues found.
402+ * @return array<int, array<string, mixed>> Array of issues found.
382403 */
383404 protected function collect_audit_issues ( $ translations ) {
384405 $ issues = [];
@@ -420,8 +441,6 @@ protected function collect_audit_issues( $translations ) {
420441 $ comments = array_filter (
421442 $ comments ,
422443 function ( $ comment ) {
423- /** @var ParsedComment|string $comment */
424- /** @var string $file_header */
425444 foreach ( $ this ->get_file_headers ( $ this ->project_type ) as $ file_header ) {
426445 if ( 0 === strpos ( $ this ->get_comment_text ( $ comment ), $ file_header ) ) {
427446 return false ;
@@ -453,7 +472,8 @@ function ( $comment ) {
453472 }
454473 }
455474
456- $ non_placeholder_content = trim ( preg_replace ( self ::SPRINTF_PLACEHOLDER_REGEX , '' , $ translation ->getOriginal () ) );
475+ $ replaced = preg_replace ( self ::SPRINTF_PLACEHOLDER_REGEX , '' , $ translation ->getOriginal () );
476+ $ non_placeholder_content = trim ( is_string ( $ replaced ) ? $ replaced : '' );
457477
458478 // Check 3: Flag empty strings without any translatable content.
459479 if ( '' === $ non_placeholder_content ) {
@@ -517,7 +537,8 @@ function ( $comment ) {
517537 /**
518538 * Outputs audit results in the specified format.
519539 *
520- * @param array $issues Array of issues found.
540+ * @param array<int, array<string, mixed>> $issues Array of issues found.
541+ * @return void
521542 */
522543 protected function output_results ( $ issues ) {
523544 if ( empty ( $ issues ) ) {
@@ -526,14 +547,15 @@ protected function output_results( $issues ) {
526547
527548 switch ( $ this ->format ) {
528549 case 'json ' :
529- WP_CLI ::line ( json_encode ( $ issues , JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES ) );
550+ $ json = json_encode ( $ issues , JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES );
551+ WP_CLI ::line ( false !== $ json ? $ json : '[] ' );
530552 break ;
531553
532554 case 'github-actions ' :
533555 foreach ( $ issues as $ issue ) {
534- $ file = $ issue ['file ' ];
535- $ line = $ issue ['line ' ] ?? 1 ;
536- $ message = $ issue ['message ' ];
556+ $ file = isset ( $ issue ['file ' ] ) && is_scalar ( $ issue [ ' file ' ] ) ? ( string ) $ issue [ ' file ' ] : '' ;
557+ $ line = isset ( $ issue ['line ' ] ) && is_scalar ( $ issue [ ' line ' ] ) ? ( int ) $ issue [ ' line ' ] : 1 ;
558+ $ message = isset ( $ issue ['message ' ] ) && is_scalar ( $ issue [ ' message ' ] ) ? ( string ) $ issue [ ' message ' ] : '' ;
537559
538560 WP_CLI ::line ( sprintf ( '::warning file=%s,line=%d::%s ' , $ file , $ line , $ message ) );
539561 }
@@ -542,9 +564,9 @@ protected function output_results( $issues ) {
542564 case 'plaintext ' :
543565 default :
544566 foreach ( $ issues as $ issue ) {
545- $ file = $ issue ['file ' ];
546- $ line = $ issue ['line ' ] ?? null ;
547- $ message = $ issue ['message ' ];
567+ $ file = isset ( $ issue ['file ' ] ) && is_scalar ( $ issue [ ' file ' ] ) ? ( string ) $ issue [ ' file ' ] : '' ;
568+ $ line = isset ( $ issue ['line ' ] ) && is_scalar ( $ issue [ ' line ' ] ) ? ( int ) $ issue [ ' line ' ] : null ;
569+ $ message = isset ( $ issue ['message ' ] ) && is_scalar ( $ issue [ ' message ' ] ) ? ( string ) $ issue [ ' message ' ] : '' ;
548570 $ location = $ line ? "$ file: $ line " : $ file ;
549571
550572 WP_CLI ::warning ( sprintf ( '%s: %s ' , $ location , $ message ) );
0 commit comments