Intereting Posts
Вставьте один HTML-файл в другой файл HTML Бенчмаркинг Производительность node.js (кластера) с пулами mysql: Lighttpd + PHP? Автоматический вход на текущий веб-сайт, если пользователь вошел в систему на другом веб-сайте Удаление повторяющихся строк из таблицы MySql PHP: strpos & substr с UTF-8 отсоединить изображение в папке с помощью codeigniter Как мне обратиться к сообщению «Устаревшие: функция eregi () устарела …»? Создать представление или использовать innerjoins? Параметры формы фильтра перед отправкой Как вы используете PHPUnit для проверки функции, если эта функция должна убивать PHP? Чтение * .csv-файлов из каталога и отображение содержимого каждого файла Заблокированный запятой множественный автозаполнение в одном поле PHP разрешен zip mimetypes Удалить родительский каталог и все дочерние файлы, а затем перенаправить Почему этот короткий скрипт php не отправляет электронную почту?

Получение имени дочернего класса в родительском классе (статический контекст)

Я создаю библиотеку ORM с повторным использованием и простотой; все идет хорошо, за исключением того, что я застрял от глупого ограничения наследования. Пожалуйста, рассмотрите приведенный ниже код:

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

Очевидно, что это не то поведение, которое я ожидал (хотя фактическое поведение также имеет смысл). Поэтому мой вопрос в том, что вы, ребята, знаете о том, чтобы получить в родительском классе имя дочернего класса.

вкратце. это невозможно. в php4 вы можете реализовать ужасный взлом (проверьте debug_backtrace() ), но этот метод не работает в PHP5. Рекомендации:

  • 30423

  • 37684

  • 34421

edit : пример поздней статической привязки в PHP 5.3 (упоминается в комментариях). обратите внимание, что в его текущей реализации существуют потенциальные проблемы ( 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" 

Вам не нужно ждать PHP 5.3, если вы в состоянии представить себе способ сделать это за пределами статического контекста. В php 5.2.9 в нестационарном методе родительского класса вы можете:

 get_class($this); 

и он вернет имя дочернего класса в виде строки.

т.е.

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

это выведет:

 Parent class: Parent Child class: Child 

сладкий ха?

Если вы не хотите использовать get_called_class (), вы можете использовать другие трюки поздней статической привязки (PHP 5.3+). Но недостатком в этом случае вам должен быть метод getClass () в каждой модели. Что немаловажно, ИМО.

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

Похоже, вы можете использовать шаблон singleton в качестве заводского шаблона. Я бы рекомендовал оценить ваши дизайнерские решения. Если одноэлемент действительно подходит, я бы также рекомендовал использовать только статические методы, где наследование нежелательно.

 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 

Возможно, это не отвечает на вопрос, но вы можете добавить параметр, чтобы get () указывал тип. то вы можете позвонить

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

вместо вызова User :: get (). Вы можете добавить логику в BaseModel :: get (), чтобы проверить, существует ли метод get в подклассе, а затем вызывать это, если вы хотите разрешить подкласс переопределять его.

В противном случае единственный способ, о котором я могу думать, – это добавить материал в каждый подкласс, что глупо:

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

Это, вероятно, сломается, если вы затем подклассифицируете пользователя, но я полагаю, вы могли бы заменить get_parent_class(__CLASS__) на 'BaseModel' в этом случае

Проблема не в языковых ограничениях, это ваш дизайн. Неважно, что у вас есть занятия; статические методы опровергают процедурный, а не объектно-ориентированный дизайн. Вы также используете глобальное состояние в той или иной форме. (Как get_row_from_db_as_array() знает, где найти базу данных?) И, наконец, очень сложно выполнить модульный тест.

Попробуйте что-нибудь в этом направлении.

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

Два варианта ответа Престона:

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

Примечание: запуск имени свойства с помощью _ – это соглашение, которое в основном означает «я знаю, что я сделал это публично, но он действительно должен был быть защищен, но я не мог этого сделать и достичь своей цели»