|
<?php |
|
|
|
define('JSON_STREAM_ERROR_OOO', 1); |
|
|
|
class JsonStreamException extends Exception { |
|
private $json_state; |
|
public function __construct($message = "", $code = 0, $state = NULL, $previous = NULL) { |
|
parent::__construct($message, $code, $previous); |
|
$this->json_state = $state; |
|
} |
|
|
|
public function getJsonState() { |
|
return $this->json_state; |
|
} |
|
} |
|
|
|
class JsonStream { |
|
private $fd; |
|
private $state; |
|
|
|
public function __construct($fd) { |
|
$this->fd = $fd; |
|
$this->state = array(); |
|
} |
|
|
|
private function currentState() { |
|
return current(array_slice($this->state, -1)); |
|
} |
|
|
|
private function pushState($state) { |
|
array_push($this->state, $state); |
|
} |
|
|
|
private function popState() { |
|
return array_pop($this->state); |
|
} |
|
|
|
private function acceptsValue() { |
|
$state = $this->currentState(); |
|
return |
|
!$state || |
|
$state->type == 'array' || |
|
($state->type == 'object' && $state->at_key); |
|
} |
|
|
|
public function startArray() { |
|
$this->writeLiteral('['); |
|
$this->pushState((object)array( |
|
'type' => 'array', |
|
'has_items' => FALSE |
|
)); |
|
return $this; |
|
} |
|
|
|
public function startObject() { |
|
$this->writeLiteral('{'); |
|
$this->pushState((object)array( |
|
'type' => 'object', |
|
'at_key' => FALSE, |
|
'has_items' => FALSE, |
|
)); |
|
return $this; |
|
} |
|
|
|
public function end() { |
|
$state = $this->popState(); |
|
|
|
if (!$state || ($state->type == 'object' && $state->at_key)) { |
|
throw new JsonStreamException("Operation out of order.", JSON_STREAM_ERROR_OOO, $state); |
|
} |
|
|
|
if ($state->type == 'object') { |
|
fwrite($this->fd, '}'); |
|
} |
|
else if ($state->type == 'array') { |
|
fwrite($this->fd, ']'); |
|
} |
|
} |
|
|
|
public function writeProperty($key, $value) { |
|
$this->writeKey($key); |
|
$this->write($value); |
|
return $this; |
|
} |
|
|
|
public function writeKey($key) { |
|
$state = $this->currentState(); |
|
|
|
if ($state->type != 'object' || $state->at_key) { |
|
throw new JsonStreamException("Operation out of order.", JSON_STREAM_ERROR_OOO, $state); |
|
} |
|
|
|
if ($state->has_items) { |
|
fwrite($this->fd, ','); |
|
} |
|
|
|
$state->has_items = TRUE; |
|
$state->at_key = TRUE; |
|
|
|
fwrite($this->fd, json_encode($key)); |
|
fwrite($this->fd, ':'); |
|
return $this; |
|
} |
|
|
|
public function writeLiteral($literal) { |
|
$state = $this->currentState(); |
|
if (!$this->acceptsValue()) { |
|
throw new JsonStreamException("Operation out of order.", JSON_STREAM_ERROR_OOO, $state); |
|
} |
|
if ($state) { |
|
if ($state->type == 'object') { |
|
$state->at_key = FALSE; |
|
} |
|
else if ($state->type == 'array') { |
|
if ($state->has_items) { |
|
fwrite($this->fd, ','); |
|
} |
|
$state->has_items = TRUE; |
|
} |
|
} |
|
fwrite($this->fd, $literal); |
|
return $this; |
|
} |
|
|
|
public function write($value) { |
|
$this->writeLiteral(json_encode($value)); |
|
return $this; |
|
} |
|
|
|
public function close($newline = FALSE) { |
|
while ($this->currentState()) { |
|
$this->end(); |
|
} |
|
if ($newline) { |
|
fwrite($this->fd, "\n"); |
|
} |
|
fclose($this->fd); |
|
} |
|
} |