Browse Source

code style finetyne

pull/8/head
Paul Rock 7 years ago
parent
commit
efd9e83179
  1. 25
      src/APIConnector.php
  2. 130
      src/APILengthCoDec.php
  3. 40
      src/Client.php
  4. 39
      src/Helpers/BinaryStringHelper.php
  5. 23
      src/Interfaces/StreamInterface.php
  6. 21
      src/SocketTrait.php
  7. 70
      src/Streams/ResourceStream.php
  8. 48
      src/Streams/StringStream.php
  9. 78
      tests/APIConnectorTest.php
  10. 4
      tests/APILengthCoDecTest.php
  11. 159
      tests/Streams/ResourceStreamTest.php
  12. 80
      tests/Streams/StringStreamTest.php

25
src/APIConnector.php

@ -1,17 +1,17 @@
<?php <?php
namespace RouterOS; namespace RouterOS;
use RouterOS\Interfaces\StreamInterface; use RouterOS\Interfaces\StreamInterface;
/** /**
* class APIConnector
*
* Class APIConnector
*
* Implement middle level dialog with router by masking word dialog implementation to client class * Implement middle level dialog with router by masking word dialog implementation to client class
* *
* @package RouterOS * @package RouterOS
* @since 0.9 * @since 0.9
*/ */
class APIConnector class APIConnector
{ {
/** /**
@ -37,20 +37,23 @@ class APIConnector
* Length of the WORD should be given as count of bytes that are going to be sent * Length of the WORD should be given as count of bytes that are going to be sent
* *
* @return string The word content, en empty string for end of SENTENCE * @return string The word content, en empty string for end of SENTENCE
*/
public function readWord() : string
*/
public function readWord(): string
{ {
// Get length of next word // Get length of next word
$length = APILengthCoDec::decodeLength($this->stream); $length = APILengthCoDec::decodeLength($this->stream);
if ($length>0) {
return $this->stream->read($length);
}
return '';
return ($length > 0) ? $this->stream->read($length) : '';
} }
public function writeWord(string $word)
/**
* Write word to stream
*
* @param string $word
* @return int return number of written bytes
*/
public function writeWord(string $word): int
{ {
$encodedLength = APILengthCoDec::encodeLength(strlen($word)); $encodedLength = APILengthCoDec::encodeLength(strlen($word));
return $this->stream->write($encodedLength.$word);
return $this->stream->write($encodedLength . $word);
} }
} }

130
src/APILengthCoDec.php

@ -7,16 +7,15 @@ use RouterOS\Helpers\BinaryStringHelper;
/** /**
* class APILengthCoDec * class APILengthCoDec
*
*
* Coder / Decoder for length field in mikrotik API communication protocol * Coder / Decoder for length field in mikrotik API communication protocol
* *
* @package RouterOS * @package RouterOS
* @since 0.9 * @since 0.9
*/ */
class APILengthCoDec class APILengthCoDec
{ {
public static function encodeLength(int $length)
public static function encodeLength(int $length): string
{ {
// Encode the length : // Encode the length :
// - if length <= 0x7F (binary : 01111111 => 7 bits set to 1) // - if length <= 0x7F (binary : 01111111 => 7 bits set to 1)
@ -26,63 +25,63 @@ class APILengthCoDec
// - length <= 0x3FFF (binary : 00111111 11111111 => 14 bits set to 1) // - length <= 0x3FFF (binary : 00111111 11111111 => 14 bits set to 1)
// - encode length with two bytes // - encode length with two bytes
// - set length value to 0x8000 (=> 10000000 00000000) // - set length value to 0x8000 (=> 10000000 00000000)
// - add length : as length maximumal value is 14 bits to 1, this does not modify the 2 most significants bits (10)
// - add length : as length maximumal value is 14 bits to 1, this does not modify the 2 most significance bits (10)
// - end // - end
// => minimal encoded value is 10000000 10000000 // => minimal encoded value is 10000000 10000000
// - length <= 0x1FFFFF (binary : 00011111 11111111 11111111 => 21 bits set to 1) // - length <= 0x1FFFFF (binary : 00011111 11111111 11111111 => 21 bits set to 1)
// - encode length with three bytes // - encode length with three bytes
// - set length value to 0xC00000 (binary : 11000000 00000000 00000000) // - set length value to 0xC00000 (binary : 11000000 00000000 00000000)
// - add length : as length maximal vlaue is 21 bits to 1, this does not modify the 3 most significants bits (110)
// - add length : as length maximal vlaue is 21 bits to 1, this does not modify the 3 most significance bits (110)
// - end // - end
// => minimal encoded value is 11000000 01000000 00000000 // => minimal encoded value is 11000000 01000000 00000000
// - length <= 0x0FFFFFFF (binary : 00001111 11111111 11111111 11111111 => 28 bits set to 1) // - length <= 0x0FFFFFFF (binary : 00001111 11111111 11111111 11111111 => 28 bits set to 1)
// - encode length with four bytes // - encode length with four bytes
// - set length value to 0xE0000000 (binary : 11100000 00000000 00000000 00000000) // - set length value to 0xE0000000 (binary : 11100000 00000000 00000000 00000000)
// - add length : as length maximal vlaue is 28 bits to 1, this does not modify the 4 most significants bits (1110)
// - add length : as length maximal vlaue is 28 bits to 1, this does not modify the 4 most significance bits (1110)
// - end // - end
// => minimal encoded value is 11100000 00100000 00000000 00000000 // => minimal encoded value is 11100000 00100000 00000000 00000000
// - length <= 0x7FFFFFFFFF (binary : 00000111 11111111 11111111 11111111 11111111 => 35 bits set to 1) // - length <= 0x7FFFFFFFFF (binary : 00000111 11111111 11111111 11111111 11111111 => 35 bits set to 1)
// - encode length with five bytes // - encode length with five bytes
// - set length value to 0xF000000000 (binary : 11110000 00000000 00000000 00000000 00000000) // - set length value to 0xF000000000 (binary : 11110000 00000000 00000000 00000000 00000000)
// - add length : as length maximal vlaue is 35 bits to 1, this does not modify the 5 most significants bits (11110)
// - add length : as length maximal vlaue is 35 bits to 1, this does not modify the 5 most significance bits (11110)
// - end // - end
// - length > 0x7FFFFFFFFF : not supported // - length > 0x7FFFFFFFFF : not supported
if ($length<0)
{
throw new \DomainException(sprintf("Length of word can not be negative (%d)", $length));
if ($length < 0) {
throw new \DomainException("Length of word can not be negative ($length)");
} }
if ($length<=0x7F) {
if ($length <= 0x7F) {
return BinaryStringHelper::IntegerToNBOBinaryString($length); return BinaryStringHelper::IntegerToNBOBinaryString($length);
} }
else if ($length<=0x3FFF) {
return BinaryStringHelper::IntegerToNBOBinaryString(0x8000+$length);
if ($length <= 0x3FFF) {
return BinaryStringHelper::IntegerToNBOBinaryString(0x8000 + $length);
} }
else if ($length<=0x1FFFFF) {
return BinaryStringHelper::IntegerToNBOBinaryString(0xC00000+$length);
if ($length <= 0x1FFFFF) {
return BinaryStringHelper::IntegerToNBOBinaryString(0xC00000 + $length);
} }
else if ($length<=0x0FFFFFFF){
return BinaryStringHelper::IntegerToNBOBinaryString(0xE0000000+$length);
if ($length <= 0x0FFFFFFF) {
return BinaryStringHelper::IntegerToNBOBinaryString(0xE0000000 + $length);
} // cannot compare with 0x7FFFFFFFFF on 32 bits systems
if (PHP_INT_SIZE < 8) {
// Cannot be done on 32 bits systems
// PHP5 windows versions of php, even on 64 bits systems was impacted
// see : https://stackoverflow.com/questions/27865340/php-int-size-returns-4-but-my-operating-system-is-64-bit
// @codeCoverageIgnoreStart
throw new \OverflowException("Your system is using 32 bits integers, cannot encode length of $length bytes on this system");
// @codeCoverageIgnoreEnd
} }
// cannot compare with 0x7FFFFFFFFF on 32 bits systems
else {
if (PHP_INT_SIZE<8) {
// Cannot be done on 32 bits systems
// PHP5 windows versions of php, even on 64 bits systems was impacted
// see : https://stackoverflow.com/questions/27865340/php-int-size-returns-4-but-my-operating-system-is-64-bit
// @codeCoverageIgnoreStart
throw new \OverflowException(sprintf("Your system is using 32 bits integers, cannot encode length of %d bytes on this system", $length));
// @codeCoverageIgnoreEnd
}
if ($length<=0x7FFFFFFFFF)
{
return BinaryStringHelper::IntegerToNBOBinaryString(0xF000000000+$length);
}
if ($length <= 0x7FFFFFFFFF) {
return BinaryStringHelper::IntegerToNBOBinaryString(0xF000000000 + $length);
} }
throw new \DomainException(sprintf('Length of word too huge (%x)', $length));
throw new \DomainException("Length of word too huge ($length)");
} }
// Decode length of data when reading : // Decode length of data when reading :
@ -107,7 +106,7 @@ class APILengthCoDec
// After receiving unknown control byte API client cannot proceed, because it cannot know how to interpret following bytes // After receiving unknown control byte API client cannot proceed, because it cannot know how to interpret following bytes
// Currently control bytes are not used // Currently control bytes are not used
public static function decodeLength(StreamInterface $stream)
public static function decodeLength(StreamInterface $stream): int
{ {
// if (false === is_resource($stream)) { // if (false === is_resource($stream)) {
// throw new \InvalidArgumentException( // throw new \InvalidArgumentException(
@ -122,76 +121,81 @@ class APILengthCoDec
$firstByte = ord($stream->read(1)); $firstByte = ord($stream->read(1));
// If first byte is not set, length is the value of the byte // If first byte is not set, length is the value of the byte
if (0==($firstByte&0x80)) {
if (0 === ($firstByte & 0x80)) {
return $firstByte; return $firstByte;
} }
// if 10xxxxxx, length is 2 bytes encoded // if 10xxxxxx, length is 2 bytes encoded
if (0x80==($firstByte&0xC0))
{
// Set 2 most significants bits to 0
if (0x80 === ($firstByte & 0xC0)) {
// Set 2 most significands bits to 0
$result = $firstByte & 0x3F; $result = $firstByte & 0x3F;
// shift left 8 bits to have 2 bytes // shift left 8 bits to have 2 bytes
$result = $result << 8;
// read next byte and use it as least significant
$result <<= 8;
// read next byte and use it as least significant
$result |= ord($stream->read(1)); $result |= ord($stream->read(1));
return $result; return $result;
} }
// if 110xxxxx, length is 3 bytes encoded // if 110xxxxx, length is 3 bytes encoded
if (0xC0 == ($firstByte & 0xE0))
{
// Set 3 most significants bits to 0
if (0xC0 === ($firstByte & 0xE0)) {
// Set 3 most significands bits to 0
$result = $firstByte & 0x1F; $result = $firstByte & 0x1F;
// shift left 16 bits to have 3 bytes // shift left 16 bits to have 3 bytes
$result = $result << 16;
$result <<= 16;
// read next 2 bytes as value and use it as least significant position // read next 2 bytes as value and use it as least significant position
$result |= (ord($stream->read(1)) << 8); $result |= (ord($stream->read(1)) << 8);
$result |= ord($stream->read(1)); $result |= ord($stream->read(1));
return $result;
return $result;
} }
// if 1110xxxx, length is 4 bytes encoded // if 1110xxxx, length is 4 bytes encoded
if (0xE0 == ($firstByte & 0xF0))
{
// Set 4 most significants bits to 0
if (0xE0 === ($firstByte & 0xF0)) {
// Set 4 most significance bits to 0
$result = $firstByte & 0x0F; $result = $firstByte & 0x0F;
// shift left 24 bits to have 4 bytes // shift left 24 bits to have 4 bytes
$result = $result << 24;
$result <<= 24;
// read next 3 bytes as value and use it as least significant position // read next 3 bytes as value and use it as least significant position
$result |= (ord($stream->read(1)) << 16); $result |= (ord($stream->read(1)) << 16);
$result |= (ord($stream->read(1)) << 8); $result |= (ord($stream->read(1)) << 8);
$result |= ord($stream->read(1)); $result |= ord($stream->read(1));
return $result;
return $result;
} }
// if 11110xxx, length is 5 bytes encoded // if 11110xxx, length is 5 bytes encoded
if (0xF0 == ($firstByte & 0xF8))
{
// Not possibe on 32 bits systems
if (PHP_INT_SIZE<8) {
if (0xF0 === ($firstByte & 0xF8)) {
// Not possible on 32 bits systems
if (PHP_INT_SIZE < 8) {
// Cannot be done on 32 bits systems // Cannot be done on 32 bits systems
// PHP5 windows versions of php, even on 64 bits systems was impacted // PHP5 windows versions of php, even on 64 bits systems was impacted
// see : https://stackoverflow.com/questions/27865340/php-int-size-returns-4-but-my-operating-system-is-64-bit // see : https://stackoverflow.com/questions/27865340/php-int-size-returns-4-but-my-operating-system-is-64-bit
// How can we test it ? // How can we test it ?
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
throw new \OverflowException(sprintf("Your system is using 32 bits integers, cannot decode this value (%x) on this system", $firstByte));
throw new \OverflowException("Your system is using 32 bits integers, cannot decode this value ($firstByte) on this system");
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
} }
// Set 5 most significants bits to 0
// Set 5 most significance bits to 0
$result = $firstByte & 0x07; $result = $firstByte & 0x07;
// shift left 232 bits to have 5 bytes // shift left 232 bits to have 5 bytes
$result = $result << 32;
$result <<= 32;
// read next 4 bytes as value and use it as least significant position // read next 4 bytes as value and use it as least significant position
$result |= (ord($stream->read(1)) << 24); $result |= (ord($stream->read(1)) << 24);
$result |= (ord($stream->read(1)) << 16); $result |= (ord($stream->read(1)) << 16);
$result |= (ord($stream->read(1)) << 8); $result |= (ord($stream->read(1)) << 8);
$result |= ord($stream->read(1)); $result |= ord($stream->read(1));
return $result;
return $result;
} }
// Now the only solution is 5 most significants bits are set to 1 (11111xxx)
// Now the only solution is 5 most significance bits are set to 1 (11111xxx)
// This is a control word, not implemented by Mikrotik for the moment // This is a control word, not implemented by Mikrotik for the moment
throw new \UnexpectedValueException("Control Word found\n");
throw new \UnexpectedValueException('Control Word found');
} }
}
}

