Skip to content

Commit f8d8fce

Browse files
committed
Code Quality: Ensure wp_filesize() always returns a non-negative-int.
A negative file size is clearly impossible, and the return value of 0 is already documented as being the error case. * Values filtered by `pre_wp_filesize` and `wp_filesize` are cast to `int` if they are numeric. * Non-numeric values returned by the `wp_filesize` filter are discarded in favor of zero. * Negative values filtered by the `pre_wp_filesize` filter are treated the same as `null` (and do not short-circuit). * Negative values returned by the `wp_filesize` filter are clamped to be at least zero. Developed as part of WordPress#12611. Follow-up to r52837, r52932. Props westonruter, apermo. See #65670, #64898. git-svn-id: https://develop.svn.wordpress.org/trunk@62813 602fd350-edb4-49c9-b593-d223f7449a82
1 parent a086706 commit f8d8fce

2 files changed

Lines changed: 94 additions & 28 deletions

File tree

src/wp-includes/functions.php

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3639,24 +3639,29 @@ function wp_get_ext_types() {
36393639
* Wrapper for PHP filesize with filters and casting the result as an integer.
36403640
*
36413641
* @since 6.0.0
3642+
* @since 7.1.0 The return value is now ensured to always be greater than or equal to zero.
36423643
*
36433644
* @link https://www.php.net/manual/en/function.filesize.php
36443645
*
36453646
* @param string $path Path to the file.
36463647
* @return int The size of the file in bytes, or 0 in the event of an error.
3648+
* @phpstan-return non-negative-int
36473649
*/
3648-
function wp_filesize( $path ) {
3650+
function wp_filesize( $path ): int {
36493651
/**
36503652
* Filters the result of wp_filesize() before the file_exists() PHP function is run.
36513653
*
36523654
* @since 6.0.0
3655+
* @since 7.1.0 Negative values are now ignored, being treated the same as null. Numeric values are cast to integers.
36533656
*
3654-
* @param null|int $size The unfiltered value. Returning an int from the callback bypasses the filesize call.
3657+
* @param null|int $size The unfiltered value. Returning a non-negative number from the callback bypasses the filesize call.
36553658
* @param string $path Path to the file.
36563659
*/
36573660
$size = apply_filters( 'pre_wp_filesize', null, $path );
3658-
3659-
if ( is_int( $size ) ) {
3661+
if ( is_numeric( $size ) ) {
3662+
$size = (int) $size;
3663+
}
3664+
if ( is_int( $size ) && $size >= 0 ) {
36603665
return $size;
36613666
}
36623667

@@ -3666,11 +3671,18 @@ function wp_filesize( $path ) {
36663671
* Filters the size of the file.
36673672
*
36683673
* @since 6.0.0
3674+
* @since 7.1.0 The return value is now always zero or greater. Numeric values are cast to integers.
36693675
*
36703676
* @param int $size The result of PHP filesize on the file.
36713677
* @param string $path Path to the file.
36723678
*/
3673-
return (int) apply_filters( 'wp_filesize', $size, $path );
3679+
$size = apply_filters( 'wp_filesize', $size, $path );
3680+
if ( is_numeric( $size ) ) {
3681+
$size = (int) $size;
3682+
} else {
3683+
$size = 0;
3684+
}
3685+
return max( 0, $size );
36743686
}
36753687

36763688
/**

tests/phpunit/tests/functions/wpFilesize.php

Lines changed: 77 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,46 +9,100 @@
99
*/
1010
class Tests_Functions_wpFilesize extends WP_UnitTestCase {
1111

12+
const TEST_FILE = DIR_TESTDATA . '/images/test-image-upside-down.jpg';
13+
1214
/**
1315
* @ticket 49412
1416
*/
15-
public function test_wp_filesize() {
16-
$file = DIR_TESTDATA . '/images/test-image-upside-down.jpg';
17-
18-
$this->assertSame( filesize( $file ), wp_filesize( $file ) );
17+
public function test_wp_filesize(): void {
18+
$this->assertSame( filesize( self::TEST_FILE ), wp_filesize( self::TEST_FILE ) );
1919
}
2020

2121
/**
2222
* @ticket 49412
23+
* @ticket 65670
2324
*/
24-
public function test_wp_filesize_filters() {
25-
$file = DIR_TESTDATA . '/images/test-image-upside-down.jpg';
25+
public function test_wp_filesize_filters(): void {
26+
add_filter( 'wp_filesize', static fn () => 999 );
27+
$this->assertSame( 999, wp_filesize( self::TEST_FILE ) );
2628

27-
add_filter(
28-
'wp_filesize',
29-
static function () {
30-
return 999;
31-
}
32-
);
29+
add_filter( 'wp_filesize', static fn () => '9991', 100 );
30+
$this->assertSame( 9991, wp_filesize( self::TEST_FILE ) );
3331

34-
$this->assertSame( 999, wp_filesize( $file ) );
32+
add_filter( 'pre_wp_filesize', static fn () => 111 );
33+
$this->assertSame( 111, wp_filesize( self::TEST_FILE ) );
3534

36-
add_filter(
37-
'pre_wp_filesize',
38-
static function () {
39-
return 111;
40-
}
41-
);
35+
add_filter( 'pre_wp_filesize', static fn () => '2222', 100 );
36+
$this->assertSame( 2222, wp_filesize( self::TEST_FILE ) );
4237

43-
$this->assertSame( 111, wp_filesize( $file ) );
38+
add_filter( 'pre_wp_filesize', static fn () => -100, 200 );
39+
$this->assertSame( 9991, wp_filesize( self::TEST_FILE ) );
4440
}
4541

4642
/**
4743
* @ticket 49412
4844
*/
49-
public function test_wp_filesize_with_nonexistent_file() {
50-
$file = 'nonexistent/file.jpg';
45+
public function test_wp_filesize_with_nonexistent_file(): void {
46+
$this->assertSame( 0, wp_filesize( 'nonexistent/file.jpg' ) );
47+
}
48+
49+
/**
50+
* @ticket 65670
51+
*/
52+
public function test_wp_filesize_pre_wp_filesize_filter_null(): void {
53+
add_filter( 'pre_wp_filesize', '__return_null' );
54+
55+
$this->assertSame( filesize( self::TEST_FILE ), wp_filesize( self::TEST_FILE ) );
56+
}
5157

52-
$this->assertSame( 0, wp_filesize( $file ) );
58+
/**
59+
* @ticket 65670
60+
*
61+
* @dataProvider data_wp_filesize_pre_wp_filesize_filter_negative
62+
*
63+
* @param float|int|string $value Negative value returned by the filter.
64+
*/
65+
public function test_wp_filesize_pre_wp_filesize_filter_negative( $value ): void {
66+
add_filter( 'pre_wp_filesize', static fn () => $value );
67+
68+
$this->assertSame( filesize( self::TEST_FILE ), wp_filesize( self::TEST_FILE ) );
69+
}
70+
71+
/**
72+
* Data provider.
73+
*
74+
* @return array<string, array{ 0: float|int|string }>
75+
*/
76+
public function data_wp_filesize_pre_wp_filesize_filter_negative(): array {
77+
return array(
78+
'negative int' => array( -1 ),
79+
'negative numeric string' => array( '-1' ),
80+
'negative float' => array( -1.5 ),
81+
);
82+
}
83+
84+
/**
85+
* @ticket 65670
86+
*
87+
* @dataProvider data_wp_filesize_filter_invalid_value
88+
*
89+
* @param mixed $value
90+
*/
91+
public function test_wp_filesize_wp_filesize_filter_invalid_value( $value ): void {
92+
add_filter( 'wp_filesize', static fn () => $value );
93+
$this->assertSame( 0, wp_filesize( self::TEST_FILE ) );
94+
}
95+
96+
/**
97+
* Data provider.
98+
*
99+
* @return array<string, array{ 0: mixed }>
100+
*/
101+
public function data_wp_filesize_filter_invalid_value(): array {
102+
return array(
103+
'negative' => array( -1 ),
104+
'null' => array( null ),
105+
'array' => array( array( 'bad', 'array' ) ),
106+
);
53107
}
54108
}

0 commit comments

Comments
 (0)