PHP Exceptions and Namespaces

I’m way too late to the PHP namespace conversation. Initially I just didn’t see the benefit of namespaces, but now I’ve seen the light.  The other issue was the production environment at work didn’t support  namespaces – I own that problem too.  Basically for way too long it was a big fail fest.

But now angel choirs are singing and I’ve seen the light.  I preface this post with these details in case you already know what I’m about to write about.

I just spent 20 minutes trying to figure out why the same pattern of code worked in one place but not another.  I’m trying to validate date strings in form submissions and had some code like this:

function validateDate()
{
	try
	{
		$dt = new \DateTime( $fieldValue );
	}
	catch( Exception $e )
	{
		return false;
	}

	return true;
}

The error I was seeing was an uncaught exception for an invalid date format.  That’s exactly what I was trying to prevent!

It took me way too long to realize that I needed to namespace the Exception class.

The solution is either to

use Exception;

at the top of my class file or to rewrite the function.

function validateDate()
{
	try
	{
		$dt = new \DateTime( $fieldValue );
	}
	catch( \Exception $e )
	{
		return false;
	}

	return true;
}

 

TLDR: Make sure you namespace the class names in your catch blocks otherwise it’s ignored and the exception is not caught.

JSON Streaming Output

I recently put together a simple little bit of code to handle streaming large amounts of JSON formatted data to the browser.

In order for you to see any benefit from this, you will need to have the following scenario.

You have a response you’re sending back with some meta information about the response – maybe a response code, or error messages or a single record.

You also have a number of rows to send back as a member of the main JSON object.

These rows don’t all need to be the same object, but you can only have a single variable that you stream.

This allows you to fetch and send a single database row at a time instead of building up a huge response object in memory on the server.

It builds up a response that looks like this (the data in “rows” is streamed one line at a time):

{
    "success":true,
    "rows":
    [
        {"id":1,"random_data":"a6b1e15cc6523e87cb8d5a6a0ea39dbc61842b23"},
        {"id":2,"random_data":"12ae19125e2182294433e835ee8dc7e403154fdd"}
    ]
}


<?
// COPYRIGHT 2015 by TIM GALLGHER (treehousetim@gmail.com)
// https://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() ) );
}

view raw

example.php

hosted with ❤ by GitHub


<?
// COPYRIGHT 2015 by TIM GALLGHER (treehousetim@gmail.com)
// https://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 );
}
}
}

Zend Framework – ZendMail timeout in validate hostname on a mac

I develop on a Mac. I love my Mac. I use Mamp Pro for my *amp stack. After beating my head against the wall for a couple of hours, including several trips to GoogleLand, I finally found a stackoverflow article about iconv_strlen causing execution timeouts.

I am now upgraded to Mamp Pro 2.x.

Happy news: it upgraded my mysql without losing any data – in fact I didn’t need to do anything.

Concerning news: it warned me that my “templates” had changed. I had changed my php.ini files. That might prove to be a pain down the road – we’ll see. I did take a backup of them.

COMET Update – Iframes block AJAX

AJAX and COMET do not peacefully co-exist.

You’ve been warned.

While the COMET iframe is loading, no ajax calls will complete – the ajax calls are blocked by the loading iframe.

Workarounds:

  1. Remove ajax from the page
  2. have the code that initiates ajax calls suspend the comet and re-initialize it after the ajax is done
  3. Change to web sockets – that’s where I’m heading.

Fantastic Friday Function – Alternate with Namespace

Here’s a freebie that can be used immediately without any supporting libraries. This will be available in the near future in my opensource php framework: tgsf It’s released under the GPLv3.

Use it like this:

<?php

for ( $ix = 0; $ix < 10; $ix++ )
{
	echo alternateNs( 'one', '1','2' ) . PHP_EOL;
	echo alternateNs( 'two', 'a','b' ) . PHP_EOL;
}

And here is the function:

<?php

