PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backup, Restore, Migration & Clone / 4.9.2
WP STAGING – WordPress Backup, Restore, Migration & Clone v4.9.2
4.9.2 4.9.1 4.9.0 4.8.1 trunk 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.10.0 3.2.0 3.3.1 3.3.2 3.3.3 3.4.1 3.4.3 3.5.0 3.6.0 3.7.1 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.8.7 3.9.0 3.9.1 3.9.2 3.9.3 3.9.4 4.0.0 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.0 4.7.0 4.7.1 4.7.2 4.7.3 4.8.0
wp-staging / Framework / Utils / DataEncoder.php
wp-staging / Framework / Utils Last commit date
Cache 7 months ago DBPermissions.php 9 months ago DataEncoder.php 10 months ago DatabaseOptions.php 2 weeks ago Escape.php 9 months ago Glob.php 2 years ago Hooks.php 2 months ago Math.php 9 months ago PluginInfo.php 5 months ago Sanitize.php 2 months ago ServerVars.php 2 years ago SlashMode.php 5 years ago Strings.php 7 months ago Times.php 5 months ago Urls.php 2 weeks ago Version.php 1 year ago WpDefaultDirectories.php 2 years ago
DataEncoder.php
229 lines
1 <?php
2
3 namespace WPStaging\Framework\Utils;
4
5 /**
6 * The class is responsible for converting data to/from binary and/or hex format
7 * It uses pack, unpack and bin2hex functions internally
8 */
9 class DataEncoder
10 {
11 /**
12 * The pack mode for 64-bit integer
13 * P -> Little Endianness
14 * @var string
15 */
16 const PACK_MODE_64BIT = 'P';
17
18 /**
19 * The pack mode for 32-bit integer
20 * V -> Little Endianness
21 * @var string
22 */
23 const PACK_MODE_32BIT = 'V';
24
25 /** @var string */
26 protected $packMode;
27
28 public function __construct()
29 {
30 $this->packMode = PHP_INT_SIZE === 8 ? self::PACK_MODE_64BIT : self::PACK_MODE_32BIT;
31 }
32
33 /**
34 * @param string $format
35 * @param int[] $intArray
36 * @return string
37 */
38 public function intArrayToHex(string $format, array $intArray): string
39 {
40 if (empty($format)) {
41 throw new \InvalidArgumentException('DataEncoder error: Format cannot be empty.');
42 }
43
44 if (empty($intArray)) {
45 throw new \InvalidArgumentException('DataEncoder error: Int array cannot be empty.');
46 }
47
48 $formats = str_split($format);
49 if (count($formats) !== count($intArray)) {
50 throw new \InvalidArgumentException(
51 'DataEncoder error: The number of characters in formats and integers in array must be equal'
52 );
53 }
54
55 if (preg_match('/[^1-8]/', $format)) {
56 throw new \InvalidArgumentException('DataEncoder error: Invalid format.');
57 }
58
59 $index = 0;
60 $result = '';
61 foreach ($formats as $format) {
62 // Use try-catch to re-throw for index position
63 try {
64 $bytes = intval($format);
65 if (!is_int($bytes)) {
66 throw new \InvalidArgumentException('DataEncoder error: Invalid format.');
67 }
68
69 $result .= $this->intToHex($intArray[$index], $bytes);
70 } catch (\InvalidArgumentException $ex) {
71 throw new \InvalidArgumentException($ex->getMessage() . ' at index ' . $index);
72 } catch (\Exception $ex) {
73 throw new \InvalidArgumentException($ex->getMessage() . ' at index ' . $index);
74 }
75
76 $index++;
77 }
78
79 return $result;
80 }
81
82 /**
83 * Convert an integer to hexadecimal representation
84 *
85 * @param int|float|null $value The value to convert (must be an integer)
86 * @param int $bytes Number of bytes for the representation
87 * @return string Hexadecimal representation
88 * @throws \InvalidArgumentException If value is null, not an integer, or invalid
89 */
90 public function intToHex($value, int $bytes = 8): string
91 {
92 if ($value === null) {
93 throw new \InvalidArgumentException('DataEncoder error: Value cannot be null');
94 }
95
96 if (!is_int($value)) {
97 throw new \InvalidArgumentException('DataEncoder error: Value must be an integer, ' . gettype($value) . ' given');
98 }
99
100 if ($value < 0 && PHP_INT_SIZE === 8) {
101 throw new \InvalidArgumentException('DataEncoder error: Value must be non-negative, ' . $value . ' given');
102 }
103
104 if ($bytes < 1 || $bytes > 8) {
105 throw new \InvalidArgumentException('DataEncoder error: Invalid number of bytes');
106 }
107
108 // convert bytes to int
109 $maxInt = (2 ** ($bytes * 8)) - 1;
110 if ($value > $maxInt) {
111 throw new \InvalidArgumentException('DataEncoder error: Pack: Value is too large for the given number of bytes');
112 }
113
114 $pack = pack($this->packMode, $value);
115 // Early bail for 64bit system or 32bit system when packing 4 or less bytes
116 if ($bytes <= PHP_INT_SIZE) {
117 return bin2hex(substr($pack, 0, $bytes));
118 }
119
120 $hex = bin2hex($pack);
121
122 // This will pad the hex string with zeros if the number of bytes is less than 8
123 // but greater than 4 for 32bit system
124 return $hex . str_repeat("00", max(0, $bytes - PHP_INT_SIZE));
125 }
126
127 /**
128 * @param string $format
129 * @param string $hex
130 *
131 * @throws \InvalidArgumentException
132 * @return int[]
133 */
134 public function hexToIntArray(string $format, string $hex): array
135 {
136 if (empty($format)) {
137 throw new \InvalidArgumentException('DataEncoder error: Format cannot be empty');
138 }
139
140 if (preg_match('/[^1-8]/', $format)) {
141 throw new \InvalidArgumentException('DataEncoder error: Invalid format: ' . $format);
142 }
143
144 if (empty($hex)) {
145 throw new \InvalidArgumentException('DataEncoder error: Hex string cannot be empty');
146 }
147
148 if (strlen($hex) % 2 !== 0) {
149 throw new \InvalidArgumentException('DataEncoder error: Invalid hex string: ' . $hex);
150 }
151
152 // check for invalid characters in hex
153 if (preg_match('/[^0-9a-fA-F]/', $hex)) {
154 throw new \InvalidArgumentException('DataEncoder error: Invalid hex string: ' . $hex);
155 }
156
157 $formats = str_split($format);
158 $index = 0;
159 $intArray = [];
160 foreach ($formats as $format) {
161 $bytes = intval($format);
162 $length = $bytes * 2;
163
164 if ($index + $length > strlen($hex)) {
165 throw new \InvalidArgumentException('DataEncoder error: Hex string is short according to format.');
166 }
167
168 $subHex = substr($hex, $index, $length);
169
170 $intArray[] = $this->hexToInt($subHex, $bytes);
171 $index += $length;
172 }
173
174 if ($index !== strlen($hex)) {
175 throw new \InvalidArgumentException('DataEncoder error: Hex string is long according to format.');
176 }
177
178 return $intArray;
179 }
180
181 public function hexToInt(string $hex, int $bytes = 8): int
182 {
183 if ($bytes < 1 || $bytes > 8) {
184 throw new \InvalidArgumentException('DataEncoder error: Invalid number of bytes.');
185 }
186
187 if (empty($hex)) {
188 throw new \InvalidArgumentException('DataEncoder error: Hex string cannot be empty.');
189 }
190
191 if (strlen($hex) / 2 > $bytes) {
192 throw new \InvalidArgumentException('DataEncoder error: Hex string is longer than the given number of bytes.');
193 }
194
195 if (strlen($hex) % 2 !== 0) {
196 throw new \InvalidArgumentException('DataEncoder error: Invalid hex string: ' . $hex);
197 }
198
199 // check for invalid characters in hex
200 if (preg_match('/[^0-9a-fA-F]/', $hex)) {
201 throw new \InvalidArgumentException('DataEncoder error: Invalid hex string: ' . $hex);
202 }
203
204 $binary = hex2bin($hex);
205 if ($bytes < PHP_INT_SIZE) {
206 $binary = str_pad($binary, PHP_INT_SIZE, "\x00", STR_PAD_RIGHT);
207 }
208
209 // Early bail for 64bit system or 32bit system when unpacking 4 or less bytes
210 if ($bytes <= PHP_INT_SIZE) {
211 return unpack($this->packMode, $binary)[1];
212 }
213
214 // For 32bit system when unpacking more than 4 bytes, let first check if those are
215 // only zeros, if not throw exception
216 $extraData = substr($binary, PHP_INT_SIZE);
217 $extraZero = str_repeat("\x00", max(0, $bytes - PHP_INT_SIZE));
218 if ($extraData !== $extraZero) {
219 throw new \InvalidArgumentException(
220 'DataEncoder error: Unpack: Value is too large for the given number of bytes.'
221 );
222 }
223
224 $dataToUnpack = substr($binary, 0, PHP_INT_SIZE);
225
226 return unpack($this->packMode, $dataToUnpack)[1];
227 }
228 }
229