Skip to content

Commit 3b058ed

Browse files
committed
Made Util::move()'s $destination optional, defaulting to NULL, upon which no destination is set at the API call;
Script::parseValueToObject() is replaced with Script::parseValueToDateTime() and Script::parseValueToDateInterval(), the first of which can accept a timezone to set DateTime values to (only if there's a time component; otherwise UTC, which is also the default for values with times).
1 parent a562e1f commit 3b058ed

File tree

4 files changed

+162
-86
lines changed

4 files changed

+162
-86
lines changed

RELEASE-1.0.0b6

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ Util stuff, mostly.
99
- getCurrentTime()
1010
- newRequest()
1111
* Script::parseValue() now supports letter notation for time (1h2m3s), not just double colon notation (01:02:03), modeled after RouterOS. Related to that is also that leading zeroes, and zero minutes and seconds are now optional (e.g. "1:" is a valid way of saying 1 hour). Sub-second information is rounded up to the nearest second on current PHP versions (future versions are expected to support sub-second information in DateInterval by allowing seconds to be a double; The code currently attempts to give DateInterval a double, falling back to rounding to a second).
12-
* Script::parseValue() now recognizes dates in the "M/d/Y H:i:s" format (used across RouterOS), and turns that into a DateTime object for that time (or midnight if the time part is omitted) in UTC.
12+
* Script::parseValue() now recognizes dates in the "M/d/Y H:i:s" format (used across RouterOS), and turns that into a DateTime object for that time (or midnight in UTC if the time part is omitted).
1313
* Util::getAll() now throws a NotSupportedException if the arguments "follow", "follow-only" or "count-only" are used. The first two, because PHP would hang (since Client::sendSync() is used under the hood), and the last one because it's unredable in the returned output (use Util::count() instead).
1414
* Util::setMenu() can now go back to the root menu.
1515
* Util::find() now works even when the underlying Client streams responses.
16+
* Util::move()'s second argument $destination is now optional, and without it (or if set to NULL), the item is moved at the bottom of the menu.
1617
* Client::login() consumes the !done or !fatal response even when called on an already logged in connection.
1718
* The console now checks whether PEAR2_CommandLine is installed, ensuring better error messages when this package is installed without its optional dependencies.

src/PEAR2/Net/RouterOS/Script.php

Lines changed: 147 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -71,62 +71,88 @@ class Script
7171
* {@link static::escapeValue()}. That is, results from that method, if
7272
* given to this method, should produce equivalent results.
7373
*
74-
* @param string $value The value to be parsed. Must be a literal of a
75-
* value, e.g. what {@link static::escapeValue()} will give you.
74+
* @param string $value The value to be parsed.
75+
* Must be a literal of a value,
76+
* e.g. what {@link static::escapeValue()} will give you.
77+
* @param DateTimeZone|null $timezone The timezone which any resulting
78+
* DateTime object (either the main value, or values within an array)
79+
* will use. Defaults to UTC.
7680
*
7781
* @return mixed Depending on RouterOS type detected:
7882
* - "nil" (the string "[]") or "nothing" (empty string) - NULL.
7983
* - "number" - int or double for large values.
8084
* - "bool" - a boolean.
8185
* - "array" - an array, with the keys and values processed recursively.
82-
* - "str" - a string.
8386
* - "time" - a {@link DateInterval} object.
8487
* - "date" (pseudo type; string in the form "M/j/Y") - a DateTime
85-
* object with the specified date, at midnight UTC time.
88+
* object with the specified date, at midnight.
8689
* - "datetime" (pseudo type; string in the form "M/j/Y H:i:s") - a
87-
* DateTime object with the specified date and UTC time.
88-
* - Unrecognized type - treated as an unquoted string.
90+
* DateTime object with the specified date and time.
91+
* - "str" (a quoted string) - a string, with the contents escaped.
92+
* - Unrecognized type - casted to a string, unmodified.
8993
*/
90-
public static function parseValue($value)
94+
public static function parseValue($value, DateTimeZone $timezone = null)
9195
{
9296
$value = static::parseValueToSimple($value);
9397
if (!is_string($value)) {
9498
return $value;
95-
} elseif ('{' === $value[0] && '}' === $value[strlen($value) - 1]) {
96-
$value = static::parseValueToArray($value);
97-
if (!is_string($value)) {
98-
return $value;
99-
}
100-
} elseif ('"' === $value[0] && '"' === $value[strlen($value) - 1]) {
101-
return str_replace(
102-
array('\"', '\\\\', "\\\n", "\\\r\n", "\\\r"),
103-
array('"', '\\'),
104-
substr($value, 1, -1)
105-
);
99+
}
100+
101+
$value = static::parseValueToArray($value, $timezone);
102+
if (!is_string($value)) {
103+
return $value;
104+
}
105+
106+
$value = static::parseValueToDateInterval($value);
107+
if (!is_string($value)) {
108+
return $value;
106109
}
107110

108-
$value = static::parseValueToObject($value);
111+
$value = static::parseValueToDateTime($value, $timezone);
109112
if (!is_string($value)) {
110113
return $value;
111114
}
112115

116+
return static::parseValueToString($value);
117+
}
118+
119+
/**
120+
* Parses a RouterOS value into a PHP string.
121+
*
122+
* @param string $value The value to be parsed.
123+
* Must be a literal of a value,
124+
* e.g. what {@link static::escapeValue()} will give you.
125+
*
126+
* @return string If a quoted string is provided, it would be parsed.
127+
* Otherwise, the value is casted to a string, and returned unmodified.
128+
*/
129+
public static function parseValueToString($value)
130+
{
131+
$value = (string)$value;
132+
if ('"' === $value[0] && '"' === $value[strlen($value) - 1]) {
133+
return str_replace(
134+
array('\"', '\\\\', "\\\n", "\\\r\n", "\\\r"),
135+
array('"', '\\'),
136+
substr($value, 1, -1)
137+
);
138+
}
113139
return $value;
114140
}
115141

116142
/**
117143
* Parses a RouterOS value into a PHP simple type.
118-
*
144+
*
119145
* Parses a RouterOS value into a PHP simple type. "Simple" types being
120146
* scalar types, plus NULL.
121-
*
147+
*
122148
* @param string $value The value to be parsed. Must be a literal of a
123149
* value, e.g. what {@link static::escapeValue()} will give you.
124-
*
150+
*
125151
* @return string|bool|int|double|null Depending on RouterOS type detected:
126152
* - "nil" (the string "[]") or "nothing" (empty string) - NULL.
127153
* - "number" - int or double for large values.
128154
* - "bool" - a boolean.
129-
* - Unrecognized type - treated as an unquoted string.
155+
* - Unrecognized type - casted to a string, unmodified.
130156
*/
131157
public static function parseValueToSimple($value)
132158
{
@@ -145,22 +171,79 @@ public static function parseValueToSimple($value)
145171
}
146172

147173
/**
148-
* Parses a RouterOS value into a PHP object.
149-
*
150-
* Parses a RouterOS value into a PHP object.
151-
*
174+
* Parses a RouterOS value into a PHP DateTime object
175+
*
176+
* Parses a RouterOS value into a PHP DateTime object.
177+
*
178+
* @param string $value The value to be parsed.
179+
* Must be a literal of a value,
180+
* e.g. what {@link static::escapeValue()} will give you.
181+
* @param DateTimeZone|null $timezone The timezone which the resulting
182+
* DateTime object will use. Defaults to UTC.
183+
*
184+
* @return string|DateTime Depending on RouterOS type detected:
185+
* - "date" (pseudo type; string in the form "M/j/Y") - a DateTime
186+
* object with the specified date, at midnight UTC time (regardless
187+
* of timezone provided).
188+
* - "datetime" (pseudo type; string in the form "M/j/Y H:i:s") - a
189+
* DateTime object with the specified date and time.
190+
* - Unrecognized type - casted to a string, unmodified.
191+
*/
192+
public static function parseValueToDateTime(
193+
$value,
194+
DateTimeZone $timezone = null
195+
) {
196+
$value = (string)$value;
197+
if ('' === $value) {
198+
return $value;
199+
}
200+
if (preg_match(
201+
'#^
202+
(?<mon>jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)
203+
/
204+
(?<day>\d\d?)
205+
/
206+
(?<year>\d{4})
207+
(?:
208+
\s+(?<time>\d{2}\:\d{2}:\d{2})
209+
)?
210+
$#uix',
211+
$value,
212+
$date
213+
)) {
214+
if (!isset($date['time'])) {
215+
$date['time'] = '00:00:00';
216+
$timezone = new DateTimeZone('UTC');
217+
} elseif (null === $timezone) {
218+
$timezone = new DateTimeZone('UTC');
219+
}
220+
try {
221+
return new DateTime(
222+
$date['year'] .
223+
'-' . ucfirst($date['mon']) .
224+
"-{$date['day']} {$date['time']}",
225+
$timezone
226+
);
227+
} catch (E $e) {
228+
return $value;
229+
}
230+
}
231+
return $value;
232+
}
233+
234+
/**
235+
* Parses a RouterOS value into a PHP DateInterval.
236+
*
237+
* Parses a RouterOS value into a PHP DateInterval.
238+
*
152239
* @param string $value The value to be parsed. Must be a literal of a
153240
* value, e.g. what {@link static::escapeValue()} will give you.
154-
*
241+
*
155242
* @return string|DateInterval|DateTime Depending on RouterOS type detected:
156243
* - "time" - a {@link DateInterval} object.
157-
* - "date" (pseudo type; string in the form "M/j/Y") - a DateTime
158-
* object with the specified date, at midnight UTC time.
159-
* - "datetime" (pseudo type; string in the form "M/j/Y H:i:s") - a
160-
* DateTime object with the specified date and UTC time.
161-
* - Unrecognized type - treated as an unquoted string.
244+
* - Unrecognized type - casted to a string, unmodified.
162245
*/
163-
public static function parseValueToObject($value)
246+
public static function parseValueToDateInterval($value)
164247
{
165248
$value = (string)$value;
166249
if ('' === $value) {
@@ -199,7 +282,7 @@ public static function parseValueToObject($value)
199282
if (empty($time[5])) {
200283
$time[5] = 0;
201284
}
202-
285+
203286
$subsecondTime = 0.0;
204287
//@codeCoverageIgnoreStart
205288
// No PHP version currently supports sub-second DateIntervals,
@@ -239,51 +322,32 @@ public static function parseValueToObject($value)
239322
);
240323
}
241324
//@codeCoverageIgnoreEnd
242-
} elseif (preg_match(
243-
'#^
244-
(?<mon>jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)
245-
/
246-
(?<day>\d\d?)
247-
/
248-
(?<year>\d{4})
249-
(?:
250-
\s+(?<time>\d{2}\:\d{2}:\d{2})
251-
)?
252-
$#uix',
253-
$value,
254-
$date
255-
)) {
256-
if (!isset($date['time'])) {
257-
$date['time'] = '00:00:00';
258-
}
259-
try {
260-
return new DateTime(
261-
$date['year'] .
262-
'-' . ucfirst($date['mon']) .
263-
"-{$date['day']} {$date['time']}",
264-
new DateTimeZone('UTC')
265-
);
266-
} catch (E $e) {
267-
return $value;
268-
}
269325
}
326+
270327
return $value;
271328
}
272329

