pestphp / pestphp/pest

Feature Request: Arch test, check for specific class methods not to be used

Open
#1,658 0 comments 0 reactions 0 assignees View on GitHub

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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.