Registry   A
last analyzed

Complexity

Total Complexity 36

Size/Duplication

Total Lines 245
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 36
eloc 76
c 2
b 0
f 0
dl 0
loc 245
rs 9.52

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __destruct() 0 3 1
A getOwnershipTag() 0 3 1
B setTaglessMode() 0 12 9
A isTaglessModeOwner() 0 7 2
A _close() 0 5 2
A _add() 0 7 2
B add() 0 30 8
A getNextResponse() 0 15 4
A __construct() 0 9 3
A parseTag() 0 11 3
A close() 0 5 1
1
<?php
2
3
/**
4
 * ~~summary~~
5
 *
6
 * ~~description~~
7
 *
8
 * PHP version 5
9
 *
10
 * @category  Net
11
 * @package   PEAR2_Net_RouterOS
12
 * @author    Vasil Rangelov <[email protected]>
13
 * @copyright 2011 Vasil Rangelov
14
 * @license   http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
15
 * @version   GIT: $Id$
16
 * @link      http://pear2.php.net/PEAR2_Net_RouterOS
17
 */
18
/**
19
 * The namespace declaration.
20
 */
21
namespace PEAR2\Net\RouterOS;
22
23
/**
24
 * Uses shared memory to keep responses in.
25
 */
26
use PEAR2\Cache\SHM;
27
28
/**
29
 * A RouterOS registry.
30
 *
31
 * Provides functionality for managing the request/response flow. Particularly
32
 * useful in persistent connections.
33
 *
34
 * Note that this class is not meant to be called directly.
35
 *
36
 * @category Net
37
 * @package  PEAR2_Net_RouterOS
38
 * @author   Vasil Rangelov <[email protected]>
39
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
40
 * @link     http://pear2.php.net/PEAR2_Net_RouterOS
41
 */
