1. バイナリファイル用サンプル

バイナリファイル用サンプル

バイナリファイルデータを処理するためのサンプルクラス・BinaryUtil.phpを使ってのテスト。 BinaryUtil.phpでは、バイナリファイルのデータを8bit、16bit、32bitやASCII文字列で取得できます。

参考 PHP・バイナリデータ

例. 先頭16バイトが以下のようなjpegの画像ファイルからのデータ取得
$ hexdump -vC -n 16 sakura01.jpg
00000000  ff d8 ff e0 00 10 4a 46  49 46 00 01 01 01 00 48  |......JFIF.....H|

サンプルクラスのBinaryUtil.phpを使ったテストプログラム。 上記JPEGファイル"sakura01.jpg"の先頭16バイトを読み込み、データを表示しています。 BinaryUtil.phpのデフォルトは、ビッグエンディアンでデータを取得するようにしています。

<?php
require_once('BinaryUtil.php');

$util = new BinaryUtil();
$util->read('sakura01.jpg', 0, 4);

$data = $util->getByte(0);                   // 先頭の0xffを取得
printf("%d (%x)%s", $data, $data, PHP_EOL);  // 255 (ff)

$data = $util->getByte(1);                   // 先頭2番目の0xd8を取得
printf("%d (%x)%s", $data, $data, PHP_EOL);  // 216 (d8)

$data = $util->getShort(0);                  // 先頭0xffd8の2バイトを取得
printf("%d (%x)%s", $data, $data, PHP_EOL);  // 65496 (ffd8)

$data = $util->getShort(2);                  // 先頭3番目0xffe0の2バイトを取得
printf("%d (%x)%s", $data, $data, PHP_EOL);  // 65504 (ffe0)

$data = $util->getInt(0);           // 先頭0xffd8fe0の4バイトを取得
printf("%d (%x)%s", $data, $data, PHP_EOL);  // 4292411360 (ffd8ffe0)

// リトルエンディアンのテスト。先頭0xff, 0xd8, 0xff, 0xe0の4バイトをリトルエンディアンで取得
$data = $util->getInt(0, BinaryUtil::LITTLE_ENDIAN);
printf("%d (%x)%s", $data, $data, PHP_EOL);   // 3774863615 (e0ffd8ff)

// 文字列のテスト。 先頭7番目から 0x4a('J')、0x46('F')、0x49('I')、0x46 (’F')を取得
$data = $util->getString(6, 4);
printf("%s(%d)%s", $data, strlen($data), PHP_EOL);
これを実行すると以下のようになります。
$ php test_bin.php
255 (ff)
216 (d8)
65496 (ffd8)
65504 (ffe0)
4292411360 (ffd8ffe0)
3774863615 (e0ffd8ff)
JFIF(4)

  • バイナリファイル処理用クラス

  • シンプルなバイナリファイルからデータを取得するためのクラスを作成。 バイナリファイルは内部でバイト単位の配列にして、PHPのint型にしています。 メソッドは、byte(8bit)、short(16bit)、int(32bit)の取得用と、ASCII文字列の取得用のみを実装しています。 日本語文字列の取得は未対応です。

    <?php
    
    class BinaryUtil
    {
        const BIG_ENDIAN = 0;
        const LITTLE_ENDIAN = 1;
    
        /**
         * The buffer for data from a binary file.
         *
         * @var
         */
        private $buffer;
    
        private $isBigEndian = true;
    
        /**
         * Set endian
         *
         * @param $val
         */
        public function setEndian($val)
        {
            $this->isBigEndian = $this->checkBigEndian($val);
        }
    
        /**
         * Return true if the value is big endian, otherwise return false.
         *
         * @param int $endian
         * @return boolean
         */
        public function checkBigEndian($endian)
        {
            if ($endian == self::BIG_ENDIAN) {
                return true;
            } elseif ($endian == self::LITTLE_ENDIAN) {
                return false;
            } else {
                throw new Exception("Invalid endian type: $endian");
            }
        }
    
        /**
         * Read from the binary file and return string data of 8bit unsigned char.
         *
         * @param string $path
         * @param int $offset
         * @param int $length
         * @return array
         */
        public function read($path, $offset = 0, $length = null)
        {
            if (is_null($length)) {
                if (empty($offset)) {
                    $buffer = file_get_contents($path);
                } else {
                    $buffer = file_get_contents($path, false, null, $offset);
                }
            } else {
                $buffer = file_get_contents($path, false, null, $offset, $length);
            }
            if ($buffer === false) {
                throw new Exception("Can not read file: $path");
            }
            return $this->buffer = unpack('C*', $buffer);
        }
    
        /**
         * Return one PHP integer data of 8bit unsigned char
         *
         * @param int $offset
         * @return mixed
         */
        public function getByte($offset)
        {
            return $this->buffer[$offset + 1];
        }
    
        /**
         * Return one PHP integer data of 16 bit unsigned short
         *
         * @@aram int $offset
         * @param int $endian  self::BIG_ENDIAN or self::LITTLE_ENDIAN
         * @return int|null
         */
        public function getShort($offset, $endian = null)
        {
            if (!isset($this->buffer[$offset + 1])
                || !isset($this->buffer[$offset + 2])
            ) {
                return null;
            }
    
            if (is_null($endian)) {
                $isBigEndian = $this->isBigEndian;
            } else {
                $isBigEndian = $this->checkBigEndian($endian);
            }
            if ($isBigEndian) {
                $first = $this->buffer[$offset + 1];
                $second = $this->buffer[$offset + 2];
            } else {
                $first = $this->buffer[$offset + 2];
                $second = $this->buffer[$offset + 1];
            }
            return ($first << 8) + $second;
        }
    
        /**
         * Return one PHP integer data of 32 bit unsigned integer
         *
         * @param int $offset
         * @param int $endian  self::BIG_ENDIAN or self::LITTLE_ENDIAN
         * @return int|null
         */
        public function getInt($offset, $endian = null)
        {
            if (!isset($this->buffer[$offset + 1])
                || !isset($this->buffer[$offset + 2])
                || !isset($this->buffer[$offset + 3])
                || !isset($this->buffer[$offset + 4])
            ) {
                return null;
            }
    
            if (is_null($endian)) {
                $isBigEndian = $this->isBigEndian;
            } else {
                $isBigEndian = $this->checkBigEndian($endian);
            }
            if ($isBigEndian) {
                $first = $this->buffer[$offset + 1];
                $second = $this->buffer[$offset + 2];
                $third = $this->buffer[$offset + 3];
                $fourth = $this->buffer[$offset + 4];
            } else {
                $first = $this->buffer[$offset + 4];
                $second = $this->buffer[$offset + 3];
                $third = $this->buffer[$offset + 2];
                $fourth = $this->buffer[$offset + 1];
            }
            return ($first << 24) + ($second << 16) + ($third << 8) + $fourth;
        }
    
        /**
         * Return PHP string data
         *
         * @param int $offset
         * @param int $length
         * @return string
         */
        public function getString($offset, $length)
        {
            $str = '';
            for ($i = 0; $i < $length; $i++) {
                $str .= chr($this->getByte($offset + $i));
            }
            return $str;
        }
    }