PEAR2_Net_RouterOS  1.0.0b1
A MikroTik RouterOS client
PEAR2/Net/RouterOS/Client.php
Go to the documentation of this file.
00001 <?php
00002 
00022 namespace PEAR2\Net\RouterOS;
00023 
00035 class Client
00036 {
00041     const FILTER_CALLBACK = 1;
00046     const FILTER_BUFFER = 2;
00050     const FILTER_ALL = 3;
00051 
00055     protected $com;
00056 
00060     protected $pendingRequestsCount = 0;
00061 
00067     protected $responseBuffer = array();
00068 
00073     protected $callbacks = array();
00074 
00078     private $_streamResponses = false;
00079 
00100     public function __construct($host, $username, $password = '', $port = 8728,
00101         $persist = false, $timeout = null, $context = null
00102     ) {
00103         $this->com = new Communicator(
00104             $host, $port, $persist, $timeout, $username, $context
00105         );
00106         //Login the user if necessary
00107         if ($this->com->getTransmitter()->isFresh()) {
00108             if (!self::login($this->com, $username, $password)) {
00109                 $this->com->close();
00110                 throw new DataFlowException(
00111                     'Invalid username or password supplied.', 100
00112                 );
00113             }
00114         }
00115     }
00116 
00126     public static function login(Communicator $com, $username, $password = '')
00127     {
00128         try {
00129             $request = new Request('/login');
00130             $request->send($com);
00131             $response = new Response($com);
00132             $request->setArgument('name', $username);
00133             $request->setArgument(
00134                 'response', '00' . md5(
00135                     chr(0) . $password
00136                     . pack('H*', $response->getArgument('ret'))
00137                 )
00138             );
00139             $request->send($com);
00140             $response = new Response($com);
00141             return $response->getType() === Response::TYPE_FINAL
00142                 && null === $response->getArgument('ret');
00143         } catch (\Exception $e) {
00144             throw ($e instanceof NotSupportedException
00145             || $e instanceof UnexpectedValueException
00146             || !$com->getTransmitter()->isDataAwaiting()) ? new SocketException(
00147                 'This is not a compatible RouterOS service', 101, $e
00148             ) : $e;
00149         }
00150     }
00151     
00177     public function setCharset(
00178         $charset, $charsetType = Communicator::CHARSET_ALL
00179     ) {
00180         return $this->com->setCharset($charset, $charsetType);
00181     }
00182     
00195     public function getCharset($charsetType)
00196     {
00197         return $this->com->getCharset($charsetType);
00198     }
00199 
00216     public function sendAsync(Request $request, $callback = null)
00217     {
00218         //Error checking
00219         $tag = $request->getTag();
00220         if (null === $tag) {
00221             throw new DataFlowException(
00222                 'Asynchonous commands must have a tag.', 102
00223             );
00224         }
00225         if ($this->isRequestActive($tag)) {
00226             throw new DataFlowException(
00227                 'There must not be multiple active requests sharing a tag.', 103
00228             );
00229         }
00230         
00231         $this->send($request);
00232 
00233         if (null === $callback) {
00234             //Register the request at the buffer
00235             $this->responseBuffer[$tag] = array();
00236         } else {
00237             //Prepare the callback
00238             $this->callbacks[$tag] = $callback;
00239         }
00240         return $this;
00241     }
00242 
00257     public function isRequestActive($tag, $filter = self::FILTER_ALL)
00258     {
00259         $result = 0;
00260         if (self::FILTER_CALLBACK === ($filter & self::FILTER_CALLBACK)) {
00261             $result |= (int) array_key_exists($tag, $this->callbacks);
00262         }
00263         if (self::FILTER_BUFFER === ($filter & self::FILTER_BUFFER)) {
00264             $result |= (int) array_key_exists($tag, $this->responseBuffer);
00265         }
00266         return 0 !== $result;
00267     }
00268 
00278     public function sendSync(Request $request)
00279     {
00280         $this->send($request);
00281         return $this->completeRequest($request->getTag());
00282     }
00283 
00298     public function completeRequest($tag = null)
00299     {
00300         $isTagNull = null === $tag;
00301         $result = $isTagNull ? array()
00302             : $this->extractNewResponses($tag)->toArray();
00303         while ((!$isTagNull && $this->isRequestActive($tag))
00304         || ($isTagNull && 0 !== $this->getPendingRequestsCount())
00305         ) {
00306             $newReply = $this->dispatchNextResponse();
00307             if ($newReply->getTag() === $tag) {
00308                 if ($isTagNull) {
00309                     $result[] = $newReply;
00310                 }
00311                 if ($newReply->getType() === Response::TYPE_FINAL) {
00312                     if (!$isTagNull) {
00313                         $result = array_merge(
00314                             $result,
00315                             $this->extractNewResponses($tag)->toArray()
00316                         );
00317                     }
00318                     break;
00319                 }
00320             }
00321         }
00322         return new ResponseCollection($result);
00323     }
00324 
00338     public function extractNewResponses($tag = null)
00339     {
00340         if (null === $tag) {
00341             $result = array();
00342             foreach (array_keys($this->responseBuffer) as $tag) {
00343                 $result = array_merge(
00344                     $result, $this->extractNewResponses($tag)->toArray()
00345                 );
00346             }
00347             return new ResponseCollection($result);
00348         } elseif ($this->isRequestActive($tag, self::FILTER_CALLBACK)) {
00349             return new ResponseCollection(array());
00350         } elseif ($this->isRequestActive($tag, self::FILTER_BUFFER)) {
00351             $result = $this->responseBuffer[$tag];
00352             if (!empty($result)) {
00353                 if ($result[count($result) - 1]->getType()
00354                     === Response::TYPE_FINAL
00355                 ) {
00356                     unset($this->responseBuffer[$tag]);
00357                 } else {
00358                     $this->responseBuffer[$tag] = array();
00359                 }
00360             }
00361             return new ResponseCollection($result);
00362         } else {
00363             throw new DataFlowException(
00364                 'No such request, or the request has already finished.', 104
00365             );
00366         }
00367     }
00368 
00383     public function loop($timeout = 0)
00384     {
00385         if ($this->getPendingRequestsCount() !== 0) {
00386             $start = microtime(true);
00387             do {
00388                 $this->dispatchNextResponse();
00389             } while (
00390             ((microtime(true) - $start) <= $timeout)
00391             || (0 === $timeout && $this->getPendingRequestsCount() !== 0)
00392             );
00393         }
00394         return $this->getPendingRequestsCount() !== 0;
00395     }
00396 
00403     public function getPendingRequestsCount()
00404     {
00405         return $this->pendingRequestsCount;
00406     }
00407 
00424     public function cancelRequest($tag = null)
00425     {
00426         $cancelRequest = new Request('/cancel');
00427         $tagIsNotNull = !(null === $tag);
00428         if ($tagIsNotNull) {
00429             if ($this->isRequestActive($tag)) {
00430                 $cancelRequest->setArgument('tag', $tag);
00431             } else {
00432                 throw new DataFlowException(
00433                     'No such request. Canceling aborted.', 105
00434                 );
00435             }
00436         }
00437         $this->sendSync($cancelRequest);
00438 
00439         if ($tagIsNotNull) {
00440             if ($this->isRequestActive($tag, self::FILTER_BUFFER)) {
00441                 unset($this->responseBuffer[$tag]);
00442             } elseif ($this->isRequestActive($tag, self::FILTER_CALLBACK)) {
00443                 unset($this->callbacks[$tag]);
00444             }
00445             $this->pendingRequestsCount--;
00446         } else {
00447             $this->responseBuffer = array();
00448             $this->callbacks = array();
00449             $this->pendingRequestsCount = 0;
00450         }
00451         return $this;
00452     }
00453 
00467     public function setStreamResponses($streamResponses)
00468     {
00469         $oldStreamResponses = $this->_streamResponses;
00470         $this->_streamResponses = (bool) $streamResponses;
00471         return $oldStreamResponses;
00472     }
00473 
00482     public function getStreamResponses()
00483     {
00484         return $this->_streamResponses;
00485     }
00486 
00496     public function close()
00497     {
00498         $result = false;
00499         try {
00500             $response = $this->sendSync(new Request('/quit'));
00501             $result = $this->com->close()
00502                 && $response->getType() === Response::TYPE_FATAL;
00503         } catch (SocketException $e) {
00504             $result = $e->getCode() === 205;
00505         }
00506         $this->callbacks = array();
00507         $this->pendingRequestsCount = 0;
00508         return $result;
00509     }
00510 
00520     protected function send(Request $request)
00521     {
00522         $request->send($this->com);
00523         $this->pendingRequestsCount++;
00524         return $this;
00525     }
00526 
00535     protected function dispatchNextResponse()
00536     {
00537         $response = new Response($this->com, $this->_streamResponses);
00538         if ($response->getType() === Response::TYPE_FATAL) {
00539             $this->pendingRequestsCount = 0;
00540             $this->com->close();
00541             return $response;
00542         }
00543 
00544         $tag = $response->getTag();
00545         $isLastForRequest = $response->getType() === Response::TYPE_FINAL;
00546         if ($isLastForRequest) {
00547             $this->pendingRequestsCount--;
00548         }
00549 
00550         if (null !== $tag) {
00551             if ($this->isRequestActive($tag, self::FILTER_CALLBACK)) {
00552                 if ($this->callbacks[$tag]($response, $this)) {
00553                     $this->cancelRequest($tag);
00554                 } elseif ($isLastForRequest) {
00555                     unset($this->callbacks[$tag]);
00556                 }
00557             } else {
00558                 $this->responseBuffer[$tag][] = $response;
00559             }
00560         }
00561         return $response;
00562     }
00563 
00564 }