$top; private Vector> $stack; public function __construct(mixed $val) { $this->top = new ArrayIterator(array($val)); $this->stack = Vector {$this->top}; parent::__construct(new TNullTransport()); } public function readMessageBegin(inout $name, inout $type, inout $seqid) { throw new TProtocolException('Not Supported'); } public function readMessageEnd() { throw new TProtocolException('Not Supported'); } public function readStructBegin(inout $name) { $name = null; $val = $this->top->current(); $this->top->next(); $itr = null; if ($val instanceof Set || $val instanceof ImmSet) { throw new TProtocolException( 'Unsupported data structure for struct: '.gettype($val), ); } else if ($val instanceof Vector) { if ($val->isEmpty()) { // Empty maps can be (de)serialized incorrectly as vectors $size = 0; $itr = $val->getIterator(); } else { throw new TProtocolException( 'Unsupported data structure for struct: '.gettype($val), ); } } else if ($val instanceof ImmVector) { if ($val->isEmpty()) { // Empty maps can be (de)serialized incorrectly as vectors $size = 0; $itr = $val->getIterator(); } else { throw new TProtocolException( 'Unsupported data structure for struct: '.gettype($val), ); } } else if ($val instanceof Map) { $itr = $val->getIterator(); } else if ($val instanceof ImmMap) { $itr = $val->getIterator(); } else if (is_array($val)) { $itr = new ArrayIterator($val); } else { $val = (array) $val; $itr = new ArrayIterator($val); } $itr = new TSimplePHPObjectProtocolKeyedIteratorWrapper($itr); $this->stack->add($itr); $this->top = $itr; } public function readStructEnd() { $this->stack->pop(); $val = $this->stack->lastValue(); invariant($val !== null, "Stack is empty, this shouldn't happen!"); $this->top = $val; } public function readFieldBegin( inout $name, inout $fieldType, inout $fieldId, ) { $val = null; while ($val === null) { if (!$this->top->valid()) { $fieldType = TType::STOP; return; } $fieldId = null; $name = $this->top->current(); $this->top->next(); $val = $this->top->current(); if ($val === null) { $this->top->next(); continue; } $fieldType = $this->guessTypeForValue($val); } } public function readFieldEnd() {} public function readMapBegin(inout $keyType, inout $valType, inout $size) { $val = $this->top->current(); $this->top->next(); $itr = null; if ($val instanceof Set || $val instanceof ImmSet) { throw new TProtocolException( 'Unsupported data structure for map: '.gettype($val), ); } else if ($val instanceof Vector) { if ($val->isEmpty()) { // Empty maps can be (de)serialized incorrectly as vectors $size = 0; $itr = $val->getIterator(); } else { throw new TProtocolException( 'Unsupported data structure for map: '.gettype($val), ); } } else if ($val instanceof ImmVector) { if ($val->isEmpty()) { // Empty maps can be (de)serialized incorrectly as vectors $size = 0; $itr = $val->getIterator(); } else { throw new TProtocolException( 'Unsupported data structure for map: '.gettype($val), ); } } else if ($val instanceof Map) { $itr = $val->getIterator(); $size = $val->count(); } else if ($val instanceof ImmMap) { $itr = $val->getIterator(); $size = $val->count(); } else if (is_array($val)) { $itr = new ArrayIterator($val); $size = count($val); } else { $val = (array) $val; $itr = new ArrayIterator($val); $size = count($val); } $itr = new TSimplePHPObjectProtocolKeyedIteratorWrapper($itr); $this->stack->add($itr); $this->top = $itr; if ($itr->valid()) { $key_sample = $itr->current(); $itr->next(); $val_sample = $itr->current(); $itr->rewind(); $keyType = $this->guessTypeForValue($key_sample); $valType = $this->guessTypeForValue($val_sample); } } public function readMapEnd() { $this->stack->pop(); $val = $this->stack->lastValue(); invariant($val !== null, "Stack is empty, this shouldn't happen!"); $this->top = $val; } public function readListBegin(&$elemType, &$size) { $val = $this->top->current(); $this->top->next(); $itr = null; if ($val instanceof Set || $val instanceof ImmSet) { throw new TProtocolException( 'Unsupported data structure for list: '.gettype($val), ); } else if ($val instanceof Map) { $itr = $val->getIterator(); $size = $val->count(); } else if ($val instanceof ImmMap) { $itr = $val->getIterator(); $size = $val->count(); } else if ($val instanceof Vector) { $itr = $val->getIterator(); $size = $val->count(); } else if ($val instanceof ImmVector) { $itr = $val->getIterator(); $size = $val->count(); } else if (is_array($val)) { $itr = new ArrayIterator($val); $size = count($val); } else { $val = (array) $val; $itr = new ArrayIterator($val); $size = count($val); } $this->stack->add($itr); $this->top = $itr; if ($itr->valid()) { $val_sample = $itr->current(); $elemType = $this->guessTypeForValue($val_sample); } } public function readListEnd() { $this->stack->pop(); $val = $this->stack->lastValue(); invariant($val !== null, "Stack is empty, this shouldn't happen!"); $this->top = $val; } public function readSetBegin(&$elemType, &$size) { $val = $this->top->current(); $this->top->next(); $itr = null; if ($val instanceof Map) { $itr = $val->getIterator(); $size = $val->count(); } else if ($val instanceof ImmMap) { $itr = $val->getIterator(); $size = $val->count(); } else if ($val instanceof Vector) { $itr = $val->getIterator(); $size = $val->count(); } else if ($val instanceof ImmVector) { $itr = $val->getIterator(); $size = $val->count(); } else if ($val instanceof Set) { $itr = $val->getIterator(); $size = $val->count(); } else if ($val instanceof ImmSet) { $itr = $val->getIterator(); $size = $val->count(); } else if (is_array($val)) { $itr = new ArrayIterator($val); $size = count($val); } else { $val = array_keys((array) $val); $itr = new ArrayIterator($val); $size = count($val); } $this->stack->add($itr); $this->top = $itr; if ($itr->valid()) { $val_sample = $itr->current(); $elemType = $this->guessTypeForValue($val_sample); } } public function readSetEnd() { $this->stack->pop(); $val = $this->stack->lastValue(); invariant($val !== null, "Stack is empty, this shouldn't happen!"); $this->top = $val; } public function readBool(&$value) { $value = (bool) $this->top->current(); $this->top->next(); } public function readByte(&$value) { $value = (int) $this->top->current(); $this->top->next(); if ($value < -0x80 || $value > 0x7F) { throw new TProtocolException('Value is outside of valid range'); } } public function readI16(&$value) { $value = (int) $this->top->current(); $this->top->next(); if ($value < -0x8000 || $value > 0x7FFF) { throw new TProtocolException('Value is outside of valid range'); } } public function readI32(&$value) { $value = (int) $this->top->current(); $this->top->next(); if ($value < -0x80000000 || $value > 0x7FFFFFFF) { throw new TProtocolException('Value is outside of valid range'); } } public function readI64(&$value) { $value = (int) $this->top->current(); $this->top->next(); if ($value < (-0x80000000 << 32) || $value > (0x7FFFFFFF << 32 | 0xFFFFFFFF)) { throw new TProtocolException('Value is outside of valid range'); } } public function readDouble(&$value) { $value = (float) $this->top->current(); $this->top->next(); } public function readFloat(&$value) { $value = (float) $this->top->current(); $this->top->next(); } public function readString(&$value) { $value = (string) $this->top->current(); $this->top->next(); } public function writeMessageBegin($name, $type, $seqid) { throw new TProtocolException('Not Implemented'); } public function writeMessageEnd() { throw new TProtocolException('Not Implemented'); } public function writeStructBegin($name) { throw new TProtocolException('Not Implemented'); } public function writeStructEnd() { throw new TProtocolException('Not Implemented'); } public function writeFieldBegin($fieldName, $fieldType, $fieldId) { throw new TProtocolException('Not Implemented'); } public function writeFieldEnd() { throw new TProtocolException('Not Implemented'); } public function writeFieldStop() { throw new TProtocolException('Not Implemented'); } public function writeMapBegin($keyType, $valType, $size) { throw new TProtocolException('Not Implemented'); } public function writeMapEnd() { throw new TProtocolException('Not Implemented'); } public function writeListBegin($elemType, $size) { throw new TProtocolException('Not Implemented'); } public function writeListEnd() { throw new TProtocolException('Not Implemented'); } public function writeSetBegin($elemType, $size) { throw new TProtocolException('Not Implemented'); } public function writeSetEnd() { throw new TProtocolException('Not Implemented'); } public function writeBool($value) { throw new TProtocolException('Not Implemented'); } public function writeByte($value) { throw new TProtocolException('Not Implemented'); } public function writeI16($value) { throw new TProtocolException('Not Implemented'); } public function writeI32($value) { throw new TProtocolException('Not Implemented'); } public function writeI64($value) { throw new TProtocolException('Not Implemented'); } public function writeDouble($value) { throw new TProtocolException('Not Implemented'); } public function writeFloat($value) { throw new TProtocolException('Not Implemented'); } public function writeString($value) { throw new TProtocolException('Not Implemented'); } private function guessTypeForValue(mixed $val): int { if (is_bool($val)) { return TType::BOOL; } else if (is_int($val)) { return TType::I64; } else if (is_float($val)) { return TType::DOUBLE; } else if (is_string($val)) { return TType::STRING; } else if (is_object($val) || is_array($val) || $val instanceof Map) { return TType::STRUCT; } else if ($val instanceof Iterable) { return TType::LST; } throw new TProtocolException( 'Unable to guess thrift type for '.gettype($val), ); } }