phanalist

E0017: The “Know-It-All” Rule (Coupling Between Objects - CBO)

Imagine a class is a person working in an office. Coupling is how many other people they need to talk to in order to do their job.

If a worker needs to constantly bother the Accountant, the Manager, the Janitor, and the CEO just to send an email, they are tightly coupled to everyone else. If any of those people change how they work, our worker breaks!

How the rule works

Coupling Between Objects (CBO) counts exactly how many distinct external classes your class knows about. We count a class if you:

  1. Extend it or implement it.
  2. Use it as a property type, parameter type, or return type.
  3. Instantiate it with new.
  4. Call its static methods like Logger::info().
  5. Catch it as an Exception.

If your score gets too high (default > 10), your class is too dependent on the rest of the system.


❌ The “Know-It-All” Example

This class has to know about 6 different external classes just to process a refund!

// Knows about: BaseService, AuditableService, UserRepository, PaymentGateway, 
// Mailer, Logger, EventBus, Order, PaidOrder, StripeRefund, RefundReceipt, 
// GatewayException, CannotRefundOrder (Score: 13)
class OrderService extends BaseService implements AuditableService
{
    private UserRepository $users;
    private PaymentGateway $payments;
    private Mailer $mailer;

    public function __construct(Logger $logger, EventBus $events) {}

    public function refund(Order $order): RefundReceipt
    {
        if ($order instanceof PaidOrder) {
            StripeRefund::create($order);
        }

        try {
            return new RefundReceipt();
        } catch (GatewayException $exception) {
            throw new CannotRefundOrder();
        }
    }
}

✅ The “Focused Worker” Example

Split responsibilities so each class only talks to the few collaborators it actually needs.

class RefundService
{
    public function __construct(
        private PaymentGateway $payments,
        private RefundReceiptFactory $receipts,
    ) {}

    public function refund(Order $order): RefundReceipt
    {
        // Much simpler! Only knows about PaymentGateway, RefundReceiptFactory, 
        // Order, and RefundReceipt.
        $this->payments->refund($order);
        return $this->receipts->createFor($order);
    }
}

Configuration

rules:
  E0017:
    max_coupling: 10

The Junior’s Rule of Thumb:

If Phanalist flags your class for high CBO, it means your class has its fingers in too many pies. Try to split it into smaller classes, or pass in simple data (like arrays or strings) instead of entire objects.