Completed
Push — develop ( 8c3dc1...869a75 )
by Vasil
02:51
created

Query::send()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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