XF 2.1 Help required on how to overwrite / overload a function of another addon

TheSkullKid

Member
Dear community

I'm just trying to create my first addon and to get more familiar with Xenforo addon structure.
For that I would like to overwrite a protected or a private function from another addon. Is that possible ?
And is it possible that my code code will be executed first ?

The reason for asking is, that I want to change the working process for a specific function of this other addon.

Thanks for your answers
 

TheSkullKid

Member
No one here who could give me a small example ? Or help and explain how it works. I was able to extend a Xenforo class or to overwrite it, but not with a third party add-on
 

Earl

Active member
Yes, it is possible.
First, find If the class of the method you are trying override is using xenforo class proxy system (XFCP_ prefix in class name),
It means, that add-on extending a xenforo core class.
And you have to extend the same core class with the lower execution order value. (just follow steps)
If that's your case follow these steps:

Find the add-on's class extension you want to overwrite in Development->Class extensions and open it.
Check it's execution order value, and memorize it.
Create a new class extension for your add-on and put a lower value than the number you have memorized before. So your class will extend before that class.
add your method with the same name as that core class.
Remember to call parent::your_method_name(); inside of your method. so it won't break xenforo core functions.

Another case is the class of the method you are trying to override is not using xenforo class proxy system, but it's just a class.
in this case, you just have to create a new class extension (Development->Class extensions) and no need to worry about the execution order.
You can just override it by having the same name as that method in your class.

remember to call parent::your_method_name(); at the end of your code, so the code in the parent class (the class you are extending) won't break.
 

TheSkullKid

Member
Thx @Earl for your answer, but it will not work, like I would :(

Lets try again.

I've got Add-On One, which has the following Class Extension:

Base class nameExtension class name
XFRM\Entity\ResourceUpdateCoder\One\XFRM\Entity\ResourceUpdate

In that class we have the following code:

PHP:
<?php

namespace Coder\One\XFRM\Entity;

use XF\Mvc\Entity\Structure;

class ResourceUpdate extends XFCP_ResourceUpdate
{
    /**
     * @throws \Exception
     */
    protected function _postSave()
    {
        // Do parent stuff
        $previous = parent::_postSave();
        $visitor = \XF::visitor();
        // Add-On does something here    
        return $previous;
    }
}

Now I created my Add-On with the Class Extension like:

Base class nameExtension class name
Coder\One\XFRM\Entity\ResourceUpdateMe\Two\One\XFRM\Entity\ResourceUpdate

And I set the order to a lower number. The code looks like that:

PHP:
<?php

namespace Me\Two\One\XFRM\Entity;

use XF\Mvc\Entity\Structure;


//class ResourceUpdate extends ResourceUpdate implements \Coder\One\XFRM\Entity
class ResourceUpdate extends XFCP_ResourceUpdate
{
    /**
     * @throws \Exception
     */
    protected function _postSave()
    {
        // Do parent stuff
        $previous = parent::_postSave();
        $visitor = \XF::visitor();
        // Add-On should do something here but prevent the execution of the above code from Add-On One    
        return $previous;
    }
}

But the code will not be executed, I also tried it with the class line which I've comment, no differences.

I did another try with that:

Base class nameExtension class name
XFRM\Entity\ResourceUpdateMe\Two\One\XFRM\Entity\ResourceUpdate

And I set the order to a lower number. The code looks like that:

PHP:
<?php

namespace Me\Two\One\XFRM\Entity;

use XF\Mvc\Entity\Structure;

class ResourceUpdate extends XFCP_ResourceUpdate
{
    /**
     * @throws \Exception
     */
    protected function _postSave()
    {
        // Do parent stuff
        $previous = parent::_postSave();
        $visitor = \XF::visitor();
        // Add-On should do something here but prevent the execution of the above code from Add-On One    
        return $previous;
    }
}

Now the code will be executed, but both codes will be executed.

Please point me what is wrong. Thx
 

TheSkullKid

Member
You want a higher execution order as it means your add-on will be called "before" other add-ons with a lower execution order.
I'm so stupid, I mixed the execution order numbers. Thx for that hint

But how can I prevent to execute the original code of the other Add-On ? If I remove the following

PHP:
$previous = parent::_postSave();

it will not executed anymore, but then also the main stuff from the third party Add-On and from Xenforo will not be executed. Is it possible somehow ?

How do I know what the parent is ? I guess these are main developer questions I should know.
 
Last edited:

Earl

Active member
I'm so stupid, I mixed the execution order numbers. Thx for that hint

But how can I prevent to execute the original code of the other Add-On ? If I remove the following

PHP:
$previous = parent::_postSave();

it will not executed anymore, but then also the main stuff from the third party Add-On and from Xenforo will not be executed. Is it possible somehow ?

How do I know what the parent is ? I guess these are main developer questions I should know.
I suggest you to test this with xdebug. Add a break point to parent::_postSave(); and keep hitting F7 and see how the code get executes.
 

TheSkullKid

Member
I suggest you to test this with xdebug. Add a break point to parent::_postSave(); and keep hitting F7 and see how the code get executes.
I already did this, and when I step into at the point where the parent is called, I run into the other original 3rd party addon, which I want to overload or I want to replace only one function.

Ok I can also edit the original Addon, but this is not what I want to do.

More hints and suggestions are welcome. Thx all for your help.
 

Earl

Active member
I already did this, and when I step into at the point where the parent is called, I run into the other original 3rd party addon, which I want to overload or I want to replace only one function.

Ok I can also edit the original Addon, but this is not what I want to do.

More hints and suggestions are welcome. Thx all for your help.
Take a look at that _postSave block in that third-party add-on, and find what's it doing there.
check If it tries to set new values or do something related to that third party addon,
if you notice anything like that, you add if {} block and use those 3rd party activities to test if you're in the 3rd party add-ons _postSave code block, if it's the case skip running parent:_postSave()

for an instance,

PHP:
namespace Vendor\YourAddOn\XF\Entity;

  protected function _postSave()
  {
    if (!isset($this->_newValues[reaction_score])) {
       parent::_postSave();
      }
  }
(I haven't tested it)
 
Last edited:

TheSkullKid

Member
Thx @Earl.

I checked the _newValues already and if I reach my AddOn I've got 16 entries in that array. I run now through all and if I get back to my one, it is still 16, so nothing has been changed in the other sections. Everything which will be done in the 3rd party Addon in the _postSave function is to trigger some events, nothing else.

Let's try to summarize it again, what has been tried and what will be executed.

Base class nameExtension class name
XFRM\Entity\ResourceUpdateMe\Two\One\XFRM\Entity\ResourceUpdate

That has been configured in the Class Extensions. Execution order number has been set to 25.

That is the entry for the 3rd party addon:

Base class nameExtension class name
XFRM\Entity\ResourceUpdateCoder\One\XFRM\Entity\ResourceUpdate

Execution order number has been set to 10.

The function _postSave of the 3rd party addon should be overwritten by my addon.

Now execute the code and set some breakpoints, here the execution order (parent::_postSave() still included):
  1. MyAddon: _postSave (ResourceUpdate)
  2. 3rd Party Addon: _postSave (ResourceUpdate)
  3. XenForo: _postSave (ResourceUpdate)
  4. 3rd party Addon: _postSave (ResourceItem)
  5. XenForo: _postSave (ResourceItem)
  6. 3rd Party Addon: _postSave continue (ResourceUpdate)
  7. MyAddon: _postSave continue (ResourceUpdate)
If I change the execution order of my Addon to 5 it looks like this:
  1. 3rd Party Addon: _postSave (ResourceUpdate)
  2. MyAddon: _postSave (ResourceUpdate)
  3. XenForo: _postSave (ResourceUpdate)
  4. 3rd party Addon: _postSave (ResourceItem)
  5. XenForo: _postSave (ResourceItem)
  6. MyAddon: _postSave continue (ResourceUpdate)
  7. 3rd Party Addon: _postSave continue (ResourceUpdate)
If I remove the call parent::_postSave() from my Addon, then only my will be executed nothing else and I guess it is important that the XenForo part should be executed, right ? I would like to have it like that:
  1. MyAddon: _postSave (ResourceUpdate)
  2. XenForo: _postSave (ResourceUpdate)
  3. 3rd party Addon: _postSave (ResourceItem)
  4. XenForo: _postSave (ResourceItem)
  5. MyAddon: _postSave continue (ResourceUpdate)
I was not able to get it to work, to create the class extension

Base class nameExtension class name
Coder\One\XFRM\Entity\ResourceUpdateMe\Two\One\XFRM\Entity\ResourceUpdate

and overwrite it. My code will never be executed.

Maybe it is still unclear and the description to bad, please let me know that is the case.

Thanks again to all who already tried to help this is much appreciated.
 

TickTackk

Well-known member
What you're looking to do will only cause compatibility issue with other add-ons sooner or later. You should try thinking of other way to override whatever the third-party add-on is doing.
 

Earl

Active member
I also strongly recommend you to follow @TickTackk 's advice, then again did you try adding a code event listener?
Create Listener.php in your addon root
Code:
public static function {\XF\Mvc\Entity\Entity $entity}{

}
then
Admincp -> Development -> Code event listeners -> Select Entity post save
Then, find 3rd party add-on's Entity class name and use it in event hint and do whatever you want with it because it only executes when the 3rd party add-on runs _postSave? or you use instanceof against $entity

_postSave function is to trigger some events, nothing else.
you also said it triggers some events, so you find those events and use a code event listener?
Can you copy and paste the 3rd party add-ons _postSave code block?
 

TheSkullKid

Member
Thanks @TickTackk and @Earl

What you're looking to do will only cause compatibility issue with other add-ons sooner or later. You should try thinking of other way to override whatever the third-party add-on is doing.

I know that it might be a bad idea, but I'm really new and I do not know how to do it correctly.

Can you copy and paste the 3rd party add-ons _postSave code block?

Here the code of the 3rd party add-on:

PHP:
    protected function _postSave()
    {
        // Do parent stuff
        $previous = parent::_postSave();
       
        if ($this->isInsert() && !$this->isDescription())
        {
            $eventTriggerRepo = $this->repository('DBTech\Credits:EventTrigger');
            $eventTriggerRepo->getHandler('resourceupdate')
                ->apply($this->resource_update_id, [
                    'content_type' => 'resource_update',
                    'content_id' => $this->resource_update_id
                ], $this->Resource->User)
            ;
        }
       
        return $previous;
    }

There are as well some other parts which I want to expand, but first of all that, I guess if I know how to do it there, then I can do it for the other sections as well.

Thanks.
 

DragonByte Tech

Well-known member
If you're trying to prevent that trigger from occurring, you have two options:
  1. Disable every event that uses this event trigger
  2. Extend DBTech\Credits\EventTrigger\XFRM\Update :: public function apply($refId, array $extraParams = [], \XF\Entity\User $user = null) and return an empty array when your condition is met.
 

TheSkullKid

Member
If you're trying to prevent that trigger from occurring, you have two options:
  1. Disable every event that uses this event trigger
  2. Extend DBTech\Credits\EventTrigger\XFRM\Update :: public function apply($refId, array $extraParams = [], \XF\Entity\User $user = null) and return an empty array when your condition is met.
Thanks a lot for your suggestion,

I will try extending.
 
Last edited:

TheSkullKid

Member
If you're trying to prevent that trigger from occurring, you have two options:
How can this also be done for the Upload of posts ? If I extend DBTech\Credits\EventTrigger\Upload the alert message are no longer visible.

1594112478092.png

Thanks for helping

Edit: Maybe I extend in the wrong way ? The same happened for the XFRM Update above.
 
Last edited:
Top