Эта тема на forum.dklab.ru


Александр Эсаулов: Наследование классов. Как узнать первоначальный класс с которого вызывался метод?
Вопрос такой, в коде все написал.

class A
{
function Fun()
{
print __CLASS__.'<br />'; // Выдает A
print get_class().'<br />'; // Выдает A

// Как здесь узнать, что вызывался имеено класс B ?
}
}

class B extends A
{

}

B::Fun();

WingedFox:
http://ru.php.net/reflection в зубы - и вперёд!
Только вот в 99% случаев сие не нужно. Вы уверены, что попадаете в 1% исключения из правила?
Миша Спларов:
В php 5.3 для статических методов есть get_called_class
Александр Эсаулов:
get_called_class() не на всех хостингах есть, поэтому не подойдет. В ReflectionClass я не нашел ни одного метода который мне возвратил нужное. Есть еще варианты?
WingedFox:
Александр Эсаулов
Я не понимаю, в чём проблема, просто перебираете родителей и их методы, находите нужное, радуетесь.
Александр Эсаулов:
WingedFox
Да в том то и дело, как перебрать? Примерчик.
WingedFox:
Вот, 3 нужные функции:
http://ru.php.net/manual/en/reflectionclass.getmethods.php
http://ru.php.net/manual/en/reflectionclass.getmethod.php
http://ru.php.net/manual/en/reflectionclass.getparentclass.php

Писать как ими пользоваться - лениво... =)
Александр Эсаулов:
WingedFox
Я написал код. Но ничего не получилось. Я думаю что reflection тут не поможет.

<?
class A
{
function Fun()
{
print __CLASS__.'<br />'; // Выдает A
print get_class().'<br />'; // Выдает A

$ref=new ReflectionClass(get_class());

print "<pre>\n\n";

print "getMethods:\n";
print_r($ref->getMethods());

print "\n\ngetMethod:\n";
print_r($ref->getMethod('Fun'));

print "\n\ngetParentClass:\n";
print_r($ref->getParentClass()); // $ref->getParentClass() Ничего не возвращает

print '</pre>';

// Так и не получилось узнать, что метод вызывался именно через класс B
}
}

class B extends A
{

}

B::Fun();
?>

То что выдало в браузер:

A
A

getMethods:
Array
(
[0] => ReflectionMethod Object
(
[name] => Fun
[class] => A
)

)


getMethod:
ReflectionMethod Object
(
[name] => Fun
[class] => A
)


getParentClass:

WingedFox:
Посмотрите http://ru.php.net/manual/en/reflectionclass.issubclassof.php
Если не взлетит - напишите разработчикам багрепорт.
Альтернатива - парсить скрипты вручную =)
Александр Эсаулов:
WingedFox
public bool ReflectionClass::isSubclassOf ( string $class )
Возвращает true/false. Врядли мне это поможет. Ладно, вроде нашел вариант, связвнный с функцией get_called_class(). Для совместимости есть такой код:
<?
if(!function_exists('get_called_class'))
{
function get_called_class($bt = false,$l = 1)
{
if (!$bt) $bt = debug_backtrace();

if (!isset($bt[$l])) throw new Exception("Cannot find called class -> stack level too deep.");
if (!isset($bt[$l]['type'])) {
throw new Exception ('type not set');
}
else switch ($bt[$l]['type']) {
case '::':
$lines = file($bt[$l]['file']);
$i = 0;
$callerLine = '';
do {
$i++;
$callerLine = $lines[$bt[$l]['line']-$i] . $callerLine;
} while (strpos($callerLine,$bt[$l]['function']) === false);
preg_match('/([a-zA-Z0-9\_]+)::'.$bt[$l]['function'].'/',
$callerLine,
$matches);
if (!isset($matches[1])) {
// must be an edge case.
throw new Exception ("Could not find caller class: originating method call is obscured.");
}
switch ($matches[1]) {
case 'self':
case 'parent':
return get_called_class($bt,$l+1);
default:
return $matches[1];
}
// won't get here.
case '->':
if(isset($bt[$l]['object']) && is_object($bt[$l]['object']))
{
$s=trim(print_r($bt[$l]['object'],true));
return substr($s,0,strpos($s,' '));
}
else
{
switch ($bt[$l]['function'])
{
case '__get':
// edge case -> get class of calling object
if (!is_object($bt[$l]['object'])) throw new Exception ("Edge case fail. __get called on non object.");
return get_class($bt[$l]['object']);
default: return $bt[$l]['class'];
}
}

default: throw new Exception ("Unknown backtrace method type");
}
}
}
?>
Хоть и связано с парсингом php кода, но все же работает. Но не всегда правильно.
WingedFox:
Александр Эсаулов
Я говорил проверить - определяется ли А как родитель B.
dimagolov:
Как-то в теме упустили самый важный момент, что необходимости в подобном шаманстве быть не должно, а если она есть, то что-то не правильно в архитектуре и/или понимании ООП
Александр Эсаулов:
WingedFox
В методе класса A я ничего не знаю о классе B. Классов которые наследуются от A будет много, и в методах класса A нужно знать имя класса с которого был вызван метод.
dimagolov
Даже незнаю, правильно или не правильно построена архитектура ООП. Но уже не изменить.
Есть один главный (назовем его так) класс, который производит различные операции с таблицами БД (основные добавление/удаление/выборка).
Есть классы, которые ссылаться на методы этого главного класса, передавая в параметре имя своего класса.
Вызванный метод главного класса определяет свойства (get_class_vars()) класса переданного в параметре и производит нужные действия. В свойствах описываются задействованые таблицы, их поля, типы полей, и.д. Вся логика работы зависит именно от описания свойств класса.

