Skip to content

Commit 9cdb61e

Browse files
committed
Reverted the script source getting addition, and in fact completely removed the concept altogether. File contents is fetched from the output of executing ":error" (which produces a !trap reply with the message in it), and this same mechanism can be used for getting output of scripts in general;
Also added tests for all previously untested features, and added two special case groups for testing encrypted connections' initialization.
1 parent a7dd5ea commit 9cdb61e

File tree

11 files changed

+410
-94
lines changed

11 files changed

+410
-94
lines changed

RELEASE-1.0.0b6

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ Util stuff, mostly.
22

33
* __BREAKING CHANGES:__
44
- Removed all $mode arguments from all Countable implementations (turns out no stable 5.6+ releases uses it...), and the COUNT_RECURSIVE implementations (which were mostly useless anyway). Util::count() has the $query argument as its first and only one.
5+
- All Util CRUD methods throw RouterErrorException when the router returns an error. Previously, you'd need to inspect the returned value to check if there's an error.
56
- Util::edit() is no longer an alias of Util::set(). It's now its own method that can modify or unset a single specified property.
7+
- Util::fileGetContents() now throws RouterErrorException if the file doesn't exist or if there are other problems with getting its contents. Previously, it returned FALSE for such cases.
68
- Util's escapeValue(), escapeString(), parseValue(), prepareScript() and appendScript() methods are now in their own new class called "Script", with the last two now being called just prepare() and append().
79
- Script::escapeValue() now converts DateTime objects into a string having the "M/d/Y H:i:s" format (used across RouterOS), or just "M/d/Y" if the time is exactly midnight, and the timezone is UTC. The old behaviour can be achieved by "manually" adding the DateTime to "new DateTime('@0')".
810
* New Util methods:
@@ -11,7 +13,6 @@ Util stuff, mostly.
1113
- newRequest()
1214
* 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).
1315
* 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).
14-
* Util::exec() now returns the script source after execution. Previously, this was a private option, but is now the only way, and it's public. Intended to be used for easier output extraction.
1516
* 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).
1617
* Util::setMenu() can now go back to the root menu.
1718
* Util::get() can now accept a query as an argument for $number

src/PEAR2/Net/RouterOS/RouterErrorException.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,10 @@ class RouterErrorException extends RuntimeException implements Exception
5959
const CODE_COMMENT_ERROR = 0x120042;
6060
const CODE_UNSET_ERROR = 0x120082;
6161
const CODE_MOVE_ERROR = 0x120107;
62-
const CODE_SCRIPT_GET_ERROR = 0x210001;
6362
const CODE_SCRIPT_ADD_ERROR = 0x220001;
6463
const CODE_SCRIPT_REMOVE_ERROR = 0x220004;
6564
const CODE_SCRIPT_RUN_ERROR = 0x240001;
65+
const CODE_SCRIPT_FILE_ERROR = 0x240003;
6666

6767
/**
6868
* @var ResponseCollection|null The complete response returned by the router.

src/PEAR2/Net/RouterOS/Util.php

Lines changed: 59 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ public function setMenu($newMenu)
126126
} elseif ('/' === $newMenu[0]) {
127127
$this->menu = $menuRequest->setCommand($newMenu)->getCommand();
128128
} else {
129-
$newMenu = substr(
129+
$newMenu = (string)substr(
130130
$menuRequest->setCommand(
131131
'/' .
132132
str_replace('/', ' ', (string)substr($this->menu, 1)) .
@@ -235,16 +235,13 @@ public function newRequest(
235235
* To eliminate any possibility of name clashes,
236236
* you can specify your own name instead.
237237
*
238-
* @return string|resource The source of the script, as it appears after
239-
* the run, right before it is removed.
240-
* This can be used for easily retrieving basic output,
241-
* by modifying the script from inside the script
242-
* (use the $"_" variable to refer to the script's name within the
243-
* "/system script" menu).
238+
* @return ResponseCollection The responses of all requests involved, i.e.
239+
* the add, the run and the remove.
244240
*
245241
* @throws RouterErrorException When there is an error in any step of the
246242
* way. The reponses include all successful commands prior to the error
247-
* as well.
243+
* as well. If the error occurs during the run, there will also be a
244+
* remove attempt, and the results will include its results as well.
248245
*/
249246
public function exec(
250247
$source,
@@ -286,56 +283,46 @@ public function exec(
286283
$request = new Request('/system/script/run');
287284
$request->setArgument('number', $name);
288285
$runResult = $this->client->sendSync($request);
286+
$request = new Request('/system/script/remove');
287+
$request->setArgument('numbers', $name);
288+
$removeResult = $this->client->sendSync($request);
289+
289290
if (count($runResult->getAllOfType(Response::TYPE_ERROR)) > 0) {
290291
throw new RouterErrorException(
291292
'Error when running script',
292293
RouterErrorException::CODE_SCRIPT_RUN_ERROR,
293294
null,
294-
new ResponseCollection(
295-
array_merge($addResult->toArray(), $runResult->toArray())
296-
)
297-
);
298-
}
299-
300-
$request = new Request('/system/script/get');
301-
$request->setArgument('number', $name);
302-
$request->setArgument('value-name', 'source');
303-
$getResult = $this->client->sendSync($request);
304-
if (count($getResult->getAllOfType(Response::TYPE_ERROR)) > 0) {
305-
throw new RouterErrorException(
306-
'Error when getting script source',
307-
RouterErrorException::CODE_SCRIPT_GET_ERROR,
308-
null,
309295
new ResponseCollection(
310296
array_merge(
311297
$addResult->toArray(),
312298
$runResult->toArray(),
313-
$getResult->toArray()
299+
$removeResult->toArray()
314300
)
315301
)
316302
);
317303
}
318-
$postSource = $getResult->end()->getProperty('ret');
319-
320-
$request = new Request('/system/script/remove');
321-
$request->setArgument('numbers', $name);
322-
$removeResult = $this->client->sendSync($request);
323304
if (count($removeResult->getAllOfType(Response::TYPE_ERROR)) > 0) {
324305
throw new RouterErrorException(
325-
'Error when getting script source',
326-
RouterErrorException::CODE_SCRIPT_GET_ERROR,
306+
'Error when removing script',
307+
RouterErrorException::CODE_SCRIPT_REMOVE_ERROR,
327308
null,
328309
new ResponseCollection(
329310
array_merge(
330311
$addResult->toArray(),
331312
$runResult->toArray(),
332-
$getResult->toArray(),
333313
$removeResult->toArray()
334314
)
335315
)
336316
);
337317
}
338-
return $postSource;
318+
319+
return new ResponseCollection(
320+
array_merge(
321+
$addResult->toArray(),
322+
$runResult->toArray(),
323+
$removeResult->toArray()
324+
)
325+
);
339326
}
340327

341328
/**
@@ -535,9 +522,6 @@ public function find()
535522
* considered the target item.
536523
* @param string|null $valueName The name of the value to get.
537524
* If omitted, or set to NULL, gets all properties of the target item.
538-
* Note that for versions that don't support omitting $valueName
539-
* natively, a "print" with "detail" argument is used as a fallback,
540-
* which may not contain certain properties.
541525
*
542526
* @return string|resource|null|array The value of the specified
543527
* property as a string or as new PHP temp stream if the underlying
@@ -551,7 +535,10 @@ public function find()
551535
*/
552536
public function get($number, $valueName = null)
553537
{
554-
if (is_int($number) || ((string)$number === (string)(int)$number)) {
538+
if ($number instanceof Query) {
539+
$number = explode(',', $this->find($number));
540+
$number = $number[0];
541+
} elseif (is_int($number) || ((string)$number === (string)(int)$number)) {
555542
$this->find();
556543
if (isset($this->idCache[(int)$number])) {
557544
$number = $this->idCache[(int)$number];
@@ -561,9 +548,6 @@ public function get($number, $valueName = null)
561548
RouterErrorException::CODE_CACHE_ERROR
562549
);
563550
}
564-
} elseif ($number instanceof Query) {
565-
$number = explode(',', $this->find($number));
566-
$number = $number[0];
567551
}
568552

569553
$request = new Request($this->menu . '/get');
@@ -584,12 +568,15 @@ public function get($number, $valueName = null)
584568
$result = stream_get_contents($result);
585569
}
586570
if (null === $valueName) {
571+
// @codeCoverageIgnoreStart
587572
//Some earlier RouterOS versions use "," instead of ";" as separator
573+
//Newer versions can't possibly enter this condition
588574
if (false === strpos($result, ';')
589-
&& 1 === preg_match('/^([^=,]+\=[^=,]*)(?:\,(?1))+$/', $result)
575+
&& preg_match('/^([^=,]+\=[^=,]*)(?:\,(?1))+$/', $result)
590576
) {
591577
$result = str_replace(',', ';', $result);
592578
}
579+
// @codeCoverageIgnoreEnd
593580
return Script::parseValueToArray('{' . $result . '}');
594581
}
595582
return $result;
@@ -1120,29 +1107,45 @@ public function filePutContents($filename, $data, $overwrite = false)
11201107
* To eliminate any possibility of name clashes,
11211108
* you can specify your own name instead.
11221109
*
1123-
* @return string|resource|false The contents of the file as a string or as
1110+
* @return string|resource The contents of the file as a string or as
11241111
* new PHP temp stream if the underlying
11251112
* {@link Client::isStreamingResponses()} is set to TRUE.
1126-
* FALSE is returned if there is no such file.
11271113
*
11281114
* @throws RouterErrorException When there's an error with the temporary
1129-
* script used to get the file.
1115+
* script used to get the file, or if the file doesn't exist.
11301116
*/
11311117
public function fileGetContents($filename, $tmpScriptName = null)
11321118
{
1133-
$checkRequest = new Request(
1134-
'/file/print .proplist=""',
1135-
Query::where('name', $filename)
1136-
);
1137-
if (1 === count($this->client->sendSync($checkRequest))) {
1138-
return false;
1119+
try {
1120+
$responses = $this->exec(
1121+
':error ("&" . [/file get $filename contents]);',
1122+
array('filename' => $filename),
1123+
null,
1124+
$tmpScriptName
1125+
);
1126+
throw new RouterErrorException(
1127+
'Unable to read file through script (no error returned)',
1128+
RouterErrorException::CODE_SCRIPT_FILE_ERROR,
1129+
null,
1130+
$responses
1131+
);
1132+
} catch (RouterErrorException $e) {
1133+
if ($e->getCode() !== RouterErrorException::CODE_SCRIPT_RUN_ERROR) {
1134+
throw $e;
1135+
}
1136+
$message = $e->getResponses()->getAllOfType(Response::TYPE_ERROR)
1137+
->getProperty('message');
1138+
if (Stream::isStream($message)) {
1139+
$successToken = fread($message, 1/*strlen('&')*/);
1140+
if ('&' === $successToken) {
1141+
return $message;
1142+
}
1143+
rewind($message);
1144+
} elseif (strpos($message, '&') === 0) {
1145+
return substr($message, 1/*strlen('&')*/);
1146+
}
1147+
throw $e;
11391148
}
1140-
return $this->exec(
1141-
'/system script set $"_" source=[/file get $filename contents]',
1142-
array('filename' => $filename),
1143-
null,
1144-
$tmpScriptName
1145-
);
11461149
}
11471150

11481151
/**

tests/Client/Safe.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -825,7 +825,7 @@ public function testSendSyncWithQueryEquals()
825825
$this->assertEquals(
826826
2,
827827
count($list),
828-
'The list should have only one item and a "done" reply.'
828+
'The list should have only one item and a "done" reply. target=' . HOSTNAME_INVALID . ';list=' . print_r($list->toArray(), true)
829829
);
830830

831831
$request->setQuery(

0 commit comments

Comments
 (0)