I was needing to provide the hex-dump of a code, but I needed to create my own. And, for fun, I decided to do it.
function hex_dump( $value )
{
$start_time = microtime(true);
switch( gettype( $value ) )
{
case 'string':
$lines = array_map(
function( $line ){
return array_map(
function( $char ){
return str_pad( dechex( ord( $char ) ), 2, 0, STR_PAD_LEFT );
},
str_split( $line )
);
},
str_split( $value, 16 )
);
break;
case 'double':
case 'integer':
$lines = array(
array_map(
function( $digits ){
return str_pad( $digits, 2, 0, STR_PAD_LEFT );
},
str_split( dechex( $value ), 2 )
)
);
break;
case 'array':
$lines =
array_map(
function( $chunk ){
return array_map(
function( $item ){
switch( gettype( $item ) )
{
case 'double':
case 'integer':
return str_pad( dechex( $item & 255 ), 2, 0, STR_PAD_LEFT );
case 'string':
return str_pad( dechex( ord( $item ) ), 2, 0, STR_PAD_LEFT );
default:
return '--';
}
},
$chunk
);
},
array_chunk( $value, 16, false )
);
break;
default:
trigger_error( 'Invalid value type passed', E_USER_WARNING );
return false;
}
$num_length = strlen( dechex( $line_count = count( $lines ) ) ) + 1;
$header = str_repeat( ' ', $num_length = $num_length + ( $num_length % 2 ) ).
' |'.
implode(
'|',
array_map(
function( $number ){
return str_pad( strtoupper( dechex( $number ) ), 2, 0, STR_PAD_LEFT );
},
range( 0, 15 )
)
).
'| TEXT ';
echo $header, PHP_EOL;
$separator = str_repeat( '-', strlen( $header) );
foreach( $lines as $current_line => &$line )
{
$line_lenth = count( $line );
echo
$separator,
PHP_EOL,
str_pad( strtoupper( dechex( $current_line ) ), $num_length - 1, 0, STR_PAD_LEFT ),
'0 |',
strtoupper(
implode(
'|',
$line_lenth < 16
?array_pad(
array_merge(
$line,
array_fill(0, 16 - $line_lenth, ' ')
),
16,
null
)
:$line
)
),
'|',
implode(
'',
array_map(
function( $value ){
if( $value == '--' )
{
return "\xBF";
}
else
{
$value = hexdec( $value );
return $value < 32 || $value > 126 ? '.' : chr( $value );
}
},
$line
)
),
PHP_EOL;
}
$stats = array(
'lines' => $line_count,
'bytes' => $line_count ? ( $line_count * 16 ) - ( 16 - count( $lines[ $line_count - 1 ] ) ) : 0,
'time' => microtime(true) - $start_time
);
echo
str_repeat( '=', strlen( $header) ),
PHP_EOL,
str_pad( 'Lines: ' . $stats['lines'], 15, ' '),
'| ',
str_pad( 'Bytes: ' . $stats['bytes'], 16, ' '),
'| Time: ',
$stats['time'],
'ms',
PHP_EOL,
PHP_EOL;
return $stats;
}
As you can see, it is a total and complete mess, even though it is logically splitted.
Since I use a lot of chained functions, I've tried to avoid to create long lines (100+ characters). But in the process, I made this mess!
It is really easy to use. Just pass a string or a number or an array of bytes, then it will make an ascii table with the dump.
For example, to dump a number:
hex_dump(12345);
And it outputs something like this:
|00|01|02|03|04|05|06|07|08|09|0A|0B|0C|0D|0E|0F| TEXT -------------------------------------------------------------------- 00 |30|39| | | | | | | | | | | | | | |09 ==================================================================== Lines: 1 | Bytes: 2 | Time: 5.2928924560547E-5ms
To dump a string:
hex_dump('A very cool string
that spans
across
multiple lines!!!');
Which outputs:
|00|01|02|03|04|05|06|07|08|09|0A|0B|0C|0D|0E|0F| TEXT -------------------------------------------------------------------- 00 |41|20|76|65|72|79|20|63|6F|6F|6C|20|73|74|72|69|A very cool stri -------------------------------------------------------------------- 10 |6E|67|0D|0A|74|68|61|74|20|73|70|61|6E|73|0D|0A|ng..that spans.. -------------------------------------------------------------------- 20 |61|63|72|6F|73|73|0D|0A|6D|75|6C|74|69|70|6C|65|across..multiple -------------------------------------------------------------------- 30 |20|6C|69|6E|65|73|21|21|21| | | | | | | | lines!!! ==================================================================== Lines: 4 | Bytes: 57 | Time: 0.00011205673217773ms
* The newlineas are represented as \x0D\x0A
, which is \r\n
(Windows newlines).
And you can even mix both, in an array of bytes:
hex_dump(
array(
123,
's',
'v <-- only that will be dumped',
0
)
);
Which will produce:
|00|01|02|03|04|05|06|07|08|09|0A|0B|0C|0D|0E|0F| TEXT -------------------------------------------------------------------- 00 |7B|73|76|00| | | | | | | | | | | | |{sv. ==================================================================== Lines: 1 | Bytes: 4 | Time: 5.9843063354492E-5ms
Considering the huge mess, how can I improve the readability of the code? Also, is this code DRY enough?