Completed
Push — develop ( b42f4c...d20a7c )
by Vasil
03:07
created

Query::_send()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 21
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 21
rs 9.0534
c 0
b 0
f 0
cc 4
eloc 15
nc 4
nop 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
 * Refers to transmitter direction constants.
25
 */
26
use PEAR2\Net\Transmitter as T;
27
28
/**
29
 * Represents a query for RouterOS requests.
30
 *
31
 * @category Net
32
 * @package  PEAR2_Net_RouterOS
33
 * @author   Vasil Rangelov <[email protected]>
34
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
35
 * @link     http://pear2.php.net/PEAR2_Net_RouterOS
36
 */
37
class Query
38
{
39
40
    /**
41
     * Checks if the property exists.
42
     */
43
    const OP_EX = '';
44
45
    /**
46
     * Checks if the property does not exist.
47
     */
48
    const OP_NEX = '-';
49
50
    /**
51
     * Checks if the property equals a certain value.
52
     */
53
    const OP_EQ = '=';
54
55
    /**
56
     * Checks if the property is less than a certain value.
57
     */
58
    const OP_LT = '<';
59
60
    /**
61
     * Checks if the property is greater than a certain value.
62
     */
63
    const OP_GT = '>';
64
65
    /**
66
     * An array of the words forming the query.
67
     *
68
     * Each value is an array with the first member being the predicate
69
     * (operator and name), and the second member being the value
70
     * for the predicate.
71
     *
72
     * @var array<string,string|null>[]
73
     */
74
    protected $words = array();
75
76
    /**
77
     * This class is not to be instantiated normally, but by static methods
78
     * instead. Use {@link static::where()} to create an instance of it.
79
     */
80
    protected function __construct()
81
    {
82
83
    }
84
85
    /**
86
     * Sanitizes the operator of a condition.
87
     *
88
     * @param string $operator The operator to sanitize.
89
     *
90
     * @return string The sanitized operator.
91
     */
92
    protected static function sanitizeOperator($operator)
93
    {
94
        $operator = (string) $operator;
95
        switch ($operator) {
96
        case Query::OP_EX:
97
        case Query::OP_NEX:
98
        case Query::OP_EQ:
99
        case Query::OP_LT:
100
        case Query::OP_GT:
101
            return $operator;
102
        default:
103
            throw new UnexpectedValueException(
104
                'Unknown operator specified',
105
                UnexpectedValueException::CODE_ACTION_UNKNOWN,
106
                null,
107
                $operator
108
            );
109
        }
110
    }
111
112
    /**
113
     * Creates a new query with an initial condition.
114
     *
115
     * @param string               $name     The name of the property to test.
116
     * @param string|resource|null $value    Value of the property as a string
117
     *     or seekable stream. Not required for existence tests.
118
     *     If a seekable stream is provided, it is sent from its current
119
     *     position to its end, and the pointer is seeked back to its current
120
     *     position after sending.
121
     *     Non seekable streams, as well as all other types, are casted to a
122
     *     string.
123
     * @param string               $operator One of the OP_* constants.
124
     *     Describes the operation to perform.
125
     *
126
     * @return static A new query object.
127
     */
128
    public static function where(
129
        $name,
130
        $value = null,
131
        $operator = self::OP_EX
132
    ) {
133
        $query = new static;
134
        return $query->addWhere($name, $value, $operator);
135
    }
136
137
    /**
138
     * Negates the query.
139
     *
140
     * @return $this The query object.
141
     */
142
    public function not()
143
    {
144
        $this->words[] = array('#!', null);
145
        return $this;
146
    }
147
148
    /**
149
     * Adds a condition as an alternative to the query.
150
     *
151
     * @param string               $name     The name of the property to test.
152
     * @param string|resource|null $value    Value of the property as a string
153
     *     or seekable stream. Not required for existence tests.
154
     *     If a seekable stream is provided, it is sent from its current
155
     *     position to its end, and the pointer is seeked back to its current
156
     *     position after sending.
157
     *     Non seekable streams, as well as all other types, are casted to a
158
     *     string.
159
     * @param string               $operator One of the OP_* constants.
160
     *     Describes the operation to perform.
161
     *
162
     * @return $this The query object.
163
     */
164
    public function orWhere($name, $value = null, $operator = self::OP_EX)
165
    {
166
        $this->addWhere($name, $value, $operator)->words[] = array('#|', null);
167
        return $this;
168
    }
169
170
    /**
171
     * Adds a condition in addition to the query.
172
     *
173
     * @param string               $name     The name of the property to test.
174
     * @param string|resource|null $value    Value of the property as a string
175
     *     or seekable stream. Not required for existence tests.
176
     *     If a seekable stream is provided, it is sent from its current
177
     *     position to its end, and the pointer is seeked back to its current
178
     *     position after sending.
179
     *     Non seekable streams, as well as all other types, are casted to a
180
     *     string.
181
     * @param string               $operator One of the OP_* constants.
182
     *     Describes the operation to perform.
183
     *
184
     * @return $this The query object.
185
     */
186
    public function andWhere($name, $value = null, $operator = self::OP_EX)
187
    {
188
        $this->addWhere($name, $value, $operator)->words[] = array('#&', null);
189
        return $this;
190
    }
191
192
    /**
193
     * Sends the query over a communicator.
194
     *
195
     * @param Communicator $com The communicator to send the query over.
196
     *
197
     * @return int The number of bytes sent.
198
     */
199
    public function send(Communicator $com)
200
    {
201
        if ($com->getTransmitter()->isPersistent()) {
202
            $old = $com->getTransmitter()->lock(T\Stream::DIRECTION_SEND);
203
            $bytes = $this->_send($com);
204
            $com->getTransmitter()->lock($old, true);
205
            return $bytes;
206
        }
207
        return $this->_send($com);
208
    }
209
210
    /**
211
     * Sends the query over a communicator.
212
     *
213
     * The only difference with the non private equivalent is that this one does
214
     * not do locking.
215
     *
216
     * @param Communicator $com The communicator to send the query over.
217
     *
218
     * @return int The number of bytes sent.
219
     */
220
    private function _send(Communicator $com)
221
    {
222
        if (!$com->getTransmitter()->isAcceptingData()) {
223
            throw new SocketException(
224
                'Transmitter is invalid. Sending aborted.',
225
                SocketException::CODE_QUERY_SEND_FAIL
226
            );
227
        }
228
        $bytes = 0;
229
        foreach ($this->words as $queryWord) {
230
            list($predicate, $value) = $queryWord;
231
            $prefix = '?' . $predicate;
232
            if (null === $value) {
233
                $bytes += $com->sendWord($prefix);
234
            } else {
235
                $prefix .= '=';
236
                $bytes += $com->sendWord($prefix, $value);
237
            }
238
        }
239
        return $bytes;
240
    }
241
242
    /**
243
     * Verifies the query.
244
     *
245
     * Verifies the query against a communicator, i.e. whether the query
246
     * could successfully be sent (assuming the connection is still opened).
247
     *
248
     * @param Communicator $com The Communicator to check against.
249
     *
250
     * @return $this The query object itself.
251
     *
252
     * @throws LengthException If the resulting length of an API word is not
253
     *     supported.
254
     */
255
    public function verify(Communicator $com)
256
    {
257
        foreach ($this->words as $queryWord) {
258
            list($predicate, $value) = $queryWord;
259
            if (null === $value) {
260
                $com::verifyLengthSupport(strlen('?' . $predicate));
261
            } elseif (is_string($value)) {
262
                $com::verifyLengthSupport(
263
                    strlen('?' . $predicate . '=' . $value)
264
                );
265
            } else {
266
                $com::verifyLengthSupport(
267
                    strlen('?' . $predicate . '=') +
268
                    $com::seekableStreamLength($value)
269
                );
270
            }
271
        }
272
        return $this;
273
    }
274
275
    /**
276
     * Adds a condition.
277
     *
278
     * @param string               $name     The name of the property to test.
279
     * @param string|resource|null $value    Value of the property as a string
280
     *     or seekable stream. Not required for existence tests.
281
     *     If a seekable stream is provided, it is sent from its current
282
     *     position to its end, and the pointer is seeked back to its current
283
     *     position after sending.
284
     *     Non seekable streams, as well as all other types, are casted to a
285
     *     string.
286
     * @param string               $operator One of the ACTION_* constants.
287
     *     Describes the operation to perform.
288
     *
289
     * @return $this The query object.
290
     */
291
    protected function addWhere($name, $value, $operator)
292
    {
293
        $this->words[] = array(
294
            static::sanitizeOperator($operator)
295
            . Message::sanitizeAttributeName($name),
296
            (null === $value ? null : Message::sanitizeAttributeValue($value))
297
        );
298
        return $this;
299
    }
300
}
301