symfony / polyfill

PHP polyfills

Home Page:https://symfony.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

The `str_contains` is not exact match with PHP 8.0+.

ve3 opened this issue · comments

return '' === $needle || false !== strpos($haystack, $needle);

Tests code.

$string = '123abcกขคょよら㑆㑇㑈456.78';

echo 'haystack is ' . $string . '<br>';
echo 'false, true, empty<br>';
var_dump(str_contains($string, false));// true
var_dump(str_contains($string, true));// false
var_dump(str_contains($string, ''));// true
echo 'contains strings<br>';
var_dump(str_contains($string, 'a'));// true
var_dump(str_contains($string, 'aa'));// false
var_dump(str_contains($string, 'z'));// false
// thai
var_dump(str_contains($string, 'ก'));// true
var_dump(str_contains($string, 'กก'));// false
var_dump(str_contains($string, 'ฮ'));// false
// japanese
var_dump(str_contains($string, 'ょ'));// true
var_dump(str_contains($string, 'ょょ'));// false
var_dump(str_contains($string, 'ゖ'));// false
// chinese
var_dump(str_contains($string, '㑆'));// true
var_dump(str_contains($string, '㑆㑆'));// false
var_dump(str_contains($string, '㔩'));// false
echo 'contains 0 &amp; 1 (int)<br>';
var_dump(str_contains($string, 0));// false
var_dump(str_contains($string, 1));// true
echo 'contains numbers (float, double)<br>';
var_dump(str_contains($string, 123.0));// true
var_dump(str_contains($string, 456.78));// true
echo 'haystack is true. check contains false, true, empty<br>';
var_dump(str_contains(true, false));// true
var_dump(str_contains(true, true));// true
var_dump(str_contains(true, ''));// true
echo 'haystack is false. check contains false, true, empty<br>';
var_dump(str_contains(false, false));// true
var_dump(str_contains(false, true));// false
var_dump(str_contains(false, ''));// true
echo 'haystack is empty. check contains false, true, empty<br>';
var_dump(str_contains('', false));// true
var_dump(str_contains('', true));// false
var_dump(str_contains('', ''));// true
echo 'error!<br>';
var_dump(str_contains($string, ['a']));
echo 'can\'t continue here.<br>';

The empty string and false are always return true.
The float (or double) type and true have to check with mb_strpos() instead of strpos() to return the exactly same result with PHP 8.0+.

Here is my function I use.

if (!function_exists('str_contains')) {
    function str_contains($haystack, $needle) {
        // verify type and show the errors as in PHP 8.
        if (!is_scalar($haystack)) {
            trigger_error('Argument #1 ($haystack) must be of type string, ' . gettype($haystack) . ' given', E_USER_ERROR);
        }
        if (!is_scalar($needle)) {
            trigger_error('Argument #2 ($needle) must be of type string, ' . gettype($needle) . ' given', E_USER_ERROR);
        }

        return '' === $needle || false === $needle || false !== mb_strpos($haystack, $needle);
    }
}

Can you show where is the difference? The current version behaves exactly the same to me: https://3v4l.org/aFtVo

Weird, My tests before is different but currently they are same. I don't know why.

Maybe my mistake.

Here is new tests code. All are fine.

<?php
if (!function_exists('str_contains')) {
    function str_contains(string $haystack, string $needle) {
        return '' === $needle || false !== strpos($haystack, $needle);
    }
}


$string = '123abcกขคょよら㑆㑇㑈456.78';
echo 'haystack is ' . var_export($string, true) . '<br>';
$tests = [
    [
        'testChar' => false,
        'expected' => true,
    ],
    [
        'testChar' => true,
        'expected' => true,
    ],
    [
        'testChar' => '',
        'expected' => true,
    ],

    // test with strings.=====
    [
        'testChar' => 'a',
        'expected' => true,
    ],
    [
        'testChar' => 'aa',
        'expected' => false,
    ],
    [
        'testChar' => 'z',
        'expected' => false,
    ],
    // Thai
    [
        'testChar' => 'ก',
        'expected' => true,
    ],
    [
        'testChar' => 'กก',
        'expected' => false,
    ],
    [
        'testChar' => 'ฮ',
        'expected' => false,
    ],
    // Japanese
    [
        'testChar' => 'ょ',
        'expected' => true,
    ],
    [
        'testChar' => 'ょょ',
        'expected' => false,
    ],
    [
        'testChar' => 'ゖ',
        'expected' => false,
    ],
    // Chinese
    [
        'testChar' => '㑆',
        'expected' => true,
    ],
    [
        'testChar' => '㑆㑆',
        'expected' => false,
    ],
    [
        'testChar' => '㔩',
        'expected' => false,
    ],
    // 0 & 1 (int) =====
    
    [
        'testChar' => 0,
        'expected' => false,
    ],
    [
        'testChar' => 1,
        'expected' => true,
    ],
    // float (double) =====
    
    [
        'testChar' => 123.0,
        'expected' => true,
    ],
    [
        'testChar' => 456.78,
        'expected' => true,
    ],
];
runTests($string, $tests);


$string = true;
echo 'haystack is ' . var_export($string, true) . '<br>';
$tests = [
    [
        'testChar' => false,
        'expected' => true,
    ],
    [
        'testChar' => true,
        'expected' => true,
    ],
    [
        'testChar' => '',
        'expected' => true,
    ],
];
runTests($string, $tests);


$string = false;
echo 'haystack is ' . var_export($string, true) . '<br>';
$tests = [
    [
        'testChar' => false,
        'expected' => true,
    ],
    [
        'testChar' => true,
        'expected' => false,
    ],
    [
        'testChar' => '',
        'expected' => true,
    ],
];
runTests($string, $tests);


$string = '';
echo 'haystack is ' . var_export($string, true) . '<br>';
$tests = [
    [
        'testChar' => false,
        'expected' => true,
    ],
    [
        'testChar' => true,
        'expected' => false,
    ],
    [
        'testChar' => '',
        'expected' => true,
    ],
];
runTests($string, $tests);


//echo 'must showing errors!<br>' . "\n";
//str_contains($string, ['a']);


function runTests($haystack, $tests)
{
    foreach ($tests as $testItem) {
        echo '<code>str_contains(' . var_export($haystack, true) . ', ' . var_export($testItem['testChar'], true) . ');</code><br>' . "\n";
        echo ' &nbsp; &nbsp;expect: <code>' . var_export($testItem['expected'], true) . '</code><br>' . "\n";
        $result = @str_contains($haystack, $testItem['testChar']);
        echo ' &nbsp; &nbsp;got: <code>' . var_export($result, true) . '</code><br>' . "\n";
        assert($testItem['expected'] === $result, 'Expect contains ' . var_export($testItem['testChar'], true) . ' to be ' . var_export($testItem['expected'], true) . ', Got ' . var_export($result, true) . '.');
    }
    unset($testItem);
}

Oh! I see that the function I use did not have string type hint.

function str_contains($haystack, $needle)

That's why it returns different results.

The string type hinting is since PHP 7+ as I remembered. I can't find document about this.
If you want to supported only PHP 7+ then it is okay but for those who use PHP 5.6 or older will get errors.

@ve3 symfony/polyfill-php80 requires at least PHP 7.0. This is because fdiv() cannot be ported to PHP 5.6 and it wouldn't make sense to provide partial polyfill.
Technically, your solution would work properly with PHP 5 but this package will no longer provide support for it.