bzarboni's avatar

Best way to override a class inside a vendor package?

Hi everyone,

I'm trying to override behaviour of a library that we use on our Laravel app:

https://github.com/barryvdh/laravel-debugbar

I'm trying to implement Content Security Policy (CSP) for our app, and our devs use the Laravel Debugbar. That packages injects itself onto our front-end, using script, style, and link tags that need a nonce applied to them.

I want to inject my own class to override theirs. It's apparently do-able (and I got it working), by using the composer.json and the autoload-dev section to override the file: https://shahednasser.medium.com/how-to-override-classes-in-php-and-composer-aa44a2a068a5

What I'm stuck on, is that I wanted to inherit from the original class, and only override 1 method. When I do the above though, the class is no longer 'found', so my class that inherits from it throws an error.

use Barryvdh\Debugbar\JavascriptRenderer as OriginalRenderer;

 /**
 * {@inheritdoc}
 * This class will be used to customize the Laravel DebugBar's
 * render methods to put CSP (Content Security Policy) nonce
 * values on the DebugBar tags
 */
class JavascriptRenderer extends OriginalRenderer
{
    ...
}
Error
Class "Barryvdh\Debugbar\JavascriptRenderer" not found

Anyone done anything like this before?

0 likes
2 replies
Tippin's avatar

@bzarboni I'd say you have two options here. First one being to fork the packages repository, make your changes, and then have your composer install via your forked repository instead of the original.

Second option being a bit more complicated. Typically you should utilize the container to override/extend vendor packages, however the class in question you are trying to override, is directly instantiated in debugbar's core class, thus you cannot directly swap it out through the container.

https://github.com/barryvdh/laravel-debugbar/blob/master/src/LaravelDebugbar.php#L659

What you can do however, is redeclare the singleton binding for LaravelDebugbar to your own custom class extending the packages concrete class, and overwrite the getJavascriptRenderer method to return your extended renderer class.

Renderer

<?php

namespace App\Support;

use Barryvdh\Debugbar\JavascriptRenderer as BaseJavascriptRenderer;

class JavascriptRenderer extends BaseJavascriptRenderer
{
    public function renderHead()
    {
        dump('made it');

        return parent::renderHead();
    }
}

Debugbar Extension

<?php

namespace App\Support;

use App\Support\JavascriptRenderer as JavascriptRendererExtended;
use Barryvdh\Debugbar\LaravelDebugbar;

class DebugBar extends LaravelDebugbar
{
    public function getJavascriptRenderer($baseUrl = null, $basePath = null)
    {
        if ($this->jsRenderer === null) {
            $this->jsRenderer = new JavascriptRendererExtended($this, $baseUrl, $basePath);
        }
        return $this->jsRenderer;
    }
}

Then just overwrite the packages binding for LaravelDebugbar in one of your ServiceProvider

<?php

namespace App\Providers;

use App\Support\DebugBar;
use Barryvdh\Debugbar\LaravelDebugbar;
use Barryvdh\Debugbar\SymfonyHttpDriver;
use Illuminate\Session\SessionManager;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->singleton(LaravelDebugbar::class, function ($app) {
            $debugbar = new DebugBar($app);

            if ($app->bound(SessionManager::class)) {
                $sessionManager = $app->make(SessionManager::class);
                $httpDriver = new SymfonyHttpDriver($sessionManager);
                $debugbar->setHttpDriver($httpDriver);
            }

            return $debugbar;
        });
    }
}
2 likes
bzarboni's avatar

Thank you, I'll give that second method a whirl, since I'm about half way down that path already. Really appreciate it.

1 like

Please or to participate in this conversation.