PEAR2_Net_RouterOS
1.0.0b1
A MikroTik RouterOS client
|
00001 <?php 00002 00022 namespace PEAR2\Net\RouterOS; 00023 00027 use PEAR2\Net\Transmitter as T; 00028 00045 class Communicator 00046 { 00050 const CHARSET_ALL = -1; 00051 00059 const CHARSET_REMOTE = 0; 00060 00068 const CHARSET_LOCAL = 1; 00069 00074 protected static $defaultCharsets = array( 00075 self::CHARSET_REMOTE => null, 00076 self::CHARSET_LOCAL => null 00077 ); 00078 00083 protected $charsets = array(); 00084 00088 protected $trans; 00089 00105 public function __construct($host, $port = 8728, $persist = false, 00106 $timeout = null, $key = '', $context = null 00107 ) { 00108 $this->trans = new T\SocketClientTransmitter( 00109 $host, $port, $persist, $timeout, $key, $context 00110 ); 00111 $this->setCharset( 00112 self::getDefaultCharset(self::CHARSET_ALL), self::CHARSET_ALL 00113 ); 00114 } 00115 00126 public static function iconvStream($in_charset, $out_charset, $stream) 00127 { 00128 $bytes = 0; 00129 $result = fopen('php://temp', 'r+b'); 00130 $iconvFilter = stream_filter_append( 00131 $result, 'convert.iconv.' . $in_charset . '.' . $out_charset, 00132 STREAM_FILTER_WRITE 00133 ); 00134 00135 flock($stream, LOCK_SH); 00136 while (!feof($stream)) { 00137 $bytes += stream_copy_to_stream($stream, $result, 0xFFFFF); 00138 } 00139 fseek($stream, -$bytes, SEEK_CUR); 00140 flock($stream, LOCK_UN); 00141 00142 stream_filter_remove($iconvFilter); 00143 rewind($result); 00144 return $result; 00145 } 00146 00163 public static function setDefaultCharset( 00164 $charset, $charsetType = self::CHARSET_ALL 00165 ) { 00166 if (array_key_exists($charsetType, self::$defaultCharsets)) { 00167 $oldCharset = self::$defaultCharsets[$charsetType]; 00168 self::$defaultCharsets[$charsetType] = $charset; 00169 return $oldCharset; 00170 } else { 00171 $oldCharsets = self::$defaultCharsets; 00172 self::$defaultCharsets = is_array($charset) ? $charset : array_fill( 00173 0, count(self::$defaultCharsets), $charset 00174 ); 00175 return $oldCharsets; 00176 } 00177 } 00178 00190 public static function getDefaultCharset($charsetType) 00191 { 00192 return array_key_exists($charsetType, self::$defaultCharsets) 00193 ? self::$defaultCharsets[$charsetType] : self::$defaultCharsets; 00194 } 00195 00219 public function setCharset($charset, $charsetType = self::CHARSET_ALL) 00220 { 00221 if (array_key_exists($charsetType, $this->charsets)) { 00222 $oldCharset = $this->charsets[$charsetType]; 00223 $this->charsets[$charsetType] = $charset; 00224 return $oldCharset; 00225 } else { 00226 $oldCharsets = $this->charsets; 00227 $this->charsets = is_array($charset) ? $charset : array_fill( 00228 0, count($this->charsets), $charset 00229 ); 00230 return $oldCharsets; 00231 } 00232 } 00233 00246 public function getCharset($charsetType) 00247 { 00248 return array_key_exists($charsetType, $this->charsets) 00249 ? $this->charsets[$charsetType] : $this->charsets; 00250 } 00251 00258 public function getTransmitter() 00259 { 00260 return $this->trans; 00261 } 00262 00274 public function sendWord($word) 00275 { 00276 if (null !== ($remoteCharset = $this->getCharset(self::CHARSET_REMOTE)) 00277 && null !== ($localCharset = $this->getCharset(self::CHARSET_LOCAL)) 00278 ) { 00279 $word = iconv( 00280 $localCharset, 00281 $remoteCharset . '//IGNORE//TRANSLIT', 00282 $word 00283 ); 00284 } 00285 $length = strlen($word); 00286 static::verifyLengthSupport($length); 00287 return $this->trans->send(self::encodeLength($length) . $word); 00288 } 00289 00304 public function sendWordFromStream($prefix, $stream) 00305 { 00306 if (null !== ($remoteCharset = $this->getCharset(self::CHARSET_REMOTE)) 00307 && null !== ($localCharset = $this->getCharset(self::CHARSET_LOCAL)) 00308 ) { 00309 $prefix = iconv( 00310 $localCharset, 00311 $remoteCharset . '//IGNORE//TRANSLIT', 00312 $prefix 00313 ); 00314 $stream = self::iconvStream( 00315 $localCharset, 00316 $remoteCharset . '//IGNORE//TRANSLIT', 00317 $stream 00318 ); 00319 } 00320 00321 flock($stream, LOCK_SH); 00322 00323 $streamPosition = (double) sprintf('%u', ftell($stream)); 00324 fseek($stream, 0, SEEK_END); 00325 $streamLength = ((double) sprintf('%u', ftell($stream))) 00326 - $streamPosition; 00327 fseek($stream, $streamPosition, SEEK_SET); 00328 $totalLength = strlen($prefix) + $streamLength; 00329 static::verifyLengthSupport($totalLength); 00330 00331 $bytes = $this->trans->send(self::encodeLength($totalLength) . $prefix); 00332 $bytes += $this->trans->sendStream($stream); 00333 00334 flock($stream, LOCK_UN); 00335 return $bytes; 00336 } 00337 00350 protected static function verifyLengthSupport($length) 00351 { 00352 if ($length > 0xFFFFFFF) { 00353 throw new LengthException( 00354 'Words with length above 0xFFFFFFF are not supported.', 10, 00355 null, $length 00356 ); 00357 } 00358 } 00359 00367 public static function encodeLength($length) 00368 { 00369 if ($length < 0) { 00370 throw new LengthException( 00371 'Length must not be negative.', 11, null, $length 00372 ); 00373 } elseif ($length < 0x80) { 00374 return chr($length); 00375 } elseif ($length < 0x4000) { 00376 return pack('n', $length |= 0x8000); 00377 } elseif ($length < 0x200000) { 00378 $length |= 0xC00000; 00379 return pack('n', $length >> 8) . chr($length & 0xFF); 00380 } elseif ($length < 0x10000000) { 00381 return pack('N', $length |= 0xE0000000); 00382 } elseif ($length <= 0xFFFFFFFF) { 00383 return chr(0xF0) . pack('N', $length); 00384 } elseif ($length <= 0x7FFFFFFFF) { 00385 $length = 'f' . base_convert($length, 10, 16); 00386 return chr(hexdec(substr($length, 0, 2))) . 00387 pack('N', hexdec(substr($length, 2))); 00388 } 00389 throw new LengthException( 00390 'Length must not be above 0x7FFFFFFFF.', 12, null, $length 00391 ); 00392 } 00393 00403 public function getNextWord() 00404 { 00405 $word = $this->trans->receive(self::decodeLength($this->trans), 'word'); 00406 if (null !== ($remoteCharset = $this->getCharset(self::CHARSET_REMOTE)) 00407 && null !== ($localCharset = $this->getCharset(self::CHARSET_LOCAL)) 00408 ) { 00409 $word = iconv( 00410 $remoteCharset, 00411 $localCharset . '//IGNORE//TRANSLIT', 00412 $word 00413 ); 00414 } 00415 return $word; 00416 } 00417 00427 public function getNextWordAsStream() 00428 { 00429 $filters = array(); 00430 if (null !== ($remoteCharset = $this->getCharset(self::CHARSET_REMOTE)) 00431 && null !== ($localCharset = $this->getCharset(self::CHARSET_LOCAL)) 00432 ) { 00433 $filters[ 00434 'convert.iconv.' . 00435 $remoteCharset . '.' . $localCharset . '//IGNORE//TRANSLIT' 00436 ] = array(); 00437 } 00438 $stream = $this->trans->receiveStream( 00439 self::decodeLength($this->trans), $filters, 'stream word' 00440 ); 00441 return $stream; 00442 } 00443 00455 public static function decodeLength(T\StreamTransmitter $trans) 00456 { 00457 $byte = ord($trans->receive(1, 'initial length byte')); 00458 if ($byte & 0x80) { 00459 if (($byte & 0xC0) === 0x80) { 00460 return (($byte & 077) << 8 ) + ord($trans->receive(1)); 00461 } elseif (($byte & 0xE0) === 0xC0) { 00462 $u = unpack('n~', $trans->receive(2)); 00463 return (($byte & 037) << 16 ) + $u['~']; 00464 } elseif (($byte & 0xF0) === 0xE0) { 00465 $u = unpack('n~/C~~', $trans->receive(3)); 00466 return (($byte & 017) << 24 ) + ($u['~'] << 8) + $u['~~']; 00467 } elseif (($byte & 0xF8) === 0xF0) { 00468 $u = unpack('N~', $trans->receive(4)); 00469 return (($byte & 007) * 0x100000000/* '<< 32' or '2^32' */) 00470 + (double) sprintf('%u', $u['~']); 00471 } 00472 throw new NotSupportedException( 00473 'Unknown control byte encountered.', 13, null, $byte 00474 ); 00475 } else { 00476 return $byte; 00477 } 00478 } 00479 00485 public function close() 00486 { 00487 return $this->trans->close(); 00488 } 00489 00490 }