Skip to content

Instantly share code, notes, and snippets.

@treehousetim
Last active August 29, 2015 14:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save treehousetim/dd001736bf3661ec5dc9 to your computer and use it in GitHub Desktop.
Save treehousetim/dd001736bf3661ec5dc9 to your computer and use it in GitHub Desktop.
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