[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/src/Plugin/ -> CMSPlugin.php (source)

   1  <?php
   2  
   3  /**
   4   * Joomla! Content Management System
   5   *
   6   * @copyright  (C) 2007 Open Source Matters, Inc. <https://www.joomla.org>
   7   * @license    GNU General Public License version 2 or later; see LICENSE.txt
   8   */
   9  
  10  namespace Joomla\CMS\Plugin;
  11  
  12  use Joomla\CMS\Application\CMSApplicationInterface;
  13  use Joomla\CMS\Extension\PluginInterface;
  14  use Joomla\CMS\Factory;
  15  use Joomla\Event\AbstractEvent;
  16  use Joomla\Event\DispatcherAwareInterface;
  17  use Joomla\Event\DispatcherAwareTrait;
  18  use Joomla\Event\DispatcherInterface;
  19  use Joomla\Event\EventInterface;
  20  use Joomla\Event\SubscriberInterface;
  21  use Joomla\Registry\Registry;
  22  
  23  // phpcs:disable PSR1.Files.SideEffects
  24  \defined('JPATH_PLATFORM') or die;
  25  // phpcs:enable PSR1.Files.SideEffects
  26  
  27  /**
  28   * Plugin Class
  29   *
  30   * @since  1.5
  31   */
  32  abstract class CMSPlugin implements DispatcherAwareInterface, PluginInterface
  33  {
  34      use DispatcherAwareTrait;
  35  
  36      /**
  37       * A Registry object holding the parameters for the plugin
  38       *
  39       * @var    Registry
  40       * @since  1.5
  41       */
  42      public $params = null;
  43  
  44      /**
  45       * The name of the plugin
  46       *
  47       * @var    string
  48       * @since  1.5
  49       */
  50      protected $_name = null;
  51  
  52      /**
  53       * The plugin type
  54       *
  55       * @var    string
  56       * @since  1.5
  57       */
  58      protected $_type = null;
  59  
  60      /**
  61       * Affects constructor behavior. If true, language files will be loaded automatically.
  62       *
  63       * @var    boolean
  64       * @since  3.1
  65       */
  66      protected $autoloadLanguage = false;
  67  
  68      /**
  69       * Should I try to detect and register legacy event listeners, i.e. methods which accept unwrapped arguments? While
  70       * this maintains a great degree of backwards compatibility to Joomla! 3.x-style plugins it is much slower. You are
  71       * advised to implement your plugins using proper Listeners, methods accepting an AbstractEvent as their sole
  72       * parameter, for best performance. Also bear in mind that Joomla! 5.x onwards will only allow proper listeners,
  73       * removing support for legacy Listeners.
  74       *
  75       * @var    boolean
  76       * @since  4.0.0
  77       *
  78       * @deprecated
  79       */
  80      protected $allowLegacyListeners = true;
  81  
  82      /**
  83       * The application object
  84       *
  85       * @var    CMSApplicationInterface
  86       *
  87       * @since  4.2.0
  88       */
  89      private $application;
  90  
  91      /**
  92       * Constructor
  93       *
  94       * @param   DispatcherInterface  &$subject  The object to observe
  95       * @param   array                $config    An optional associative array of configuration settings.
  96       *                                          Recognized key values include 'name', 'group', 'params', 'language'
  97       *                                         (this list is not meant to be comprehensive).
  98       *
  99       * @since   1.5
 100       */
 101      public function __construct(&$subject, $config = array())
 102      {
 103          // Get the parameters.
 104          if (isset($config['params'])) {
 105              if ($config['params'] instanceof Registry) {
 106                  $this->params = $config['params'];
 107              } else {
 108                  $this->params = new Registry($config['params']);
 109              }
 110          }
 111  
 112          // Get the plugin name.
 113          if (isset($config['name'])) {
 114              $this->_name = $config['name'];
 115          }
 116  
 117          // Get the plugin type.
 118          if (isset($config['type'])) {
 119              $this->_type = $config['type'];
 120          }
 121  
 122          // Load the language files if needed.
 123          if ($this->autoloadLanguage) {
 124              $this->loadLanguage();
 125          }
 126  
 127          if (property_exists($this, 'app')) {
 128              @trigger_error('The application should be injected through setApplication() and requested through getApplication().', E_USER_DEPRECATED);
 129              $reflection = new \ReflectionClass($this);
 130              $appProperty = $reflection->getProperty('app');
 131  
 132              if ($appProperty->isPrivate() === false && \is_null($this->app)) {
 133                  $this->app = Factory::getApplication();
 134              }
 135          }
 136  
 137          if (property_exists($this, 'db')) {
 138              @trigger_error('The database should be injected through the DatabaseAwareInterface and trait.', E_USER_DEPRECATED);
 139              $reflection = new \ReflectionClass($this);
 140              $dbProperty = $reflection->getProperty('db');
 141  
 142              if ($dbProperty->isPrivate() === false && \is_null($this->db)) {
 143                  $this->db = Factory::getDbo();
 144              }
 145          }
 146  
 147          // Set the dispatcher we are to register our listeners with
 148          $this->setDispatcher($subject);
 149      }
 150  
 151      /**
 152       * Loads the plugin language file
 153       *
 154       * @param   string  $extension  The extension for which a language file should be loaded
 155       * @param   string  $basePath   The basepath to use
 156       *
 157       * @return  boolean  True, if the file has successfully loaded.
 158       *
 159       * @since   1.5
 160       */
 161      public function loadLanguage($extension = '', $basePath = JPATH_ADMINISTRATOR)
 162      {
 163          if (empty($extension)) {
 164              $extension = 'Plg_' . $this->_type . '_' . $this->_name;
 165          }
 166  
 167          $extension = strtolower($extension);
 168          $lang      = $this->getApplication() ? $this->getApplication()->getLanguage() : Factory::getLanguage();
 169  
 170          // If language already loaded, don't load it again.
 171          if ($lang->getPaths($extension)) {
 172              return true;
 173          }
 174  
 175          return $lang->load($extension, $basePath)
 176              || $lang->load($extension, JPATH_PLUGINS . '/' . $this->_type . '/' . $this->_name);
 177      }
 178  
 179      /**
 180       * Registers legacy Listeners to the Dispatcher, emulating how plugins worked under Joomla! 3.x and below.
 181       *
 182       * By default, this method will look for all public methods whose name starts with "on". It will register
 183       * lambda functions (closures) which try to unwrap the arguments of the dispatched Event into method call
 184       * arguments and call your on<Something> method. The result will be passed back to the Event into its 'result'
 185       * argument.
 186       *
 187       * This method additionally supports Joomla\Event\SubscriberInterface and plugins implementing this will be
 188       * registered to the dispatcher as a subscriber.
 189       *
 190       * @return  void
 191       *
 192       * @since   4.0.0
 193       */
 194      public function registerListeners()
 195      {
 196          // Plugins which are SubscriberInterface implementations are handled without legacy layer support
 197          if ($this instanceof SubscriberInterface) {
 198              $this->getDispatcher()->addSubscriber($this);
 199  
 200              return;
 201          }
 202  
 203          $reflectedObject = new \ReflectionObject($this);
 204          $methods = $reflectedObject->getMethods(\ReflectionMethod::IS_PUBLIC);
 205  
 206          /** @var \ReflectionMethod $method */
 207          foreach ($methods as $method) {
 208              if (substr($method->name, 0, 2) !== 'on') {
 209                  continue;
 210              }
 211  
 212              // Save time if I'm not to detect legacy listeners
 213              if (!$this->allowLegacyListeners) {
 214                  $this->registerListener($method->name);
 215  
 216                  continue;
 217              }
 218  
 219              /** @var \ReflectionParameter[] $parameters */
 220              $parameters = $method->getParameters();
 221  
 222              // If the parameter count is not 1 it is by definition a legacy listener
 223              if (\count($parameters) !== 1) {
 224                  $this->registerLegacyListener($method->name);
 225  
 226                  continue;
 227              }
 228  
 229              /** @var \ReflectionParameter $param */
 230              $param = array_shift($parameters);
 231              $paramName = $param->getName();
 232  
 233              // No type hint / type hint class not an event or parameter name is not "event"? It's a legacy listener.
 234              if ($paramName !== 'event' || !$this->parameterImplementsEventInterface($param)) {
 235                  $this->registerLegacyListener($method->name);
 236  
 237                  continue;
 238              }
 239  
 240              // Everything checks out, this is a proper listener.
 241              $this->registerListener($method->name);
 242          }
 243      }
 244  
 245      /**
 246       * Registers a legacy event listener, i.e. a method which accepts individual arguments instead of an AbstractEvent
 247       * in its arguments. This provides backwards compatibility to Joomla! 3.x-style plugins.
 248       *
 249       * This method will register lambda functions (closures) which try to unwrap the arguments of the dispatched Event
 250       * into old style method arguments and call your on<Something> method with them. The result will be passed back to
 251       * the Event, as an element into an array argument called 'result'.
 252       *
 253       * @param   string  $methodName  The method name to register
 254       *
 255       * @return  void
 256       *
 257       * @since   4.0.0
 258       */
 259      final protected function registerLegacyListener(string $methodName)
 260      {
 261          $this->getDispatcher()->addListener(
 262              $methodName,
 263              function (AbstractEvent $event) use ($methodName) {
 264                  // Get the event arguments
 265                  $arguments = $event->getArguments();
 266  
 267                  // Extract any old results; they must not be part of the method call.
 268                  $allResults = [];
 269  
 270                  if (isset($arguments['result'])) {
 271                      $allResults = $arguments['result'];
 272  
 273                      unset($arguments['result']);
 274                  }
 275  
 276                  // Convert to indexed array for unpacking.
 277                  $arguments = \array_values($arguments);
 278  
 279                  $result = $this->{$methodName}(...$arguments);
 280  
 281                  // Ignore null results
 282                  if ($result === null) {
 283                      return;
 284                  }
 285  
 286                  // Restore the old results and add the new result from our method call
 287                  $allResults[]    = $result;
 288                  $event['result'] = $allResults;
 289              }
 290          );
 291      }
 292  
 293      /**
 294       * Registers a proper event listener, i.e. a method which accepts an AbstractEvent as its sole argument. This is the
 295       * preferred way to implement plugins in Joomla! 4.x and will be the only possible method with Joomla! 5.x onwards.
 296       *
 297       * @param   string  $methodName  The method name to register
 298       *
 299       * @return  void
 300       *
 301       * @since   4.0.0
 302       */
 303      final protected function registerListener(string $methodName)
 304      {
 305          $this->getDispatcher()->addListener($methodName, [$this, $methodName]);
 306      }
 307  
 308      /**
 309       * Checks if parameter is typehinted to accept \Joomla\Event\EventInterface.
 310       *
 311       * @param   \ReflectionParameter  $parameter
 312       *
 313       * @return  boolean
 314       *
 315       * @since   4.0.0
 316       */
 317      private function parameterImplementsEventInterface(\ReflectionParameter $parameter): bool
 318      {
 319          $reflectionType = $parameter->getType();
 320  
 321          // Parameter is not typehinted.
 322          if ($reflectionType === null) {
 323              return false;
 324          }
 325  
 326          // Parameter is nullable.
 327          if ($reflectionType->allowsNull()) {
 328              return false;
 329          }
 330  
 331          // Handle standard typehints.
 332          if ($reflectionType instanceof \ReflectionNamedType) {
 333              return \is_a($reflectionType->getName(), EventInterface::class, true);
 334          }
 335  
 336          // Handle PHP 8 union types.
 337          if ($reflectionType instanceof \ReflectionUnionType) {
 338              foreach ($reflectionType->getTypes() as $type) {
 339                  if (!\is_a($type->getName(), EventInterface::class, true)) {
 340                      return false;
 341                  }
 342              }
 343  
 344              return true;
 345          }
 346  
 347          return false;
 348      }
 349  
 350      /**
 351       * Returns the internal application or null when not set.
 352       *
 353       * @return  CMSApplicationInterface|null
 354       *
 355       * @since   4.2.0
 356       */
 357      protected function getApplication(): ?CMSApplicationInterface
 358      {
 359          return $this->application;
 360      }
 361  
 362      /**
 363       * Sets the application to use.
 364       *
 365       * @param   CMSApplicationInterface  $application  The application
 366       *
 367       * @return  void
 368       *
 369       * @since   4.2.0
 370       */
 371      public function setApplication(CMSApplicationInterface $application): void
 372      {
 373          $this->application = $application;
 374      }
 375  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer