references_plugins::getInstance()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 0
loc 9
rs 9.9666
c 0
b 0
f 0
1
<?php
2
/**
3
 * ****************************************************************************
4
 * references - MODULE FOR XOOPS
5
 * Copyright (c) Hervé Thouzard of Instant Zero (http://www.instant-zero.com)
6
 *
7
 * You may not change or alter any portion of this comment or credits
8
 * of supporting developers from this source code or any supporting source code
9
 * which is considered copyrighted (c) material of the original comment or credit authors.
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
 * @copyright       Hervé Thouzard of Instant Zero (http://www.instant-zero.com)
15
 * @license         http://www.fsf.org/copyleft/gpl.html GNU public license
16
 * @package         references
17
 * @author          Hervé Thouzard of Instant Zero (http://www.instant-zero.com)
18
 *
19
 * ****************************************************************************
20
 */
21
22
/**
23
 * Gestion des plugins du module
24
 *
25
 */
26
class references_plugins
27
{
28
    /**
29
     * Dictionnaire des évènements
30
     */
31
    const EVENT_ON_REFERENCE_CREATE = 'onReferenceCreate';
32
    const EVENT_ON_CATEGORY_CREATE  = 'onCategoryCreate';
33
34
    // Pour limiter les dépendances
35
    const MODULE_DIRNAME = REFERENCES_DIRNAME;
36
37
    /**
38
     * Types d'évènements
39
     */
40
    const PLUGIN_ACTION = 0;
41
    const PLUGIN_FILTER = 1;
42
43
    /**
44
     * Nom du script Php inclut qui contient l'inscription des plugins
45
     */
46
    const PLUGIN_SCRIPT_NAME = 'plugins.php';
47
48
    /**
49
     * Dans le fichier Php qui contient l'inscription des plugins, méthode à appeler pour récupérer la liste des plugins
50
     */
51
    const PLUGIN_DESCRIBE_METHOD = 'registerEvents';
52
53
    /**
54
     * Nom de la variable de session qui contient la liste des plugins détachés
55
     */
56
    const PLUGIN_UNPLUG_SESSION_NAME = 'references_plugins';
57
58
    /**
59
     * Priorités des plugins
60
     * @var constant
61
     */
62
    const EVENT_PRIORITY_1 = 1;    // Priorité la plus haute
63
    const EVENT_PRIORITY_2 = 2;
64
    const EVENT_PRIORITY_3 = 3;
65
    const EVENT_PRIORITY_4 = 4;
66
    const EVENT_PRIORITY_5 = 5;    // Priorité la plus basse
67
68
    /**
69
     * Utilisé pour construire le nom de la classe
70
     */
71
    private $pluginsTypeLabel = array(self::PLUGIN_ACTION => 'Action', self::PLUGIN_FILTER => 'Filter');
72
73
    /**
74
     * Nom des classes qu'il faut étendre en tant que plugin
75
     */
76
    private $pluginsClassName = array(self::PLUGIN_ACTION => 'references_action', self::PLUGIN_FILTER => 'references_filter');
77
78
    /**
79
     * Nom de chacun des dossiers en fonction du type de plugin
80
     */
81
    private $pluginsTypesFolder = array(self::PLUGIN_ACTION => 'actions', self::PLUGIN_FILTER => 'filters');
82
83
    /**
84
     * Contient l'unique instance de l'objet
85
     * @var object
86
     */
87
    private static $instance = false;
88
89
    /**
90
     * Liste des évènements
91
     * @var array
92
     */
93
    private static $events = array();
94
95
    /**
96
     * Retourne l'instance unique de la classe
97
     *
98
     * @return object
99
     */
100
    public static function getInstance()
101
    {
102
        static $instance;
103
        if (null === $instance) {
104
            $instance = new static();
105
        }
106
107
        return $instance;
108
    }
109
110
    /**
111
     * Chargement des 2 types de plugins
112
     *
113
     */
114
    private function __construct()
115
    {
116
        $this->events = array();
117
        $this->loadPlugins();
118
    }
119
120
    /**
121
     * Chargement des plugins (actions et filtres)
122
     * @return void
123
     */
124
    public function loadPlugins()
125
    {
126
        $this->loadPluginsFiles(REFERENCES_PLUGINS_PATH . $this->pluginsTypesFolder[self::PLUGIN_ACTION], self::PLUGIN_ACTION);
127
        $this->loadPluginsFiles(REFERENCES_PLUGINS_PATH . $this->pluginsTypesFolder[self::PLUGIN_FILTER], self::PLUGIN_FILTER);
128
    }
129
130
    /**
131
     * 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
132
     * @param string  $fullPathName Chemin complet vers le fichier (répertoire + nom)
133
     * @param integer $type         Type de plugin recherché (action ou filtre)
134
     * @param string  $pluginFolder Le nom du répertoire dans lequel se trouve le fichier (le "dernier nom")
135
     * @return void
136
     */
137
    private function loadClass($fullPathName, $type, $pluginFolder)
138
    {
139
        require_once $fullPathName;
140
        // Du style referencesRegionalizationFilter
141
        $className = self::MODULE_DIRNAME . ucfirst(strtolower($pluginFolder)) . $this->pluginsTypeLabel[$type];
142
        if (class_exists($className) && get_parent_class($className) == $this->pluginsClassName[$type]) {
143
            // TODO: Vérifier que l'évènement n'est pas déjà en mémoire
144
            $events = call_user_func(array($className, self::PLUGIN_DESCRIBE_METHOD));
145
            foreach ($events as $event) {
146
                $eventName                                         = $event[0];
147
                $eventPriority                                     = $event[1];
148
                $fileToInclude                                     = REFERENCES_PLUGINS_PATH . $this->pluginsTypesFolder[$type] . DIRECTORY_SEPARATOR . $pluginFolder . DIRECTORY_SEPARATOR . $event[2];
149
                $classToCall                                       = $event[3];
150
                $methodToCall                                      = $event[4];
151
                $this->events[$type][$eventName][$eventPriority][] = array('fullPathName' => $fileToInclude, 'className' => $classToCall, 'method' => $methodToCall);
152
            }
153
        }
154
    }
155
156
    /**
157
     * Part à la recherche d'un type de plugin dans les répertoires
158
     *
159
     * @param  string $path La racine
160
     * @param integer $type Le type de plugin recherché (action ou filtre)
161
     * @return void
162
     */
163
    private function loadPluginsFiles($path, $type)
164
    {
165
        $objects = new DirectoryIterator($path);
166
        foreach ($objects as $object) {
167
            if ($object->isDir() && !$object->isDot()) {
168
                $file = $path . DIRECTORY_SEPARATOR . $object->current() . DIRECTORY_SEPARATOR . self::PLUGIN_SCRIPT_NAME;
169
                if (file_exists($file)) {
170
                    $this->loadClass($file, $type, $object->current());
171
                }
172
            }
173
        }
174
    }
175
176
    /**
177
     * Déclenchement d'une action et appel des plugins liés
178
     *
179
     * @param string                       $eventToFire L'action déclenchée
180
     * @param object|references_parameters $parameters  Les paramètres à passer à chaque plugin
0 ignored issues
show
Documentation introduced by
Should the type for parameter $parameters not be null|references_parameters?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
181
     * @return object L'objet lui même pour chaîner
182
     */
183
    public function fireAction($eventToFire, references_parameters $parameters = null)
184
    {
185 View Code Duplication
        if (!isset($this->events[self::PLUGIN_ACTION][$eventToFire])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
186
            trigger_error(sprintf(_MD_REFERENCES_PLUGINS_ERROR_1, $eventToFire));
187
188
            return $this;
189
        }
190
        ksort($this->events[self::PLUGIN_ACTION][$eventToFire]);    // Tri par priorit�
191
        foreach ($this->events[self::PLUGIN_ACTION][$eventToFire] as $priority => $events) {
192
            foreach ($events as $event) {
193 View Code Duplication
                if ($this->isUnplug(self::PLUGIN_ACTION, $eventToFire, $event['fullPathName'], $event['className'], $event['method'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
194
                    continue;
195
                }
196
                require_once $event['fullPathName'];
197
                if (!class_exists($event['className'])) {
198
                    $class = new $event['className'];
199
                }
200
                if (!method_exists($event['className'], $event['method'])) {
201
                    continue;
202
                }
203
                call_user_func(array($event['className'], $event['method']), $parameters);
204
                unset($class);
205
            }
206
        }
207
208
        return $this;
209
    }
210
211
    /**
212
     * Déclenchement d'un filtre et appel des plugins liés
213
     *
214
     * @param string                       $eventToFire Le filtre appelé
215
     * @param object|references_parameters $parameters  Les paramètres à passer à chaque plugin
216
     * @return object Le contenu de l'objet passé en paramètre
0 ignored issues
show
Documentation introduced by
Should the return type not be references_plugins|references_parameters|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
217
     */
218
    public function fireFilter($eventToFire, references_parameters $parameters)
219
    {
220 View Code Duplication
        if (!isset($this->events[self::PLUGIN_FILTER][$eventToFire])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
221
            trigger_error(sprintf(_MD_REFERENCES_PLUGINS_ERROR_1, $eventToFire));
222
223
            return $this;
224
        }
225
        ksort($this->events[self::PLUGIN_FILTER][$eventToFire]);    // Tri par priorité
226
        foreach ($this->events[self::PLUGIN_FILTER][$eventToFire] as $priority => $events) {
227
            foreach ($events as $event) {
228 View Code Duplication
                if ($this->isUnplug(self::PLUGIN_FILTER, $eventToFire, $event['fullPathName'], $event['className'], $event['method'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
229
                    continue;
230
                }
231
                require_once $event['fullPathName'];
232
                if (!method_exists($event['className'], $event['method'])) {
233
                    continue;
234
                }
235
                //if (!class_exists($event['className'])) {
236
                $class = new $event['className'];
237
                //}
238
                $class->$event['method']($parameters);
239
                //call_user_func(array($event['className'], $event['method']), $parameters);
240
                unset($class);
241
            }
242
        }
243
244
        if (!is_null($parameters)) {
245
            return $parameters;
246
        }
247
    }
248
249
    /**
250
     * Indique si un plugin s'est détaché d'un évènement particulier
251
     *
252
     * @param  integer $eventType
253
     * @param  string  $eventToFire
254
     * @param  string  $fullPathName
255
     * @param  string  $className
256
     * @param  string  $method
257
     * @return boolean
258
     */
259
    public function isUnplug($eventType, $eventToFire, $fullPathName, $className, $method)
260
    {
261
        $unplug = array();
0 ignored issues
show
Unused Code introduced by
$unplug is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
262
        if (isset($_SESSION[self::PLUGIN_UNPLUG_SESSION_NAME])) {
263
            $unplug = $_SESSION[self::PLUGIN_UNPLUG_SESSION_NAME];
264
        } else {
265
            return false;
266
        }
267
268
        return isset($unplug[$eventType][$eventToFire][$fullPathName][$className][$method]);
269
    }
270
271
    /**
272
     * Permet à un plugin de se détacher d'un évènement
273
     *
274
     * @param  integer $eventType
275
     * @param  string  $eventToFire
276
     * @param  string  $fullPathName
277
     * @param  string  $className
278
     * @param  string  $method
279
     * @return void
280
     */
281
    public function unplugFromEvent($eventType, $eventToFire, $fullPathName, $className, $method)
282
    {
283
        $unplug = array();
284
        if (isset($_SESSION[self::PLUGIN_UNPLUG_SESSION_NAME])) {
285
            $unplug = $_SESSION[self::PLUGIN_UNPLUG_SESSION_NAME];
286
        }
287
        $unplug[$eventType][$eventToFire][$fullPathName][$className][$method] = true;
288
        $_SESSION[self::PLUGIN_UNPLUG_SESSION_NAME]                           = $unplug;
289
    }
290
}
291