1
0
mirror of https://github.com/danog/PHPStruct.git synced 2024-11-30 04:19:08 +01:00

Reorganized even more classes and implemented padding for the @ format.

This commit is contained in:
danogentili 2016-07-28 13:40:22 +02:00
parent 9a85ef7718
commit 0c225600d6
6 changed files with 199 additions and 57 deletions

View File

@ -2,6 +2,8 @@ language: php
php: php:
- '7.0' - '7.0'
- nightly - nightly
- hhvm
- '5.6'
before_script: before_script:
- composer update --dev - composer update --dev

View File

@ -41,7 +41,7 @@ composer require danog/phpstruct
Dynamic (recommended) Dynamic (recommended)
``` ```
require('vendor/autoload.php'); require('vendor/autoload.php');
$struct = new \danog\PHP\Struct(); $struct = new \danog\PHP\StructClass();
$pack = $struct->pack("2cxi", "ab", 44); $pack = $struct->pack("2cxi", "ab", 44);
$unpack = $struct->unpack("2cxi", $pack); $unpack = $struct->unpack("2cxi", $pack);
var_dump($unpack); var_dump($unpack);
@ -51,7 +51,7 @@ $count = $struct->calcsize("2cxi");
Dynamic (while specifying format string during istantiation) Dynamic (while specifying format string during istantiation)
``` ```
require('vendor/autoload.php'); require('vendor/autoload.php');
$struct = new \danog\PHP\Struct("2cxi"); $struct = new \danog\PHP\StructClass("2cxi");
$pack = $struct->pack("ab", 44); $pack = $struct->pack("ab", 44);
$unpack = $struct->unpack($pack); $unpack = $struct->unpack($pack);
var_dump($unpack); var_dump($unpack);

View File

