Plugin::fireAction()   B
last analyzed

Complexity

Conditions 7
Paths 8

Size

Total Lines 26
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 16
dl 0
loc 26
rs 8.8333
c 0
b 0
f 0
cc 7
nc 8
nop 2
1
<?php
2
3
namespace XoopsModules\Oledrion;
4
5
/*
6
 You may not change or alter any portion of this comment or credits
7
 of supporting developers from this source code or any supporting source code
8
 which is considered copyrighted (c) material of the original comment or credit authors.
9
10
 This program is distributed in the hope that it will be useful,
11
 but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
*/
14
15
/**
16
 * oledrion
17
 *
18
 * @copyright   {@link https://xoops.org/ XOOPS Project}
19
 * @license     {@link http://www.fsf.org/copyleft/gpl.html GNU public license}
20
 * @author      Hervé Thouzard (http://www.herve-thouzard.com/)
21
 */
22
23
use XoopsModules\Oledrion;
24
25
/**
26
 * Gestion des plugins du module
27
 *
28
 * On distingue les "filtres" (plugin qui reçoit du contenu qu'il peut modifier et dont le retour est passé au filtre suivant)
29
 * des "actions" (plugin qui est généralement appelé sur un évènement et qui ne doit rien modifier).
30
 *
31
 * Chaque plugin dispose d'une priorité de 1 à 5, 1 étant la priorité la plus haute, 5 la plus basse
32
 *
33
 * Dans le répertoire plugins on a 2 sous répertoires, "actions" et "filters", en fonction du type du plugin
34
 *
35
 * Chaque plugin doit se trouver dans son propre sous-répertoire, par exemple :
36
 *     /xoops/modules/oledrion/plugins/pdf/
37
 *
38
 * Cela permet aux plugins d'avoir plusieurs fichiers et de ne pas les mélanger avec les autres plugins
39
 *
40
 * Le module scrute ces 2 répertoires ("actions" et "filters") pour charger les plugins.
41
 * Chaque répertoire doit contenir un script "plugins.php" qui permet la descriptions des plugins qui se trouvent dans ce répertoire.
42
 * A titre d'exemple, voir ceux fournits de base avec le module
43
 *
44
 * Les modèles de classe à étendre se trouvent dans /xoops/modules/oledrion/plugins/modules/oledrion_action.php et OledrionFilter.php
45
 *
46
 * @since 2.31
47
 */
48
class Plugin
49
{
50
    /**
51
     * Dictionnaire des évènements
52
     */
53
    const EVENT_ON_PRODUCT_CREATE = 'onProductCreate';
54
    const EVENT_ON_CATEGORY_CREATE = 'onCategoryCreate';
55
    const EVENT_ON_PRODUCT_DOWNLOAD = 'onProductDownload';
56
    // **************************************************************
57
58
    /**
59
     * Types d'évènements
60
     */
61
    const PLUGIN_ACTION = 0;
62
    const PLUGIN_FILTER = 1;
63
64
    /**
65
     * Nom du script Php inclut qui contient l'inscription des plugins
66
     */
67
    const PLUGIN_SCRIPT_NAME = 'plugins.php';
68
69
    /**
70
     * Dans le fichier Php qui contient l'inscription des plugins, méthode à appeler pour récupérer la liste des plugins
71
     */
72
    const PLUGIN_DESCRIBE_METHOD = 'registerEvents';
73
74
    /**
75
     * Nom de la variable de session qui contient la liste des plugins détachés
76
     */
77
    const PLUGIN_UNPLUG_SESSION_NAME = 'oledrion_plugins';
78
79
    /**
80
     * Priorités des plugins
81
     */
82
    const EVENT_PRIORITY_1 = 1; // Priorité la plus haute
83
    const EVENT_PRIORITY_2 = 2;
84
    const EVENT_PRIORITY_3 = 3;
85
    const EVENT_PRIORITY_4 = 4;
86
    const EVENT_PRIORITY_5 = 5; // Priorité la plus basse
87
88
    /**
89
     * Utilisé pour construire le nom de la classe
90
     */
91
    private $pluginsTypeLabel = [self::PLUGIN_ACTION => 'Action', self::PLUGIN_FILTER => 'Filter'];
92
93
    /**
94
     * Nom des classes qu'il faut étendre en tant que plugin
95
     */
96
    private $pluginsClassName = [
97
        self::PLUGIN_ACTION => 'oledrion_action',
98
        self::PLUGIN_FILTER => 'oledrion_filter',
99
    ];
100
101
    /**
102
     * Nom de chacun des dossiers en fonction du type de plugin
103
     */
104
    private $pluginsTypesFolder = [self::PLUGIN_ACTION => 'actions', self::PLUGIN_FILTER => 'filters'];
105
106
    /**
107
     * Contient l'unique instance de l'objet
108
     * @var Plugin
109
     */
110
    private static $instance = false;
0 ignored issues
show
introduced by
The private property $instance is not used, and could be removed.
Loading history...
111
112
    /**
113
     * Liste des évènements
114
     * @var array
115
     */
116
    private static $events = [];
117
118
    /**
119
     * Retourne l'instance unique de la classe
120
     *
121
     * @return Plugin
122
     */
123
    public static function getInstance()
124
    {
125
        static $instance;
126
        if (null === $instance) {
127
            $instance = new static();
128
        }
129
130
        return $instance;
131
    }
132
133
    /**
134
     * Chargement des 2 types de plugins
135
     */
136
    private function __construct()
137
    {
138
        $this->events = [];
139
        $this->loadPlugins();
140
    }
141
142
    /**
143
     * Chargement des plugins (actions et filtres)
144
     */
145
    public function loadPlugins()
146
    {
147
        $this->loadPluginsFiles(OLEDRION_PLUGINS_PATH . $this->pluginsTypesFolder[self::PLUGIN_ACTION], self::PLUGIN_ACTION);
148
        $this->loadPluginsFiles(OLEDRION_PLUGINS_PATH . $this->pluginsTypesFolder[self::PLUGIN_FILTER], self::PLUGIN_FILTER);
149
    }
150
151
    /**
152
     * Vérifie que le fichier Php passé en paramètre contient bien une classe de filtre ou d'action et si c'est le cas, le charge dans la liste des plugins
153
     * @param  string $fullPathName Chemin complet vers le fichier (répertoire + nom)
154
     * @param int     $type         Type de plugin recherché (action ou filtre)
155
     * @param  string $pluginFolder Le nom du répertoire dans lequel se trouve le fichier (le "dernier nom")
156
     */
157
    private function loadClass($fullPathName, $type, $pluginFolder)
158
    {
159
        require_once $fullPathName;
160
        $className = mb_strtolower($pluginFolder) . $this->pluginsTypeLabel[$type];
161
        if (class_exists($className) && get_parent_class($className) == $this->pluginsClassName[$type]) {
162
            // TODO: Check that the event is not already in memory
163
            $events = call_user_func([$className, self::PLUGIN_DESCRIBE_METHOD]);
164
            foreach ($events as $event) {
165
                $eventName                                         = $event[0];
166
                $eventPriority                                     = $event[1];
167
                $fileToInclude                                     = OLEDRION_PLUGINS_PATH . $this->pluginsTypesFolder[$type] . '/' . $pluginFolder . '/' . $event[2];
168
                $classToCall                                       = $event[3];
169
                $methodToCall                                      = $event[4];
170
                $this->events[$type][$eventName][$eventPriority][] = [
171
                    'fullPathName' => $fileToInclude,
172
                    'className'    => $classToCall,
173
                    'method'       => $methodToCall,
174
                ];
175
            }
176
        }
177
    }
178
179
    /**
180
     * Part à la recherche d'un type de plugin dans les répertoires
181
     *
182
     * @param  string $path La racine
183
     * @param int     $type Le type de plugin recherché (action ou filtre)
184
     */
185
    private function loadPluginsFiles($path, $type)
186
    {
187
        $objects = new \DirectoryIterator($path);
188
        foreach ($objects as $object) {
189
            if ($object->isDir() && !$object->isDot()) {
190
                $file = $path . '/' . $object->current() . '/' . self::PLUGIN_SCRIPT_NAME;
191
                if (file_exists($file)) {
192
                    $this->loadClass($file, $type, $object->current());
193
                }
194
            }
195
        }
196
    }
197
198
    /**
199
     * Déclenchement d'une action et appel des plugins liés
200
     *
201
     * @param  string     $eventToFire L'action déclenchée
202
     * @param  Parameters $parameters  Les paramètres à passer à chaque plugin
203
     * @return Plugin                     L'objet lui même pour chaîner
204
     */
205
    public function fireAction($eventToFire, Parameters $parameters = null)
206
    {
207
        if (!isset($this->events[self::PLUGIN_ACTION][$eventToFire])) {
208
            trigger_error(sprintf(_OLEDRION_PLUGINS_ERROR_1, $eventToFire));
209
210
            return $this;
211
        }
212
        ksort($this->events[self::PLUGIN_ACTION][$eventToFire]); // Tri par priorité
213
        foreach ($this->events[self::PLUGIN_ACTION][$eventToFire] as $priority => $events) {
214
            foreach ($events as $event) {
215
                if ($this->isUnplug(self::PLUGIN_ACTION, $eventToFire, $event['fullPathName'], $event['className'], $event['method'])) {
216
                    continue;
217
                }
218
                require_once $event['fullPathName'];
219
                if (!class_exists($event['className'])) {
220
                    $class = new $event['className']();
221
                }
222
                if (!method_exists($event['className'], $event['method'])) {
223
                    continue;
224
                }
225
                call_user_func([$event['className'], $event['method']], $parameters);
226
                unset($class);
227
            }
228
        }
229
230
        return $this;
231
    }
232
233
    /**
234
     * Triggering a filter and calling linked plugins
235
     *
236
     * @param  string          $eventToFire The called filter
237
     * @param  Parameters|null $parameters  The parameters to be passed to each plugin
238
     * @return Parameters|Plugin            The content of the object passed as parameter
239
     */
240
    public function fireFilter($eventToFire, Parameters $parameters = null)
241
    {
242
        if (!isset($this->events[self::PLUGIN_FILTER][$eventToFire])) {
243
            trigger_error(sprintf(_OLEDRION_PLUGINS_ERROR_1, $eventToFire));
244
245
            return $this;
246
        }
247
        ksort($this->events[self::PLUGIN_FILTER][$eventToFire]); // Sort by priority
248
        foreach ($this->events[self::PLUGIN_FILTER][$eventToFire] as $priority => $events) {
249
            foreach ($events as $event) {
250
                if ($this->isUnplug(self::PLUGIN_FILTER, $eventToFire, $event['fullPathName'], $event['className'], $event['method'])) {
251
                    continue;
252
                }
253
                require_once $event['fullPathName'];
254
                if (!class_exists($event['className'])) {
255
                    $class = new $event['className']();
256
                }
257
                if (!method_exists($event['className'], $event['method'])) {
258
                    continue;
259
                }
260
                call_user_func([$event['className'], $event['method']], $parameters);
261
                unset($class);
262
            }
263
        }
264
265
        if (null !== $parameters) {
266
            return $parameters;
267
        }
268
    }
269
270
    /**
271
     * Indique si un plugin s'est détaché d'un évènement particulier
272
     *
273
     * @param int     $eventType
274
     * @param  string $eventToFire
275
     * @param  string $fullPathName
276
     * @param  string $className
277
     * @param  string $method
278
     * @return bool
279
     */
280
    public function isUnplug($eventType, $eventToFire, $fullPathName, $className, $method)
281
    {
282
        $unplug = [];
0 ignored issues
show
Unused Code introduced by
The assignment to $unplug is dead and can be removed.
Loading history...
283
        if (isset($_SESSION[self::PLUGIN_UNPLUG_SESSION_NAME])) {
284
            $unplug = $_SESSION[self::PLUGIN_UNPLUG_SESSION_NAME];
285
        } else {
286
            return false;
287
        }
288
289
        return isset($unplug[$eventType][$eventToFire][$fullPathName][$className][$method]);
290
    }
291
292
    /**
293
     * Permet à un plugin de se détacher d'un évènement
294
     *
295
     * @param int     $eventType
296
     * @param  string $eventToFire
297
     * @param  string $fullPathName
298
     * @param  string $className
299
     * @param  string $method
300
     */
301
    public function unplugFromEvent($eventType, $eventToFire, $fullPathName, $className, $method)
302
    {
303
        $unplug = [];
304
        if (isset($_SESSION[self::PLUGIN_UNPLUG_SESSION_NAME])) {
305
            $unplug = $_SESSION[self::PLUGIN_UNPLUG_SESSION_NAME];
306
        }
307
        $unplug[$eventType][$eventToFire][$fullPathName][$className][$method] = true;
308
        $_SESSION[self::PLUGIN_UNPLUG_SESSION_NAME]                           = $unplug;
309
    }
310
}
311