Это было создано для сокращения кол-ва кода, да и для более безошибочной работы. Делая изменения/дополнения в главном классе, изменения получают все классы.

Но, в этих классах приходится создавать вручную методы, такие же как и в главном. Это не удобно. Гораздо проще наследоваться с главного и иметь все его методы. Остается лишь одно, в методах главного класса узнать откуда он был вызван.
WingedFox:
Александр Эсаулов
Читайте внимательнее - имеет смысл проверить, определяется ли класс A как родитель B и если нет - надо писать багрепорт.
Конечно, это уже за пределами конкретной задачи... но, тем не менее, имеет смысл помочь сообществу.
dimagolov:

Есть один главный (назовем его так) класс, который производит различные операции с таблицами БД (основные добавление/удаление/выборка).
Есть классы, которые ссылаться на методы этого главного класса, передавая в параметре имя своего класса.
Вызванный метод главного класса определяет свойства (get_class_vars()) класса переданного в параметре и производит нужные действия. В свойствах описываются задействованые таблицы, их поля, типы полей, и.д. Вся логика работы зависит именно от описания свойств класса.

это ORM такой?

Тут часом недавняя Стратегия не уместна?
Миша Спларов:
Александр Эсаулов
Способ с debug_backtrace на сколько я помню по разному работает в php4 и в php5, если это играет для вас роль. А через Reflection точно можно сделать то, что вы хотите.
P. S. Вспомнилась занимательная задачка.
Александр Эсаулов:
Миша Спларов
Используется в любом случае PHP5. В занимательная задачке речь идет о методах. Там нет решения данной задачи. Будем тогда пока использовать debug_backtrace(), а что делать. Если есть готовое решение, прошу поделиться.
Maus:
в методах класса A нужно знать имя класса с которого был вызван метод.
Не нужно.
Решить это можно двояко:
1) заводится protected свойство, и его значение перегружается (вручную) у каждого потомка.
2) посмотреть в сторону ключевого слово self: вроде бы оно должно помочь обойти проблему магической константы __CLASS__

Кстати, в том коде, что Вы приводили, как минимум небрежность: Вы нестатический метод вызывается как статический.
Миша Спларов:
Александр Эсаулов
Извиняюсь, я ошибся на счёт Reflection, вашу задачу с помощью него не решить.
Александр Эсаулов:
Maus
1) Вы конечно правы, но приведу пример.

<?
class A
{
function aa()
{
print $this->ClassName;
}
}

class B extends A
{
var $ClassName='This class B';
}

$obj=new B;
$obj->aa();

//B::aa(); // Как быть, если так вызывать метод?
?>

Что если мы через "::" вызваем метод, как получить свойства вызываемого класса?
Maus:
Александр Эсаулов
Учите матчасть!
1) Статические методы и статические свойства.
2) var deprecated, нэ?
Александр Эсаулов:
Maus
1) есть одно решение, но опять же для PHP 5.3.0. Вот здесь описано http://ua2.php.net/oop5.late-static-bindings. Мне это не подходит, есть другие способы?
2) вот это я так и не понял, что это?
dimagolov:
Александр Эсаулов, Вы бы посмотрели, как реолизованы другие ORM (e.g. http://sourceforge.net/projects/phplightorm/, http://www.doctrine-project.org/) и сделали бы нормальный рефакторинг своего кода, вместо выдумывания кривых костылей
Александр Эсаулов:
dimagolov
я считаю, что переделывать мне код не нужно. Если бы делал сейчас с нуля, всеравно так бы сделал.
Код прекрасно работает, вот только если бы удалось как то определять свойства или название класса наследника, удалось бы избавиться от некоторого кол-ва кода.

И так, еще раз привожу код:

<?
class A
{
public static function Fun()
{
// Как здесь получить значение свойства ClassName из класса "B" ?
// Или название класса "B" ?
}
}

class B extends A
{
public static $ClassName='This class B';
}

B::Fun();



Господа, давайте уже окончательно определимся, возможно ли это как то узнать для PHP<5.3.0? Если нет нормального способа, так и говорите. Дайте четкий ответ.
Maus:
Признаю, был неправ. Действительно, всё упирается в то, что в PHP версий меньше 5.3 нет позднего связывания. Поэтому для статических вызовов способов нет.
Отсюда: почему бы Вам не создавать объект?

P.S. когда-то наталкивался на эту штуку со static, но уже забыл :((
Anonymous:
Объект создавать не хочется, т.к. эти классы и используются уже в написании скриптов/сайтов. Отсюда, меньше кода (не нужно объявлять класс)... Более читабельный и понимаемый код... Новичку, который не силен в ООП, несложно разобраться(вызываются как функции, да и все)...

Да и изначально, я так спланировал и переделывать не собираюсь.
Александр Эсаулов:
Извиняюсь, предыдущий пост мой.
Valera2010: http://php.net/manual/en/language.oop5.late-static-bindings.php
используй в родителе static вместо self
http://php.net/manual/en/language.oop5.late-static-bindings.php
Александр Михалицын:
Valera2010,
у нас на форуме принято общение на "Вы".

Эта тема на forum.dklab.ru