@@ -59,12 +59,114 @@ class Util
59
59
* keys, and the corresponding IDs as values.
60
60
*/
61
61
protected $ idCache = null ;
62
+
63
+ /**
64
+ * Parses a value from a RouterOS scripting context.
65
+ *
66
+ * Turns a value from RouterOS into an equivalent PHP value, based on
67
+ * determining the type in the same way RouterOS would determine it for a
68
+ * literal.
69
+ *
70
+ * This method is intended to be the very opposite of {@link escapeValue()}.
71
+ * That is, results from that method, if given to this method, should
72
+ * produce equivalent results.
73
+ *
74
+ * @param string $value The value to be parsed. Must be a literal of a
75
+ * value, e.g. what {@link escapeValue()} will give you.
76
+ *
77
+ * @return mixed Depending on RouterOS type detected:
78
+ * - "nil" or "nothing" - NULL.
79
+ * - "number" - int or double for large values.
80
+ * - "bool" - a boolean.
81
+ * - "time" - a {@link DateInterval} object.
82
+ * - "array" - an array, with the values processed recursively.
83
+ * - "str" - a string.
84
+ * - Unrecognized type - treated as an unquoted string.
85
+ */
86
+ public static function parseValue ($ value )
87
+ {
88
+ $ value = (string )$ value ;
89
+
90
+ if ('' === $ value ) {
91
+ return null ;
92
+ } elseif (in_array ($ value , array ('true ' , 'false ' , 'yes ' , 'no ' ), true )) {
93
+ return $ value === 'true ' || $ value === 'yes ' ;
94
+ } elseif ($ value === (string )($ num = (int )$ value )
95
+ || $ value === (string )($ num = (double )$ value )
96
+ ) {
97
+ return $ num ;
98
+ } elseif (preg_match (
99
+ '/^
100
+ (?:(\d+)w)?
101
+ (?:(\d+)d)?
102
+ (?:(\d\d)\:)?
103
+ (\d\d)\:
104
+ (\d\d(:\.\d{1,6})?)
105
+ $/x ' ,
106
+ $ value ,
107
+ $ time
108
+ )) {
109
+ $ days = isset ($ time [2 ]) ? (int )$ time [2 ] : 0 ;
110
+ if (isset ($ time [1 ])) {
111
+ $ days += 7 * (int )$ time [1 ];
112
+ }
113
+ if ('' === $ time [3 ]) {
114
+ $ time [3 ] = 0 ;
115
+ }
116
+ return new DateInterval (
117
+ "P {$ days }DT {$ time [3 ]}H {$ time [4 ]}M {$ time [5 ]}S "
118
+ );
119
+ } elseif (('" ' === $ value [0 ]) && substr (strrev ($ value ), 0 , 1 ) === '" ' ) {
120
+ return str_replace (
121
+ array ('\" ' , '\\\\' , "\\\n" , "\\\r\n" , "\\\r" ),
122
+ array ('" ' , '\\' ),
123
+ substr ($ value , 1 , -1 )
124
+ );
125
+ } elseif ('{ ' === $ value [0 ]) {
126
+ $ len = strlen ($ value );
127
+ if ($ value [$ len - 1 ] === '} ' ) {
128
+ $ value = substr ($ value , 1 , -1 );
129
+ if ('' === $ value ) {
130
+ return array ();
131
+ }
132
+ $ parsedValue = preg_split (
133
+ '/
134
+ (\"[^"]*\")
135
+ |
136
+ (\{[^{}]*(?2)?\})
137
+ |
138
+ ([^;]+)
139
+ /sx ' ,
140
+ $ value ,
141
+ null ,
142
+ PREG_SPLIT_DELIM_CAPTURE
143
+ );
144
+ $ result = array ();
145
+ foreach ($ parsedValue as $ token ) {
146
+ if ('' === $ token || '; ' === $ token ) {
147
+ continue ;
148
+ }
149
+ $ result [] = static ::parseValue ($ token );
150
+ }
151
+ return $ result ;
152
+ }
153
+ }
154
+ return $ value ;
155
+ }
62
156
63
157
/**
64
158
* Escapes a value for a RouterOS scripting context.
65
159
*
66
- * Turns any PHP value into an equivalent whole value that can be inserted
67
- * as part of a RouterOS script.
160
+ * Turns any native PHP value into an equivalent whole value that can be
161
+ * inserted as part of a RouterOS script.
162
+ *
163
+ * DateTime and DateInterval objects will be casted to RouterOS' "time"
164
+ * type. A DateTime object will be converted to a time relative to the UNIX
165
+ * epoch time. Note that if a DateInterval does not have the "days" property
166
+ * ("a" in formatting), then its months and years will be ignored, because
167
+ * they can't be unambigiously converted to a "time" value.
168
+ *
169
+ * Unrecognized types are casted to strings.
68
170
*
69
171
* @param mixed $value The value to be escaped.
70
172
*
@@ -85,7 +187,7 @@ public static function escapeValue($value)
85
187
break ;
86
188
case 'array ' :
87
189
if (0 === count ($ value )) {
88
- $ value = '{} ' ;
190
+ $ value = '({}) ' ;
89
191
break ;
90
192
}
91
193
$ result = '' ;
@@ -105,13 +207,11 @@ public static function escapeValue($value)
105
207
}
106
208
if ($ value instanceof DateInterval) {
107
209
if (false === $ value ->days || $ value ->days < 0 ) {
108
- $ value = $ value ->format ('%r ' )
109
- . ($ value ->y * 365 + $ value ->m * 12 + $ value ->d )
110
- . $ value ->format ('d%H:%I:%S ' );
210
+ $ value = $ value ->format ('%r%dd%H:%I:%S ' );
111
211
} else {
112
212
$ value = $ value ->format ('%r%ad%H:%I:%S ' );
113
213
}
114
- if (isset ($ usec )) {
214
+ if (strpos ( ' . ' , $ value ) === false && isset ($ usec )) {
115
215
$ value .= '. ' . $ usec ;
116
216
}
117
217
break ;
@@ -226,11 +326,10 @@ public function changeMenu($newMenu = '')
226
326
* @param string $source A script to execute.
227
327
* @param array $params An array of local variables to make available in
228
328
* the script. Variable names are array keys, and variable values are
229
- * array values. Note that the script's (generated) name is always added
230
- * as the variable "_", which you can overwrite from here.
231
- * Native PHP types will be converted to their RouterOS equivalents.
232
- * DateTime and DateInterval objects will be casted to RouterOS' "time"
233
- * type. Other types are casted to strings.
329
+ * array values. Array values are automatically processed with
330
+ * {@link escapeValue()}.
331
+ * Note that the script's (generated) name is always added as the
332
+ * variable "_", which you can overwrite from here.
234
333
* @param string $policy Allows you to specify a policy the script must
235
334
* follow. Has the same format as in terminal. If left NULL, the script
236
335
* has no restrictions.
@@ -369,9 +468,10 @@ public function find()
369
468
/**
370
469
* Gets a value of a specified entry at the current menu.
371
470
*
372
- * @param int $number A number identifying the entry you're
373
- * targeting. Can also be an ID or (in some menus) name.
374
- * @param string $value_name The name of the value you want to get.
471
+ * @param int|string|null $number A number identifying the entry you're
472
+ * targeting. Can also be an ID or (in some menus) name. For menus where
473
+ * there are no entries (e.g. "/system identity"), you can specify NULL.
474
+ * @param string $value_name The name of the value you want to get.
375
475
*
376
476
* @return string|null|bool The value of the specified property. If the
377
477
* property is not set, NULL will be returned. If no such entry exists,
@@ -388,11 +488,13 @@ public function get($number, $value_name)
388
488
}
389
489
}
390
490
391
- $ number = (string )$ number ;
392
- $ request = new Request (
393
- $ this ->menu . '/print ' ,
394
- Query::where ('.id ' , $ number )->orWhere ('name ' , $ number )
395
- );
491
+ $ request = new Request ($ this ->menu . '/print ' );
492
+ if (null !== $ number ) {
493
+ $ number = (string )$ number ;
494
+ $ request ->setQuery (
495
+ Query::where ('.id ' , $ number )->orWhere ('name ' , $ number )
496
+ );
497
+ }
396
498
$ request ->setArgument ('.proplist ' , $ value_name );
397
499
$ responses = $ this ->client ->sendSync ($ request )
398
500
->getAllOfType (Response::TYPE_DATA );
@@ -457,7 +559,8 @@ public function remove()
457
559
* which match certain criteria.
458
560
*
459
561
* @param mixed $numbers Targeted entries. Can be any criteria accepted by
460
- * {@link find()}.
562
+ * {@link find()} or NULL in case the menu is one without entries
563
+ * (e.g. "/system identity").
461
564
* @param array $newValues An array with the names of each property to set
462
565
* as an array key, and the new value as an array value.
463
566
*
@@ -470,9 +573,10 @@ public function set($numbers, array $newValues)
470
573
foreach ($ newValues as $ name => $ value ) {
471
574
$ setRequest ->setArgument ($ name , $ value );
472
575
}
473
- return $ this ->client ->sendSync (
474
- $ setRequest ->setArgument ('numbers ' , $ this ->find ($ numbers ))
475
- );
576
+ if (null !== $ numbers ) {
577
+ $ setRequest ->setArgument ('numbers ' , $ this ->find ($ numbers ));
578
+ }
579
+ return $ this ->client ->sendSync ($ setRequest );
476
580
}
477
581
478
582
/**
@@ -516,11 +620,11 @@ public function unsetValue($numbers, $value_name)
516
620
/**
517
621
* Adds a new entry at the current menu.
518
622
*
519
- * @param array $values Accepts one or more entries to add to the
623
+ * @param array $values Accepts one or more entries to add to the
520
624
* current menu. The data about each entry is specified as an array with
521
625
* the names of each property as an array key, and the value as an array
522
626
* value.
523
- * @param array $values, ... Additional entries.
627
+ * @param array $... Additional entries.
524
628
*
525
629
* @return string A comma separated list of the new entries' IDs.
526
630
*/
0 commit comments