/**
* Alternate with a namespace
* The first argument is a namespace to enable multiple calls in a single loop
* Call with a set of arguments and this function will return them based on a modulus of the quantity of invocations
* in other words, each time you call this function it will return the next item in the list of parameters.
* to reset, call with a namespace and different arguments
*/
function alternateNs()
{
	$args = func_get_args();
	$ns = array_shift( $args );
	$argCnt = count($args);

	if ( empty( $ns ) )
	{
		throw new Exception( 'alternateNs called without a namespace' );
	}

	static $cache = array();

	if ( empty( $cache[$ns] ) )
	{
		$cache[$ns]['current'] = 0;
		$cache[$ns]['items'] = array();
	}

	$items =& $cache[$ns]['items'];
	$current =& $cache[$ns]['current'];

	if ( ! ( $items === $args ) || $argCnt == 0 )
	{
		$current = 0;
		$items = $args;
	}
	else
	{
		$current++;
	}

	return $argCnt>0?$args[$current % $argCnt]:'';
}

PHP based COMET Using iframes

So I’m doing some research into, and will probably be using COMET for a project I’m working on. Yes, I know that the Apache-PHP combination is not the best for COMET, however this will be on an administrative dashboard with one or two simultaneous users at most.  I’m no COMET expert – this is my first foray into the technology.  If I learn amazing things while doing a live implementation I’ll write more articles.

While searching I ran across several tutorials and some code, but nothing that was a simple 2 file “Here’s how it’s done” example.

The basic premise (this is not new stuff – I ran across stuff from the mid 2000’s while searching) is that you have an iframe that loads a long-running PHP script that is generating <script> tags.  The JavaScript in these tags must reference the parent document.  In my test code I’m referencing a function defined in the parent document.

Some takeaways:

  1. If the script that’s referenced by your iframe doesn’t output enough data before it starts outputting <script> tags, you’ll see a delay until some sort of buffer is filled up.  I’m blaming Firefox, but I’m not certain as I didn’t investigate further.  You’ll notice a bunch of white space in my sample code – that’s a whole ton of spaces to stop the delay I was seeing in Firefox.
  2. On localhost this is very fast.  I had to introduce a usleep into the code so that it wouldn’t be so fast as to be unusable.  I haven’t tested against a remote web server, but I can assure you this will be dramatically faster than ajax polling.
  3. Firefox’s javascript engine seems to process the <script> tags being returned in the iframe faster than the DOM is able to update. I view this as good news.
  4. You must output the opening and closing script tags each time you want to update the browser.  Leaving them out will cause the browser to wait until the request is loaded – the opposite of what we’re trying to accomplish.

The example code is not an infinite loop – it’s a long running script (just a very long for loop).  In practice I’ll be using other factors to determine when to break out of an output loop.  The project requirements call for this to be running for hours so we’ll find out if that tests the limits of what this approach can handle.

Download the code

Double Reference Variable Assignments in PHP

I just got through tracking down a very pernicious bug.

The problem was that an array assigned to an object was retaining its original link after cloning.  The original array was created with its values being assigned by reference.  This wouldn’t have been a problem in and of itself, but additionally there was a second array being created with a reference to the same variable.  This was both unnecessary and certain death to my script.

The takeaway from this is to avoid unnecessary variable assignment by reference. The original code was written over two years ago.

If I recall correctly my justification for using references was to save memory.

This is wrong.

Don’t think about references in a context of memory consumption (especially since PHP allocates on write), think about references in a context of “How do I need to treat this data?”

Here is the full script that is broken. Notice in the foreach two referential assignments.

$in = array( 'a' => '123', 'b' => '456' );
$vars = array();

foreach( $in as $key => $val )
{
	$_GET[$key] =& $val;
	$vars[] =& $val;
	unset( $val );
}

class test
{
	protected $_data;

	public function set( $data )
	{
		$this->_data = $data;
	}

	public function setVar( $key, $val )
	{
		$this->_data[$key] = $val;
	}
}

header( 'content-type: text/plain' );

