[Web] Update composer libraries

This commit is contained in:
FreddleSpl0it committed 2026-10-28 11:56:29 +01:00
1 parent 9cc8ac2908
commit bd8c784118
1297 files changed
+71585 -24460

No files matched your search

+25
View File
@@ -0,0 +1,25 @@
CHANGELOG
=========
7.1
---
* Add `DatePoint::getMicrosecond()` and `DatePoint::setMicrosecond()`
6.4
---
* Add `DatePoint`: an immutable DateTime implementation with stricter error handling and return types
* Throw `DateMalformedStringException`/`DateInvalidTimeZoneException` when appropriate
* Add `$modifier` argument to the `now()` helper
6.3
---
* Add `ClockAwareTrait` to help write time-sensitive classes
* Add `Clock` class and `now()` function
6.2
---
* Add the component
+89
View File
@@ -0,0 +1,89 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Clock;
use Psr\Clock\ClockInterface as PsrClockInterface;
/**
* A global clock.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
final class Clock implements ClockInterface
{
private static ClockInterface $globalClock;
public function __construct(
private readonly ?PsrClockInterface $clock = null,
private ?\DateTimeZone $timezone = null,
) {
}
/**
* Returns the current global clock.
*
* Note that you should prefer injecting a ClockInterface or using
* ClockAwareTrait when possible instead of using this method.
*/
public static function get(): ClockInterface
{
return self::$globalClock ??= new NativeClock();
}
public static function set(PsrClockInterface $clock): void
{
self::$globalClock = $clock instanceof ClockInterface ? $clock : new self($clock);
}
public function now(): DatePoint
{
$now = ($this->clock ?? self::get())->now();
if (!$now instanceof DatePoint) {
$now = DatePoint::createFromInterface($now);
}
return isset($this->timezone) ? $now->setTimezone($this->timezone) : $now;
}
public function sleep(float|int $seconds): void
{
$clock = $this->clock ?? self::get();
if ($clock instanceof ClockInterface) {
$clock->sleep($seconds);
} else {
(new NativeClock())->sleep($seconds);
}
}
/**
* @throws \DateInvalidTimeZoneException When $timezone is invalid
*/
public function withTimeZone(\DateTimeZone|string $timezone): static
{
if (\PHP_VERSION_ID >= 80300 && \is_string($timezone)) {
$timezone = new \DateTimeZone($timezone);
} elseif (\is_string($timezone)) {
try {
$timezone = new \DateTimeZone($timezone);
} catch (\Exception $e) {
throw new \DateInvalidTimeZoneException($e->getMessage(), $e->getCode(), $e);
}
}
$clone = clone $this;
$clone->timezone = $timezone;
return $clone;
}
}
@@ -0,0 +1,38 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Clock;
use Psr\Clock\ClockInterface;
use Symfony\Contracts\Service\Attribute\Required;
/**
* A trait to help write time-sensitive classes.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
trait ClockAwareTrait
{
private readonly ClockInterface $clock;
#[Required]
public function setClock(ClockInterface $clock): void
{
$this->clock = $clock;
}
protected function now(): DatePoint
{
$now = ($this->clock ??= new Clock())->now();
return $now instanceof DatePoint ? $now : DatePoint::createFromInterface($now);
}
}
@@ -0,0 +1,24 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Clock;
use Psr\Clock\ClockInterface as PsrClockInterface;
/**
* @author Nicolas Grekas <p@tchwork.com>
*/
interface ClockInterface extends PsrClockInterface
{
public function sleep(float|int $seconds): void;
public function withTimeZone(\DateTimeZone|string $timezone): static;
}
+205
View File
@@ -0,0 +1,205 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Clock;
/**
* An immmutable DateTime with stricter error handling and return types than the native one.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
final class DatePoint extends \DateTimeImmutable
{
/**
* @throws \DateMalformedStringException When $datetime is invalid
*/
public function __construct(string $datetime = 'now', ?\DateTimeZone $timezone = null, ?parent $reference = null)
{
if (null === $reference) {
$clock = Clock::get();
// copying the clock's date into $this goes through its local time, which is ambiguous during the DST fall-back hour
if ('now' === $datetime && $clock instanceof NativeClock) {
parent::__construct('now', $timezone ?? $clock->now()->getTimezone());
return;
}
$reference = $clock->now();
}
$now = $reference;
if ('now' !== $datetime) {
if (!$now instanceof static) {
$now = static::createFromInterface($now);
}
if (\PHP_VERSION_ID < 80300) {
try {
$builtInDate = new parent($datetime, $timezone ?? $now->getTimezone());
$timezone = $builtInDate->getTimezone();
} catch (\Exception $e) {
throw new \DateMalformedStringException($e->getMessage(), $e->getCode(), $e);
}
} else {
$builtInDate = new parent($datetime, $timezone ?? $now->getTimezone());
$timezone = $builtInDate->getTimezone();
}
$parsed = date_parse($datetime);
if (false !== $parsed['year'] && false !== $parsed['month'] && false !== $parsed['day']) {
// the native result does not depend on the current time when $datetime has a full date
$now = $builtInDate;
} else {
$now = $now->setTimezone($timezone);
// the native constructor starts from midnight when $datetime has a date but no time, modify() does not
if (false === $parsed['hour'] && (false !== $parsed['month'] || false !== $parsed['day'])) {
$datetime = 'midnight '.$datetime;
} elseif (false === $parsed['fraction'] && (false !== $parsed['year'] || false !== $parsed['hour'])) {
// it also resets the microseconds when $datetime sets a field without a fraction
$now = \PHP_INT_SIZE >= 8 ? $now->setTimestamp($now->getTimestamp()) : static::createFromFormat('U', $now->format('U'))->setTimezone($timezone);
}
$now = $now->modify($datetime);
}
} elseif (null !== $timezone) {
$now = $now->setTimezone($timezone);
}
$this->__unserialize((array) $now);
// copying $now goes through its local time, which is ambiguous during the DST fall-back hour
if ($this != $now) {
// PHP picks the DST occurrence when the timezone is part of the string
$date = $now->format('Y-m-d H:i:s.u e');
if ($now == new parent($date)) {
parent::__construct($date);
}
}
}
/**
* @throws \DateMalformedStringException When $format or $datetime are invalid
*/
public static function createFromFormat(string $format, string $datetime, ?\DateTimeZone $timezone = null): static
{
return parent::createFromFormat($format, $datetime, $timezone) ?: throw new \DateMalformedStringException(static::getLastErrors()['errors'][0] ?? 'Invalid date string or format.');
}
public static function createFromInterface(\DateTimeInterface $object): static
{
return parent::createFromInterface($object);
}
public static function createFromMutable(\DateTime $object): static
{
return parent::createFromMutable($object);
}
public static function createFromTimestamp(int|float $timestamp): static
{
if (\PHP_VERSION_ID >= 80400) {
return parent::createFromTimestamp($timestamp);
}
if (\is_int($timestamp) || !$ms = (int) $timestamp - $timestamp) {
return static::createFromFormat('U', (string) $timestamp);
}
if (!is_finite($timestamp) || \PHP_INT_MAX + 1.0 <= $timestamp || \PHP_INT_MIN > $timestamp) {
throw new \DateRangeError(\sprintf('DateTimeImmutable::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between %s and %s.999999, %s given', \PHP_INT_MIN, \PHP_INT_MAX, $timestamp));
}
if ($timestamp < 0) {
$timestamp = (int) $timestamp - 2.0 + $ms;
}
return static::createFromFormat('U.u', \sprintf('%.6F', $timestamp));
}
public function add(\DateInterval $interval): static
{
return parent::add($interval);
}
public function sub(\DateInterval $interval): static
{
return parent::sub($interval);
}
/**
* @throws \DateMalformedStringException When $modifier is invalid
*/
public function modify(string $modifier): static
{
if (\PHP_VERSION_ID < 80300) {
return @parent::modify($modifier) ?: throw new \DateMalformedStringException(error_get_last()['message'] ?? \sprintf('Invalid modifier: "%s".', $modifier));
}
return parent::modify($modifier);
}
public function setTimestamp(int $value): static
{
return parent::setTimestamp($value);
}
public function setDate(int $year, int $month, int $day): static
{
return parent::setDate($year, $month, $day);
}
public function setISODate(int $year, int $week, int $day = 1): static
{
return parent::setISODate($year, $week, $day);
}
public function setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0): static
{
return parent::setTime($hour, $minute, $second, $microsecond);
}
public function setTimezone(\DateTimeZone $timezone): static
{
return parent::setTimezone($timezone);
}
public function getTimezone(): \DateTimeZone
{
return parent::getTimezone() ?: throw new \DateInvalidTimeZoneException('The DatePoint object has no timezone.');
}
public function setMicrosecond(int $microsecond): static
{
if ($microsecond < 0 || $microsecond > 999999) {
throw new \DateRangeError('DatePoint::setMicrosecond(): Argument #1 ($microsecond) must be between 0 and 999999, '.$microsecond.' given');
}
if (\PHP_VERSION_ID < 80400) {
return $this->setTime(...explode('.', $this->format('H.i.s.'.$microsecond)));
}
return parent::setMicrosecond($microsecond);
}
public function getMicrosecond(): int
{
if (\PHP_VERSION_ID >= 80400) {
return parent::getMicrosecond();
}
return $this->format('u');
}
}
@@ -1,4 +1,4 @@
Copyright (c) 2021-present Fabien Potencier
Copyright (c) 2022-present Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+106
View File
@@ -0,0 +1,106 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Clock;
/**
* A clock that always returns the same date, suitable for testing time-sensitive logic.
*
* Consider using ClockSensitiveTrait in your test cases instead of using this class directly.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
final class MockClock implements ClockInterface
{
private DatePoint $now;
/**
* @throws \DateMalformedStringException When $now is invalid
* @throws \DateInvalidTimeZoneException When $timezone is invalid
*/
public function __construct(\DateTimeImmutable|string $now = 'now', \DateTimeZone|string|null $timezone = null)
{
if (\PHP_VERSION_ID >= 80300 && \is_string($timezone)) {
$timezone = new \DateTimeZone($timezone);
} elseif (\is_string($timezone)) {
try {
$timezone = new \DateTimeZone($timezone);
} catch (\Exception $e) {
throw new \DateInvalidTimeZoneException($e->getMessage(), $e->getCode(), $e);
}
}
if (\is_string($now)) {
$now = new DatePoint($now, $timezone ?? new \DateTimeZone('UTC'));
} elseif (!$now instanceof DatePoint) {
$now = DatePoint::createFromInterface($now);
}
$this->now = null !== $timezone ? $now->setTimezone($timezone) : $now;
}
public function now(): DatePoint
{
return clone $this->now;
}
public function sleep(float|int $seconds): void
{
if (0 >= $seconds) {
return;
}
$s = (float) $this->now->format('U') + floor($seconds);
$us = (int) $this->now->format('u') + (int) round(fmod($seconds, 1) * 1e6);
if (1000000 <= $us) {
++$s;
$us -= 1000000;
}
$this->now = DatePoint::createFromFormat('U.u', \sprintf('%.0F.%06d', $s, $us))->setTimezone($this->now->getTimezone());
}
/**
* @throws \DateMalformedStringException When $modifier is invalid
*/
public function modify(string $modifier): void
{
if (\PHP_VERSION_ID < 80300) {
$this->now = @$this->now->modify($modifier) ?: throw new \DateMalformedStringException(error_get_last()['message'] ?? \sprintf('Invalid modifier: "%s". Could not modify MockClock.', $modifier));
return;
}
$this->now = $this->now->modify($modifier);
}
/**
* @throws \DateInvalidTimeZoneException When the timezone name is invalid
*/
public function withTimeZone(\DateTimeZone|string $timezone): static
{
if (\PHP_VERSION_ID >= 80300 && \is_string($timezone)) {
$timezone = new \DateTimeZone($timezone);
} elseif (\is_string($timezone)) {
try {
$timezone = new \DateTimeZone($timezone);
} catch (\Exception $e) {
throw new \DateInvalidTimeZoneException($e->getMessage(), $e->getCode(), $e);
}
}
$clone = clone $this;
$clone->now = $clone->now->setTimezone($timezone);
return $clone;
}
}
@@ -0,0 +1,93 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Clock;
/**
* A monotonic clock suitable for performance profiling.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
final class MonotonicClock implements ClockInterface
{
private int $sOffset;
private int $usOffset;
private \DateTimeZone $timezone;
/**
* @throws \DateInvalidTimeZoneException When $timezone is invalid
*/
public function __construct(\DateTimeZone|string|null $timezone = null)
{
if (false === $offset = hrtime()) {
throw new \RuntimeException('hrtime() returned false: the runtime environment does not provide access to a monotonic timer.');
}
$time = explode(' ', microtime(), 2);
$this->sOffset = $time[1] - $offset[0];
$this->usOffset = (int) ($time[0] * 1000000) - (int) ($offset[1] / 1000);
$this->timezone = \is_string($timezone ??= date_default_timezone_get()) ? $this->withTimeZone($timezone)->timezone : $timezone;
}
public function now(): DatePoint
{
[$s, $us] = hrtime();
if (1000000 <= $us = (int) ($us / 1000) + $this->usOffset) {
++$s;
$us -= 1000000;
} elseif (0 > $us) {
--$s;
$us += 1000000;
}
if (6 !== \strlen($now = (string) $us)) {
$now = str_pad($now, 6, '0', \STR_PAD_LEFT);
}
$now = '@'.($s + $this->sOffset).'.'.$now;
return DatePoint::createFromInterface(new \DateTimeImmutable($now, $this->timezone))->setTimezone($this->timezone);
}
public function sleep(float|int $seconds): void
{
if (0 < $s = (int) $seconds) {
sleep($s);
}
if (0 < $us = $seconds - $s) {
usleep((int) ($us * 1E6));
}
}
/**
* @throws \DateInvalidTimeZoneException When $timezone is invalid
*/
public function withTimeZone(\DateTimeZone|string $timezone): static
{
if (\PHP_VERSION_ID >= 80300 && \is_string($timezone)) {
$timezone = new \DateTimeZone($timezone);
} elseif (\is_string($timezone)) {
try {
$timezone = new \DateTimeZone($timezone);
} catch (\Exception $e) {
throw new \DateInvalidTimeZoneException($e->getMessage(), $e->getCode(), $e);
}
}
$clone = clone $this;
$clone->timezone = $timezone;
return $clone;
}
}
+67
View File
@@ -0,0 +1,67 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Clock;
/**
* A clock that relies the system time.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
final class NativeClock implements ClockInterface
{
private \DateTimeZone $timezone;
/**
* @throws \DateInvalidTimeZoneException When $timezone is invalid
*/
public function __construct(\DateTimeZone|string|null $timezone = null)
{
$this->timezone = \is_string($timezone ??= date_default_timezone_get()) ? $this->withTimeZone($timezone)->timezone : $timezone;
}
public function now(): DatePoint
{
return DatePoint::createFromInterface(new \DateTimeImmutable('now', $this->timezone));
}
public function sleep(float|int $seconds): void
{
if (0 < $s = (int) $seconds) {
sleep($s);
}
if (0 < $us = $seconds - $s) {
usleep((int) ($us * 1E6));
}
}
/**
* @throws \DateInvalidTimeZoneException When $timezone is invalid
*/
public function withTimeZone(\DateTimeZone|string $timezone): static
{
if (\PHP_VERSION_ID >= 80300 && \is_string($timezone)) {
$timezone = new \DateTimeZone($timezone);
} elseif (\is_string($timezone)) {
try {
$timezone = new \DateTimeZone($timezone);
} catch (\Exception $e) {
throw new \DateInvalidTimeZoneException($e->getMessage(), $e->getCode(), $e);
}
}
$clone = clone $this;
$clone->timezone = $timezone;
return $clone;
}
}
+47
View File
@@ -0,0 +1,47 @@
Clock Component
===============
Symfony Clock decouples applications from the system clock.
Getting Started
---------------
```bash
composer require symfony/clock
```
```php
use Symfony\Component\Clock\NativeClock;
use Symfony\Component\Clock\ClockInterface;
class MyClockSensitiveClass
{
public function __construct(
private ClockInterface $clock,
) {
// Only if you need to force a timezone:
//$this->clock = $clock->withTimeZone('UTC');
}
public function doSomething()
{
$now = $this->clock->now();
// [...] do something with $now, which is a \DateTimeImmutable object
$this->clock->sleep(2.5); // Pause execution for 2.5 seconds
}
}
$clock = new NativeClock();
$service = new MyClockSensitiveClass($clock);
$service->doSomething();
```
Resources
---------
* [Documentation](https://symfony.com/doc/current/components/clock.html)
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)
in the [main Symfony repository](https://github.com/symfony/symfony)
+28
View File
@@ -0,0 +1,28 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Clock;
if (!\function_exists(now::class)) {
/**
* @throws \DateMalformedStringException When the modifier is invalid
*/
function now(string $modifier = 'now'): DatePoint
{
if ('now' !== $modifier) {
return new DatePoint($modifier);
}
$now = Clock::get()->now();
return $now instanceof DatePoint ? $now : DatePoint::createFromInterface($now);
}
}
@@ -0,0 +1,77 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Clock\Test;
use PHPUnit\Framework\Attributes\After;
use PHPUnit\Framework\Attributes\Before;
use PHPUnit\Framework\Attributes\BeforeClass;
use Symfony\Component\Clock\Clock;
use Symfony\Component\Clock\ClockInterface;
use Symfony\Component\Clock\MockClock;
use function Symfony\Component\Clock\now;
/**
* Helps with mocking the time in your test cases.
*
* This trait provides one self::mockTime() method that freezes the time.
* It restores the global clock after each test case.
* self::mockTime() accepts either a string (eg '+1 days' or '2022-12-22'),
* a DateTimeImmutable, or a boolean (to freeze/restore the global clock).
*
* @author Nicolas Grekas <p@tchwork.com>
*/
trait ClockSensitiveTrait
{
public static function mockTime(string|\DateTimeImmutable|bool $when = true): ClockInterface
{
Clock::set(match (true) {
false === $when => self::saveClockBeforeTest(false),
true === $when => new MockClock(),
$when instanceof \DateTimeImmutable => new MockClock($when),
default => new MockClock(now($when)),
});
return Clock::get();
}
/**
* @beforeClass
*
* @before
*
* @internal
*/
#[Before]
#[BeforeClass]
public static function saveClockBeforeTest(bool $save = true): ClockInterface
{
static $originalClock;
if ($save && $originalClock) {
self::restoreClockAfterTest();
}
return $save ? $originalClock = Clock::get() : $originalClock;
}
/**
* @after
*
* @internal
*/
#[After]
protected static function restoreClockAfterTest(): void
{
Clock::set(self::saveClockBeforeTest(false));
}
}
+34
View File
@@ -0,0 +1,34 @@
{
"name": "symfony/clock",
"type": "library",
"description": "Decouples applications from the system clock",
"keywords": ["clock", "time", "psr20"],
"homepage": "https://symfony.com",
"license": "MIT",
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"provide": {
"psr/clock-implementation": "1.0"
},
"require": {
"php": ">=8.2",
"psr/clock": "^1.0",
"symfony/polyfill-php83": "^1.28"
},
"autoload": {
"files": [ "Resources/now.php" ],
"psr-4": { "Symfony\\Component\\Clock\\": "" },
"exclude-from-classmap": [
"/Tests/"
]
},
"minimum-stability": "dev"
}
@@ -25,7 +25,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-main": "3.5-dev"
"dev-main": "3.7-dev"
},
"thanks": {
"name": "symfony/contracts",
+4 -4
View File
@@ -211,6 +211,10 @@ final class Ctype
*/
private static function convert_int_to_char_for_ctype($int, $function)
{
if (\PHP_VERSION_ID >= 80100 && !\is_string($int)) {
@trigger_error($function.'(): Argument of type '.get_debug_type($int).' will be interpreted as string in the future', \E_USER_DEPRECATED);
}
if (!\is_int($int)) {
return $int;
}
@@ -219,10 +223,6 @@ final class Ctype
return (string) $int;
}
if (\PHP_VERSION_ID >= 80100) {
@trigger_error($function.'(): Argument of type int will be interpreted as string in the future', \E_USER_DEPRECATED);
}
if ($int < 0) {
$int += 256;
}
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2018-2019 Fabien Potencier
Copyright (c) 2018-present Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+1 -1
View File
@@ -4,7 +4,7 @@ Symfony Polyfill / Ctype
This component provides `ctype_*` functions to users who run php versions without the ctype extension.
More information can be found in the
[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md).
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).
License
=======
@@ -16,7 +16,7 @@
}
],
"require": {
"php": ">=7.1"
"php": ">=7.2"
},
"provide": {
"ext-ctype": "*"
@@ -30,9 +30,6 @@
},
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-main": "1.23-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,5 @@
<?php
// from W and F in https://unicode.org/Public/UNIDATA/EastAsianWidth.txt
return '/[\x{1100}-\x{115F}\x{231A}-\x{231B}\x{2329}-\x{232A}\x{23E9}-\x{23EC}\x{23F0}\x{23F3}\x{25FD}-\x{25FE}\x{2614}-\x{2615}\x{2630}-\x{2637}\x{2648}-\x{2653}\x{267F}\x{268A}-\x{268F}\x{2693}\x{26A1}\x{26AA}-\x{26AB}\x{26BD}-\x{26BE}\x{26C4}-\x{26C5}\x{26CE}\x{26D4}\x{26EA}\x{26F2}-\x{26F3}\x{26F5}\x{26FA}\x{26FD}\x{2705}\x{270A}-\x{270B}\x{2728}\x{274C}\x{274E}\x{2753}-\x{2755}\x{2757}\x{2795}-\x{2797}\x{27B0}\x{27BF}\x{2B1B}-\x{2B1C}\x{2B50}\x{2B55}\x{2E80}-\x{2E99}\x{2E9B}-\x{2EF3}\x{2F00}-\x{2FD5}\x{2FF0}-\x{303E}\x{3041}-\x{3096}\x{3099}-\x{30FF}\x{3105}-\x{312F}\x{3131}-\x{318E}\x{3190}-\x{31E5}\x{31EF}-\x{321E}\x{3220}-\x{3247}\x{3250}-\x{A48C}\x{A490}-\x{A4C6}\x{A960}-\x{A97C}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE52}\x{FE54}-\x{FE66}\x{FE68}-\x{FE6B}\x{FF01}-\x{FF60}\x{FFE0}-\x{FFE6}\x{16FE0}-\x{16FE4}\x{16FF0}-\x{16FF6}\x{17000}-\x{18CD5}\x{18CFF}-\x{18D1E}\x{18D80}-\x{18DF2}\x{1AFF0}-\x{1AFF3}\x{1AFF5}-\x{1AFFB}\x{1AFFD}-\x{1AFFE}\x{1B000}-\x{1B122}\x{1B132}\x{1B150}-\x{1B152}\x{1B155}\x{1B164}-\x{1B167}\x{1B170}-\x{1B2FB}\x{1D300}-\x{1D356}\x{1D360}-\x{1D376}\x{1F004}\x{1F0CF}\x{1F18E}\x{1F191}-\x{1F19A}\x{1F200}-\x{1F202}\x{1F210}-\x{1F23B}\x{1F240}-\x{1F248}\x{1F250}-\x{1F251}\x{1F260}-\x{1F265}\x{1F300}-\x{1F320}\x{1F32D}-\x{1F335}\x{1F337}-\x{1F37C}\x{1F37E}-\x{1F393}\x{1F3A0}-\x{1F3CA}\x{1F3CF}-\x{1F3D3}\x{1F3E0}-\x{1F3F0}\x{1F3F4}\x{1F3F8}-\x{1F43E}\x{1F440}\x{1F442}-\x{1F4FC}\x{1F4FF}-\x{1F53D}\x{1F54B}-\x{1F54E}\x{1F550}-\x{1F567}\x{1F57A}\x{1F595}-\x{1F596}\x{1F5A4}\x{1F5FB}-\x{1F64F}\x{1F680}-\x{1F6C5}\x{1F6CC}\x{1F6D0}-\x{1F6D2}\x{1F6D5}-\x{1F6D8}\x{1F6DC}-\x{1F6DF}\x{1F6EB}-\x{1F6EC}\x{1F6F4}-\x{1F6FC}\x{1F7E0}-\x{1F7EB}\x{1F7F0}\x{1F90C}-\x{1F93A}\x{1F93C}-\x{1F945}\x{1F947}-\x{1F9FF}\x{1FA70}-\x{1FA7C}\x{1FA80}-\x{1FA8A}\x{1FA8E}-\x{1FAC6}\x{1FAC8}\x{1FACD}-\x{1FADC}\x{1FADF}-\x{1FAEA}\x{1FAEF}-\x{1FAF8}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u';
@@ -0,0 +1,139 @@
<?php
return array (
'DŽ' => 'Dž',
'Dž' => 'Dž',
'dž' => 'Dž',
'LJ' => 'Lj',
'Lj' => 'Lj',
'lj' => 'Lj',
'NJ' => 'Nj',
'Nj' => 'Nj',
'nj' => 'Nj',
'DZ' => 'Dz',
'Dz' => 'Dz',
'dz' => 'Dz',
'ა' => 'ა',
'ბ' => 'ბ',
'გ' => 'გ',
'დ' => 'დ',
'ე' => 'ე',
'ვ' => 'ვ',
'ზ' => 'ზ',
'თ' => 'თ',
'ი' => 'ი',
'კ' => 'კ',
'ლ' => 'ლ',
'მ' => 'მ',
'ნ' => 'ნ',
'ო' => 'ო',
'პ' => 'პ',
'ჟ' => 'ჟ',
'რ' => 'რ',
'ს' => 'ს',
'ტ' => 'ტ',
'უ' => 'უ',
'ფ' => 'ფ',
'ქ' => 'ქ',
'ღ' => 'ღ',
'ყ' => 'ყ',
'შ' => 'შ',
'ჩ' => 'ჩ',
'ც' => 'ც',
'ძ' => 'ძ',
'წ' => 'წ',
'ჭ' => 'ჭ',
'ხ' => 'ხ',
'ჯ' => 'ჯ',
'ჰ' => 'ჰ',
'ჱ' => 'ჱ',
'ჲ' => 'ჲ',
'ჳ' => 'ჳ',
'ჴ' => 'ჴ',
'ჵ' => 'ჵ',
'ჶ' => 'ჶ',
'ჷ' => 'ჷ',
'ჸ' => 'ჸ',
'ჹ' => 'ჹ',
'ჺ' => 'ჺ',
'ჽ' => 'ჽ',
'ჾ' => 'ჾ',
'ჿ' => 'ჿ',
'ß' => 'Ss',
'ff' => 'Ff',
'fi' => 'Fi',
'fl' => 'Fl',
'ffi' => 'Ffi',
'ffl' => 'Ffl',
'ſt' => 'St',
'st' => 'St',
'և' => 'Եւ',
'ﬓ' => 'Մն',
'ﬔ' => 'Մե',
'ﬕ' => 'Մի',
'ﬖ' => 'Վն',
'ﬗ' => 'Մխ',
'ᾀ' => 'ᾈ',
'ᾁ' => 'ᾉ',
'ᾂ' => 'ᾊ',
'ᾃ' => 'ᾋ',
'ᾄ' => 'ᾌ',
'ᾅ' => 'ᾍ',
'ᾆ' => 'ᾎ',
'ᾇ' => 'ᾏ',
'ᾈ' => 'ᾈ',
'ᾉ' => 'ᾉ',
'ᾊ' => 'ᾊ',
'ᾋ' => 'ᾋ',
'ᾌ' => 'ᾌ',
'ᾍ' => 'ᾍ',
'ᾎ' => 'ᾎ',
'ᾏ' => 'ᾏ',
'ᾐ' => 'ᾘ',
'ᾑ' => 'ᾙ',
'ᾒ' => 'ᾚ',
'ᾓ' => 'ᾛ',
'ᾔ' => 'ᾜ',
'ᾕ' => 'ᾝ',
'ᾖ' => 'ᾞ',
'ᾗ' => 'ᾟ',
'ᾘ' => 'ᾘ',
'ᾙ' => 'ᾙ',
'ᾚ' => 'ᾚ',
'ᾛ' => 'ᾛ',
'ᾜ' => 'ᾜ',
'ᾝ' => 'ᾝ',
'ᾞ' => 'ᾞ',
'ᾟ' => 'ᾟ',
'ᾠ' => 'ᾨ',
'ᾡ' => 'ᾩ',
'ᾢ' => 'ᾪ',
'ᾣ' => 'ᾫ',
'ᾤ' => 'ᾬ',
'ᾥ' => 'ᾭ',
'ᾦ' => 'ᾮ',
'ᾧ' => 'ᾯ',
'ᾨ' => 'ᾨ',
'ᾩ' => 'ᾩ',
'ᾪ' => 'ᾪ',
'ᾫ' => 'ᾫ',
'ᾬ' => 'ᾬ',
'ᾭ' => 'ᾭ',
'ᾮ' => 'ᾮ',
'ᾯ' => 'ᾯ',
'ᾳ' => 'ᾼ',
'ᾼ' => 'ᾼ',
'ῃ' => 'ῌ',
'ῌ' => 'ῌ',
'ῳ' => 'ῼ',
'ῼ' => 'ῼ',
'ᾲ' => 'Ὰͅ',
'ᾴ' => 'Άͅ',
'ῂ' => 'Ὴͅ',
'ῄ' => 'Ήͅ',
'ῲ' => 'Ὼͅ',
'ῴ' => 'Ώͅ',
'ᾷ' => 'ᾼ͂',
'ῇ' => 'ῌ͂',
'ῷ' => 'ῼ͂',
);
@@ -9,143 +9,8 @@
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Mbstring as p;
if (\PHP_VERSION_ID >= 80000) {
return require __DIR__.'/bootstrap80.php';
}
if (!function_exists('mb_convert_encoding')) {
function mb_convert_encoding($string, $to_encoding, $from_encoding = null) { return p\Mbstring::mb_convert_encoding($string, $to_encoding, $from_encoding); }
}
if (!function_exists('mb_decode_mimeheader')) {
function mb_decode_mimeheader($string) { return p\Mbstring::mb_decode_mimeheader($string); }
}
if (!function_exists('mb_encode_mimeheader')) {
function mb_encode_mimeheader($string, $charset = null, $transfer_encoding = null, $newline = "\r\n", $indent = 0) { return p\Mbstring::mb_encode_mimeheader($string, $charset, $transfer_encoding, $newline, $indent); }
}
if (!function_exists('mb_decode_numericentity')) {
function mb_decode_numericentity($string, $map, $encoding = null) { return p\Mbstring::mb_decode_numericentity($string, $map, $encoding); }
}
if (!function_exists('mb_encode_numericentity')) {
function mb_encode_numericentity($string, $map, $encoding = null, $hex = false) { return p\Mbstring::mb_encode_numericentity($string, $map, $encoding, $hex); }
}
if (!function_exists('mb_convert_case')) {
function mb_convert_case($string, $mode, $encoding = null) { return p\Mbstring::mb_convert_case($string, $mode, $encoding); }
}
if (!function_exists('mb_internal_encoding')) {
function mb_internal_encoding($encoding = null) { return p\Mbstring::mb_internal_encoding($encoding); }
}
if (!function_exists('mb_language')) {
function mb_language($language = null) { return p\Mbstring::mb_language($language); }
}
if (!function_exists('mb_list_encodings')) {
function mb_list_encodings() { return p\Mbstring::mb_list_encodings(); }
}
if (!function_exists('mb_encoding_aliases')) {
function mb_encoding_aliases($encoding) { return p\Mbstring::mb_encoding_aliases($encoding); }
}
if (!function_exists('mb_check_encoding')) {
function mb_check_encoding($value = null, $encoding = null) { return p\Mbstring::mb_check_encoding($value, $encoding); }
}
if (!function_exists('mb_detect_encoding')) {
function mb_detect_encoding($string, $encodings = null, $strict = false) { return p\Mbstring::mb_detect_encoding($string, $encodings, $strict); }
}
if (!function_exists('mb_detect_order')) {
function mb_detect_order($encoding = null) { return p\Mbstring::mb_detect_order($encoding); }
}
if (!function_exists('mb_parse_str')) {
function mb_parse_str($string, &$result = []) { parse_str($string, $result); return (bool) $result; }
}
if (!function_exists('mb_strlen')) {
function mb_strlen($string, $encoding = null) { return p\Mbstring::mb_strlen($string, $encoding); }
}
if (!function_exists('mb_strpos')) {
function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strpos($haystack, $needle, $offset, $encoding); }
}
if (!function_exists('mb_strtolower')) {
function mb_strtolower($string, $encoding = null) { return p\Mbstring::mb_strtolower($string, $encoding); }
}
if (!function_exists('mb_strtoupper')) {
function mb_strtoupper($string, $encoding = null) { return p\Mbstring::mb_strtoupper($string, $encoding); }
}
if (!function_exists('mb_substitute_character')) {
function mb_substitute_character($substitute_character = null) { return p\Mbstring::mb_substitute_character($substitute_character); }
}
if (!function_exists('mb_substr')) {
function mb_substr($string, $start, $length = 2147483647, $encoding = null) { return p\Mbstring::mb_substr($string, $start, $length, $encoding); }
}
if (!function_exists('mb_stripos')) {
function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_stripos($haystack, $needle, $offset, $encoding); }
}
if (!function_exists('mb_stristr')) {
function mb_stristr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_stristr($haystack, $needle, $before_needle, $encoding); }
}
if (!function_exists('mb_strrchr')) {
function mb_strrchr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strrchr($haystack, $needle, $before_needle, $encoding); }
}
if (!function_exists('mb_strrichr')) {
function mb_strrichr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strrichr($haystack, $needle, $before_needle, $encoding); }
}
if (!function_exists('mb_strripos')) {
function mb_strripos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strripos($haystack, $needle, $offset, $encoding); }
}
if (!function_exists('mb_strrpos')) {
function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strrpos($haystack, $needle, $offset, $encoding); }
}
if (!function_exists('mb_strstr')) {
function mb_strstr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strstr($haystack, $needle, $before_needle, $encoding); }
}
if (!function_exists('mb_get_info')) {
function mb_get_info($type = 'all') { return p\Mbstring::mb_get_info($type); }
}
if (!function_exists('mb_http_output')) {
function mb_http_output($encoding = null) { return p\Mbstring::mb_http_output($encoding); }
}
if (!function_exists('mb_strwidth')) {
function mb_strwidth($string, $encoding = null) { return p\Mbstring::mb_strwidth($string, $encoding); }
}
if (!function_exists('mb_substr_count')) {
function mb_substr_count($haystack, $needle, $encoding = null) { return p\Mbstring::mb_substr_count($haystack, $needle, $encoding); }
}
if (!function_exists('mb_output_handler')) {
function mb_output_handler($string, $status) { return p\Mbstring::mb_output_handler($string, $status); }
}
if (!function_exists('mb_http_input')) {
function mb_http_input($type = null) { return p\Mbstring::mb_http_input($type); }
}
if (!function_exists('mb_convert_variables')) {
function mb_convert_variables($to_encoding, $from_encoding, &...$vars) { return p\Mbstring::mb_convert_variables($to_encoding, $from_encoding, ...$vars); }
}
if (!function_exists('mb_ord')) {
function mb_ord($string, $encoding = null) { return p\Mbstring::mb_ord($string, $encoding); }
}
if (!function_exists('mb_chr')) {
function mb_chr($codepoint, $encoding = null) { return p\Mbstring::mb_chr($codepoint, $encoding); }
}
if (!function_exists('mb_scrub')) {
function mb_scrub($string, $encoding = null) { $encoding = null === $encoding ? mb_internal_encoding() : $encoding; return mb_convert_encoding($string, $encoding, $encoding); }
}
if (!function_exists('mb_str_split')) {
function mb_str_split($string, $length = 1, $encoding = null) { return p\Mbstring::mb_str_split($string, $length, $encoding); }
}
if (!function_exists('mb_str_pad')) {
function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = STR_PAD_RIGHT, ?string $encoding = null): string { return p\Mbstring::mb_str_pad($string, $length, $pad_string, $pad_type, $encoding); }
}
if (extension_loaded('mbstring')) {
return;
}
if (!defined('MB_CASE_UPPER')) {
define('MB_CASE_UPPER', 0);
}
if (!defined('MB_CASE_LOWER')) {
define('MB_CASE_LOWER', 1);
}
if (!defined('MB_CASE_TITLE')) {
define('MB_CASE_TITLE', 2);
}
return require __DIR__.'/bootstrap72.php';
@@ -0,0 +1,173 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Mbstring as p;
if (!function_exists('mb_convert_encoding')) {
function mb_convert_encoding($string, $to_encoding, $from_encoding = null) { return p\Mbstring::mb_convert_encoding($string, $to_encoding, $from_encoding); }
}
if (!function_exists('mb_decode_mimeheader')) {
function mb_decode_mimeheader($string) { return p\Mbstring::mb_decode_mimeheader($string); }
}
if (!function_exists('mb_encode_mimeheader')) {
function mb_encode_mimeheader($string, $charset = null, $transfer_encoding = null, $newline = "\r\n", $indent = 0) { return p\Mbstring::mb_encode_mimeheader($string, $charset, $transfer_encoding, $newline, $indent); }
}
if (!function_exists('mb_decode_numericentity')) {
function mb_decode_numericentity($string, $map, $encoding = null) { return p\Mbstring::mb_decode_numericentity($string, $map, $encoding); }
}
if (!function_exists('mb_encode_numericentity')) {
function mb_encode_numericentity($string, $map, $encoding = null, $hex = false) { return p\Mbstring::mb_encode_numericentity($string, $map, $encoding, $hex); }
}
if (!function_exists('mb_convert_case')) {
function mb_convert_case($string, $mode, $encoding = null) { return p\Mbstring::mb_convert_case($string, $mode, $encoding); }
}
if (!function_exists('mb_internal_encoding')) {
function mb_internal_encoding($encoding = null) { return p\Mbstring::mb_internal_encoding($encoding); }
}
if (!function_exists('mb_language')) {
function mb_language($language = null) { return p\Mbstring::mb_language($language); }
}
if (!function_exists('mb_list_encodings')) {
function mb_list_encodings() { return p\Mbstring::mb_list_encodings(); }
}
if (!function_exists('mb_encoding_aliases')) {
function mb_encoding_aliases($encoding) { return p\Mbstring::mb_encoding_aliases($encoding); }
}
if (!function_exists('mb_check_encoding')) {
function mb_check_encoding($value = null, $encoding = null) { return p\Mbstring::mb_check_encoding($value, $encoding); }
}
if (!function_exists('mb_detect_encoding')) {
function mb_detect_encoding($string, $encodings = null, $strict = false) { return p\Mbstring::mb_detect_encoding($string, $encodings, $strict); }
}
if (!function_exists('mb_detect_order')) {
function mb_detect_order($encoding = null) { return p\Mbstring::mb_detect_order($encoding); }
}
if (!function_exists('mb_parse_str')) {
function mb_parse_str($string, &$result = []) { parse_str($string, $result); return (bool) $result; }
}
if (!function_exists('mb_strlen')) {
function mb_strlen($string, $encoding = null) { return p\Mbstring::mb_strlen($string, $encoding); }
}
if (!function_exists('mb_strpos')) {
function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strpos($haystack, $needle, $offset, $encoding); }
}
if (!function_exists('mb_strtolower')) {
function mb_strtolower($string, $encoding = null) { return p\Mbstring::mb_strtolower($string, $encoding); }
}
if (!function_exists('mb_strtoupper')) {
function mb_strtoupper($string, $encoding = null) { return p\Mbstring::mb_strtoupper($string, $encoding); }
}
if (!function_exists('mb_substitute_character')) {
function mb_substitute_character($substitute_character = null) { return p\Mbstring::mb_substitute_character($substitute_character); }
}
if (!function_exists('mb_substr')) {
function mb_substr($string, $start, $length = 2147483647, $encoding = null) { return p\Mbstring::mb_substr($string, $start, $length, $encoding); }
}
if (!function_exists('mb_stripos')) {
function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_stripos($haystack, $needle, $offset, $encoding); }
}
if (!function_exists('mb_stristr')) {
function mb_stristr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_stristr($haystack, $needle, $before_needle, $encoding); }
}
if (!function_exists('mb_strrchr')) {
function mb_strrchr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strrchr($haystack, $needle, $before_needle, $encoding); }
}
if (!function_exists('mb_strrichr')) {
function mb_strrichr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strrichr($haystack, $needle, $before_needle, $encoding); }
}
if (!function_exists('mb_strripos')) {
function mb_strripos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strripos($haystack, $needle, $offset, $encoding); }
}
if (!function_exists('mb_strrpos')) {
function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strrpos($haystack, $needle, $offset, $encoding); }
}
if (!function_exists('mb_strstr')) {
function mb_strstr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strstr($haystack, $needle, $before_needle, $encoding); }
}
if (!function_exists('mb_get_info')) {
function mb_get_info($type = 'all') { return p\Mbstring::mb_get_info($type); }
}
if (!function_exists('mb_http_output')) {
function mb_http_output($encoding = null) { return p\Mbstring::mb_http_output($encoding); }
}
if (!function_exists('mb_strwidth')) {
function mb_strwidth($string, $encoding = null) { return p\Mbstring::mb_strwidth($string, $encoding); }
}
if (!function_exists('mb_substr_count')) {
function mb_substr_count($haystack, $needle, $encoding = null) { return p\Mbstring::mb_substr_count($haystack, $needle, $encoding); }
}
if (!function_exists('mb_output_handler')) {
function mb_output_handler($string, $status) { return p\Mbstring::mb_output_handler($string, $status); }
}
if (!function_exists('mb_http_input')) {
function mb_http_input($type = null) { return p\Mbstring::mb_http_input($type); }
}
if (!function_exists('mb_convert_variables')) {
function mb_convert_variables($to_encoding, $from_encoding, &...$vars) { return p\Mbstring::mb_convert_variables($to_encoding, $from_encoding, ...$vars); }
}
if (!function_exists('mb_ord')) {
function mb_ord($string, $encoding = null) { return p\Mbstring::mb_ord($string, $encoding); }
}
if (!function_exists('mb_chr')) {
function mb_chr($codepoint, $encoding = null) { return p\Mbstring::mb_chr($codepoint, $encoding); }
}
if (!function_exists('mb_scrub')) {
function mb_scrub($string, $encoding = null) { return p\Mbstring::mb_scrub($string, $encoding); }
}
if (!function_exists('mb_str_split')) {
function mb_str_split($string, $length = 1, $encoding = null) { return p\Mbstring::mb_str_split($string, $length, $encoding); }
}
if (!function_exists('mb_str_pad')) {
/** @return string|false */
function mb_str_pad(?string $string, ?int $length, ?string $pad_string = ' ', ?int $pad_type = STR_PAD_RIGHT, ?string $encoding = null) { return p\Mbstring::mb_str_pad((string) $string, (int) $length, (string) $pad_string, (int) $pad_type, $encoding); }
}
if (!function_exists('mb_ucfirst')) {
/** @return string|false */
function mb_ucfirst(?string $string, ?string $encoding = null) { return p\Mbstring::mb_ucfirst((string) $string, $encoding); }
}
if (!function_exists('mb_lcfirst')) {
/** @return string|false */
function mb_lcfirst(?string $string, ?string $encoding = null) { return p\Mbstring::mb_lcfirst((string) $string, $encoding); }
}
if (!function_exists('mb_trim')) {
/** @return string|false */
function mb_trim(?string $string, ?string $characters = null, ?string $encoding = null) { return p\Mbstring::mb_trim((string) $string, $characters, $encoding); }
}
if (!function_exists('mb_ltrim')) {
/** @return string|false */
function mb_ltrim(?string $string, ?string $characters = null, ?string $encoding = null) { return p\Mbstring::mb_ltrim((string) $string, $characters, $encoding); }
}
if (!function_exists('mb_rtrim')) {
/** @return string|false */
function mb_rtrim(?string $string, ?string $characters = null, ?string $encoding = null) { return p\Mbstring::mb_rtrim((string) $string, $characters, $encoding); }
}
if (extension_loaded('mbstring')) {
return;
}
if (!defined('MB_CASE_UPPER')) {
define('MB_CASE_UPPER', 0);
}
if (!defined('MB_CASE_LOWER')) {
define('MB_CASE_LOWER', 1);
}
if (!defined('MB_CASE_TITLE')) {
define('MB_CASE_TITLE', 2);
}
@@ -93,7 +93,7 @@ if (!function_exists('mb_strstr')) {
function mb_strstr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_strstr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); }
}
if (!function_exists('mb_get_info')) {
function mb_get_info(?string $type = 'all'): array|string|int|false { return p\Mbstring::mb_get_info((string) $type); }
function mb_get_info(?string $type = 'all'): array|string|int|false|null { return p\Mbstring::mb_get_info((string) $type); }
}
if (!function_exists('mb_http_output')) {
function mb_http_output(?string $encoding = null): string|bool { return p\Mbstring::mb_http_output($encoding); }
@@ -122,14 +122,34 @@ if (!function_exists('mb_chr')) {
function mb_chr(?int $codepoint, ?string $encoding = null): string|false { return p\Mbstring::mb_chr((int) $codepoint, $encoding); }
}
if (!function_exists('mb_scrub')) {
function mb_scrub(?string $string, ?string $encoding = null): string { $encoding ??= mb_internal_encoding(); return mb_convert_encoding((string) $string, $encoding, $encoding); }
function mb_scrub(?string $string, ?string $encoding = null): string { return p\Mbstring::mb_scrub($string, $encoding); }
}
if (!function_exists('mb_str_split')) {
function mb_str_split(?string $string, ?int $length = 1, ?string $encoding = null): array { return p\Mbstring::mb_str_split((string) $string, (int) $length, $encoding); }
}
if (!function_exists('mb_str_pad')) {
function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = STR_PAD_RIGHT, ?string $encoding = null): string { return p\Mbstring::mb_str_pad($string, $length, $pad_string, $pad_type, $encoding); }
function mb_str_pad(?string $string, ?int $length, ?string $pad_string = ' ', ?int $pad_type = STR_PAD_RIGHT, ?string $encoding = null): string { return p\Mbstring::mb_str_pad((string) $string, (int) $length, (string) $pad_string, (int) $pad_type, $encoding); }
}
if (!function_exists('mb_ucfirst')) {
function mb_ucfirst(?string $string, ?string $encoding = null): string { return p\Mbstring::mb_ucfirst((string) $string, $encoding); }
}
if (!function_exists('mb_lcfirst')) {
function mb_lcfirst(?string $string, ?string $encoding = null): string { return p\Mbstring::mb_lcfirst((string) $string, $encoding); }
}
if (!function_exists('mb_trim')) {
function mb_trim(?string $string, ?string $characters = null, ?string $encoding = null): string { return p\Mbstring::mb_trim((string) $string, $characters, $encoding); }
}
if (!function_exists('mb_ltrim')) {
function mb_ltrim(?string $string, ?string $characters = null, ?string $encoding = null): string { return p\Mbstring::mb_ltrim((string) $string, $characters, $encoding); }
}
if (!function_exists('mb_rtrim')) {
function mb_rtrim(?string $string, ?string $characters = null, ?string $encoding = null): string { return p\Mbstring::mb_rtrim((string) $string, $characters, $encoding); }
}
if (extension_loaded('mbstring')) {
@@ -16,7 +16,8 @@
}
],
"require": {
"php": ">=7.1"
"php": ">=7.2",
"ext-iconv": "*"
},
"provide": {
"ext-mbstring": "*"
+1 -1
View File
@@ -60,7 +60,7 @@ final class Php80
public static function get_resource_id($res): int
{
if (!\is_resource($res) && null === @get_resource_type($res)) {
throw new \TypeError(sprintf('Argument 1 passed to get_resource_id() must be of the type resource, %s given', get_debug_type($res)));
throw new \TypeError(\sprintf('Argument 1 passed to get_resource_id() must be of the type resource, %s given', get_debug_type($res)));
}
return (int) $res;
+11 -6
View File
@@ -29,7 +29,7 @@ class PhpToken implements \Stringable
public $text;
/**
* @var int
* @var -1|positive-int
*/
public $line;
@@ -38,6 +38,9 @@ class PhpToken implements \Stringable
*/
public $pos;
/**
* @param -1|positive-int $line
*/
public function __construct(int $id, string $text, int $line = -1, int $position = -1)
{
$this->id = $id;
@@ -48,11 +51,11 @@ class PhpToken implements \Stringable
public function getTokenName(): ?string
{
if ('UNKNOWN' === $name = token_name($this->id)) {
$name = \strlen($this->text) > 1 || \ord($this->text) < 32 ? null : $this->text;
if ($this->id < 256) {
return \chr($this->id & 0xFF);
}
return $name;
return 'UNKNOWN' === ($name = token_name($this->id)) ? null : $name;
}
/**
@@ -80,7 +83,7 @@ class PhpToken implements \Stringable
}
/**
* @return static[]
* @return list<static>
*/
public static function tokenize(string $code, int $flags = 0): array
{
@@ -89,13 +92,15 @@ class PhpToken implements \Stringable
$tokens = token_get_all($code, $flags);
foreach ($tokens as $index => $token) {
if (\is_string($token)) {
$id = \ord($token);
// b" is the only such token longer than one char
$id = \ord($token[-1]);
$text = $token;
} else {
[$id, $text, $line] = $token;
}
$tokens[$index] = new static($id, $text, $line, $position);
$position += \strlen($text);
$line += preg_match_all('/\r\n?|\n/', $text);
}
return $tokens;
@@ -20,7 +20,7 @@
}
],
"require": {
"php": ">=7.1"
"php": ">=7.2"
},
"autoload": {
"psr-4": { "Symfony\\Polyfill\\Php80\\": "" },
@@ -1,37 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Php81;
/**
* @author Nicolas Grekas <p@tchwork.com>
*
* @internal
*/
final class Php81
{
public static function array_is_list(array $array): bool
{
if ([] === $array || $array === array_values($array)) {
return true;
}
$nextKey = -1;
foreach ($array as $k => $v) {
if ($k !== ++$nextKey) {
return false;
}
}
return true;
}
}
@@ -1,18 +0,0 @@
Symfony Polyfill / Php81
========================
This component provides features added to PHP 8.1 core:
- [`array_is_list`](https://php.net/array_is_list)
- [`enum_exists`](https://php.net/enum-exists)
- [`MYSQLI_REFRESH_REPLICA`](https://php.net/mysqli.constants#constantmysqli-refresh-replica) constant
- [`ReturnTypeWillChange`](https://wiki.php.net/rfc/internal_method_return_types)
- [`CURLStringFile`](https://php.net/CURLStringFile) (but only if PHP >= 7.4 is used)
More information can be found in the
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).
License
=======
This library is released under the [MIT license](LICENSE).
@@ -1,51 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (\PHP_VERSION_ID >= 70400 && extension_loaded('curl')) {
/**
* @property string $data
*/
class CURLStringFile extends CURLFile
{
private $data;
public function __construct(string $data, string $postname, string $mime = 'application/octet-stream')
{
$this->data = $data;
parent::__construct('data://application/octet-stream;base64,'.base64_encode($data), $mime, $postname);
}
public function __set(string $name, $value): void
{
if ('data' !== $name) {
$this->$name = $value;
return;
}
if (is_object($value) ? !method_exists($value, '__toString') : !is_scalar($value)) {
throw new TypeError('Cannot assign '.gettype($value).' to property CURLStringFile::$data of type string');
}
$this->name = 'data://application/octet-stream;base64,'.base64_encode($value);
}
public function __isset(string $name): bool
{
return isset($this->$name);
}
public function &__get(string $name)
{
return $this->$name;
}
}
}
@@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Php81 as p;
if (\PHP_VERSION_ID >= 80100) {
return;
}
if (defined('MYSQLI_REFRESH_SLAVE') && !defined('MYSQLI_REFRESH_REPLICA')) {
define('MYSQLI_REFRESH_REPLICA', 64);
}
if (!function_exists('array_is_list')) {
function array_is_list(array $array): bool { return p\Php81::array_is_list($array); }
}
if (!function_exists('enum_exists')) {
function enum_exists(string $enum, bool $autoload = true): bool { return $autoload && class_exists($enum) && false; }
}
+19
View File
@@ -0,0 +1,19 @@
Copyright (c) 2022-present Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@@ -0,0 +1,23 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Php82;
/**
* @internal
*/
trait NoDynamicProperties
{
public function __set(string $name, $value): void
{
throw new \Error('Cannot create dynamic property '.self::class.'::$'.$name);
}
}
+399
View File
@@ -0,0 +1,399 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Php82;
/**
* @author Alexander M. Turek <me@derrabus.de>
* @author Greg Roach <greg@subaqua.co.uk>
*
* @internal
*/
class Php82
{
/**
* Determines if a string matches the ODBC quoting rules.
*
* A valid quoted string begins with a '{', ends with a '}', and has no '}'
* inside of the string that aren't repeated (as to be escaped).
*
* These rules are what .NET also follows.
*
* @see https://github.com/php/php-src/blob/838f6bffff6363a204a2597cbfbaad1d7ee3f2b6/main/php_odbc_utils.c#L31-L57
*/
public static function odbc_connection_string_is_quoted(string $str): bool
{
if ('' === $str || '{' !== $str[0]) {
return false;
}
/* Check for } that aren't doubled up or at the end of the string */
$length = \strlen($str) - 1;
for ($i = 0; $i < $length; ++$i) {
if ('}' !== $str[$i]) {
continue;
}
if ('}' !== $str[++$i]) {
return false;
}
}
return true;
}
/**
* Determines if a value for a connection string should be quoted.
*
* The ODBC specification mentions:
* "Because of connection string and initialization file grammar, keywords and
* attribute values that contain the characters []{}(),;?*=!@ not enclosed
* with braces should be avoided."
*
* Note that it assumes that the string is *not* already quoted. You should
* check beforehand.
*
* @see https://github.com/php/php-src/blob/838f6bffff6363a204a2597cbfbaad1d7ee3f2b6/main/php_odbc_utils.c#L59-L73
*/
public static function odbc_connection_string_should_quote(string $str): bool
{
return false !== strpbrk($str, '[]{}(),;?*=!@ ');
}
public static function odbc_connection_string_quote(string $str): string
{
return '{'.str_replace('}', '}}', $str).'}';
}
/**
* Implementation closely based on the original C code - including the GOTOs
* and pointer-style string access.
*
* @see https://github.com/php/php-src/blob/master/Zend/zend_ini.c
*/
public static function ini_parse_quantity(string $value): int
{
// Avoid dependency on ctype_space()
$ctype_space = " \t\v\r\n\f";
$str = 0;
$str_end = \strlen($value);
$digits = $str;
$overflow = false;
/* Ignore leading whitespace, but keep it for error messages. */
while ($digits < $str_end && false !== strpos($ctype_space, $value[$digits])) {
++$digits;
}
/* Ignore trailing whitespace, but keep it for error messages. */
while ($digits < $str_end && false !== strpos($ctype_space, $value[$str_end - 1])) {
--$str_end;
}
if ($digits === $str_end) {
return 0;
}
$is_negative = false;
if ('+' === $value[$digits]) {
++$digits;
} elseif ('-' === $value[$digits]) {
$is_negative = true;
++$digits;
}
if ($digits === $str_end || $value[$digits] < '0' || $value[$digits] > '9') {
$message = \sprintf(
'Invalid quantity "%s": no valid leading digits, interpreting as "0" for backwards compatibility',
self::escapeString($value)
);
trigger_error($message, \E_USER_WARNING);
return 0;
}
$base = 10;
$allowed_digits = '0123456789';
if ('0' === $value[$digits] && ($digits + 1 === $str_end || false === strpos($allowed_digits, $value[$digits + 1]))) {
if ($digits + 1 === $str_end) {
return 0;
}
switch ($value[$digits + 1]) {
case 'g':
case 'G':
case 'm':
case 'M':
case 'k':
case 'K':
goto evaluation;
case 'x':
case 'X':
$base = 16;
$allowed_digits = '0123456789abcdefABCDEF';
break;
case 'o':
case 'O':
$base = 8;
$allowed_digits = '01234567';
break;
case 'b':
case 'B':
$base = 2;
$allowed_digits = '01';
break;
default:
$message = \sprintf(
'Invalid prefix "0%s", interpreting as "0" for backwards compatibility',
$value[$digits + 1]
);
trigger_error($message, \E_USER_WARNING);
return 0;
}
$digits += 2;
if ($digits === $str_end) {
$message = \sprintf(
'Invalid quantity "%s": no digits after base prefix, interpreting as "0" for backwards compatibility',
self::escapeString($value)
);
trigger_error($message, \E_USER_WARNING);
return 0;
}
$digits_consumed = $digits;
/* Ignore leading whitespace. */
while ($digits_consumed < $str_end && false !== strpos($ctype_space, $value[$digits_consumed])) {
++$digits_consumed;
}
if ($digits_consumed !== $str_end && ('+' === $value[$digits_consumed] || '-' === $value[$digits_consumed])) {
++$digits_consumed;
}
if ($digits_consumed + 1 < $str_end && '0' === $value[$digits_consumed]) {
switch ($value[$digits_consumed + 1]) {
case 'x':
case 'X':
case 'o':
case 'O':
$digits_consumed += 2;
break;
case 'b':
case 'B':
// 0b is a valid pair of digits in base 16
if (16 !== $base) {
$digits_consumed += 2;
}
break;
}
}
if ($digits !== $digits_consumed) {
$message = \sprintf(
'Invalid quantity "%s": no digits after base prefix, interpreting as "0" for backwards compatibility',
self::escapeString($value)
);
trigger_error($message, \E_USER_WARNING);
return 0;
}
}
evaluation:
if (10 === $base && '0' === $value[$digits]) {
$base = 8;
$allowed_digits = '01234567';
}
while ($digits < $str_end && ' ' === $value[$digits]) {
++$digits;
}
if ($digits < $str_end && '+' === $value[$digits]) {
++$digits;
} elseif ($digits < $str_end && '-' === $value[$digits]) {
$is_negative = true;
$overflow = true;
++$digits;
}
$digits_end = $digits;
while ($digits_end < $str_end && false !== strpos($allowed_digits, $value[$digits_end])) {
++$digits_end;
}
// Mimic strtoul() on a zend_ulong using two half-width limbs, as PHP has no unsigned integers
$bits = \PHP_INT_SIZE << 2;
$mask = (1 << $bits) - 1;
$high = $low = 0;
for ($i = $digits; $i < $digits_end; ++$i) {
$low = $low * $base + hexdec($value[$i]);
$high = $high * $base + ($low >> $bits);
$low &= $mask;
if ($high > $mask) {
break;
}
}
if ($high > $mask) {
$retval = -1;
$overflow = true;
} else {
$retval = ($high << $bits) | $low;
if ($is_negative && 0 === $retval) {
$is_negative = false;
$overflow = false;
} elseif (0 > $retval) {
// -PHP_INT_MIN is PHP_INT_MIN itself
$overflow = $overflow || !$is_negative || \PHP_INT_MIN !== $retval;
} elseif ($is_negative) {
$retval = -$retval;
}
}
if ($digits_end === $digits) {
$message = \sprintf(
'Invalid quantity "%s": no valid leading digits, interpreting as "0" for backwards compatibility',
self::escapeString($value)
);
trigger_error($message, \E_USER_WARNING);
return 0;
}
/* Allow for whitespace between integer portion and any suffix character */
while ($digits_end < $str_end && false !== strpos($ctype_space, $value[$digits_end])) {
++$digits_end;
}
/* No exponent suffix. */
if ($digits_end === $str_end) {
goto end;
}
switch ($value[$str_end - 1]) {
case 'g':
case 'G':
$shift = 30;
break;
case 'm':
case 'M':
$shift = 20;
break;
case 'k':
case 'K':
$shift = 10;
break;
default:
/* Unknown suffix */
$invalid = self::escapeString($value);
$interpreted = self::escapeString(substr($value, $str, $digits_end - $str));
$chr = self::escapeString($value[$str_end - 1]);
$message = \sprintf(
'Invalid quantity "%s": unknown multiplier "%s", interpreting as "%s" for backwards compatibility',
$invalid,
$chr,
$interpreted
);
trigger_error($message, \E_USER_WARNING);
return $retval;
}
$factor = 1 << $shift;
if (!$overflow) {
if ($retval > 0) {
$overflow = $retval > \PHP_INT_MAX / $factor;
} else {
$overflow = $retval < \PHP_INT_MIN / $factor;
}
}
if (\is_float($retval * $factor)) {
$overflow = true;
$retval <<= $shift;
} else {
$retval *= $factor;
}
if ($digits_end !== $str_end - 1) {
/* More than one character in suffix */
$message = \sprintf(
'Invalid quantity "%s", interpreting as "%s%s" for backwards compatibility',
self::escapeString($value),
self::escapeString(substr($value, $str, $digits_end - $str)),
self::escapeString($value[$str_end - 1])
);
trigger_error($message, \E_USER_WARNING);
return $retval;
}
end:
if ($overflow) {
/* Not specifying the resulting value here because the caller may make
* additional conversions. Not specifying the allowed range
* because the caller may do narrower range checks. */
$message = \sprintf(
'Invalid quantity "%s": value is out of range, using overflow result for backwards compatibility',
self::escapeString($value)
);
trigger_error($message, \E_USER_WARNING);
}
return $retval;
}
/**
* Escape the string to avoid null bytes and to make non-printable chars visible.
*/
private static function escapeString(string $string): string
{
$escaped = '';
for ($n = 0, $len = \strlen($string); $n < $len; ++$n) {
$c = \ord($string[$n]);
if ($c < 32 || '\\' === $string[$n] || $c > 126) {
switch ($string[$n]) {
case "\n": $escaped .= '\\n'; break;
case "\r": $escaped .= '\\r'; break;
case "\t": $escaped .= '\\t'; break;
case "\f": $escaped .= '\\f'; break;
case "\v": $escaped .= '\\v'; break;
case '\\': $escaped .= '\\\\'; break;
case "\x1B": $escaped .= '\\e'; break;
default:
$escaped .= '\\x'.strtoupper(\sprintf('%02x', $c));
}
} else {
$escaped .= $string[$n];
}
}
return $escaped;
}
}
@@ -0,0 +1,23 @@
Symfony Polyfill / Php82
========================
This component provides features added to PHP 8.2 core:
- [`AllowDynamicProperties`](https://wiki.php.net/rfc/deprecate_dynamic_properties)
- [`SensitiveParameter`](https://wiki.php.net/rfc/redact_parameters_in_back_traces)
- [`SensitiveParameterValue`](https://wiki.php.net/rfc/redact_parameters_in_back_traces)
- [`Random\Engine`](https://wiki.php.net/rfc/rng_extension)
- [`Random\Engine\CryptoSafeEngine`](https://wiki.php.net/rfc/rng_extension)
- [`Random\Engine\Secure`](https://wiki.php.net/rfc/rng_extension) (check [arokettu/random-polyfill](https://packagist.org/packages/arokettu/random-polyfill) for more engines)
- [`odbc_connection_string_is_quoted()`](https://php.net/odbc_connection_string_is_quoted)
- [`odbc_connection_string_should_quote()`](https://php.net/odbc_connection_string_should_quote)
- [`odbc_connection_string_quote()`](https://php.net/odbc_connection_string_quote)
- [`ini_parse_quantity()`](https://php.net/ini_parse_quantity)
More information can be found in the
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).
License
=======
This library is released under the [MIT license](LICENSE).
@@ -0,0 +1,50 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Php82\Random\Engine;
use Random\RandomException;
use Symfony\Polyfill\Php82\NoDynamicProperties;
/**
* @author Tim Düsterhus <tim@bastelstu.be>
* @author Anton Smirnov <sandfox@sandfox.me>
*
* @internal
*/
class Secure
{
use NoDynamicProperties;
public function generate(): string
{
try {
return random_bytes(\PHP_INT_SIZE);
} catch (\Exception $e) {
throw new RandomException($e->getMessage(), $e->getCode(), $e->getPrevious());
}
}
public function __sleep(): array
{
throw new \Exception("Serialization of 'Random\Engine\Secure' is not allowed");
}
public function __wakeup(): void
{
throw new \Exception("Unserialization of 'Random\Engine\Secure' is not allowed");
}
public function __clone()
{
throw new \Error('Trying to clone an uncloneable object of class Random\Engine\Secure');
}
}
@@ -0,0 +1,20 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (\PHP_VERSION_ID < 80200) {
#[Attribute(Attribute::TARGET_CLASS)]
final class AllowDynamicProperties
{
public function __construct()
{
}
}
}
@@ -0,0 +1,18 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Random;
if (\PHP_VERSION_ID < 80200) {
class BrokenRandomEngineError extends RandomError
{
}
}
@@ -0,0 +1,18 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Random;
if (\PHP_VERSION_ID < 80200) {
interface CryptoSafeEngine extends Engine
{
}
}
@@ -0,0 +1,19 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Random;
if (\PHP_VERSION_ID < 80200) {
interface Engine
{
public function generate(): string;
}
}
@@ -0,0 +1,20 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Random\Engine;
use Symfony\Polyfill\Php82 as p;
if (\PHP_VERSION_ID < 80200) {
final class Secure extends p\Random\Engine\Secure implements \Random\CryptoSafeEngine
{
}
}
@@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Random;
use Symfony\Polyfill\Php82\NoDynamicProperties;
if (\PHP_VERSION_ID < 80200) {
class RandomError extends \Error
{
use NoDynamicProperties;
}
}
@@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Random;
use Symfony\Polyfill\Php82\NoDynamicProperties;
if (\PHP_VERSION_ID < 80200) {
class RandomException extends \Exception
{
use NoDynamicProperties;
}
}
@@ -0,0 +1,20 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (\PHP_VERSION_ID < 80200) {
#[Attribute(Attribute::TARGET_PARAMETER)]
final class SensitiveParameter
{
public function __construct()
{
}
}
}
@@ -0,0 +1,16 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (\PHP_VERSION_ID < 80200) {
final class SensitiveParameterValue extends Symfony\Polyfill\Php82\SensitiveParameterValue
{
}
}
@@ -0,0 +1,47 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Php82;
/**
* @author Tim Düsterhus <duesterhus@woltlab.com>
*
* @internal
*/
class SensitiveParameterValue
{
private $value;
public function __construct($value)
{
$this->value = $value;
}
public function getValue()
{
return $this->value;
}
public function __debugInfo(): array
{
return [];
}
public function __sleep(): array
{
throw new \Exception("Serialization of 'SensitiveParameterValue' is not allowed");
}
public function __wakeup(): void
{
throw new \Exception("Unserialization of 'SensitiveParameterValue' is not allowed");
}
}
@@ -0,0 +1,38 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Php82 as p;
if (\PHP_VERSION_ID >= 80200) {
return;
}
if (\extension_loaded('curl') && !defined('CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256') && curl_version()['version_number'] >= 0x075000) {
define('CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256', 10311);
}
if (extension_loaded('odbc')) {
if (!function_exists('odbc_connection_string_is_quoted')) {
function odbc_connection_string_is_quoted(string $str): bool { return p\Php82::odbc_connection_string_is_quoted($str); }
}
if (!function_exists('odbc_connection_string_should_quote')) {
function odbc_connection_string_should_quote(string $str): bool { return p\Php82::odbc_connection_string_should_quote($str); }
}
if (!function_exists('odbc_connection_string_quote')) {
function odbc_connection_string_quote(string $str): string { return p\Php82::odbc_connection_string_quote($str); }
}
}
if (!function_exists('ini_parse_quantity')) {
function ini_parse_quantity(string $shorthand): int { return p\Php82::ini_parse_quantity($shorthand); }
}
@@ -1,7 +1,7 @@
{
"name": "symfony/polyfill-php81",
"name": "symfony/polyfill-php82",
"type": "library",
"description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions",
"description": "Symfony polyfill backporting some PHP 8.2+ features to lower PHP versions",
"keywords": ["polyfill", "shim", "compatibility", "portable"],
"homepage": "https://symfony.com",
"license": "MIT",
@@ -19,7 +19,7 @@
"php": ">=7.2"
},
"autoload": {
"psr-4": { "Symfony\\Polyfill\\Php81\\": "" },
"psr-4": { "Symfony\\Polyfill\\Php82\\": "" },
"files": [ "bootstrap.php" ],
"classmap": [ "Resources/stubs" ]
},
+19
View File
@@ -0,0 +1,19 @@
Copyright (c) 2022-present Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+185
View File
@@ -0,0 +1,185 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Php83;
/**
* @author Ion Bazan <ion.bazan@gmail.com>
* @author Pierre Ambroise <pierre27.ambroise@gmail.com>
*
* @internal
*/
final class Php83
{
private const JSON_MAX_DEPTH = 0x7FFFFFFF; // see https://www.php.net/manual/en/function.json-decode.php
public static function json_validate(string $json, int $depth = 512, int $flags = 0): bool
{
if (0 !== $flags && \defined('JSON_INVALID_UTF8_IGNORE') && \JSON_INVALID_UTF8_IGNORE !== $flags) {
throw new \ValueError('json_validate(): Argument #3 ($flags) must be a valid flag (allowed flags: JSON_INVALID_UTF8_IGNORE)');
}
if ('' === $json) {
json_decode('');
return false;
}
if ($depth <= 0) {
json_decode('0');
throw new \ValueError('json_validate(): Argument #2 ($depth) must be greater than 0');
}
if ($depth > self::JSON_MAX_DEPTH) {
json_decode('0');
throw new \ValueError(\sprintf('json_validate(): Argument #2 ($depth) must be less than %d', self::JSON_MAX_DEPTH));
}
json_decode($json, true, $depth, $flags);
return \JSON_ERROR_NONE === json_last_error();
}
/** @return string|false */
public static function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = \STR_PAD_RIGHT, ?string $encoding = null)
{
if (null === $encoding) {
$encoding = mb_internal_encoding();
}
$errorToTrigger = null;
try {
if (!@mb_check_encoding('', $encoding)) {
$errorToTrigger = \sprintf('mb_str_pad(): Argument #5 ($encoding) must be a valid encoding, "%s" given', $encoding);
}
} catch (\ValueError $e) {
$errorToTrigger = \sprintf('mb_str_pad(): Argument #5 ($encoding) must be a valid encoding, "%s" given', $encoding);
}
if (null === $errorToTrigger && 0 >= $padStringLength = mb_strlen($pad_string, $encoding)) {
$errorToTrigger = 'mb_str_pad(): Argument #3 ($pad_string) must be a non-empty string';
}
if (null === $errorToTrigger && !\in_array($pad_type, [\STR_PAD_RIGHT, \STR_PAD_LEFT, \STR_PAD_BOTH], true)) {
$errorToTrigger = 'mb_str_pad(): Argument #4 ($pad_type) must be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH';
}
if (null !== $errorToTrigger) {
if (80000 > \PHP_VERSION_ID) {
trigger_error($errorToTrigger, \E_USER_WARNING);
return false;
}
throw new \ValueError($errorToTrigger);
}
$paddingRequired = $length - mb_strlen($string, $encoding);
if ($paddingRequired < 1) {
return $string;
}
switch ($pad_type) {
case \STR_PAD_LEFT:
$leftPaddingLength = $paddingRequired;
break;
case \STR_PAD_RIGHT:
$leftPaddingLength = 0;
break;
default:
$leftPaddingLength = intdiv($paddingRequired, 2);
}
$rightPaddingLength = $paddingRequired - $leftPaddingLength;
return str_repeat($pad_string, intdiv($leftPaddingLength, $padStringLength)).mb_substr($pad_string, 0, $leftPaddingLength % $padStringLength, $encoding)
.$string
.str_repeat($pad_string, intdiv($rightPaddingLength, $padStringLength)).mb_substr($pad_string, 0, $rightPaddingLength % $padStringLength, $encoding);
}
public static function str_increment(string $string): string
{
if ('' === $string) {
throw new \ValueError('str_increment(): Argument #1 ($string) cannot be empty');
}
if (!preg_match('/^[a-zA-Z0-9]++$/D', $string)) {
throw new \ValueError('str_increment(): Argument #1 ($string) must be composed only of alphanumeric ASCII characters');
}
for ($i = \strlen($string) - 1; $i >= 0; --$i) {
$char = $string[$i];
if ('z' === $char) {
$string[$i] = 'a';
continue;
}
if ('Z' === $char) {
$string[$i] = 'A';
continue;
}
if ('9' === $char) {
$string[$i] = '0';
continue;
}
$string[$i] = \chr(\ord($char) + 1);
return $string;
}
switch ($string[0]) {
case 'a': return 'a'.$string;
case 'A': return 'A'.$string;
}
return '1'.$string;
}
public static function str_decrement(string $string): string
{
if ('' === $string) {
throw new \ValueError('str_decrement(): Argument #1 ($string) cannot be empty');
}
if (!preg_match('/^[a-zA-Z0-9]++$/D', $string)) {
throw new \ValueError('str_decrement(): Argument #1 ($string) must be composed only of alphanumeric ASCII characters');
}
if ('0' === $string[0] || 'a' === $string || 'A' === $string) {
throw new \ValueError(\sprintf('str_decrement(): Argument #1 ($string) "%s" is out of decrement range', $string));
}
for ($i = \strlen($string) - 1; $i >= 0; --$i) {
$char = $string[$i];
if ('a' === $char) {
$string[$i] = 'z';
continue;
}
if ('A' === $char) {
$string[$i] = 'Z';
continue;
}
if ('0' === $char) {
$string[$i] = '9';
continue;
}
$string[$i] = \chr(\ord($char) - 1);
return '0' === $string[0] && isset($string[1]) ? substr($string, 1) : $string;
}
return substr($string, 1);
}
}
@@ -0,0 +1,22 @@
Symfony Polyfill / Php83
========================
This component provides features added to PHP 8.3 core:
- [`json_validate`](https://wiki.php.net/rfc/json_validate)
- [`Override`](https://wiki.php.net/rfc/marking_overriden_methods)
- [`mb_str_pad`](https://wiki.php.net/rfc/mb_str_pad)
- [`ldap_exop_sync`](https://wiki.php.net/rfc/deprecate_functions_with_overloaded_signatures)
- [`ldap_connect_wallet`](https://wiki.php.net/rfc/deprecate_functions_with_overloaded_signatures)
- [`stream_context_set_options`](https://wiki.php.net/rfc/deprecate_functions_with_overloaded_signatures)
- [`str_increment` and `str_decrement`](https://wiki.php.net/rfc/saner-inc-dec-operators)
- [`Date*Exception/Error classes`](https://wiki.php.net/rfc/datetime-exceptions)
- [`SQLite3Exception`](https://wiki.php.net/rfc/sqlite3_exceptions)
More information can be found in the
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).
License
=======
This library is released under the [MIT license](LICENSE).
@@ -0,0 +1,16 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (\PHP_VERSION_ID < 80300) {
class DateError extends Error
{
}
}
@@ -0,0 +1,16 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (\PHP_VERSION_ID < 80300) {
class DateException extends Exception
{
}
}
@@ -0,0 +1,16 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (\PHP_VERSION_ID < 80300) {
class DateInvalidOperationException extends DateException
{
}
}
@@ -0,0 +1,16 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (\PHP_VERSION_ID < 80300) {
class DateInvalidTimeZoneException extends DateException
{
}
}
@@ -0,0 +1,16 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (\PHP_VERSION_ID < 80300) {
class DateMalformedIntervalStringException extends DateException
{
}
}
@@ -0,0 +1,16 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (\PHP_VERSION_ID < 80300) {
class DateMalformedPeriodStringException extends DateException
{
}
}
@@ -0,0 +1,16 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (\PHP_VERSION_ID < 80300) {
class DateMalformedStringException extends DateException
{
}
}
@@ -0,0 +1,16 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (\PHP_VERSION_ID < 80300) {
class DateObjectError extends DateError
{
}
}
@@ -0,0 +1,16 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (\PHP_VERSION_ID < 80300) {
class DateRangeError extends DateError
{
}
}
@@ -9,9 +9,9 @@
* file that was distributed with this source code.
*/
if (\PHP_VERSION_ID < 80100) {
if (\PHP_VERSION_ID < 80300) {
#[Attribute(Attribute::TARGET_METHOD)]
final class ReturnTypeWillChange
final class Override
{
public function __construct()
{
@@ -0,0 +1,16 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (\PHP_VERSION_ID < 80300) {
class SQLite3Exception extends Exception
{
}
}
@@ -0,0 +1,54 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Php83 as p;
if (\PHP_VERSION_ID >= 80300) {
return;
}
if (!function_exists('json_validate')) {
function json_validate(string $json, int $depth = 512, int $flags = 0): bool { return p\Php83::json_validate($json, $depth, $flags); }
}
if (!function_exists('stream_context_set_options')) {
function stream_context_set_options($context, array $options): bool { return stream_context_set_option($context, $options); }
}
if (!function_exists('str_increment')) {
function str_increment(string $string): string { return p\Php83::str_increment($string); }
}
if (!function_exists('str_decrement')) {
function str_decrement(string $string): string { return p\Php83::str_decrement($string); }
}
if (\PHP_VERSION_ID < 80000) {
require __DIR__.'/bootstrap72.php';
}
if (extension_loaded('mbstring')) {
if (!function_exists('mb_str_pad')) {
function mb_str_pad(?string $string, ?int $length, ?string $pad_string = ' ', ?int $pad_type = STR_PAD_RIGHT, ?string $encoding = null): string { return p\Php83::mb_str_pad((string) $string, (int) $length, (string) $pad_string, (int) $pad_type, $encoding); }
}
}
if (\PHP_VERSION_ID >= 80100) {
return require __DIR__.'/bootstrap81.php';
}
if (!function_exists('ldap_exop_sync') && function_exists('ldap_exop')) {
function ldap_exop_sync($ldap, string $request_oid, ?string $request_data = null, ?array $controls = null, &$response_data = null, &$response_oid = null): bool { return ldap_exop($ldap, $request_oid, $request_data, $response_data, $response_oid); }
}
if (!function_exists('ldap_connect_wallet') && function_exists('ldap_connect')) {
function ldap_connect_wallet(?string $uri, string $wallet, string $password, int $auth_mode = \GSLC_SSL_NO_AUTH) { return ldap_connect($uri, $wallet, $password, $auth_mode); }
}
@@ -0,0 +1,25 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Php83 as p;
if (!class_exists('ValueError', false)) {
class ValueError extends Error
{
}
}
if (extension_loaded('mbstring')) {
if (!function_exists('mb_str_pad')) {
/** @return string|false */
function mb_str_pad(?string $string, ?int $length, ?string $pad_string = ' ', ?int $pad_type = STR_PAD_RIGHT, ?string $encoding = null) { return p\Php83::mb_str_pad((string) $string, (int) $length, (string) $pad_string, (int) $pad_type, $encoding); }
}
}
@@ -0,0 +1,40 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Php83 as p;
if (\PHP_VERSION_ID >= 80300) {
return;
}
if (!function_exists('json_validate')) {
function json_validate(string $json, int $depth = 512, int $flags = 0): bool { return p\Php83::json_validate($json, $depth, $flags); }
}
if (!function_exists('str_increment')) {
function str_increment(string $string): string { return p\Php83::str_increment($string); }
}
if (!function_exists('str_decrement')) {
function str_decrement(string $string): string { return p\Php83::str_decrement($string); }
}
if (extension_loaded('mbstring') && !function_exists('mb_str_pad')) {
function mb_str_pad(?string $string, ?int $length, ?string $pad_string = ' ', ?int $pad_type = STR_PAD_RIGHT, ?string $encoding = null): string { return p\Php83::mb_str_pad((string) $string, (int) $length, (string) $pad_string, (int) $pad_type, $encoding); }
}
if (!function_exists('ldap_exop_sync') && function_exists('ldap_exop')) {
function ldap_exop_sync(\LDAP\Connection $ldap, string $request_oid, ?string $request_data = null, ?array $controls = null, &$response_data = null, &$response_oid = null): bool { return ldap_exop($ldap, $request_oid, $request_data, $response_data, $response_oid); }
}
if (!function_exists('ldap_connect_wallet') && function_exists('ldap_connect')) {
function ldap_connect_wallet(?string $uri, string $wallet, #[\SensitiveParameter] string $password, int $auth_mode = \GSLC_SSL_NO_AUTH): \LDAP\Connection|false { return ldap_connect($uri, $wallet, $password, $auth_mode); }
}
@@ -0,0 +1,33 @@
{
"name": "symfony/polyfill-php83",
"type": "library",
"description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions",
"keywords": ["polyfill", "shim", "compatibility", "portable"],
"homepage": "https://symfony.com",
"license": "MIT",
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"require": {
"php": ">=7.2"
},
"autoload": {
"psr-4": { "Symfony\\Polyfill\\Php83\\": "" },
"files": [ "bootstrap.php" ],
"classmap": [ "Resources/stubs" ]
},
"minimum-stability": "dev",
"extra": {
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
}
}
+19
View File
@@ -0,0 +1,19 @@
Copyright (c) 2024-present Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+549
View File
@@ -0,0 +1,549 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Php84;
/**
* @author Ayesh Karunaratne <ayesh@aye.sh>
* @author Pierre Ambroise <pierre27.ambroise@gmail.com>
*
* @internal
*/
final class Php84
{
private const BCMATH_WELL_FORMED = '/^[+-]?[0-9]*+(?:\.[0-9]*+)?$/D';
/** @return string|false */
public static function mb_ucfirst(string $string, ?string $encoding = null)
{
if (null === $encoding) {
$encoding = mb_internal_encoding();
}
try {
$validEncoding = @mb_check_encoding('', $encoding);
} catch (\ValueError $e) {
throw new \ValueError(\sprintf('mb_ucfirst(): Argument #2 ($encoding) must be a valid encoding, "%s" given', $encoding));
}
if (!$validEncoding) {
if (80000 > \PHP_VERSION_ID) {
trigger_error(\sprintf('mb_ucfirst(): Argument #2 ($encoding) must be a valid encoding, "%s" given', $encoding), \E_USER_WARNING);
return false;
}
throw new \ValueError(\sprintf('mb_ucfirst(): Argument #2 ($encoding) must be a valid encoding, "%s" given', $encoding));
}
$firstChar = mb_substr($string, 0, 1, $encoding);
$firstChar = mb_convert_case($firstChar, \MB_CASE_TITLE, $encoding);
return $firstChar.mb_substr($string, 1, null, $encoding);
}
/** @return string|false */
public static function mb_lcfirst(string $string, ?string $encoding = null)
{
if (null === $encoding) {
$encoding = mb_internal_encoding();
}
try {
$validEncoding = @mb_check_encoding('', $encoding);
} catch (\ValueError $e) {
throw new \ValueError(\sprintf('mb_lcfirst(): Argument #2 ($encoding) must be a valid encoding, "%s" given', $encoding));
}
if (!$validEncoding) {
if (80000 > \PHP_VERSION_ID) {
trigger_error(\sprintf('mb_lcfirst(): Argument #2 ($encoding) must be a valid encoding, "%s" given', $encoding), \E_USER_WARNING);
return false;
}
throw new \ValueError(\sprintf('mb_lcfirst(): Argument #2 ($encoding) must be a valid encoding, "%s" given', $encoding));
}
$firstChar = mb_substr($string, 0, 1, $encoding);
$firstChar = mb_convert_case($firstChar, \MB_CASE_LOWER, $encoding);
return $firstChar.mb_substr($string, 1, null, $encoding);
}
public static function array_find(array $array, callable $callback)
{
foreach ($array as $key => $value) {
if ($callback($value, $key)) {
return $value;
}
}
return null;
}
public static function array_find_key(array $array, callable $callback)
{
foreach ($array as $key => $value) {
if ($callback($value, $key)) {
return $key;
}
}
return null;
}
public static function array_any(array $array, callable $callback): bool
{
foreach ($array as $key => $value) {
if ($callback($value, $key)) {
return true;
}
}
return false;
}
public static function array_all(array $array, callable $callback): bool
{
foreach ($array as $key => $value) {
if (!$callback($value, $key)) {
return false;
}
}
return true;
}
public static function fpow(float $num, float $exponent): float
{
if (0.0 === $num && $exponent < 0.0) {
// IEEE 754 pow(±0, y < 0), which the ** operator deprecates since PHP 8.4
return -1.0 === fmod($exponent, 2.0) && '-' === ((string) $num)[0] ? -\INF : \INF;
}
return $num ** $exponent;
}
/** @return string|false */
public static function mb_trim(string $string, ?string $characters = null, ?string $encoding = null)
{
return self::mb_internal_trim('{^[%s]+|[%1$s]+$}Du', $string, $characters, $encoding, __FUNCTION__);
}
/** @return string|false */
public static function mb_ltrim(string $string, ?string $characters = null, ?string $encoding = null)
{
return self::mb_internal_trim('{^[%s]+}Du', $string, $characters, $encoding, __FUNCTION__);
}
/** @return string|false */
public static function mb_rtrim(string $string, ?string $characters = null, ?string $encoding = null)
{
return self::mb_internal_trim('{[%s]+$}Du', $string, $characters, $encoding, __FUNCTION__);
}
/** @return string|false */
private static function mb_internal_trim(string $regex, string $string, ?string $characters, ?string $encoding, string $function)
{
if (null === $encoding) {
$encoding = mb_internal_encoding();
}
try {
$validEncoding = @mb_check_encoding('', $encoding);
} catch (\ValueError $e) {
throw new \ValueError(\sprintf('%s(): Argument #3 ($encoding) must be a valid encoding, "%s" given', $function, $encoding));
}
if (!$validEncoding) {
if (80000 > \PHP_VERSION_ID) {
trigger_error(\sprintf('%s(): Argument #3 ($encoding) must be a valid encoding, "%s" given', $function, $encoding), \E_USER_WARNING);
return false;
}
throw new \ValueError(\sprintf('%s(): Argument #3 ($encoding) must be a valid encoding, "%s" given', $function, $encoding));
}
if ('' === $characters) {
return $string;
}
if ('UTF-8' === $encoding || \in_array(strtolower($encoding), ['utf-8', 'utf8'], true)) {
$encoding = 'UTF-8';
}
if ('UTF-8' === $encoding && (!preg_match('//u', $string) || (null !== $characters && !preg_match('//u', $characters)))) {
return self::mb_trim_invalid_utf8($string, $characters, $function);
}
$string = mb_convert_encoding($string, 'UTF-8', $encoding);
if (null !== $characters) {
$characters = mb_convert_encoding($characters, 'UTF-8', $encoding);
}
if (null === $characters) {
$characters = "\\0 \f\n\r\t\v\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{2028}\u{2029}\u{202F}\u{205F}\u{3000}\u{0085}\u{180E}";
} else {
$characters = preg_quote($characters);
}
$string = preg_replace(\sprintf($regex, $characters), '', $string);
if ('UTF-8' === $encoding) {
return $string;
}
return mb_convert_encoding($string, $encoding, 'UTF-8');
}
/**
* Trims ill-formed UTF-8 like mbstring: each maximal subpart of an ill-formed
* sequence counts as one character, and the string is returned as is when
* nothing is trimmed, else re-encoded with the substitute character.
*/
private static function mb_trim_invalid_utf8(string $string, ?string $characters, string $function): string
{
// Well-formed characters in group 1, then the maximal subparts of ill-formed sequences
$regex = '/([\x00-\x7F]|[\xC2-\xDF][\x80-\xBF]|\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}|\xED[\x80-\x9F][\x80-\xBF]|\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2})|\xE0[\xA0-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]|\xED[\x80-\x9F]|\xF0[\x90-\xBF][\x80-\xBF]?|[\xF1-\xF3][\x80-\xBF]{1,2}|\xF4[\x80-\x8F][\x80-\xBF]?|[\x80-\xFF]/';
// Ill-formed sequences leave group 1 empty: they are trimmed when $characters has one too
preg_match_all($regex, $characters ?? "\0 \f\n\r\t\v\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{2028}\u{2029}\u{202F}\u{205F}\u{3000}\u{0085}\u{180E}", $m);
$trimmed = array_flip($m[1]);
$start = 0;
$end = \strlen($string);
while ('mb_rtrim' !== $function && $start < $end && preg_match($regex, $string, $m, 0, $start) && isset($trimmed[$m[1] ?? ''])) {
$start += \strlen($m[0]);
}
while ('mb_ltrim' !== $function && $start < $end) {
// Characters are at most 4 bytes long, and only continuation bytes don't start one
for ($i = $end - 1; $i > $start && $i > $end - 4 && 0x80 === (\ord($string[$i]) & 0xC0); --$i) {
}
preg_match_all($regex, substr($string, $i, $end - $i), $m);
if (!isset($trimmed[end($m[1])])) {
break;
}
$end -= \strlen(end($m[0]));
}
if (0 === $start && \strlen($string) === $end) {
return $string;
}
$string = substr($string, $start, $end - $start);
return 'none' === mb_substitute_character() ? preg_replace($regex, '$1', $string) : mb_convert_encoding($string, 'UTF-8', 'UTF-8');
}
public static function grapheme_str_split(string $string, int $length)
{
if (0 >= $length || 1073741823 < $length) {
throw new \ValueError('grapheme_str_split(): Argument #2 ($length) must be greater than 0 and less than or equal to 1073741823');
}
if ('' === $string) {
return [];
}
$regex = ((float) \PCRE_VERSION >= 10.44)
? '\X'
: '(?:\r\n|[\x{1F1E6}-\x{1F1FF}][\x{1F1E6}-\x{1F1FF}]?|(?:[ -~\x{200C}\x{200D}]|[ᆨ-ᇹ]+|[ᄀ-ᅟ]*(?:[가개갸걔거게겨계고과괘괴교구궈궤귀규그긔기까깨꺄꺠꺼께껴꼐꼬꽈꽤꾀꾜꾸꿔꿰뀌뀨끄끠끼나내냐냬너네녀녜노놔놰뇌뇨누눠눼뉘뉴느늬니다대댜댸더데뎌뎨도돠돼되됴두둬뒈뒤듀드듸디따때땨떄떠떼뗘뗴또똬뙈뙤뚀뚜뚸뛔뛰뜌뜨띄띠라래랴럐러레려례로롸뢔뢰료루뤄뤠뤼류르릐리마매먀먜머메며몌모뫄뫠뫼묘무뭐뭬뮈뮤므믜미바배뱌뱨버베벼볘보봐봬뵈뵤부붜붸뷔뷰브븨비빠빼뺘뺴뻐뻬뼈뼤뽀뽜뽸뾔뾰뿌뿨쀄쀠쀼쁘쁴삐사새샤섀서세셔셰소솨쇄쇠쇼수숴쉐쉬슈스싀시싸쌔쌰썌써쎄쎠쎼쏘쏴쐐쐬쑈쑤쒀쒜쒸쓔쓰씌씨아애야얘어에여예오와왜외요우워웨위유으의이자재쟈쟤저제져졔조좌좨죄죠주줘줴쥐쥬즈즤지짜째쨔쨰쩌쩨쪄쪠쪼쫘쫴쬐쬬쭈쭤쮀쮜쮸쯔쯰찌차채챠챼처체쳐쳬초촤쵀최쵸추춰췌취츄츠츼치카캐캬컈커케켜켸코콰쾌쾨쿄쿠쿼퀘퀴큐크킈키타태탸턔터테텨톄토톼퇘퇴툐투퉈퉤튀튜트틔티파패퍄퍠퍼페펴폐포퐈퐤푀표푸풔풰퓌퓨프픠피하해햐햬허헤혀혜호화홰회효후훠훼휘휴흐희히]?[ᅠ-ᆢ]+|[가-힣])[ᆨ-ᇹ]*|[ᄀ-ᅟ]+|[^\p{Cc}\p{Cf}\p{Zl}\p{Zp}])[\p{Mn}\p{Mc}\p{Me}\x{09BE}\x{09D7}\x{0B3E}\x{0B57}\x{0BBE}\x{0BD7}\x{0CC2}\x{0CD5}\x{0CD6}\x{0D3E}\x{0D57}\x{0DCF}\x{0DDF}\x{200C}\x{1D165}\x{1D16E}-\x{1D172}\x{1F3FB}-\x{1F3FF}\x{FE0E}-\x{FE0F}\x{E0020}-\x{E007F}]*(?:\x{200D}(?:[\x{1F1E6}-\x{1F1FF}]|[ -~\x{200C}\x{200D}]|[ᆨ-ᇹ]+|[ᄀ-ᅟ]*(?:[가-힣]?[ᅠ-ᆢ]+|[가-힣])[ᆨ-ᇹ]*|[ᄀ-ᅟ]+|[^\p{Cc}\p{Cf}\p{Zl}\p{Zp}])[\p{Mn}\p{Mc}\p{Me}\x{09BE}\x{09D7}\x{0B3E}\x{0B57}\x{0BBE}\x{0BD7}\x{0CC2}\x{0CD5}\x{0CD6}\x{0D3E}\x{0D57}\x{0DCF}\x{0DDF}\x{200C}\x{1D165}\x{1D16E}-\x{1D172}\x{1F3FB}-\x{1F3FF}\x{FE0E}-\x{FE0F}\x{E0020}-\x{E007F}]*)*|[\p{Cc}\p{Cf}\p{Zl}\p{Zp}])';
if (preg_match_all('/'.$regex.'/u', $string, $matches)) {
$graphemes = $matches[0];
} else {
$graphemes = self::splitIllFormed($string, $regex);
}
if (!$graphemes) {
return false;
}
if (1 === $length) {
return $graphemes;
}
$chunks = array_chunk($graphemes, $length);
foreach ($chunks as &$chunk) {
$chunk = implode('', $chunk);
}
return $chunks;
}
public static function bcceil(string $num): string
{
if (!preg_match(self::BCMATH_WELL_FORMED, $num)) {
throw new \ValueError('bcceil(): Argument #1 ($num) is not well-formed');
}
return self::bcround($num, 0, \RoundingMode::PositiveInfinity);
}
public static function bcdivmod(string $num1, string $num2, ?int $scale = null): ?array
{
if (null === $quot = @bcdiv($num1, $num2, 0)) {
throw new \DivisionByZeroError('Division by zero');
}
$scale = $scale ?? (\PHP_VERSION_ID >= 70300 ? bcscale() : (\ini_get('bcmath.scale') ?: 0));
return [$quot, bcmod($num1, $num2, $scale)];
}
public static function bcfloor(string $num): string
{
if (!preg_match(self::BCMATH_WELL_FORMED, $num)) {
throw new \ValueError('bcfloor(): Argument #1 ($num) is not well-formed');
}
return self::bcround($num, 0, \RoundingMode::NegativeInfinity);
}
/**
* @param \RoundingMode|\RoundingMode::* $mode
*/
public static function bcround(string $num, int $precision = 0, $mode = \RoundingMode::HalfAwayFromZero): string
{
if (!preg_match(self::BCMATH_WELL_FORMED, $num)) {
throw new \ValueError('bcround(): Argument #1 ($num) is not well-formed');
}
$sign = 1;
if ('' !== $num && ('-' === $num[0] || '+' === $num[0])) {
if ('-' === $num[0]) {
$sign = -1;
}
$num = substr($num, 1);
}
if (false !== strpos($num, '.')) {
[$intPart, $fracPart] = array_pad(explode('.', $num, 2), 2, '');
} else {
$intPart = $num;
$fracPart = '';
}
if ('' === $intPart) {
$intPart = '0';
}
$intPart = self::trimLeadingZeros($intPart);
$fracPart = (string) $fracPart;
if ($precision >= 0) {
$fracLength = \strlen($fracPart);
if ($precision <= $fracLength) {
$scaledInt = $intPart.(string) substr($fracPart, 0, $precision);
$scaledFrac = (string) substr($fracPart, $precision);
} else {
$scaledInt = $intPart.$fracPart.str_repeat('0', $precision - $fracLength);
$scaledFrac = '';
}
} else {
$shift = -$precision;
$intLength = \strlen($intPart);
if ($shift <= $intLength) {
$splitPos = $intLength - $shift;
$scaledInt = substr($intPart, 0, $splitPos);
$scaledInt = '' === $scaledInt ? '0' : $scaledInt;
$scaledFrac = substr($intPart, $splitPos).$fracPart;
} else {
$scaledInt = '0';
$scaledFrac = str_repeat('0', $shift - $intLength).$intPart.$fracPart;
}
}
$roundedInt = self::roundIntegerPart($scaledInt, $scaledFrac, $sign, $mode);
$isZero = '' === trim($roundedInt, '0');
$absResult = self::formatRoundedDigits($roundedInt, $precision);
if (-1 === $sign && !$isZero) {
$absResult = '-'.$absResult;
}
return $absResult;
}
private static function roundIntegerPart(string $intPart, string $fracPart, int $sign, $mode): string
{
$intPart = self::trimLeadingZeros($intPart);
if ('' === $fracPart || '' === trim($fracPart, '0')) {
return $intPart;
}
$firstDigit = $fracPart[0];
$tail = (string) substr($fracPart, 1);
$tailNonZero = '' !== trim($tail, '0');
$isGreaterThanHalf = $firstDigit > '5' || ('5' === $firstDigit && $tailNonZero);
$isExactlyHalf = '5' === $firstDigit && !$tailNonZero;
$shouldIncrease = false;
switch ($mode) {
case \RoundingMode::TowardsZero:
break;
case \RoundingMode::AwayFromZero:
$shouldIncrease = true;
break;
case \RoundingMode::PositiveInfinity:
$shouldIncrease = $sign > 0;
break;
case \RoundingMode::NegativeInfinity:
$shouldIncrease = $sign < 0;
break;
case \RoundingMode::HalfAwayFromZero:
$shouldIncrease = $isGreaterThanHalf || $isExactlyHalf;
break;
case \RoundingMode::HalfTowardsZero:
$shouldIncrease = $isGreaterThanHalf;
break;
case \RoundingMode::HalfEven:
if ($isGreaterThanHalf) {
$shouldIncrease = true;
} elseif ($isExactlyHalf && 1 === self::lastDigit($intPart) % 2) {
$shouldIncrease = true;
}
break;
case \RoundingMode::HalfOdd:
if ($isGreaterThanHalf) {
$shouldIncrease = true;
} elseif ($isExactlyHalf && 0 === self::lastDigit($intPart) % 2) {
$shouldIncrease = true;
}
break;
}
if ($shouldIncrease) {
$intPart = self::incrementDigits($intPart);
}
return self::trimLeadingZeros($intPart);
}
private static function formatRoundedDigits(string $roundedInt, int $precision): string
{
if ($precision > 0) {
if (\strlen($roundedInt) <= $precision) {
$roundedInt = str_pad($roundedInt, $precision + 1, '0', \STR_PAD_LEFT);
}
$intDigits = substr($roundedInt, 0, -$precision);
$fracDigits = substr($roundedInt, -$precision);
$intDigits = self::trimLeadingZeros('' === $intDigits ? '0' : $intDigits);
$fracDigits = str_pad($fracDigits, $precision, '0', \STR_PAD_LEFT);
return $intDigits.'.'.$fracDigits;
}
if (0 === $precision) {
return self::trimLeadingZeros($roundedInt);
}
$shift = -$precision;
$digits = $roundedInt.str_repeat('0', $shift);
return self::trimLeadingZeros($digits);
}
private static function incrementDigits(string $digits): string
{
$digits = '' === $digits ? '0' : $digits;
$index = \strlen($digits) - 1;
$result = $digits;
$carry = 1;
while ($index >= 0 && $carry) {
$value = \ord($result[$index]) - 48 + $carry;
$carry = $value >= 10 ? 1 : 0;
$result[$index] = \chr(48 + ($value % 10));
--$index;
}
return $carry ? '1'.$result : $result;
}
private static function trimLeadingZeros(string $digits): string
{
$digits = ltrim($digits, '0');
return '' === $digits ? '0' : $digits;
}
private static function lastDigit(string $digits): int
{
$length = \strlen($digits);
return $length ? \ord($digits[$length - 1]) - 48 : 0;
}
/**
* Splits a string that is not valid UTF-8 into grapheme clusters.
*
* The intl extension keeps the ill-formed bytes and breaks around them the
* way it would around the U+FFFD each maximal subpart stands for. Standing
* one in for real makes the cluster regexp applicable, then each cluster is
* mapped back to the bytes it was made of.
*/
private static function splitIllFormed(string $string, string $regex): array
{
// Table 3-7 of the Unicode standard for the well-formed byte sequences (group 1),
// then the maximal subparts an ill-formed sequence is made of (group 2).
preg_match_all('/([\x00-\x7F]|[\xC2-\xDF][\x80-\xBF]|\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC][\x80-\xBF]{2}|\xED[\x80-\x9F][\x80-\xBF]|[\xEE-\xEF][\x80-\xBF]{2}|\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2})|(\xF0[\x90-\xBF][\x80-\xBF]?|[\xF1-\xF3][\x80-\xBF]{1,2}|\xF4[\x80-\x8F][\x80-\xBF]?|\xE0[\xA0-\xBF]|[\xE1-\xEC][\x80-\xBF]|\xED[\x80-\x9F]|[\xEE-\xEF][\x80-\xBF]|[\x80-\xFF])/s', $string, $matches, \PREG_SET_ORDER);
$segments = [];
$segmentAt = [];
$sanitized = '';
foreach ($matches as $m) {
$segmentAt[\strlen($sanitized)] = \count($segments);
if (isset($m[2]) && '' !== $m[2]) {
$segments[] = $m[2];
$sanitized .= "\xEF\xBF\xBD";
} else {
$segments[] = $m[1];
$sanitized .= $m[1];
}
}
$segmentAt[\strlen($sanitized)] = \count($segments);
preg_match_all('/'.$regex.'/u', $sanitized, $clusters, \PREG_OFFSET_CAPTURE);
$graphemes = [];
foreach ($clusters[0] as $cluster) {
$start = $segmentAt[$cluster[1]];
$graphemes[] = implode('', \array_slice($segments, $start, $segmentAt[$cluster[1] + \strlen($cluster[0])] - $start));
}
return $graphemes;
}
}
@@ -0,0 +1,23 @@
Symfony Polyfill / Php84
========================
This component provides features added to PHP 8.4 core:
- [`array_find`, `array_find_key`, `array_any` and `array_all`](https://wiki.php.net/rfc/array_find)
- [`bcdivmod`](https://wiki.php.net/rfc/add_bcdivmod_to_bcmath)
- [`Deprecated`](https://wiki.php.net/rfc/deprecated_attribute)
- `CURL_HTTP_VERSION_3` and `CURL_HTTP_VERSION_3ONLY` constants
- [`fpow`](https://wiki.php.net/rfc/raising_zero_to_power_of_negative_number)
- [`grapheme_str_split`](https://wiki.php.net/rfc/grapheme_str_split)
- [`mb_trim`, `mb_ltrim` and `mb_rtrim`](https://wiki.php.net/rfc/mb_trim)
- [`mb_ucfirst` and `mb_lcfirst`](https://wiki.php.net/rfc/mb_ucfirst)
- [`PDO` driver specific sub-classes](https://wiki.php.net/rfc/pdo_driver_specific_subclasses)
- [`ReflectionConstant`](https://github.com/php/php-src/pull/13669)
More information can be found in the
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).
License
=======
This library is released under the [MIT license](LICENSE).
@@ -0,0 +1,25 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (\PHP_VERSION_ID < 80400) {
#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_FUNCTION | Attribute::TARGET_CLASS_CONSTANT)]
final class Deprecated
{
public readonly ?string $message;
public readonly ?string $since;
public function __construct(?string $message = null, ?string $since = null)
{
$this->message = $message;
$this->since = $since;
}
}
}
@@ -0,0 +1,24 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (\PHP_VERSION_ID < 80400) {
enum RoundingMode
{
case HalfAwayFromZero;
case HalfTowardsZero;
case HalfEven;
case HalfOdd;
case TowardsZero;
case AwayFromZero;
case NegativeInfinity;
case PositiveInfinity;
}
}
@@ -0,0 +1,34 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (\PHP_VERSION_ID < 80100) {
#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_FUNCTION | Attribute::TARGET_CLASS_CONSTANT)]
final class Deprecated
{
/**
* @readonly
*/
public ?string $message;
/**
* @readonly
*/
public ?string $since;
public function __construct(?string $message = null, ?string $since = null)
{
$this->message = $message;
$this->since = $since;
}
}
} elseif (\PHP_VERSION_ID < 80400) {
require dirname(__DIR__).'/Deprecated.php';
}
@@ -0,0 +1,43 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Pdo;
if (\PHP_VERSION_ID < 80400 && \extension_loaded('pdo_dblib')) {
class Dblib extends \PDO
{
public const ATTR_CONNECTION_TIMEOUT = \PDO::DBLIB_ATTR_CONNECTION_TIMEOUT;
public const ATTR_QUERY_TIMEOUT = \PDO::DBLIB_ATTR_QUERY_TIMEOUT;
public const ATTR_STRINGIFY_UNIQUEIDENTIFIER = \PDO::DBLIB_ATTR_STRINGIFY_UNIQUEIDENTIFIER;
public const ATTR_VERSION = \PDO::DBLIB_ATTR_VERSION;
public const ATTR_TDS_VERSION = \PHP_VERSION_ID >= 70300 ? \PDO::DBLIB_ATTR_TDS_VERSION : 1004;
public const ATTR_SKIP_EMPTY_ROWSETS = \PHP_VERSION_ID >= 70300 ? \PDO::DBLIB_ATTR_SKIP_EMPTY_ROWSETS : 1005;
public const ATTR_DATETIME_CONVERT = \PHP_VERSION_ID >= 70300 ? \PDO::DBLIB_ATTR_DATETIME_CONVERT : 1006;
public function __construct(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null)
{
parent::__construct($dsn, $username, $password, $options);
if ('dblib' !== $driver = $this->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
throw new \PDOException(\sprintf('Pdo\Dblib::__construct() cannot be used for connecting to the "%s" driver', $driver));
}
}
public static function connect(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null): self
{
try {
return new self($dsn, $username, $password, $options);
} catch (\PDOException $e) {
throw preg_match('/^Pdo\\\\Dblib::__construct\(\) cannot be used for connecting to the "([a-z]+)" driver/', $e->getMessage(), $matches) ? new \PDOException(\sprintf('Pdo\Dblib::connect() cannot be used for connecting to the "%s" driver', $matches[1])) : $e;
}
}
}
}
@@ -0,0 +1,39 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Pdo;
if (\PHP_VERSION_ID < 80400 && \extension_loaded('pdo_firebird')) {
class Firebird extends \PDO
{
public const ATTR_DATE_FORMAT = \PDO::FB_ATTR_DATE_FORMAT;
public const ATTR_TIME_FORMAT = \PDO::FB_ATTR_TIME_FORMAT;
public const ATTR_TIMESTAMP_FORMAT = \PDO::FB_ATTR_TIMESTAMP_FORMAT;
public function __construct(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null)
{
parent::__construct($dsn, $username, $password, $options);
if ('firebird' !== $driver = $this->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
throw new \PDOException(\sprintf('Pdo\Firebird::__construct() cannot be used for connecting to the "%s" driver', $driver));
}
}
public static function connect(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null): self
{
try {
return new self($dsn, $username, $password, $options);
} catch (\PDOException $e) {
throw preg_match('/^Pdo\\\\Firebird::__construct\(\) cannot be used for connecting to the "([a-z]+)" driver/', $e->getMessage(), $matches) ? new \PDOException(\sprintf('Pdo\Firebird::connect() cannot be used for connecting to the "%s" driver', $matches[1])) : $e;
}
}
}
}
@@ -0,0 +1,140 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Pdo;
use PDO;
if (\PHP_VERSION_ID < 80400 && \extension_loaded('pdo_mysql')) {
// Feature detection for non-mysqlnd; see also https://www.php.net/manual/en/class.pdo-mysql.php#pdo-mysql.constants.attr-max-buffer-size
if (\defined('PDO::MYSQL_ATTR_MAX_BUFFER_SIZE') && \defined('PDO::MYSQL_ATTR_READ_DEFAULT_FILE') && \defined('PDO::MYSQL_ATTR_READ_DEFAULT_GROUP')) {
class Mysql extends \PDO
{
public const ATTR_COMPRESS = \PDO::MYSQL_ATTR_COMPRESS;
public const ATTR_DIRECT_QUERY = \PDO::MYSQL_ATTR_DIRECT_QUERY;
public const ATTR_FOUND_ROWS = \PDO::MYSQL_ATTR_FOUND_ROWS;
public const ATTR_IGNORE_SPACE = \PDO::MYSQL_ATTR_IGNORE_SPACE;
public const ATTR_INIT_COMMAND = \PDO::MYSQL_ATTR_INIT_COMMAND;
public const ATTR_LOCAL_INFILE = \PDO::MYSQL_ATTR_LOCAL_INFILE;
public const ATTR_LOCAL_INFILE_DIRECTORY = \PHP_VERSION_ID >= 80100 ? \PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY : 1015;
public const ATTR_MAX_BUFFER_SIZE = \PDO::MYSQL_ATTR_MAX_BUFFER_SIZE;
public const ATTR_MULTI_STATEMENTS = \PDO::MYSQL_ATTR_MULTI_STATEMENTS;
public const ATTR_READ_DEFAULT_FILE = \PDO::MYSQL_ATTR_READ_DEFAULT_FILE;
public const ATTR_READ_DEFAULT_GROUP = \PDO::MYSQL_ATTR_READ_DEFAULT_GROUP;
public const ATTR_SERVER_PUBLIC_KEY = \PDO::MYSQL_ATTR_SERVER_PUBLIC_KEY;
public const ATTR_SSL_CA = \PDO::MYSQL_ATTR_SSL_CA;
public const ATTR_SSL_CAPATH = \PDO::MYSQL_ATTR_SSL_CAPATH;
public const ATTR_SSL_CERT = \PDO::MYSQL_ATTR_SSL_CERT;
public const ATTR_SSL_CIPHER = \PDO::MYSQL_ATTR_SSL_CIPHER;
public const ATTR_SSL_KEY = \PDO::MYSQL_ATTR_SSL_KEY;
public const ATTR_USE_BUFFERED_QUERY = \PDO::MYSQL_ATTR_USE_BUFFERED_QUERY;
public function __construct(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null)
{
parent::__construct($dsn, $username, $password, $options);
if ('mysql' !== $driver = $this->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
throw new \PDOException(\sprintf('Pdo\Mysql::__construct() cannot be used for connecting to the "%s" driver', $driver));
}
}
public static function connect(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null): self
{
try {
return new self($dsn, $username, $password, $options);
} catch (\PDOException $e) {
throw preg_match('/^Pdo\\\\Mysql::__construct\(\) cannot be used for connecting to the "([a-z]+)" driver/', $e->getMessage(), $matches) ? new \PDOException(\sprintf('Pdo\Mysql::connect() cannot be used for connecting to the "%s" driver', $matches[1])) : $e;
}
}
}
} elseif (\defined('PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT')) {
class Mysql extends \PDO
{
public const ATTR_COMPRESS = \PDO::MYSQL_ATTR_COMPRESS;
public const ATTR_DIRECT_QUERY = \PDO::MYSQL_ATTR_DIRECT_QUERY;
public const ATTR_FOUND_ROWS = \PDO::MYSQL_ATTR_FOUND_ROWS;
public const ATTR_IGNORE_SPACE = \PDO::MYSQL_ATTR_IGNORE_SPACE;
public const ATTR_INIT_COMMAND = \PDO::MYSQL_ATTR_INIT_COMMAND;
public const ATTR_LOCAL_INFILE = \PDO::MYSQL_ATTR_LOCAL_INFILE;
public const ATTR_LOCAL_INFILE_DIRECTORY = \PHP_VERSION_ID >= 80100 ? \PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY : 1015;
// public const ATTR_MAX_BUFFER_SIZE = PDO::MYSQL_ATTR_MAX_BUFFER_SIZE; // disabled for mysqlnd
public const ATTR_MULTI_STATEMENTS = \PDO::MYSQL_ATTR_MULTI_STATEMENTS;
// public const ATTR_READ_DEFAULT_FILE = PDO::MYSQL_ATTR_READ_DEFAULT_FILE; // disabled for mysqlnd
// public const ATTR_READ_DEFAULT_GROUP = PDO::MYSQL_ATTR_READ_DEFAULT_GROUP; // disabled for mysqlnd
public const ATTR_SERVER_PUBLIC_KEY = \PDO::MYSQL_ATTR_SERVER_PUBLIC_KEY;
public const ATTR_SSL_CA = \PDO::MYSQL_ATTR_SSL_CA;
public const ATTR_SSL_CAPATH = \PDO::MYSQL_ATTR_SSL_CAPATH;
public const ATTR_SSL_CERT = \PDO::MYSQL_ATTR_SSL_CERT;
public const ATTR_SSL_CIPHER = \PDO::MYSQL_ATTR_SSL_CIPHER;
public const ATTR_SSL_KEY = \PDO::MYSQL_ATTR_SSL_KEY;
public const ATTR_SSL_VERIFY_SERVER_CERT = \PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT;
public const ATTR_USE_BUFFERED_QUERY = \PDO::MYSQL_ATTR_USE_BUFFERED_QUERY;
public function __construct(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null)
{
parent::__construct($dsn, $username, $password, $options);
if ('mysql' !== $driver = $this->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
throw new \PDOException(\sprintf('Pdo\Mysql::__construct() cannot be used for connecting to the "%s" driver', $driver));
}
}
public static function connect(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null): self
{
try {
return new self($dsn, $username, $password, $options);
} catch (\PDOException $e) {
throw preg_match('/^Pdo\\\\Mysql::__construct\(\) cannot be used for connecting to the "([a-z]+)" driver/', $e->getMessage(), $matches) ? new \PDOException(\sprintf('Pdo\Mysql::connect() cannot be used for connecting to the "%s" driver', $matches[1])) : $e;
}
}
}
} else {
class Mysql extends \PDO
{
public const ATTR_COMPRESS = \PDO::MYSQL_ATTR_COMPRESS;
public const ATTR_DIRECT_QUERY = \PDO::MYSQL_ATTR_DIRECT_QUERY;
public const ATTR_FOUND_ROWS = \PDO::MYSQL_ATTR_FOUND_ROWS;
public const ATTR_IGNORE_SPACE = \PDO::MYSQL_ATTR_IGNORE_SPACE;
public const ATTR_INIT_COMMAND = \PDO::MYSQL_ATTR_INIT_COMMAND;
public const ATTR_LOCAL_INFILE = \PDO::MYSQL_ATTR_LOCAL_INFILE;
public const ATTR_LOCAL_INFILE_DIRECTORY = \PHP_VERSION_ID >= 80100 ? \PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY : 1015;
// public const ATTR_MAX_BUFFER_SIZE = PDO::MYSQL_ATTR_MAX_BUFFER_SIZE; // disabled for mysqlnd
public const ATTR_MULTI_STATEMENTS = \PDO::MYSQL_ATTR_MULTI_STATEMENTS;
// public const ATTR_READ_DEFAULT_FILE = PDO::MYSQL_ATTR_READ_DEFAULT_FILE; // disabled for mysqlnd
// public const ATTR_READ_DEFAULT_GROUP = PDO::MYSQL_ATTR_READ_DEFAULT_GROUP; // disabled for mysqlnd
public const ATTR_SERVER_PUBLIC_KEY = \PDO::MYSQL_ATTR_SERVER_PUBLIC_KEY;
public const ATTR_SSL_CA = \PDO::MYSQL_ATTR_SSL_CA;
public const ATTR_SSL_CAPATH = \PDO::MYSQL_ATTR_SSL_CAPATH;
public const ATTR_SSL_CERT = \PDO::MYSQL_ATTR_SSL_CERT;
public const ATTR_SSL_CIPHER = \PDO::MYSQL_ATTR_SSL_CIPHER;
public const ATTR_SSL_KEY = \PDO::MYSQL_ATTR_SSL_KEY;
public const ATTR_USE_BUFFERED_QUERY = \PDO::MYSQL_ATTR_USE_BUFFERED_QUERY;
public function __construct(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null)
{
parent::__construct($dsn, $username, $password, $options);
if ('mysql' !== $driver = $this->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
throw new \PDOException(\sprintf('Pdo\Mysql::__construct() cannot be used for connecting to the "%s" driver', $driver));
}
}
public static function connect(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null): self
{
try {
return new self($dsn, $username, $password, $options);
} catch (\PDOException $e) {
throw preg_match('/^Pdo\\\\Mysql::__construct\(\) cannot be used for connecting to the "([a-z]+)" driver/', $e->getMessage(), $matches) ? new \PDOException(\sprintf('Pdo\Mysql::connect() cannot be used for connecting to the "%s" driver', $matches[1])) : $e;
}
}
}
}
}
@@ -0,0 +1,41 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Pdo;
if (\PHP_VERSION_ID < 80400 && \extension_loaded('pdo_odbc')) {
class Odbc extends \PDO
{
public const ATTR_USE_CURSOR_LIBRARY = \PDO::ODBC_ATTR_USE_CURSOR_LIBRARY;
public const ATTR_ASSUME_UTF8 = \PDO::ODBC_ATTR_ASSUME_UTF8;
public const SQL_USE_IF_NEEDED = \PDO::ODBC_SQL_USE_IF_NEEDED;
public const SQL_USE_DRIVER = \PDO::ODBC_SQL_USE_DRIVER;
public const SQL_USE_ODBC = \PDO::ODBC_SQL_USE_ODBC;
public function __construct(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null)
{
parent::__construct($dsn, $username, $password, $options);
if ('odbc' !== $driver = $this->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
throw new \PDOException(\sprintf('Pdo\Odbc::__construct() cannot be used for connecting to the "%s" driver', $driver));
}
}
public static function connect(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null): self
{
try {
return new self($dsn, $username, $password, $options);
} catch (\PDOException $e) {
throw preg_match('/^Pdo\\\\Odbc::__construct\(\) cannot be used for connecting to the "([a-z]+)" driver/', $e->getMessage(), $matches) ? new \PDOException(\sprintf('Pdo\Odbc::connect() cannot be used for connecting to the "%s" driver', $matches[1])) : $e;
}
}
}
}
@@ -0,0 +1,94 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Pdo;
if (\PHP_VERSION_ID < 80400 && \extension_loaded('pdo_pgsql')) {
class Pgsql extends \PDO
{
public const ATTR_DISABLE_PREPARES = \PDO::PGSQL_ATTR_DISABLE_PREPARES;
public function __construct(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null)
{
parent::__construct($dsn, $username, $password, $options);
if ('pgsql' !== $driver = $this->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
throw new \PDOException(\sprintf('Pdo\Pgsql::__construct() cannot be used for connecting to the "%s" driver', $driver));
}
}
public static function connect(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null): self
{
try {
return new self($dsn, $username, $password, $options);
} catch (\PDOException $e) {
throw preg_match('/^Pdo\\\\Pgsql::__construct\(\) cannot be used for connecting to the "([a-z]+)" driver/', $e->getMessage(), $matches) ? new \PDOException(\sprintf('Pdo\Pgsql::connect() cannot be used for connecting to the "%s" driver', $matches[1])) : $e;
}
}
public function copyFromArray(string $tableName, array $rows, string $separator = "\t", string $nullAs = '\\\\N', ?string $fields = null): bool
{
return $this->pgsqlCopyFromArray($tableName, $rows, $separator, $nullAs, $fields);
}
public function copyFromFile(string $tableName, string $filename, string $separator = "\t", string $nullAs = '\\\\N', ?string $fields = null): bool
{
return $this->pgsqlCopyFromFile($tableName, $filename, $separator, $nullAs, $fields);
}
/**
* @return array|false
*/
public function copyToArray(string $tableName, string $separator = "\t", string $nullAs = '\\\\N', ?string $fields = null)
{
return $this->pgsqlCopyToArray($tableName, $separator, $nullAs, $fields);
}
public function copyToFile(string $tableName, string $filename, string $separator = "\t", string $nullAs = '\\\\N', ?string $fields = null): bool
{
return $this->pgsqlCopyToFile($tableName, $filename, $separator, $nullAs, $fields);
}
/**
* @return array|false
*/
public function getNotify(int $fetchMode = \PDO::FETCH_DEFAULT, int $timeoutMilliseconds = 0)
{
return $this->pgsqlGetNotify($fetchMode, $timeoutMilliseconds);
}
public function getPid(): int
{
return $this->pgsqlGetPid();
}
/**
* @return string|false
*/
public function lobCreate()
{
return $this->pgsqlLOBCreate();
}
/**
* @return resource|false
*/
public function lobOpen(string $oid, string $mode = 'rb')
{
return $this->pgsqlLOBOpen($oid, $mode);
}
public function lobUnlink(string $oid): bool
{
return $this->pgsqlLOBUnlink($oid);
}
}
}
@@ -0,0 +1,58 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Pdo;
if (\PHP_VERSION_ID < 80400 && \extension_loaded('pdo_sqlite')) {
class Sqlite extends \PDO
{
public const ATTR_EXTENDED_RESULT_CODES = \PHP_VERSION_ID >= 70400 ? \PDO::SQLITE_ATTR_EXTENDED_RESULT_CODES : 1002;
public const ATTR_OPEN_FLAGS = \PHP_VERSION_ID >= 70300 ? \PDO::SQLITE_ATTR_OPEN_FLAGS : 1000;
public const ATTR_READONLY_STATEMENT = \PHP_VERSION_ID >= 70400 ? \PDO::SQLITE_ATTR_READONLY_STATEMENT : 1001;
public const DETERMINISTIC = \PDO::SQLITE_DETERMINISTIC;
public const OPEN_READONLY = \PHP_VERSION_ID >= 70300 ? \PDO::SQLITE_OPEN_READONLY : 1;
public const OPEN_READWRITE = \PHP_VERSION_ID >= 70300 ? \PDO::SQLITE_OPEN_READWRITE : 2;
public const OPEN_CREATE = \PHP_VERSION_ID >= 70300 ? \PDO::SQLITE_OPEN_CREATE : 4;
public function __construct(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null)
{
parent::__construct($dsn, $username, $password, $options);
if ('sqlite' !== $driver = $this->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
throw new \PDOException(\sprintf('Pdo\Sqlite::__construct() cannot be used for connecting to the "%s" driver', $driver));
}
}
public static function connect(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null): self
{
try {
return new self($dsn, $username, $password, $options);
} catch (\PDOException $e) {
throw preg_match('/^Pdo\\\\Sqlite::__construct\(\) cannot be used for connecting to the "([a-z]+)" driver/', $e->getMessage(), $matches) ? new \PDOException(\sprintf('Pdo\Sqlite::connect() cannot be used for connecting to the "%s" driver', $matches[1])) : $e;
}
}
public function createAggregate(string $name, callable $step, callable $finalize, int $numArgs = -1): bool
{
return $this->sqliteCreateAggregate($name, $step, $finalize, $numArgs);
}
public function createCollation(string $name, callable $callback): bool
{
return $this->sqliteCreateCollation($name, $callback);
}
public function createFunction(string $function_name, callable $callback, int $num_args = -1, int $flags = 0): bool
{
return $this->sqliteCreateFunction($function_name, $callback, $num_args, $flags);
}
}
}
@@ -0,0 +1,157 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (\PHP_VERSION_ID < 80400) {
// @author Daniel Scherzer <daniel.e.scherzer@gmail.com>
final class ReflectionConstant
{
/**
* @var string
*
* @readonly
*/
public $name;
private $value;
private $deprecated;
private $persistent;
private static $persistentConstants = [];
public function __construct(string $name)
{
if (!defined($name) || false !== strpos($name, '::')) {
throw new ReflectionException("Constant \"$name\" does not exist");
}
$this->name = ltrim($name, '\\');
$deprecated = false;
$eh = set_error_handler(static function ($type, $msg, $file, $line) use ($name, &$deprecated, &$eh) {
if (\E_DEPRECATED === $type && "Constant $name is deprecated" === $msg) {
return $deprecated = true;
}
return $eh && $eh($type, $msg, $file, $line);
});
try {
$this->value = constant($name);
$this->deprecated = $deprecated;
} finally {
restore_error_handler();
}
}
public function getName(): string
{
return $this->name;
}
public function getValue()
{
return $this->value;
}
public function getNamespaceName(): string
{
if (false === $slashPos = strrpos($this->name, '\\')) {
return '';
}
return substr($this->name, 0, $slashPos);
}
public function getShortName(): string
{
if (false === $slashPos = strrpos($this->name, '\\')) {
return $this->name;
}
return substr($this->name, $slashPos + 1);
}
public function isDeprecated(): bool
{
return $this->deprecated;
}
public function __toString(): string
{
// A constant is persistent if provided by PHP itself rather than
// being defined by users. If we got here, we know that it *is*
// defined, so we just need to figure out if it is defined by the
// user or not
if (!self::$persistentConstants) {
$persistentConstants = get_defined_constants(true);
unset($persistentConstants['user']);
foreach ($persistentConstants as $constants) {
self::$persistentConstants += $constants;
}
}
$persistent = array_key_exists($this->name, self::$persistentConstants);
// Can't match the inclusion of `no_file_cache` but the rest is
// possible to match
$result = 'Constant [ ';
if ($persistent || $this->deprecated) {
$result .= '<';
if ($persistent) {
$result .= 'persistent';
if ($this->deprecated) {
$result .= ', ';
}
}
if ($this->deprecated) {
$result .= 'deprecated';
}
$result .= '> ';
}
// Cannot just use gettype() to match zend_zval_type_name()
if (is_object($this->value)) {
$result .= \PHP_VERSION_ID >= 80000 ? get_debug_type($this->value) : gettype($this->value);
} elseif (is_bool($this->value)) {
$result .= 'bool';
} elseif (is_int($this->value)) {
$result .= 'int';
} elseif (is_float($this->value)) {
$result .= 'float';
} elseif (null === $this->value) {
$result .= 'null';
} else {
$result .= gettype($this->value);
}
$result .= ' ';
$result .= $this->name;
$result .= ' ] { ';
if (is_array($this->value)) {
$result .= 'Array';
} else {
// This will throw an exception if the value is an object that
// cannot be converted to string; that is expected and matches
// the behavior of zval_get_string_func()
$result .= (string) $this->value;
}
$result .= " }\n";
return $result;
}
public function __sleep(): array
{
throw new Exception("Serialization of 'ReflectionConstant' is not allowed");
}
public function __wakeup(): void
{
throw new Exception("Unserialization of 'ReflectionConstant' is not allowed");
}
}
}
@@ -0,0 +1,44 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (\PHP_VERSION_ID < 80100) {
final class RoundingMode
{
const HalfAwayFromZero = 0;
const HalfTowardsZero = 1;
const HalfEven = 2;
const HalfOdd = 3;
const TowardsZero = 4;
const AwayFromZero = 5;
const NegativeInfinity = 6;
const PositiveInfinity = 7;
private function __construct()
{
}
public static function cases(): array
{
return [
self::HalfAwayFromZero,
self::HalfTowardsZero,
self::HalfEven,
self::HalfOdd,
self::TowardsZero,
self::AwayFromZero,
self::NegativeInfinity,
self::PositiveInfinity,
];
}
}
} elseif (\PHP_VERSION_ID < 80400) {
require dirname(__DIR__).'/RoundingMode.php';
}
@@ -0,0 +1,99 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Php84 as p;
if (\PHP_VERSION_ID >= 80400) {
return;
}
if (\extension_loaded('curl')) {
// CURL_VERSION_HTTP3 is defined by PHP 8.2+ when libcurl >= 7.66.0
if (defined('CURL_VERSION_HTTP3') || \PHP_VERSION_ID < 80200 && curl_version()['version_number'] >= 0x074200) {
if (!defined('CURL_HTTP_VERSION_3')) {
define('CURL_HTTP_VERSION_3', 30);
}
// CURL_HTTP_VERSION_3ONLY requires libcurl >= 7.88.0 and is not gated by any PHP-defined constant before 8.4
if (!defined('CURL_HTTP_VERSION_3ONLY') && curl_version()['version_number'] >= 0x075800) {
define('CURL_HTTP_VERSION_3ONLY', 31);
}
}
}
if (!function_exists('array_find')) {
function array_find(array $array, callable $callback) { return p\Php84::array_find($array, $callback); }
}
if (!function_exists('array_find_key')) {
function array_find_key(array $array, callable $callback) { return p\Php84::array_find_key($array, $callback); }
}
if (!function_exists('array_any')) {
function array_any(array $array, callable $callback): bool { return p\Php84::array_any($array, $callback); }
}
if (!function_exists('array_all')) {
function array_all(array $array, callable $callback): bool { return p\Php84::array_all($array, $callback); }
}
if (!function_exists('fpow')) {
function fpow(float $num, float $exponent): float { return p\Php84::fpow($num, $exponent); }
}
if (\PHP_VERSION_ID < 80000) {
require __DIR__.'/bootstrap72.php';
}
if (extension_loaded('mbstring')) {
if (!function_exists('mb_ucfirst')) {
function mb_ucfirst(?string $string, ?string $encoding = null): string { return p\Php84::mb_ucfirst((string) $string, $encoding); }
}
if (!function_exists('mb_lcfirst')) {
function mb_lcfirst(?string $string, ?string $encoding = null): string { return p\Php84::mb_lcfirst((string) $string, $encoding); }
}
if (!function_exists('mb_trim')) {
function mb_trim(?string $string, ?string $characters = null, ?string $encoding = null): string { return p\Php84::mb_trim((string) $string, $characters, $encoding); }
}
if (!function_exists('mb_ltrim')) {
function mb_ltrim(?string $string, ?string $characters = null, ?string $encoding = null): string { return p\Php84::mb_ltrim((string) $string, $characters, $encoding); }
}
if (!function_exists('mb_rtrim')) {
function mb_rtrim(?string $string, ?string $characters = null, ?string $encoding = null): string { return p\Php84::mb_rtrim((string) $string, $characters, $encoding); }
}
}
if (extension_loaded('bcmath')) {
if (!function_exists('bcceil')) {
function bcceil(string $num): string { return p\Php84::bcceil($num); }
}
if (!function_exists('bcdivmod')) {
function bcdivmod(string $num1, string $num2, ?int $scale = null): ?array { return p\Php84::bcdivmod($num1, $num2, $scale); }
}
if (!function_exists('bcfloor')) {
function bcfloor(string $num): string { return p\Php84::bcfloor($num); }
}
if (!function_exists('bcround')) {
function bcround(string $num, int $precision = 0, $mode = \RoundingMode::HalfAwayFromZero): string { return p\Php84::bcround($num, $precision, $mode); }
}
}
if (\PHP_VERSION_ID >= 80200) {
return require __DIR__.'/bootstrap82.php';
}
if (extension_loaded('intl') && !function_exists('grapheme_str_split')) {
function grapheme_str_split(string $string, int $length = 1) { return p\Php84::grapheme_str_split($string, $length); }
}
@@ -0,0 +1,39 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Php84 as p;
if (extension_loaded('mbstring')) {
if (!function_exists('mb_ucfirst')) {
/** @return string|false */
function mb_ucfirst(?string $string, ?string $encoding = null) { return p\Php84::mb_ucfirst((string) $string, $encoding); }
}
if (!function_exists('mb_lcfirst')) {
/** @return string|false */
function mb_lcfirst(?string $string, ?string $encoding = null) { return p\Php84::mb_lcfirst((string) $string, $encoding); }
}
if (!function_exists('mb_trim')) {
/** @return string|false */
function mb_trim(?string $string, ?string $characters = null, ?string $encoding = null) { return p\Php84::mb_trim((string) $string, $characters, $encoding); }
}
if (!function_exists('mb_ltrim')) {
/** @return string|false */
function mb_ltrim(?string $string, ?string $characters = null, ?string $encoding = null) { return p\Php84::mb_ltrim((string) $string, $characters, $encoding); }
}
if (!function_exists('mb_rtrim')) {
/** @return string|false */
function mb_rtrim(?string $string, ?string $characters = null, ?string $encoding = null) { return p\Php84::mb_rtrim((string) $string, $characters, $encoding); }
}
}
@@ -0,0 +1,20 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Php84 as p;
if (\PHP_VERSION_ID >= 80400) {
return;
}
if (extension_loaded('intl') && !function_exists('grapheme_str_split')) {
function grapheme_str_split(string $string, int $length = 1): array|false { return p\Php84::grapheme_str_split($string, $length); }
}
@@ -0,0 +1,33 @@
{
"name": "symfony/polyfill-php84",
"type": "library",
"description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions",
"keywords": ["polyfill", "shim", "compatibility", "portable"],
"homepage": "https://symfony.com",
"license": "MIT",
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"require": {
"php": ">=7.2"
},
"autoload": {
"psr-4": { "Symfony\\Polyfill\\Php84\\": "" },
"files": [ "bootstrap.php" ],
"classmap": [ "Resources/stubs" ]
},
"minimum-stability": "dev",
"extra": {
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
}
}
+19
View File
@@ -0,0 +1,19 @@
Copyright (c) 2025-present Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+182
View File
@@ -0,0 +1,182 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Php85;
/**
* @author Pierre Ambroise <pierre27.ambroise@gmail.com>
* @author Alexander Schranz <alexander@sulu.io>
*
* @internal
*/
final class Php85
{
public static function get_error_handler(): ?callable
{
$handler = set_error_handler(null);
restore_error_handler();
return $handler;
}
public static function get_exception_handler(): ?callable
{
$handler = set_exception_handler(null);
restore_exception_handler();
return $handler;
}
public static function array_first(array $array)
{
foreach ($array as $value) {
return $value;
}
return null;
}
public static function array_last(array $array)
{
return $array ? current(\array_slice($array, -1)) : null;
}
// Taken from CLDR 44 (ICU 74); the exact set is ICU-version dependent and won't match every version of the native locale_is_right_to_left().
private const RTL_SCRIPTS = [
'Adlm' => true, 'Arab' => true, 'Armi' => true, 'Avst' => true,
'Chrs' => true, 'Cprt' => true, 'Elym' => true, 'Hatr' => true,
'Hebr' => true, 'Hung' => true, 'Khar' => true, 'Lydi' => true,
'Mand' => true, 'Mani' => true, 'Mend' => true, 'Merc' => true,
'Mero' => true, 'Narb' => true, 'Nbat' => true, 'Nkoo' => true,
'Orkh' => true, 'Ougr' => true, 'Palm' => true, 'Phli' => true,
'Phlp' => true, 'Phnx' => true, 'Prti' => true, 'Rohg' => true,
'Samr' => true, 'Sarb' => true, 'Sogd' => true, 'Sogo' => true,
'Syrc' => true, 'Thaa' => true, 'Yezi' => true,
];
private const LANG_TO_SCRIPT = [
'aeb' => 'Arab',
'ar' => 'Arab',
'arc' => 'Armi',
'arq' => 'Arab',
'ary' => 'Arab',
'arz' => 'Arab',
'bal' => 'Arab',
'bgn' => 'Arab',
'ckb' => 'Arab',
'dv' => 'Thaa',
'fa' => 'Arab',
'glk' => 'Arab',
'haz' => 'Arab',
'he' => 'Hebr',
'iw' => 'Hebr',
'kas' => 'Arab',
'ks' => 'Arab',
'lrc' => 'Arab',
'mzn' => 'Arab',
'nqo' => 'Nkoo',
'pnb' => 'Arab',
'ps' => 'Arab',
'rhg' => 'Rohg',
'sam' => 'Samr',
'sd' => 'Arab',
'sdh' => 'Arab',
'skr' => 'Arab',
'syr' => 'Syrc',
'ug' => 'Arab',
'ur' => 'Arab',
'yi' => 'Hebr',
];
public static function locale_is_right_to_left(string $locale): bool
{
if ('' === $locale) {
$locale = \Locale::getDefault();
}
$parts = preg_split('/[_-]/', substr($locale, 0, strcspn($locale, '@.')));
$language = strtolower($parts[0]);
if (isset($parts[1]) && 4 === \strlen($parts[1]) && ctype_alpha($parts[1])) {
return isset(self::RTL_SCRIPTS[ucfirst(strtolower($parts[1]))]);
}
return isset(self::LANG_TO_SCRIPT[$language]) && isset(self::RTL_SCRIPTS[self::LANG_TO_SCRIPT[$language]]);
}
public static function grapheme_levenshtein(string $s1, string $s2, int $insertion_cost = 1, int $replacement_cost = 1, int $deletion_cost = 1, string $locale = '')
{
if ($insertion_cost <= 0 || $insertion_cost > 1073741823) {
throw new \ValueError('grapheme_levenshtein(): Argument #3 ($insertion_cost) must be greater than 0 and less than or equal to 1073741823');
}
if ($replacement_cost <= 0 || $replacement_cost > 1073741823) {
throw new \ValueError('grapheme_levenshtein(): Argument #4 ($replacement_cost) must be greater than 0 and less than or equal to 1073741823');
}
if ($deletion_cost <= 0 || $deletion_cost > 1073741823) {
throw new \ValueError('grapheme_levenshtein(): Argument #5 ($deletion_cost) must be greater than 0 and less than or equal to 1073741823');
}
if (!preg_match('//u', $s1) || !preg_match('//u', $s2)) {
return false;
}
$regex = ((float) \PCRE_VERSION >= 10.44)
? '\X'
: '(?:\r\n|[\x{1F1E6}-\x{1F1FF}][\x{1F1E6}-\x{1F1FF}]?|(?:[ -~\x{200C}\x{200D}]|[ᆨ-ᇹ]+|[ᄀ-ᅟ]*(?:[가개갸걔거게겨계고과괘괴교구궈궤귀규그긔기까깨꺄꺠꺼께껴꼐꼬꽈꽤꾀꾜꾸꿔꿰뀌뀨끄끠끼나내냐냬너네녀녜노놔놰뇌뇨누눠눼뉘뉴느늬니다대댜댸더데뎌뎨도돠돼되됴두둬뒈뒤듀드듸디따때땨떄떠떼뗘뗴또똬뙈뙤뚀뚜뚸뛔뛰뜌뜨띄띠라래랴럐러레려례로롸뢔뢰료루뤄뤠뤼류르릐리마매먀먜머메며몌모뫄뫠뫼묘무뭐뭬뮈뮤므믜미바배뱌뱨버베벼볘보봐봬뵈뵤부붜붸뷔뷰브븨비빠빼뺘뺴뻐뻬뼈뼤뽀뽜뽸뾔뾰뿌뿨쀄쀠쀼쁘쁴삐사새샤섀서세셔셰소솨쇄쇠쇼수숴쉐쉬슈스싀시싸쌔쌰썌써쎄쎠쎼쏘쏴쐐쐬쑈쑤쒀쒜쒸쓔쓰씌씨아애야얘어에여예오와왜외요우워웨위유으의이자재쟈쟤저제져졔조좌좨죄죠주줘줴쥐쥬즈즤지짜째쨔쨰쩌쩨쪄쪠쪼쫘쫴쬐쬬쭈쭤쮀쮜쮸쯔쯰찌차채챠챼처체쳐쳬초촤쵀최쵸추춰췌취츄츠츼치카캐캬컈커케켜켸코콰쾌쾨쿄쿠쿼퀘퀴큐크킈키타태탸턔터테텨톄토톼퇘퇴툐투퉈퉤튀튜트틔티파패퍄퍠퍼페펴폐포퐈퐤푀표푸풔풰퓌퓨프픠피하해햐햬허헤혀혜호화홰회효후훠훼휘휴흐희히]?[ᅠ-ᆢ]+|[가-힣])[ᆨ-ᇹ]*|[ᄀ-ᅟ]+|[^\p{Cc}\p{Cf}\p{Zl}\p{Zp}])[\p{Mn}\p{Mc}\p{Me}\x{09BE}\x{09D7}\x{0B3E}\x{0B57}\x{0BBE}\x{0BD7}\x{0CC2}\x{0CD5}\x{0CD6}\x{0D3E}\x{0D57}\x{0DCF}\x{0DDF}\x{200C}\x{1D165}\x{1D16E}-\x{1D172}\x{1F3FB}-\x{1F3FF}\x{FE0E}-\x{FE0F}\x{E0020}-\x{E007F}]*(?:\x{200D}(?:[\x{1F1E6}-\x{1F1FF}]|[ -~\x{200C}\x{200D}]|[ᆨ-ᇹ]+|[ᄀ-ᅟ]*(?:[가-힣]?[ᅠ-ᆢ]+|[가-힣])[ᆨ-ᇹ]*|[ᄀ-ᅟ]+|[^\p{Cc}\p{Cf}\p{Zl}\p{Zp}])[\p{Mn}\p{Mc}\p{Me}\x{09BE}\x{09D7}\x{0B3E}\x{0B57}\x{0BBE}\x{0BD7}\x{0CC2}\x{0CD5}\x{0CD6}\x{0D3E}\x{0D57}\x{0DCF}\x{0DDF}\x{200C}\x{1D165}\x{1D16E}-\x{1D172}\x{1F3FB}-\x{1F3FF}\x{FE0E}-\x{FE0F}\x{E0020}-\x{E007F}]*)*|[\p{Cc}\p{Cf}\p{Zl}\p{Zp}])';
preg_match_all('/'.$regex.'/u', $s1, $s1);
preg_match_all('/'.$regex.'/u', $s2, $s2);
$s1 = $s1[0];
$s2 = $s2[0];
$l1 = \count($s1);
$l2 = \count($s2);
// Keep the rows as short as possible. Reversing the transformation
// swaps the meaning of insertion and deletion.
if ($l1 < $l2) {
[$s1, $s2] = [$s2, $s1];
[$l1, $l2] = [$l2, $l1];
[$insertion_cost, $deletion_cost] = [$deletion_cost, $insertion_cost];
}
if (0 === $l2) {
return $l1 * $deletion_cost;
}
// Graphemes are equal when the collator says so, as with ucol_strcoll() in intl
try {
$collator = new \Collator('' === $locale ? 'root' : $locale);
} catch (\IntlException $e) {
return false;
}
$s1 = array_map([$collator, 'getSortKey'], $s1);
$s2 = array_map([$collator, 'getSortKey'], $s2);
$previousRow = $currentRow = array_fill(0, $l2 + 1, 0);
for ($j = 1; $j <= $l2; ++$j) {
$previousRow[$j] = $previousRow[$j - 1] + $insertion_cost;
}
for ($i = 1; $i <= $l1; ++$i) {
$currentRow[0] = $previousRow[0] + $deletion_cost;
for ($j = 1; $j <= $l2; ++$j) {
$cost = ($s1[$i - 1] === $s2[$j - 1]) ? 0 : $replacement_cost;
$currentRow[$j] = min($previousRow[$j] + $deletion_cost, $currentRow[$j - 1] + $insertion_cost, $previousRow[$j - 1] + $cost);
}
[$previousRow, $currentRow] = [$currentRow, $previousRow];
}
return $previousRow[$l2];
}
}
@@ -0,0 +1,20 @@
Symfony Polyfill / Php85
========================
This component provides features added to PHP 8.5 core:
- [`get_error_handler` and `get_exception_handler`](https://wiki.php.net/rfc/get-error-exception-handler)
- [`NoDiscard`](https://wiki.php.net/rfc/marking_return_value_as_important)
- [`array_first` and `array_last`](https://wiki.php.net/rfc/array_first_last)
- [`DelayedTargetValidation`](https://wiki.php.net/rfc/delayedtargetvalidation_attribute)
- [`Filter\FilterException class`](https://wiki.php.net/rfc/filter_throw_on_failure)
- [`Filter\FilterFailedException class`](https://wiki.php.net/rfc/filter_throw_on_failure)
- [`locale_is_right_to_left`](https://php.watch/versions/8.5/locale_is_right_to_left-Locale-isRightToleft)
More information can be found in the
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).
License
=======
This library is released under the [MIT license](LICENSE).
@@ -0,0 +1,18 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (\PHP_VERSION_ID < 80500) {
// @author Daniel Scherzer <daniel.e.scherzer@gmail.com>
#[Attribute(Attribute::TARGET_ALL)]
final class DelayedTargetValidation
{
}
}
@@ -0,0 +1,18 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Filter;
if (\PHP_VERSION_ID < 80500) {
class FilterException extends \Exception
{
}
}
@@ -0,0 +1,18 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Filter;
if (\PHP_VERSION_ID < 80500) {
class FilterFailedException extends FilterException
{
}
}
@@ -0,0 +1,23 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (\PHP_VERSION_ID < 80500) {
#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_FUNCTION)]
final class NoDiscard
{
public ?string $message;
public function __construct(?string $message = null)
{
$this->message = $message;
}
}
}
@@ -0,0 +1,52 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Php85 as p;
if (\PHP_VERSION_ID >= 80500) {
return;
}
if (!function_exists('get_error_handler')) {
function get_error_handler(): ?callable { return p\Php85::get_error_handler(); }
}
if (!function_exists('get_exception_handler')) {
function get_exception_handler(): ?callable { return p\Php85::get_exception_handler(); }
}
if (!function_exists('array_first')) {
function array_first(array $array) { return p\Php85::array_first($array); }
}
if (!function_exists('array_last')) {
function array_last(array $array) { return p\Php85::array_last($array); }
}
if (extension_loaded('intl') && !function_exists('locale_is_right_to_left')) {
function locale_is_right_to_left(string $locale): bool { return p\Php85::locale_is_right_to_left($locale); }
}
if (\PHP_VERSION_ID >= 80000) {
require __DIR__.'/bootstrap80.php';
return;
}
if (!class_exists('ValueError', false)) {
class ValueError extends Error
{
}
}
if (extension_loaded('intl') && !function_exists('grapheme_levenshtein')) {
function grapheme_levenshtein(string $string1, string $string2, ?int $insertion_cost = 1, ?int $replacement_cost = 1, ?int $deletion_cost = 1, string $locale = '') { return p\Php85::grapheme_levenshtein($string1, $string2, (int) $insertion_cost, (int) $replacement_cost, (int) $deletion_cost, $locale); }
}
@@ -0,0 +1,20 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Php85 as p;
if (\PHP_VERSION_ID >= 80500) {
return;
}
if (extension_loaded('intl') && !function_exists('grapheme_levenshtein')) {
function grapheme_levenshtein(string $string1, string $string2, ?int $insertion_cost = 1, ?int $replacement_cost = 1, ?int $deletion_cost = 1, string $locale = ''): int|false { return p\Php85::grapheme_levenshtein($string1, $string2, (int) $insertion_cost, (int) $replacement_cost, (int) $deletion_cost, $locale); }
}
@@ -0,0 +1,33 @@
{
"name": "symfony/polyfill-php85",
"type": "library",
"description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions",
"keywords": ["polyfill", "shim", "compatibility", "portable"],
"homepage": "https://symfony.com",
"license": "MIT",
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"require": {
"php": ">=7.2"
},
"autoload": {
"psr-4": { "Symfony\\Polyfill\\Php85\\": "" },
"files": [ "bootstrap.php" ],
"classmap": [ "Resources/stubs" ]
},
"minimum-stability": "dev",
"extra": {
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
}
}
+19
View File
@@ -0,0 +1,19 @@
Copyright (c) 2026-present Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+21
View File
@@ -0,0 +1,21 @@
Symfony Polyfill / Time
=======================
This component provides the [`Time` API](https://wiki.php.net/rfc/duration_class)
added to PHP 8.6 core, for PHP >= 8.1:
- `Time\TimeException`, `Time\Duration`
The phpt tests of the native implementation, borrowed from php-src, run against
the polyfill as part of the test suite. One divergence remains: `Duration` is a
plain final class with readonly properties instead of a `readonly` class, so
`ReflectionClass::newInstanceWithoutConstructor()` succeeds where the native
class rejects it. Dynamic properties are rejected as they are natively.
More information can be found in the
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).
License
=======
This library is released under the [MIT license](LICENSE).
@@ -0,0 +1,489 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Time;
if (\PHP_VERSION_ID < 80600) {
/**
* @see https://wiki.php.net/rfc/duration_class
*/
final class Duration
{
private const NANOS_PER_SECOND = 1_000_000_000;
/**
* Maximum seconds accepted by Duration.
*
* One second is reserved to avoid overflowing when arithmetic operations
* temporarily require carrying nanoseconds into the seconds component.
*/
private const MAX_SECONDS = \PHP_INT_SIZE >= 8 ? 9_223_372_035 : \PHP_INT_MAX;
private const RANGE_ERROR = \PHP_INT_SIZE >= 8
? 'The maximum representable range is 9_223_372_035 seconds (roughly 292 years)'
: 'The maximum representable range is 2_147_483_647 seconds (roughly 68 years)';
/**
* Regular expression to parse an ISO-8601 duration string.
*
* Year, Month, Week and Day components are intentionally not supported.
* The biggest supported component is Hour.
*/
private const REGEXP_ISO8601 = '@^
PT
(?=(?:\d+H|\d+M|\d+S)) # look ahead to avoid PT without argument
(?:(?<hour>\d+)H)?
(?:(?<minute>\d+)M)?
(?:(?<second>\d+)S)?
$@x';
/**
* Regular expression matching any ISO-8601 duration, including the
* date components that Duration does not support.
*/
private const REGEXP_ISO8601_ANY = '@^
P
(?:(?<year>\d+)Y)?
(?:(?<month>\d+)M)?
(?:(?<week>\d+)W)?
(?:(?<day>\d+)D)?
(?:T(?:\d+H)?(?:\d+M)?(?:\d+S)?)?
$@x';
private function __construct(
public readonly int $seconds,
public readonly int $nanoseconds,
public readonly bool $negative,
) {}
/**
* Create a duration representing $seconds seconds and $nanoseconds nanoseconds. Neither parameter
* may be negative. $nanoseconds must be less than 1_000_000_000 (the number of nanoseconds in a
* second).
*
* This constructor creates a Duration from its “atomic” components.
*
* @throws TimeException
*/
public static function fromSeconds(int $seconds, int $nanoseconds = 0): self
{
if ($seconds < 0) {
throw new \ValueError(__METHOD__.'(): Argument #1 ($seconds) must be greater than or equal to 0');
}
if ($nanoseconds < 0) {
throw new \ValueError(__METHOD__.'(): Argument #2 ($nanoseconds) must be greater than or equal to 0');
}
if ($nanoseconds >= self::NANOS_PER_SECOND) {
throw new \ValueError(__METHOD__.'(): Argument #2 ($nanoseconds) must be less than 1_000_000_000');
}
return self::create($seconds, $nanoseconds, false);
}
/**
* Create a duration representing $nanoseconds nanoseconds. $nanoseconds must not be negative.
*
* @throws TimeException
*/
public static function fromNanoseconds(int $nanoseconds): self
{
if ($nanoseconds < 0) {
throw new \ValueError(__METHOD__.'(): Argument #1 ($nanoseconds) must be greater than or equal to 0');
}
return self::create(intdiv($nanoseconds, self::NANOS_PER_SECOND), $nanoseconds % self::NANOS_PER_SECOND, false);
}
/**
* Create a duration representing $microseconds microseconds. $microseconds must not be negative.
*
* @throws TimeException
*/
public static function fromMicroseconds(int $microseconds): self
{
if ($microseconds < 0) {
throw new \ValueError(__METHOD__.'(): Argument #1 ($microseconds) must be greater than or equal to 0');
}
return self::create(intdiv($microseconds, 1_000_000), ($microseconds % 1_000_000) * 1_000, false);
}
/**
* Create a duration representing $milliseconds milliseconds. $milliseconds must not be negative.
*
* @throws TimeException
*/
public static function fromMilliseconds(int $milliseconds): self
{
if ($milliseconds < 0) {
throw new \ValueError(__METHOD__.'(): Argument #1 ($milliseconds) must be greater than or equal to 0');
}
return self::create(intdiv($milliseconds, 1_000), ($milliseconds % 1_000) * 1_000_000, false);
}
/**
* Create a duration representing $minutes minutes. $minutes must not be negative.
*
* @throws TimeException
*/
public static function fromMinutes(int $minutes): self
{
if ($minutes < 0) {
throw new \ValueError(__METHOD__.'(): Argument #1 ($minutes) must be greater than or equal to 0');
}
if ($minutes > intdiv(self::MAX_SECONDS, 60)) {
throw new TimeException(self::RANGE_ERROR);
}
return self::create($minutes * 60, 0, false);
}
/**
* Create a duration representing $hours hours. $hours must not be negative.
*
* @throws TimeException
*/
public static function fromHours(int $hours): self
{
if ($hours < 0) {
throw new \ValueError(__METHOD__.'(): Argument #1 ($hours) must be greater than or equal to 0');
}
if ($hours > intdiv(self::MAX_SECONDS, 3_600)) {
throw new TimeException(self::RANGE_ERROR);
}
return self::create($hours * 3_600, 0, false);
}
/**
* Parse a ISO-8601 period. ISO-8601 periods with a date component will be rejected.
* The biggest allowed component is H.
*
* @throws TimeException
*/
public static function fromIso8601DurationString(string $specification): self
{
if (!preg_match(self::REGEXP_ISO8601, $specification, $parts)) {
throw new TimeException(self::iso8601Error($specification));
}
$hours = (int) ($parts['hour'] ?? 0);
$minutes = (int) ($parts['minute'] ?? 0);
$seconds = (int) ($parts['second'] ?? 0);
// each step is checked against the maximum so that no intermediate value can overflow
if ($hours > intdiv(self::MAX_SECONDS, 3_600)) {
throw new TimeException(self::RANGE_ERROR);
}
$total = $hours * 3_600;
if ($minutes > intdiv(self::MAX_SECONDS - $total, 60)) {
throw new TimeException(self::RANGE_ERROR);
}
$total += $minutes * 60;
if ($seconds > self::MAX_SECONDS - $total) {
throw new TimeException(self::RANGE_ERROR);
}
return self::create($total + $seconds, 0, false);
}
/**
* Negates the duration.
*
* @return self -$this
*/
public function negate(): self
{
if (0 === $this->seconds && 0 === $this->nanoseconds) {
return $this;
}
return new self($this->seconds, $this->nanoseconds, !$this->negative);
}
/**
* Returns the absolute value of the duration.
*
* @return self abs($this)
*/
public function absolute(): self
{
if (!$this->negative) {
return $this;
}
return new self($this->seconds, $this->nanoseconds, false);
}
/**
* Add the given duration to the duration.
*
* @return self $this + $duration
*
* @throws TimeException
*/
public function add(self $duration): self
{
$seconds = $this->negative ? -$this->seconds : $this->seconds;
$nanoseconds = $this->negative ? -$this->nanoseconds : $this->nanoseconds;
$addSeconds = $duration->negative ? -$duration->seconds : $duration->seconds;
$addNanoseconds = $duration->negative ? -$duration->nanoseconds : $duration->nanoseconds;
if ($this->negative === $duration->negative && $this->seconds > self::MAX_SECONDS - $duration->seconds) {
throw new TimeException(self::RANGE_ERROR);
}
$seconds += $addSeconds;
$nanoseconds += $addNanoseconds;
if ($nanoseconds >= self::NANOS_PER_SECOND || $nanoseconds <= -self::NANOS_PER_SECOND) {
$carry = intdiv($nanoseconds, self::NANOS_PER_SECOND);
$nanoseconds %= self::NANOS_PER_SECOND;
if ($carry > 0 ? $seconds > self::MAX_SECONDS - $carry : $seconds < -self::MAX_SECONDS - $carry) {
throw new TimeException(self::RANGE_ERROR);
}
$seconds += $carry;
}
if (0 < $seconds && 0 > $nanoseconds) {
--$seconds;
$nanoseconds += self::NANOS_PER_SECOND;
}
if (0 > $seconds && 0 < $nanoseconds) {
++$seconds;
$nanoseconds -= self::NANOS_PER_SECOND;
}
return self::create(abs($seconds), abs($nanoseconds), 0 > $seconds || 0 > $nanoseconds);
}
/**
* Subtract the given duration from the duration.
*
* @return self $this - $duration
*
* @throws TimeException
*/
public function sub(self $duration): self
{
return $this->add($duration->negate());
}
/**
* Multiply the length of the duration by the given factor. $factor must not be negative.
*
* @return self $this * $factor
*
* @throws TimeException
*/
public function multiplyBy(int $factor): self
{
if ($factor < 0) {
throw new \ValueError(__METHOD__.'(): Argument #1 ($factor) must be greater than or equal to 0');
}
if (0 === $factor) {
return new self(0, 0, false);
}
if (1 === $factor) {
return $this;
}
// double-and-add: multiplying the nanoseconds directly would overflow
$seconds = 0;
$nanoseconds = 0;
$addSeconds = $this->seconds;
$addNanoseconds = $this->nanoseconds;
while (true) {
if (1 & $factor) {
[$seconds, $nanoseconds] = self::addMagnitudes($seconds, $nanoseconds, $addSeconds, $addNanoseconds);
}
if (0 === $factor >>= 1) {
break;
}
// the remaining factor is not zero, so an out of range double is out of range for the result too
[$addSeconds, $addNanoseconds] = self::addMagnitudes($addSeconds, $addNanoseconds, $addSeconds, $addNanoseconds);
}
return self::create($seconds, $nanoseconds, $this->negative);
}
/**
* Divide the length of the duration by the given divisor. $divisor must be positive.
*
* Fractional nanoseconds will be truncated.
*
* @return self $this / $divisor
*
* @throws TimeException
*/
public function divideBy(int $divisor): self
{
if ($divisor < 0) {
throw new \ValueError(__METHOD__.'(): Argument #1 ($divisor) must be greater than or equal to 0');
}
if (0 === $divisor) {
throw new \DivisionByZeroError('Division by zero');
}
if (1 === $divisor) {
return $this;
}
$remainder = $this->seconds % $divisor;
$nanoseconds = $remainder > intdiv(\PHP_INT_MAX - $this->nanoseconds, self::NANOS_PER_SECOND)
? self::divideNanoseconds($remainder, $this->nanoseconds, $divisor)
: intdiv($this->nanoseconds + $remainder * self::NANOS_PER_SECOND, $divisor);
return self::create(intdiv($this->seconds, $divisor), $nanoseconds, $this->negative);
}
/**
* Returns -1, 0, 1 if $a is less than, equal to, or greater than $b respectively.
*/
public static function compare(self $a, self $b): int
{
if ($a->negative !== $b->negative) {
return $a->negative ? -1 : 1;
}
$comparison = $a->seconds <=> $b->seconds ?: $a->nanoseconds <=> $b->nanoseconds;
return $a->negative ? -$comparison : $comparison;
}
/**
* The native class is declared readonly, which forbids dynamic properties.
*/
public function __set(string $name, mixed $value): void
{
throw new \Error(\sprintf('Cannot create dynamic property %s::$%s', self::class, $name));
}
/**
* Adds two non-negative (seconds, nanoseconds) pairs.
*
* @return array{0: int, 1: int}
*
* @throws TimeException when the sum is out of the representable range
*/
private static function addMagnitudes(int $seconds, int $nanoseconds, int $addSeconds, int $addNanoseconds): array
{
if ($seconds > self::MAX_SECONDS - $addSeconds) {
throw new TimeException(self::RANGE_ERROR);
}
$seconds += $addSeconds;
// the sum of two nanoseconds components is always below 2**31
$nanoseconds += $addNanoseconds;
if ($nanoseconds >= self::NANOS_PER_SECOND) {
if (self::MAX_SECONDS === $seconds) {
throw new TimeException(self::RANGE_ERROR);
}
++$seconds;
$nanoseconds -= self::NANOS_PER_SECOND;
}
return [$seconds, $nanoseconds];
}
/**
* Returns intdiv($remainder * 1_000_000_000 + $nanoseconds, $divisor) for
* a $remainder that is lower than $divisor.
*
* The dividend does not fit in an integer on 32 bit platforms, so the
* division is done one decimal digit at a time. Every intermediate value
* stays below 2**53, where floats represent integers exactly.
*/
private static function divideNanoseconds(int $remainder, int $nanoseconds, int $divisor): int
{
$quotient = 0;
$rest = (float) $remainder;
foreach (str_split(str_pad((string) $nanoseconds, 9, '0', \STR_PAD_LEFT)) as $digit) {
$rest = $rest * 10 + (int) $digit;
$digit = (int) ($rest / $divisor);
$rest -= $digit * $divisor;
// the float division may be off by one in either direction
while (0 > $rest) {
--$digit;
$rest += $divisor;
}
while ($rest >= $divisor) {
++$digit;
$rest -= $divisor;
}
$quotient = $quotient * 10 + $digit;
}
return $quotient;
}
/**
* @throws TimeException when the duration is out of the representable range
*/
private static function create(int $seconds, int $nanoseconds, bool $negative): self
{
if ($seconds > self::MAX_SECONDS) {
throw new TimeException(self::RANGE_ERROR);
}
// a zero duration is never negative
return new self($seconds, $nanoseconds, $negative && (0 !== $seconds || 0 !== $nanoseconds));
}
/**
* Mirrors the error reporting of timelib_duration_create_from_iso8601string().
*/
private static function iso8601Error(string $specification): string
{
if (preg_match(self::REGEXP_ISO8601_ANY, $specification, $parts)) {
if ((int) ($parts['year'] ?? 0) || (int) ($parts['month'] ?? 0) || (int) ($parts['week'] ?? 0) || (int) ($parts['day'] ?? 0)) {
return 'The ISO 8601 duration string may only contain the time (T) aspect';
}
return 'The ISO 8601 duration string could not be parsed';
}
if (str_contains($specification, '/')) {
return 'The ISO 8601 duration string may only contain the period (P) aspect';
}
if (!str_contains($specification, 'P') && false !== @strtotime($specification)) {
return 'The ISO 8601 duration string is missing the period (P) aspect';
}
return 'The ISO 8601 duration string could not be parsed';
}
}
}
@@ -0,0 +1,18 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Time;
if (\PHP_VERSION_ID < 80600) {
class TimeException extends \Exception
{
}
}
@@ -0,0 +1,36 @@
{
"name": "symfony/polyfill-time",
"type": "library",
"description": "Symfony polyfill for the Time API added to PHP 8.6",
"keywords": ["polyfill", "shim", "compatibility", "portable", "time", "duration"],
"homepage": "https://symfony.com",
"license": "MIT",
"authors": [
{
"name": "Ignace Philippe Nyamagana Butera",
"email": "nyamsprod@gmail.com"
},
{
"name": "Tim Düsterhus",
"email": "tim@tideways-gmbh.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"require": {
"php": ">=8.1"
},
"autoload": {
"psr-4": { "Symfony\\Polyfill\\Time\\": "" },
"classmap": [ "Resources/stubs" ]
},
"minimum-stability": "dev",
"extra": {
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
}
}
Loaded 100 of 1297 files, more files were not shown because too many files have changed in this diff. Show more