processwire-issues icon indicating copy to clipboard operation
processwire-issues copied to clipboard

Allow to use string object name for hooks

Open trk opened this issue 1 year ago • 4 comments

Allow to use string class name as object when using hooks


use Foo\Bar\Controller;

// This is not working
$wire->addHook('/hello', Controller::class, 'indexHello');

// This one is working
$wire->addHook('/hello', new Controller, 'indexHello');

<?php

namespace Foo\Bar;

class Controller
{

public function indexHello(HookEvent $event)
{
     header('Content-Type: application/json');
     json_encode(['message' => 'String class name not working :(']);
     exit;
}

}

trk avatar Dec 20 '24 00:12 trk

@trk An instance of a class can have methods called, but a class name on its own can't, so isn't going to be useful here, unless maybe referring to a static method. ProcessWire knows nothing about your custom class or how it needs to be constructed, initialized, dependencies, etc. So what I'd suggest instead is this:

$wire->addHook('/hello', function($e) {
  (new Controller())->indexHello();
}); 

Or encapsulate it all in your class. Extend Wire and attach your hooks within the class:

class Controller extends Wire {
  public function __construct() {
    $this->addHook('/hello', $this, 'indexHello');
  }
  public function indexHello($e) { ... }
}
new Controller();

Lastly, do you even need a class at all?

$wire->addHook('/hello', function($e) {
  header('Content-Type: application/json');
  return json_encode(['message' => 'hello world']);
}); 

ryancramerdesign avatar Dec 20 '24 14:12 ryancramerdesign

@ryancramerdesign

Above example usage is only for example to show you the simplest usage

$wire->addHook('/user/register', User::class, 'register');
$wire->addHook('/user/login', User::class, 'login');
$wire->addHook('/user/logout', User::class, 'logout');
$wire->addHook('/user/reset-password', User::class, 'resetPassWord');
$wire->addHook('/product/{id}/similar', ProductPage::class, 'similar');
$wire->addHook('/product/{id}/slider', ProductPage::class, 'slider');

Checking $toObject variable at https://github.com/processwire/processwire/blob/3cc76cc886a49313b4bfb9a1a904bd88d11b7cb7/wire/core/WireHooks.php#L1091 line can help

if (is_string($toObject) && class_exists($toObject)) {
     $toObject = new $toObject;
}

The main purpose of this usage is don't call class until the hook triggered.

if you don't want to extend this object usage, I will use your first suggestion

$wire->addHook('/hello', function($e) {
  (new Controller())->indexHello();
}); 

trk avatar Dec 20 '24 16:12 trk

@trk are you ok with Ryan's answer? If yes, can we close this?

matjazpotocnik avatar Jan 04 '25 21:01 matjazpotocnik

@matjazpotocnik I think we can use Class names as string for hooks, waiting for @ryancramerdesign answer

trk avatar Jan 05 '25 10:01 trk