<?php /** * @package VikWP - Libraries * @subpackage adapter.event * @author E4J s.r.l. * @copyright Copyright (C) 2023 E4J s.r.l. All Rights Reserved. * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL * @link https://vikwp.com */ // No direct access defined('ABSPATH') or die('No script kiddies please!'); JLoader::import('adapter.event.event'); /** * Class to handle dispatching of events. * * @since 10.1.11 */ class JEventDispatcher { /** * Stores the singleton instance of the dispatcher. * * @var JEventDispatcher */ protected static $instance = null; /** * A list of observed classes. * * @var string */ protected $observers = array(); /** * Returns the global Event Dispatcher object, only creating it * if it doesn't already exist. * * @return JEventDispatcher The EventDispatcher object. */ public static function getInstance() { if (self::$instance === null) { self::$instance = new static(); } return self::$instance; } /** * Triggers an event by dispatching arguments to all observers that handle * the event and returning their return values. * * @param string $event The event to trigger. * @param array $args An array of arguments. * * @return array An array of results from each called function. */ public function trigger($event, $args = array()) { // make sure we are passing an array of arguments $args = (array) $args; // Push an empty value at the beginning of the list for being // manipulated at every execution of the filter. array_unshift($args, null); /** * Check whether the specified event uses the Joomla notation. * In that case, rebuild the event to look more similar to * WordPress hooks. * * @since 10.1.32 */ if (preg_match("/^on[A-Z]/", $event)) { $bc = $event; // obtain WP hook version $event = $this->getHook($event); // check whether someone is using a deprecated hook if (has_filter($bc)) { // trigger warning to inform the user that a manual adjustment is needed trigger_error( sprintf('expected <b>%s</b> hook, <b>%s</b> given', $event, $bc), E_USER_WARNING ); /** * Trigger the event for backward compatibility. * Assign the returned value to the first argument, * so that other plugins assigned to the correct * hook will be able to manipulate it. * * @since 10.1.32 * * @deprecated 10.2 Use the correct hook instead. */ $args[0] = apply_filters_ref_array($bc, $args); } } /** * Trigger the event. * Use `apply_filters_ref_array` instead of `do_action_ref_array` * to start supporting return values. * * @since 10.1.23 */ $return = apply_filters_ref_array($event, $args); if (is_null($return)) { // return an empty list as none manipulated the return value return array(); } // push the returned value within a list for Joomla standards return array($return); } /** * Registers an event handler to the event dispatcher * * @param string $event Name of the event to register handler for. * @param string $handler Name of the event handler. * * @return void * * @since 10.1.30 * @throws InvalidArgumentException */ public function register($event, $handler) { // are we dealing with a class or callback type handler? if (is_callable($handler)) { // function type event handler, let's attach it $method = array('event' => $event, 'handler' => $handler); $this->attach($method); } else if (class_exists($handler)) { // class type event handler, let's instantiate and attach it $this->attach(new $handler($this)); } else { throw new InvalidArgumentException('Invalid event handler.'); } } /** * Attach an observer object. * * @param mixed $observer An observer object to attach. * * @return void * * @since 10.1.29 */ public function attach($observer) { if (is_array($observer)) { if (!isset($observer['handler']) || !isset($observer['event']) || !is_callable($observer['handler'])) { return; } // make sure we haven't already attached this array as an observer foreach ($this->observers as $check) { if (is_array($check) && $check['event'] === $observer['event'] && $check['handler'] === $observer['handler']) { // callback already registered return; } } // obtain callback reflection and retrieve arguments $reflection = new ReflectionFunction($observer['handler']); $params = $reflection->getParameters(); // Register filter. // Always increase parameters counter by one in order to // accept the return value, which is never passed on Joomla. add_filter($observer['event'], $observer['handler'], 10, count($params) + 1); } else { // make sure we haven't already attached this object as an observer $class = get_class($observer); foreach ($this->observers as $check) { if ($check instanceof $class) { // class already observed return; } } // obtain class reflection $reflection = new ReflectionClass($observer); // retrieve all methods $methods = $reflection->getMethods(); // retrieve all JEvent methods $default = get_class_methods('JEvent'); foreach ($methods as $method) { // make sure the method is not already used by the parent class, // it is public and it is not static if (!in_array($method->name, $default) && $method->isPublic() && !$method->isStatic()) { // get method parameters $params = $method->getParameters(); /** * Refactor event name at runtime to support the * WordPress hooks notation. * * @since 10.1.32 */ $hook = $this->getHook($method->name); // Register filter. // Always increase parameters counter by one in order to // accept the return value, which is never passed on Joomla. add_filter($hook, array($observer, $method->name), 10, count($params) + 1); } } } // cache observer to avoid registering the same events more than once $this->observers[] = $observer; } /** * Checks whether the specified event uses the Joomla notation. * In that case, rebuild the event to look more similar to * WordPress hooks. * * @param string $event The event to check. * * @return string The modified event, if needed. * * @since 10.1.32 */ protected function getHook($event) { // make sure it starts with "on" if (preg_match("/^on[A-Z]/", $event)) { // remove initial "on" $event = preg_replace("/^on/", '', $event); // recover option from request $option = JFactory::getApplication()->input->get('option'); if ($option) { // clean "com_" from option $option = preg_replace("/^com_/", '', $option); // remove plugin name from event $event = preg_replace("/{$option}/i", '', $event); // always prepend plugin name at the beginning $event = strtolower($option) . $event; } // place an underscore between each camelCase $event = strtolower(preg_replace("/([a-z])([A-Z])/", '$1_$2', $event)); } return $event; } }