-
Notifications
You must be signed in to change notification settings - Fork 674
/
Dispatcher.php
170 lines (157 loc) · 7.18 KB
/
Dispatcher.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
<?php
namespace TYPO3\CMS\Extbase\SignalSlot;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* A dispatcher which dispatches signals by calling its registered slot methods
* and passing them the method arguments which were originally passed to the
* signal method.
*
* @api
*/
class Dispatcher implements \TYPO3\CMS\Core\SingletonInterface
{
/**
* @var bool
*/
protected $isInitialized = false;
/**
* @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
*/
protected $objectManager;
/**
* Information about all slots connected a certain signal.
* Indexed by [$signalClassName][$signalMethodName] and then numeric with an
* array of information about the slot
*
* @var array
*/
protected $slots = array();
/**
* Initializes this object.
*
* This methods needs to be used as alternative to inject aspects.
* Since this dispatches is used very early when the ObjectManager
* is not fully initialized (especially concerning caching framework),
* this is the only way.
*
* @return void
*/
public function initializeObject()
{
if (!$this->isInitialized) {
$this->objectManager = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\ObjectManager::class);
$this->isInitialized = true;
}
}
/**
* Connects a signal with a slot.
* One slot can be connected with multiple signals by calling this method multiple times.
*
* @param string $signalClassName Name of the class containing the signal
* @param string $signalName Name of the signal
* @param mixed $slotClassNameOrObject Name of the class containing the slot or the instantiated class or a Closure object
* @param string $slotMethodName Name of the method to be used as a slot. If $slotClassNameOrObject is a Closure object, this parameter is ignored
* @param bool $passSignalInformation If set to TRUE, the last argument passed to the slot will be information about the signal (EmitterClassName::signalName)
* @throws \InvalidArgumentException
* @return void
* @api
*/
public function connect($signalClassName, $signalName, $slotClassNameOrObject, $slotMethodName = '', $passSignalInformation = true)
{
$class = null;
$object = null;
if (is_object($slotClassNameOrObject)) {
$object = $slotClassNameOrObject;
$method = $slotClassNameOrObject instanceof \Closure ? '__invoke' : $slotMethodName;
} else {
if ($slotMethodName === '') {
throw new \InvalidArgumentException('The slot method name must not be empty (except for closures).', 1229531659);
}
$class = $slotClassNameOrObject;
$method = $slotMethodName;
}
$slot = array(
'class' => $class,
'method' => $method,
'object' => $object,
'passSignalInformation' => $passSignalInformation === true
);
if (!is_array($this->slots[$signalClassName][$signalName]) || !in_array($slot, $this->slots[$signalClassName][$signalName])) {
$this->slots[$signalClassName][$signalName][] = $slot;
}
}
/**
* Dispatches a signal by calling the registered Slot methods
*
* @param string $signalClassName Name of the class containing the signal
* @param string $signalName Name of the signal
* @param array $signalArguments arguments passed to the signal method
* @return mixed
* @throws Exception\InvalidSlotException if the slot is not valid
* @throws Exception\InvalidSlotReturnException if a slot returns invalid arguments (too few or return value is not an array)
* @api
*/
public function dispatch($signalClassName, $signalName, array $signalArguments = array())
{
$this->initializeObject();
if (!isset($this->slots[$signalClassName][$signalName])) {
return $signalArguments;
}
foreach ($this->slots[$signalClassName][$signalName] as $slotInformation) {
if (isset($slotInformation['object'])) {
$object = $slotInformation['object'];
} else {
if (!isset($this->objectManager)) {
throw new Exception\InvalidSlotException(sprintf('Cannot dispatch %s::%s to class %s. The object manager is not yet available in the Signal Slot Dispatcher and therefore it cannot dispatch classes.', $signalClassName, $signalName, $slotInformation['class']), 1298113624);
}
if (!$this->objectManager->isRegistered($slotInformation['class'])) {
throw new Exception\InvalidSlotException('The given class "' . $slotInformation['class'] . '" is not a registered object.', 1245673367);
}
$object = $this->objectManager->get($slotInformation['class']);
}
if (!method_exists($object, $slotInformation['method'])) {
throw new Exception\InvalidSlotException('The slot method ' . get_class($object) . '->' . $slotInformation['method'] . '() does not exist.', 1245673368);
}
$preparedSlotArguments = $signalArguments;
if ($slotInformation['passSignalInformation'] === true) {
$preparedSlotArguments[] = $signalClassName . '::' . $signalName;
}
$slotReturn = call_user_func_array(array($object, $slotInformation['method']), $preparedSlotArguments);
if ($slotReturn) {
if (!is_array($slotReturn)) {
throw new Exception\InvalidSlotReturnException('The slot method ' . get_class($object) . '->' . $slotInformation['method'] . '()\'s return value is of an not allowed type ('
. gettype($slotReturn) . ').', 1376683067);
} elseif (count($slotReturn) !== count($signalArguments)) {
throw new Exception\InvalidSlotReturnException('The slot method ' . get_class($object) . '->' . $slotInformation['method'] . '() returned a different number ('
. count($slotReturn) . ') of arguments, than it received (' . count($signalArguments) . ').', 1376683066);
} else {
$signalArguments = $slotReturn;
}
}
}
return $signalArguments;
}
/**
* Returns all slots which are connected with the given signal
*
* @param string $signalClassName Name of the class containing the signal
* @param string $signalName Name of the signal
* @return array An array of arrays with slot information
* @api
*/
public function getSlots($signalClassName, $signalName)
{
return isset($this->slots[$signalClassName][$signalName]) ? $this->slots[$signalClassName][$signalName] : array();
}
}