Skip to content

Commit

Permalink
Merge pull request #2644 from briannesbitt/feature/php-8-2
Browse files Browse the repository at this point in the history
Add PHP 8.2 support
  • Loading branch information
kylekatarnls authored Jul 29, 2022
2 parents 00a259a + 53632e4 commit e9fce1e
Show file tree
Hide file tree
Showing 12 changed files with 326 additions and 59 deletions.
16 changes: 11 additions & 5 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ jobs:
- php: 8.1
setup: lowest

- php: 8.2
- php: 8.2
setup: lowest

- php: 8.0
laravel: true
reference: 9.x
Expand Down Expand Up @@ -123,8 +127,8 @@ jobs:
if [[ "${{ matrix.laravel }}" != 'true' ]]; then
composer remove --no-update kylekatarnls/multi-tester --no-interaction --dev;
fi;
${{ matrix.php >= 8.1 && 'composer require --no-update phpunit/phpunit:^9.5.20 --no-interaction;' || '' }}
composer update --prefer-dist --no-progress --prefer-${{ matrix.setup || 'stable' }} ${{ matrix.classmap-authoritative && '--classmap-authoritative' || '' }};
${{ matrix.php >= 8.1 && 'composer require --no-update phpunit/phpunit:^9.5.20 --no-interaction --dev;' || '' }}
composer update --prefer-dist --no-progress --prefer-${{ matrix.setup || 'stable' }} ${{ matrix.classmap-authoritative && '--classmap-authoritative' || '' }}${{ matrix.php >= 8.2 && ' --ignore-platform-reqs' || '' }};
- name: Run test suite
run: |
Expand Down Expand Up @@ -196,7 +200,9 @@ jobs:
- php: 8.1
setup: lowest

- php: 7.3.0
- php: 8.2
- php: 8.2
setup: lowest

name: PHP ${{ matrix.php }} - ${{ matrix.setup || 'stable' }} - windows

Expand Down Expand Up @@ -230,8 +236,8 @@ jobs:
max_attempts: 3
command: |
composer remove --no-update kylekatarnls/multi-tester phpmd/phpmd friendsofphp/php-cs-fixer --no-interaction --dev;
${{ matrix.php >= 8.1 && 'composer require --no-update phpunit/phpunit:^9.5.20 --no-interaction;' || '' }}
composer update --prefer-dist --no-progress --prefer-${{ matrix.setup || 'stable' }};
${{ matrix.php >= 8.1 && 'composer require --no-update phpunit/phpunit:^9.5.20 --no-interaction --dev;' || '' }}
composer update --prefer-dist --no-progress --prefer-${{ matrix.setup || 'stable' }}${{ matrix.php >= 8.2 && ' --ignore-platform-reqs' || '' }};
- name: Run test suite
run: |
Expand Down
26 changes: 24 additions & 2 deletions src/Carbon/CarbonPeriod.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
use DateInterval;
use DatePeriod;
use DateTime;
use DateTimeImmutable;
use DateTimeInterface;
use DateTimeZone;
use InvalidArgumentException;
Expand Down Expand Up @@ -1507,9 +1508,9 @@ public function cast(string $className)
if (!method_exists($className, 'instance')) {
if (is_a($className, DatePeriod::class, true)) {
return new $className(
$this->getStartDate(),
$this->rawDate($this->getStartDate()),
$this->getDateInterval(),
$this->getEndDate() ? $this->getIncludedEndDate() : $this->getRecurrences(),
$this->getEndDate() ? $this->rawDate($this->getIncludedEndDate()) : $this->getRecurrences(),
$this->isStartExcluded() ? DatePeriod::EXCLUDE_START_DATE : 0
);
}
Expand Down Expand Up @@ -2618,4 +2619,25 @@ private function isInfiniteDate($date): bool
{
return $date instanceof CarbonInterface && ($date->isEndOfTime() || $date->isStartOfTime());
}

private function rawDate($date): ?DateTimeInterface
{
if ($date === false || $date === null) {
return null;
}

if ($date instanceof CarbonInterface) {
return $date->isMutable()
? $date->toDateTime()
: $date->toDateTimeImmutable();
}

if (\in_array(\get_class($date), [DateTime::class, DateTimeImmutable::class], true)) {
return $date;
}

$class = $date instanceof DateTime ? DateTime::class : DateTimeImmutable::class;

return new $class($date->format('Y-m-d H:i:s.u'), $date->getTimezone());
}
}
12 changes: 12 additions & 0 deletions src/Carbon/CarbonTimeZone.php
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,18 @@ public function __toString()
return $this->getName();
}

/**
* Return the type number:
*
* Type 1; A UTC offset, such as -0300
* Type 2; A timezone abbreviation, such as GMT
* Type 3: A timezone identifier, such as Europe/London
*/
public function getType(): int
{
return preg_match('/"timezone_type";i:(\d)/', serialize($this), $match) ? (int) $match[1] : 3;
}

