Skip to content

Commit

Permalink
Merge pull request #2991 from briannesbitt/fix/macro-diff-name
Browse files Browse the repository at this point in the history
Re-allow macro with names starting with diff
  • Loading branch information
kylekatarnls committed Apr 5, 2024
2 parents 02a9502 + dcecf75 commit 82c2827
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 71 deletions.
1 change: 1 addition & 0 deletions .github/workflows/laravel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,4 @@ jobs:
DYNAMODB_ENDPOINT: "http://localhost:8888"
AWS_ACCESS_KEY_ID: randomKey
AWS_SECRET_ACCESS_KEY: randomSecret
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2 changes: 1 addition & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ parameters:
- '#^Call to static method get\(\) on an unknown class Symfony\\Component\\Translation\\PluralizationRules\.$#'
- '#^Call to an undefined static method#'
- '#^Call to an undefined method Carbon\\Carbon(Immutable)?::floatDiffIn([A-Za-z]+)\(\)\.$#'
- '#^Call to an undefined method Carbon\\Carbon(Immutable)?::diffInReal([A-Za-z]+)\(\)\.$#'
- '#^Call to an undefined method Carbon\\Carbon(Immutable)?::(diffInBusinessDays|diffInReal([A-Za-z]+))\(\)\.$#'
- '#^Call to an undefined method Carbon\\Carbon(Immutable)?::(add|sub)Real([A-Za-z]+)\(\)\.$#'
- '#^Unsafe usage of new static\(\)\.$#'
- '#^Method Carbon\\Carbon(Interface|Immutable)?::(add|sub)[A-Z][A-Za-z]+\(\) invoked with 1 parameter, 0 required\.$#'
Expand Down
152 changes: 82 additions & 70 deletions src/Carbon/Traits/Date.php
Original file line number Diff line number Diff line change
Expand Up @@ -2607,77 +2607,12 @@ public static function sleep(int|float $seconds): void
*/
public function __call(string $method, array $parameters): mixed
{
if (preg_match('/^(diff|floatDiff)In(Real|UTC|Utc)?(.+)$/', $method, $match)) {
$mode = strtoupper($match[2] ?? '');
$betterMethod = $match[1] === 'floatDiff' ? str_replace('floatDiff', 'diff', $method) : null;

if ($mode === 'REAL') {
$mode = 'UTC';
$betterMethod = str_replace($match[2], 'UTC', $betterMethod ?? $method);
}

if ($betterMethod) {
@trigger_error(
"Use the method $betterMethod instead to make it more explicit about what it does.\n".
'On next major version, "float" prefix will be removed (as all diff are now returning floating numbers)'.
' and "Real" methods will be removed in favor of "UTC" because what it actually does is to convert both'.
' dates to UTC timezone before comparison, while by default it does it only if both dates don\'t have'.
' exactly the same timezone (Note: 2 timezones with the same offset but different names are considered'.
" different as it's not safe to assume they will always have the same offset).",
\E_USER_DEPRECATED,
);
}

$unit = self::pluralUnit($match[3]);
$method = 'diffIn'.ucfirst($unit);

if (\in_array($unit, ['days', 'weeks', 'months', 'quarters', 'years'])) {
$parameters['utc'] = ($mode === 'UTC');
}

if (method_exists($this, $method)) {
return $this->$method(...$parameters);
}
}

$diffSizes = [
// @mode diffForHumans
'short' => true,
// @mode diffForHumans
'long' => false,
];
$diffSyntaxModes = [
// @call diffForHumans
'Absolute' => CarbonInterface::DIFF_ABSOLUTE,
// @call diffForHumans
'Relative' => CarbonInterface::DIFF_RELATIVE_AUTO,
// @call diffForHumans
'RelativeToNow' => CarbonInterface::DIFF_RELATIVE_TO_NOW,
// @call diffForHumans
'RelativeToOther' => CarbonInterface::DIFF_RELATIVE_TO_OTHER,
];
$sizePattern = implode('|', array_keys($diffSizes));
$syntaxPattern = implode('|', array_keys($diffSyntaxModes));

if (preg_match("/^(?<size>$sizePattern)(?<syntax>$syntaxPattern)DiffForHumans$/", $method, $match)) {
$dates = array_filter($parameters, function ($parameter) {
return $parameter instanceof DateTimeInterface;
});
$other = null;

if (\count($dates)) {
$key = key($dates);
$other = current($dates);
array_splice($parameters, $key, 1);
}
$result = $this->callDiffAlias($method, $parameters)
?? $this->callHumanDiffAlias($method, $parameters)
?? $this->callRoundMethod($method, $parameters);

return $this->diffForHumans($other, $diffSyntaxModes[$match['syntax']], $diffSizes[$match['size']], ...$parameters);
}

$roundedValue = $this->callRoundMethod($method, $parameters);

if ($roundedValue !== null) {
return $roundedValue;
if ($result !== null) {
return $result;
}

$unit = rtrim($method, 's');
Expand Down Expand Up @@ -2957,4 +2892,81 @@ private function getUTCUnit(string $unit): ?string

return null;
}

private function callDiffAlias(string $method, array $parameters): mixed
{
if (preg_match('/^(diff|floatDiff)In(Real|UTC|Utc)?(.+)$/', $method, $match)) {
$mode = strtoupper($match[2] ?? '');
$betterMethod = $match[1] === 'floatDiff' ? str_replace('floatDiff', 'diff', $method) : null;

if ($mode === 'REAL') {
$mode = 'UTC';
$betterMethod = str_replace($match[2], 'UTC', $betterMethod ?? $method);
}

if ($betterMethod) {
@trigger_error(
"Use the method $betterMethod instead to make it more explicit about what it does.\n".
'On next major version, "float" prefix will be removed (as all diff are now returning floating numbers)'.
' and "Real" methods will be removed in favor of "UTC" because what it actually does is to convert both'.
' dates to UTC timezone before comparison, while by default it does it only if both dates don\'t have'.
' exactly the same timezone (Note: 2 timezones with the same offset but different names are considered'.
" different as it's not safe to assume they will always have the same offset).",
\E_USER_DEPRECATED,
);
}

$unit = self::pluralUnit($match[3]);
$diffMethod = 'diffIn'.ucfirst($unit);

if (\in_array($unit, ['days', 'weeks', 'months', 'quarters', 'years'])) {
$parameters['utc'] = ($mode === 'UTC');
}

if (method_exists($this, $diffMethod)) {
return $this->$diffMethod(...$parameters);
}
}

return null;
}

private function callHumanDiffAlias(string $method, array $parameters): ?string
{
$diffSizes = [
// @mode diffForHumans
'short' => true,
// @mode diffForHumans
'long' => false,
];
$diffSyntaxModes = [
// @call diffForHumans
'Absolute' => CarbonInterface::DIFF_ABSOLUTE,
// @call diffForHumans
'Relative' => CarbonInterface::DIFF_RELATIVE_AUTO,
// @call diffForHumans
'RelativeToNow' => CarbonInterface::DIFF_RELATIVE_TO_NOW,
// @call diffForHumans
'RelativeToOther' => CarbonInterface::DIFF_RELATIVE_TO_OTHER,
];
$sizePattern = implode('|', array_keys($diffSizes));
$syntaxPattern = implode('|', array_keys($diffSyntaxModes));

if (preg_match("/^(?<size>$sizePattern)(?<syntax>$syntaxPattern)DiffForHumans$/", $method, $match)) {
$dates = array_filter($parameters, function ($parameter) {
return $parameter instanceof DateTimeInterface;
});
$other = null;

if (\count($dates)) {
$key = key($dates);
$other = current($dates);
array_splice($parameters, $key, 1);
}

return $this->diffForHumans($other, $diffSyntaxModes[$match['syntax']], $diffSizes[$match['size']], ...$parameters);
}

return null;
}
}
7 changes: 7 additions & 0 deletions tests/Carbon/MacroTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -343,4 +343,11 @@ public function testSubClassMacro()

SubCarbon::resetMacros();
}

public function testMacroNameCanStartWithDiff()
{
Carbon::macro('diffInBusinessDays', static fn () => 2);

$this->assertSame(2, Carbon::now()->diffInBusinessDays());
}
}

0 comments on commit 82c2827

Please sign in to comment.