Search code examples
phpmagic-methods

PHP avoid recursion in magic methods


I haven't found an exact duplicate of the question I'm posting here, so here it is:

class CBaseEntity
{
    public function __construct()
    {

    }

    public function __get($property)
    {
        $property = "m_".$property;
        echo $property;
        if(property_exists($this, $property))
        {           
            $result = $this->$property;
            return $result;
        }
        else 
        {
            throw new Exception("Property not found $property");
        }
    }
}

class CCategoryEntity extends CBaseEntity
{
    private $m_id;
    private $m_name;
    private $m_description;

    public function __construct()
    {

    }

    public static function CreateFromParams($id,$name, $description)
    {
        $instance = new CCategoryEntity();
        $instance->m_id=$id;
        $instance->m_name=$name;
        $instance->m_description=$description;
        return $instance;
    }

    public static function CreateFromArray(array $catData)
    {
        $instance = new CCategoryEntity();
        $instance->m_id = $catData["id"];
        $instance->m_name = $catData["name"];
        $instance->m_description = $catData["description"];
        return $instance;
    }
}

    $categoryEntity = CCategoryEntity::CreateFromParams(3, "EntityA", "Entity");
    echo $categoryEntity->name;

Fatal error: Uncaught exception 'Exception' with message 'Property not found m_m_name'
 Exception: Property not found m_m_name in ***\models\BaseModel.php on line 33

It appears that the name property is resolved via the get magic method, but the return in the body of this getter will again issue a call to itself instead of, somehow, intelligently stopping as the string in $property coincides with the name of the sought attribute. I could do a string compare and see if the property starts with m_, but I don't want a second call being issued already in this case.

There are other SO questions involving similar code that does not yield unnecessary recursion. Did something change in php or is this the default behaviour? What can be done to reach the goal intended by this design in an elegant way?


Solution

  • Recursion appears because you are checking only if property exists, not if it is accessible from current scope. Since it is declared private, it is not accessible from parent or child classes, only in class where it was declared, thats why __get is called second time (first was for non-existent property, now it is for inaccessible one). Try out get_object_vars to get accessible properties.

    You may want to make it protected, or define public getter methods for private properties.