php - poo - Averigüe qué clase llama un método en otra clase
php poo (8)
@Pascal MARTIN: Sí, en las aplicaciones normales probablemente no sea necesario. Pero a veces puede ser útil. Considere un ejemplo de mi propia aplicación:
Hay una subclase de Controlador que puede usar un objeto Plantilla para preparar su salida. Cada plantilla tiene un nombre al que referirse. Cuando un controlador necesita una plantilla, le pide al TemplateManager que le dé ese nombre como parámetro. Pero podría haber muchos archivos de plantilla con ese nombre para diferentes Controladores. Los controladores se utilizan como complementos, y pueden ser escritos por diferentes usuarios, por lo que los nombres utilizados por ellos no se pueden controlar para que no entren en conflicto entre sí. Se necesitan espacios de nombres para las plantillas. Por lo tanto, TemplateManager, que es una fábrica de objetos de plantilla, necesita el nombre de la plantilla y el nombre del espacio de nombres para ubicar el archivo fuente de plantilla adecuado. Este espacio de nombres está relacionado con el nombre de clase del Controlador en particular.
Pero, en la mayoría de los casos, cada Controlador utilizará plantillas de su propio espacio de nombres y solo en casos raros de otros espacios de nombres. Por lo tanto, especificar el espacio de nombres en cada llamada a TemplateManager :: getTemplate () cada vez sería un desastre. ¡Es mejor si el espacio de nombres es opcional y por defecto ... el Controlador que llama al TemplateManager :: getTemplate ()! Y aquí hay un buen lugar para conocer a la persona que llama.
Por supuesto, el controlador de llamadas podría pasar a sí mismo o su nombre como parámetro, pero en realidad no difiere mucho de pasar el nombre del espacio de nombres. No podría ser opcional de ninguna manera.
Pero si puede conocer a la persona que llama, puede usar esa información para predeterminar el espacio de nombres automáticamente dentro de getTemplate (), sin siquiera molestar a la persona que llama. No tiene que saber cómo getTemplate () lo maneja en su interior y cómo sabe el espacio de nombres predeterminado apropiado. Solo necesita saber que lo hace, y que puede pasar cualquier otro espacio de nombres opcionalmente si realmente lo necesita.
¿Hay una manera en PHP para averiguar qué objeto se llama qué método en otro objeto.
Ejemplo:
class Foo
{
public function __construct()
{
$bar = new Bar();
$bar->test();
}
}
class Bar
{
public function test()
{
}
}
$foo = new Foo();
¿Habría alguna manera de descubrir que el método de prueba fue llamado desde el objeto foo?
Aquí hay una solución de línea
list(, $caller) = debug_backtrace(false, 2);
A partir de PHP7, esto no funcionará según los documentos: http://php.net/manual/en/function.list.php ya que no podemos tener propiedades vacías, aquí hay una pequeña actualización:
list($childClass, $caller) = debug_backtrace(false, 2);
Como mínimo, podría usar debug_backtrace y analizarlo para encontrar el método de llamada.
Creo que también deberías poder hacerlo usando la API de reflexión , pero ha pasado demasiado tiempo desde que uso PHP y no recuerdo exactamente cómo. Sin embargo, los enlaces deberían al menos ayudarte a comenzar.
Esta función hace el trabajo sin debug_backtrace:
/*
usage :
some code...
getRealCallClass(__FUNCTION__);
some code...
*/
function getRealCallClass($functionName) //Parameter value must always be __FUNCTION__
{
try
{
throw new exception();
}
catch(exception $e)
{
$trace = $e->getTrace();
$bInfunction = false;
foreach($trace as $trace_piece)
{
if ($trace_piece[''function''] == $functionName)
{
if (!$bInfunction)
$bInfunction = true;
}
elseif($bInfunction) //found !!!
{
return $trace_piece[''class''];
}
}
}
}
Podrías usar debug_backtrace
, un poco así:
Por cierto, eche un vistazo a los comentarios en la página del manual: hay algunas funciones y consejos útiles proporcionados ;-)
class Foo
{
public function __construct()
{
$bar = new Bar();
$bar->test();
}
}
class Bar
{
public function test()
{
$trace = debug_backtrace();
if (isset($trace[1])) {
// $trace[0] is ourself
// $trace[1] is our caller
// and so on...
var_dump($trace[1]);
echo "called by {$trace[1][''class'']} :: {$trace[1][''function'']}";
}
}
}
$foo = new Foo();
El var_dump
daría salida:
array
''file'' => string ''/home/squale/developpement/tests/temp/temp.php'' (length=46)
''line'' => int 29
''function'' => string ''__construct'' (length=11)
''class'' => string ''Foo'' (length=3)
''object'' =>
object(Foo)[1]
''type'' => string ''->'' (length=2)
''args'' =>
array
empty
y el echo
:
called by Foo :: __construct
Pero, por muy bonito que parezca, no estoy seguro de que deba utilizarse como una "cosa normal" en su aplicación ... Parece extraño, en realidad: con un buen diseño, un método no debe saber cómo se llama. , en mi opinión.
Probablemente pueda lograr esto con un seguimiento de depuración , aunque esto parezca un poco extraño.
Su opción alternativa es pasar un parámetro a esa clase y decirle desde dónde se llama, cuando crea una instancia de la clase dentro de otra.
También puede hacer que el objeto llamante se pase a sí mismo como un argumento.
p.ej
class Foo
{
public function __construct()
{
$bar = new Bar();
$bar->test($this);
}
}
class Bar
{
public function test()
{
}
}
$foo = new Foo();
Obtuve esta idea del libro "Patrones de diseño: elementos de software orientado a objetos reutilizables" de Erich Gamma, et al, en la página 278 en la discusión sobre el patrón estructural "Mediador".
El punto del patrón es reducir el número de conexiones de muchos a muchos entre un montón de objetos / clases. Crea una clase de mediador que todas esas clases tratan como un centro. De esa manera las clases no necesitan conocerse unas de otras. El mediador maneja las interacciones. Para que el mediador esté informado de los cambios en las clases que rastrea, pueden pasar a sí mismos como argumentos, o se puede implementar el mediador utilizando el patrón "Observador".
EDICIÓN 2018:
A veces uso interfaces con el código anterior, como este:
interface someInterface // many classes may implement this interface
{
public function giveMeBar();
}
class Foo implements someInterface
{
public function __construct()
{
$bar = new Bar();
$bar->test($this);
}
public function giveMeBar() {
return ''Bar'';
}
}
class Bar
{
public function test(someInterface $a)
{
echo $a->giveMeBar();
}
}
$foo = new Foo(); // prints "Bar"
var_dump(getClass($this));
Utilizado en un método en el espacio de nombres B, esto le dará la clase que llamó a un método en el espacio de nombres B del espacio de nombres A.