Skip to content

Commit 47fc059

Browse files
swissspidyCopilot
andauthored
Add initial PHPStan config (#218)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 33fb0a0 commit 47fc059

7 files changed

Lines changed: 438 additions & 136 deletions

File tree

features/profile.feature

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ Feature: Basic profile usage
1414
See 'wp help profile <command>' for more information on a specific command.
1515
"""
1616

17+
# Skip when object cache is used because sqlite-object-cache fails during manual wp core install
18+
# due to missing wp_options table during cron scheduling.
19+
@skip-object-cache
1720
Scenario: Error when SAVEQUERIES is defined to false
1821
Given an empty directory
1922
And WP files

phpstan.neon.dist

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
parameters:
2+
level: 9
3+
paths:
4+
- src
5+
- profile-command.php
6+
scanDirectories:
7+
- vendor/wp-cli/wp-cli/php
8+
scanFiles:
9+
- vendor/php-stubs/wordpress-stubs/wordpress-stubs.php
10+
- tests/phpstan/scan-files.php
11+
12+
treatPhpDocTypesAsCertain: false

src/Command.php

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -129,14 +129,20 @@ class Command {
129129
*
130130
* @skipglobalargcheck
131131
* @when before_wp_load
132+
*
133+
* @param array{0?: string} $args Positional arguments.
134+
* @param array{all?: bool, spotlight?: bool, url?: string, fields?: string, format: string, order: string, orderby?: string} $assoc_args Associative arguments.
135+
* @return void
132136
*/
133137
public function stage( $args, $assoc_args ) {
134138
global $wpdb;
135139

136140
$focus = Utils\get_flag_value( $assoc_args, 'all', isset( $args[0] ) ? $args[0] : null );
137141

138-
$order = Utils\get_flag_value( $assoc_args, 'order', 'ASC' );
139-
$orderby = Utils\get_flag_value( $assoc_args, 'orderby', null );
142+
$order_val = Utils\get_flag_value( $assoc_args, 'order', 'ASC' );
143+
$order = is_string( $order_val ) ? $order_val : 'ASC';
144+
$orderby_val = Utils\get_flag_value( $assoc_args, 'orderby', null );
145+
$orderby = ( is_string( $orderby_val ) || is_null( $orderby_val ) ) ? $orderby_val : null;
140146

141147
$valid_stages = array( 'bootstrap', 'main_query', 'template' );
142148
if ( $focus && ( true !== $focus && ! in_array( $focus, $valid_stages, true ) ) ) {
@@ -181,6 +187,7 @@ public function stage( $args, $assoc_args ) {
181187
$fields = array_merge( $base, $metrics );
182188
$formatter = new Formatter( $assoc_args, $fields );
183189
$loggers = $profiler->get_loggers();
190+
/** @var array<string, bool|string> $assoc_args */
184191
if ( Utils\get_flag_value( $assoc_args, 'spotlight' ) ) {
185192
$loggers = self::shine_spotlight( $loggers, $metrics );
186193
}
@@ -257,13 +264,19 @@ public function stage( $args, $assoc_args ) {
257264
*
258265
* @skipglobalargcheck
259266
* @when before_wp_load
267+
*
268+
* @param array{0?: string} $args Positional arguments.
269+
* @param array{all?: bool, spotlight?: bool, url?: string, fields?: string, format: string, order: string, orderby?: string} $assoc_args
270+
* @return void
260271
*/
261272
public function hook( $args, $assoc_args ) {
262273

263274
$focus = Utils\get_flag_value( $assoc_args, 'all', isset( $args[0] ) ? $args[0] : null );
264275

265-
$order = Utils\get_flag_value( $assoc_args, 'order', 'ASC' );
266-
$orderby = Utils\get_flag_value( $assoc_args, 'orderby', null );
276+
$order_val = Utils\get_flag_value( $assoc_args, 'order', 'ASC' );
277+
$order = is_string( $order_val ) ? $order_val : 'ASC';
278+
$orderby_val = Utils\get_flag_value( $assoc_args, 'orderby', null );
279+
$orderby = ( is_string( $orderby_val ) || is_null( $orderby_val ) ) ? $orderby_val : null;
267280

268281
$profiler = new Profiler( 'hook', $focus );
269282
$profiler->run();
@@ -293,11 +306,14 @@ public function hook( $args, $assoc_args ) {
293306
$fields = array_merge( $base, $metrics );
294307
$formatter = new Formatter( $assoc_args, $fields );
295308
$loggers = $profiler->get_loggers();
309+
/** @var array<string, bool|string> $assoc_args */
296310
if ( Utils\get_flag_value( $assoc_args, 'spotlight' ) ) {
297311
$loggers = self::shine_spotlight( $loggers, $metrics );
298312
}
299-
$search = Utils\get_flag_value( $assoc_args, 'search', false );
300-
if ( false !== $search && '' !== $search ) {
313+
/** @var array<string, bool|string> $assoc_args */
314+
$search_val = Utils\get_flag_value( $assoc_args, 'search', '' );
315+
$search = is_string( $search_val ) ? $search_val : '';
316+
if ( '' !== $search ) {
301317
if ( ! $focus ) {
302318
WP_CLI::error( '--search requires --all or a specific hook.' );
303319
}
@@ -357,13 +373,19 @@ public function hook( $args, $assoc_args ) {
357373
* | 0.1009s | 100% | 1 |
358374
* +---------+-------------+---------------+
359375
*
376+
* @param array{0: string} $args Positional arguments.
377+
* @param array{hook?: bool|string, fields: string, format: string, order: string, orderby?: string} $assoc_args Associative arguments.
378+
* @return void
379+
*
360380
* @subcommand eval
361381
*/
362382
public function eval_( $args, $assoc_args ) {
363383
$statement = $args[0];
364384

365-
$order = Utils\get_flag_value( $assoc_args, 'order', 'ASC' );
366-
$orderby = Utils\get_flag_value( $assoc_args, 'orderby', null );
385+
$order_val = Utils\get_flag_value( $assoc_args, 'order', 'ASC' );
386+
$order = is_string( $order_val ) ? $order_val : 'ASC';
387+
$orderby_val = Utils\get_flag_value( $assoc_args, 'orderby', null );
388+
$orderby = ( is_string( $orderby_val ) || is_null( $orderby_val ) ) ? $orderby_val : null;
367389

368390
self::profile_eval_ish(
369391
$assoc_args,
@@ -426,14 +448,20 @@ function () use ( $statement ) {
426448
* | 0.1009s | 100% | 1 |
427449
* +---------+-------------+---------------+
428450
*
451+
* @param array{0: string} $args Positional arguments.
452+
* @param array{hook?: string|bool, fields?: string, format: string, order: string, orderby?: string} $assoc_args Associative arguments.
453+
* @return void
454+
*
429455
* @subcommand eval-file
430456
*/
431457
public function eval_file( $args, $assoc_args ) {
432458

433459
$file = $args[0];
434460

435-
$order = Utils\get_flag_value( $assoc_args, 'order', 'ASC' );
436-
$orderby = Utils\get_flag_value( $assoc_args, 'orderby', null );
461+
$order_val = Utils\get_flag_value( $assoc_args, 'order', 'ASC' );
462+
$order = is_string( $order_val ) ? $order_val : 'ASC';
463+
$orderby_val = Utils\get_flag_value( $assoc_args, 'orderby', null );
464+
$orderby = ( is_string( $orderby_val ) || is_null( $orderby_val ) ) ? $orderby_val : null;
437465

438466
if ( ! file_exists( $file ) ) {
439467
WP_CLI::error( "'$file' does not exist." );
@@ -451,6 +479,12 @@ function () use ( $file ) {
451479

452480
/**
453481
* Profile an eval or eval-file statement.
482+
*
483+
* @param array{hook?: string|bool} $assoc_args
484+
* @param callable $profile_callback
485+
* @param string $order
486+
* @param string|null $orderby
487+
* @return void
454488
*/
455489
private static function profile_eval_ish( $assoc_args, $profile_callback, $order = 'ASC', $orderby = null ) {
456490
$hook = Utils\get_flag_value( $assoc_args, 'hook' );
@@ -500,6 +534,7 @@ private static function profile_eval_ish( $assoc_args, $profile_callback, $order
500534
* Include a file without exposing it to current scope
501535
*
502536
* @param string $file
537+
* @return void
503538
*/
504539
private static function include_file( $file ) {
505540
include $file;
@@ -508,9 +543,9 @@ private static function include_file( $file ) {
508543
/**
509544
* Filter loggers with zero-ish values.
510545
*
511-
* @param array $loggers
512-
* @param array $metrics
513-
* @return array
546+
* @param array<\WP_CLI\Profile\Logger> $loggers
547+
* @param array<string> $metrics
548+
* @return array<\WP_CLI\Profile\Logger>
514549
*/
515550
private static function shine_spotlight( $loggers, $metrics ) {
516551

@@ -550,9 +585,9 @@ private static function shine_spotlight( $loggers, $metrics ) {
550585
/**
551586
* Filter loggers to only those whose callback name matches a pattern.
552587
*
553-
* @param array $loggers
554-
* @param string $pattern
555-
* @return array
588+
* @param array<\WP_CLI\Profile\Logger> $loggers
589+
* @param string $pattern
590+
* @return array<\WP_CLI\Profile\Logger>
556591
*/
557592
private static function filter_by_callback( $loggers, $pattern ) {
558593
return array_filter(

src/Formatter.php

Lines changed: 70 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,32 @@
44

55
class Formatter {
66

7+
/**
8+
* @var \WP_CLI\Formatter
9+
*/
710
private $formatter;
811

12+
/**
13+
* @var array<string, mixed>
14+
*/
915
private $args;
1016

17+
/**
18+
* @var int|null
19+
*/
1120
private $total_cell_index;
1221

22+
/**
23+
* Formatter constructor.
24+
*
25+
* @param array<mixed> $assoc_args
26+
* @param array<string>|null $fields
27+
* @param string|false $prefix
28+
*/
1329
public function __construct( &$assoc_args, $fields = null, $prefix = false ) {
30+
if ( null === $fields ) {
31+
$fields = [];
32+
}
1433
$format_args = array(
1534
'format' => 'table',
1635
'fields' => $fields,
@@ -24,10 +43,19 @@ public function __construct( &$assoc_args, $fields = null, $prefix = false ) {
2443
}
2544

2645
if ( ! is_array( $format_args['fields'] ) ) {
27-
$format_args['fields'] = explode( ',', $format_args['fields'] );
46+
$fields_val = $format_args['fields'];
47+
$fields_str = is_scalar( $fields_val ) ? (string) $fields_val : '';
48+
$format_args['fields'] = explode( ',', $fields_str );
2849
}
2950

30-
$format_args['fields'] = array_filter( array_map( 'trim', $format_args['fields'] ) );
51+
$format_args['fields'] = array_filter(
52+
array_map(
53+
function ( $val ) {
54+
return trim( is_scalar( $val ) ? (string) $val : '' );
55+
},
56+
$format_args['fields']
57+
)
58+
);
3159

3260
if ( isset( $assoc_args['fields'] ) ) {
3361
if ( empty( $format_args['fields'] ) ) {
@@ -39,9 +67,9 @@ public function __construct( &$assoc_args, $fields = null, $prefix = false ) {
3967
}
4068
}
4169

42-
if ( 'time' !== $fields[0] ) {
70+
if ( ! empty( $fields ) && 'time' !== $fields[0] ) {
4371
$index = array_search( $fields[0], $format_args['fields'], true );
44-
$this->total_cell_index = ( false !== $index ) ? $index : null;
72+
$this->total_cell_index = ( false !== $index ) ? (int) $index : null;
4573
}
4674

4775
$this->args = $format_args;
@@ -51,11 +79,17 @@ public function __construct( &$assoc_args, $fields = null, $prefix = false ) {
5179
/**
5280
* Display multiple items according to the output arguments.
5381
*
54-
* @param array $items
82+
* @param array<\WP_CLI\Profile\Logger> $items
83+
* @param bool $include_total
84+
* @param string $order
85+
* @param string|null $orderby
86+
* @return void
5587
*/
5688
public function display_items( $items, $include_total, $order, $orderby ) {
5789
if ( 'table' === $this->args['format'] && empty( $this->args['field'] ) ) {
58-
$this->show_table( $order, $orderby, $items, $this->args['fields'], $include_total );
90+
/** @var array<string> $fields */
91+
$fields = $this->args['fields'];
92+
$this->show_table( $order, $orderby, $items, $fields, $include_total );
5993
} else {
6094
$this->formatter->display_items( $items );
6195
}
@@ -64,15 +98,17 @@ public function display_items( $items, $include_total, $order, $orderby ) {
6498
/**
6599
* Function to compare floats.
66100
*
67-
* @param double $a Floating number.
68-
* @param double $b Floating number.
101+
* @param float $a Floating number.
102+
* @param float $b Floating number.
103+
* @return int
69104
*/
70105
private function compare_float( $a, $b ) {
71-
$a = number_format( $a, 4 );
72-
$b = number_format( $b, 4 );
73-
if ( 0 === $a - $b ) {
106+
$a = round( $a, 4 );
107+
$b = round( $b, 4 );
108+
$diff = $a - $b;
109+
if ( 0.0 === $diff ) {
74110
return 0;
75-
} elseif ( $a - $b < 0 ) {
111+
} elseif ( $diff < 0 ) {
76112
return -1;
77113
} else {
78114
return 1;
@@ -82,8 +118,12 @@ private function compare_float( $a, $b ) {
82118
/**
83119
* Show items in a \cli\Table.
84120
*
85-
* @param array $items
86-
* @param array $fields
121+
* @param string $order
122+
* @param string|null $orderby
123+
* @param array<\WP_CLI\Profile\Logger> $items
124+
* @param array<string> $fields
125+
* @param bool $include_total
126+
* @return void
87127
*/
88128
private function show_table( $order, $orderby, $items, $fields, $include_total ) {
89129
$table = new \cli\Table();
@@ -109,7 +149,7 @@ function ( $a, $b ) use ( $order, $orderby ) {
109149
list( $first, $second ) = $orderby_array;
110150

111151
if ( is_numeric( $first->$orderby ) && is_numeric( $second->$orderby ) ) {
112-
return $this->compare_float( $first->$orderby, $second->$orderby );
152+
return $this->compare_float( (float) $first->$orderby, (float) $second->$orderby );
113153
}
114154

115155
return strcmp( $first->$orderby, $second->$orderby );
@@ -139,13 +179,17 @@ function ( $a, $b ) use ( $order, $orderby ) {
139179
}
140180
if ( stripos( $fields[ $i ], '_ratio' ) ) {
141181
if ( ! is_null( $value ) ) {
182+
assert( is_array( $totals[ $i ] ) );
142183
$totals[ $i ][] = $value;
143184
}
144185
} else {
145-
$totals[ $i ] += $value;
186+
$current_total = is_numeric( $totals[ $i ] ) ? $totals[ $i ] : 0;
187+
$add_value = is_numeric( $value ) ? $value : 0;
188+
$totals[ $i ] = $current_total + $add_value;
146189
}
147190
if ( stripos( $fields[ $i ], '_time' ) || 'time' === $fields[ $i ] ) {
148-
$values[ $i ] = round( $value, 4 ) . 's';
191+
$value_num = is_numeric( $value ) ? (float) $value : 0.0;
192+
$values[ $i ] = round( $value_num, 4 ) . 's';
149193
}
150194
}
151195
$table->addRow( $values );
@@ -156,11 +200,18 @@ function ( $a, $b ) use ( $order, $orderby ) {
156200
continue;
157201
}
158202
if ( stripos( $fields[ $i ], '_time' ) || 'time' === $fields[ $i ] ) {
159-
$totals[ $i ] = round( $value, 4 ) . 's';
203+
assert( is_numeric( $value ) );
204+
$totals[ $i ] = round( (float) $value, 4 ) . 's';
160205
}
161206
if ( is_array( $value ) ) {
162207
if ( ! empty( $value ) ) {
163-
$totals[ $i ] = round( ( array_sum( array_map( 'floatval', $value ) ) / count( $value ) ), 2 ) . '%';
208+
$float_values = array_map(
209+
function ( $val ) {
210+
return floatval( is_scalar( $val ) ? $val : 0 );
211+
},
212+
$value
213+
);
214+
$totals[ $i ] = round( ( array_sum( $float_values ) / count( $value ) ), 2 ) . '%';
164215
} else {
165216
$totals[ $i ] = null;
166217
}

0 commit comments

Comments
 (0)