Changeset View
Changeset View
Standalone View
Standalone View
src/channel/PhutilPHPObjectProtocolChannel.php
- This file was added.
| <?php | |||||
| /** | |||||
| * Channel that transmits PHP objects using PHP serialization. This channel | |||||
| * is binary safe. | |||||
| * | |||||
| * @task protocol Protocol Implementation | |||||
| */ | |||||
| final class PhutilPHPObjectProtocolChannel extends PhutilProtocolChannel { | |||||
| const MODE_LENGTH = 'length'; | |||||
| const MODE_OBJECT = 'object'; | |||||
| /** | |||||
| * Size of the "length" frame of the protocol in bytes. | |||||
| */ | |||||
| const SIZE_LENGTH = 4; | |||||
| private $mode = self::MODE_LENGTH; | |||||
| private $byteLengthOfNextChunk = self::SIZE_LENGTH; | |||||
| private $buf = ''; | |||||
| /* -( Protocol Implementation )-------------------------------------------- */ | |||||
| /** | |||||
| * Encode a message for transmission over the channel. The message should | |||||
| * be any serializable PHP object. The entire object will be serialized, so | |||||
| * avoid transmitting objects which connect to large graphs of other objects, | |||||
| * etc. | |||||
| * | |||||
| * This channel can transmit class instances, but the receiving end must be | |||||
| * running the same version of the code. There are no builtin safeguards | |||||
| * to protect against versioning problems in object serialization. | |||||
| * | |||||
| * Objects are transmitted as: | |||||
| * | |||||
| * <len><serialized PHP object> | |||||
| * | |||||
| * ...where <len> is a 4-byte unsigned big-endian integer. | |||||
| * | |||||
| * @task protocol | |||||
| */ | |||||
| protected function encodeMessage($message) { | |||||
| $message = serialize($message); | |||||
| $len = pack('N', strlen($message)); | |||||
| return "{$len}{$message}"; | |||||
| } | |||||
| /** | |||||
| * Decode a message received from the other end of the channel. | |||||
| * | |||||
| * @task protocol | |||||
| */ | |||||
| protected function decodeStream($data) { | |||||
| $this->buf .= $data; | |||||
| $objects = array(); | |||||
| while (strlen($this->buf) >= $this->byteLengthOfNextChunk) { | |||||
| switch ($this->mode) { | |||||
| case self::MODE_LENGTH: | |||||
| $len = substr($this->buf, 0, self::SIZE_LENGTH); | |||||
| $this->buf = substr($this->buf, self::SIZE_LENGTH); | |||||
| $this->mode = self::MODE_OBJECT; | |||||
| $this->byteLengthOfNextChunk = head(unpack('N', $len)); | |||||
| break; | |||||
| case self::MODE_OBJECT: | |||||
| $data = substr($this->buf, 0, $this->byteLengthOfNextChunk); | |||||
| $this->buf = substr($this->buf, $this->byteLengthOfNextChunk); | |||||
| $obj = @unserialize($data); | |||||
| if ($obj === false) { | |||||
| throw new Exception(pht('Failed to unserialize object: %s', $data)); | |||||
| } else { | |||||
| $objects[] = $obj; | |||||
| } | |||||
| $this->mode = self::MODE_LENGTH; | |||||
| $this->byteLengthOfNextChunk = self::SIZE_LENGTH; | |||||
| break; | |||||
| } | |||||
| } | |||||
| return $objects; | |||||
| } | |||||
| } | |||||