echo 'Should be 123 and 456' . PHP_EOL;
var_dump( $_GET );

$test = new test();
$test->set( $_GET );

echo 'Should be 123 and 456' . PHP_EOL;
var_dump( $_GET );

$second = clone $test;
$second->setVar( 'a', 'abc' );

echo 'Should be 123 and 456' . PHP_EOL;
var_dump( $_GET );

Output

Should be 123 and 456
array(2) {
  ["a"]=>
  &string(3) "123"
  ["b"]=>
  &string(3) "456"
}
Should be 123 and 456
array(2) {
  ["a"]=>
  &string(3) "123"
  ["b"]=>
  &string(3) "456"
}
Should be 123 and 456
array(2) {
  ["a"]=>
  &string(3) "abc"
  ["b"]=>
  &string(3) "456"
}

Corrected Script

$in = array( 'a' => '123', 'b' => '456' );

foreach( $in as $key => $val )
{
	$_GET[$key] = $val;
}

class test
{
	protected $_data;

	public function set( $data )
	{
		$this->_data = $data;
	}

	public function setVar( $key, $val )
	{
		$this->_data[$key] = $val;
	}
}

header( 'content-type: text/plain' );

echo 'Should be 123 and 456' . PHP_EOL;
var_dump( $_GET );

$test = new test();
$test->set( $_GET );

echo 'Should be 123 and 456' . PHP_EOL;
var_dump( $_GET );

$second = clone $test;
$second->setVar( 'a', 'abc' );

echo 'Should be 123 and 456' . PHP_EOL;
var_dump( $_GET );

Correct Output

Should be 123 and 456
array(2) {
  ["a"]=>
  string(3) "123"
  ["b"]=>
  string(3) "456"
}
Should be 123 and 456
array(2) {
  ["a"]=>
  string(3) "123"
  ["b"]=>
  string(3) "456"
}
Should be 123 and 456
array(2) {
  ["a"]=>
  string(3) "123"
  ["b"]=>
  string(3) "456"
}

Mixing MySQL InnoDB and MyISAM tables with transactions

Just learned this from personal experience.  Hoping to save someone else some pain.

I tried the following searches:

mysql transaction partial rollback
does fetching commit an innodb transaction
innodb rollback not working

The symptoms I was experiencing included inserted records were rolled back, but an update to a table was not. My PHP code threw an exception inside a transaction, and the catch did a rollback, but the update still went through.

The problem was that the table I was updating happened to be MyISAM. MySQL silently ignored the fact that I included a table in my transaction that couldn’t be rolled back. When the rollback occurred, this was also silently ignored.

Moral of the story: use all InnoDB if you’re going to be doing transactions.

Making Sense of Variable Scope in PHP 5 OOP

I’ve learned a lot about PHP5 OOP visibility recently.  I’m posting a test script that will share some of my discoveries.

Take away:

  1. Private really does mean private.
  2. Visibility keywords are honored while cloning
  3. PHP does not enforce the visibility keywords on variables using a fatal error.
    It uses an E_NOTICE : Notice: Undefined property
define( 'SCRIPT_EOL', '<br>' );

abstract class a_base_class
{
    private $name = 'Johannes Kepler';
    protected $color = 'Green';

    public function __set($name, $value)
    {
        throw new Exception( 'a_base_class - Undefined or private property: ' . $name );
    }

    public function __clone()
    {
        // this does work - $this->name is private but is accessible in this class
        $this->name = 'Isaac Newton';
    }

    public function getName()
    {
        return $this->name;
    }

    public function echoName()
    {
        echo $this->name;
    }
}

class exampleClass1 extends a_base_class
{
    function __clone()
    {
        // this does not work - $this->name is private to ac and can't be modified here
        $this->name = 'Isaac Newton';
    }

    function echoName()
    {
        // this will issue an E_NOTICE because the variable is undefined in this class (private variables do not carry through to descendants)
        echo $this->name;
    }

    function echoColor()
    {
        echo $this->color;
    }
}

