|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/* |
|
4
|
|
|
* Copyright (C) 2018 |
|
5
|
|
|
* Nathan Boiron <[email protected]> |
|
6
|
|
|
* Romain Canon <[email protected]> |
|
7
|
|
|
* |
|
8
|
|
|
* This file is part of the TYPO3 NotiZ project. |
|
9
|
|
|
* It is free software; you can redistribute it and/or modify it |
|
10
|
|
|
* under the terms of the GNU General Public License, either |
|
11
|
|
|
* version 3 of the License, or any later version. |
|
12
|
|
|
* |
|
13
|
|
|
* For the full copyright and license information, see: |
|
14
|
|
|
* http://www.gnu.org/licenses/gpl-3.0.html |
|
15
|
|
|
*/ |
|
16
|
|
|
|
|
17
|
|
|
namespace CuyZ\Notiz\Core\Definition\Tree\EventGroup\Event\Connection; |
|
18
|
|
|
|
|
19
|
|
|
use Closure; |
|
20
|
|
|
use CuyZ\Notiz\Core\Definition\Tree\AbstractDefinitionComponent; |
|
21
|
|
|
use CuyZ\Notiz\Core\Event\Runner\EventRunner; |
|
22
|
|
|
use CuyZ\Notiz\Core\Event\Runner\EventRunnerContainer; |
|
23
|
|
|
use CuyZ\Notiz\Core\Exception\ClassNotFoundException; |
|
24
|
|
|
use CuyZ\Notiz\Core\Exception\WrongFormatException; |
|
25
|
|
|
use TYPO3\CMS\Core\Utility\ArrayUtility; |
|
26
|
|
|
use TYPO3\CMS\Core\Utility\VersionNumberUtility; |
|
27
|
|
|
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; |
|
28
|
|
|
|
|
29
|
|
|
class Hook extends AbstractDefinitionComponent implements Connection |
|
30
|
|
|
{ |
|
31
|
|
|
const INTERNAL_HOOK_KEY = '__notiz'; |
|
32
|
|
|
|
|
33
|
|
|
/** |
|
34
|
|
|
* @var string |
|
35
|
|
|
* |
|
36
|
|
|
* @validate NotEmpty |
|
37
|
|
|
*/ |
|
38
|
|
|
protected $path; |
|
39
|
|
|
|
|
40
|
|
|
/** |
|
41
|
|
|
* @var string |
|
42
|
|
|
* |
|
43
|
|
|
* @validate Romm.ConfigurationObject:ClassExists |
|
44
|
|
|
*/ |
|
45
|
|
|
protected $interface; |
|
46
|
|
|
|
|
47
|
|
|
/** |
|
48
|
|
|
* @var string |
|
49
|
|
|
* |
|
50
|
|
|
* @validate RegularExpression(regularExpression=/^[_a-zA-Z]+\w*$/) |
|
51
|
|
|
*/ |
|
52
|
|
|
protected $method; |
|
53
|
|
|
|
|
54
|
|
|
/** |
|
55
|
|
|
* @return string |
|
56
|
|
|
*/ |
|
57
|
|
|
public function getPath() |
|
58
|
|
|
{ |
|
59
|
|
|
return $this->path; |
|
60
|
|
|
} |
|
61
|
|
|
|
|
62
|
|
|
/** |
|
63
|
|
|
* Registers the hook in TYPO3. |
|
64
|
|
|
* |
|
65
|
|
|
* Two registration methods exist. The first one is pretty clean, the other |
|
66
|
|
|
* probably already killed dozens of puppies. |
|
67
|
|
|
* |
|
68
|
|
|
* @param EventRunner $eventRunner |
|
69
|
|
|
*/ |
|
70
|
|
|
public function register(EventRunner $eventRunner) |
|
71
|
|
|
{ |
|
72
|
|
|
if ($this->hookIsRegistered()) { |
|
73
|
|
|
return; |
|
74
|
|
|
} |
|
75
|
|
|
|
|
76
|
|
|
if (!empty($this->interface) |
|
77
|
|
|
|| !empty($this->method) |
|
78
|
|
|
) { |
|
79
|
|
|
$closure = $this->preventEvalNeverIdealStuff($eventRunner); |
|
80
|
|
|
} else { |
|
81
|
|
|
$closure = function () use ($eventRunner) { |
|
82
|
|
|
return call_user_func_array($eventRunner->getCallable(), func_get_args()); |
|
83
|
|
|
}; |
|
84
|
|
|
} |
|
85
|
|
|
|
|
86
|
|
|
$this->injectHookInGlobalArray($closure); |
|
87
|
|
|
|
|
88
|
|
|
if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '8.0.0', '<')) { |
|
89
|
|
|
$this->injectHookInFrontendController($closure); |
|
|
|
|
|
|
90
|
|
|
} |
|
91
|
|
|
} |
|
92
|
|
|
|
|
93
|
|
|
/** |
|
94
|
|
|
* @param Closure|string $closure |
|
95
|
|
|
*/ |
|
96
|
|
|
protected function injectHookInGlobalArray($closure) |
|
97
|
|
|
{ |
|
98
|
|
|
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'] = ArrayUtility::setValueByPath( |
|
99
|
|
|
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'], |
|
100
|
|
|
$this->getFullPath(), |
|
101
|
|
|
$closure, |
|
102
|
|
|
'|' |
|
103
|
|
|
); |
|
104
|
|
|
} |
|
105
|
|
|
|
|
106
|
|
|
/** |
|
107
|
|
|
* @param Closure|string $closure |
|
108
|
|
|
* |
|
109
|
|
|
* @deprecated Must be removed when TYPO3 v7 is not supported anymore. |
|
110
|
|
|
*/ |
|
111
|
|
|
protected function injectHookInFrontendController($closure) |
|
112
|
|
|
{ |
|
113
|
|
|
$tsfe = $this->getTypoScriptFrontendController(); |
|
114
|
|
|
|
|
115
|
|
|
if ($tsfe) { |
|
|
|
|
|
|
116
|
|
|
$tsfe->TYPO3_CONF_VARS['SC_OPTIONS'] = ArrayUtility::setValueByPath( |
|
117
|
|
|
$tsfe->TYPO3_CONF_VARS['SC_OPTIONS'], |
|
118
|
|
|
$this->getFullPath(), |
|
119
|
|
|
$closure, |
|
120
|
|
|
'|' |
|
121
|
|
|
); |
|
122
|
|
|
} |
|
123
|
|
|
} |
|
124
|
|
|
|
|
125
|
|
|
/** |
|
126
|
|
|
* This method is called when a hook is bound to a class that must implement |
|
127
|
|
|
* an interface. |
|
128
|
|
|
* |
|
129
|
|
|
* In this case, to keep the handling we need to dynamically create some |
|
130
|
|
|
* proxy class that will call the internal API itself. |
|
131
|
|
|
* |
|
132
|
|
|
* If you don't want to lose your faith in humanity, you probably should not |
|
133
|
|
|
* read the code below. Really. |
|
134
|
|
|
* |
|
135
|
|
|
* @param EventRunner $eventRunner |
|
136
|
|
|
* @return string |
|
137
|
|
|
* |
|
138
|
|
|
* @throws ClassNotFoundException |
|
139
|
|
|
* @throws WrongFormatException |
|
140
|
|
|
*/ |
|
141
|
|
|
protected function preventEvalNeverIdealStuff(EventRunner $eventRunner) |
|
142
|
|
|
{ |
|
143
|
|
|
$className = 'notiz_hook_' . sha1($eventRunner->getEventDefinition()->getFullIdentifier()); |
|
144
|
|
|
|
|
145
|
|
|
$implements = $this->interface |
|
146
|
|
|
? 'implements ' . $this->interface |
|
147
|
|
|
: ''; |
|
148
|
|
|
|
|
149
|
|
|
$method = $this->method ?: 'run'; |
|
150
|
|
|
|
|
151
|
|
|
if ($this->interface |
|
152
|
|
|
&& !interface_exists($this->interface) |
|
153
|
|
|
) { |
|
154
|
|
|
throw ClassNotFoundException::eventHookInterfaceNotFound($this->interface, $this); |
|
155
|
|
|
} |
|
156
|
|
|
|
|
157
|
|
|
if (!preg_match('/^[_a-zA-Z]+\w*$/', $method)) { |
|
158
|
|
|
throw WrongFormatException::eventHookMethodNameWrongFormat($method, $this); |
|
159
|
|
|
} |
|
160
|
|
|
|
|
161
|
|
|
$classPhpCode = $this->anotherNonUsefulSystem($className, $implements, $method, $eventRunner); |
|
162
|
|
|
|
|
163
|
|
|
// Please lord, forgive me. |
|
164
|
|
|
eval($classPhpCode); |
|
|
|
|
|
|
165
|
|
|
|
|
166
|
|
|
return $className; |
|
167
|
|
|
} |
|
168
|
|
|
|
|
169
|
|
|
/** |
|
170
|
|
|
* @see \CuyZ\Notiz\Core\Definition\Tree\EventGroup\Event\Connection\Hook::preventEvalNeverIdealStuff |
|
171
|
|
|
* |
|
172
|
|
|
* @param string $className |
|
173
|
|
|
* @param string $implements |
|
174
|
|
|
* @param string $method |
|
175
|
|
|
* @param EventRunner $eventRunner |
|
176
|
|
|
* @return string |
|
177
|
|
|
*/ |
|
178
|
|
|
protected function anotherNonUsefulSystem($className, $implements, $method, EventRunner $eventRunner) |
|
179
|
|
|
{ |
|
180
|
|
|
$eventRunnerContainerClass = EventRunnerContainer::class; |
|
181
|
|
|
|
|
182
|
|
|
return <<<PHP |
|
183
|
|
|
class $className $implements |
|
184
|
|
|
{ |
|
185
|
|
|
public function $method(...\$arguments) |
|
186
|
|
|
{ |
|
187
|
|
|
\$eventRunner = $eventRunnerContainerClass::getInstance()->get('{$eventRunner->getEventDefinition()->getFullIdentifier()}'); |
|
188
|
|
|
|
|
189
|
|
|
call_user_func_array(\$eventRunner->getCallable(), \$arguments); |
|
190
|
|
|
} |
|
191
|
|
|
} |
|
192
|
|
|
PHP; |
|
193
|
|
|
} |
|
194
|
|
|
|
|
195
|
|
|
/** |
|
196
|
|
|
* @return bool |
|
197
|
|
|
*/ |
|
198
|
|
|
protected function hookIsRegistered() |
|
199
|
|
|
{ |
|
200
|
|
|
return ArrayUtility::isValidPath($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'], $this->getFullPath(), '|'); |
|
201
|
|
|
} |
|
202
|
|
|
|
|
203
|
|
|
/** |
|
204
|
|
|
* @return string |
|
205
|
|
|
*/ |
|
206
|
|
|
protected function getFullPath() |
|
207
|
|
|
{ |
|
208
|
|
|
return $this->path . '|' . self::INTERNAL_HOOK_KEY; |
|
209
|
|
|
} |
|
210
|
|
|
|
|
211
|
|
|
/** |
|
212
|
|
|
* @return TypoScriptFrontendController |
|
213
|
|
|
*/ |
|
214
|
|
|
protected function getTypoScriptFrontendController() |
|
215
|
|
|
{ |
|
216
|
|
|
return $GLOBALS['TSFE']; |
|
217
|
|
|
} |
|
218
|
|
|
} |
|
219
|
|
|
|
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.