Listener   A
last analyzed

Complexity

Total Complexity 26

Size/Duplication

Total Lines 190
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 26
lcom 1
cbo 0
dl 0
loc 190
ccs 0
cts 93
cp 0
rs 10
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A attach() 0 9 3
A detach() 0 8 3
A notify() 0 6 2
A getIterator() 0 4 1
A count() 0 4 1
A setPluginsAtLevel() 0 4 1
A getPluginsAtLevel() 0 8 2
A hook() 0 11 3
A callPlugin() 0 14 4
B loadPlugin() 0 27 4
A loadPlugins() 0 6 2
1
<?php
2
3
/**
4
 *
5
 * This file is part of the Apix Project.
6
 *
7
 * (c) Franck Cassedanne <franck at ouarz.net>
8
 *
9
 * @license     http://opensource.org/licenses/BSD-3-Clause  New BSD License
10
 *
11
 */
12
13
namespace Apix;
14
15
// Pluggable
16
class Listener implements \SplSubject, \IteratorAggregate, \Countable
17
{
18
    /**
19
     * @var array of SplObjectStorage
20
     */
21
    protected $observers = array();
22
23
    /**
24
     * @var array of plugins
25
     */
26
    protected static $plugins = array();
27
28
     /**
29
     * Attaches a new observer
30
     *
31
     * @param  \SplObserver $observer Any object implementing SplObserver
32
     * @return void|false
33
     */
34
    public function attach(\SplObserver $observer)
35
    {
36
        foreach ($this->observers as $attached) {
37
            if ($attached === $observer) {
38
                return;
39
            }
40
        }
41
        $this->observers[] = $observer;
42
    }
43
44
    /**
45
     * Detaches an existing observer
46
     *
47
     * @param \SplObserver $observer Any object implementing SplObserver
48
     */
49
    public function detach(\SplObserver $observer)
50
    {
51
        foreach ($this->observers as $key => $attached) {
52
            if ($attached === $observer) {
53
                unset($this->observers[$key]);
54
            }
55
        }
56
    }
57
58
    /**
59
     * Notifies all the observers
60
     *
61
     * @return void
62
     */
63
    public function notify()
64
    {
65
        foreach ($this->observers as $observer) {
66
            $observer->update($this);
67
        }
68
    }
69
70
    /**
71
     * Gets the IteratorAggregate
72
     *
73
     * @return \Iterator
74
     */
75
    public function getIterator()
76
    {
77
        return $this->observers;
78
    }
79
80
    /**
81
     * Countable
82
     *
83
     * @return integer
84
     */
85
    public function count()
86
    {
87
        return count($this->observers);
88
    }
89
90
    /**
91
     * Sets the plugins at the specified level
92
     *
93
     * @param  array $key     The level key
94
     * @param  array $plugins An array of plugins
95
     * @return void
96
     */
97
    public function setPluginsAtLevel($key, array $plugins)
98
    {
99
        self::$plugins[$key] = $plugins;
100
    }
101
102
    /**
103
     * Gets the plugins at the specified level
104
     *
105
     * @param  string      $key
106
     * @return array|false An array of plugins
107
     */
108
    public function getPluginsAtLevel($key)
109
    {
110
        if (!isset(self::$plugins[$key])) {
111
            return false;
112
        }
113
114
        return self::$plugins[$key];
115
    }
116
117
    /**
118
     * Calls the plugins of the specified level and type
119
     *
120
     * @param string $level
121
     * @param string $type
122
     */
123
    public function hook($level, $type)
124
    {
125
        $plugins = $this->getPluginsAtLevel($level);
126
        if (isset($plugins[$type])) {
127
            foreach ($plugins[$type] as $plugin => $args) {
128
                $obs = $this->callPlugin($plugin, $args);
0 ignored issues
show
Documentation introduced by
$plugin is of type integer|string, but the function expects a object<Apix\mix>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
129
                $this->attach($obs);
130
                $obs->update($this);
131
            }
132
        }
133
   }
134
135
    /**
136
     * Calls and executes the plugin
137
     *
138
     * @param mix $plugin
139
     * @param mix $args
140
     */
141
    private function callPlugin($plugin, $args)
142
    {
143
        if (is_int($plugin)) {
144
            return $args instanceof \Closure
145
                    ? $args()
146
                    : new $args();
147
        }
148
149
        $args = $args instanceof \Closure
150
                ? $args()
151
                : $args;
152
153
        return new $plugin($args);
154
    }
155
156
    /**
157
     * Load the specified plugin
158
     *
159
     * @param  string           $name The plugin name to load
0 ignored issues
show
Bug introduced by
There is no parameter named $name. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
160
     * @param  mix              $mix  The plugin options
161
     * @return boolean          True on success, false otherwise
162
     * @throws RuntimeException If the plugin does not exists
163
     * @throws DomainException  If the plugin does not have a $hook property.
164
     */
165
    public function loadPlugin($key, $mix = null)
166
    {
167
       $name = is_int($key) ? $mix : $key;
168
169
       if ( !class_exists($name) ) {
170
            throw new \RuntimeException(
171
                sprintf('Plugin class "%s" not found', $name)
172
            );
173
        }
174
175
        if (!isset($name::$hook)) {
176
            throw new \DomainException(
177
                sprintf(
178
                    'Plugin class "%s" does not have the expected '
179
                    . 'static \'hook\' property',
180
                    $name
181
                )
182
            );
183
        }
184
185
        $level = $name::$hook[0];
186
        $type = $name::$hook[1];
187
188
        self::$plugins[$level][$type][$key] = $mix;
189
190
        return true;
191
    }
192
193
    /**
194
     * Load all the plugins
195
     *
196
     * @param array $plugins The array of plugins to load
197
     */
198
    public function loadPlugins(array $plugins)
199
    {
200
        foreach ($plugins as $key => $mix) {
201
            $this->loadPlugin($key, $mix);
202
        }
203
    }
204
205
}
206