PEAR2_Net_RouterOS
1.0.0b1
A MikroTik RouterOS client
|
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 }