Getting the name of a child class in the parent class (static context)

PhpInheritanceStatic Methods

Php Problem Overview


I'm building an ORM library with reuse and simplicity in mind; everything goes fine except that I got stuck by a stupid inheritance limitation. Please consider the code below:

class BaseModel {
    /*
     * Return an instance of a Model from the database.
     */
    static public function get (/* varargs */) {
        // 1. Notice we want an instance of User
        $class = get_class(parent); // value: bool(false)
        $class = get_class(self);   // value: bool(false)
        $class = get_class();       // value: string(9) "BaseModel"
        $class =  __CLASS__;        // value: string(9) "BaseModel"

        // 2. Query the database with id
        $row = get_row_from_db_as_array(func_get_args());

        // 3. Return the filled instance
        $obj = new $class();
        $obj->data = $row;
        return $obj;
    }
}

class User extends BaseModel {
    protected $table = 'users';
    protected $fields = array('id', 'name');
    protected $primary_keys = array('id');
}
class Section extends BaseModel {
    // [...]
}

$my_user = User::get(3);
$my_user->name = 'Jean';

$other_user = User::get(24);
$other_user->name = 'Paul';

$my_user->save();
$other_user->save();

$my_section = Section::get('apropos');
$my_section->delete();

Obviously, this is not the behavior I was expecting (although the actual behavior also makes sense).. So my question is if you guys know of a mean to get, in the parent class, the name of child class.

Php Solutions


Solution 1 - Php

You don't need to wait for PHP 5.3 if you're able to conceive of a way to do this outside of a static context. In php 5.2.9, in a non-static method of the parent class, you can do:

get_class($this);

and it will return the name of the child class as a string.

i.e.

class Parent() {
    function __construct() {
        echo 'Parent class: ' . get_class() . "\n" . 'Child class: ' . get_class($this);
    }
}

class Child() {
    function __construct() {
        parent::construct();
    }
}

$x = new Child();

this will output:

Parent class: Parent
Child class: Child

sweet huh?

Solution 2 - Php

in short. this is not possible. in php4 you could implement a terrible hack (examine the debug_backtrace()) but that method does not work in PHP5. references:

edit: an example of late static binding in PHP 5.3 (mentioned in comments). note there are potential problems in it's current implementation (src).

class Base {
    public static function whoAmI() {
        return get_called_class();
    }
}

class User extends Base {}

print Base::whoAmI(); // prints "Base"
print User::whoAmI(); // prints "User"

Solution 3 - Php

I know this question is really old, but for those looking for a more practical solution than defining a property in every class containing the class name:

You can use the static keyword for this.

As explained in http://php.net/manual/en/language.oop5.static.php#104823">this contributor note in the php documentation

> the static keyword can be used inside a super class to access the sub class from which a method is called.

Example:

class Base
{
	public static function init() // Initializes a new instance of the static class
	{
		return new static();
	}
	
	public static function getClass() // Get static class
	{
		return static::class;
	}
	
	public function getStaticClass() // Non-static function to get static class
	{
		return static::class;
	}
}

class Child extends Base
{
	
}

$child = Child::init();         // Initializes a new instance of the Child class

                                // Output:
var_dump($child);               // object(Child)#1 (0) {}
echo $child->getStaticClass();  // Child
echo Child::getClass();	        // Child

Solution 4 - Php

I know its old post but want to share the solution I have found.

Tested with PHP 7+ Use the function get_class()link

<?php
abstract class bar {
    public function __construct()
    {
        var_dump(get_class($this));
        var_dump(get_class());
    }
}

class foo extends bar {
}

new foo;
?>

The above example will output:

string(3) "foo"
string(3) "bar"

Solution 5 - Php

In case you don't want to use get_called_class() you can use other tricks of late static binding (PHP 5.3+). But the downside in this case you need to have getClass() method in every model. Which is not a big deal IMO.

<?php

class Base 
{
	public static function find($id)
	{
		$table = static::$_table;
		$class = static::getClass();
		// $data = find_row_data_somehow($table, $id);
		$data = array('table' => $table, 'id' => $id);
		return new $class($data);
	}

	public function __construct($data)
	{
		echo get_class($this) . ': ' . print_r($data, true) . PHP_EOL;
	}
}

