[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |