PHP API fileGetContents problem

Hi Folks, I’m having trouble reading files via the PHP API.
My script will happily make a backup file but I’m then unable to get that file back to the webserver:

$util = new RouterOS\Util(
    $client = new RouterOS\Client('******', '******', '******')
);

$filename = 'backup.rsc';

$addRequest = new RouterOS\Request('/export');

$addRequest->setArgument('compact', '');
$addRequest->setArgument('file', '$filename');

if ($client->sendSync($addRequest)->getType() !== RouterOS\Response::TYPE_FINAL) {
    die("Error");
}

file_put_contents("C:/inetpub/wwwroot/backup/".$filename, $util->fileGetContents($filename));

Can anyone please help?

I’ve tried just echoing $util->fileGetContents($filename) but I don’t seem to be getting anything back.

Many thanks.

Couldn’t get it to work. In the end I scripted pushing the export file to an FTP server from the target device.

//ftp transfer export file
	$addRequest = new RouterOS\Request('/tool fetch');
	
	$addRequest->setArgument('address', '***.***.***.***');
	$addRequest->setArgument('src-path', 'auto_export.rsc');
	$addRequest->setArgument('user', '****');
	$addRequest->setArgument('mode', 'ftp');
	$addRequest->setArgument('password', '****');
	$addRequest->setArgument('dst-path', auto_export.rsc);
	$addRequest->setArgument('upload', 'yes');
	
	$responses = $client->sendSync($addRequest);
	
	echo "<br>";
	
	foreach ($responses as $response) {
		foreach ($response as $name => $value) {
			echo "{$name}: {$value}\n";
		}
		echo "<br>";
	}

Hi. Sorry for not noticing this topic earlier…

If you’re using PHP 5.6.8, it’s possible you’re experiencing a new issue that was introduced there… Basically, sending streams over TCP is broken in PHP, and Util::fileGetContents() relies on that.

I’m glad you got it working - FTP would’ve been exactly my suggestion, though I would’ve used PHP’s file_get_contents() instead of RouterOS’ “/tool fetch”, or better yet, PHP’s copy(), e.g.

copy('ftp://user:password@router-ip/backup.rsc', 'C:\inetpub\wwwroot\backup\backup.rsc');

Hey, thanks for replying.

I’m using php ver 5.4.24 and the export files I’m trying to transfer are all around 2k - 12k. I’m talking to various RouterOS versions but my test system is on ver 6.24.

We ended up pushing rather than pulling just to have one less port open on these devices. Unfortunately I’m seeing some mixed results from older RouterOS versions on this part of the script. I’ve not pinned down where the version crossover is yet but the FTP push seems to hang my php script on some of the older boxes or at least it takes a very long time to run and then I don’t get any $responses back. I know a box on v5.25 isn’t working quite right.

I think the issue is with the validation I’m doing based on $responses.

foreach ($responses as $response) {
		foreach ($response as $name => $value) {
			echo "{$name}: {$value}\n";
		}
		echo "<br>";
	}

Look familiar? :slight_smile: - I’ve cut and pasted a lot of your documented scripts, thanks for writing such a detailed wiki btw.

I’m looking for the string “Finished” in the $value variable to indicate the transfer was successful and for these older boxes I don’t seem to be getting anything back in the $responses although I can see the files are successfully transferred.

Thanks again for replying, I really appreciate the help.

The problem is on older RouterOS versions, “/tool fetch” never terminates. MikroTik fixed it in… I think it was 6.0… where the second last reply has status=finished (or was it status=Finished?.. I haven’t actually tested it in a long while), and the very next reply is a !done.

For commands that don’t terminate, you need to use sendAsync(), and at some point cancel the request.

e.g.

$client->sendAsync(
    new Request(
        '/tool fetch upload=yes
        mode=ftp user=*** password=*** address=***.***.***.*** dst-path=auto_export.rsc
        src-path=auto_export.rsc',
        null,
        'f'
    ),
    function ($response) {
        switch ($response->getArgument('status')) {
        case 'failed':
            //Upload failed. Do error handling here.
            //break; intentionally omitted, as we want to cancel on error too.
        case 'finished':
            return true;//cancels the request.
        }
    }
);
$client->loop();  

Ahh that’s brilliant thank you very much.

Actually it makes me think of another issue I’m having. I have one (so far as I’ve found) box that crashes my script if I try to get health data from it. It’s v6.20 but I have another v6.20 box that works just fine. I’ve tried manually running this process on it and it returns no data.

//get temp
	  $util->setMenu('/system health');
	  $temperature = $util->get(null, 'temperature');

I’m not too bothered about not getting that data for this box but I would like to continue working through the rest of my script, unfortunately it exits at that point.

Any idea how I can prevent it from exiting? Do I need to use $client instead of (the very convenient) $util?

Many thanks.

It’s v6.20 but I have another v6.20 box that works just fine.

Are the two boxes of different architectures (e.g. MIPS-BE and PPC) by any chance? I remember seeing in change logs recently lots of issues related to the health menu, some of which were applicable only to some architectures.

Regardless, the best solution is to upgrade.

I’ve tried manually running this process on it and it returns no data.

but I would like to continue working through the rest of my script, unfortunately it exits at that point.

So wait, does the script exit, or does it simply return no data? If it exits, does it hang first, or does it exit (more or less) immediately as a sort of crash?

If it returns no data (i.e. returns null/false/empty string), you should be able to just check that, and act accordingly.

If it crashes, you could run each router as a separate process (see proc_open() and proc_close()) so that even if the child process crashes, the main one can dispatch the next router.

If it hangs… That’s a difficult one… I guess again proc_open(), but also make sure to set some reasonable script time limit, so that the hang lets the script continue.

I’m going to guess you mean it hangs, because the API can be prone to that, as it waits for the router to reply with SOMETHING. I used to use a timeout like how other clients still do, but I stopped primarily because of commands like “listen” and “print” with “follow” argument - those can, by design, not return anything to the client in ages, and suddenly return a thing or two. For example, if you wanted to create a script that executes as soon as a firewall rule is added/removed by a 3rd party (e.g. UPnP), you’d need to call such commands, along with a callback to do whatever you want on that event.

When I say “Manually” I mean; from Terminal on the device. Where I get a blank response if I run /system health print.

The boxes that fail are both mipsbe:

routerboard: yes
model: 2011iL
current-firmware: 3.18

routerboard: yes
model: 751U-2HnD
current-firmware: 2.37

You’re right of course, we should just update them.

So wait, does the script exit, or does it simply return no data?

We’re running this php script from cmd in windows. When it reaches:

//get temp
		  $util->setMenu('/system health');
		  $temperature = $util->get(null, 'temperature');

On either of the above devices it immediately stops executing. Regardless of the while loop it’s half way through.

If it crashes, you could run each router as a separate process (see proc_open() and proc_close())

I’ve not used proc_open() before, but having read up now, I can see that working well and being useful for a few other things too. Great suggestion.

Thanks again for all your help.