Pregunta

Estoy construyendo una biblioteca ORM con la reutilización y la simplicidad en mente; todo va bien, excepto que me quedé atrapado por una estúpida limitación de herencia. Considere el siguiente código:

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

Obviamente, este no es el comportamiento que esperaba (aunque el comportamiento real también tiene sentido) ... Entonces mi pregunta es si ustedes saben de un medio para obtener, en la clase principal, el nombre de la clase secundaria.

¿Fue útil?

Solución

en resumen. esto no es posible. en php4 podría implementar un hack terrible (examine el debug_backtrace () ) pero ese método no funciona en PHP5. referencias:

edit : un ejemplo de enlace estático tardío en PHP 5.3 (mencionado en los comentarios). tenga en cuenta que hay problemas potenciales en su implementación actual ( 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"

Otros consejos

No necesita esperar PHP 5.3 si puede concebir una forma de hacerlo fuera de un contexto estático. En php 5.2.9, en un método no estático de la clase padre, puede hacer:

get_class($this);

y devolverá el nombre de la clase secundaria como una cadena.

es decir

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

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

$x = new Child();

esto generará:

Parent class: Parent
Child class: Child

dulce ¿eh?

Sé que esta pregunta es muy antigua, pero para aquellos que buscan una solución más práctica que definir una propiedad en cada clase que contenga el nombre de la clase:

Puede usar la palabra clave static para esto.

Como se explica en esta nota del contribuyente en la documentación de php

  

la palabra clave static se puede usar dentro de una superclase para acceder a la subclase desde la que se llama a un método.

Ejemplo :

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

Conozco su publicación anterior pero quiero compartir la solución que he encontrado.

Probado con PHP 7+ Utilice la función get_class () enlace

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

class foo extends bar {
}

new foo;
?>

Se mostrará el ejemplo anterior:

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

En caso de que no quiera usar get_called_class () puede usar otros trucos de enlace estático tardío (PHP 5.3+). Pero la desventaja en este caso necesita tener el método getClass () en cada modelo. Lo cual no es un gran problema OMI.

<?php

class Base 
{
    public static function find($id)
    {
        $table = static::

En caso de que no quiera usar get_called_class () puede usar otros trucos de enlace estático tardío (PHP 5.3+). Pero la desventaja en este caso necesita tener el método getClass () en cada modelo. Lo cual no es un gran problema OMI.

<*>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

En caso de que no quiera usar get_called_class () puede usar otros trucos de enlace estático tardío (PHP 5.3+). Pero la desventaja en este caso necesita tener el método getClass () en cada modelo. Lo cual no es un gran problema OMI.

<*>table = 'users'; public static function getClass() { return __CLASS__; } } class Image extends Base { protected static

En caso de que no quiera usar get_called_class () puede usar otros trucos de enlace estático tardío (PHP 5.3+). Pero la desventaja en este caso necesita tener el método getClass () en cada modelo. Lo cual no es un gran problema OMI.

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

Parece que podría estar intentando usar un patrón singleton como patrón de fábrica. Recomendaría evaluar sus decisiones de diseño. Si un singleton es realmente apropiado, también recomendaría usar solo métodos estáticos donde la herencia es no deseada.

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

Tal vez esto no esté respondiendo la pregunta, pero podría agregar un parámetro para obtener () especificando el tipo. entonces puedes llamar

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

en lugar de llamar a User :: get (). Puede agregar lógica en BaseModel :: get () para verificar si existe un método get en la subclase y luego llamarlo si desea permitir que la subclase lo anule.

De lo contrario, la única forma en que puedo pensar obviamente es agregando cosas a cada subclase, lo cual es estúpido:

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

Esto probablemente se rompería si luego subclasificara Usuario, pero supongo que podría reemplazar get_parent_class (__ CLASS__) con 'BaseModel' en ese caso

El problema no es una limitación de idioma, es su diseño. No importa que tengas clases; Los métodos estáticos creen un diseño de procedimiento más que orientado a objetos. También está utilizando el estado global de alguna forma. (¿Cómo get_row_from_db_as_array () sabe dónde encontrar la base de datos?) Y, finalmente, parece muy difícil realizar una prueba unitaria.

Pruebe algo en este sentido.

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

Dos variaciones en la respuesta de Preston:

1)

class Base 
{
    public static function find($id)
    {
        $table = static::

Dos variaciones en la respuesta de Preston:

1)

class Base 
{
    public static function _find($class, $id)
    {
        $table = static::

Dos variaciones en la respuesta de Preston:

1)

class Base 
{
    public static function find($id)
    {
        $table = static::

Dos variaciones en la respuesta de Preston:

1)

<*>

2)

<*>

Nota: comenzar un nombre de propiedad con _ es una convención que básicamente significa "sé que lo hice público, pero realmente debería haber sido protegido, pero no pude hacerlo y lograr mi objetivo"

table; $class = static::

Dos variaciones en la respuesta de Preston:

1)

<*>

2)

<*>

Nota: comenzar un nombre de propiedad con _ es una convención que básicamente significa "sé que lo hice público, pero realmente debería haber sido protegido, pero no pude hacerlo y lograr mi objetivo"

class; $data = array('table' => $table, 'id' => $id); return new $class($data); } } class User extends Base { public static

Dos variaciones en la respuesta de Preston:

1)

<*>

2)

<*>

Nota: comenzar un nombre de propiedad con _ es una convención que básicamente significa "sé que lo hice público, pero realmente debería haber sido protegido, pero no pude hacerlo y lograr mi objetivo"

class = 'User'; }

2)

<*>

Nota: comenzar un nombre de propiedad con _ es una convención que básicamente significa "sé que lo hice público, pero realmente debería haber sido protegido, pero no pude hacerlo y lograr mi objetivo"

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

2)

<*>

Nota: comenzar un nombre de propiedad con _ es una convención que básicamente significa "sé que lo hice público, pero realmente debería haber sido protegido, pero no pude hacerlo y lograr mi objetivo"

table; $class = static::

Dos variaciones en la respuesta de Preston:

1)

<*>

2)

<*>

Nota: comenzar un nombre de propiedad con _ es una convención que básicamente significa "sé que lo hice público, pero realmente debería haber sido protegido, pero no pude hacerlo y lograr mi objetivo"

class; $data = array('table' => $table, 'id' => $id); return new $class($data); } } class User extends Base { public static

Dos variaciones en la respuesta de Preston:

1)

<*>

2)

<*>

Nota: comenzar un nombre de propiedad con _ es una convención que básicamente significa "sé que lo hice público, pero realmente debería haber sido protegido, pero no pude hacerlo y lograr mi objetivo"

class = 'User'; }

2)

<*>

Nota: comenzar un nombre de propiedad con _ es una convención que básicamente significa "sé que lo hice público, pero realmente debería haber sido protegido, pero no pude hacerlo y lograr mi objetivo"

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top