Extending class from another plugin

I’m trying to add some functionality to the Lightslider plugin by creating a plugin that extends Lightslider. I’m doing this the same way as you would create a child theme.
Everything works except for the php. Instead of having my class extend Plugin, I want to extend LightsliderPlugin. However, I’m getting the error Class ‘Grav\Plugin\LightsliderPlugin’ not found in <path to my plugin’s php>.
I’ve tried extending LightsliderPlugin and \Grav\Plugin\LightsliderPlugin. PHPStorm recognizes what I’m trying to do. What am I doing wrong?

@Jrbdog I’ve tried the following which seems to extend another plugin and override its parent’s methods.

In fresh install of Graf 1.7

  • Create plugin Base: $ bin/plugin devtools new-plugin
  • Add event handlers:
    class BasePlugin extends Plugin
    {
      ...
      
      // Add event subscriptions
      'onPagesInitialized' => ['onPagesInitialized', 0],
      'onPageInitialized' => ['onPageInitialized', 0],
    
      ...
      
      // Add eventhandlers
      public function onPagesInitialized($event) {
          echo 'Base: onPagesInitialized';
      }
    
      public function onPageInitialized($event) {
          echo 'Base: onPageInitialized';
      }
    
  • Create plugin Child: $ bin/plugin devtools new-plugin
    // Add import
    use Grav\Plugin\BasePlugin;
    
    class ChildPlugin extends BasePlugin
    {
    
      // Removed getSubscribedEvents()
    
      // Add overriding eventhandler
      public function onPageInitialized($event) {
          echo 'Child: onPagesInitialized';
          parent::onPageInitialized($event);
      }
    
  • Update composer.json of Child:
    "psr-4": {
      "Grav\\Plugin\\Child\\": "classes/",
      // Add path to class of BasePlugin
      "Grav\\Plugin\\BasePlugin\\": "../base"
    },
    
  • Run $ composer update

Note:

  • Removed getSubscribedEvents() in Child
  • Plugin Base must be disabled else Grav will continue calling eventhandlers of Base in parallel to Child.

When debugging the following breakpoints are hit:

  1. echo ‘Base: onPagesInitialized’;
  2. echo ‘Child: onPageInitialized’;
  3. echo ‘Base: onPageInitialized’; (called by parent::onPageInitialized($page);)

Update:
I’ve renamed ‘Parent’ to ‘Base’, it seems ‘Parent’ doesn’t work as expected…

1 Like

I tried doing what you did and got the same error. I eventually got it working with

require './user/plugins/lightslider/lightslider.php';

class MyPlugin extends LightsliderPlugin
{
}

@Jrbdog, That seems like a hack for an issue with ‘autoload’…

From your solution I guess:

  • Grav has not called ‘/vendor/autoload.php’ in your plugin
  • or your ‘psr-4’ section in composer.json isn’t configured properly
  • or you may have forgotten to recreate the autoload files

Are you sure you added the code that allows Grav to call require __DIR__ . '/vendor/autoload.php'; in your plugin?

Could you try running $ bin/plugin devtools new-plugin and see if your code matches the required autoloader code as in the generated skeleton.

Could you try doing the steps as I outlined? Does that work?

I already tried creating a skeleton parent and child plugin and went through the steps you said. That didn’t work either. Could it have something to do with the local server it’s running on?

@Jrbdog,

I’m using localhost using Windows Subsystem for Linux with Apache inside Ubuntu 20.04

Did you debug and set a breakpoint inside function autoload() to check if vendor/autoload is indeed loaded?

public function autoload(): \Composer\Autoload\ClassLoader
{
    return require __DIR__ . '/vendor/autoload.php';
}

Autoload does appear to be working. I think the problem is that the autoload() function is inside the child class. It works when I include autoload php for the class declaration.

@Jrbdog, I was trying to setup autoloader again and noticed something that may have set things up for failure…

I initialially created plugins ‘Xx’ (parent) and ‘Yy’ (child) which worked fine. For clearity, I named them ‘Parent’ and ‘Child’ here on the forum. However, I can’t get Parent/Child working. Maybe because ‘parent’ is a reserved work…

Using names like Base/Child or any other combination works fine though. I’ve update my example.

Wonder if you can get Base/Child to work.

Yes, changing the name of the parent class worked.
However, it doesn’t seem to be actually extending the class. If you change the child class to extend Plugin your echos still echo.
I think you have to require the autoload class before the class declaration because the class won’t run if it can’t extend the parent class, so the autoload function won’t run either.

@Jrbdog, Just to be sure if we are on the same page…

Setup:

  • Create plugin ‘Base’
    • $ bin/plugin devtools new-plugin
    • $ cd /user/plugins/base
    • $ composer update
    • In ‘user/plugins/base/base.yaml’ switch of the plugin
      enabled: false
      
  • Create plugin ‘Child’
    • $ bin/plugin devtools new-plugin
    • $ cd /user/plugins/child
    • Update psr-4 section in ‘/user/plugins/child/composer.json’ to:
      "psr-4": {
        "Grav\\Plugin\\Child\\": "classes/",
        "Grav\\Plugin\\BasePlugin\\": "../base"
      },
      
    • $ composer update
    • Update code in ‘user/plugins/child/child.php’ to:
      <?php
      namespace Grav\Plugin;
      
      use Grav\Plugin\BasePlugin;
      
      class ChildPlugin extends BasePlugin
      {
          public function onPluginsInitialized(): void
          {
              parent::onPluginsInitialized();
          }
      }
      

Testing:
Set breakpoints in every method of Base and Child.

Running Grav 1.6, the following breakpoints are hit:

  • Base::getSubscribedEvents()
  • Base::autoload()
  • Child::onPluginsInitialized()
    • Base::onPluginsInitialized()
      Called in Child by parent::onPluginsInitialized();

Running Grav 1.7, the following breakpoints are hit:

  • Base::autoload()
    Called because 1.7 calls autoload directly
  • Base::getSubscribedEvents()
  • Base::autoload()
    getSubscribed() should be update to remove subscripion to autoload
  • Child::onPluginsInitialized()
    • Base::onPluginsInitialized()
      Called in Child by parent::onPluginsInitialized();

Breakpoints after update Child to inherit from standard ‘Plugin’.

  • Child::onPluginsInitialized()
    Exception thrown because method Plugin::onPluginsInitialized() does not exist.

Looking at the above results, IMHO, Child inherits/extends from Base.

Ok, so nothing special is actually needed. I created a Base and Child plugin. Ended up not doing anything with autoloader, didn’t require any files. I simply extended the Base plugin. It worked.
In my original plugin, I extended both the Form and Error plugins. That worked. Only Lightslider can’t be extended normally. It can’t be extended through the autoload class either.

@Jrbdog, I cannot get LightSlider to work either using autoloader, but the following workaround does work (using Grav 1.7.0-rc.20) :

  • No need to alter default ‘/user/plugins/child/composer.json’
  • To override LightSlider::onTwigSiteVariables() in ‘child.php’ use:
    <?php
    namespace Grav\Plugin;
    
    use Composer\Autoload\ClassLoader;
    
    require __DIR__ . '/../lightslider/lightslider.php';
    
    use Grav\Plugin\LightsliderPlugin;
    
    class ChildPlugin extends LightSliderPlugin
    {
      public function autoload(): ClassLoader
      {
        return require __DIR__ . '/vendor/autoload.php';
      }
    
      public function onTwigSiteVariables() {
          parent::onTwigSiteVariables();
      }
    }
    
1 Like