Feature Request: Arch test, check for specific class methods not to be used
Nobody has claimed this yet.
- Dominant language
- PHP
- Stars
- 11.7k
- Forks
- 538
- Avg merge
- 4d 11h
- Merged PRs (30d)
- 8
Description
The problem
I recently found a "bug" (not really, just unintuitive api) where I got a carbon date and I want to do some operations on the previous month. Like this:
$prevoiusMonth = today()->subMonth()->startOfMonth();
I was expecting to have the previous month, but i got the same month as today, normally it works fine, so why today its not working?
Turns out I ran this code on 2026-03-31, so carbon subtracted 1 month, making it 2026-02-31, but because this doesn't exist it casts to 2026-02-28 + 3 days, returning 2026-03-03, finally startOfMonth makes it 2026-03-01 instead of the expected 2026-02-01.
$prevoiusMonth = Carbon::parse('2026-03-31')->subMonth()->startOfMonth(); // 2026-03-01
How this should be resolved
The correct way to do it, is understanding how carbon works internally, and always remember to do this type of things in a really specific way, like this:
$prevoiusMonth = Carbon::parse('2026-03-31')->startOfMonth()->subMonth(); // 2026-02-01
Or use the safe and declarative methods carbon provides and always avoid subMonth (or similar methods)
$prevoiusMonth = Carbon::parse('2026-03-31')->subMonthNoOverflow()->startOfMonth()->toDateString(); // 2026-02-01
$prevoiusMonth = Carbon::parse('2026-03-31')->subMonthWithNoOverflow()->startOfMonth()->toDateString(); // 2026-02-01
$prevoiusMonth = Carbon::parse('2026-03-31')->subMonthWithoutOverflow()->startOfMonth()->toDateString(); // 2026-02-01
$prevoiusMonthOverflowed = Carbon::parse('2026-03-31')->subMonthWithOverflow()->startOfMonth()->toDateString(); // 2026-03-01
How i would like to solve this (currently not possible?)
Ban this methods whit arch test, and never see them in my code base, I don't want to be worried if my dates are wrong because of overflowing shenanigans.
I would also like to ban use of ->dd(), really useful function, really dangerous on untested code.
I ended up creating a custom PHPStan rule, but i would prefer to have this directly on pest arch tests
Here is it if anyone is interested, it work on Illuminate\Support\Carbon, Carbon\Carbon, Carbon\CarbonImmutable and Illuminate\Support\Facades\Date facade:
<?php
declare(strict_types=1);
namespace Tests\PHPStan\Rules;
use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Identifier;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\IdentifierRuleError;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Type\VerbosityLevel;
/**
* @implements Rule<MethodCall>
*/
final class ForbidCarbonUnintuitiveMethodsRule implements Rule
{
public function getNodeType(): string
{
return MethodCall::class;
}
/**
* @return list<IdentifierRuleError>
*/
public function processNode(Node $node, Scope $scope): array
{
$methodName = $node->name instanceof Identifier ? $node->name->toString() : null;
if ($methodName === null) {
return [];
}
$forbiddenMethods = [
'addCenturies',
'addCentury',
'addDecade',
'addDecades',
'addMillennia',
'addMillennium',
'addMonth',
'addMonths',
'addQuarter',
'addQuarters',
'addYear',
'addYears',
'subCenturies',
'subCentury',
'subDecade',
'subDecades',
'subMillennia',
'subMillennium',
'subMonth',
'subMonths',
'subQuarter',
'subQuarters',
'subYear',
'subYears',
];
if (in_array($methodName, $forbiddenMethods, true)) {
$calledOnType = $scope->getType($node->var)->describe(VerbosityLevel::typeOnly());
if (str_contains($calledOnType, 'Carbon')) {
return [
RuleErrorBuilder::message("Forbidden use of Carbon::{$methodName}, use Carbon::{$methodName}WithoutOverflow or Carbon::{$methodName}WithOverflow methods instead.")
->line($node->getStartLine())
->identifier('carbon.forbidCarbonUnintuitiveMethods')
->build(),
];
}
}
return [];
}
}
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading the issue's custom PHPStan rule and the existing Pest architecture-testing documentation or entry points. Determine how an arch test could identify forbidden methods on Carbon-related classes and how users would declare that restriction. Done means the requested methods, including the listed date operations, can be rejected through Pest arch tests with coverage for the intended behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- php
- Domain
- testing-qa
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 47/100