Skip to content

Commit 783790c

Browse files
authored
Fix incorrect PHP polyfill implementations (#338)
## Summary The polyfills for `str_starts_with`, `str_contains`, and `str_ends_with` had incorrect implementations ever since they were added in 4b0dab5: - **`str_ends_with` broken parentheses**: The original `substr($haystack, -strlen($needle) === $needle)` had the `=== $needle` comparison inside the `substr()` call as the length argument, instead of comparing the `substr()` result. The function never worked correctly. - **`empty()` vs `'' ===`**: All three functions used `empty($needle)` to check for an empty needle. Since `empty("0")` returns `true` in PHP, this caused wrong results for strings like `"0"` — e.g., `str_contains("abc", "0")` would incorrectly return `true`. - **`str_starts_with` improvement**: Now uses `strncmp` instead of `strpos` for correctness and performance. The implementations now follow the [Symfony polyfill-php80](https://github.com/symfony/polyfill-php80) package. Also removes the duplicate (buggy) polyfill definitions from the test bootstrap file, which already includes `php-polyfills.php`. ## Test plan - [ ] Verify `str_starts_with`, `str_contains`, and `str_ends_with` behave correctly with empty strings, `"0"` strings, and regular inputs. - [ ] Run `composer run test` to confirm no regressions.
2 parents 5a1f25f + 661e90d commit 783790c

2 files changed

Lines changed: 19 additions & 55 deletions

File tree

php-polyfills.php

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
<?php
22
/**
3-
* Polyfills for php 7 & 8 functions
3+
* Polyfills for PHP 8.0 string functions.
4+
*
5+
* Implementation follows the Symfony polyfill-php80 package.
6+
*
7+
* @see https://github.com/symfony/polyfill-php80
48
*
59
* @package wp-sqlite-integration
610
*/
@@ -17,7 +21,7 @@
1721
* @return bool
1822
*/
1923
function str_starts_with( string $haystack, string $needle ) {
20-
return empty( $needle ) || 0 === strpos( $haystack, $needle );
24+
return 0 === strncmp( $haystack, $needle, strlen( $needle ) );
2125
}
2226
}
2327

@@ -33,7 +37,7 @@ function str_starts_with( string $haystack, string $needle ) {
3337
* @return bool
3438
*/
3539
function str_contains( string $haystack, string $needle ) {
36-
return empty( $needle ) || false !== strpos( $haystack, $needle );
40+
return '' === $needle || false !== strpos( $haystack, $needle );
3741
}
3842
}
3943

@@ -49,6 +53,16 @@ function str_contains( string $haystack, string $needle ) {
4953
* @return bool
5054
*/
5155
function str_ends_with( string $haystack, string $needle ) {
52-
return empty( $needle ) || substr( $haystack, -strlen( $needle ) === $needle );
56+
if ( '' === $needle || $needle === $haystack ) {
57+
return true;
58+
}
59+
60+
if ( '' === $haystack ) {
61+
return false;
62+
}
63+
64+
$needle_length = strlen( $needle );
65+
66+
return $needle_length <= strlen( $haystack ) && 0 === substr_compare( $haystack, $needle, -$needle_length );
5367
}
5468
}

tests/bootstrap.php

Lines changed: 1 addition & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<?php
22

3+
require_once __DIR__ . '/../php-polyfills.php';
34
require_once __DIR__ . '/wp-sqlite-schema.php';
45
require_once __DIR__ . '/../wp-pdo-mysql-on-sqlite.php';
56
require_once __DIR__ . '/../wp-includes/sqlite/class-wp-sqlite-query-rewriter.php';
@@ -50,57 +51,6 @@ function apply_filters( $tag, $value, ...$args ) {
5051
}
5152
}
5253

53-
/**
54-
* Polyfills for php 7 & 8 functions
55-
*/
56-
57-
if ( ! function_exists( 'str_starts_with' ) ) {
58-
/**
59-
* Check if a string starts with a specific substring.
60-
*
61-
* @param string $haystack The string to search in.
62-
* @param string $needle The string to search for.
63-
*
64-
* @see https://www.php.net/manual/en/function.str-starts-with
65-
*
66-
* @return bool
67-
*/
68-
function str_starts_with( string $haystack, string $needle ) {
69-
return empty( $needle ) || 0 === strpos( $haystack, $needle );
70-
}
71-
}
72-
73-
if ( ! function_exists( 'str_contains' ) ) {
74-
/**
75-
* Check if a string contains a specific substring.
76-
*
77-
* @param string $haystack The string to search in.
78-
* @param string $needle The string to search for.
79-
*
80-
* @see https://www.php.net/manual/en/function.str-contains
81-
*
82-
* @return bool
83-
*/
84-
function str_contains( string $haystack, string $needle ) {
85-
return empty( $needle ) || false !== strpos( $haystack, $needle );
86-
}
87-
}
88-
89-
if ( ! function_exists( 'str_ends_with' ) ) {
90-
/**
91-
* Check if a string ends with a specific substring.
92-
*
93-
* @param string $haystack The string to search in.
94-
* @param string $needle The string to search for.
95-
*
96-
* @see https://www.php.net/manual/en/function.str-ends-with
97-
*
98-
* @return bool
99-
*/
100-
function str_ends_with( string $haystack, string $needle ) {
101-
return empty( $needle ) || substr( $haystack, -strlen( $needle ) ) === $needle;
102-
}
103-
}
10454
if ( extension_loaded( 'mbstring' ) ) {
10555

10656
if ( ! function_exists( 'mb_str_starts_with' ) ) {

0 commit comments

Comments
 (0)