Completed
Push — master ( 00e474...9d3fbd )
by Michael
04:26
created

Oledrion_plugins   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 269
Duplicated Lines 20.82 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 0
Metric Value
dl 56
loc 269
rs 9.6
c 0
b 0
f 0
wmc 32
lcom 1
cbo 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A getInstance() 0 9 2
A __construct() 0 5 1
A loadPlugins() 0 5 1
A loadClass() 0 21 4
B loadPluginsFiles() 0 12 5
C fireAction() 27 27 7
C fireFilter() 29 29 8
A isUnplug() 0 11 2
A unplugFromEvent() 0 9 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
/*
3
 You may not change or alter any portion of this comment or credits
4
 of supporting developers from this source code or any supporting source code
5
 which is considered copyrighted (c) material of the original comment or credit authors.
6
7
 This program is distributed in the hope that it will be useful,
8
 but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10
*/
11
12
/**
13
 * oledrion
14
 *
15
 * @copyright   {@link http://xoops.org/ XOOPS Project}
16
 * @license     {@link http://www.fsf.org/copyleft/gpl.html GNU public license}
17
 * @author      Hervé Thouzard (http://www.herve-thouzard.com/)
18
 */
19
20
/**
21
 * Gestion des plugins du module
22
 *
23
 * On distingue les "filtres" (plugin qui reçoit du contenu qu'il peut modifier et dont le retour est passé au filtre suivant)
24
 * des "actions" (plugin qui est généralement appelé sur un évènement et qui ne doit rien modifier).
25
 *
26
 * Chaque plugin dispose d'une priorité de 1 à 5, 1 étant la priorité la plus haute, 5 la plus basse
27
 *
28
 * Dans le répertoire plugins on a 2 sous répertoires, "actions" et "filters", en fonction du type du plugin
29
 *
30
 * Chaque plugin doit se trouver dans son propre sous-répertoire, par exemple :
31
 *     /xoops/modules/oledrion/plugins/pdf/
32
 *
33
 * Cela permet aux plugins d'avoir plusieurs fichiers et de ne pas les mélanger avec les autres plugins
34
 *
35
 * Le module scrute ces 2 répertoires ("actions" et "filters") pour charger les plugins.
36
 * Chaque répertoire doit contenir un script "plugins.php" qui permet la descriptions des plugins qui se trouvent dans ce répertoire.
37
 * A titre d'exemple, voir ceux fournits de base avec le module
38
 *
39
 * Les modèles de classe à étendre se trouvent dans /xoops/modules/oledrion/plugins/modules/oledrion_action.php et oledrion_filter.php
40
 *
41
 * @since 2.31
42
 */