273330
/**
274331
* Parses a RouterOS value into a PHP array.
275-
*
332+
*
276333
* Parses a RouterOS value into a PHP array.
277-
*
278-
* @param string $value The value to be parsed. Must be a literal of a
279-
* value, e.g. what {@link static::escapeValue()} will give you.
280-
*
334+
*
335+
* @param string $value The value to be parsed.
336+
* Must be a literal of a value,
337+
* e.g. what {@link static::escapeValue()} will give you.
338+
* @param DateTimeZone|null $timezone The timezone which any resulting
339+
* DateTime object within the array will use. Defaults to UTC.
340+
*
281341
* @return string|array Depending on RouterOS type detected:
282-
* - "array" - an array, with the keys and values processed recursively.
283-
* - Unrecognized type - treated as an unquoted string.
342+
* - "array" - an array, with the and values processed recursively,
343+
* the keys with {@link static::parseValueToSimple()},
344+
* and the values with {@link static::parseValue()}
345+
* - Unrecognized type - casted to a string, unmodified.
284346
*/
285-
public static function parseValueToArray($value)
286-
{
347+
public static function parseValueToArray(
348+
$value,
349+
DateTimeZone $timezone = null
350+
) {
287351
$value = (string)$value;
288352
if ('{' === $value[0] && '}' === $value[strlen($value) - 1]) {
289353
$value = substr($value, 1, -1);
@@ -319,10 +383,10 @@ public static function parseValueToArray($value)
319383
break;
320384
case '=':
321385
$newKey = static::parseValueToSimple($parsedValue[$i - 1]);
322-
$newVal = static::parseValue($parsedValue[++$i]);
386+
$newVal = static::parseValue($parsedValue[++$i], $timezone);
323387
break;
324388
default:
325-
$newVal = static::parseValue($parsedValue[$i]);
389+
$newVal = static::parseValue($parsedValue[$i], $timezone);
326390
}
327391
}
328392
if (null === $newKey) {
@@ -437,13 +501,13 @@ public static function append(
437501
* inserted as part of a RouterOS script.
438502
*
439503
* DateInterval objects will be casted to RouterOS' "time" type.
440-
*
504+
*
441505
* DateTime objects will be casted to a string following the "M/d/Y H:i:s"
442506
* format. If the time is exactly midnight (including microseconds), and
443507
* the timezone is UTC, the string will include only the "M/d/Y" date.
444508
*
445509
* Unrecognized types (i.e. resources and other objects) are casted to
446-
* strings.
510+
* strings, and those strings are then escaped.
447511
*
448512
* @param mixed $value The value to be escaped.
449513
*
@@ -508,10 +572,18 @@ public static function escapeValue($value)
508572
* surrounded with quotes at a RouterOS script (or concatenated onto a
509573
* larger string first), and you can be sure there won't be any code
510574
* injections coming from it.
575+
*
576+
* For the sake of brevity of the output, alphanumeric characters and
577+
* underscores are left untouched
511578
*
512579
* @param string $value Value to be escaped.
513580
*
514581
* @return string The escaped value.
582+
*
583+
* @internal Why leave ONLY those characters and not also others?
584+
* Because those can't in any way be mistaken for language constructs,
585+
* unlike many other "safe inside strings, but not outside" ASCII
586+
* characters, like ",", ".", "+", "-", "~", etc.
515587
*/
516588
public static function escapeString($value)
517589
{

src/PEAR2/Net/RouterOS/Util.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -692,23 +692,27 @@ public function add(array $values)
692692
*
693693
* @param mixed $numbers Targeted items. Can be any criteria accepted
694694
* by {@link static::find()}.
695-
* @param mixed $destination item before which the targeted items will be
695+
* @param mixed $destination Item before which the targeted items will be
696696
* moved to. Can be any criteria accepted by {@link static::find()}.
697697
* If multiple items match the criteria, the targeted items will move
698698
* above the first match.
699+
* If NULL is given (or this argument is omitted), the targeted items
700+
* will be moved to the bottom of the menu.
699701
*
700702
* @return ResponseCollection Returns the response collection, allowing you
701703
* to inspect errors, if any.
702704
*/
703-
public function move($numbers, $destination)
705+
public function move($numbers, $destination = null)
704706
{
705707
$moveRequest = new Request($this->menu . '/move');
706708
$moveRequest->setArgument('numbers', $this->find($numbers));
707-
$destination = $this->find($destination);
708-
if (false !== strpos($destination, ',')) {
709-
$destination = strstr($destination, ',', true);
709+
if (null !== $destination) {
710+
$destination = $this->find($destination);
711+
if (false !== strpos($destination, ',')) {
712+
$destination = strstr($destination, ',', true);
713+
}
714+
$moveRequest->setArgument('destination', $destination);
710715
}
711-
$moveRequest->setArgument('destination', $destination);
712716
$this->clearIdCache();
713717
return $this->client->sendSync($moveRequest);
714718
}

0 commit comments

Comments
 (0)