Gotcha - Fatal error: Call to undefined method stdClass::myMethod() in /myscript.php on line

I had a bit of confusion trying to debug a relatively complex set of classes that find an object by Id. See the code example below.What confused me was an error indicating that there had been a ‘Call to undefined method’ on the ’stdClass’.  It was surprising because I was pretty sure that I was not instantiating this php’s standard class anywhere.

I searched long and hard to find out where this pesky object was being created. I used PHP’s debug_backtrace() function in various places to trace the creation of the variable until I realised…

<?php

$dog = null;

//php casts $dog to ’stdClass’
$dog->foo = bah;

//Fatal error: Call to undefined method stdClass::rollover() in…
$dog->rollover();

‘False’ was returned from the finder class. I had made the mistake of not checking that the resulting variable was an object.  When I directly assigned a property on that non-object value ($object->foo = bah;) PHP silently cast the return variable to an object of type ’stdClass’ to accommodate it. This meant that no errors occurred until I tried to invoke a method on, what turned out to be, the ’stdClass’ object. If I had not tried to invoke a method, I’d be none the wiser.

I don’t like magic. I normally take the time to write or generate boiler-plate getter and setter methods for my classes and rarely have ‘public’ class properties. If I had done so in this code, I’d have seen an error like “Fatal error: Call to a member function setProperty() on a non-object” close to the bug’s location. Instead, and only ‘by luck’, I had a “Call to undefined method stdClass” error way down the script.

This is yet another reason to avoid using magic ‘__set()’ and ‘__get()’ methods and to take the time to write hard-code where possible for clarity.

As Steve McConnel says “…favouring write-time convenience over read-time convenience is a false economy.” (Code Complete, 2nd Edition, pg 842).

<?php

//Ensure errors are displayed.
ini_set(‘display_errors’, 1);
error_reporting(E_ALL);
// *** Define Some Classes ***

/**
* @desc Represents a dog.
*/
class Dog 
    {
    public $id = null;
    public $ownerName = null;

    public function __construct( $id )
        {
        $this->id = $id;
        }
    
    public function rollover()
        {
        echo ‘Oi look at my furry tummy!’;
        }
    }

/**
* @desc Responsible for finding dogs by id.
*/
class DogRepository
    {
    /**
    * @desc finds a dog by id
    * @param string - name of the dog
    * @return Dog if found | False if not found
    */

    public function findDogById( $id )
        {
        if (( $id == ‘Tibbles’ ) || ( $id == ‘Bovril’ ))
            {
            return new Dog( $id );
            }

        return FALSE;
        }
    }

// *** Start Control Logic ***

//Create the finder object.
$dogRepository = new DogRepository();

//Returns FALSE for ‘Barney’.
$dog = $dogRepository->findDogById(‘Barney’);

//No notices or errors thrown while php casts ‘$dog’ to an object of ’stdClass’.
$dog->ownerName = ‘Mike’;

//Triggers: Fatal error: Call to undefined method stdClass::rollover() in /DogHandlerScript.php on line 65
$dog->rollover();

//If you were none the wiser, you might be wondering how on earth ’stdClass’ was created.

———–

Other reasons for this error

According to Andy Shellam from his answer on Stackoverflow:

This error could also be thrown when an attempt is made to  re-read/de-serialise a serialised class when the class definition has not been loaded yet. If that occurs PHP creates it as an “stdClass” (standard class)  and the pandemonium begins.

———–

Leave a Reply