diff --git a/phpstan.neon b/phpstan.neon index 82acf43dc3..00241ceda0 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -3,6 +3,8 @@ parameters: - lazy/Carbon/MessageFormatter/MessageFormatterMapperStrongType.php - lazy/Carbon/PHPStan/MacroStrongType.php - lazy/Carbon/TranslatorStrongType.php + - tests/Fixtures/DateMalformedIntervalStringException.php + - tests/Fixtures/DateMalformedStringException.php - vendor/autoload.php level: 3 paths: diff --git a/readme.md b/readme.md index a178c3c702..16fec2477b 100644 --- a/readme.md +++ b/readme.md @@ -2,7 +2,7 @@ [![Latest Stable Version](https://img.shields.io/packagist/v/nesbot/carbon.svg?style=flat-square)](https://packagist.org/packages/nesbot/carbon) [![Total Downloads](https://img.shields.io/packagist/dt/nesbot/carbon.svg?style=flat-square)](https://packagist.org/packages/nesbot/carbon) -[![GitHub Actions](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fbriannesbitt%2FCarbon%2Fbadge&style=flat-square&label=Build&logo=none)](https://actions-badge.atrox.dev/briannesbitt/Carbon/goto) +[![GitHub Actions](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fbriannesbitt%2FCarbon%2Fbadge&style=flat-square&label=Build&logo=none)](https://github.com/briannesbitt/Carbon/actions) [![codecov.io](https://img.shields.io/codecov/c/github/briannesbitt/Carbon.svg?style=flat-square)](https://codecov.io/github/briannesbitt/Carbon?branch=master) [![Tidelift](https://tidelift.com/badges/github/briannesbitt/Carbon)](https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme) diff --git a/src/Carbon/CarbonInterval.php b/src/Carbon/CarbonInterval.php index 796f9cdcd0..9eb49c028f 100644 --- a/src/Carbon/CarbonInterval.php +++ b/src/Carbon/CarbonInterval.php @@ -28,6 +28,7 @@ use Carbon\Traits\ToStringFormat; use Closure; use DateInterval; +use DateMalformedIntervalStringException; use DateTimeInterface; use DateTimeZone; use Exception; @@ -1004,8 +1005,14 @@ protected static function makeFromString(string $interval) return static::fromString($interval); } - /** @var static $interval */ - $interval = static::createFromDateString($interval); + // @codeCoverageIgnoreStart + try { + /** @var static $interval */ + $interval = static::createFromDateString($interval); + } catch (DateMalformedIntervalStringException $e) { + return null; + } + // @codeCoverageIgnoreEnd return !$interval || $interval->isEmpty() ? null : $interval; } diff --git a/src/Carbon/Traits/Creator.php b/src/Carbon/Traits/Creator.php index 2cd604a55c..184f81080a 100644 --- a/src/Carbon/Traits/Creator.php +++ b/src/Carbon/Traits/Creator.php @@ -19,6 +19,7 @@ use Carbon\Exceptions\OutOfRangeException; use Carbon\Translator; use Closure; +use DateMalformedStringException; use DateTimeInterface; use DateTimeZone; use Exception; @@ -184,7 +185,13 @@ public static function rawParse($time = null, $tz = null) try { return new static($time, $tz); } catch (Exception $exception) { - $date = @static::now($tz)->change($time); + // @codeCoverageIgnoreStart + try { + $date = @static::now($tz)->change($time); + } catch (DateMalformedStringException $ignoredException) { + $date = null; + } + // @codeCoverageIgnoreEnd if (!$date) { throw new InvalidFormatException("Could not parse '$time': ".$exception->getMessage(), 0, $exception); diff --git a/src/Carbon/Traits/Units.php b/src/Carbon/Traits/Units.php index f4f797d9b3..7aadd65bde 100644 --- a/src/Carbon/Traits/Units.php +++ b/src/Carbon/Traits/Units.php @@ -17,6 +17,7 @@ use Carbon\Exceptions\UnitException; use Closure; use DateInterval; +use DateMalformedStringException; use ReturnTypeWillChange; /** @@ -304,12 +305,17 @@ public function addUnit($unit, $value = 1, $overflow = null) $unit = 'second'; $value = $second; } - $date = $date->modify("$value $unit"); - if (isset($timeString)) { - $date = $date->setTimeFromTimeString($timeString); - } elseif (isset($canOverflow, $day) && $canOverflow && $day !== $date->day) { - $date = $date->modify('last day of previous month'); + try { + $date = $date->modify("$value $unit"); + + if (isset($timeString)) { + $date = $date->setTimeFromTimeString($timeString); + } elseif (isset($canOverflow, $day) && $canOverflow && $day !== $date->day) { + $date = $date->modify('last day of previous month'); + } + } catch (DateMalformedStringException $ignoredException) { // @codeCoverageIgnore + $date = null; // @codeCoverageIgnore } if (!$date) { diff --git a/tests/Carbon/ModifyTest.php b/tests/Carbon/ModifyTest.php index 1e51668880..5fae404c5b 100644 --- a/tests/Carbon/ModifyTest.php +++ b/tests/Carbon/ModifyTest.php @@ -14,6 +14,8 @@ namespace Tests\Carbon; use Carbon\Carbon; +use Closure; +use DateMalformedStringException; use InvalidArgumentException; use Tests\AbstractTestCase; @@ -256,9 +258,34 @@ public function testNextAndPrevious() public function testInvalidModifier(): void { - $this->assertFalse(@Carbon::parse('2000-01-25')->change('invalid')); - $this->assertFalse(@Carbon::now()->next('invalid')); - $this->assertFalse(@Carbon::now()->previous('invalid')); + $this->checkInvalid('invalid', static function () { + return @Carbon::parse('2000-01-25')->change('invalid'); + }); + $this->checkInvalid('next invalid', static function () { + return @Carbon::now()->next('invalid'); + }); + $this->checkInvalid('last invalid', static function () { + return @Carbon::now()->previous('invalid'); + }); + } + + private function checkInvalid(string $message, Closure $callback): void + { + if (PHP_VERSION < 8.3) { + $this->assertFalse($callback()); + + return; + } + + try { + $callback(); + } catch (DateMalformedStringException $exception) { + $this->assertStringContainsString("Failed to parse time string ($message)", $exception->getMessage()); + + return; + } + + $this->fail('This should throw a DateMalformedStringException in PHP 8.3'); } public function testImplicitCast(): void diff --git a/tests/Fixtures/DateMalformedIntervalStringException.php b/tests/Fixtures/DateMalformedIntervalStringException.php new file mode 100644 index 0000000000..ad485b007c --- /dev/null +++ b/tests/Fixtures/DateMalformedIntervalStringException.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +class DateMalformedIntervalStringException extends Exception +{ +} diff --git a/tests/Fixtures/DateMalformedStringException.php b/tests/Fixtures/DateMalformedStringException.php new file mode 100644 index 0000000000..9193c52c98 --- /dev/null +++ b/tests/Fixtures/DateMalformedStringException.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +class DateMalformedStringException extends Exception +{ +}