webentwicklung-frage-antwort-db.com.de

Abrufen des Namens einer untergeordneten Klasse in der übergeordneten Klasse (statischer Kontext)

Ich baue eine ORM-Bibliothek unter Berücksichtigung der Wiederverwendung und Einfachheit. alles geht gut, außer dass ich durch eine dumme Erbschaftsbeschränkung stecken geblieben bin. Bitte beachten Sie den folgenden Code:

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();

Offensichtlich ist dies nicht das Verhalten, das ich erwartet hatte (obwohl das tatsächliche Verhalten auch Sinn ergibt). Meine Frage ist also, ob Sie wissen, ob Sie in der übergeordneten Klasse den Namen der Kindklasse angeben.

79
saalaa

zusamenfassend. das ist nicht möglich. In PHP4 könnten Sie einen schrecklichen Hack implementieren (untersuchen Sie die debug_backtrace()), aber diese Methode funktioniert nicht in PHP5. Verweise:

edit: ein Beispiel für eine späte statische Bindung in PHP 5.3 (in Kommentaren erwähnt). Beachten Sie, dass bei der aktuellen Implementierung möglicherweise Probleme auftreten ( 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"
82
Owen

Sie müssen nicht auf PHP 5.3 warten, wenn Sie sich einen Weg außerhalb des statischen Kontextes vorstellen können. In php 5.2.9 können Sie in einer nicht statischen Methode der übergeordneten Klasse Folgendes tun:

get_class($this);

der Name der Kindklasse wird als String zurückgegeben.

d.h.

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

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

$x = new Child();

dies wird ausgegeben:

Parent class: Parent
Child class: Child

süß, nicht wahr?

165
Jon47

Ich weiß, dass diese Frage wirklich alt ist, aber für diejenigen, die eine praktischere Lösung suchen, als in jeder Klasse eine Eigenschaft zu definieren, die den Klassennamen enthält: 

Sie können dazu das Schlüsselwort static verwenden.

Wie in dieser Mitwirkenden-Anmerkung in der PHP-Dokumentation erläutert

das Schlüsselwort static kann innerhalb einer Oberklasse verwendet werden, um auf die Unterklasse zuzugreifen, von der eine Methode aufgerufen wird.

Beispiel:

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
10
Joas

Falls Sie get_called_class () nicht verwenden möchten, können Sie andere Tricks der späten statischen Bindung verwenden (PHP 5.3+). Der Nachteil ist jedoch, dass Sie in jedem Modell die Methode getClass () verwenden müssen. Welches ist keine große Sache 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)
5
user365784

Es scheint, dass Sie versuchen, ein Singleton-Muster als Factory-Muster zu verwenden. Ich würde empfehlen, Ihre Entwurfsentscheidungen zu bewerten. Wenn ein Singleton wirklich geeignet ist, würde ich auch nur statische Methoden empfehlen, bei denen Vererbung nicht erwünscht ist.

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
2
Rabbit

Vielleicht beantwortet dies die Frage nicht wirklich, aber Sie könnten einen Parameter hinzufügen, um den Typ anzugeben. dann kannst du anrufen

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

anstatt User :: get () aufzurufen. Sie können in BaseModel :: get () eine Logik hinzufügen, um zu prüfen, ob eine get-Methode in der Unterklasse vorhanden ist.

Ansonsten kann ich natürlich nur daran denken, jeder Unterklasse Zeug hinzuzufügen, was dumm ist:

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);

Dies würde wahrscheinlich kaputt gehen, wenn Sie dann den Benutzer subclassed, aber ich denke, Sie könnten in diesem Fall get_parent_class(__CLASS__) durch 'BaseModel' ersetzen

2
Tom Haigh

Ich kenne den alten Beitrag, möchte aber die gefundene Lösung mit anderen teilen.

Getestet mit PHP 7 + Verwenden Sie die Funktion 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;
?>

Das obige Beispiel gibt Folgendes aus:

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

Das Problem ist keine Sprachbeschränkung, es ist Ihr Design. Egal, dass Sie Unterricht haben; Die statischen Methoden glauben eher an ein prozedurales als an ein objektorientiertes Design. Sie verwenden in irgendeiner Form auch den globalen Zustand. (Woher weiß get_row_from_db_as_array(), wo die Datenbank zu finden ist?) Und zum Schluss sieht es sehr schwierig aus, den Komponententest durchzuführen.

Versuchen Sie etwas in dieser Richtung.

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

Zwei Varianten der Antwort von Preston:

1) 

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);
    }
}

Hinweis: Das Starten eines Eigenschaftennamens mit _ ist eine Konvention, die im Wesentlichen bedeutet: "Ich weiß, dass ich dies öffentlich gemacht habe, aber es hätte eigentlich geschützt werden sollen, aber das konnte ich nicht und mein Ziel erreichen."

0
lo_fye