/**
* Create a CarbonTimeZone from mixed input.
*
Expand Down
76 changes: 70 additions & 6 deletions src/Carbon/Traits/Serialization.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,39 @@ public function __sleep()
return $properties;
}

public function __serialize(): array
{
if (isset($this->timezone_type)) {
return [
'date' => $this->date ?? null,
'timezone_type' => $this->timezone_type,
'timezone' => $this->timezone ?? null,
];
}

$timezone = $this->getTimezone();
$export = [
'date' => $this->format('Y-m-d H:i:s.u'),
'timezone_type' => $timezone->getType(),
'timezone' => $timezone->getName(),
];

// @codeCoverageIgnoreStart
if (\extension_loaded('msgpack') && isset($this->constructedObjectId)) {
$export['dumpDateProperties'] = [
'date' => $this->format('Y-m-d H:i:s.u'),
'timezone' => serialize($this->timezone ?? null),
];
}
// @codeCoverageIgnoreEnd

if ($this->localTranslator ?? null) {
$export['dumpLocale'] = $this->locale ?? null;
}

return $export;
}

/**
* Set locale if specified on unserialize() called.
*
Expand All @@ -147,9 +180,13 @@ public function __wakeup()
try {
parent::__wakeup();
} catch (Throwable $exception) {
// FatalError occurs when calling msgpack_unpack() in PHP 7.4 or later.
['date' => $date, 'timezone' => $timezone] = $this->dumpDateProperties;
parent::__construct($date, unserialize($timezone));
try {
// FatalError occurs when calling msgpack_unpack() in PHP 7.4 or later.
['date' => $date, 'timezone' => $timezone] = $this->dumpDateProperties;
parent::__construct($date, unserialize($timezone));
} catch (Throwable $ignoredException) {
throw $exception;
}
}
// @codeCoverageIgnoreEnd
}
Expand All @@ -164,6 +201,31 @@ public function __wakeup()
$this->cleanupDumpProperties();
}

public function __unserialize(array $data): void
{
// @codeCoverageIgnoreStart
try {
$this->__construct($data['date'] ?? null, $data['timezone'] ?? null);
} catch (Throwable $exception) {
if (!isset($data['dumpDateProperties']['date'], $data['dumpDateProperties']['timezone'])) {
throw $exception;
}

try {
// FatalError occurs when calling msgpack_unpack() in PHP 7.4 or later.
['date' => $date, 'timezone' => $timezone] = $data['dumpDateProperties'];
$this->__construct($date, unserialize($timezone));
} catch (Throwable $ignoredException) {
throw $exception;
}
}
// @codeCoverageIgnoreEnd

if (isset($data['dumpLocale'])) {
$this->locale($data['dumpLocale']);
}
}

/**
* Prepare the object for JSON serialization.
*
Expand Down Expand Up @@ -207,9 +269,11 @@ public static function serializeUsing($callback)
*/
public function cleanupDumpProperties()
{
foreach ($this->dumpProperties as $property) {
if (isset($this->$property)) {
unset($this->$property);
if (PHP_VERSION < 8.2) {
foreach ($this->dumpProperties as $property) {
if (isset($this->$property)) {
unset($this->$property);
}
}
}

Expand Down
51 changes: 32 additions & 19 deletions tests/Carbon/SerializationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use ReflectionObject;
use ReflectionProperty;
use Tests\AbstractTestCase;
use Throwable;

class SerializationTest extends AbstractTestCase
{
Expand All @@ -35,17 +36,23 @@ protected function setUp(): void

$this->serialized = \extension_loaded('msgpack')
? [
"O:13:\"Carbon\Carbon\":4:{s:4:\"date\";s:26:\"2016-02-01 13:20:25.000000\";s:13:\"timezone_type\";i:3;s:8:\"timezone\";s:15:\"America/Toronto\";s:21:\"\0*\0dumpDateProperties\";a:2:{s:4:\"date\";s:26:\"2016-02-01 13:20:25.000000\";s:8:\"timezone\";s:96:\"O:21:\"Carbon\CarbonTimeZone\":2:{s:13:\"timezone_type\";i:3;s:8:\"timezone\";s:15:\"America/Toronto\";}\";}}",
"O:13:\"Carbon\Carbon\":4:{s:4:\"date\";s:26:\"2016-02-01 13:20:25.000000\";s:13:\"timezone_type\";i:3;s:8:\"timezone\";s:15:\"America/Toronto\";s:21:\"\0*\0dumpDateProperties\";a:2:{s:4:\"date\";s:26:\"2016-02-01 13:20:25.000000\";s:8:\"timezone\";s:23:\"s:15:\"America/Toronto\";\";}}",
"O:13:\"Carbon\Carbon\":4:{s:4:\"date\";s:26:\"2016-02-01 13:20:25.000000\";s:13:\"timezone_type\";i:3;s:8:\"timezone\";s:15:\"America/Toronto\";s:18:\"dumpDateProperties\";a:2:{s:4:\"date\";s:26:\"2016-02-01 13:20:25.000000\";s:8:\"timezone\";s:96:\"O:21:\"Carbon\CarbonTimeZone\":2:{s:13:\"timezone_type\";i:3;s:8:\"timezone\";s:15:\"America/Toronto\";}\";}}",
"O:13:\"Carbon\Carbon\":4:{s:4:\"date\";s:26:\"2016-02-01 13:20:25.000000\";s:13:\"timezone_type\";i:3;s:8:\"timezone\";s:15:\"America/Toronto\";s:18:\"dumpDateProperties\";a:2:{s:4:\"date\";s:26:\"2016-02-01 13:20:25.000000\";s:8:\"timezone\";s:23:\"s:15:\"America/Toronto\";\";}}",
]
: ['O:13:"Carbon\Carbon":3:{s:4:"date";s:26:"2016-02-01 13:20:25.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:15:"America/Toronto";}'];
}

protected function cleanSerialization(string $serialization): string
{
return preg_replace('/s:\d+:\"[^"]*dumpDateProperties\"/', 's:18:"dumpDateProperties"', $serialization);
}

public function testSerialize()
{
$dt = Carbon::create(2016, 2, 1, 13, 20, 25);
$this->assertContains($dt->serialize(), $this->serialized);
$this->assertContains(serialize($dt), $this->serialized);
$message = "not in:\n".implode("\n", $this->serialized);
$this->assertContains($this->cleanSerialization($dt->serialize()), $this->serialized, $message);
$this->assertContains($this->cleanSerialization(serialize($dt)), $this->serialized, $message);
}

public function testFromUnserialized()
Expand Down Expand Up @@ -91,25 +98,31 @@ public function testFromUnserializedWithInvalidValue($value)

public function testDateSerializationReflectionCompatibility()
{
$d = (new ReflectionClass(DateTime::class))->newInstanceWithoutConstructor();

$d->date = '1990-01-17 10:28:07';
$d->timezone_type = 3;
$d->timezone = 'US/Pacific';

$x = unserialize(serialize($d));
try {
$reflection = (new ReflectionClass(DateTime::class))->newInstanceWithoutConstructor();

@$reflection->date = '1990-01-17 10:28:07';
@$reflection->timezone_type = 3;
@$reflection->timezone = 'US/Pacific';

$date = unserialize(serialize($reflection));
} catch (Throwable $exception) {
$this->markTestSkipped(
"It fails on DateTime so Carbon can't support it, error was:\n".$exception->getMessage()
);
}

$this->assertSame('1990-01-17 10:28:07', $x->format('Y-m-d h:i:s'));
$this->assertSame('1990-01-17 10:28:07', $date->format('Y-m-d h:i:s'));

$d = (new ReflectionClass(Carbon::class))->newInstanceWithoutConstructor();
$reflection = (new ReflectionClass(Carbon::class))->newInstanceWithoutConstructor();

$d->date = '1990-01-17 10:28:07';
$d->timezone_type = 3;
$d->timezone = 'US/Pacific';
@$reflection->date = '1990-01-17 10:28:07';
@$reflection->timezone_type = 3;
@$reflection->timezone = 'US/Pacific';

$x = unserialize(serialize($d));
$date = unserialize(serialize($reflection));

$this->assertSame('1990-01-17 10:28:07', $x->format('Y-m-d h:i:s'));
$this->assertSame('1990-01-17 10:28:07', $date->format('Y-m-d h:i:s'));

$reflection = new ReflectionObject(Carbon::parse('1990-01-17 10:28:07'));
$target = (new ReflectionClass(Carbon::class))->newInstanceWithoutConstructor();
Expand All @@ -128,7 +141,7 @@ public function testDateSerializationReflectionCompatibility()
return;
}

$target->$key = $value;
@$target->$key = $value;
};

$setValue('date', '1990-01-17 10:28:07');
Expand Down
6 changes: 3 additions & 3 deletions tests/Carbon/SettersTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ public function testTimeZoneOfUnserialized()

$this->assertSame('America/Vancouver', $new->getTimezone()->getName());

$new->timezone = 'UTC';
@$new->timezone = 'UTC';

$this->assertSame('UTC', $new->getTimezone()->getName());

Expand All @@ -303,13 +303,13 @@ public function testTimeZoneOfUnserialized()

$this->assertSame('America/Vancouver', $date->getTimezone()->getName());

$date->timezone = 'UTC';
@$date->timezone = 'UTC';

$this->assertSame('UTC', $date->getTimezone()->getName());

$this->assertSame('America/Vancouver', $new->getTimezone()->getName());

$new->timezone = 'UTC';
@$new->timezone = 'UTC';

$this->assertSame('UTC', $new->getTimezone()->getName());

Expand Down
2 changes: 1 addition & 1 deletion tests/Carbon/StrictModeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public function testSetAndGetWithoutStrictMode()
Carbon::useStrictMode(false);
/** @var mixed $date */
$date = Carbon::now();
$date->foobar = 'biz';
@$date->foobar = 'biz';
$this->assertSame('biz', $date->foobar);
}

Expand Down
Loading

0 comments on commit e9fce1e

Please sign in to comment.