Streaming JSON to browser using PHP for large record sets of repeating rows
<? | |
// COPYRIGHT 2015 by TIM GALLGHER (treehousetim@gmail.com) | |
// http://treehousetim.com/2015/04/08/new-json-streaming-output-code/ | |
//The MIT License (MIT) | |
// Permission is hereby granted, free of charge, to any person obtaining a copy | |
// of this software and associated documentation files (the "Software"), to deal | |
// in the Software without restriction, including without limitation the rights | |
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
// copies of the Software, and to permit persons to whom the Software is | |
// furnished to do so, subject to the following conditions: | |
// The above copyright notice and this permission notice shall be included in | |
// all copies or substantial portions of the Software. | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
// THE SOFTWARE. | |
// Example: | |
// create your response | |
// this can be as complex as you want, but it must have single array of records to be effective. | |
// it sends in 3 stages. | |
// stage 1: send the entire response object except for the rows | |
// stage 2: send the rows, streaming one row at a time | |
// stage 3: send the closing JSON syntax | |
// if you bundle all your data into a single row, this approach will not yield any benefit | |
// the primary point of this file | |
include( 'streamBrowserJSON.php' ); | |
$response = new stdClass(); | |
$response->success = true; | |
$response->rows = array(); | |
// start example | |
outStreamJSON::getInstance() | |
->createTemplate( $response, 'rows' ) | |
// adds ")]}',\n" by default, or pass in a value of your own | |
->csrfHeader() | |
// only one line handler is used at a time. | |
// pass a standard callback that can be used with call_user_func | |
->addLineHandler( 'createRow' ) | |
// the syntax if you're using | |
//->addLineHandler( array( $this, 'getRow' ) ) | |
->addOutput() | |
->render(); | |
// an example row handling function | |
function createRow() | |
{ | |
static $cnt = 0; | |
if( $cnt++ > 10000 ) | |
{ | |
return false; | |
} | |
// create a bunch of example data | |
return (object)array( 'id' => $cnt, 'random_data' => sha1( rand() ) . sha1( rand() ) . sha1( rand() ) . sha1( rand() ) . sha1( rand() ) . sha1( rand() ) . sha1( rand() ) . sha1(rand() ) . sha1(rand() ) ); | |
} |
<? | |
// COPYRIGHT 2015 by TIM GALLGHER (treehousetim@gmail.com) | |
// http://treehousetim.com/2015/04/08/new-json-streaming-output-code/ | |
//The MIT License (MIT) | |
// Permission is hereby granted, free of charge, to any person obtaining a copy | |
// of this software and associated documentation files (the "Software"), to deal | |
// in the Software without restriction, including without limitation the rights | |
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
// copies of the Software, and to permit persons to whom the Software is | |
// furnished to do so, subject to the following conditions: | |
// The above copyright notice and this permission notice shall be included in | |
// all copies or substantial portions of the Software. | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
// THE SOFTWARE. | |
// the output class - allows for outputting to mutiple streams at once - i.e. file logging and browser | |
class outStreamJSONOutput | |
{ | |
public $stream; | |
protected $filename; | |
public function __construct( $filename = null ) | |
{ | |
if ( $filename == null ) | |
{ | |
$filename = 'php://output'; | |
} | |
$this->stream = new SplFileObject( $filename, 'w' ); | |
$this->filename = $filename; | |
} | |
//------------------------------------------------------------------------------ | |
public function out( $out ) | |
{ | |
$this->stream->fwrite( $out ); | |
} | |
//------------------------------------------------------------------------------ | |
public function encodeRow( $data ) | |
{ | |
static $first = true; | |
$line = ($first?'':',') . json_encode( $data ); | |
$this->out( $line ); | |
$first = false; | |
} | |
} | |
// the main class | |
class outStreamJSON | |
{ | |
protected $_templateTop; | |
protected $_templateBottom; | |
protected $_csrfHeader = ''; | |
protected $_outputs = array(); | |
protected $_lineCb; | |
public static function getInstance() | |
{ | |
return new outStreamJSON(); | |
} | |
//------------------------------------------------------------------------------ | |
public function csrfHeader( $header = ")]}',\n" ) | |
{ | |
$this->_csrfHeader = $header; | |
return $this; | |
} | |
//------------------------------------------------------------------------------ | |
public function createTemplate( $obj, $arrName ) | |
{ | |
unset( $obj->{$arrName} ); | |
$obj->{$arrName} = array(); | |
$template = json_encode( $obj ); | |
$splitter = '"' . $arrName . '":['; | |
$parts = explode( $splitter, $template ); | |
$parts[0] .= $splitter; | |
$this->_templateTop = $parts[0]; | |
$this->_templateBottom = $parts[1]; | |
return $this; | |
} | |
//------------------------------------------------------------------------ | |
public function addOutput( $filename = null ) | |
{ | |
$this->_outputs[] = new outStreamJSONOutput( $filename ); | |
return $this; | |
} | |
//------------------------------------------------------------------------------ | |
public function addLineHandler( $cb ) | |
{ | |
$this->_lineCb = $cb; | |
return $this; | |
} | |
//------------------------------------------------------------------------ | |
public function render() | |
{ | |
foreach( $this->_outputs as $output ) | |
{ | |
$output->out( $this->_csrfHeader . $this->_templateTop ); | |
} | |
while( $line = call_user_func( $this->_lineCb ) ) | |
{ | |
foreach( $this->_outputs as $output ) | |
{ | |
$output->encodeRow( $line ); | |
} | |
} | |
foreach( $this->_outputs as $output ) | |
{ | |
$output->out( $this->_templateBottom ); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment