PHP Passing an array to pack () function
pack()
syntax (from http://php.net/manual/en/function.pack.php )
string pack ( string $format [, mixed $args [, mixed $... ]] )
so, assuming that I need to pack three bytes
$packed = pack( "c*", 65, 66, 67 );
But what if I need to pack an arbitrary number of bytes?
They could be conveniently stored in an array so that I would naively try
$a = array( 65, 66, 67 );
$packed = pack( "c*", $a );
But that won't work.
Is there a way to make the pack()
array work?
source to share
You can create your own function array_pack
that calls internally pack
using call_user_func or call_user_func_array so that you can pass the correct number of parameters to it.
Maybe something like this might work (not tested though ... But you get the general idea)
function array_pack(array $arr) {
return call_user_func_array("pack", array_merge(array("c*"), $arr));
}
source to share
use string concatenation instead pack()
When packing bytes, packed binary data (string) can be obtained by simple use chr()
, concatenation .
and loop foreach
:
packed = "";
foreach ( $a as $byte ) {
$packed .= chr( $byte );
}
Where $a
is the original array and $packed
is the resulting binary data stored in a string variable as per the original question.
test
Having come, at the time of writing, with 5 different working solutions , it is worth doing a test if the amount of data in the package is huge.
I checked five cases with 1048576 array of elements to get 1MB of binary data. I measured the runtime and memory consumed.
Test conditions: PHP 5.6.30
- Mac OS X
-2.2 GHz Intel Core I7
(of course only one core is used)
// pack with ... operator: 57 ms - 1.3 MB
// string concatentation: 197 ms - 1.3 MB
// call_user_func_array: 249 ms - 1.5 MB
// multiple pack: 298 ms - 1.3 MB
// array_reduce: 39114 ms - 1.3 MB
Operator ...
used directly with a function pack
, if actually the fastest solution ( accepted answer )
If ...
not available (PHP version prior to 5.6), the solution suggested by this answer ( ) is the fastest. string concatentation
Memory usage is almost the same for every case.
I am posting the test code if anyone is interested.
<?php
// Return elapsed time from epoch time in milliseconds
function milliseconds() {
$mt = explode(' ', microtime());
return ((int)$mt[1]) * 1000 + ((int)round($mt[0] * 1000));
}
// Which test to run [1..5]
$test = $argv[ 1 ];
// Test 1024x1024 sized array
$arr = array();
for( $i = 0; $i < 1024 * 1024; $i++ )
{
$arr[] = rand( 0, 255 );
}
// Initial memory usage and time
$ms0 = milliseconds();
$mem0 = memory_get_usage( true );
// Test 1: string concatentation
if( $test == '1' )
{
$data = "";
foreach ( $arr as $byte ) {
$data .= chr( $byte );
}
$test = "string concatentation";
}
// Test 2: call_user_func_array
if( $test == '2' )
{
$data = call_user_func_array("pack", array_merge(array("c*"), $arr));
$test = "call_user_func_array";
}
// Test 3: pack with ... operator
if( $test == '3' )
{
$data = pack("c*", ...$arr);
$test = "pack with ... operator";
}
// Test 4: array_reduce
if( $test == '4' )
{
$data = array_reduce($arr, function($carry, $item) { return $carry .= pack('c', $item); });
$test = "array_reduce";
}
// Test 5: Multiple pack
if( $test == '5' )
{
$data = "";
foreach ($arr as $item) $data .= pack("c", $item);
$test = "multiple pack";
}
// Output result
$ms = milliseconds() - $ms0;
$mem = round( ( memory_get_usage( true ) - $mem0 ) / ( 1024 * 1024 ), 1 );
echo "$test: $ms ms; $mem MB\n";
source to share