AlphaNumeric::class, 'n' => AlphaNumeric::class, 's' => AlphaNumeric::class, 'an' => AlphaNumeric::class, 'as' => AlphaNumeric::class, 'ns' => AlphaNumeric::class, 'ans' => AlphaNumeric::class, 'b' => Binary::class, 'z' => AlphaNumeric::class ]; public function __construct(Protocol $protocol , $options = []) { $defaults = [ 'lengthPrefix' => null ]; $this->options = $options + $defaults; $this->protocol = $protocol; } protected function shrink(&$message, $length) { $message = substr($message, $length); } public function pack() { // 设置 TPDU // $tpdu = $this->tpdu; $tpdu = bin2hex($this->tpdu); // 设置 报文头 // $header = $this->header; $header = bin2hex($this->header); // 设置 加密信息 // $encrypted = $this->encrypted; $encrypted = bin2hex($this->encrypted); // 设置 MTI // $mti = $this->mti; $mti = bin2hex($this->mti); // Dropping bad fields foreach($this->fields as $key=>$val) { if (in_array($key, [1, 65])) { unset($this->fields[$key]); } } // 填充 位图 $bitmap = ""; $bitmapLength = 64 * (floor(max(array_keys($this->fields)) / 64) + 1); $tmpBitmap = ""; for($i=1; $i <= $bitmapLength; $i++) { if ( $i == 1 && $bitmapLength > 64 || $i == 65 && $bitmapLength > 128 || isset($this->fields[$i]) ) { $tmpBitmap .= '1'; } else { $tmpBitmap .= '0'; } if ($i % 64 == 0) { for($i=0; $i<64; $i+=4){ $bitmap .= sprintf('%01x', base_convert(substr($tmpBitmap, $i, 4), 2, 10)); } } } $this->bitmap = $bitmap; // 域排序 ksort($this->fields); // 打包8583报文 $message = ""; foreach($this->fields as $id => $data) { $fieldData = $this->protocol->getFieldData($id); $fieldMapper = $fieldData['type']; if (!isset($this->mappers[$fieldMapper])) { throw new \Exception('Unknown field mapper for "' . $fieldMapper . '" type'); } $mapper = new $this->mappers[$fieldMapper]($fieldData['length']); if ( ($mapper->getLength() > strlen($data) && $mapper->getVariableLength() === 0 ) || $mapper->getLength() < strlen($data) ) { $error = 'FIELD [' . $id . '] should have length: ' . $mapper->getLength() . ' and your message "' . $data . "' is " . strlen($data); throw new PackError($error); } $message .= $mapper->pack($data); } //加密信息中包含 报文长度 if (strlen($encrypted) != 0) { $encrypted = bin2hex(sprintf('%03d', strlen($message) / 2)) . $encrypted; } // 打包所有字段 依次为 TPDU、报文头、加密信息、报文体(MTI、位图、域值) $message = $mti . $bitmap . $message; //首位长度字段 if ($this->options['lengthPrefix'] > 0) { $message = bin2hex(sprintf('%0' . $this->options['lengthPrefix'] . 'd', strlen($message) / 2)) . $message; } $message = $tpdu . $header . $encrypted . $message; //返回打包后的信息 return $message; } public function unpack($message) { // Getting message length if we have one if ($this->options['lengthPrefix'] > 0) { $length = (int)hex2bin(substr($message, 0, (int)$this->options['lengthPrefix'] * 2)); $this->shrink($message, (int)$this->options['lengthPrefix'] * 2); if (strlen($message) != $length * 2) { throw new UnpackError('Message length is ' . strlen($message) / 2 . ' and should be ' . $length); } } // Parsing TPDU $this->setTPDU(hex2bin(substr($message, 0, 20))); $this->shrink($message, 20); // Parsing Header $this->setHeader(hex2bin(substr($message, 0, 24))); $this->shrink($message, 24); // Parsing length $this->length = hex2bin(substr($message, 0, 6)); // Parsing Encrypted $this->setEncrypted(hex2bin(substr($message, 6, 76))); $this->shrink($message, 82); // Parsing MTI $this->setMTI(hex2bin(substr($message, 0, 8))); $this->shrink($message, 8); // Parsing bitmap $bitmap = ""; for(;;) { $tmp = implode(null, array_map( function($bit) { return str_pad(base_convert($bit, 16, 2), 8, 0, STR_PAD_LEFT); }, str_split(substr($message, 0, 16), 2) ) ); $this->shrink($message, 16); $bitmap .= $tmp; if (substr($tmp, 0, 1) !== "1" || strlen($bitmap) > 128) { break; } } $this->bitmap = $bitmap; if (strlen($message) != $this->length * 2) { throw new UnpackError('Message length is ' . strlen($message) / 2 . ' and should be ' . $this->length); } // Parsing fields for($i=0; $i < strlen($bitmap); $i++) { if ($bitmap[$i] === "1") { $fieldNumber = $i + 1; if ($fieldNumber === 1 || $fieldNumber === 65) { continue; } $fieldData = $this->protocol->getFieldData($fieldNumber); $fieldMapper = $fieldData['type']; if (!isset($this->mappers[$fieldMapper])) { throw new \Exception('找不到指定的类型定义:' . $fieldMapper); } $mapper = new $this->mappers[$fieldMapper]($fieldData['length']); $unpacked = $mapper->unpack($message); $this->setField($fieldNumber, $unpacked); } } } public function getTPDU() { return $this->tpdu; } public function setTPDU($tpdu) { // if (!preg_match('/^60[0-9]{8}$/', $tpdu)) { // throw new UnpackError('TPDU字段应该是以60开头的10位数字:' . $tpdu); // } $this->tpdu = $tpdu; } public function getHeader() { return $this->header; } public function setHeader($header) { // if (!preg_match('/^[0-9]{12}$/', $header)) { // throw new UnpackError('Header字段应该是12位数字:' . $header); // } $this->header = $header; } //直接获取加密信息中包含报文长度 public function getEncrypted() { return $this->encrypted; } public function setEncrypted($encrypted) { // if (strlen($encrypted) != 38) { // throw new UnpackError('Encrypted字段应该是38位字符:' . $encrypted); // } $this->encrypted = $encrypted; } public function getMTI() { return $this->mti; } public function setMTI($mti) { // if (!preg_match('/^[0-9]{4}$/', $mti)) { // throw new UnpackError('MTI字段应该是4位数字!'); // } $this->mti = $mti; } public function set(array $fields) { $this->fields = $fields; } public function getFieldsIds() { $keys = array_keys($this->fields); sort($keys); return $keys; } public function getFields() { ksort($this->fields); return $this->fields; } public function setField($field, $value) { $this->fields[(int)$field] = $value; } public function getField($field) { return isset($this->fields[$field]) ? $this->fields[$field] : null; } public function getBitmap() { return $this->bitmap; } }