class exampleClass2 extends a_base_class
{
    public function __clone()
    {
        $this->color = 'Red';
        // without this, $this->name will not get changed to Isaac Newton
        parent::__clone();
    }

    public function echoColor()
    {
        echo $this->color;
    }
}

echo '</pre>

<hr />

<pre>
';
echo '</pre>
<h2>exampleClass1</h2>
<pre>
';
$instance = new exampleClass1();
echo 'calling echoName()' . SCRIPT_EOL;
$instance->echoName();
echo 'echo $instance->name' . SCRIPT_EOL;
echo $instance->name . SCRIPT_EOL;
echo SCRIPT_EOL;
echo 'echoing getName()' . SCRIPT_EOL;
echo $instance->getName(); // calling public method on base class
echo SCRIPT_EOL;
echo 'calling echoColor()' . SCRIPT_EOL;
$instance->echoColor();
echo SCRIPT_EOL;

try
{
    $newInstance = clone $instance;
}
catch( Exception $e )
{
    echo SCRIPT_EOL . '------------------------------------------------' . SCRIPT_EOL;
    echo "Exception Thrown: " . $e->getMessage();
    echo SCRIPT_EOL;
echo '
';
    echo $e->getTraceAsString();
    echo '

';
    echo SCRIPT_EOL . '------------------------------------------------' . SCRIPT_EOL;
}
echo SCRIPT_EOL;
echo 'var_dump' . SCRIPT_EOL;
var_dump( $newInstance );

echo '</pre>

<hr />

<pre>
';
echo '</pre>
<h2>exampleClass2</h2>
<pre>
';

$instance = new exampleClass2();
echo 'calling echoName()' . SCRIPT_EOL;
$instance->echoName();
echo SCRIPT_EOL;
echo 'echo $instance->name' . SCRIPT_EOL;
echo $instance->name . SCRIPT_EOL;
echo 'echoing getName()' . SCRIPT_EOL;
echo $instance->getName(); // calling public method on base class
echo SCRIPT_EOL;
echo 'calling echoColor()' . SCRIPT_EOL;
$instance->echoColor();
echo SCRIPT_EOL;

try
{
    $newInstance = clone $instance;
}
catch( Exception $e )
{
    echo SCRIPT_EOL . '------------------------------------------------' . SCRIPT_EOL;
    echo "Exception Thrown: " . $e->getMessage();
    echo SCRIPT_EOL;
    echo $e->getTraceAsString();
    echo SCRIPT_EOL . '------------------------------------------------' . SCRIPT_EOL;
}

echo '
';
var_dump( $newInstance );
echo '

';

Output


exampleClass1

calling echoName()

Notice: Undefined property: exampleClass1::$name in /Users/timgallagher/Desktop/xampp/se/php5oop.php on line 43
echo $instance->name

Notice: Undefined property: exampleClass1::$name in /Users/timgallagher/Desktop/xampp/se/php5oop.php on line 73

echoing getName()
Johannes Kepler
calling echoColor()
Green

————————————————
Exception Thrown: a_base_class – Undefined or private property: name

#0 /Users/timgallagher/Desktop/xampp/se/php5oop.php(37): a_base_class->__set('name', 'Isaac Newton')
#1 /Users/timgallagher/Desktop/xampp/se/php5oop.php(84): exampleClass1->__clone()
#2 {main}

————————————————

var_dump

Notice: Undefined variable: newInstance in /Users/timgallagher/Desktop/xampp/se/php5oop.php on line 98
NULL


exampleClass2

calling echoName()
Johannes Kepler
echo $instance->name

Notice: Undefined property: exampleClass2::$name in /Users/timgallagher/Desktop/xampp/se/php5oop.php on line 108

echoing getName()
Johannes Kepler
calling echoColor()
Green

object(exampleClass2)#1 (2) {
  ["name:private"]=>
  string(12) "Isaac Newton"
  ["color:protected"]=>
  string(3) "Red"
}