Skip to content

Helper Function Abuse Analyzer

Analyzer IDCategorySeverityTime To Fix
helper-function-abuse✨ Best PracticesLow25 minutes

What This Checks

Detects excessive use of Laravel helper functions that hide dependencies and violate SOLID principles. Tracks:

  • Helper function calls in classes/traits: Counts calls to common Laravel helpers like auth(), request(), cache(), config(), session(), etc.
  • Threshold violations: Flags classes exceeding the configured threshold (default: 5 helpers)
  • Severity escalation: Low severity for moderate violations, Medium for 10+ over threshold, High for 20+ over threshold
  • Per-helper tracking: Shows which specific helpers are used and how many times

Tracked Helpers (36 by default): app, auth, cache, config, cookie, event, logger, old, redirect, request, response, route, session, storage_path, url, view, abort, abort_if, abort_unless, bcrypt, collect, dd, dispatch, info, now, optional, policy, resolve, retry, tap, throw_if, throw_unless, today, validator, value, report

Why It Matters

  • Hidden Dependencies: Helper functions hide what a class actually depends on, making dependencies invisible
  • Testing Difficulty: Impossible to mock or stub helper functions, making unit testing extremely difficult
  • Violates SOLID: Breaks Dependency Inversion Principle by depending on global functions instead of abstractions
  • Maintenance Burden: Difficult to track what services a class uses when they're hidden behind helpers
  • Coupling: Creates tight coupling to Laravel's global scope instead of explicit dependencies
  • Refactoring Resistance: Hard to refactor when dependencies aren't explicit in constructors

Real-world impact:

  • Controllers with 10+ helper calls become untestable without full application bootstrap
  • Services using helpers can't be instantiated independently for unit testing
  • Code reviews miss dependency violations because helpers hide them
  • Refactoring becomes risky without comprehensive integration tests

How to Fix

Quick Fix (10 minutes)

Scenario 1: Inject Common Dependencies

php
// ❌ BAD - Helper abuse (6 helpers)
class UserController
{
    public function update($id)
    {
        $user = auth()->user();
        $data = request()->validate([...]);

        cache()->forget("user.{$id}");
        event(new UserUpdated($user));
        session()->flash('success', 'Updated!');

        return redirect()->back();
    }
}

// ✅ GOOD - Dependency injection
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;

class UserController
{
    public function update(Request $request, $id)
    {
        $user = Auth::user();  // Or inject AuthManager
        $data = $request->validate([...]);

        Cache::forget("user.{$id}");
        event(new UserUpdated($user));

        return redirect()->back()
            ->with('success', 'Updated!');
    }
}

Scenario 2: Use Facades for Global Services

php
// ❌ BAD - Helper functions everywhere
class ReportGenerator
{
    public function generate()
    {
        $start = now()->startOfMonth();
        $end = now()->endOfMonth();

        $data = cache()->remember('report', 3600, function() {
            return collect($this->getData())->filter(...);
        });

        logger()->info('Report generated');

        return $data;
    }
}

// ✅ GOOD - Use Facades (still testable via fake())
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Carbon\Carbon;

class ReportGenerator
{
    public function generate()
    {
        $start = Carbon::now()->startOfMonth();
        $end = Carbon::now()->endOfMonth();

        $data = Cache::remember('report', 3600, function() {
            return collect($this->getData())->filter(...);
        });

        Log::info('Report generated');

        return $data;
    }
}

Proper Fix (25 minutes)

Implement comprehensive dependency injection across your application:

1. Constructor Injection for Core Dependencies

php
// ❌ BAD - Heavy helper usage
class OrderService
{
    public function process($order)
    {
        $user = auth()->user();

        if (!$user->can('process', $order)) {
            abort(403);
        }

        cache()->put("order.{$order->id}", $order, 3600);
        event(new OrderProcessed($order));
        logger()->info("Order {$order->id} processed");

        return response()->json($order);
    }
}

// ✅ GOOD - Explicit dependencies
use Illuminate\Contracts\Auth\Access\Gate;
use Illuminate\Contracts\Cache\Repository as Cache;
use Illuminate\Contracts\Events\Dispatcher;
use Psr\Log\LoggerInterface;

class OrderService
{
    public function __construct(
        private Gate $gate,
        private Cache $cache,
        private Dispatcher $events,
        private LoggerInterface $logger
    ) {}

    public function process(User $user, Order $order)
    {
        if (!$this->gate->allows('process', $order)) {
            throw new AuthorizationException();
        }

        $this->cache->put("order.{$order->id}", $order, 3600);
        $this->events->dispatch(new OrderProcessed($order));
        $this->logger->info("Order {$order->id} processed");

        return $order;
    }
}

2. Acceptable Helper Usage

Some helpers are fine to use:

php
// ✅ ACCEPTABLE - View helpers in Blade templates
@foreach($posts as $post)
    {{ $post->title }}
    <small>{{ $post->created_at->diffForHumans() }}</small>
@endforeach

// ✅ ACCEPTABLE - Collection helpers for data manipulation
public function transform(array $data)
{
    return collect($data)
        ->map(fn($item) => $this->process($item))
        ->filter()
        ->values()
        ->all();
}

// ✅ ACCEPTABLE - Response helpers at controller return
public function store(Request $request)
{
    $result = $this->service->create($request->validated());

    return response()->json($result, 201); // OK - just for response formatting
}

// ✅ ACCEPTABLE - Route helpers in routes file
Route::get('/posts', [PostController::class, 'index'])->name('posts.index');
Route::redirect('/home', '/dashboard');

3. Configuration for Legitimate Thresholds and Functions

Customize the analyzer for your project, publish the config:

bash
php artisan vendor:publish --tag=shieldci-config

Then in config/shieldci.php:

php
'analyzers' => [
    'best-practices' => [
        'enabled' => true,
        
        'helper-function-abuse' => [
            // Threshold before flagging (default: 5)
            'threshold' => 5,

            // Customize which helpers to track
            'helper_functions' => [
                'auth', 'request', 'cache', 'config', 'session',
                'event', 'logger', 'app', 'resolve',
                // Add or remove helpers as needed
            ],
        ],
    ],
],

4. Gradual Migration Strategy

For legacy codebases:

php
// Step 1: Identify worst offenders
// Run analyzer to find classes with 15+ helpers

// Step 2: Start with services layer
// Refactor services first as they're easiest to test

// Step 3: Move to controllers
// Inject Request, use Facades for occasional needs

// Step 4: Use baseline to ignore legacy code
php artisan shield:baseline

// Step 5: Enforce on new code
// Set lower threshold for new classes

References