class User extends Base
{
	protected static $_table = 'users';

	public static function getClass()
	{
		return __CLASS__;
	}
}

class Image extends Base
{
	protected static $_table = 'images';

	public static function getClass()
	{
		return __CLASS__;
	}
}

$user = User::find(1); // User: Array ([table] => users [id] => 1)  
$image = Image::find(5); // Image: Array ([table] => images [id] => 5)

Solution 6 - Php

It appears you might be trying to use a singleton pattern as a factory pattern. I would recommend evaluating your design decisions. If a singleton really is appropriate, I would also recommend only using static methods where inheritance is not desired.

class BaseModel
{

	public function get () {
		echo get_class($this);
	
	}

	public static function instance () {
		static $Instance;
		if ($Instance === null) {
			$Instance = new self;
		
		}
		return $Instance;
	}
}

class User
extends BaseModel
{
	public static function instance () {
		static $Instance;
		if ($Instance === null) {
			$Instance = new self;
		
		}
		return $Instance;
	}
}

class SpecialUser
extends User
{
	public static function instance () {
		static $Instance;
		if ($Instance === null) {
			$Instance = new self;
		
		}
		return $Instance;
	}
}


BaseModel::instance()->get();   // value: BaseModel
User::instance()->get();        // value: User
SpecialUser::instance()->get(); // value: SpecialUser

Solution 7 - Php

Maybe this isn't actually answering the question, but you could add a parameter to get() specifing the type. then you can call

BaseModel::get('User', 1);

instead of calling User::get(). You could add logic in BaseModel::get() to check whether a get method exists in the subclass and then call that if you want to allow the subclass to override it.

Otherwise the only way I can think of obviously is by adding stuff to each subclass, which is stupid:

class BaseModel {
    public static function get() {
        $args = func_get_args();
        $className = array_shift($args);
        
        //do stuff
        echo $className;
        print_r($args);
    }
}

class User extends BaseModel {
    public static function get() { 
        $params = func_get_args();
        array_unshift($params, __CLASS__);
        return call_user_func_array( array(get_parent_class(__CLASS__), 'get'), $params); 
    }
}


User::get(1);

This would probably break if you then subclassed User, but I suppose you could replace get_parent_class(__CLASS__) with 'BaseModel' in that case

Solution 8 - Php

The problem is not a language limitation, it is your design. Never mind that you have classes; the static methods belie a procedural rather than object-oriented design. You're also using global state in some form. (How does get_row_from_db_as_array() know where to find the database?) And finally it looks very difficult to unit test.

Try something along these lines.

$db = new DatabaseConnection('dsn to database...');
$userTable = new UserTable($db);
$user = $userTable->get(24);

Solution 9 - Php

Two variations on Preston's answer:

class Base 
{
    public static function find($id)
    {
        $table = static::$_table;
        $class = static::$_class;
        $data = array('table' => $table, 'id' => $id);
        return new $class($data);
    }
}

class User extends Base
{
    public static $_class = 'User';
}

2)

class Base 
{
    public static function _find($class, $id)
    {
        $table = static::$_table;
        $data = array('table' => $table, 'id' => $id);
        return new $class($data);
    }
}

class User extends Base
{
    public static function find($id)
    {
        return self::_find(get_class($this), $id);
    }
}

Note: starting a property name with _ is a convention that basically means "i know i made this public, but it really should have been protected, but i couldn't do that and achieve my goal"

Solution 10 - Php

you can use class_basename($this) this will print classname of the file

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionsaalaaView Question on Stackoverflow
Solution 1 - PhpJon47View Answer on Stackoverflow
Solution 2 - PhpOwenView Answer on Stackoverflow
Solution 3 - PhpJoasView Answer on Stackoverflow
Solution 4 - PhpEngr Syed Rowshan AliView Answer on Stackoverflow
Solution 5 - Phpuser365784View Answer on Stackoverflow
Solution 6 - PhpRabbitView Answer on Stackoverflow
Solution 7 - PhpTom HaighView Answer on Stackoverflow
Solution 8 - PhpPrestonView Answer on Stackoverflow
Solution 9 - Phplo_fyeView Answer on Stackoverflow
Solution 10 - Phpsyam lalView Answer on Stackoverflow