Hook   A
last analyzed

Complexity

Total Complexity 16

Size/Duplication

Total Lines 165
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 42
dl 0
loc 165
rs 10
c 0
b 0
f 0
wmc 16

8 Methods

Rating   Name   Duplication   Size   Complexity  
A getPath() 0 3 1
A anotherNonUsefulSystem() 0 10 1
A injectHookInGlobalArray() 0 7 1
A getTypoScriptFrontendController() 0 3 1
A getFullPath() 0 3 1
A preventEvalNeverIdealStuff() 0 26 6
A register() 0 17 4
A hookIsRegistered() 0 3 1
1
<?php
2
declare(strict_types=1);
3
4
/*
5
 * Copyright (C)
6
 * Nathan Boiron <[email protected]>
7
 * Romain Canon <[email protected]>
8
 *
9
 * This file is part of the TYPO3 NotiZ project.
10
 * It is free software; you can redistribute it and/or modify it
11
 * under the terms of the GNU General Public License, either
12
 * version 3 of the License, or any later version.
13
 *
14
 * For the full copyright and license information, see:
15
 * http://www.gnu.org/licenses/gpl-3.0.html
16
 */
17
18
namespace CuyZ\Notiz\Core\Definition\Tree\EventGroup\Event\Connection;
19
20
use Closure;
21
use CuyZ\Notiz\Core\Definition\Tree\AbstractDefinitionComponent;
22
use CuyZ\Notiz\Core\Event\Runner\EventRunner;
23
use CuyZ\Notiz\Core\Event\Runner\EventRunnerContainer;
24
use CuyZ\Notiz\Core\Exception\ClassNotFoundException;
25
use CuyZ\Notiz\Core\Exception\WrongFormatException;
26
use TYPO3\CMS\Core\Utility\ArrayUtility;
27
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
0 ignored issues
show
Bug introduced by
The type TYPO3\CMS\Frontend\Contr...criptFrontendController was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
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(): string
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
89
    /**
90
     * @param Closure|string $closure
91
     */
92
    protected function injectHookInGlobalArray($closure)
93
    {
94
        $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'] = ArrayUtility::setValueByPath(
95
            $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'],
96
            $this->getFullPath(),
97
            $closure,
98
            '|'
99
        );
100
    }
101
102
    /**
103
     * This method is called when a hook is bound to a class that must implement
104
     * an interface.
105
     *
106
     * In this case, to keep the handling we need to dynamically create some
107
     * proxy class that will call the internal API itself.
108
     *
109
     * If you don't want to lose your faith in humanity, you probably should not
110
     * read the code below. Really.
111
     *
112
     * @param EventRunner $eventRunner
113
     * @return string
114
     *
115
     * @throws ClassNotFoundException
116
     * @throws WrongFormatException
117
     */
118
    protected function preventEvalNeverIdealStuff(EventRunner $eventRunner): string
119
    {
120
        $className = 'notiz_hook_' . sha1($eventRunner->getEventDefinition()->getFullIdentifier());
121
122
        $implements = $this->interface
123
            ? 'implements ' . $this->interface
124
            : '';
125
126
        $method = $this->method ?: 'run';
127
128
        if ($this->interface
129
            && !interface_exists($this->interface)
130
        ) {
131
            throw ClassNotFoundException::eventHookInterfaceNotFound($this->interface, $this);
132
        }
133
134
        if (!preg_match('/^[_a-zA-Z]+\w*$/', $method)) {
135
            throw WrongFormatException::eventHookMethodNameWrongFormat($method, $this);
136
        }
137
138
        $classPhpCode = $this->anotherNonUsefulSystem($className, $implements, $method, $eventRunner);
139
140
        // Please lord, forgive me.
141
        eval($classPhpCode);
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
142
143
        return $className;
144
    }
145
146
    /**
147
     * @see \CuyZ\Notiz\Core\Definition\Tree\EventGroup\Event\Connection\Hook::preventEvalNeverIdealStuff
148
     *
149
     * @param string $className
150
     * @param string $implements
151
     * @param string $method
152
     * @param EventRunner $eventRunner
153
     * @return string
154
     */
155
    protected function anotherNonUsefulSystem(string $className, string $implements, string $method, EventRunner $eventRunner): string
156
    {
157
        $eventRunnerContainerClass = EventRunnerContainer::class;
158
159
        return <<<PHP
160
class $className $implements
161
{
162
    public function $method(...\$arguments)
163
    {
164
        \$eventRunner = $eventRunnerContainerClass::getInstance()->get('{$eventRunner->getEventDefinition()->getFullIdentifier()}');
165
166
        call_user_func_array(\$eventRunner->getCallable(), \$arguments);
167
    }
168
}
169
PHP;
170
    }
171
172
    /**
173
     * @return bool
174
     */
175
    protected function hookIsRegistered(): bool
176
    {
177
        return ArrayUtility::isValidPath($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'], $this->getFullPath(), '|');
178
    }
179
180
    /**
181
     * @return string
182
     */
183
    protected function getFullPath(): string
184
    {
185
        return $this->path . '|' . self::INTERNAL_HOOK_KEY;
186
    }
187
188
    /**
189
     * @return TypoScriptFrontendController
190
     */
191
    protected function getTypoScriptFrontendController(): TypoScriptFrontendController
192
    {
193
        return $GLOBALS['TSFE'];
194
    }
195
}
196