una - php object class
PHP get_called_class() alternativa (9)
Tengo una superclase de PHP abstracta, que contiene código que necesita saber en qué subclase se está ejecutando.
class Foo {
static function _get_class_name() {
return get_called_class();
//works in PHP 5.3.*, but not in PHP 5.2.*
}
static function other_code() {
//needs to know
echo self::_get_class_name();
}
}
class Bar extends Foo {
}
class FooBar extends Foo {
}
Bar::other_code(); // i need ''Bar''
FooBar::other_code(); // i need ''FooBar''
Esto funcionaría si llamara a la función get_called_class()
; sin embargo, este código se ejecutará en PHP versión 5.2. *, Por lo que esa función no está disponible.
Hay algunas implementaciones PHP personalizadas de get_called_class()
, pero todas dependen de ir a través de debug_backtrack()
, analizar un nombre de archivo y número de línea y ejecutar una expresión regular (ya que el programador no sabe que PHP 5.2 tiene reflejo) para Encuentra el nombre de la clase. Este código debe poder ejecutarse con php, es decir. no solo de un archivo .php (Necesita trabajar desde un shell php -a
, o una sentencia eval()
).
Idealmente, una solución funcionaría sin requerir que se agregue ningún código a las subclases ... La única solución potencial que puedo ver es agregar el siguiente código a cada subclase, lo que obviamente es un truco asqueroso:
class FooBar extends Foo {
static function _get_class_name() {
return ''FooBar'';
}
}
EDIT: espera, esto ni siquiera parece funcionar. Habría sido mi último recurso. ¿Alguien puede pensar en algo similar a esta solución que me proporcione la funcionalidad requerida? Es decir, estoy dispuesto a aceptar una solución que me obligue a agregar una función o variable a cada subclase que le diga cuál es su nombre de clase. Desafortunadamente, parece que llamar a self::_get_class_name()
desde la superclase llama a la implementación de la clase padre, incluso si la subclase la ha anulado.
En realidad, a menudo es útil conocer la clase (sub) llamada real cuando se ejecuta un método de superclase, y no estoy de acuerdo en que haya algo de malo en querer resolver este problema.
Por ejemplo, mis objetos necesitan saber el nombre de la clase, pero lo que hacen con esa información es siempre el mismo y podría extraerse en un método de superclase SI pude obtener el nombre de la clase llamada. Incluso el equipo de PHP pensó que esto era lo suficientemente útil como para incluirlo en PHP 5.3.
La respuesta correcta y no predicativa, por lo que puedo decir, es que antes de la 5.3, debes hacer algo atroz (por ejemplo, retroceder) o simplemente incluir un código duplicado en cada una de las subclases.
Esta función hace el mismo trabajo pero también funciona con instancias:
if (!function_exists(''get_called_class'')) {
function get_called_class() {
$bt = debug_backtrace();
/*
echo ''<br><br>'';
echo ''<pre>'';
print_r($bt);
echo ''</pre>'';
*/
if (self::$fl == $bt[1][''file''] . $bt[1][''line'']) {
self::$i++;
} else {
self::$i = 0;
self::$fl = $bt[1][''file''] . $bt[1][''line''];
}
if ($bt[1][''type''] == ''::'') {
$lines = file($bt[1][''file'']);
preg_match_all(''/([a-zA-Z0-9/_]+)::'' . $bt[1][''function''] . ''/'', $lines[$bt[1][''line''] - 1], $matches);
$result = $matches[1][self::$i];
} else if ($bt[1][''type''] == ''->'') {
$result = get_class($bt[1][''object'']);
}
return $result;
}
}
Este truco incluye el uso atroz de debug_backtrace ... no es bonito, pero cumple su función:
<?php
function callerName($functionName=null)
{
$btArray = debug_backtrace();
$btIndex = count($btArray) - 1;
while($btIndex > -1)
{
if(!isset($btArray[$btIndex][''file'']))
{
$btIndex--;
if(isset($matches[1]))
{
if(class_exists($matches[1]))
{
return $matches[1];
}
else
{
continue;
}
}
else
{
continue;
}
}
else
{
$lines = file($btArray[$btIndex][''file'']);
$callerLine = $lines[$btArray[$btIndex][''line'']-1];
if(!isset($functionName))
{
preg_match(''/([a-zA-Z/_]+)::/'',
$callerLine,
$matches);
}
else
{
preg_match(''/([a-zA-Z/_]+)::''.$functionName.''/'',
$callerLine,
$matches);
}
$btIndex--;
if(isset($matches[1]))
{
if(class_exists($matches[1]))
{
return $matches[1];
}
else
{
continue;
}
}
else
{
continue;
}
}
}
return $matches[1];
}
Esto no es posible.
El concepto de "clase llamada" fue introducido en PHP 5.3. Esta información no fue rastreada en versiones anteriores.
Como una debug_backtrace
fea, posiblemente podría utilizar debug_backtrace
para ver la pila de llamadas, pero no es equivalente. Por ejemplo, en PHP 5.3, el uso de ClassName::method()
no reenvía la llamada estática; no tienes forma de decirlo con debug_backtrace
. Además, debug_backtrace
es relativamente lento.
He hecho una pregunta como esta antes, porque quería que un padre tuviera un método de fábrica que fuera algo así.
public static function factory() {
return new __CLASS__;
}
Pero siempre devolvió la clase padre, no la heredada.
Me dijeron que no es posible sin un enlace estático tardío . Fue introducido en PHP 5.3. Puedes leer la documentation .
La alternativa de PHP / 5.2 al enlace estático tardío que mantiene el código duplicado al mínimo mientras evita los hacks extraños sería crear líneas de un solo liner en las clases secundarias que pasan el nombre de la clase como argumento:
abstract class Transaction{
public $id;
public function __construct($id){
$this->id = $id;
}
protected static function getInstanceHelper($class_name, $id){
return new $class_name($id);
}
}
class Payment extends Transaction{
public static function getInstance($id){
return parent::getInstanceHelper(__CLASS__, $id);
}
}
class Refund extends Transaction{
public static function getInstance($id){
return parent::getInstanceHelper(__CLASS__, $id);
}
}
var_dump( Payment::getInstance(1), Refund::getInstance(2) );
object(Payment)#1 (1) {
["id"]=>
int(1)
}
object(Refund)#2 (1) {
["id"]=>
int(2)
}
La solucion es:
get_class($this);
Sin embargo, no sé si esta oración funciona en funciones estáticas. Pruébalo y dime tu opinión.
Solución de trabajo:
function getCalledClass(){
$arr = array();
$arrTraces = debug_backtrace();
foreach ($arrTraces as $arrTrace){
if(!array_key_exists("class", $arrTrace)) continue;
if(count($arr)==0) $arr[] = $arrTrace[''class''];
else if(get_parent_class($arrTrace[''class''])==end($arr)) $arr[] = $arrTrace[''class''];
}
return end($arr);
}
<?php
class Foo {
private static $instance;
static function _get_class_name() {
return self::myNameIs();
}
static function other_code() {
//needs to know
echo self::_get_class_name();
}
}
class Bar extends Foo {
public static function myNameIs() {
self::$instance = new Bar();
return get_class(self::$instance);
}
}
class FooBar extends Foo {
public static function myNameIs() {
self::$instance = new FooBar();
return get_class(self::$instance);
}
}
Bar::other_code(); // i need ''Bar''
FooBar::other_code(); // i need ''FooBar''