Completed
Push — master ( e6646d...4dc794 )
by
unknown
14:55
created

Dispatcher::reportDeprecatedSignalSlots()   A

Complexity

Conditions 6
Paths 10

Size

Total Lines 18
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

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