diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c3f828ffd..dbb2e9190 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,7 +12,6 @@ jobs: env: COMPOSER_ROOT_VERSION: 1.x-dev - SYMFONY_PHPUNIT_VERSION: 8.5 strategy: matrix: @@ -41,10 +40,12 @@ jobs: tools: "composer:v2" - name: Install dependencies - run: composer --prefer-source --no-progress --ansi install + run: | + composer --prefer-source --no-progress --ansi install + ./phpunit install - name: Run tests run: | ok=0 - ./vendor/bin/simple-phpunit || ok=1 + ./phpunit || ok=1 [[ "${{ matrix.mode }}" = experimental ]] || (exit $ok) diff --git a/.gitignore b/.gitignore index a9246d0fd..b025ac2c9 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,6 @@ .phpunit.result.cache composer.lock phpunit.xml +.phpunit vendor/ /tests/unicode diff --git a/appveyor.yml b/appveyor.yml index 9df8b9a65..93b8c784d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -10,8 +10,8 @@ cache: init: - SET PATH=c:\php;%PATH% - SET COMPOSER_NO_INTERACTION=1 + - SET COMPOSER_ROOT_VERSION=1.x-dev - SET PHP=1 - - SET SYMFONY_PHPUNIT_VERSION=8.5 install: - cinst wget @@ -41,8 +41,8 @@ install: - appveyor DownloadFile https://github.com/composer/composer/releases/download/2.7.9/composer.phar - cd c:\projects\polyfill - mkdir %APPDATA%\Composer && copy /Y .github\composer-config.json %APPDATA%\Composer\config.json - - SET COMPOSER_ROOT_VERSION=1.x-dev - composer update --prefer-source --no-progress --ansi + - php -d allow_url_fopen=0 ./phpunit install test_script: - - php -d allow_url_fopen=0 ./vendor/symfony/phpunit-bridge/bin/simple-phpunit + - php -d allow_url_fopen=0 ./phpunit diff --git a/phpunit b/phpunit new file mode 100755 index 000000000..82cf9caa3 --- /dev/null +++ b/phpunit @@ -0,0 +1,13 @@ +#!/usr/bin/env php + 80400 && '' === $domainName) { + throw new \ValueError('idn_to_ascii(): Argument #1 ($domain) cannot be empty'); + } + if (self::INTL_IDNA_VARIANT_2003 === $variant) { @trigger_error('idn_to_ascii(): INTL_IDNA_VARIANT_2003 is deprecated', \E_USER_DEPRECATED); } @@ -198,6 +202,10 @@ public static function idn_to_ascii($domainName, $options = self::IDNA_DEFAULT, */ public static function idn_to_utf8($domainName, $options = self::IDNA_DEFAULT, $variant = self::INTL_IDNA_VARIANT_UTS46, &$idna_info = []) { + if (\PHP_VERSION_ID > 80400 && '' === $domainName) { + throw new \ValueError('idn_to_utf8(): Argument #1 ($domain) cannot be empty'); + } + if (self::INTL_IDNA_VARIANT_2003 === $variant) { @trigger_error('idn_to_utf8(): INTL_IDNA_VARIANT_2003 is deprecated', \E_USER_DEPRECATED); } diff --git a/src/Mbstring/bootstrap80.php b/src/Mbstring/bootstrap80.php index 5be7d2018..5236e6dcc 100644 --- a/src/Mbstring/bootstrap80.php +++ b/src/Mbstring/bootstrap80.php @@ -133,11 +133,11 @@ function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $ } if (!function_exists('mb_ucfirst')) { - function mb_ucfirst($string, ?string $encoding = null): string { return p\Mbstring::mb_ucfirst($string, $encoding); } + function mb_ucfirst(string $string, ?string $encoding = null): string { return p\Mbstring::mb_ucfirst($string, $encoding); } } if (!function_exists('mb_lcfirst')) { - function mb_lcfirst($string, ?string $encoding = null): string { return p\Mbstring::mb_lcfirst($string, $encoding); } + function mb_lcfirst(string $string, ?string $encoding = null): string { return p\Mbstring::mb_lcfirst($string, $encoding); } } if (!function_exists('mb_trim')) { diff --git a/src/Php84/bootstrap.php b/src/Php84/bootstrap.php index 73fb1f404..342158c63 100644 --- a/src/Php84/bootstrap.php +++ b/src/Php84/bootstrap.php @@ -41,11 +41,11 @@ function array_all(array $array, callable $callback): bool { return p\Php84::arr if (extension_loaded('mbstring')) { if (!function_exists('mb_ucfirst')) { - function mb_ucfirst($string, ?string $encoding = null): string { return p\Php84::mb_ucfirst($string, $encoding); } + function mb_ucfirst(string $string, ?string $encoding = null): string { return p\Php84::mb_ucfirst($string, $encoding); } } if (!function_exists('mb_lcfirst')) { - function mb_lcfirst($string, ?string $encoding = null): string { return p\Php84::mb_lcfirst($string, $encoding); } + function mb_lcfirst(string $string, ?string $encoding = null): string { return p\Php84::mb_lcfirst($string, $encoding); } } if (!function_exists('mb_trim')) { diff --git a/src/Util/TestListenerTrait.php b/src/Util/TestListenerTrait.php index 3b87d426d..5339d4f6e 100644 --- a/src/Util/TestListenerTrait.php +++ b/src/Util/TestListenerTrait.php @@ -123,6 +123,16 @@ function {$f['name']}{$f['signature']} $polyfillSignature = ReflectionCaster::castFunctionAbstract(new \ReflectionFunction($testNamespace.'\\'.$f['name']), [], new Stub(), true); $polyfillSignature = ReflectionCaster::getSignature($polyfillSignature); + if ('mb_get_info' === $r->name && false === strpos($originalSignature, '|null') && false !== strpos($polyfillSignature, '|null')) { + // Added to PHP 8.2.14/8.3.1 + $originalSignature .= '|null'; + } + + if (false === strpos($bootstrap->getPath(), '80.php')) { + // mixed return type cannot be used before PHP 8 + $originalSignature = str_replace(': mixed', '', $originalSignature); + } + $map = [ '?' => '', 'IDNA_DEFAULT' => \PHP_VERSION_ID >= 80100 ? 'IDNA_DEFAULT' : '0', @@ -131,7 +141,7 @@ function {$f['name']}{$f['signature']} 'array|string|null $from_encoding' => 'array|string $from_encoding', ]; - if (strtr($polyfillSignature, $map) !== $originalSignature) { + if (strtr($polyfillSignature, $map) !== str_replace('?', '', $originalSignature)) { $warnings[] = TestListener::warning("Incompatible signature for PHP >= 8:\n- {$f['name']}$originalSignature\n+ {$f['name']}$polyfillSignature"); } } diff --git a/tests/Compiler.php b/tests/Compiler.php index bd61adc6e..85538460b 100644 --- a/tests/Compiler.php +++ b/tests/Compiler.php @@ -16,7 +16,7 @@ * and charset data to a format suitable for other Utf8 classes. * * See https://unicode.org/Public/UNIDATA/ for unicode data - * See https://github.com/unicode-org/cldr/blob/master/common/transforms/ for Latin-ASCII.xml + * See https://github.com/unicode-org/cldr/blob/main/common/transforms/ for Latin-ASCII.xml * * @author Nicolas Grekas * diff --git a/tests/Intl/Icu/AbstractIntlDateFormatterTest.php b/tests/Intl/Icu/AbstractIntlDateFormatterTest.php index 3e01dfa10..7761353e3 100644 --- a/tests/Intl/Icu/AbstractIntlDateFormatterTest.php +++ b/tests/Intl/Icu/AbstractIntlDateFormatterTest.php @@ -35,11 +35,11 @@ public function testConstructorDefaultTimeZone() { $formatter = $this->getDateFormatter('en', IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT); - $this->assertEquals(date_default_timezone_get(), $formatter->getTimeZoneId()); + $this->assertSame(date_default_timezone_get(), $formatter->getTimeZoneId()); - $this->assertEquals( + $this->assertSame( $this->getDateTime(0, $formatter->getTimeZoneId())->format('M j, Y, g:i A'), - $formatter->format(0) + str_replace("\u{202F}", ' ', $formatter->format(0)) ); } @@ -50,7 +50,7 @@ public function testConstructorWithoutDateType() { $formatter = $this->getDateFormatter('en', null, IntlDateFormatter::SHORT, 'UTC', IntlDateFormatter::GREGORIAN); - $this->assertSame('EEEE, MMMM d, y \'at\' h:mm a', $formatter->getPattern()); + $this->assertSame('EEEE, MMMM d, y \'at\' h:mm a', str_replace("\u{202F}", ' ', $formatter->getPattern())); } /** @@ -60,7 +60,7 @@ public function testConstructorWithoutTimeType() { $formatter = $this->getDateFormatter('en', IntlDateFormatter::SHORT, null, 'UTC', IntlDateFormatter::GREGORIAN); - $this->assertSame('M/d/yy, h:mm:ss a zzzz', $formatter->getPattern()); + $this->assertSame('M/d/yy, h:mm:ss a zzzz', str_replace("\u{202F}", ' ', $formatter->getPattern())); } /** @@ -498,7 +498,11 @@ public function testFormatIgnoresPatternForRelativeDateType() $datetime = \DateTime::createFromFormat('U', time(), new \DateTimeZone('GMT')); $datetime->setTime(0, 0, 0); - $this->assertSame('today at 12:00:00 AM Greenwich Mean Time', $formatter->format($datetime)); + $formatted = $formatter->format($datetime); + $formatted = str_replace(' at ', ', ', $formatted); + $formatted = str_replace("\u{202F}", ' ', $formatted); + + $this->assertSame('today, 12:00:00 AM Greenwich Mean Time', $formatted); } /** @@ -507,7 +511,7 @@ public function testFormatIgnoresPatternForRelativeDateType() public function testDateAndTimeType($timestamp, $datetype, $timetype, $expected) { $formatter = $this->getDateFormatter('en', $datetype, $timetype, 'UTC'); - $this->assertSame($expected, $formatter->format($timestamp)); + $this->assertSame($expected, str_replace("\u{202F}", ' ', $formatter->format($timestamp))); } public static function dateAndTimeTypeProvider() @@ -533,7 +537,14 @@ public function testRelativeDateType($timestamp, $datetype, $timetype, $expected $datetime->setTime(0, 0, 0); $formatter = $this->getDateFormatter('en', $datetype, $timetype, 'UTC'); - $this->assertSame($expected, $formatter->format($datetime)); + + $formatted = $formatter->format($datetime); + + // Ignore differences that vary by version of PHP or ICU + $formatted = str_replace(' at ', ', ', $formatted); + $formatted = str_replace("\u{202F}", ' ', $formatted); + + $this->assertSame($expected, $formatted); } public static function relativeDateTypeProvider() @@ -545,17 +556,17 @@ public static function relativeDateTypeProvider() [0, IntlDateFormatter::RELATIVE_SHORT, IntlDateFormatter::NONE, '1/1/70'], [time(), IntlDateFormatter::RELATIVE_FULL, IntlDateFormatter::NONE, 'today'], - [time(), IntlDateFormatter::RELATIVE_LONG, IntlDateFormatter::FULL, 'today at 12:00:00 AM Coordinated Universal Time'], + [time(), IntlDateFormatter::RELATIVE_LONG, IntlDateFormatter::FULL, 'today, 12:00:00 AM Coordinated Universal Time'], [time(), IntlDateFormatter::RELATIVE_MEDIUM, IntlDateFormatter::LONG, 'today, 12:00:00 AM UTC'], [time(), IntlDateFormatter::RELATIVE_SHORT, IntlDateFormatter::SHORT, 'today, 12:00 AM'], [strtotime('-1 day', time()), IntlDateFormatter::RELATIVE_FULL, IntlDateFormatter::NONE, 'yesterday'], - [strtotime('-1 day', time()), IntlDateFormatter::RELATIVE_LONG, IntlDateFormatter::FULL, 'yesterday at 12:00:00 AM Coordinated Universal Time'], + [strtotime('-1 day', time()), IntlDateFormatter::RELATIVE_LONG, IntlDateFormatter::FULL, 'yesterday, 12:00:00 AM Coordinated Universal Time'], [strtotime('-1 day', time()), IntlDateFormatter::RELATIVE_MEDIUM, IntlDateFormatter::LONG, 'yesterday, 12:00:00 AM UTC'], [strtotime('-1 day', time()), IntlDateFormatter::RELATIVE_SHORT, IntlDateFormatter::SHORT, 'yesterday, 12:00 AM'], [strtotime('+1 day', time()), IntlDateFormatter::RELATIVE_FULL, IntlDateFormatter::NONE, 'tomorrow'], - [strtotime('+1 day', time()), IntlDateFormatter::RELATIVE_LONG, IntlDateFormatter::FULL, 'tomorrow at 12:00:00 AM Coordinated Universal Time'], + [strtotime('+1 day', time()), IntlDateFormatter::RELATIVE_LONG, IntlDateFormatter::FULL, 'tomorrow, 12:00:00 AM Coordinated Universal Time'], [strtotime('+1 day', time()), IntlDateFormatter::RELATIVE_MEDIUM, IntlDateFormatter::LONG, 'tomorrow, 12:00:00 AM UTC'], [strtotime('+1 day', time()), IntlDateFormatter::RELATIVE_SHORT, IntlDateFormatter::SHORT, 'tomorrow, 12:00 AM'], ]; diff --git a/tests/Intl/Idn/IdnTest.php b/tests/Intl/Idn/IdnTest.php index 1df884372..bf5b60017 100644 --- a/tests/Intl/Idn/IdnTest.php +++ b/tests/Intl/Idn/IdnTest.php @@ -122,8 +122,12 @@ static function (array $matches): string { */ public function testToUnicode($source, $toUnicode, $toUnicodeStatus, $toAsciiN, $toAsciiNStatus, $toAsciiT, $toAsciiTStatus) { + if (\PHP_VERSION_ID >= 80400 && '' === $source) { + $this->expectException(\ValueError::class); + } + $options = \IDNA_CHECK_BIDI | \IDNA_CHECK_CONTEXTJ | \IDNA_USE_STD3_RULES | \IDNA_NONTRANSITIONAL_TO_UNICODE; - $result = idn_to_utf8($source, $options, \INTL_IDNA_VARIANT_UTS46, $info); + idn_to_utf8($source, $options, \INTL_IDNA_VARIANT_UTS46, $info); if (null === $info) { $this->markTestSkipped('PHP Bug #72506.'); @@ -150,8 +154,12 @@ public function testToUnicode($source, $toUnicode, $toUnicodeStatus, $toAsciiN, */ public function testToAsciiNonTransitional($source, $toUnicode, $toUnicodeStatus, $toAsciiN, $toAsciiNStatus, $toAsciiT, $toAsciiTStatus) { + if (\PHP_VERSION_ID >= 80400 && '' === $source) { + $this->expectException(\ValueError::class); + } + $options = \IDNA_CHECK_BIDI | \IDNA_CHECK_CONTEXTJ | \IDNA_USE_STD3_RULES | \IDNA_NONTRANSITIONAL_TO_ASCII; - $result = idn_to_ascii($source, $options, \INTL_IDNA_VARIANT_UTS46, $info); + idn_to_ascii($source, $options, \INTL_IDNA_VARIANT_UTS46, $info); if (null === $info) { $this->markTestSkipped('PHP Bug #72506.'); @@ -178,8 +186,12 @@ public function testToAsciiNonTransitional($source, $toUnicode, $toUnicodeStatus */ public function testToAsciiTransitional($source, $toUnicode, $toUnicodeStatus, $toAsciiN, $toAsciiNStatus, $toAsciiT, $toAsciiTStatus) { + if (\PHP_VERSION_ID >= 80400 && '' === $source) { + $this->expectException(\ValueError::class); + } + $options = \IDNA_CHECK_BIDI | \IDNA_CHECK_CONTEXTJ | \IDNA_USE_STD3_RULES; - $result = idn_to_ascii($source, $options, \INTL_IDNA_VARIANT_UTS46, $info); + idn_to_ascii($source, $options, \INTL_IDNA_VARIANT_UTS46, $info); if (null === $info) { $this->markTestSkipped('PHP Bug #72506.'); diff --git a/tests/Mbstring/MbstringTest.php b/tests/Mbstring/MbstringTest.php index 75bb3be00..bacaf3dbe 100644 --- a/tests/Mbstring/MbstringTest.php +++ b/tests/Mbstring/MbstringTest.php @@ -729,9 +729,9 @@ public static function paddingEncodingProvider(): iterable public static function mbStrPadInvalidArgumentsProvider(): iterable { - yield ['mb_str_pad(): Argument #3 ($pad_string) must be a non-empty string', '▶▶', 6, '', \STR_PAD_RIGHT]; - yield ['mb_str_pad(): Argument #3 ($pad_string) must be a non-empty string', '▶▶', 6, '', \STR_PAD_LEFT]; - yield ['mb_str_pad(): Argument #3 ($pad_string) must be a non-empty string', '▶▶', 6, '', \STR_PAD_BOTH]; + yield ['mb_str_pad(): Argument #3 ($pad_string)', '▶▶', 6, '', \STR_PAD_RIGHT]; + yield ['mb_str_pad(): Argument #3 ($pad_string)', '▶▶', 6, '', \STR_PAD_LEFT]; + yield ['mb_str_pad(): Argument #3 ($pad_string)', '▶▶', 6, '', \STR_PAD_BOTH]; yield ['mb_str_pad(): Argument #4 ($pad_type) must be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH', '▶▶', 6, ' ', 123456]; yield ['mb_str_pad(): Argument #5 ($encoding) must be a valid encoding, "unexisting" given', '▶▶', 6, ' ', \STR_PAD_BOTH, 'unexisting']; } diff --git a/tests/Php74/Php74Test.php b/tests/Php74/Php74Test.php index 8a4123429..44a17c995 100644 --- a/tests/Php74/Php74Test.php +++ b/tests/Php74/Php74Test.php @@ -123,11 +123,13 @@ class A private $priv = 3; } +#[\AllowDynamicProperties] class B extends A { private $priv = 4; } +#[\AllowDynamicProperties] class AO extends \ArrayObject { private $priv = 1; diff --git a/tests/Php83/Php83Test.php b/tests/Php83/Php83Test.php index 08416a9af..545a8fdc1 100644 --- a/tests/Php83/Php83Test.php +++ b/tests/Php83/Php83Test.php @@ -115,9 +115,9 @@ public static function paddingEncodingProvider(): iterable public static function mbStrPadInvalidArgumentsProvider(): iterable { - yield ['mb_str_pad(): Argument #3 ($pad_string) must be a non-empty string', '▶▶', 6, '', \STR_PAD_RIGHT]; - yield ['mb_str_pad(): Argument #3 ($pad_string) must be a non-empty string', '▶▶', 6, '', \STR_PAD_LEFT]; - yield ['mb_str_pad(): Argument #3 ($pad_string) must be a non-empty string', '▶▶', 6, '', \STR_PAD_BOTH]; + yield ['mb_str_pad(): Argument #3 ($pad_string)', '▶▶', 6, '', \STR_PAD_RIGHT]; + yield ['mb_str_pad(): Argument #3 ($pad_string)', '▶▶', 6, '', \STR_PAD_LEFT]; + yield ['mb_str_pad(): Argument #3 ($pad_string)', '▶▶', 6, '', \STR_PAD_BOTH]; yield ['mb_str_pad(): Argument #4 ($pad_type) must be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH', '▶▶', 6, ' ', 123456]; yield ['mb_str_pad(): Argument #5 ($encoding) must be a valid encoding, "unexisting" given', '▶▶', 6, ' ', \STR_PAD_BOTH, 'unexisting']; } @@ -280,7 +280,7 @@ public function testInvalidStrIncrement(string $errorMessage, string $string) public static function strInvalidIncrementProvider(): iterable { - yield ['str_increment(): Argument #1 ($string) cannot be empty', '']; + yield ['str_increment(): Argument #1 ($string)', '']; yield ['str_increment(): Argument #1 ($string) must be composed only of alphanumeric ASCII characters', '-cc']; yield ['str_increment(): Argument #1 ($string) must be composed only of alphanumeric ASCII characters', 'Z ']; yield ['str_increment(): Argument #1 ($string) must be composed only of alphanumeric ASCII characters', ' Z']; @@ -311,7 +311,7 @@ public function testInvalidStrDecrement(string $errorMessage, string $string) public static function strInvalidDecrementProvider(): iterable { - yield ['str_decrement(): Argument #1 ($string) cannot be empty', '']; + yield ['str_decrement(): Argument #1 ($string)', '']; yield ['str_decrement(): Argument #1 ($string) must be composed only of alphanumeric ASCII characters', '我喜歡雞肉']; yield ['str_decrement(): Argument #1 ($string) "0" is out of decrement range', '0']; yield ['str_decrement(): Argument #1 ($string) "a" is out of decrement range', 'a']; diff --git a/tests/update-unidata.php b/tests/update-unidata.php index 1ce8c3863..10b082f93 100755 --- a/tests/update-unidata.php +++ b/tests/update-unidata.php @@ -21,7 +21,7 @@ file_put_contents(__DIR__.'/unicode/data/'.$file, $data); } -$data = file_get_contents('https://github.com/unicode-org/cldr/raw/master/common/transforms/Latin-ASCII.xml'); +$data = file_get_contents('https://github.com/unicode-org/cldr/raw/main/common/transforms/Latin-ASCII.xml'); file_put_contents(__DIR__.'/unicode/data/Latin-ASCII.xml', $data); Compiler::translitMap(__DIR__.'/../src/Iconv/Resources/charset/');