@ -2,6 +2,7 @@
//require('vendor/autoload.php'); //require('vendor/autoload.php');
require 'lib/danog/PHP/StructTools.php'; require 'lib/danog/PHP/StructTools.php';
require 'lib/danog/PHP/StructClass.php';
require 'lib/danog/PHP/StructException.php'; require 'lib/danog/PHP/StructException.php';
require 'lib/danog/PHP/Struct.php'; require 'lib/danog/PHP/Struct.php';
//var_dump(["nv", 61, 61, false, 333, 444, 232423, 234342, 243342423424, 234234234234, 234234234234, 234234234234, 34434, 344434, 2.2343, 3.03424, "dd"]); //var_dump(["nv", 61, 61, false, 333, 444, 232423, 234342, 243342423424, 234234234234, 234234234234, 234234234234, 34434, 344434, 2.2343, 3.03424, "dd"]);
@ -11,10 +12,10 @@ require 'lib/danog/PHP/Struct.php';
3.03424, 'df', 'asdfghjkl', 1283912 3.03424, 'df', 'asdfghjkl', 1283912
);die;*/ );die;*/
$struct = new \danog\PHP\Struct('2cxbxBx?xhxHxixIxlxLxqxQxnxNxfxdx2sx5pP'); $struct = new \danog\PHP\StructClass('2cxbxBx?xhxHxixIxlxLxqxQxnxNxfxdx2sx5pP');
var_dump($struct->unpack($struct->pack('n', 'v', -127, 100, true, 333, 444, 232423, 234342, 999999999999, 999999999999, -888888888888,888888888888, 34434, 344434, 2.2343, 3.03424, 'df', 'asdfghjkl', 1283912)), $struct->format);
var_dump($struct->unpack($struct->pack('n', 'v', -127, 100, true, 333, 444, 232423, 234342, 999999999999, 999999999999, -888888888888,888888888888, 34434, 344434, 2.2343, 3.03424, 'df', 'asdfghjkl', 1283912)));
die;
var_dump(\danog\PHP\Struct::unpack('2cxbxBx?xhxHxixIxlxLxqxQxnxNxfxdx2sx5pP', var_dump(\danog\PHP\Struct::unpack('2cxbxBx?xhxHxixIxlxLxqxQxnxNxfxdx2sx5pP',
\danog\PHP\Struct::pack('2cxbxBx?xhxHxixIxlxLxqxQxnxNxfxdx2sx5pP', \danog\PHP\Struct::pack('2cxbxBx?xhxHxixIxlxLxqxQxnxNxfxdx2sx5pP',
'n', 'v', -127, 100, true, 333, 444, 232423, 234342, 999999999999, 999999999999, -888888888888, 'n', 'v', -127, 100, true, 333, 444, 232423, 234342, 999999999999, 999999999999, -888888888888,

View File

@ -12,26 +12,23 @@ namespace danog\PHP;
* @author Daniil Gentili <daniil@daniil.it> * @author Daniil Gentili <daniil@daniil.it>
* @license MIT license * @license MIT license
*/ */
// Wrapper class // Struct class (for static access)
class Struct extends StructTools class Struct
{ {
/** /**
* pack. * pack.
* *
* Packs data into bytes * Packs data into bytes
* *
* @param ...$data Parameters to encode (may also contain format string) * @param $format Format string
* @param ...$data Parameters to encode
* *
* @return Encoded data * @return Encoded data
*/ */
public function pack(...$data) public static function pack($format, ...$data)
{ {
if (!(isset($this) && get_class($this) == __CLASS__) || $this->format == null) { $struct = new \danog\PHP\StructClass($format);
$struct = new \danog\PHP\Struct(array_shift($data)); return $struct->pack(...$data);
return $struct->_pack(...$data);
}
return $this->_pack(...$data);
} }
/** /**
@ -44,13 +41,10 @@ class Struct extends StructTools
* *
* @return Decoded data * @return Decoded data
*/ */
public function unpack($format, $data = null) public static function unpack($format, $data)
{ {
if (!(isset($this) && get_class($this) == __CLASS__) || $this->format == null) { $struct = new \danog\PHP\StructClass($format);
$struct = new \danog\PHP\Struct($format); return $struct->unpack($data);
return $struct->_unpack($data);
}
return $this->_unpack($format);
} }
/** /**
@ -63,12 +57,9 @@ class Struct extends StructTools
* *
* @return int with size of the struct. * @return int with size of the struct.
*/ */
public function calcsize($format = null) public static function calcsize($format)
{ {
if (!(isset($this) && get_class($this) == __CLASS__) || $this->format == null) { $struct = new \danog\PHP\StructClass($format);
$struct = new \danog\PHP\Struct($format); return $struct->size;
return $struct->_calcsize();
}
return $this->_calcsize();
} }
} }

View File

@ -0,0 +1,84 @@
<?php
namespace danog\PHP;
/**
* PHPStruct
* PHP implementation of Python's struct module.
* This library was created to help me develop a [client for the mtproto protocol](https://github.com/danog/MadelineProto).
* The functions and the formats are exactly the ones used in python's struct (https://docs.python.org/3/library/struct.html)
* For now custom byte size may not work properly on certain machines for the i, I, f and d formats.
*
* @author Daniil Gentili <daniil@daniil.it>
* @license MIT license
*/
// Struct class (for dynamic access)
class StructClass {
public $struct = null; // Will store an instance of the StructTools class
public $format = null; // Will contain the format
public $size = null; // Will contain the size
/**
* Constructor.
*
* Stores instance of the StructTools class and optional format/size
*/
public function __construct($format = null) {
$this->struct = new \danog\PHP\StructTools();
if($format !== null) {
$this->format = $format;
$this->size = $this->struct->calcsize($format);
}
}
/**
* pack.
*
* Packs data into bytes
*
* @param ...$data Parameters to encode (may contain format string)
*
* @return Encoded data
*/
public function pack(...$data)
{
if($this->format === null) {
$format = array_shift($data);
} else $format = $this->format;
return $this->struct->pack($format, ...$data);
}
/**
* unpack.
*
* Unpacks data into an array
*
* @param $format_maybe_data Format string (may be data if class is istantiated with format string)
* @param $data Data to decode
*
* @return Decoded data
*/
public function unpack($format_maybe_data, $data = null)
{
if($this->format === null) {
$format = $format_maybe_data;
} else {
$format = $this->format;
$data = $format_maybe_data;
}
return $this->struct->unpack($format, $data);
}
/**
* calcsize.
*
* Return the size of the struct (and hence of the string) corresponding to the given format.
*
* @param $format Format string
*
* @return int with size of the struct.
*/
public function calcsize($format = null)
{
return ($struct->format !== null && $struct->size !== null) ? $struct->size : $this->struct->calcsize($format);
}
}

View File

@ -12,11 +12,9 @@ namespace danog\PHP;
* @author Daniil Gentili <daniil@daniil.it> * @author Daniil Gentili <daniil@daniil.it>
* @license MIT license * @license MIT license
*/ */
// Main class // Working class
class StructTools class StructTools
{ {
public $format = null; // Will contain the format
public $size = null; // Will contain the size
/** /**
* Constructor. * Constructor.
* *
@ -195,6 +193,7 @@ class StructTools
'SIZE' => $this->SIZE, 'SIZE' => $this->SIZE,
'FORMATS' => $this->FORMATS, 'FORMATS' => $this->FORMATS,
'TYPE' => $this->TYPE, 'TYPE' => $this->TYPE,
'MODIFIER' => '<'
], ],
'>' => [ '>' => [
'BIG_ENDIAN' => true, 'BIG_ENDIAN' => true,
@ -202,6 +201,7 @@ class StructTools
'SIZE' => $this->SIZE, 'SIZE' => $this->SIZE,
'FORMATS' => $this->FORMATS, 'FORMATS' => $this->FORMATS,
'TYPE' => $this->TYPE, 'TYPE' => $this->TYPE,
'MODIFIER' => '>'
], ],
'!' => [ '!' => [
'BIG_ENDIAN' => true, 'BIG_ENDIAN' => true,
@ -209,6 +209,7 @@ class StructTools
'SIZE' => $this->SIZE, 'SIZE' => $this->SIZE,
'FORMATS' => $this->FORMATS, 'FORMATS' => $this->FORMATS,
'TYPE' => $this->TYPE, 'TYPE' => $this->TYPE,
'MODIFIER' => '!'
], ],
'=' => [ '=' => [
'BIG_ENDIAN' => $this->BIG_ENDIAN, 'BIG_ENDIAN' => $this->BIG_ENDIAN,
@ -216,6 +217,7 @@ class StructTools
'SIZE' => $this->SIZE, 'SIZE' => $this->SIZE,
'FORMATS' => $this->FORMATS, 'FORMATS' => $this->FORMATS,
'TYPE' => $this->TYPE, 'TYPE' => $this->TYPE,
'MODIFIER' => '='
], ],
'@' => [ '@' => [
'BIG_ENDIAN' => $this->BIG_ENDIAN, 'BIG_ENDIAN' => $this->BIG_ENDIAN,
@ -223,12 +225,10 @@ class StructTools
'SIZE' => $this->NATIVE_SIZE, 'SIZE' => $this->NATIVE_SIZE,
'FORMATS' => $this->NATIVE_FORMATS, 'FORMATS' => $this->NATIVE_FORMATS,
'TYPE' => $this->NATIVE_TYPE, 'TYPE' => $this->NATIVE_TYPE,
'MODIFIER' => '@'
], ],
]; ];
if($format !== null) {
$this->format = $format;
$this->size = $this->_calcsize();
}
} }
/** /**
@ -250,14 +250,18 @@ class StructTools
* *
* Packs data into bytes * Packs data into bytes
* *
* @param $format Format string
* @param ...$data Parameters to encode * @param ...$data Parameters to encode
* *
* @return Encoded data * @return Encoded data
*/ */
public function _pack(...$data) public function pack($format, ...$data)
{ {
$format = $this->padformat($format);
$result = null; // Data to return $result = null; // Data to return
$packcommand = $this->parseformat($this->array_each_strlen($data)); // Get pack parameters $size = $this->calcsize($format);
var_dump($size);
$packcommand = $this->parseformat($format, $this->array_each_strlen($data)); // Get pack parameters
set_error_handler([$this, 'ExceptionErrorHandler']); set_error_handler([$this, 'ExceptionErrorHandler']);
foreach ($packcommand as $key => $command) { foreach ($packcommand as $key => $command) {
try { try {
@ -292,7 +296,7 @@ class StructTools
case 'p': case 'p':
$curresult = pack('c', ($command['count'] - 1 > 255) ? 255 : $command['count'] - 1).pack('a'.($command['count'] - 1), $data[$command['datakey']]); $curresult = pack('c', ($command['count'] - 1 > 255) ? 255 : $command['count'] - 1).pack('a'.($command['count'] - 1), $data[$command['datakey']]);
break; break;
case 'q': /*case 'q':
case 'Q': case 'Q':
case 'l': case 'l':
case 'L': case 'L':
@ -303,7 +307,7 @@ class StructTools
case 'c': case 'c':
case 'C': case 'C':
$curresult = $this->num_pack($data[$command['datakey']], $command['modifiers']['SIZE'], ctype_upper($command['phpformat'])); $curresult = $this->num_pack($data[$command['datakey']], $command['modifiers']['SIZE'], ctype_upper($command['phpformat']));
break; break;*/
case '?': case '?':
$curresult = pack('c'.$command['count'], $data[$command['datakey']]); // Pack current char $curresult = pack('c'.$command['count'], $data[$command['datakey']]); // Pack current char
break; break;
@ -339,8 +343,8 @@ class StructTools
$result .= $curresult; $result .= $curresult;
} }
restore_error_handler(); restore_error_handler();
if (strlen($result) != $this->size) { if (strlen($result) != $size) {
throw new StructException('Length of generated data ('.strlen($result).') is different from length calculated using format string ('.$this->size.').'); throw new StructException('Length of generated data ('.strlen($result).') is different from length calculated using format string ('.$size.').');
} }
return $result; return $result;
@ -351,22 +355,25 @@ class StructTools
* *
* Unpacks data into an array * Unpacks data into an array
* *
* @param $format Format string
* @param $data Data to decode * @param $data Data to decode
* *
* @return Decoded data * @return Decoded data
*/ */
public function _unpack($data) public function unpack($format, $data)
{ {
if (strlen($data) != $this->size) { $format = $this->padformat($format);
throw new StructException('Length of given data ('.strlen($data).') is different from length calculated using format string ('.$this->size.').'); $size = $this->calcsize($format);
if (strlen($data) != $size) {
throw new StructException('Length of given data ('.strlen($data).') is different from length calculated using format string ('.$size.').');
} }
$dataarray = $this->data2array($data); $dataarray = $this->data2array($format, $data);
if ($this->array_total_strlen($dataarray) != $this->size) { if ($this->array_total_strlen($dataarray) != $size) {
throw new StructException('Length of given data array ('.$this->array_total_strlen($dataarray).') is different from length calculated using format string ('.$this->size.').'); throw new StructException('Length of array ('.$this->array_total_strlen($dataarray).') is different from length calculated using format string ('.$size.').');
} }
$result = []; // Data to return $result = []; // Data to return
$packcommand = $this->parseformat($this->array_each_strlen($dataarray), true); // Get unpack parameters $packcommand = $this->parseformat($format, $this->array_each_strlen($dataarray), true); // Get unpack parameters
set_error_handler([$this, 'ExceptionErrorHandler']); set_error_handler([$this, 'ExceptionErrorHandler']);
$arraycount = 0; $arraycount = 0;
foreach ($packcommand as $key => $command) { foreach ($packcommand as $key => $command) {
@ -449,13 +456,14 @@ class StructTools
* *
* @return int with size of the struct. * @return int with size of the struct.
*/ */
public function _calcsize() public function calcsize($format)
{ {
$format = $this->padformat($format);
$size = 0; $size = 0;
$modifier = $this->MODIFIERS['@']; $modifier = $this->MODIFIERS['@'];
$count = null; $count = null;
if($this->format == '') return 0; if($format == '') return 0;
foreach (str_split($this->format) as $offset => $currentformatchar) { foreach (str_split($format) as $offset => $currentformatchar) {
if (isset($this->MODIFIERS[$currentformatchar])) { if (isset($this->MODIFIERS[$currentformatchar])) {
$modifier = $this->MODIFIERS[$currentformatchar]; // Set the modifiers for the current format char $modifier = $this->MODIFIERS[$currentformatchar]; // Set the modifiers for the current format char
} elseif (is_numeric($currentformatchar) && ((int) $currentformatchar > 0 || (int) $currentformatchar <= 9)) { } elseif (is_numeric($currentformatchar) && ((int) $currentformatchar > 0 || (int) $currentformatchar <= 9)) {
@ -485,7 +493,7 @@ class StructTools
* *
* @return array with format and modifiers for each object to encode/decode * @return array with format and modifiers for each object to encode/decode
*/ */
public function parseformat($arraycount, $unpack = false) public function parseformat($format, $arraycount, $unpack = false)
{ {
$datarraycount = 0; // Current element of the array to pack/unpack $datarraycount = 0; // Current element of the array to pack/unpack
$formatcharcount = 0; // Current element to pack/unpack (sometimes there isn't a correspondant element in the array) $formatcharcount = 0; // Current element to pack/unpack (sometimes there isn't a correspondant element in the array)
@ -493,7 +501,8 @@ class StructTools
$result = []; // Array with the results $result = []; // Array with the results
$count = null; $count = null;
$loopcount = 0; $loopcount = 0;
foreach (str_split($this->format) as $offset => $currentformatchar) { // Current format char $totallength = 0;
foreach (str_split($format) as $offset => $currentformatchar) { // Current format char
if (!isset($result[$formatcharcount]) || !is_array($result[$formatcharcount])) { if (!isset($result[$formatcharcount]) || !is_array($result[$formatcharcount])) {
$result[$formatcharcount] = []; // Create array for current element $result[$formatcharcount] = []; // Create array for current element
} }
@ -556,16 +565,15 @@ class StructTools
* *
* @return array * @return array
**/ **/
public function data2array($data) public function data2array($format, $data)
{ {
$dataarray = []; $dataarray = [];
$dataarraykey = 0; $dataarraykey = 0;
$datakey = 0; $datakey = 0;
$count = null; $count = null;
$loopcount = 0; $loopcount = 0;
$modifier = $this->MODIFIERS['@']; $modifier = $this->MODIFIERS['@'];
foreach (str_split($this->format) as $offset => $currentformatchar) { foreach (str_split($format) as $offset => $currentformatchar) {
if (isset($this->MODIFIERS[$currentformatchar])) { if (isset($this->MODIFIERS[$currentformatchar])) {
$modifier = $this->MODIFIERS[$currentformatchar]; // Set the modifiers for the current format char $modifier = $this->MODIFIERS[$currentformatchar]; // Set the modifiers for the current format char
} elseif (is_numeric($currentformatchar) && ((int) $currentformatchar > 0 || (int) $currentformatchar <= 9)) { } elseif (is_numeric($currentformatchar) && ((int) $currentformatchar > 0 || (int) $currentformatchar <= 9)) {
@ -597,10 +605,52 @@ class StructTools
throw new StructException('Unkown format or modifier supplied ('.$currentformatchar.' at offset '.$offset.').'); throw new StructException('Unkown format or modifier supplied ('.$currentformatchar.' at offset '.$offset.').');
} }
} }
return $dataarray; return $dataarray;
} }
/**
* pdaformt.
*
* Pad format string with x format where needed
*
* @param $format Format string to pad
*
* @return Padded format string
**/
public function padformat($format) {
$modifier = $this->MODIFIERS['@'];
$result = null; // Result gormat string
$count = null;
$totallength = 0;
foreach (str_split($format) as $offset => $currentformatchar) { // Current format char
if (isset($this->MODIFIERS[$currentformatchar])) { // If current format char is a modifier
$modifier = $this->MODIFIERS[$currentformatchar]; // Set the modifiers for the current format char
} elseif (is_numeric($currentformatchar) && ((int) $currentformatchar >= 0 || (int) $currentformatchar <= 9)) {
$count .= (int) $currentformatchar; // Set the count for the current format char
} elseif (isset($modifier['FORMATS'][$currentformatchar])) {
if (!isset($count) || $count == null) {
$count = 1; // Set count to 1 by default.
}
$count = (int) $count;
if ($currentformatchar == 's' || $currentformatchar == 'p') {
$result .= $count.$currentformatchar;
} else {
for ($x = 0; $x < $count; $x++) {
if($modifier['MODIFIER'] == '@'){
$result .= str_pad("", $this->posmod(-$totallength, $modifier['SIZE'][$currentformatchar]), "x");
$totallength += $this->posmod(-$totallength, $modifier['SIZE'][$currentformatchar]) + $modifier['SIZE'][$currentformatchar];
}
$result .= $currentformatchar;
}
}
$count = null;
} else {
throw new StructException('Unkown format or modifier supplied at offset '.$offset.' ('.$currentformatchar.').');
}
}
return $result;
}
/** /**
* decbin. * decbin.
@ -712,7 +762,7 @@ class StructTools
* byte string with binary zeros so that the length is the * byte string with binary zeros so that the length is the
* blocksize. * blocksize.
* *
* @param $s Number to pack * @param $n Number to pack
* @param $blocksize Block size * @param $blocksize Block size
* @param $unsigned Boolean that determines whether to work in signed or unsigned mode * @param $unsigned Boolean that determines whether to work in signed or unsigned mode
* *
@ -780,8 +830,22 @@ class StructTools
foreach (str_split($s) as $i) { foreach (str_split($s) as $i) {
$bits .= $this->decbin(ord($i), 8); $bits .= $this->decbin(ord($i), 8);
} }
//var_dump($bits);
return $this->bindec($bits, $unsigned); return $this->bindec($bits, $unsigned);
} }
/**
* posmod(numeric,numeric) : numeric
* Works just like the % (modulus) operator, only returns always a postive number.
*/
function posmod($a, $b)
{
$resto = $a % $b;
if ($resto < 0) {
$resto += abs($b);
}
return $resto;
}
/** /**
* array_each_strlen. * array_each_strlen.
* *