40
src/Client.php

@ -18,27 +18,6 @@ class Client implements Interfaces\ClientInterface
use SocketTrait; use SocketTrait;
/** /**
* Socket resource
*
* @var resource|null
*/
private $_socket;
/**
* Code of error
*
* @var int
*/
private $_socket_err_num;
/**
* Description of socket error
*
* @var string
*/
private $_socket_err_str;
/**
* Configuration of connection * Configuration of connection
* *
* @var \RouterOS\Config * @var \RouterOS\Config
@ -48,10 +27,10 @@ class Client implements Interfaces\ClientInterface
/** /**
* API communication object * API communication object
* *
* @var APIConnector
* @var \RouterOS\APIConnector
*/ */
private $connector;
private $_connector;
/** /**
* Client constructor. * Client constructor.
@ -121,7 +100,6 @@ class Client implements Interfaces\ClientInterface
* *
* @param string|array|\RouterOS\Query $query * @param string|array|\RouterOS\Query $query
* @return \RouterOS\Client * @return \RouterOS\Client
* @throws \RouterOS\Exceptions\ClientException
* @throws \RouterOS\Exceptions\QueryException * @throws \RouterOS\Exceptions\QueryException
*/ */
public function write($query): Client public function write($query): Client
@ -139,11 +117,11 @@ class Client implements Interfaces\ClientInterface
// Send commands via loop to router // Send commands via loop to router
foreach ($query->getQuery() as $command) { foreach ($query->getQuery() as $command) {
$this->connector->writeWord(trim($command));
$this->_connector->writeWord(trim($command));
} }
// Write zero-terminator (empty string) // Write zero-terminator (empty string)
$this->connector->writeWord('');
$this->_connector->writeWord('');
return $this; return $this;
} }
@ -156,7 +134,7 @@ class Client implements Interfaces\ClientInterface
* Each block end with an zero byte (empty line) * Each block end with an zero byte (empty line)
* Reply ends with a complete !done or !fatal block (ended with 'empty line') * Reply ends with a complete !done or !fatal block (ended with 'empty line')
* A !fatal block precedes TCP connexion close * A !fatal block precedes TCP connexion close
*
*
* @param bool $parse * @param bool $parse
* @return array * @return array
*/ */
@ -169,9 +147,9 @@ class Client implements Interfaces\ClientInterface
// Read answer from socket in loop // Read answer from socket in loop
while (true) { while (true) {
$word = $this->connector->readWord();
$word = $this->_connector->readWord();
if (''===$word) {
if ('' === $word) {
if ($lastReply) { if ($lastReply) {
// We received a !done or !fatal message in a precedent loop // We received a !done or !fatal message in a precedent loop
// response is complete // response is complete
@ -202,7 +180,6 @@ class Client implements Interfaces\ClientInterface
* *
* @param string|array|\RouterOS\Query $query * @param string|array|\RouterOS\Query $query
* @return \RouterOS\Client * @return \RouterOS\Client
* @throws \RouterOS\Exceptions\ClientException
* @throws \RouterOS\Exceptions\QueryException * @throws \RouterOS\Exceptions\QueryException
*/ */
public function w($query): Client public function w($query): Client
@ -373,7 +350,7 @@ class Client implements Interfaces\ClientInterface
// If socket is active // If socket is active
if (null !== $this->getSocket()) { if (null !== $this->getSocket()) {
$this->connector = new APIConnector(new Streams\ResourceStream($this->getSocket()));
$this->_connector = new APIConnector(new Streams\ResourceStream($this->getSocket()));
// If we logged in then exit from loop // If we logged in then exit from loop
if (true === $this->login()) { if (true === $this->login()) {
$connected = true; $connected = true;
@ -382,7 +359,6 @@ class Client implements Interfaces\ClientInterface
// Else close socket and start from begin // Else close socket and start from begin
$this->closeSocket(); $this->closeSocket();
$this->stream = null;
} }
// Sleep some time between tries // Sleep some time between tries

39
src/Helpers/BinaryStringHelper.php

@ -1,60 +1,65 @@
<?php <?php
namespace RouterOS\Helpers; namespace RouterOS\Helpers;
/** /**
* class BinaryStringHelper * class BinaryStringHelper
*
* Strings and binary datas manipulations
*
* Strings and binary data manipulations
* *
* @package RouterOS\Helpers * @package RouterOS\Helpers
* @since 0.9 * @since 0.9
*/ */
class BinaryStringHelper class BinaryStringHelper
{ {
/** /**
* Convert an integer value in a "Network Byte Ordered" binary string (most significant value first) * Convert an integer value in a "Network Byte Ordered" binary string (most significant value first)
* *
* Reads the integer, starting from the most signficant byte, one byte a time.
* Reads the integer, starting from the most significant byte, one byte a time.
* Once reach a non 0 byte, construct a binary string representing this values * Once reach a non 0 byte, construct a binary string representing this values
* ex :
* ex :
* 0xFF7 => chr(0x0F).chr(0xF7) * 0xFF7 => chr(0x0F).chr(0xF7)
* 0x12345678 => chr(0x12).chr(0x34).chr(0x56).chr(0x76) * 0x12345678 => chr(0x12).chr(0x34).chr(0x56).chr(0x76)
* Compatible with 8, 16, 32, 64 etc.. bits systems * Compatible with 8, 16, 32, 64 etc.. bits systems
* *
* @see https://en.wikipedia.org/wiki/Endianness * @see https://en.wikipedia.org/wiki/Endianness
* @param int $value the integer value to be converted
* @return string the binary string
*/
public static function IntegerToNBOBinaryString(int $value)
* @param int $value the integer value to be converted
* @return string the binary string
*/
public static function IntegerToNBOBinaryString(int $value): string
{ {
// Initialize an empty string // Initialize an empty string
$buffer = ''; $buffer = '';
// Lets start from the most significant byte // Lets start from the most significant byte
for ($i=(PHP_INT_SIZE-1); $i>=0; $i--) {
for ($i = (PHP_INT_SIZE - 1); $i >= 0; $i--) {
// Prepare a mask to keep only the most significant byte of $value // Prepare a mask to keep only the most significant byte of $value
$mask = 0xFF << ($i*8);
$mask = 0xFF << ($i * 8);
// If the most significant byte is not 0, the final string must contain it // If the most significant byte is not 0, the final string must contain it
// If we have already started to construct the string (i.e. there are more signficant digits) // If we have already started to construct the string (i.e. there are more signficant digits)
// we must set the byte, even if it is a 0. // we must set the byte, even if it is a 0.
// 0xFF00FF, for example, require to set the second byte byte with a 0 value // 0xFF00FF, for example, require to set the second byte byte with a 0 value
if (($value & $mask) || strlen($buffer)!=0) {
// Get the curent byte by shifting it to least significant position and add it to the string
if (($value & $mask) || $buffer !== '') {
// Get the current byte by shifting it to least significant position and add it to the string
// 0xFF12345678 => 0xFF // 0xFF12345678 => 0xFF
$byte = $value>>(8*$i);
$byte = $value >> (8 * $i);
$buffer .= chr($byte); $buffer .= chr($byte);
// Set the most significant byte to 0 so we can restart the process being shure // Set the most significant byte to 0 so we can restart the process being shure
// that the value is left padded with 0 // that the value is left padded with 0
// 0xFF12345678 => 0x12345678 // 0xFF12345678 => 0x12345678
// -1 = 0xFFFFF.... (number of F depend of PHP_INT_SIZE ) // -1 = 0xFFFFF.... (number of F depend of PHP_INT_SIZE )
$mask = -1 >> ((PHP_INT_SIZE-$i)*8);
$mask = -1 >> ((PHP_INT_SIZE - $i) * 8);
$value &= $mask; $value &= $mask;
} }
} }
// Special case, 0 will not fill the buffer, have to construct it manualy // Special case, 0 will not fill the buffer, have to construct it manualy
if (0==$value) {
if (0 === $value) {
$buffer = chr(0); $buffer = chr(0);
} }
return $buffer; return $buffer;
} }
}
}

23
src/Interfaces/StreamInterface.php

@ -1,4 +1,5 @@
<?php <?php
namespace RouterOS\Interfaces; namespace RouterOS\Interfaces;
/** /**
@ -17,23 +18,29 @@ interface StreamInterface
* Reads $length bytes from the stream, returns the bytes into a string * Reads $length bytes from the stream, returns the bytes into a string
* Must be binary safe (as fread). * Must be binary safe (as fread).
* *
* @param int $length the numer of bytes to read
* @return string a binary string containing the readed byes
* @param int $length the numer of bytes to read
* @return string a binary string containing the readed byes
*/ */
public function read(int $length) : string;
public function read(int $length): string;
/** /**
* Writes a string to a stream * Writes a string to a stream
* *
* Write $length bytes of string, if not mentioned, write all the string
* Write $length bytes of string, if not mentioned, write all the string
* Must be binary safe (as fread). * Must be binary safe (as fread).
* if $length is greater than string length, write all string and return number of writen bytes * if $length is greater than string length, write all string and return number of writen bytes
* if $length os smaller than string length, remaining bytes are losts. * if $length os smaller than string length, remaining bytes are losts.
* *
* @param int $length the numer of bytes to read
* @return int the numer of writen bytes
* @param string $string
* @param int $length the number of bytes to read
* @return int return number of written bytes
*/ */
public function write(string $string, $length=-1) : int;
public function write(string $string, int $length = -1): int;
/**
* Close stream connection
*
* @return void
*/
public function close(); public function close();
}
}

21
src/SocketTrait.php

@ -7,6 +7,27 @@ use RouterOS\Exceptions\ClientException;
trait SocketTrait trait SocketTrait
{ {
/** /**
* Socket resource
*
* @var resource|null
*/
private $_socket;
/**
* Code of error
*
* @var int
*/
private $_socket_err_num;
/**
* Description of socket error
*
* @var string
*/
private $_socket_err_str;
/**
* Initiate socket session * Initiate socket session
* *
* @return void * @return void

70
src/Streams/ResourceStream.php

@ -1,4 +1,5 @@
<?php <?php
namespace RouterOS\Streams; namespace RouterOS\Streams;
use RouterOS\Interfaces\StreamInterface; use RouterOS\Interfaces\StreamInterface;
@ -6,20 +7,24 @@ use RouterOS\Exceptions\StreamException;
/** /**
* class ResourceStream * class ResourceStream
*
*
* Stream using a resource (socket, file, pipe etc.) * Stream using a resource (socket, file, pipe etc.)
* *
* @package RouterOS * @package RouterOS
* @since 0.9 * @since 0.9
*/ */
class ResourceStream implements StreamInterface class ResourceStream implements StreamInterface
{ {
protected $stream; protected $stream;
/**
* ResourceStream constructor.
*
* @param $stream
*/
public function __construct($stream) public function __construct($stream)
{ {
if (false === is_resource($stream)) {
if (!is_resource($stream)) {
throw new \InvalidArgumentException( throw new \InvalidArgumentException(
sprintf( sprintf(
'Argument must be a valid resource type. %s given.', 'Argument must be a valid resource type. %s given.',
@ -27,54 +32,79 @@ class ResourceStream implements StreamInterface
) )
); );
} }
// // TODO : Should we verify the resource type ?
// TODO: Should we verify the resource type?
$this->stream = $stream; $this->stream = $stream;
} }
/** /**
* {@inheritDoc}
* @throws \InvalidArgumentException
* @param int $length
* @return string
* @throws \RouterOS\Exceptions\StreamException
* @throws \InvalidArgumentException
*/ */
public function read(int $length) : string
public function read(int $length): string
{ {
if ($length<=0) {
throw new \InvalidArgumentException("Cannot read zero ot negative count of bytes from a stream");
if ($length <= 0) {
throw new \InvalidArgumentException('Cannot read zero ot negative count of bytes from a stream');
} }
// TODO: Ignore errors here, but why?
$result = @fread($this->stream, $length); $result = @fread($this->stream, $length);
if (false === $result) { if (false === $result) {
throw new StreamException(sprintf("Error reading %d bytes", $length));
throw new StreamException("Error reading $length bytes");
} }
return $result; return $result;
} }
/** /**
* {@inheritDoc}
* Writes a string to a stream
*
* Write $length bytes of string, if not mentioned, write all the string
* Must be binary safe (as fread).
* if $length is greater than string length, write all string and return number of writen bytes
* if $length os smaller than string length, remaining bytes are losts.
*
* @param string $string
* @param int|null $length the numer of bytes to read
* @return int the number of written bytes
* @throws \RouterOS\Exceptions\StreamException
*/ */
public function write(string $string, $length=null) : int
public function write(string $string, int $length = null): int
{ {
if (is_null($length)) {
if (null === $length) {
$length = strlen($string); $length = strlen($string);
} }
// TODO: Ignore errors here, but why?
$result = @fwrite($this->stream, $string, $length); $result = @fwrite($this->stream, $string, $length);
if (false === $result) { if (false === $result) {
throw new StreamException(sprintf("Error writing %d bytes", $length));
throw new StreamException("Error writing $length bytes");
} }
return $result; return $result;
} }
/**
* Close stream connection
*
* @return void
* @throws \RouterOS\Exceptions\StreamException
*/
public function close() public function close()
{ {
$hasBeenClosed = false; $hasBeenClosed = false;
if (!is_null($this->stream)) {
if (null !== $this->stream) {
$hasBeenClosed = @fclose($this->stream); $hasBeenClosed = @fclose($this->stream);
$this->stream=null;
$this->stream = null;
} }
if (false===$hasBeenClosed) {
throw new StreamException("Error closing stream");
if (false === $hasBeenClosed) {
throw new StreamException('Error closing stream');
} }
} }
}
}

48
src/Streams/StringStream.php

@ -1,4 +1,5 @@
<?php <?php
namespace RouterOS\Streams; namespace RouterOS\Streams;
use RouterOS\Interfaces\StreamInterface; use RouterOS\Interfaces\StreamInterface;
@ -6,14 +7,13 @@ use RouterOS\Exceptions\StreamException;
/** /**
* class StringStream * class StringStream
*
*
* Initialized with a string, the read method retreive it as done with fread, consuming the buffer. * Initialized with a string, the read method retreive it as done with fread, consuming the buffer.
* When all the string has been read, exception is thrown when try to read again.
* When all the string has been read, exception is thrown when try to read again.
* *
* @package RouterOS\Streams * @package RouterOS\Streams
* @since 0.9 * @since 0.9
*/ */
class StringStream implements StreamInterface class StringStream implements StreamInterface
{ {
/** /**
@ -22,7 +22,7 @@ class StringStream implements StreamInterface
protected $buffer; protected $buffer;
/** /**
* Constuctor
* StringStream constructor.
* *
* @param string $string * @param string $string
*/ */
@ -37,25 +37,24 @@ class StringStream implements StreamInterface
* @throws \InvalidArgumentException when length parameter is invalid * @throws \InvalidArgumentException when length parameter is invalid
* @throws StreamException when the stream have been tatly red and read methd is called again * @throws StreamException when the stream have been tatly red and read methd is called again
*/ */
public function read(int $length) : string
public function read(int $length): string
{ {
$remaining = strlen($this->buffer); $remaining = strlen($this->buffer);
if ($length<0) {
throw new \InvalidArgumentException("Cannot read a negative count of bytes from a stream");
if ($length < 0) {
throw new \InvalidArgumentException('Cannot read a negative count of bytes from a stream');
} }
if (0 == $remaining) {
throw new StreamException("End of stream");
if (0 === $remaining) {
throw new StreamException('End of stream');
} }
if ($length>=$remaining) {
if ($length >= $remaining) {
// returns all // returns all
$result = $this->buffer; $result = $this->buffer;
// No more in the buffer // No more in the buffer
$this->buffer='';
}
else {
$this->buffer = '';
} else {
// acquire $length characters from the buffer // acquire $length characters from the buffer
$result = substr($this->buffer, 0, $length); $result = substr($this->buffer, 0, $length);
// remove $length characters from the buffer // remove $length characters from the buffer
@ -68,26 +67,31 @@ class StringStream implements StreamInterface
/** /**
* Fake write method, do nothing except return the "writen" length * Fake write method, do nothing except return the "writen" length
* *
* @param string $string The string to write
* @param int|null $length the number of characters to write
* @throws \InvalidArgumentException on invalid length
* @return number of "writen" bytes
* @param string $string The string to write
* @param int|null $length the number of characters to write
* @return int number of "writen" bytes
* @throws \InvalidArgumentException on invalid length
*/ */
public function write(string $string, $length=null) : int
public function write(string $string, int $length = null): int
{ {
if(null === $length) {
if (null === $length) {
$length = strlen($string); $length = strlen($string);
} }
if ($length<0) {
throw new \InvalidArgumentException("Cannot write a negative count of bytes");
if ($length < 0) {
throw new \InvalidArgumentException('Cannot write a negative count of bytes');
} }
return min($length, strlen($string)); return min($length, strlen($string));
} }
/**
* Close stream connection
*
* @return void
*/
public function close() public function close()
{ {
$this->buffer = ''; $this->buffer = '';
} }
}
}

78
tests/APIConnectorTest.php

@ -3,30 +3,29 @@
namespace RouterOS\Tests; namespace RouterOS\Tests;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use RouterOS\APIConnector; use RouterOS\APIConnector;
use RouterOS\Streams\StringStream; use RouterOS\Streams\StringStream;
use RouterOS\Streams\ResourceStream; use RouterOS\Streams\ResourceStream;
use RouterOS\APILengthCoDec; use RouterOS\APILengthCoDec;
use RouterOS\Interfaces\StreamInterface; use RouterOS\Interfaces\StreamInterface;
/** /**
* Limit code coverage to the class RouterOS\APIStream * Limit code coverage to the class RouterOS\APIStream
* @coversDefaultClass RouterOS\APIConnector
*
* @coversDefaultClass \RouterOS\APIConnector
*/ */
class APIConnectorTest extends TestCase class APIConnectorTest extends TestCase
{ {
/** /**
* Test that constructor is OK with different kinds of resources * Test that constructor is OK with different kinds of resources
*
*
* @covers ::__construct * @covers ::__construct
* @dataProvider constructProvider * @dataProvider constructProvider
* @param Resource $resource Cannot typehint, PHP refuse it
* @param bool $closeResource shall we close the resource ?
*
* @param StreamInterface $stream Cannot typehint, PHP refuse it
* @param bool $closeResource shall we close the resource ?
*/ */
public function test_construct(StreamInterface $stream, bool $closeResource=false)
public function test_construct(StreamInterface $stream, bool $closeResource = false)
{ {
$apiStream = new APIConnector($stream); $apiStream = new APIConnector($stream);
$this->assertInstanceOf(APIConnector::class, $apiStream); $this->assertInstanceOf(APIConnector::class, $apiStream);
@ -35,14 +34,14 @@ class APIConnectorTest extends TestCase
} }
} }
public function constructProvider()
public function constructProvider(): array
{ {
return [ return [
[ new ResourceStream(fopen(__FILE__, 'r')), ], // Myself, sure I exists
[ new ResourceStream(fsockopen('tcp://127.0.0.1', 18728)), ], // Socket
[ new ResourceStream(STDIN), false ], // Try it, but do not close STDIN please !!!
[ new StringStream('Hello World !!!') ], // Try it, but do not close STDIN please !!!
[ new StringStream('') ], // Try it, but do not close STDIN please !!!
[new ResourceStream(fopen(__FILE__, 'rb')),], // Myself, sure I exists
[new ResourceStream(fsockopen('tcp://127.0.0.1', 18728)),], // Socket
[new ResourceStream(STDIN), false], // Try it, but do not close STDIN please !!!
[new StringStream('Hello World !!!')], // Try it, but do not close STDIN please !!!
[new StringStream('')], // Try it, but do not close STDIN please !!!
// What else ? // What else ?
]; ];
} }
@ -50,43 +49,50 @@ class APIConnectorTest extends TestCase
/** /**
* @covers ::readWord * @covers ::readWord
* @dataProvider readWordProvider * @dataProvider readWordProvider
*
* @param APIConnector $connector
* @param string $expected
*/ */
public function test__readWord(APIConnector $connector, $expected)
public function test__readWord(APIConnector $connector, string $expected)
{ {
$this->assertSame($expected, $connector->readWord()); $this->assertSame($expected, $connector->readWord());
} }
public function readWordProvider()
public function readWordProvider(): array
{ {
$longString = '=comment='.str_repeat('a',10000);
$length = strlen($longString);
return [
[ new APIConnector(new StringStream(chr(0))), ''],
[ new APIConnector(new StringStream(chr(3).'!re')), '!re'],
[ new APIConnector(new StringStream(chr(5).'!done')), '!done'],
[ new APIConnector(new StringStream(APILengthCoDec::encodeLength($length).$longString)), $longString],
$longString = '=comment=' . str_repeat('a', 10000);
$length = strlen($longString);
return [
[new APIConnector(new StringStream(chr(0))), ''],
[new APIConnector(new StringStream(chr(3) . '!re')), '!re'],
[new APIConnector(new StringStream(chr(5) . '!done')), '!done'],
[new APIConnector(new StringStream(APILengthCoDec::encodeLength($length) . $longString)), $longString],
]; ];
} }
/**
* @covers ::writeWord
* @dataProvider writeWordProvider
*/
/**
* @covers ::writeWord
* @dataProvider writeWordProvider
*
* @param APIConnector $connector
* @param string $toWrite
* @param int $expected
*/
public function test_writeWord(APIConnector $connector, string $toWrite, int $expected) public function test_writeWord(APIConnector $connector, string $toWrite, int $expected)
{ {
$this->assertEquals($expected, $connector->writeWord($toWrite)); $this->assertEquals($expected, $connector->writeWord($toWrite));
} }
public function writeWordProvider()
public function writeWordProvider(): array
{ {
return [ return [
[ new APIConnector(new StringStream('Have FUN !!!')), '', 1 ], // length is 0, but have to write it on 1 byte, minimum
[ new APIConnector(new StringStream('Have FUN !!!')), str_repeat(' ', 54), 55 ], // arbitrary value
[ new APIConnector(new StringStream('Have FUN !!!')), str_repeat(' ', 127), 128 ], // maximum value for 1 byte encoding lentgth
[ new APIConnector(new StringStream('Have FUN !!!')), str_repeat(' ', 128), 130 ], // minimum value for 2 bytes encoding lentgth
[ new APIConnector(new StringStream('Have FUN !!!')), str_repeat(' ', 254), 256 ], // special value isn't it ?
[ new APIConnector(new StringStream('Have FUN !!!')), str_repeat(' ', 255), 257 ], // special value isn't it ?
[new APIConnector(new StringStream('Have FUN !!!')), '', 1], // length is 0, but have to write it on 1 byte, minimum
[new APIConnector(new StringStream('Have FUN !!!')), str_repeat(' ', 54), 55], // arbitrary value
[new APIConnector(new StringStream('Have FUN !!!')), str_repeat(' ', 127), 128], // maximum value for 1 byte encoding lentgth
[new APIConnector(new StringStream('Have FUN !!!')), str_repeat(' ', 128), 130], // minimum value for 2 bytes encoding lentgth
[new APIConnector(new StringStream('Have FUN !!!')), str_repeat(' ', 254), 256], // special value isn't it ?
[new APIConnector(new StringStream('Have FUN !!!')), str_repeat(' ', 255), 257], // special value isn't it ?
]; ];
} }
}
}

4
tests/APILengthCoDecTest.php

@ -11,7 +11,7 @@ use RouterOS\Helpers\BinaryStringHelper;
/** /**
* Limit code coverage to the class * Limit code coverage to the class
* @coversDefaultClass RouterOS\APILengthCoDec
* @coversDefaultClass \RouterOS\APILengthCoDec
*/ */
class APILengthCoDecTest extends TestCase class APILengthCoDecTest extends TestCase
{ {
@ -117,4 +117,4 @@ class APILengthCoDecTest extends TestCase
[chr(0xFF)], // maximum [chr(0xFF)], // maximum
]; ];
} }
}
}

159
tests/Streams/ResourceStreamTest.php

@ -4,22 +4,23 @@ namespace RouterOS\Tests\Streams;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Constraint\IsType; use PHPUnit\Framework\Constraint\IsType;
use RouterOS\Streams\ResourceStream; use RouterOS\Streams\ResourceStream;
use RouterOS\Exceptions\StreamException;
/** /**
* Limit code coverage to the class RouterOS\APIStream * Limit code coverage to the class RouterOS\APIStream
* @coversDefaultClass RouterOS\Streams\ResourceStream
*
* @coversDefaultClass \RouterOS\Streams\ResourceStream
*/ */
class ResourceStreamTest extends TestCase class ResourceStreamTest extends TestCase
{ {
/** /**
* Test that constructor throws an InvalidArgumentException on bad parameter type * Test that constructor throws an InvalidArgumentException on bad parameter type
*
*
* @covers ::__construct * @covers ::__construct
* @expectedException \InvalidArgumentException * @expectedException \InvalidArgumentException
* @dataProvider constructNotResourceProvider * @dataProvider constructNotResourceProvider
*
* @param $notResource
*/ */
public function test__constructNotResource($notResource) public function test__constructNotResource($notResource)
@ -32,37 +33,37 @@ class ResourceStreamTest extends TestCase
* *
* returns data not of type resource * returns data not of type resource
*/ */
public function constructNotResourceProvider()
public function constructNotResourceProvider(): array
{ {
return [ return [
[0], // integer [0], // integer
[3.14], // float [3.14], // float
['a string'], // string ['a string'], // string
[ [
[ 0 , 3.14 ] // Array
[0, 3.14] // Array
], ],
[ new \stdClass() ], // Object
[new \stdClass()], // Object
// What else ? // What else ?
]; ];
} }
/** /**
* Test that constructor is OK with different kinds of resources * Test that constructor is OK with different kinds of resources
*
*
* @covers ::__construct * @covers ::__construct
* @dataProvider constructProvider * @dataProvider constructProvider
* @param resource $resource Cannot typehint, PHP refuse it
* @param bool $closeResource shall we close the resource ?
*
* @param resource $resource Cannot typehint, PHP refuse it
* @param bool $closeResource shall we close the resource ?
*/ */
public function test_construct($resource, bool $closeResource=true)
public function test_construct($resource, bool $closeResource = true)
{ {
$resourceStream = new ResourceStream($resource); $resourceStream = new ResourceStream($resource);
$stream = $this->getObjectAttribute($resourceStream, 'stream'); $stream = $this->getObjectAttribute($resourceStream, 'stream');
$this->assertInternalType(IsType::TYPE_RESOURCE, $stream); $this->assertInternalType(IsType::TYPE_RESOURCE, $stream);
if ($closeResource)
{
if ($closeResource) {
fclose($resource); fclose($resource);
} }
} }
@ -70,142 +71,157 @@ class ResourceStreamTest extends TestCase
/** /**
* Data provider for test__construct * Data provider for test__construct
* *
* returns data of type resource
* @return array data of type resource
*/ */
public function constructProvider()
public function constructProvider(): array
{ {
return [ return [
[ fopen(__FILE__, 'r'), ], // Myself, sure I exists
[ fsockopen('tcp://127.0.0.1', 18728), ], // Socket
[ STDIN, false ], // Try it, but do not close STDIN please !!!
[fopen(__FILE__, 'rb'),], // Myself, sure I exists
[fsockopen('tcp://127.0.0.1', 18728),], // Socket
[STDIN, false], // Try it, but do not close STDIN please !!!
// What else ? // What else ?
]; ];
} }
/** /**
* Test that read function return expected values, and that consecutive reads return data * Test that read function return expected values, and that consecutive reads return data
*
*
* @covers ::read * @covers ::read
* @dataProvider readProvider * @dataProvider readProvider
* @param resource $resource Cannot typehint, PHP refuse it
* @param string $expected the rsult we should have
*
* @param ResourceStream $stream Cannot typehint, PHP refuse it
* @param string $expected the result we should have
* @throws \RouterOS\Exceptions\StreamException
* @throws \InvalidArgumentException
*/ */
public function test__read(ResourceStream $stream, string $expected) public function test__read(ResourceStream $stream, string $expected)
{ {
$this->assertSame($expected, $stream->read(strlen($expected))); $this->assertSame($expected, $stream->read(strlen($expected)));
} }
public function readProvider()
public function readProvider(): array
{ {
$resource = fopen(__FILE__, 'r');
$me = new ResourceStream($resource);
$resource = fopen(__FILE__, 'rb');
$me = new ResourceStream($resource);
return [ return [
[ $me, '<'], // Read for byte
[ $me, '?php'], // Read following bytes. File statrts with "<php"
[$me, '<'], // Read for byte
[$me, '?php'], // Read following bytes. File statrts with "<php"
]; ];
fclose($resource);
} }
/** /**
* Test that read invalid lengths * Test that read invalid lengths
*
*
* @covers ::read * @covers ::read
* @dataProvider readBadLengthProvider * @dataProvider readBadLengthProvider
* @expectedException \InvalidArgumentException * @expectedException \InvalidArgumentException
* @param resource $resource Cannot typehint, PHP refuse it
*
* @param ResourceStream $stream Cannot typehint, PHP refuse it
* @param int $length
* @throws \RouterOS\Exceptions\StreamException
* @throws \InvalidArgumentException
*/ */
public function test__readBadLength(ResourceStream $stream, int $length) public function test__readBadLength(ResourceStream $stream, int $length)
{ {
$stream->read($length); $stream->read($length);
} }
public function readBadLengthProvider()
public function readBadLengthProvider(): array
{ {
$resource = fopen(__FILE__, 'r');
$me = new ResourceStream($resource);
$resource = fopen(__FILE__, 'rb');
$me = new ResourceStream($resource);
return [ return [
[ $me, 0 ],
[ $me, -1 ],
[$me, 0],
[$me, -1],
]; ];
fclose($resource);
} }
/** /**
* Test read to invalid resource * Test read to invalid resource
*
*
* @covers ::read * @covers ::read
* @dataProvider readBadResourceProvider * @dataProvider readBadResourceProvider
* @expectedException RouterOS\Exceptions\StreamException
* @param resource $resource Cannot typehint, PHP refuse it
* @expectedException \RouterOS\Exceptions\StreamException
*
* @param ResourceStream $stream Cannot typehint, PHP refuse it
* @param int $length
*/ */
public function test__readBadResource(ResourceStream $stream, int $length) public function test__readBadResource(ResourceStream $stream, int $length)
{ {
$stream->read($length); $stream->read($length);
} }
public function readBadResourceProvider()
public function readBadResourceProvider(): array
{ {
$resource = fopen(__FILE__, 'r');
$me = new ResourceStream($resource);
$resource = fopen(__FILE__, 'rb');
$me = new ResourceStream($resource);
fclose($resource); fclose($resource);
return [ return [
[ $me, 1 ],
[$me, 1],
]; ];
} }
/** /**
* Test that write function returns writen length * Test that write function returns writen length
*
*
* @covers ::write * @covers ::write
* @dataProvider writeProvider * @dataProvider writeProvider
* @param ResourceStram $resource to test
* @param string $toWrite the writed string
*
* @param ResourceStream $stream to test
* @param string $toWrite the writed string
* @throws \RouterOS\Exceptions\StreamException
*/ */
public function test__write(ResourceStream $stream, string $toWrite) public function test__write(ResourceStream $stream, string $toWrite)
{ {
$this->assertEquals(strlen($toWrite) , $stream->write($toWrite));
$this->assertEquals(strlen($toWrite), $stream->write($toWrite));
} }
public function writeProvider()
public function writeProvider(): array
{ {
$resource = fopen("/dev/null", 'w');
$null = new ResourceStream($resource);
$resource = fopen('/dev/null', 'wb');
$null = new ResourceStream($resource);
return [ return [
[ $null, 'yyaagagagag'], // Take that
[$null, 'yyaagagagag'], // Take that
]; ];
fclose($resource);
} }
/** /**
* Test write to invalid resource * Test write to invalid resource
*
*
* @covers ::write * @covers ::write
* @dataProvider writeBadResourceProvider * @dataProvider writeBadResourceProvider
* @expectedException RouterOS\Exceptions\StreamException
* @param resource $resource to test
* @param string $toWrite the writed string
* @expectedException \RouterOS\Exceptions\StreamException
*
* @param ResourceStream $stream to test
* @param string $toWrite the written string
*/ */
public function test__writeBadResource(ResourceStream $stream, string $toWrite) public function test__writeBadResource(ResourceStream $stream, string $toWrite)
{ {
$stream->write($toWrite); $stream->write($toWrite);
} }
public function writeBadResourceProvider()
public function writeBadResourceProvider(): array
{ {
$resource = fopen('/dev/null', 'w');
$me = new ResourceStream($resource);
$resource = fopen('/dev/null', 'wb');
$me = new ResourceStream($resource);
fclose($resource); fclose($resource);
return [ return [
[ $me, 'sasasaas' ], // Take that
[$me, 'sasasaas'], // Take that
]; ];
} }
/** /**
* Test double close resource * Test double close resource
*
*
* @covers ::close * @covers ::close
* @dataProvider doubleCloseProvider * @dataProvider doubleCloseProvider
* @expectedException RouterOS\Exceptions\StreamException
* @param resource $resource to test
* @expectedException \RouterOS\Exceptions\StreamException
*
* @param ResourceStream $stream to test
*/ */
public function test_doubleClose(ResourceStream $stream) public function test_doubleClose(ResourceStream $stream)
{ {
@ -213,22 +229,23 @@ class ResourceStreamTest extends TestCase
$stream->close(); $stream->close();
} }
public function doubleCloseProvider()
public function doubleCloseProvider(): array
{ {
return [ return [
[ new ResourceStream(fopen('/dev/null', 'w')), 'sasasaas' ], // Take that
[new ResourceStream(fopen('/dev/null', 'wb')), 'sasasaas'], // Take that
]; ];
} }
/** /**
* Test write to closed resource * Test write to closed resource
*
*
* @covers ::close * @covers ::close
* @covers ::write * @covers ::write
* @dataProvider writeClosedResourceProvider * @dataProvider writeClosedResourceProvider
* @expectedException RouterOS\Exceptions\StreamException
* @param resource $resource to test
* @param string $toWrite the writed string
* @expectedException \RouterOS\Exceptions\StreamException
*
* @param ResourceStream $stream to test
* @param string $toWrite the written string
*/ */
public function test_close(ResourceStream $stream, string $toWrite) public function test_close(ResourceStream $stream, string $toWrite)
{ {
@ -236,11 +253,11 @@ class ResourceStreamTest extends TestCase
$stream->write($toWrite); $stream->write($toWrite);
} }
public function writeClosedResourceProvider()
public function writeClosedResourceProvider(): array
{ {
return [ return [
[ new ResourceStream(fopen('/dev/null', 'w')), 'sasasaas' ], // Take that
[new ResourceStream(fopen('/dev/null', 'wb')), 'sasasaas'], // Take that
]; ];
} }
}
}

80
tests/Streams/StringStreamTest.php

@ -10,66 +10,70 @@ use RouterOS\Exceptions\StreamException;
/** /**
* Limit code coverage to the class RouterOS\APIStream * Limit code coverage to the class RouterOS\APIStream
* @coversDefaultClass RouterOS\Streams\StringStream
*
* @coversDefaultClass \RouterOS\Streams\StringStream
*/ */
class StringStreamTest extends TestCase class StringStreamTest extends TestCase
{ {
/** /**
* @covers ::__construct * @covers ::__construct
* @dataProvider constructProvider * @dataProvider constructProvider
*
* @param string $string
*/ */
public function test__construct(string $string) public function test__construct(string $string)
{ {
$this->assertInstanceOf(StringStream::class, new StringStream($string)); $this->assertInstanceOf(StringStream::class, new StringStream($string));
} }
public function constructProvider()
public function constructProvider(): array
{ {
return [ return [
[ chr(0) ],
[ '' ],
[ '1' ],
[ 'lkjl'.chr(0).'kjkljllkjkljljklkjkljlkjljlkjkljkljlkjjll'],
[chr(0)],
[''],
['1'],
['lkjl' . chr(0) . 'kjkljllkjkljljklkjkljlkjljlkjkljkljlkjjll'],
]; ];
}
}
/** /**
* test that write function returns the effective writen bytes
* Test that write function returns the effective written bytes
*
* @covers ::write * @covers ::write
* @dataProvider writeProvider * @dataProvider writeProvider
* @param string $toWrite the string to write
* @param int|null $length the count if bytes to write
* @param int $expected the number of bytes that must be writen
*
* @param string $string the string to write
* @param int|null $length the count if bytes to write
* @param int $expected the number of bytes that must be writen
*/ */
public function test__write(string $string, $length, int $expected) public function test__write(string $string, $length, int $expected)
{ {
$stream = new StringStream('Does not matters'); $stream = new StringStream('Does not matters');
if (is_null($length)) {
if (null === $length) {
$this->assertEquals($expected, $stream->write($string)); $this->assertEquals($expected, $stream->write($string));
}
else {
} else {
$this->assertEquals($expected, $stream->write($string, $length)); $this->assertEquals($expected, $stream->write($string, $length));
} }
} }
public function writeProvider()
public function writeProvider(): array
{ {
return [ return [
[ '', 0, 0 ],
[ '', 10, 0 ],
[ '', null, 0 ],
[ 'Yabala', 0, 0],
[ 'Yabala', 1, 1],
[ 'Yabala', 6, 6],
[ 'Yabala', 100, 6],
[ 'Yabala', null, 6],
[ chr(0), 0, 0],
[ chr(0), 1, 1],
[ chr(0), 100, 1],
[ chr(0), null, 1],
['', 0, 0],
['', 10, 0],
['', null, 0],
['Yabala', 0, 0],
['Yabala', 1, 1],
['Yabala', 6, 6],
['Yabala', 100, 6],
['Yabala', null, 6],
[chr(0), 0, 0],
[chr(0), 1, 1],
[chr(0), 100, 1],
[chr(0), null, 1],
]; ];
} }
@ -80,11 +84,13 @@ class StringStreamTest extends TestCase
public function test__writeWithNegativeLength() public function test__writeWithNegativeLength()
{ {
$stream = new StringStream('Does not matters'); $stream = new StringStream('Does not matters');
$stream->write("PLOP", -1);
$stream->write('PLOP', -1);
} }
/** /**
* Test read function * Test read function
*
* @throws \RouterOS\Exceptions\StreamException
*/ */
public function test__read() public function test__read()
{ {
@ -99,7 +105,9 @@ class StringStreamTest extends TestCase
} }
/** /**
* @expectedException InvalidArgumentException
* @expectedException \InvalidArgumentException
*
* @throws \RouterOS\Exceptions\StreamException
*/ */
public function test__readBadLength() public function test__readBadLength()
{ {
@ -111,12 +119,20 @@ class StringStreamTest extends TestCase
* @covers ::read * @covers ::read
* @dataProvider readWhileEmptyProvider * @dataProvider readWhileEmptyProvider
* @expectedException \RouterOS\Exceptions\StreamException * @expectedException \RouterOS\Exceptions\StreamException
*
* @param StringStream $stream
* @param int $length
* @throws \RouterOS\Exceptions\StreamException
*/ */
public function test__readWhileEmpty(StringStream $stream, int $length) public function test__readWhileEmpty(StringStream $stream, int $length)
{ {
$stream->read($length);
$stream->read($length);
} }
/**
* @return \Generator
* @throws StreamException
*/
public function readWhileEmptyProvider() public function readWhileEmptyProvider()
{ {
$stream = new StringStream('123456789'); $stream = new StringStream('123456789');
@ -133,7 +149,7 @@ class StringStreamTest extends TestCase
} }
/** /**
* @expectedException \RouterOS\Exceptions\StreamException
* @expectedException \RouterOS\Exceptions\StreamException
*/ */
public function testReadClosed() public function testReadClosed()
{ {
@ -141,4 +157,4 @@ class StringStreamTest extends TestCase
$stream->close(); $stream->close();
$stream->read(1); $stream->read(1);
} }
}
}
Loading…
Cancel
Save