Multiplay Labs

tech hits and tips from Multiplay

Archive for October, 2010

PHP class_exists fails to return for missing dependent classes

without comments

If you call class_exists( $sClass ) which in turn triggers an autoloader that fails to load a dependent class of $sClass e.g. where $sClass extends a missing class, then class_exists fails silently and never returns.

Note this only fails on missing dependent classes if the original class fails to load then class_exists returns false as expected.

The following code demonstrates the bug:
AutoLoader.php:

<?php
class AutoLoader
{
    // Public Methods
    public static function factory( $sClass )
    {
        error_log( "factory: $sClass" );
        if ( class_exists( $sClass ) )
        {
            error_log( "Class Exists" );
            return new $sClass;
        }
        error_log( "Failed to autoload: $sClass" );
        return null;
    }
    public static function autoload( $sClass )
    {
        // NOTE: its unclear if the return codes of autoload functions are ever used?
        error_log( "autoload: $sClass" );
        if ( class_exists( $sClass, false ) )
        {
            error_log( "Already loaded" );
            return true;
        }
        error_log( "including: $sClass" );
        $iRet = @include_once( "$sClass.php" );
        error_log( "included: $iRet for $sClass, " . class_exists( $sClass, false ) );
 
        return class_exists( $sClass, false );
    }
}
 
// Register our Autoloader
spl_autoload_register( array( 'AutoLoader', 'autoload' ) );
 
$oMyClass = AutoLoader::factory( 'MyClass' );
if ( $oMyClass )
{
    error_log( "Got object:" . get_class( $oMyClass ) );
}
else
{
    error_log( "Failed to load 'MyClass'" );
}
?>

MyClass.php

<?php class MyClass extends MyBaseClass {} ?>

This bug is registered here: http://bugs.php.net/bug.php?id=53036

Written by Dilbert

October 10th, 2010 at 4:22 pm

Posted in Code,PHP

PayPal PHP SOAP Interface Memory Leak Fix

without comments

Due to the fact that the PayPal SOAP interface relies heavily on exceptions within pear its call to backtrace quickly causes a significant memory leak resulting in long running processes exiting with something like:
PHP Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to allocate 13 bytes) in /usr/local/share/pear/PEAR.php on line 884

Fortunately the fix is quite simple:-

--- /usr/local/share/pear.old/PEAR.php.orig     Tue Feb  2 18:47:42 2010
+++ /usr/local/share/pear.old/PEAR.php  Tue Feb  2 23:24:45 2010
@@ -860,7 +862,7 @@
         $this->userinfo  = $userinfo;
         if (function_exists("debug_backtrace")) {
             if (@!PEAR::getStaticProperty('PEAR_Error', 'skiptrace')) {
-                $this->backtrace = debug_backtrace();
+                $this->backtrace = debug_backtrace( false ); // Don't populate the object reference as this causes circular reference and hence memory leak
             }
         }
         if ($mode & PEAR_ERROR_CALLBACK) {

Written by Dilbert

October 4th, 2010 at 4:24 pm

Posted in Code,PHP