43
class Oledrion_plugins
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
44
{
45
    /**
46
     * Dictionnaire des évènements
47
     */
48
    const EVENT_ON_PRODUCT_CREATE = 'onProductCreate';
49
    const EVENT_ON_CATEGORY_CREATE = 'onCategoryCreate';
50
    const EVENT_ON_PRODUCT_DOWNLOAD = 'onProductDownload';
51
    // **************************************************************
52
53
    /**
54
     * Types d'évènements
55
     */
56
    const PLUGIN_ACTION = 0;
57
    const PLUGIN_FILTER = 1;
58
59
    /**
60
     * Nom du script Php inclut qui contient l'inscription des plugins
61
     */
62
    const PLUGIN_SCRIPT_NAME = 'plugins.php';
63
64
    /**
65
     * Dans le fichier Php qui contient l'inscription des plugins, méthode à appeler pour récupérer la liste des plugins
66
     */
67
    const PLUGIN_DESCRIBE_METHOD = 'registerEvents';
68
69
    /**
70
     * Nom de la variable de session qui contient la liste des plugins détachés
71
     */
72
    const PLUGIN_UNPLUG_SESSION_NAME = 'oledrion_plugins';
73
74
    /**
75
     * Priorités des plugins
76
     * @var constant
77
     */
78
    const EVENT_PRIORITY_1 = 1; // Priorité la plus haute
79
    const EVENT_PRIORITY_2 = 2;
80
    const EVENT_PRIORITY_3 = 3;
81
    const EVENT_PRIORITY_4 = 4;
82
    const EVENT_PRIORITY_5 = 5; // Priorité la plus basse
83
84
    /**
85
     * Utilisé pour construire le nom de la classe
86
     */
87
    private $pluginsTypeLabel = array(self::PLUGIN_ACTION => 'Action', self::PLUGIN_FILTER => 'Filter');
88
89
    /**
90
     * Nom des classes qu'il faut étendre en tant que plugin
91
     */
92
    private $pluginsClassName = array(
93
        self::PLUGIN_ACTION => 'oledrion_action',
94
        self::PLUGIN_FILTER => 'oledrion_filter'
95
    );
96
97
    /**
98
     * Nom de chacun des dossiers en fonction du type de plugin
99
     */
100
    private $pluginsTypesFolder = array(self::PLUGIN_ACTION => 'actions', self::PLUGIN_FILTER => 'filters');
101
102
    /**
103
     * Contient l'unique instance de l'objet
104
     * @var object
105
     */
106
    private static $instance = false;
0 ignored issues
show
Unused Code introduced by
The property $instance is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
107
108
    /**
109
     * Liste des évènements
110
     * @var array
111
     */
112
    private static $events = array();
113
114
    /**
115
     * Retourne l'instance unique de la classe
116
     *
117
     * @return object
118
     */
119
    public static function getInstance()
120
    {
121
        static $instance;
122
        if (null === $instance) {
123
            $instance = new static();
124
        }
125
126
        return $instance;
127
    }
128
129
    /**
130
     * Chargement des 2 types de plugins
131
     *
132
     */
133
    private function __construct()
134
    {
135
        $this->events = array();
136
        $this->loadPlugins();
137
    }
138
139
    /**
140
     * Chargement des plugins (actions et filtres)
141
     * @return void
142
     */
143
    public function loadPlugins()
144
    {
145
        $this->loadPluginsFiles(OLEDRION_PLUGINS_PATH . $this->pluginsTypesFolder[self::PLUGIN_ACTION], self::PLUGIN_ACTION);
146
        $this->loadPluginsFiles(OLEDRION_PLUGINS_PATH . $this->pluginsTypesFolder[self::PLUGIN_FILTER], self::PLUGIN_FILTER);
147
    }
148
149
    /**
150
     * 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
151
     * @param  string  $fullPathName Chemin complet vers le fichier (répertoire + nom)
152
     * @param  integer $type         Type de plugin recherché (action ou filtre)
153
     * @param  string  $pluginFolder Le nom du répertoire dans lequel se trouve le fichier (le "dernier nom")
154
     * @return void
155
     */
156
    private function loadClass($fullPathName, $type, $pluginFolder)
157
    {
158
        require_once $fullPathName;
159
        $className = strtolower($pluginFolder) . $this->pluginsTypeLabel[$type];
160
        if (class_exists($className) && get_parent_class($className) == $this->pluginsClassName[$type]) {
161
            // TODO: Vérifier que l'évènement n'est pas déjà en mémoire
162
            $events = call_user_func(array($className, self::PLUGIN_DESCRIBE_METHOD));
163
            foreach ($events as $event) {
164
                $eventName                                         = $event[0];
165
                $eventPriority                                     = $event[1];
166
                $fileToInclude                                     = OLEDRION_PLUGINS_PATH . $this->pluginsTypesFolder[$type] . '/' . $pluginFolder . '/' . $event[2];
167
                $classToCall                                       = $event[3];
168
                $methodToCall                                      = $event[4];
169
                $this->events[$type][$eventName][$eventPriority][] = array(
170
                    'fullPathName' => $fileToInclude,
171
                    'className'    => $classToCall,
172
                    'method'       => $methodToCall
173
                );
174
            }
175
        }
176
    }
177
178
    /**
179
     * Part à la recherche d'un type de plugin dans les répertoires
180
     *
181
     * @param  string  $path La racine
182
     * @param  integer $type Le type de plugin recherché (action ou filtre)
183
     * @return void
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  object|Oledrion_parameters $parameters  Les paramètres à passer à chaque plugin
0 ignored issues
show
Documentation introduced by
Should the type for parameter $parameters not be null|Oledrion_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...
203
     * @return object                     L'objet lui même pour chaîner
204
     */
205 View Code Duplication
    public function fireAction($eventToFire, Oledrion_parameters $parameters = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
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(array($event['className'], $event['method']), $parameters);
226
                unset($class);
227
            }
228
        }
229
230
        return $this;
231
    }
232
233
    /**
234
     * Déclenchement d'un filtre et appel des plugins liés
235
     *
236
     * @param  string                     $eventToFire Le filtre appelé
237
     * @param  object|Oledrion_parameters $parameters  Les paramètres à passer à chaque plugin
238
     * @return object                     Le contenu de l'objet passé en paramètre
0 ignored issues
show
Documentation introduced by
Should the return type not be Oledrion_plugins|Oledrion_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...
239
     */
240 View Code Duplication
    public function fireFilter($eventToFire, Oledrion_parameters $parameters)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
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]); // Tri par priorité
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(array($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  integer $eventType
274
     * @param  string  $eventToFire
275
     * @param  string  $fullPathName
276
     * @param  string  $className
277
     * @param  string  $method
278
     * @return boolean
279
     */
280
    public function isUnplug($eventType, $eventToFire, $fullPathName, $className, $method)
0 ignored issues
show
Coding Style introduced by
isUnplug uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
281
    {
282
        $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...
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  integer $eventType
296
     * @param  string  $eventToFire
297
     * @param  string  $fullPathName
298
     * @param  string  $className
299
     * @param  string  $method
300
     * @return void
301
     */
302
    public function unplugFromEvent($eventType, $eventToFire, $fullPathName, $className, $method)
0 ignored issues
show
Coding Style introduced by
unplugFromEvent uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
303
    {
304
        $unplug = array();
305
        if (isset($_SESSION[self::PLUGIN_UNPLUG_SESSION_NAME])) {
306
            $unplug = $_SESSION[self::PLUGIN_UNPLUG_SESSION_NAME];
307
        }
308
        $unplug[$eventType][$eventToFire][$fullPathName][$className][$method] = true;
309
        $_SESSION[self::PLUGIN_UNPLUG_SESSION_NAME]                           = $unplug;
310
    }
311
}
312