From afcddac1d6f2963ee9d4a65c4ca1813e6238c8f7 Mon Sep 17 00:00:00 2001 From: Compolomus Date: Thu, 16 Apr 2020 22:29:59 +0200 Subject: [PATCH 01/13] Add gitattributes --- .gitattributes | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..54c9e70 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +*.md text eol=lf +*.php text eol=lf +*.ini text eol=lf +*.json text eol=lf +*.yml text eol=lf +*.tcl text eol=lf From 012da677daa23048c9bee75baa7335282ab708dd Mon Sep 17 00:00:00 2001 From: Compolomus Date: Thu, 16 Apr 2020 22:51:33 +0200 Subject: [PATCH 02/13] Add editorconfig and refactoring Client class --- .editorconfig | 226 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/Client.php | 166 ++++++++++++++++++++++-------------------- 2 files changed, 312 insertions(+), 80 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..d975ecf --- /dev/null +++ b/.editorconfig @@ -0,0 +1,226 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = false +max_line_length = 140 +tab_width = 4 +ij_continuation_indent_size = 8 +ij_formatter_off_tag = @formatter:off +ij_formatter_on_tag = @formatter:on +ij_formatter_tags_enabled = false +ij_smart_tabs = false +ij_wrap_on_typing = false + +[{*.module,*.hphp,*.phtml,*.php5,*.php4,*.php,*.ctp,*.inc}] +max_line_length = 999 +ij_continuation_indent_size = 4 +ij_php_align_assignments = true +ij_php_align_class_constants = true +ij_php_align_group_field_declarations = true +ij_php_align_inline_comments = true +ij_php_align_key_value_pairs = true +ij_php_align_multiline_array_initializer_expression = true +ij_php_align_multiline_binary_operation = false +ij_php_align_multiline_chained_methods = false +ij_php_align_multiline_extends_list = false +ij_php_align_multiline_for = true +ij_php_align_multiline_parameters = false +ij_php_align_multiline_parameters_in_calls = false +ij_php_align_multiline_ternary_operation = false +ij_php_align_phpdoc_comments = true +ij_php_align_phpdoc_param_names = true +ij_php_anonymous_brace_style = end_of_line +ij_php_api_weight = 28 +ij_php_array_initializer_new_line_after_left_brace = true +ij_php_array_initializer_right_brace_on_new_line = true +ij_php_array_initializer_wrap = off +ij_php_assignment_wrap = off +ij_php_author_weight = 28 +ij_php_binary_operation_sign_on_next_line = false +ij_php_binary_operation_wrap = off +ij_php_blank_lines_after_class_header = 0 +ij_php_blank_lines_after_function = 1 +ij_php_blank_lines_after_imports = 1 +ij_php_blank_lines_after_opening_tag = 0 +ij_php_blank_lines_after_package = 1 +ij_php_blank_lines_around_class = 1 +ij_php_blank_lines_around_constants = 0 +ij_php_blank_lines_around_field = 0 +ij_php_blank_lines_around_method = 1 +ij_php_blank_lines_before_class_end = 0 +ij_php_blank_lines_before_imports = 1 +ij_php_blank_lines_before_method_body = 0 +ij_php_blank_lines_before_package = 1 +ij_php_blank_lines_before_return_statement = 0 +ij_php_blank_lines_between_imports = 0 +ij_php_block_brace_style = end_of_line +ij_php_call_parameters_new_line_after_left_paren = false +ij_php_call_parameters_right_paren_on_new_line = false +ij_php_call_parameters_wrap = normal +ij_php_catch_on_new_line = false +ij_php_category_weight = 28 +ij_php_class_brace_style = next_line +ij_php_comma_after_last_array_element = false +ij_php_concat_spaces = true +ij_php_copyright_weight = 28 +ij_php_deprecated_weight = 28 +ij_php_do_while_brace_force = always +ij_php_else_if_style = combine +ij_php_else_on_new_line = false +ij_php_example_weight = 28 +ij_php_extends_keyword_wrap = off +ij_php_extends_list_wrap = off +ij_php_fields_default_visibility = private +ij_php_filesource_weight = 28 +ij_php_finally_on_new_line = false +ij_php_for_brace_force = always +ij_php_for_statement_new_line_after_left_paren = false +ij_php_for_statement_right_paren_on_new_line = false +ij_php_for_statement_wrap = off +ij_php_force_short_declaration_array_style = true +ij_php_global_weight = 28 +ij_php_group_use_wrap = on_every_item +ij_php_if_brace_force = always +ij_php_if_lparen_on_next_line = false +ij_php_if_rparen_on_next_line = false +ij_php_ignore_weight = 28 +ij_php_import_sorting = alphabetic +ij_php_indent_break_from_case = true +ij_php_indent_case_from_switch = true +ij_php_indent_code_in_php_tags = false +ij_php_internal_weight = 28 +ij_php_keep_blank_lines_after_lbrace = 2 +ij_php_keep_blank_lines_before_right_brace = 2 +ij_php_keep_blank_lines_in_code = 2 +ij_php_keep_blank_lines_in_declarations = 2 +ij_php_keep_control_statement_in_one_line = true +ij_php_keep_first_column_comment = true +ij_php_keep_indents_on_empty_lines = false +ij_php_keep_line_breaks = true +ij_php_keep_rparen_and_lbrace_on_one_line = true +ij_php_keep_simple_methods_in_one_line = false +ij_php_lambda_brace_style = end_of_line +ij_php_license_weight = 28 +ij_php_line_comment_add_space = false +ij_php_line_comment_at_first_column = true +ij_php_link_weight = 28 +ij_php_lower_case_boolean_const = true +ij_php_lower_case_null_const = true +ij_php_method_brace_style = next_line +ij_php_method_call_chain_wrap = off +ij_php_method_parameters_new_line_after_left_paren = true +ij_php_method_parameters_right_paren_on_new_line = true +ij_php_method_parameters_wrap = on_every_item +ij_php_method_weight = 28 +ij_php_modifier_list_wrap = false +ij_php_multiline_chained_calls_semicolon_on_new_line = false +ij_php_namespace_brace_style = 1 +ij_php_null_type_position = in_the_end +ij_php_package_weight = 28 +ij_php_param_weight = 0 +ij_php_parentheses_expression_new_line_after_left_paren = false +ij_php_parentheses_expression_right_paren_on_new_line = false +ij_php_phpdoc_blank_line_before_tags = true +ij_php_phpdoc_blank_lines_around_parameters = true +ij_php_phpdoc_keep_blank_lines = true +ij_php_phpdoc_param_spaces_between_name_and_description = 1 +ij_php_phpdoc_param_spaces_between_tag_and_type = 1 +ij_php_phpdoc_param_spaces_between_type_and_name = 1 +ij_php_phpdoc_use_fqcn = true +ij_php_phpdoc_wrap_long_lines = false +ij_php_place_assignment_sign_on_next_line = false +ij_php_place_parens_for_constructor = 0 +ij_php_property_read_weight = 28 +ij_php_property_weight = 28 +ij_php_property_write_weight = 28 +ij_php_return_type_on_new_line = false +ij_php_return_weight = 1 +ij_php_see_weight = 28 +ij_php_since_weight = 28 +ij_php_sort_phpdoc_elements = true +ij_php_space_after_colon = true +ij_php_space_after_colon_in_return_type = true +ij_php_space_after_comma = true +ij_php_space_after_for_semicolon = true +ij_php_space_after_quest = true +ij_php_space_after_type_cast = true +ij_php_space_after_unary_not = false +ij_php_space_before_array_initializer_left_brace = false +ij_php_space_before_catch_keyword = true +ij_php_space_before_catch_left_brace = true +ij_php_space_before_catch_parentheses = true +ij_php_space_before_class_left_brace = true +ij_php_space_before_closure_left_parenthesis = true +ij_php_space_before_colon = true +ij_php_space_before_colon_in_return_type = false +ij_php_space_before_comma = false +ij_php_space_before_do_left_brace = true +ij_php_space_before_else_keyword = true +ij_php_space_before_else_left_brace = true +ij_php_space_before_finally_keyword = true +ij_php_space_before_finally_left_brace = true +ij_php_space_before_for_left_brace = true +ij_php_space_before_for_parentheses = true +ij_php_space_before_for_semicolon = false +ij_php_space_before_if_left_brace = true +ij_php_space_before_if_parentheses = true +ij_php_space_before_method_call_parentheses = false +ij_php_space_before_method_left_brace = true +ij_php_space_before_method_parentheses = false +ij_php_space_before_quest = true +ij_php_space_before_switch_left_brace = true +ij_php_space_before_switch_parentheses = true +ij_php_space_before_try_left_brace = true +ij_php_space_before_unary_not = false +ij_php_space_before_while_keyword = true +ij_php_space_before_while_left_brace = true +ij_php_space_before_while_parentheses = true +ij_php_space_between_ternary_quest_and_colon = false +ij_php_spaces_around_additive_operators = true +ij_php_spaces_around_arrow = false +ij_php_spaces_around_assignment_in_declare = false +ij_php_spaces_around_assignment_operators = true +ij_php_spaces_around_bitwise_operators = true +ij_php_spaces_around_equality_operators = true +ij_php_spaces_around_logical_operators = true +ij_php_spaces_around_multiplicative_operators = true +ij_php_spaces_around_null_coalesce_operator = true +ij_php_spaces_around_relational_operators = true +ij_php_spaces_around_shift_operators = true +ij_php_spaces_around_unary_operator = false +ij_php_spaces_around_var_within_brackets = false +ij_php_spaces_within_array_initializer_braces = false +ij_php_spaces_within_brackets = false +ij_php_spaces_within_catch_parentheses = false +ij_php_spaces_within_for_parentheses = false +ij_php_spaces_within_if_parentheses = false +ij_php_spaces_within_method_call_parentheses = false +ij_php_spaces_within_method_parentheses = false +ij_php_spaces_within_parentheses = false +ij_php_spaces_within_short_echo_tags = true +ij_php_spaces_within_switch_parentheses = false +ij_php_spaces_within_while_parentheses = false +ij_php_special_else_if_treatment = false +ij_php_subpackage_weight = 28 +ij_php_ternary_operation_signs_on_next_line = false +ij_php_ternary_operation_wrap = off +ij_php_throws_weight = 2 +ij_php_todo_weight = 28 +ij_php_unknown_tag_weight = 28 +ij_php_upper_case_boolean_const = false +ij_php_upper_case_null_const = false +ij_php_uses_weight = 28 +ij_php_var_weight = 28 +ij_php_variable_naming_style = mixed +ij_php_version_weight = 28 +ij_php_while_brace_force = always +ij_php_while_on_new_line = false + +[{phpunit.xml.dist,*.jhm,*.rng,*.wsdl,*.fxml,*.xslt,*.jrxml,*.ant,*.xul,*.xsl,*.xsd,*.tld,*.jnlp,*.xml}] +ij_xml_block_comment_at_first_column = true +ij_xml_keep_indents_on_empty_lines = false +ij_xml_line_comment_at_first_column = true diff --git a/src/Client.php b/src/Client.php index be1469c..7c94e19 100644 --- a/src/Client.php +++ b/src/Client.php @@ -7,6 +7,17 @@ use RouterOS\Exceptions\ConfigException; use RouterOS\Exceptions\QueryException; use RouterOS\Helpers\ArrayHelper; +use function array_shift; +use function chr; +use function count; +use function is_array; +use function is_string; +use function md5; +use function pack; +use function preg_match_all; +use function sleep; +use function trim; + /** * Class Client for RouterOS management * @@ -20,14 +31,14 @@ class Client implements Interfaces\ClientInterface /** * Configuration of connection * - * @var \RouterOS\Config + * @var Config */ private $_config; /** * API communication object * - * @var \RouterOS\APIConnector + * @var APIConnector */ private $_connector; @@ -35,16 +46,16 @@ class Client implements Interfaces\ClientInterface /** * Client constructor. * - * @param array|\RouterOS\Config $config + * @param array|Config $config * - * @throws \RouterOS\Exceptions\ClientException - * @throws \RouterOS\Exceptions\ConfigException - * @throws \RouterOS\Exceptions\QueryException + * @throws ClientException + * @throws ConfigException + * @throws QueryException */ public function __construct($config) { // If array then need create object - if (\is_array($config)) { + if (is_array($config)) { $config = new Config($config); } @@ -68,7 +79,7 @@ class Client implements Interfaces\ClientInterface * @param string $parameter Name of required parameter * * @return mixed - * @throws \RouterOS\Exceptions\ConfigException + * @throws ConfigException */ private function config(string $parameter) { @@ -78,20 +89,20 @@ class Client implements Interfaces\ClientInterface /** * Send write query to RouterOS * - * @param string|array|\RouterOS\Query $query + * @param string|array|Query $query * - * @return \RouterOS\Client - * @throws \RouterOS\Exceptions\QueryException + * @return Client + * @throws QueryException * @deprecated * @codeCoverageIgnore */ public function write($query): Client { - if (\is_string($query)) { + if (is_string($query)) { $query = new Query($query); - } elseif (\is_array($query)) { + } elseif (is_array($query)) { $endpoint = array_shift($query); - $query = new Query($endpoint, $query); + $query = new Query($endpoint, $query); } if (!$query instanceof Query) { @@ -105,14 +116,14 @@ class Client implements Interfaces\ClientInterface /** * Send write query to RouterOS (modern version of write) * - * @param string|Query $endpoint Path of API query or Query object - * @param array|null $where List of where filters - * @param string|null $operations Some operations which need make on response - * @param string|null $tag Mark query with tag + * @param string|Query $endpoint Path of API query or Query object + * @param array|null $where List of where filters + * @param string|null $operations Some operations which need make on response + * @param string|null $tag Mark query with tag * - * @return \RouterOS\Client - * @throws \RouterOS\Exceptions\QueryException - * @throws \RouterOS\Exceptions\ClientException + * @return Client + * @throws QueryException + * @throws ClientException * @since 1.0.0 */ public function query($endpoint, array $where = null, string $operations = null, string $tag = null): Client @@ -128,48 +139,10 @@ class Client implements Interfaces\ClientInterface // If array is multidimensional, then parse each line if (is_array($where[0])) { foreach ($where as $item) { - - // Null by default - $key = null; - $operator = null; - $value = null; - - switch (\count($item)) { - case 1: - list($key) = $item; - break; - case 2: - list($key, $operator) = $item; - break; - case 3: - list($key, $operator, $value) = $item; - break; - default: - throw new ClientException('From 1 to 3 parameters of "where" condition is allowed'); - } - $query->where($key, $operator, $value); + $query = $this->preQuery($item, $query); } } else { - // Null by default - $key = null; - $operator = null; - $value = null; - - switch (\count($where)) { - case 1: - list($key) = $where; - break; - case 2: - list($key, $operator) = $where; - break; - case 3: - list($key, $operator, $value) = $where; - break; - default: - throw new ClientException('From 1 to 3 parameters of "where" condition is allowed'); - } - - $query->where($key, $operator, $value); + $query = $this->preQuery($where, $query); } } @@ -189,12 +162,45 @@ class Client implements Interfaces\ClientInterface } /** + * Query helper + * + * @param array $item + * @param Query $query + * @return Query + * @throws ClientException + * @throws QueryException + */ + private function preQuery(array $item, Query $query): Query + { + // Null by default + $key = null; + $operator = null; + $value = null; + + switch (count($item)) { + case 1: + [$key] = $item; + break; + case 2: + [$key, $operator] = $item; + break; + case 3: + [$key, $operator, $value] = $item; + break; + default: + throw new ClientException('From 1 to 3 parameters of "where" condition is allowed'); + } + + return $query->where($key, $operator, $value); + } + + /** * Send write query object to RouterOS * - * @param \RouterOS\Query $query + * @param Query $query * - * @return \RouterOS\Client - * @throws \RouterOS\Exceptions\QueryException + * @return Client + * @throws QueryException * @since 1.0.0 */ private function writeRAW(Query $query): Client @@ -278,7 +284,7 @@ class Client implements Interfaces\ClientInterface /** * Read using Iterators to improve performance on large dataset * - * @return \RouterOS\ResponseIterator + * @return ResponseIterator * @since 1.0.0 */ public function readAsIterator(): ResponseIterator @@ -302,9 +308,9 @@ class Client implements Interfaces\ClientInterface private function rosario(array $raw): array { // This RAW should't be an error - $positions = array_keys($raw, '!re'); - $count = count($raw); - $result = []; + $positions = \array_keys($raw, '!re'); + $count = count($raw); + $result = []; if (isset($positions[1])) { @@ -341,8 +347,8 @@ class Client implements Interfaces\ClientInterface public function parseResponse(array $response): array { $result = []; - $i = -1; - $lines = \count($response); + $i = -1; + $lines = count($response); foreach ($response as $key => $value) { switch ($value) { case '!re': @@ -379,9 +385,9 @@ class Client implements Interfaces\ClientInterface * Parse result from RouterOS by regular expression * * @param string $value - * @param array $matches + * @param array $matches */ - private function pregResponse(string $value, &$matches) + private function pregResponse(string $value, &$matches): void { preg_match_all('/^[=|\.](.*)=(.*)/', $value, $matches); } @@ -392,9 +398,9 @@ class Client implements Interfaces\ClientInterface * @param bool $legacyRetry Retry login if we detect legacy version of RouterOS * * @return bool - * @throws \RouterOS\Exceptions\ClientException - * @throws \RouterOS\Exceptions\ConfigException - * @throws \RouterOS\Exceptions\QueryException + * @throws ClientException + * @throws ConfigException + * @throws QueryException */ private function login(bool $legacyRetry = false): bool { @@ -406,7 +412,7 @@ class Client implements Interfaces\ClientInterface // Now need use this hash for authorization $query = new Query('/login', [ '=name=' . $this->config('user'), - '=response=00' . md5(\chr(0) . $this->config('pass') . pack('H*', $response['after']['ret'])) + '=response=00' . md5(chr(0) . $this->config('pass') . pack('H*', $response['after']['ret'])) ]); } else { // Just login with our credentials @@ -452,16 +458,16 @@ class Client implements Interfaces\ClientInterface */ private function isLegacy(array &$response): bool { - return \count($response) > 1 && $response[0] === '!done' && !$this->config('legacy'); + return count($response) > 1 && $response[0] === '!done' && !$this->config('legacy'); } /** * Connect to socket server * * @return bool - * @throws \RouterOS\Exceptions\ClientException - * @throws \RouterOS\Exceptions\ConfigException - * @throws \RouterOS\Exceptions\QueryException + * @throws ClientException + * @throws ConfigException + * @throws QueryException */ private function connect(): bool { From 809eec7e744f0898c63c09d7a1b680c1bad9491f Mon Sep 17 00:00:00 2001 From: Compolomus Date: Thu, 16 Apr 2020 23:06:29 +0200 Subject: [PATCH 03/13] Tune repo --- .gitignore | 2 ++ composer.json | 106 +++++++++++++++++++++++++++++++-------------------------- src/Client.php | 2 +- 3 files changed, 61 insertions(+), 49 deletions(-) diff --git a/.gitignore b/.gitignore index f4686f8..25aeec1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ /.idea/ /vendor/ /composer.lock +/clover.xml +/.phpunit.result.cache \ No newline at end of file diff --git a/composer.json b/composer.json index 5a7af1b..229921a 100644 --- a/composer.json +++ b/composer.json @@ -1,51 +1,61 @@ { - "name": "evilfreelancer/routeros-api-php", - "type": "library", - "description": "Modern Mikrotik RouterOS API PHP client for your applications (with Laravel support)", - "keywords": [ - "socket-client", - "psr-4", - "routeros", - "mikrotik", - "laravel", - "plugin", - "facade" - ], - "license": "MIT", - "autoload": { - "psr-4": { - "RouterOS\\": "./src/" + "name": "evilfreelancer/routeros-api-php", + "type": "library", + "description": "Modern Mikrotik RouterOS API PHP client for your applications (with Laravel support)", + "keywords": [ + "socket-client", + "psr-4", + "routeros", + "mikrotik", + "laravel", + "plugin", + "facade" + ], + "license": "MIT", + "autoload": { + "psr-4": { + "RouterOS\\": "./src/" + } + }, + "autoload-dev": { + "psr-4": { + "RouterOS\\Tests\\": "./tests/" + } + }, + "authors": [ + { + "name": "Paul Rock", + "email": "paul@drteam.rocks", + "homepage": "http://drteam.rocks/", + "role": "Developer" + } + ], + "extra": { + "laravel": { + "providers": [ + "RouterOS\\Laravel\\ClientServiceProvider" + ], + "aliases": { + "RouterOS": "RouterOS\\Laravel\\ClientFacade" + } + } + }, + "require": { + "php": "^7.2", + "ext-sockets": "*" + }, + "require-dev": { + "phpunit/phpunit": "^7.0", + "orchestra/testbench": "^3.0", + "roave/security-advisories": "dev-master", + "squizlabs/php_codesniffer": "^3.5" + }, + "scripts": { + "test": "phpunit --coverage-clover clover.xml", + "check": [ + "@cs-check" + ], + "cs-check": "phpcs", + "cs-fix": "phpcbf" } - }, - "autoload-dev": { - "psr-4": { - "RouterOS\\Tests\\": "./tests/" - } - }, - "authors": [ - { - "name": "Paul Rock", - "email": "paul@drteam.rocks", - "homepage": "http://drteam.rocks/", - "role": "Developer" - } - ], - "extra": { - "laravel": { - "providers": [ - "RouterOS\\Laravel\\ClientServiceProvider" - ], - "aliases": { - "RouterOS": "RouterOS\\Laravel\\ClientFacade" - } - } - }, - "require": { - "php": "^7.2", - "ext-sockets": "*" - }, - "require-dev": { - "phpunit/phpunit": "^7.0", - "orchestra/testbench": "^3.0" - } } diff --git a/src/Client.php b/src/Client.php index 7c94e19..b1ccb21 100644 --- a/src/Client.php +++ b/src/Client.php @@ -163,7 +163,7 @@ class Client implements Interfaces\ClientInterface /** * Query helper - * + * * @param array $item * @param Query $query * @return Query From 5701aee3eeed859bae6a513e958ba0334a5bafe2 Mon Sep 17 00:00:00 2001 From: Compolomus Date: Thu, 16 Apr 2020 23:58:52 +0200 Subject: [PATCH 04/13] Refactoring parseResponse method --- src/APILengthCoDec.php | 63 +++++++++++++++++++++++++------------------------- src/Client.php | 29 +++++++++++++++-------- 2 files changed, 52 insertions(+), 40 deletions(-) diff --git a/src/APILengthCoDec.php b/src/APILengthCoDec.php index 2f275bd..068652e 100644 --- a/src/APILengthCoDec.php +++ b/src/APILengthCoDec.php @@ -2,6 +2,7 @@ namespace RouterOS; +use DomainException; use RouterOS\Interfaces\StreamInterface; use RouterOS\Helpers\BinaryStringHelper; @@ -18,43 +19,43 @@ class APILengthCoDec /** * Encode string to length of string * + * Encode the length : + - if length <= 0x7F (binary : 01111111 => 7 bits set to 1) + - encode length with one byte + - set the byte to length value, as length maximal value is 7 bits set to 1, the most significant bit is always 0 + - end + - length <= 0x3FFF (binary : 00111111 11111111 => 14 bits set to 1) + - encode length with two bytes + - 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 significance bits (10) + - end + => minimal encoded value is 10000000 10000000 + - length <= 0x1FFFFF (binary : 00011111 11111111 11111111 => 21 bits set to 1) + - encode length with three bytes + - set length value to 0xC00000 (binary : 11000000 00000000 00000000) + - add length : as length maximal value is 21 bits to 1, this does not modify the 3 most significance bits (110) + - end + => minimal encoded value is 11000000 01000000 00000000 + - length <= 0x0FFFFFFF (binary : 00001111 11111111 11111111 11111111 => 28 bits set to 1) + - encode length with four bytes + - set length value to 0xE0000000 (binary : 11100000 00000000 00000000 00000000) + - add length : as length maximal value is 28 bits to 1, this does not modify the 4 most significance bits (1110) + - end + => minimal encoded value is 11100000 00100000 00000000 00000000 + - length <= 0x7FFFFFFFFF (binary : 00000111 11111111 11111111 11111111 11111111 => 35 bits set to 1) + - encode length with five bytes + - set length value to 0xF000000000 (binary : 11110000 00000000 00000000 00000000 00000000) + - add length : as length maximal value is 35 bits to 1, this does not modify the 5 most significance bits (11110) + - end + - length > 0x7FFFFFFFFF : not supported + * * @param int|float $length * @return string */ public static function encodeLength($length): string { - // Encode the length : - // - if length <= 0x7F (binary : 01111111 => 7 bits set to 1) - // - encode length with one byte - // - set the byte to length value, as length maximal value is 7 bits set to 1, the most significant bit is always 0 - // - end - // - length <= 0x3FFF (binary : 00111111 11111111 => 14 bits set to 1) - // - encode length with two bytes - // - 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 significance bits (10) - // - end - // => minimal encoded value is 10000000 10000000 - // - length <= 0x1FFFFF (binary : 00011111 11111111 11111111 => 21 bits set to 1) - // - encode length with three bytes - // - 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 significance bits (110) - // - end - // => minimal encoded value is 11000000 01000000 00000000 - // - length <= 0x0FFFFFFF (binary : 00001111 11111111 11111111 11111111 => 28 bits set to 1) - // - encode length with four bytes - // - 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 significance bits (1110) - // - end - // => minimal encoded value is 11100000 00100000 00000000 00000000 - // - length <= 0x7FFFFFFFFF (binary : 00000111 11111111 11111111 11111111 11111111 => 35 bits set to 1) - // - encode length with five bytes - // - 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 significance bits (11110) - // - end - // - length > 0x7FFFFFFFFF : not supported - if ($length < 0) { - throw new \DomainException("Length of word could not to be negative ($length)"); + throw new DomainException("Length of word could not to be negative ($length)"); } if ($length <= 0x7F) { diff --git a/src/Client.php b/src/Client.php index b1ccb21..d5cf800 100644 --- a/src/Client.php +++ b/src/Client.php @@ -7,6 +7,7 @@ use RouterOS\Exceptions\ConfigException; use RouterOS\Exceptions\QueryException; use RouterOS\Helpers\ArrayHelper; +use function array_keys; use function array_shift; use function chr; use function count; @@ -308,7 +309,7 @@ class Client implements Interfaces\ClientInterface private function rosario(array $raw): array { // This RAW should't be an error - $positions = \array_keys($raw, '!re'); + $positions = array_keys($raw, '!re'); $count = count($raw); $result = []; @@ -363,18 +364,12 @@ class Client implements Interfaces\ClientInterface for ($j = $key + 1; $j <= $lines; $j++) { // If we have lines after current one if (isset($response[$j])) { - $this->pregResponse($response[$j], $matches); - if (isset($matches[1][0], $matches[2][0])) { - $result['after'][$matches[1][0]] = $matches[2][0]; - } + $this->preParseResponse($response[$j], $result, $matches, $j); } } break 2; default: - $this->pregResponse($value, $matches); - if (isset($matches[1][0], $matches[2][0])) { - $result[$i][$matches[1][0]] = $matches[2][0]; - } + $this->preParseResponse($value, $result, $matches); break; } } @@ -382,6 +377,22 @@ class Client implements Interfaces\ClientInterface } /** + * Response helper + * + * @param $value + * @param $result + * @param $matches + * @param string $iterator + */ + private function preParseResponse($value, &$result, &$matches, $iterator = 'after'): void + { + $this->pregResponse($value, $matches); + if (isset($matches[1][0], $matches[2][0])) { + $result[$iterator][$matches[1][0]] = $matches[2][0]; + } + } + + /** * Parse result from RouterOS by regular expression * * @param string $value From 8b592a5e59da9fb15db8064271aa1c22c1837256 Mon Sep 17 00:00:00 2001 From: Paul Rock Date: Fri, 17 Apr 2020 23:09:51 +0300 Subject: [PATCH 05/13] codestyle in composer.json fixed --- composer.json | 116 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/composer.json b/composer.json index 229921a..a70a207 100644 --- a/composer.json +++ b/composer.json @@ -1,61 +1,61 @@ { - "name": "evilfreelancer/routeros-api-php", - "type": "library", - "description": "Modern Mikrotik RouterOS API PHP client for your applications (with Laravel support)", - "keywords": [ - "socket-client", - "psr-4", - "routeros", - "mikrotik", - "laravel", - "plugin", - "facade" - ], - "license": "MIT", - "autoload": { - "psr-4": { - "RouterOS\\": "./src/" - } - }, - "autoload-dev": { - "psr-4": { - "RouterOS\\Tests\\": "./tests/" - } - }, - "authors": [ - { - "name": "Paul Rock", - "email": "paul@drteam.rocks", - "homepage": "http://drteam.rocks/", - "role": "Developer" - } - ], - "extra": { - "laravel": { - "providers": [ - "RouterOS\\Laravel\\ClientServiceProvider" - ], - "aliases": { - "RouterOS": "RouterOS\\Laravel\\ClientFacade" - } - } - }, - "require": { - "php": "^7.2", - "ext-sockets": "*" - }, - "require-dev": { - "phpunit/phpunit": "^7.0", - "orchestra/testbench": "^3.0", - "roave/security-advisories": "dev-master", - "squizlabs/php_codesniffer": "^3.5" - }, - "scripts": { - "test": "phpunit --coverage-clover clover.xml", - "check": [ - "@cs-check" - ], - "cs-check": "phpcs", - "cs-fix": "phpcbf" + "name": "evilfreelancer/routeros-api-php", + "type": "library", + "description": "Modern Mikrotik RouterOS API PHP client for your applications (with Laravel support)", + "keywords": [ + "socket-client", + "psr-4", + "routeros", + "mikrotik", + "laravel", + "plugin", + "facade" + ], + "license": "MIT", + "autoload": { + "psr-4": { + "RouterOS\\": "./src/" + } + }, + "autoload-dev": { + "psr-4": { + "RouterOS\\Tests\\": "./tests/" } + }, + "authors": [ + { + "name": "Paul Rock", + "email": "paul@drteam.rocks", + "homepage": "http://drteam.rocks/", + "role": "Developer" + } + ], + "extra": { + "laravel": { + "providers": [ + "RouterOS\\Laravel\\ClientServiceProvider" + ], + "aliases": { + "RouterOS": "RouterOS\\Laravel\\ClientFacade" + } + } + }, + "require": { + "php": "^7.2", + "ext-sockets": "*" + }, + "require-dev": { + "phpunit/phpunit": "^7.0", + "orchestra/testbench": "^3.0", + "roave/security-advisories": "dev-master", + "squizlabs/php_codesniffer": "^3.5" + }, + "scripts": { + "test": "phpunit --coverage-clover clover.xml", + "check": [ + "@cs-check" + ], + "cs-check": "phpcs", + "cs-fix": "phpcbf" + } } From d6fefd6861fbf70e987aa8210586782af27f67d2 Mon Sep 17 00:00:00 2001 From: Paul Rock Date: Fri, 17 Apr 2020 23:10:12 +0300 Subject: [PATCH 06/13] required parameters added to travis-ci config --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6f0135f..dd98256 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,8 +20,8 @@ before_script: - sudo apt-get install -y expect - docker pull evilfreelancer/docker-routeros:6.42 - docker pull evilfreelancer/docker-routeros:latest -- docker run -d -p 12223:23 -p 18728:8728 -p 18729:8729 -ti evilfreelancer/docker-routeros:6.42 -- docker run -d -p 22223:23 -p 8728:8728 -p 8729:8729 -ti evilfreelancer/docker-routeros:latest +- docker run --device=/dev/net/tun --cap-add NET_ADMIN -d -p 12223:23 -p 18728:8728 -p 18729:8729 -ti evilfreelancer/docker-routeros:6.42 +- docker run --device=/dev/net/tun --cap-add NET_ADMIN -d -p 22223:23 -p 8728:8728 -p 8729:8729 -ti evilfreelancer/docker-routeros:latest - docker ps -a - sleep 60 - ./preconf.tcl 12223 > /dev/null || true From 4d0bf8e610923ebf816be1d51b6fa3afee25891f Mon Sep 17 00:00:00 2001 From: Paul Rock Date: Fri, 17 Apr 2020 23:10:55 +0300 Subject: [PATCH 07/13] additional rules of editorconfig --- .editorconfig | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.editorconfig b/.editorconfig index d975ecf..0f45882 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,21 +3,23 @@ root = true [*] charset = utf-8 end_of_line = lf -indent_size = 4 +indent_size = 2 indent_style = space insert_final_newline = false max_line_length = 140 -tab_width = 4 -ij_continuation_indent_size = 8 +tab_width = 2 +ij_continuation_indent_size = 2 ij_formatter_off_tag = @formatter:off ij_formatter_on_tag = @formatter:on ij_formatter_tags_enabled = false ij_smart_tabs = false ij_wrap_on_typing = false -[{*.module,*.hphp,*.phtml,*.php5,*.php4,*.php,*.ctp,*.inc}] +[{*.ctp,*.hphp,*.inc,*.module,*.php,*.php4,*.php5,*.phtml}] max_line_length = 999 ij_continuation_indent_size = 4 +indent_size = 4 +tab_width = 4 ij_php_align_assignments = true ij_php_align_class_constants = true ij_php_align_group_field_declarations = true @@ -109,6 +111,7 @@ ij_php_line_comment_add_space = false ij_php_line_comment_at_first_column = true ij_php_link_weight = 28 ij_php_lower_case_boolean_const = true +ij_php_lower_case_keywords = true ij_php_lower_case_null_const = true ij_php_method_brace_style = next_line ij_php_method_call_chain_wrap = off @@ -119,6 +122,7 @@ ij_php_method_weight = 28 ij_php_modifier_list_wrap = false ij_php_multiline_chained_calls_semicolon_on_new_line = false ij_php_namespace_brace_style = 1 +ij_php_new_line_after_php_opening_tag = false ij_php_null_type_position = in_the_end ij_php_package_weight = 28 ij_php_param_weight = 0 @@ -172,6 +176,7 @@ ij_php_space_before_method_call_parentheses = false ij_php_space_before_method_left_brace = true ij_php_space_before_method_parentheses = false ij_php_space_before_quest = true +ij_php_space_before_short_closure_left_parenthesis = false ij_php_space_before_switch_left_brace = true ij_php_space_before_switch_parentheses = true ij_php_space_before_try_left_brace = true From ee4a91087a1b696a3a82d9e47c1203a2fa47d5e6 Mon Sep 17 00:00:00 2001 From: Paul Rock Date: Fri, 17 Apr 2020 23:11:40 +0300 Subject: [PATCH 08/13] full paths of classes in phpdoc of Client class is reverted back --- src/Client.php | 92 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/src/Client.php b/src/Client.php index d5cf800..a13eaea 100644 --- a/src/Client.php +++ b/src/Client.php @@ -32,14 +32,14 @@ class Client implements Interfaces\ClientInterface /** * Configuration of connection * - * @var Config + * @var \RouterOS\Config */ private $_config; /** * API communication object * - * @var APIConnector + * @var \RouterOS\APIConnector */ private $_connector; @@ -47,11 +47,11 @@ class Client implements Interfaces\ClientInterface /** * Client constructor. * - * @param array|Config $config + * @param array|\RouterOS\Interfaces\ConfigInterface $config * - * @throws ClientException - * @throws ConfigException - * @throws QueryException + * @throws \RouterOS\Exceptions\ClientException + * @throws \RouterOS\Exceptions\ConfigException + * @throws \RouterOS\Exceptions\QueryException */ public function __construct($config) { @@ -80,7 +80,7 @@ class Client implements Interfaces\ClientInterface * @param string $parameter Name of required parameter * * @return mixed - * @throws ConfigException + * @throws \RouterOS\Exceptions\ConfigException */ private function config(string $parameter) { @@ -92,10 +92,9 @@ class Client implements Interfaces\ClientInterface * * @param string|array|Query $query * - * @return Client - * @throws QueryException + * @return \RouterOS\Client + * @throws \RouterOS\Exceptions\QueryException * @deprecated - * @codeCoverageIgnore */ public function write($query): Client { @@ -103,7 +102,7 @@ class Client implements Interfaces\ClientInterface $query = new Query($query); } elseif (is_array($query)) { $endpoint = array_shift($query); - $query = new Query($endpoint, $query); + $query = new Query($endpoint, $query); } if (!$query instanceof Query) { @@ -117,14 +116,14 @@ class Client implements Interfaces\ClientInterface /** * Send write query to RouterOS (modern version of write) * - * @param string|Query $endpoint Path of API query or Query object - * @param array|null $where List of where filters - * @param string|null $operations Some operations which need make on response - * @param string|null $tag Mark query with tag + * @param string|\RouterOS\Query $endpoint Path of API query or Query object + * @param array|null $where List of where filters + * @param string|null $operations Some operations which need make on response + * @param string|null $tag Mark query with tag * - * @return Client - * @throws QueryException - * @throws ClientException + * @return \RouterOS\Client + * @throws \RouterOS\Exceptions\QueryException + * @throws \RouterOS\Exceptions\ClientException * @since 1.0.0 */ public function query($endpoint, array $where = null, string $operations = null, string $tag = null): Client @@ -167,16 +166,17 @@ class Client implements Interfaces\ClientInterface * * @param array $item * @param Query $query - * @return Query - * @throws ClientException - * @throws QueryException + * + * @return \RouterOS\Query + * @throws \RouterOS\Exceptions\ClientException + * @throws \RouterOS\Exceptions\QueryException */ private function preQuery(array $item, Query $query): Query { // Null by default - $key = null; + $key = null; $operator = null; - $value = null; + $value = null; switch (count($item)) { case 1: @@ -198,10 +198,10 @@ class Client implements Interfaces\ClientInterface /** * Send write query object to RouterOS * - * @param Query $query + * @param \RouterOS\Query $query * - * @return Client - * @throws QueryException + * @return \RouterOS\Client + * @throws \RouterOS\Exceptions\QueryException * @since 1.0.0 */ private function writeRAW(Query $query): Client @@ -285,7 +285,7 @@ class Client implements Interfaces\ClientInterface /** * Read using Iterators to improve performance on large dataset * - * @return ResponseIterator + * @return \RouterOS\ResponseIterator * @since 1.0.0 */ public function readAsIterator(): ResponseIterator @@ -310,8 +310,8 @@ class Client implements Interfaces\ClientInterface { // This RAW should't be an error $positions = array_keys($raw, '!re'); - $count = count($raw); - $result = []; + $count = count($raw); + $result = []; if (isset($positions[1])) { @@ -348,8 +348,8 @@ class Client implements Interfaces\ClientInterface public function parseResponse(array $response): array { $result = []; - $i = -1; - $lines = count($response); + $i = -1; + $lines = count($response); foreach ($response as $key => $value) { switch ($value) { case '!re': @@ -379,12 +379,12 @@ class Client implements Interfaces\ClientInterface /** * Response helper * - * @param $value - * @param $result - * @param $matches - * @param string $iterator + * @param string $value Value which should be parsed + * @param array $result Array with parsed response + * @param array $matches Matched words + * @param string|int $iterator Type of iterations or number of item */ - private function preParseResponse($value, &$result, &$matches, $iterator = 'after'): void + private function preParseResponse(string $value, array &$result, array &$matches, $iterator = 'after'): void { $this->pregResponse($value, $matches); if (isset($matches[1][0], $matches[2][0])) { @@ -396,11 +396,11 @@ class Client implements Interfaces\ClientInterface * Parse result from RouterOS by regular expression * * @param string $value - * @param array $matches + * @param array $matches */ - private function pregResponse(string $value, &$matches): void + private function pregResponse(string $value, array &$matches): void { - preg_match_all('/^[=|\.](.*)=(.*)/', $value, $matches); + preg_match_all('/^[=|.](.*)=(.*)/', $value, $matches); } /** @@ -409,9 +409,9 @@ class Client implements Interfaces\ClientInterface * @param bool $legacyRetry Retry login if we detect legacy version of RouterOS * * @return bool - * @throws ClientException - * @throws ConfigException - * @throws QueryException + * @throws \RouterOS\Exceptions\ClientException + * @throws \RouterOS\Exceptions\ConfigException + * @throws \RouterOS\Exceptions\QueryException */ private function login(bool $legacyRetry = false): bool { @@ -465,7 +465,7 @@ class Client implements Interfaces\ClientInterface * @param array $response * * @return bool - * @throws ConfigException + * @throws \RouterOS\Exceptions\ConfigException */ private function isLegacy(array &$response): bool { @@ -476,9 +476,9 @@ class Client implements Interfaces\ClientInterface * Connect to socket server * * @return bool - * @throws ClientException - * @throws ConfigException - * @throws QueryException + * @throws \RouterOS\Exceptions\ClientException + * @throws \RouterOS\Exceptions\ConfigException + * @throws \RouterOS\Exceptions\QueryException */ private function connect(): bool { From e5992edb045dc7ad6cd203acb3e7a708ee46be34 Mon Sep 17 00:00:00 2001 From: Paul Rock Date: Fri, 17 Apr 2020 23:12:05 +0300 Subject: [PATCH 09/13] code style fix in APIConnector --- src/APIConnector.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/APIConnector.php b/src/APIConnector.php index f85c013..9da509d 100644 --- a/src/APIConnector.php +++ b/src/APIConnector.php @@ -48,8 +48,9 @@ class APIConnector /** * Write word to stream * - * @param string $word - * @return int return number of written bytes + * @param string $word + * + * @return int return number of written bytes */ public function writeWord(string $word): int { From c7130effd0b0816b18095895ff2d248d5b116964 Mon Sep 17 00:00:00 2001 From: Paul Rock Date: Fri, 17 Apr 2020 23:12:23 +0300 Subject: [PATCH 10/13] code style fix in APILengthCoDec --- src/APILengthCoDec.php | 121 ++++++++++++++++++++++++------------------------- 1 file changed, 59 insertions(+), 62 deletions(-) diff --git a/src/APILengthCoDec.php b/src/APILengthCoDec.php index 068652e..5c9bda5 100644 --- a/src/APILengthCoDec.php +++ b/src/APILengthCoDec.php @@ -19,37 +19,38 @@ class APILengthCoDec /** * Encode string to length of string * - * Encode the length : - - if length <= 0x7F (binary : 01111111 => 7 bits set to 1) - - encode length with one byte - - set the byte to length value, as length maximal value is 7 bits set to 1, the most significant bit is always 0 - - end - - length <= 0x3FFF (binary : 00111111 11111111 => 14 bits set to 1) - - encode length with two bytes - - 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 significance bits (10) - - end - => minimal encoded value is 10000000 10000000 - - length <= 0x1FFFFF (binary : 00011111 11111111 11111111 => 21 bits set to 1) - - encode length with three bytes - - set length value to 0xC00000 (binary : 11000000 00000000 00000000) - - add length : as length maximal value is 21 bits to 1, this does not modify the 3 most significance bits (110) - - end - => minimal encoded value is 11000000 01000000 00000000 - - length <= 0x0FFFFFFF (binary : 00001111 11111111 11111111 11111111 => 28 bits set to 1) - - encode length with four bytes - - set length value to 0xE0000000 (binary : 11100000 00000000 00000000 00000000) - - add length : as length maximal value is 28 bits to 1, this does not modify the 4 most significance bits (1110) - - end - => minimal encoded value is 11100000 00100000 00000000 00000000 - - length <= 0x7FFFFFFFFF (binary : 00000111 11111111 11111111 11111111 11111111 => 35 bits set to 1) - - encode length with five bytes - - set length value to 0xF000000000 (binary : 11110000 00000000 00000000 00000000 00000000) - - add length : as length maximal value is 35 bits to 1, this does not modify the 5 most significance bits (11110) - - end - - length > 0x7FFFFFFFFF : not supported + * Encode the length: + * - if length <= 0x7F (binary : 01111111 => 7 bits set to 1) + * - encode length with one byte + * - set the byte to length value, as length maximal value is 7 bits set to 1, the most significant bit is always 0 + * - end + * - length <= 0x3FFF (binary : 00111111 11111111 => 14 bits set to 1) + * - encode length with two bytes + * - 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 significance bits (10) + * - end + * => minimal encoded value is 10000000 10000000 + * - length <= 0x1FFFFF (binary : 00011111 11111111 11111111 => 21 bits set to 1) + * - encode length with three bytes + * - set length value to 0xC00000 (binary : 11000000 00000000 00000000) + * - add length : as length maximal value is 21 bits to 1, this does not modify the 3 most significance bits (110) + * - end + * => minimal encoded value is 11000000 01000000 00000000 + * - length <= 0x0FFFFFFF (binary : 00001111 11111111 11111111 11111111 => 28 bits set to 1) + * - encode length with four bytes + * - set length value to 0xE0000000 (binary : 11100000 00000000 00000000 00000000) + * - add length : as length maximal value is 28 bits to 1, this does not modify the 4 most significance bits (1110) + * - end + * => minimal encoded value is 11100000 00100000 00000000 00000000 + * - length <= 0x7FFFFFFFFF (binary : 00000111 11111111 11111111 11111111 11111111 => 35 bits set to 1) + * - encode length with five bytes + * - set length value to 0xF000000000 (binary : 11110000 00000000 00000000 00000000 00000000) + * - add length : as length maximal value is 35 bits to 1, this does not modify the 5 most significance bits (11110) + * - end + * - length > 0x7FFFFFFFFF : not supported + * + * @param int|float $length * - * @param int|float $length * @return string */ public static function encodeLength($length): string @@ -79,39 +80,35 @@ class APILengthCoDec return BinaryStringHelper::IntegerToNBOBinaryString(0xF000000000 + $length); } - // Decode length of data when reading : - // The 5 firsts bits of the first byte specify how the length is encoded. - // The position of the first 0 value bit, starting from the most significant postion. - // - 0xxxxxxx => The 7 remainings bits of the first byte is the length : - // => min value of length is 0x00 - // => max value of length is 0x7F (127 bytes) - // - 10xxxxxx => The 6 remainings bits of the first byte plus the next byte represent the lenght - // NOTE : the next byte MUST be at least 0x80 !! - // => min value of length is 0x80 - // => max value of length is 0x3FFF (16,383 bytes, near 16 KB) - // - 110xxxxx => The 5 remainings bits of th first byte and the two next bytes represent the length - // => max value of length is 0x1FFFFF (2,097,151 bytes, near 2 MB) - // - 1110xxxx => The 4 remainings bits of the first byte and the three next bytes represent the length - // => max value of length is 0xFFFFFFF (268,435,455 bytes, near 270 MB) - // - 11110xxx => The 3 remainings bits of the first byte and the four next bytes represent the length - // => max value of length is 0x7FFFFFFF (2,147,483,647 byes, 2GB) - // - 11111xxx => This byte is not a length-encoded word but a control byte. - // => Extracted from Mikrotik API doc : - // it is a reserved control byte. - // After receiving unknown control byte API client cannot proceed, because it cannot know how to interpret following bytes - // Currently control bytes are not used - + /** + * Decode length of data when reading : + * The 5 firsts bits of the first byte specify how the length is encoded. + * The position of the first 0 value bit, starting from the most significant postion. + * - 0xxxxxxx => The 7 remainings bits of the first byte is the length : + * => min value of length is 0x00 + * => max value of length is 0x7F (127 bytes) + * - 10xxxxxx => The 6 remainings bits of the first byte plus the next byte represent the lenght + * NOTE : the next byte MUST be at least 0x80 !! + * => min value of length is 0x80 + * => max value of length is 0x3FFF (16,383 bytes, near 16 KB) + * - 110xxxxx => The 5 remainings bits of th first byte and the two next bytes represent the length + * => max value of length is 0x1FFFFF (2,097,151 bytes, near 2 MB) + * - 1110xxxx => The 4 remainings bits of the first byte and the three next bytes represent the length + * => max value of length is 0xFFFFFFF (268,435,455 bytes, near 270 MB) + * - 11110xxx => The 3 remainings bits of the first byte and the four next bytes represent the length + * => max value of length is 0x7FFFFFFF (2,147,483,647 byes, 2GB) + * - 11111xxx => This byte is not a length-encoded word but a control byte. + * => Extracted from Mikrotik API doc: + * it is a reserved control byte. + * After receiving unknown control byte API client cannot proceed, because it cannot know how to interpret following bytes + * Currently control bytes are not used + * + * @param \RouterOS\Interfaces\StreamInterface $stream + * + * @return int + */ public static function decodeLength(StreamInterface $stream): int { - // if (false === is_resource($stream)) { - // throw new \InvalidArgumentException( - // sprintf( - // 'Argument must be a stream resource type. %s given.', - // gettype($stream) - // ) - // ); - // } - // Read first byte $firstByte = ord($stream->read(1)); @@ -191,7 +188,7 @@ class APILengthCoDec } // 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'); } } From d9393d2aaca31e7c3ef3ed32e1b2df3835a1d2c8 Mon Sep 17 00:00:00 2001 From: Paul Rock Date: Fri, 17 Apr 2020 23:51:33 +0300 Subject: [PATCH 11/13] response types of Query class methods switched from self to QueryInterface --- src/Query.php | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Query.php b/src/Query.php index 9458d91..fe629f9 100644 --- a/src/Query.php +++ b/src/Query.php @@ -81,11 +81,11 @@ class Query implements QueryInterface * @param bool|string|int $value Value which need to check (by default true) * @param bool|string|int $operator It may be one from list [-,=,>,<] * - * @return \RouterOS\Query + * @return \RouterOS\Interfaces\QueryInterface * @throws \RouterOS\Exceptions\QueryException * @since 1.0.0 */ - public function where(string $key, $operator = null, $value = null): self + public function where(string $key, $operator = null, $value = null): QueryInterface { return $this->world('?' . $key, $operator, $value); } @@ -96,11 +96,11 @@ class Query implements QueryInterface * @param string $key Key which need to find * @param bool|string|int $value Value which need to check (by default true) * - * @return \RouterOS\Query + * @return \RouterOS\Interfaces\QueryInterface * @throws \RouterOS\Exceptions\QueryException * @since 1.1 */ - public function equal(string $key, $value = null): self + public function equal(string $key, $value = null): QueryInterface { return $this->world('=' . $key, null, $value); } @@ -112,10 +112,10 @@ class Query implements QueryInterface * @param bool|string|int $value Value which need to check (by default true) * @param bool|string|int $operator It may be one from list [-,=,>,<] * - * @return \RouterOS\Query + * @return \RouterOS\Interfaces\QueryInterface * @throws \RouterOS\Exceptions\QueryException */ - private function world(string $key, $operator = null, $value = null): self + private function world(string $key, $operator = null, $value = null): QueryInterface { if (null !== $operator && null === $value) { @@ -148,10 +148,10 @@ class Query implements QueryInterface * * @param string $operations * - * @return \RouterOS\Query + * @return \RouterOS\Interfaces\QueryInterface * @since 1.0.0 */ - public function operations(string $operations): self + public function operations(string $operations): QueryInterface { $this->_operations = '?#' . $operations; return $this; @@ -162,10 +162,10 @@ class Query implements QueryInterface * * @param string $name * - * @return \RouterOS\Query + * @return \RouterOS\Interfaces\QueryInterface * @since 1.0.0 */ - public function tag(string $name): self + public function tag(string $name): QueryInterface { $this->_tag = '.tag=' . $name; return $this; @@ -176,9 +176,9 @@ class Query implements QueryInterface * * @param string $word * - * @return \RouterOS\Query + * @return \RouterOS\Interfaces\QueryInterface */ - public function add(string $word): Query + public function add(string $word): QueryInterface { $this->_attributes[] = $word; return $this; @@ -199,10 +199,10 @@ class Query implements QueryInterface * * @param array $attributes * - * @return \RouterOS\Query + * @return \RouterOS\Interfaces\QueryInterface * @since 0.7 */ - public function setAttributes(array $attributes): Query + public function setAttributes(array $attributes): QueryInterface { $this->_attributes = $attributes; return $this; @@ -213,7 +213,7 @@ class Query implements QueryInterface * * @return string|null */ - public function getEndpoint() + public function getEndpoint(): ?string { return $this->_endpoint; } @@ -223,10 +223,10 @@ class Query implements QueryInterface * * @param string|null $endpoint * - * @return \RouterOS\Query + * @return \RouterOS\Interfaces\QueryInterface * @since 0.7 */ - public function setEndpoint(string $endpoint = null): Query + public function setEndpoint(string $endpoint = null): QueryInterface { $this->_endpoint = $endpoint; return $this; From 841e5b2c7adc1b258382e6a713e09aa924286f34 Mon Sep 17 00:00:00 2001 From: Paul Rock Date: Fri, 17 Apr 2020 23:52:07 +0300 Subject: [PATCH 12/13] response types in QueryInterface fixed, method equal added to interface --- src/Interfaces/QueryInterface.php | 40 ++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/Interfaces/QueryInterface.php b/src/Interfaces/QueryInterface.php index ff864a4..cdd334f 100644 --- a/src/Interfaces/QueryInterface.php +++ b/src/Interfaces/QueryInterface.php @@ -2,8 +2,6 @@ namespace RouterOS\Interfaces; -use RouterOS\Query; - /** * Interface QueryInterface * @@ -19,38 +17,54 @@ interface QueryInterface * @param bool|string|int $value Value which need to check (by default true) * @param bool|string|int $operator It may be one from list [-,=,>,<] * - * @return \RouterOS\Query + * @return \RouterOS\Interfaces\QueryInterface * @throws \RouterOS\Exceptions\ClientException * @since 1.0.0 */ - public function where(string $key, $operator = '=', $value = null); + public function where(string $key, $operator = '=', $value = null): QueryInterface; + + /** + * Setter for write/update queries + * + * @param string $key Key which need to find + * @param bool|string|int $value Value which need to check (by default true) + * + * @return \RouterOS\Interfaces\QueryInterface + * @throws \RouterOS\Exceptions\QueryException + * @since 1.1 + */ + public function equal(string $key, $value = null): QueryInterface; /** * Append additional operations * * @param string $operations * + * @return \RouterOS\Interfaces\QueryInterface + * * @since 1.0.0 */ - public function operations(string $operations); + public function operations(string $operations): QueryInterface; /** * Append tag to query (it should be at end) * * @param string $name * + * @return \RouterOS\Interfaces\QueryInterface + * * @since 1.0.0 */ - public function tag(string $name); + public function tag(string $name): QueryInterface; /** * Append to array yet another attribute of query * * @param string $word * - * @return \RouterOS\Query + * @return \RouterOS\Interfaces\QueryInterface */ - public function add(string $word): Query; + public function add(string $word): QueryInterface; /** * Get attributes array of current query @@ -64,27 +78,27 @@ interface QueryInterface * * @param array $attributes * - * @return \RouterOS\Query + * @return \RouterOS\Interfaces\QueryInterface * @since 0.7 */ - public function setAttributes(array $attributes): Query; + public function setAttributes(array $attributes): QueryInterface; /** * Get endpoint of current query * * @return string|null */ - public function getEndpoint(); + public function getEndpoint(): ?string; /** * Set endpoint of query * * @param string $endpoint * - * @return \RouterOS\Query + * @return \RouterOS\Interfaces\QueryInterface * @since 0.7 */ - public function setEndpoint(string $endpoint): Query; + public function setEndpoint(string $endpoint): QueryInterface; /** * Build body of query From 17522170e22b0fa5d19c79da230a67af791524b7 Mon Sep 17 00:00:00 2001 From: Paul Rock Date: Fri, 17 Apr 2020 23:52:30 +0300 Subject: [PATCH 13/13] couple bugs fixed in Client class --- src/Client.php | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/Client.php b/src/Client.php index a13eaea..a6c8346 100644 --- a/src/Client.php +++ b/src/Client.php @@ -7,6 +7,7 @@ use RouterOS\Exceptions\ConfigException; use RouterOS\Exceptions\QueryException; use RouterOS\Helpers\ArrayHelper; +use RouterOS\Interfaces\QueryInterface; use function array_keys; use function array_shift; use function chr; @@ -90,7 +91,7 @@ class Client implements Interfaces\ClientInterface /** * Send write query to RouterOS * - * @param string|array|Query $query + * @param string|array|\RouterOS\Query $query * * @return \RouterOS\Client * @throws \RouterOS\Exceptions\QueryException @@ -164,8 +165,8 @@ class Client implements Interfaces\ClientInterface /** * Query helper * - * @param array $item - * @param Query $query + * @param array $item + * @param \RouterOS\Interfaces\QueryInterface $query * * @return \RouterOS\Query * @throws \RouterOS\Exceptions\ClientException @@ -364,12 +365,12 @@ class Client implements Interfaces\ClientInterface for ($j = $key + 1; $j <= $lines; $j++) { // If we have lines after current one if (isset($response[$j])) { - $this->preParseResponse($response[$j], $result, $matches, $j); + $this->preParseResponse($response[$j], $result, $matches); } } break 2; default: - $this->preParseResponse($value, $result, $matches); + $this->preParseResponse($value, $result, $matches, $i); break; } } @@ -381,10 +382,10 @@ class Client implements Interfaces\ClientInterface * * @param string $value Value which should be parsed * @param array $result Array with parsed response - * @param array $matches Matched words + * @param null|array $matches Matched words * @param string|int $iterator Type of iterations or number of item */ - private function preParseResponse(string $value, array &$result, array &$matches, $iterator = 'after'): void + private function preParseResponse(string $value, array &$result, ?array &$matches, $iterator = 'after'): void { $this->pregResponse($value, $matches); if (isset($matches[1][0], $matches[2][0])) { @@ -395,10 +396,10 @@ class Client implements Interfaces\ClientInterface /** * Parse result from RouterOS by regular expression * - * @param string $value - * @param array $matches + * @param string $value + * @param null|array $matches */ - private function pregResponse(string $value, array &$matches): void + private function pregResponse(string $value, ?array &$matches): void { preg_match_all('/^[=|.](.*)=(.*)/', $value, $matches); }