/libraries/src/Event/AbstractEvent.php

https://github.com/joomla/joomla-cms · PHP · 178 lines · 61 code · 23 blank · 94 comment · 12 complexity · bda9b53f98f2f15e05b9089c69a970d8 MD5 · raw file

  1. <?php
  2. /**
  3. * Joomla! Content Management System
  4. *
  5. * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org>
  6. * @license GNU General Public License version 2 or later; see LICENSE
  7. */
  8. namespace Joomla\CMS\Event;
  9. use BadMethodCallException;
  10. use Joomla\Event\Event;
  11. use Joomla\Event\Event as BaseEvent;
  12. use Joomla\String\Normalise;
  13. /**
  14. * This class implements the base Event object used system-wide to offer orthogonality. Core objects such as Models,
  15. * Controllers, etc create such events on-the-fly and dispatch them through the application's Dispatcher (colloquially
  16. * known as the "Joomla! plugin system"). This way a suitable plugin, typically a "system" plugin, can modify the
  17. * behaviour of any internal class, providing system-wide services such as tags, content versioning, comments or even
  18. * low-level services such as the implementation of created/modified/locked behaviours, record hit counter etc.
  19. *
  20. * You can create a new Event with something like this:
  21. *
  22. * $event = AbstractEvent::create('onModelBeforeSomething', $myModel, $arguments);
  23. *
  24. * You can access the subject object from your event Listener using $event['subject']. It is up to your listener to
  25. * determine whether it should apply its functionality against the subject.
  26. *
  27. * This AbstractEvent class implements a mutable event which is allowed to change its arguments at runtime. This is
  28. * generally unadvisable. It's best to use AbstractImmutableEvent instead and constrict all your interaction to the
  29. * subject class.
  30. *
  31. * @since 4.0.0
  32. */
  33. abstract class AbstractEvent extends BaseEvent
  34. {
  35. use CoreEventAware;
  36. /**
  37. * Creates a new CMS event object for a given event name and subject. The following arguments must be given:
  38. * subject object The subject of the event. This is the core object you are going to manipulate.
  39. * eventClass string The Event class name. If you do not provide it Joomla\CMS\Events\<eventNameWithoutOnPrefix>
  40. * will be used.
  41. *
  42. * @param string $eventName The name of the event, e.g. onTableBeforeLoad
  43. * @param array $arguments Additional arguments to pass to the event
  44. *
  45. * @return static
  46. *
  47. * @since 4.0.0
  48. * @throws BadMethodCallException If you do not provide a subject argument
  49. */
  50. public static function create(string $eventName, array $arguments = [])
  51. {
  52. // Get the class name from the arguments, if specified
  53. $eventClassName = '';
  54. if (isset($arguments['eventClass'])) {
  55. $eventClassName = $arguments['eventClass'];
  56. unset($arguments['eventClass']);
  57. }
  58. /**
  59. * If the class name isn't set/found determine it from the event name, e.g. TableBeforeLoadEvent from
  60. * the onTableBeforeLoad event name.
  61. */
  62. if (empty($eventClassName) || !class_exists($eventClassName, true)) {
  63. $bareName = strpos($eventName, 'on') === 0 ? substr($eventName, 2) : $eventName;
  64. $parts = Normalise::fromCamelCase($bareName, true);
  65. $eventClassName = __NAMESPACE__ . '\\' . ucfirst(array_shift($parts)) . '\\';
  66. $eventClassName .= implode('', $parts);
  67. $eventClassName .= 'Event';
  68. }
  69. // Make sure a non-empty subject argument exists and that it is an object
  70. if (!isset($arguments['subject']) || empty($arguments['subject']) || !\is_object($arguments['subject'])) {
  71. throw new BadMethodCallException("No subject given for the $eventName event");
  72. }
  73. // Create and return the event object
  74. if (class_exists($eventClassName, true)) {
  75. return new $eventClassName($eventName, $arguments);
  76. }
  77. /**
  78. * The detection code above failed. This is to be expected, it was written back when we only
  79. * had the Table events. It does not address most other core events. So, let's use our
  80. * fancier detection instead.
  81. */
  82. $eventClassName = self::getEventClassByEventName($eventName);
  83. if (!empty($eventClassName) && ($eventClassName !== Event::class)) {
  84. return new $eventClassName($eventName, $arguments);
  85. }
  86. return new GenericEvent($eventName, $arguments);
  87. }
  88. /**
  89. * Constructor. Overridden to go through the argument setters.
  90. *
  91. * @param string $name The event name.
  92. * @param array $arguments The event arguments.
  93. *
  94. * @since 4.0.0
  95. */
  96. public function __construct(string $name, array $arguments = [])
  97. {
  98. parent::__construct($name, $arguments);
  99. $this->arguments = [];
  100. foreach ($arguments as $argumentName => $value) {
  101. $this->setArgument($argumentName, $value);
  102. }
  103. }
  104. /**
  105. * Get an event argument value. It will use a getter method if one exists. The getters have the signature:
  106. *
  107. * get<ArgumentName>($value): mixed
  108. *
  109. * where:
  110. *
  111. * $value is the value currently stored in the $arguments array of the event
  112. * It returns the value to return to the caller.
  113. *
  114. * @param string $name The argument name.
  115. * @param mixed $default The default value if not found.
  116. *
  117. * @return mixed The argument value or the default value.
  118. *
  119. * @since 4.0.0
  120. */
  121. public function getArgument($name, $default = null)
  122. {
  123. $methodName = 'get' . ucfirst($name);
  124. $value = parent::getArgument($name, $default);
  125. if (method_exists($this, $methodName)) {
  126. return $this->{$methodName}($value);
  127. }
  128. return $value;
  129. }
  130. /**
  131. * Add argument to event. It will use a setter method if one exists. The setters have the signature:
  132. *
  133. * set<ArgumentName>($value): mixed
  134. *
  135. * where:
  136. *
  137. * $value is the value being set by the user
  138. * It returns the value to return to set in the $arguments array of the event.
  139. *
  140. * @param string $name Argument name.
  141. * @param mixed $value Value.
  142. *
  143. * @return $this
  144. *
  145. * @since 4.0.0
  146. */
  147. public function setArgument($name, $value)
  148. {
  149. $methodName = 'set' . ucfirst($name);
  150. if (method_exists($this, $methodName)) {
  151. $value = $this->{$methodName}($value);
  152. }
  153. return parent::setArgument($name, $value);
  154. }
  155. }