Jump to content

Hooks inside init.php to namespaced classes does not work [SOLVED]


Juergen
 Share

Recommended Posts

Hello @all

I have tried to apply a hook to a class of a module of my mine which runs inside a namespace. The hook is included inside the site/init.php.

$this->wire()->addHookAfter('Label::renderAsterisk', function(HookEvent $event) {
    bd('render Asterisk');
});

This hook runs on the function renderAsterisk() which is hookable and part of the class "Label" (original File: Label.php).

This class descends from Wire so hooks should work inside this class, but I guess the main problem is that this class runs inside its own namespace called "FrontendForms".


So I tried different versions within the hook to define the class:

  • FrontendForms\Label::renderAsterisk
  • \FrontendForms\Label::renderAsterisk
  • \\FrontendForms\Label::renderAsterisk
  • FrontendForms\\Label::renderAsterisk

 None of the versions above work!

After a search on the internet, I found a discussion about this problem here. It seems that other people have also stumbled upon this problem.

My question: Has anyone already struggled with the same problem and found a solution on how to get hooks to work on classes with namespaces, or could there be another cause responsible for this behavior?

Additional info: This issue comes from my FrontendForms module and I've used hooks in the past to manipulate the output a bit (look here). The hooks have worked in the past, but it seems that they don't work anymore in PW version 3.

Thanks for your help!

Link to comment
Share on other sites

Hello @bernhard

This is interesting, but in my case the hook does not get fired. It seems that that it will be ignored.

The function is hookable by prefixing 3 underscores:

public function ___renderAsterisk(): string
    {
        return '<span class="asterisk">*</span>';
    }

The class 'Label' itself (where the function above is part of) is derived from the Wire class (take a look here on the top parent class), so hooks must be implemented and should work. Only to mention: the top parent class called 'Tag' is an abstract class - could this have an impact on the issue?

Do you have a working example in one of your numerous modules, where you use a hook inside the init.php to hook a method in one of your modules?

Link to comment
Share on other sites

/site/init.php

wire()->addHookAfter('Foo::hello', function ($event) {
  $event->return .= ' - hooked :)';
});

/site/modules/WhatEver/Test.module.php

<?php

namespace ProcessWire;

use MyNamespace\Foo;

class Test extends WireData implements Module
{

  public static function getModuleInfo()
  {
    return [
      'title' => 'Test',
      'version' => '0.0.1',
      'summary' => 'Your module description',
      'autoload' => true,
      'singular' => true,
      'icon' => 'smile-o',
      'requires' => [],
      'installs' => [],
    ];
  }

  public function init()
  {
    wire()->classLoader->addNamespace('MyNamespace', __DIR__ . '/classes');
  }

  public function ready()
  {
    $foo = new Foo();
    bd($foo->hello());
  }
}

/site/modules/WhatEver/classes/Foo.php

<?php

namespace MyNamespace;

use ProcessWire\Wire;

class Foo extends Wire
{
  public function ___hello()
  {
    return "HELLO!";
  }
}

dvTQHNQ.png

  • Like 2
Link to comment
Share on other sites

Thanks for your quick response @bernhard

I compared your example to mine and I see that you are calling the method ($foo->hello()) inside your ready() method. In this case it works on my side too, but this is not the scenario that I want.

I want to manipulate the output not inside the ready() but before page render, so the markup of the method should be changed before the page will be rendered.

 public function ___renderAsterisk(): string
    {
        return '<span class="asterisk">*</span>';
    }

This is a render function, which will be called during the render function of the form and the form will be rendered during the page will be rendered. So I want to change the output of the renderAsterisk() method inside the site/init.php and this does not work at all.

wire()->addHookAfter('Label::renderAsterisk', function ($event) {
        $event->return = ' - hooked aha :)';
    });

So this works fine if the renderAsterisk() method will be called inside the ready.php separately.

$foo = new Label();
    bd($foo->renderAsterisk());

But it does not work if the renderAsterisk() method is included inside another render function (in this case inside the render function of the form. This should be the issue of the problem.

To simplify: It works if the renderAsterisk() method will be called directly, but not if this method is included within another method and will be called there.

  • Like 1
Link to comment
Share on other sites

Conclusion:

It is not possible to hook a class that is not loaded directly on a page.

In my case, the Label class is loaded and used indirectly within the form, as it is used within the inputfield class and not directly in the form class. The form class is loaded directly onto the page and can be hooked - the Label class is not.

Link to comment
Share on other sites

  • Juergen changed the title to Hooks inside init.php to namespaced classes does not work [SOLVED]

I'd be interested, but I don't understand what you are saying. Neither what you said yesterday nor what you said today. What is "a class loaded directly on a page"? And what is "indirectly within a form" ?!

Link to comment
Share on other sites

  On 1/29/2025 at 5:08 PM, Juergen said:

I want to manipulate the output not inside the ready() but before page render

Expand  

It should not matter when/where you do it. The only thing that matters is that you attach your hook before you actually call the method that is hooked. If you call the method first and than add the hook it will obviously not return the hooked result but the plain result.

Link to comment
Share on other sites

It is a little bit difficult to explain:

Direct call of a class method on a page:

$form = new Form();
echo $form->render();

The method will be called directly on a page, so hooking Form::render works.

Direct call of a method of another class inside a method on a page:

public function ___render()
{
	$out = '';	
	$object = new Element();
	$out .= $object->renderElement();	
	return $out;
}

As you can see the method renderElement of the class Element will be called inside the render method of the Form class so Element::renderElement is hookable too.

The problem occurs if a method of the Element class should be hooked that is not directly called inside the given renderElement() method. 

class Element {

  public function ___test()
  {
      return 'Test';
  }

  public function ___renderElement{
      return 'Element';
  }

}

So if you want to hook Element::test it will not be triggered, because it will not executed inside the renderElement() method. That was my experience with the hook problem

 

 

 

Link to comment
Share on other sites

Here is the relevant part of the docs for reference: https://processwire.com/docs/modules/hooks/#how-can-i-define-my-own-hookable-methods

  Quote

Calls to a hookable method should always be without the 3 underscores, i.e.
$this->hookableMethod('a', 'b'); 
If for some reason you need to bypass ProcessWire's hook system for a given method, you can include the 3 underscores in the method call and no hooks will be executed.

Expand  

 

  • Like 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...