42
class Registry
43
{
44
    /**
45
     * The storage.
46
     *
47
     * @var SHM
48
     */
49
    protected $shm;
50
51
    /**
52
     * ID of request. Populated at first instance in request.
53
     *
54
     * @var int
55
     */
56
    protected static $requestId = -1;
57
58
    /**
59
     * ID to be given to next instance, after incrementing it.
60
     *
61
     * @var int
62
     */
63
    protected static $instanceIdSeed = -1;
64
65
    /**
66
     * ID of instance within the request.
67
     *
68
     * @var int
69
     */
70
    protected $instanceId;
71
72
    /**
73
     * Creates a registry.
74
     *
75
     * @param string $uri An URI to bind the registry to.
76
     */
77
    public function __construct($uri)
78
    {
79
        $this->shm = SHM::factory(__CLASS__ . ' ' . $uri);
80
        if (-1 === self::$requestId) {
81
            self::$requestId = $this->shm->add('requestId', 0)
82
                ? 0 : $this->shm->inc('requestId');
83
        }
84
        $this->instanceId = ++self::$instanceIdSeed;
85
        $this->shm->add('responseBuffer_' . $this->getOwnershipTag(), array());
86
    }
87
88
    /**
89
     * Parses a tag.
90
     *
91
     * Parses a tag to reveal the ownership part of it, and the original tag.
92
     *
93
     * @param string|null $tag The tag (as received) to parse.
94
     *
95
     * @return array<int,string|null> An array with
96
     *     the first member being the ownership tag, and
97
     *     the second one being the original tag.
98
     */
99
    public static function parseTag($tag)
100
    {
101
        if (null === $tag) {
102
            return array(null, null);
103
        }
104
        $result = explode('__', $tag, 2);
105
        $result[0] .= '__';
106
        if ('' === $result[1]) {
107
            $result[1] = null;
108
        }
109
        return $result;
110
    }
111
112
    /**
113
     * Checks if this instance is the tagless mode owner.
114
     *
115
     * @return bool TRUE if this instance is the tagless mode owner, FALSE
116
     *     otherwise.
117
     */
118
    public function isTaglessModeOwner()
119
    {
120
        $this->shm->lock('taglessModeOwner');
121
        $result = $this->shm->exists('taglessModeOwner')
122
            && $this->getOwnershipTag() === $this->shm->get('taglessModeOwner');
123
        $this->shm->unlock('taglessModeOwner');
124
        return $result;
125
    }
126
127
    /**
128
     * Sets the "tagless mode" setting.
129
     *
130
     * While in tagless mode, this instance will claim ownership of any
131
     * responses without a tag. While not in this mode, any requests without a
132
     * tag will be given to all instances.
133
     *
134
     * Regardless of mode, if the type of the response is
135
     * {@link Response::TYPE_FATAL}, it will be given to all instances.
136
     *
137
     * @param bool $taglessMode TRUE to claim tagless ownership, FALSE to
138
     *     release such ownership, if taken.
139
     *
140
     * @return bool TRUE on success, FALSE on failure.
141
     */
142
    public function setTaglessMode($taglessMode)
143
    {
144
        return $taglessMode
145
            ?   ($this->shm->lock('taglessMode')
146
                && $this->shm->lock('taglessModeOwner')
147
                && $this->shm->add('taglessModeOwner', $this->getOwnershipTag())
148
                && $this->shm->unlock('taglessModeOwner'))
149
            :   ($this->isTaglessModeOwner()
150
                && $this->shm->lock('taglessModeOwner')
151
                && $this->shm->delete('taglessModeOwner')
152
                && $this->shm->unlock('taglessModeOwner')
153
                && $this->shm->unlock('taglessMode'));
154
    }
155
156
    /**
157
     * Get the ownership tag for this instance.
158
     *
159
     * @return string The ownership tag for this registry instance.
160
     */
161
    public function getOwnershipTag()
162
    {
163
        return self::$requestId . '_' . $this->instanceId . '__';
164
    }
165
166
    /**
167
     * Add a response to the registry.
168
     *
169
     * @param Response    $response     The response to add. The caller of this
170
     *     function is responsible for ensuring that the ownership tag and the
171
     *     original tag are separated, so that only the original one remains in
172
     *     the response.
173
     * @param string|null $ownershipTag The ownership tag that the response had.
174
     *
175
     * @return bool TRUE if the request was added to its buffer, FALSE if
176
     *     this instance owns the response, and therefore doesn't need to add
177
     *     the response to its buffer.
178
     */
179
    public function add(Response $response, $ownershipTag)
180
    {
181
        if ($this->getOwnershipTag() === $ownershipTag
182
            || ($this->isTaglessModeOwner()
183
            && $response->getType() !== Response::TYPE_FATAL)
184
        ) {
185
            return false;
186
        }
187
188
        if (null === $ownershipTag) {
189
            $this->shm->lock('taglessModeOwner');
190
            if ($this->shm->exists('taglessModeOwner')
191
                && $response->getType() !== Response::TYPE_FATAL
192
            ) {
193
                $ownershipTag = $this->shm->get('taglessModeOwner');
194
                $this->shm->unlock('taglessModeOwner');
195
            } else {
196
                $this->shm->unlock('taglessModeOwner');
197
                foreach ($this->shm->getIterator(
198
                    '/^(responseBuffer\_)/',
199
                    true
200
                ) as $targetBufferName) {
201
                    $this->_add($response, $targetBufferName);
202
                }
203
                return true;
204
            }
205
        }
206
207
        $this->_add($response, 'responseBuffer_' . $ownershipTag);
208
        return true;
209
    }
210
211
    /**
212
     * Adds a response to a buffer.
213
     *
214
     * @param Response $response         The response to add.
215
     * @param string   $targetBufferName The name of the buffer to add the
216
     *     response to.
217
     *
218
     * @return void
219
     */
220
    private function _add(Response $response, $targetBufferName)
221
    {
222
        if ($this->shm->lock($targetBufferName)) {
223
            $targetBuffer = $this->shm->get($targetBufferName);
224
            $targetBuffer[] = $response;
225
            $this->shm->set($targetBufferName, $targetBuffer);
226
            $this->shm->unlock($targetBufferName);
227
        }
228
    }
229
230
    /**
231
     * Gets the next response from this instance's buffer.
232
     *
233
     * @return Response|null The next response, or NULL if there isn't one.
234
     */
235
    public function getNextResponse()
236
    {
237
        $response = null;
238
        $targetBufferName = 'responseBuffer_' . $this->getOwnershipTag();
239
        if ($this->shm->exists($targetBufferName)
240
            && $this->shm->lock($targetBufferName)
241
        ) {
242
            $targetBuffer = $this->shm->get($targetBufferName);
243
            if (!empty($targetBuffer)) {
244
                $response = array_shift($targetBuffer);
245
                $this->shm->set($targetBufferName, $targetBuffer);
246
            }
247
            $this->shm->unlock($targetBufferName);
248
        }
249
        return $response;
250
    }
251
252
    /**
253
     * Closes the registry.
254
     *
255
     * Closes the registry, meaning that all buffers are cleared.
256
     *
257
     * @return void
258
     */
259
    public function close()
260
    {
261
        self::$requestId = -1;
262
        self::$instanceIdSeed = -1;
263
        $this->shm->clear();
264
    }
265
266
    /**
267
     * Removes a buffer.
268
     *
269
     * @param string $targetBufferName The buffer to remove.
270
     *
271
     * @return void
272
     */
273
    private function _close($targetBufferName)
274
    {
275
        if ($this->shm->lock($targetBufferName)) {
276
            $this->shm->delete($targetBufferName);
277
            $this->shm->unlock($targetBufferName);
278
        }
279
    }
280
281
    /**
282
     * Removes this instance's buffer.
283
     */
284
    public function __destruct()
285
    {
286
        $this->_close('responseBuffer_' . $this->getOwnershipTag());
287
    }
288
}
289