Add #[NoDiscard] to CarbonImmutable modify(), next(), previous() and __call()
On PHP 8.5, DateTimeImmutable::modify() warns when its result is discarded, but CarbonImmutable overrides it and lost the attribute. Override modify(), next(), previous() and __call() in CarbonImmutable with #[NoDiscard] so a discarded result warns again. The attribute is ignored on older PHP versions, and mutable Carbon is unchanged. Existing immutable tests that discarded results now keep them; in IsTest::testIsSameMonth this was a real slip, the loop never moved $dt. Fixes #3377
This commit is contained in:
1 parent
8dc8cfa977
commit
b3fc145df6
9 files changed
+143
-11
No files matched your search
@@ -13,9 +13,15 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Carbon;
|
namespace Carbon;
|
||||||
|
|
||||||
|
use BadMethodCallException;
|
||||||
|
use Carbon\Exceptions\UnknownMethodException;
|
||||||
use Carbon\Traits\Date;
|
use Carbon\Traits\Date;
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
use DateTimeInterface;
|
use DateTimeInterface;
|
||||||
|
use NoDiscard;
|
||||||
|
use ReflectionException;
|
||||||
|
use ReturnTypeWillChange;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple API extension for DateTimeImmutable.
|
* A simple API extension for DateTimeImmutable.
|
||||||
@@ -839,6 +845,10 @@ class CarbonImmutable extends DateTimeImmutable implements CarbonInterface
|
|||||||
{
|
{
|
||||||
use Date {
|
use Date {
|
||||||
__clone as dateTraitClone;
|
__clone as dateTraitClone;
|
||||||
|
__call as dateTraitCall;
|
||||||
|
modify as dateTraitModify;
|
||||||
|
next as dateTraitNext;
|
||||||
|
previous as dateTraitPrevious;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __clone(): void
|
public function __clone(): void
|
||||||
@@ -848,6 +858,66 @@ class CarbonImmutable extends DateTimeImmutable implements CarbonInterface
|
|||||||
$this->startOfTime = false;
|
$this->startOfTime = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dynamically handle calls to the class.
|
||||||
|
*
|
||||||
|
* @param string $method magic method name called
|
||||||
|
* @param array $parameters parameters list
|
||||||
|
*
|
||||||
|
* @throws UnknownMethodException|BadMethodCallException|ReflectionException|Throwable
|
||||||
|
*/
|
||||||
|
#[NoDiscard]
|
||||||
|
public function __call(string $method, array $parameters): mixed
|
||||||
|
{
|
||||||
|
return $this->dateTraitCall($method, $parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls \DateTimeImmutable::modify.
|
||||||
|
*
|
||||||
|
* @see https://php.net/manual/en/datetimeimmutable.modify.php
|
||||||
|
*
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
#[NoDiscard]
|
||||||
|
#[ReturnTypeWillChange]
|
||||||
|
public function modify($modify)
|
||||||
|
{
|
||||||
|
return $this->dateTraitModify($modify);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modify to the next occurrence of a given modifier such as a day of
|
||||||
|
* the week. If no modifier is provided, modify to the next occurrence
|
||||||
|
* of the current day of the week. Use the supplied constants
|
||||||
|
* to indicate the desired dayOfWeek, ex. static::MONDAY.
|
||||||
|
*
|
||||||
|
* @param string|int|null $modifier
|
||||||
|
*
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
#[NoDiscard]
|
||||||
|
public function next($modifier = null)
|
||||||
|
{
|
||||||
|
return $this->dateTraitNext($modifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modify to the previous occurrence of a given modifier such as a day of
|
||||||
|
* the week. If no dayOfWeek is provided, modify to the previous occurrence
|
||||||
|
* of the current day of the week. Use the supplied constants
|
||||||
|
* to indicate the desired dayOfWeek, ex. static::MONDAY.
|
||||||
|
*
|
||||||
|
* @param string|int|null $modifier
|
||||||
|
*
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
#[NoDiscard]
|
||||||
|
public function previous($modifier = null)
|
||||||
|
{
|
||||||
|
return $this->dateTraitPrevious($modifier);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a very old date representing start of time.
|
* Create a very old date representing start of time.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1686,7 +1686,7 @@ class DiffTest extends AbstractTestCase
|
|||||||
{
|
{
|
||||||
$from = Carbon::parse('2021-03-27 20:00 Europe/Warsaw');
|
$from = Carbon::parse('2021-03-27 20:00 Europe/Warsaw');
|
||||||
$to = Carbon::parse('2021-03-27 20:00 Europe/London');
|
$to = Carbon::parse('2021-03-27 20:00 Europe/London');
|
||||||
$from->floatDiffInRealDays($to);
|
$diff = $from->floatDiffInRealDays($to);
|
||||||
|
|
||||||
$this->assertSame('2021-03-27 20:00:00 Europe/Warsaw', $from->format('Y-m-d H:i:s e'));
|
$this->assertSame('2021-03-27 20:00:00 Europe/Warsaw', $from->format('Y-m-d H:i:s e'));
|
||||||
$this->assertSame('2021-03-27 20:00:00 Europe/London', $to->format('Y-m-d H:i:s e'));
|
$this->assertSame('2021-03-27 20:00:00 Europe/London', $to->format('Y-m-d H:i:s e'));
|
||||||
@@ -1751,7 +1751,7 @@ class DiffTest extends AbstractTestCase
|
|||||||
{
|
{
|
||||||
$from = Carbon::parse('2021-03-27 20:00 Europe/Warsaw');
|
$from = Carbon::parse('2021-03-27 20:00 Europe/Warsaw');
|
||||||
$to = Carbon::parse('2021-03-27 20:00 Europe/London');
|
$to = Carbon::parse('2021-03-27 20:00 Europe/London');
|
||||||
$from->floatDiffInUtcDays($to);
|
$diff = $from->floatDiffInUtcDays($to);
|
||||||
|
|
||||||
$this->assertSame('2021-03-27 20:00:00 Europe/Warsaw', $from->format('Y-m-d H:i:s e'));
|
$this->assertSame('2021-03-27 20:00:00 Europe/Warsaw', $from->format('Y-m-d H:i:s e'));
|
||||||
$this->assertSame('2021-03-27 20:00:00 Europe/London', $to->format('Y-m-d H:i:s e'));
|
$this->assertSame('2021-03-27 20:00:00 Europe/London', $to->format('Y-m-d H:i:s e'));
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ class GenericMacroTest extends AbstractTestCaseWithOldNow
|
|||||||
$message = null;
|
$message = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$now->barBiz();
|
$result = $now->barBiz();
|
||||||
} catch (BadMethodCallException $exception) {
|
} catch (BadMethodCallException $exception) {
|
||||||
$message = $exception->getMessage();
|
$message = $exception->getMessage();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -413,9 +413,9 @@ class IsTest extends AbstractTestCase
|
|||||||
$this->assertTrue(Carbon::now()->isSameMonth(Carbon::now()));
|
$this->assertTrue(Carbon::now()->isSameMonth(Carbon::now()));
|
||||||
$dt = Carbon::now();
|
$dt = Carbon::now();
|
||||||
for ($year = 1990; $year < Carbon::now()->year; $year++) {
|
for ($year = 1990; $year < Carbon::now()->year; $year++) {
|
||||||
$dt->modify($year.$dt->format('-m-').'01');
|
$dt = $dt->modify($year.$dt->format('-m-').'01');
|
||||||
$this->assertTrue(Carbon::now()->isSameMonth($dt, false));
|
$this->assertTrue(Carbon::now()->isSameMonth($dt, false));
|
||||||
$dt->modify($year.$dt->format('-m-').'28');
|
$dt = $dt->modify($year.$dt->format('-m-').'28');
|
||||||
$this->assertTrue(Carbon::now()->isSameMonth($dt, false));
|
$this->assertTrue(Carbon::now()->isSameMonth($dt, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ class MacroTest extends AbstractTestCaseWithOldNow
|
|||||||
|
|
||||||
/** @var mixed $date */
|
/** @var mixed $date */
|
||||||
$date = Carbon::now();
|
$date = Carbon::now();
|
||||||
$date->nonExistingMacro();
|
$result = $date->nonExistingMacro();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testTraitMixin()
|
public function testTraitMixin()
|
||||||
|
|||||||
@@ -249,6 +249,6 @@ class ModifyTest extends AbstractTestCase
|
|||||||
'$anchorDay is not compatible with overflow',
|
'$anchorDay is not compatible with overflow',
|
||||||
));
|
));
|
||||||
|
|
||||||
Carbon::parse('2023-02-28')->addYearsWithOverflow(2, anchorDay: 29);
|
$date = Carbon::parse('2023-02-28')->addYearsWithOverflow(2, anchorDay: 29);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file is part of the Carbon package.
|
||||||
|
*
|
||||||
|
* (c) Brian Nesbitt <brian@nesbot.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tests\CarbonImmutable;
|
||||||
|
|
||||||
|
use Carbon\Carbon as MutableCarbon;
|
||||||
|
use Carbon\CarbonImmutable as Carbon;
|
||||||
|
use ErrorException;
|
||||||
|
use Generator;
|
||||||
|
use PHPUnit\Framework\Attributes\DataProvider;
|
||||||
|
use PHPUnit\Framework\Attributes\RequiresPhp;
|
||||||
|
use Tests\AbstractTestCase;
|
||||||
|
|
||||||
|
#[RequiresPhp('>=8.5.0')]
|
||||||
|
class NoDiscardTest extends AbstractTestCase
|
||||||
|
{
|
||||||
|
public static function dataForDiscardedCalls(): Generator
|
||||||
|
{
|
||||||
|
yield ['modify', ['+1 day']];
|
||||||
|
yield ['next', []];
|
||||||
|
yield ['previous', []];
|
||||||
|
yield ['addDays', [2]];
|
||||||
|
}
|
||||||
|
|
||||||
|
#[DataProvider('dataForDiscardedCalls')]
|
||||||
|
public function testDiscardedResultTriggersWarning(string $method, array $arguments): void
|
||||||
|
{
|
||||||
|
$this->expectException(ErrorException::class);
|
||||||
|
$this->expectExceptionMessage(
|
||||||
|
"The return value of method Carbon\\CarbonImmutable::$method() should either be used or intentionally ignored by casting it as (void)",
|
||||||
|
);
|
||||||
|
|
||||||
|
$date = Carbon::parse('2026-10-02 12:00');
|
||||||
|
|
||||||
|
$this->withErrorAsException(static function () use ($date, $method, $arguments) {
|
||||||
|
$date->$method(...$arguments);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMutableResultCanBeDiscarded(): void
|
||||||
|
{
|
||||||
|
$date = MutableCarbon::parse('2026-10-02 12:00');
|
||||||
|
|
||||||
|
$this->withErrorAsException(static function () use ($date) {
|
||||||
|
$date->modify('+1 day');
|
||||||
|
$date->next();
|
||||||
|
$date->addDays(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->assertSame('2026-10-12 00:00:00', $date->format('Y-m-d H:i:s'));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -52,7 +52,7 @@ class SettersTest extends AbstractTestCase
|
|||||||
));
|
));
|
||||||
|
|
||||||
$d = Carbon::now();
|
$d = Carbon::now();
|
||||||
$d->setHours(Month::February);
|
$d = $d->setHours(Month::February);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testMonthEnum()
|
public function testMonthEnum()
|
||||||
@@ -74,7 +74,7 @@ class SettersTest extends AbstractTestCase
|
|||||||
));
|
));
|
||||||
|
|
||||||
$d = Carbon::parse('2023-10-25 21:14:51');
|
$d = Carbon::parse('2023-10-25 21:14:51');
|
||||||
$d->setMonth(2.5);
|
$d = $d->setMonth(2.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testMonthFloatPassWithZeroDecimalPart()
|
public function testMonthFloatPassWithZeroDecimalPart()
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ class GettersTest extends AbstractTestCase
|
|||||||
{
|
{
|
||||||
$period = CarbonPeriodFactory::withStartIntervalEnd(static::$periodClass);
|
$period = CarbonPeriodFactory::withStartIntervalEnd(static::$periodClass);
|
||||||
|
|
||||||
$period->getStartDate()->subDays(3);
|
$date = $period->getStartDate()->subDays(3);
|
||||||
|
|
||||||
$this->assertSame('2012-07-01', $period->getStartDate()->format('Y-m-d'));
|
$this->assertSame('2012-07-01', $period->getStartDate()->format('Y-m-d'));
|
||||||
}
|
}
|
||||||
@@ -99,7 +99,7 @@ class GettersTest extends AbstractTestCase
|
|||||||
{
|
{
|
||||||
$period = CarbonPeriodFactory::withStartIntervalEnd(static::$periodClass);
|
$period = CarbonPeriodFactory::withStartIntervalEnd(static::$periodClass);
|
||||||
|
|
||||||
$period->getEndDate()->addDays(3);
|
$date = $period->getEndDate()->addDays(3);
|
||||||
|
|
||||||
$this->assertSame('2012-07-15', $period->getEndDate()->format('Y-m-d'));
|
$this->assertSame('2012-07-15', $period->getEndDate()->format('Y-m-d'));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user