tag should short_open_tag open off ensure enable close active php class namespaces

should - short tags de php



¿Cómo obtengo el nombre de clase no calificado(corto) de un objeto? (19)

¿Cómo verifico la clase de un objeto dentro del entorno espaciado de nombre de PHP sin especificar la clase de espacio de nombre completo?

Por ejemplo, supongamos que tengo una biblioteca de objetos / Entidad / Contrato / Nombre.

El siguiente código no funciona, ya que get_class devuelve la clase de espacio de nombres completo.

If(get_class($object) == ''Name'') { ... do this ... }

La palabra clave magic del espacio de nombres devuelve el espacio de nombres actual, lo cual no sirve de nada si el objeto evaluado tiene otro espacio de nombres.

Simplemente podría especificar el nombre de clase completo con espacios de nombres, pero esto parece estar bloqueado en la estructura del código. Tampoco sería de mucha utilidad si quisiera cambiar el espacio de nombres dinámicamente.

¿Alguien puede pensar en una forma eficiente de hacer esto? Supongo que una opción es regex.


Añadí substr a la prueba de https://.com/a/25472778/2386943 y esa es la manera más rápida que pude probar (CentOS PHP 5.3.3, Ubuntu PHP 5.5.9) ambos con un i5.

$classNameWithNamespace=get_class($this); return substr($classNameWithNamespace, strrpos($classNameWithNamespace, ''//')+1);

Resultados

Reflection: 0.068084406852722 s ClassA Basename: 0.12301609516144 s ClassA Explode: 0.14073524475098 s ClassA Substring: 0.059865570068359 s ClassA

Código

namespace foo/bar/baz; class ClassA{ public function getClassExplode(){ $c = array_pop(explode(''//', get_class($this))); return $c; } public function getClassReflection(){ $c = (new /ReflectionClass($this))->getShortName(); return $c; } public function getClassBasename(){ $c = basename(str_replace(''//', ''/'', get_class($this))); return $c; } public function getClassSubstring(){ $classNameWithNamespace = get_class($this); return substr($classNameWithNamespace, strrpos($classNameWithNamespace, ''//')+1); } } $a = new ClassA(); $num = 100000; $rounds = 10; $res = array( "Reflection" => array(), "Basename" => array(), "Explode" => array(), "Substring" => array() ); for($r = 0; $r < $rounds; $r++){ $start = microtime(true); for($i = 0; $i < $num; $i++){ $a->getClassReflection(); } $end = microtime(true); $res["Reflection"][] = ($end-$start); $start = microtime(true); for($i = 0; $i < $num; $i++){ $a->getClassBasename(); } $end = microtime(true); $res["Basename"][] = ($end-$start); $start = microtime(true); for($i = 0; $i < $num; $i++){ $a->getClassExplode(); } $end = microtime(true); $res["Explode"][] = ($end-$start); $start = microtime(true); for($i = 0; $i < $num; $i++){ $a->getClassSubstring(); } $end = microtime(true); $res["Substring"][] = ($end-$start); } echo "Reflection: ".array_sum($res["Reflection"])/count($res["Reflection"])." s ".$a->getClassReflection()."/n"; echo "Basename: ".array_sum($res["Basename"])/count($res["Basename"])." s ".$a->getClassBasename()."/n"; echo "Explode: ".array_sum($res["Explode"])/count($res["Explode"])." s ".$a->getClassExplode()."/n"; echo "Substring: ".array_sum($res["Substring"])/count($res["Substring"])." s ".$a->getClassSubstring()."/n";

== ACTUALIZACIÓN ==

Como se menciona en los comentarios de @MrBandersnatch, existe una forma más rápida de hacerlo:

return substr(strrchr(get_class($this), ''//'), 1);

Aquí están los resultados de las pruebas actualizadas con "SubstringStrChr" (guarda hasta aproximadamente 0.001 s):

Reflection: 0.073065280914307 s ClassA Basename: 0.12585079669952 s ClassA Explode: 0.14593172073364 s ClassA Substring: 0.060415267944336 s ClassA SubstringStrChr: 0.059880912303925 s ClassA


Aquí hay una forma más fácil de hacerlo si está utilizando el framework PHP de Laravel:

<?php // usage anywhere // returns HelloWorld $name = class_basename(''Path/To/YourClass/HelloWorld''); // usage inside a class // returns HelloWorld $name = class_basename(__CLASS__);


Aquí hay una solución simple para PHP 5.4+

namespace { trait Names { public static function getNamespace() { return implode(''//', array_slice(explode(''//', get_called_class()), 0, -1)); } public static function getBaseClassName() { return basename(str_replace(''//', ''/'', get_called_class())); } } }

¿Qué será el regreso?

namespace x/y/z { class SomeClass { use /Names; } echo /x/y/z/SomeClass::getNamespace() . PHP_EOL; // x/y/z echo /x/y/z/SomeClass::getBaseClassName() . PHP_EOL; // SomeClass }

El nombre de clase y el espacio de nombres extendidos funcionan bien para:

namespace d/e/f { class DifferentClass extends /x/y/z/SomeClass { } echo /d/e/f/DifferentClass::getNamespace() . PHP_EOL; // d/e/f echo /d/e/f/DifferentClass::getBaseClassName() . PHP_EOL; // DifferentClass }

¿Qué pasa con la clase en el espacio de nombres global?

namespace { class ClassWithoutNamespace { use /Names; } echo ClassWithoutNamespace::getNamespace() . PHP_EOL; // empty string echo ClassWithoutNamespace::getBaseClassName() . PHP_EOL; // ClassWithoutNamespace }


Basado en la respuesta de @MaBi, hice esto:

trait ClassShortNameTrait { public static function getClassShortName() { if ($pos = strrchr(static::class, ''//')) { return substr($pos, 1); } else { return static::class; } } }

Que puedes usar así:

namespace Foo/Bar/Baz; class A { use ClassShortNameTrait; }

A::class devuelve Foo/Bar/Baz/A , pero A::getClassShortName() devuelve A

Funciona para PHP> = 5.5.


Citando php.net:

En Windows, tanto la barra inclinada (/) como la barra invertida () se usan como carácter separador de directorio. En otros entornos, es la barra inclinada (/).

En función de esta información y de la ampliación de la respuesta arzzzen, esto debería funcionar en los sistemas Windows y Nix *:

<?php if (basename(str_replace(''//', ''/'', get_class($object))) == ''Name'') { // ... do this ... }

Nota: Hice un punto de referencia de ReflectionClass contra basename+str_replace+get_class y el uso de la reflexión es aproximadamente un 20% más rápido que con el enfoque del nombre base, pero con YMMV.


La solución más rápida e inmediata que funciona en cualquier entorno es:

<?php namespace /My/Awesome/Namespace; class Foo { private $shortName; public function fastShortName() { if ($this->shortName === null) { $this->shortName = explode("//", static::class); $this->shortName = end($this->shortName); } return $this->shortName; } public function shortName() { return basename(strtr(static::class, "//", "/")); } } echo (new Foo())->shortName(); // "Foo" ?>


Me encontré en una situación única en la que instanceof no se podía usar (específicamente rasgos con espacios de nombres) y necesitaba el nombre corto de la manera más eficiente posible, así que hice un pequeño punto de referencia propio. Incluye todos los diferentes métodos y variaciones de las respuestas en esta pregunta.

$bench = new /xori/Benchmark(1000, 1000); # https://github.com/Xorifelse/php-benchmark-closure $shell = new /my/fancy/namespace/classname(); # Just an empty class named `classname` defined in the `/my/fancy/namespace/` namespace $bench->register(''strrpos'', (function(){ return substr(static::class, strrpos(static::class, ''//') + 1); })->bindTo($shell)); $bench->register(''safe strrpos'', (function(){ return substr(static::class, ($p = strrpos(static::class, ''//')) !== false ? $p + 1 : 0); })->bindTo($shell)); $bench->register(''strrchr'', (function(){ return substr(strrchr(static::class, ''//'), 1); })->bindTo($shell)); $bench->register(''reflection'', (function(){ return (new /ReflectionClass($this))->getShortName(); })->bindTo($shell)); $bench->register(''reflection 2'', (function($obj){ return $obj->getShortName(); })->bindTo($shell), new /ReflectionClass($shell)); $bench->register(''basename'', (function(){ return basename(str_replace(''//', ''/'', static::class)); })->bindTo($shell)); $bench->register(''explode'', (function(){ $e = explode("//", static::class); return end($e); })->bindTo($shell)); $bench->register(''slice'', (function(){ return join('''',array_slice(explode(''//', static::class), -1)); })->bindTo($shell)); print_r($bench->start());

Una lista de todos los resultados está here pero aquí están los aspectos más destacados:

  • Si va a utilizar el reflejo de todos modos, usar $obj->getShortName() es el método más rápido; usar la reflexión solo para obtener el nombre corto es casi el método más lento.
  • ''strrpos'' puede devolver un valor incorrecto si el objeto no está en un espacio de nombres, mientras que ''safe strrpos'' es un poco más lento, diría que este es el ganador.
  • Para hacer que ''basename'' compatible entre Linux y Windows, debe usar str_replace() que hace que este método sea el más lento de todos.

Una tabla simplificada de resultados, la velocidad se mide en comparación con el método más lento:

+-----------------+--------+ | registered name | speed | +-----------------+--------+ | reflection 2 | 70.75% | | strrpos | 60.38% | | safe strrpos | 57.69% | | strrchr | 54.88% | | explode | 46.60% | | slice | 37.02% | | reflection | 16.75% | | basename | 0.00% | +-----------------+--------+


Para obtener el nombre corto como una línea (desde PHP 5.4 ):

echo (new ReflectionClass($obj))->getShortName();

Es un enfoque limpio y rápido razonable .


Puede obtener un resultado inesperado cuando la clase no tiene un espacio de nombres. Es decir get_class devuelve Foo , entonces $baseClass sería oo .

$baseClass = substr(strrchr(get_class($this), ''//'), 1);

Esto se puede solucionar fácilmente get_class prefijo get_class con una barra diagonal inversa:

$baseClass = substr(strrchr(''//'.get_class($this), ''//'), 1);

Ahora también las clases sin un espacio de nombres devolverán el valor correcto.


Puede usar explode para separar el espacio de nombre y end para obtener el nombre de clase:

$ex = explode("//", get_class($object)); $className = end($ex);


Puedes hacer esto con reflexión. Específicamente, puede usar el método php.net/manual/en/reflectionclass.getshortname.php , que obtiene el nombre de la clase sin su espacio de nombres.

En primer lugar, debe compilar una instancia de ReflectionClass y luego llamar al método getShortName de esa instancia:

$reflect = new ReflectionClass($object); if ($reflect->getShortName() === ''Name'') { // do this }

Sin embargo, no puedo imaginar muchas circunstancias en las que esto sería deseable. Si desea requerir que el objeto sea miembro de cierta clase, la forma de probarlo es con instanceof . Si desea una forma más flexible de señalar ciertas restricciones, la forma de hacerlo es escribir una interfaz y exigir que el código implemente esa interfaz. De nuevo, la forma correcta de hacerlo es con instanceof . (Puedes hacerlo con ReflectionClass , pero tendría un rendimiento mucho peor).


Se encuentra en la página de documentación de get_class , donde fue publicado por mí en nwhiting dot com .

function get_class_name($object = null) { if (!is_object($object) && !is_string($object)) { return false; } $class = explode(''//', (is_string($object) ? $object : get_class($object))); return $class[count($class) - 1]; }

Pero la idea de los espacios de nombres es estructurar su código. Eso también significa que puede tener clases con el mismo nombre en múltiples espacios de nombres. Entonces, teóricamente, el objeto que pase podría tener el nombre de la clase de nombre (despojado), sin dejar de ser un objeto totalmente diferente de lo que esperaba.

Además de eso, es posible que desee comprobar para una clase base específica, en cuyo caso get_class no hace el truco en absoluto. Es posible que desee verificar la instanceof operador de.


Si necesita saber el nombre de clase que se llamó desde dentro de una clase, y no desea el espacio de nombres, puede usar este

$calledClass = get_called_class(); $name = strpos($calledClass, ''//') === false ? $calledClass : substr($calledClass, strrpos($calledClass, ''//') + 1);

Esto es genial cuando tienes un método dentro de una clase que se extiende por otras clases. Además, esto también funciona si los espacios de nombres no se utilizan en absoluto.

Ejemplo:

<?php namespace One/Two { class foo { public function foo() { $calledClass = get_called_class(); $name = strpos($calledClass, ''//') === false ? $calledClass : substr($calledClass, strrpos($calledClass, ''//') + 1); var_dump($name); } } } namespace Three { class bar extends /One/Two/foo { public function bar() { $this->foo(); } } } namespace { (new One/Two/foo)->foo(); (new Three/bar)->bar(); } // test.php:11:string ''foo'' (length=3) // test.php:11:string ''bar'' (length=3)


Si solo está quitando espacios de nombres y quiere algo después del último / en el nombre de una clase con espacio de nombre (o solo el nombre si no hay ''/'), puede hacer algo como esto:

$base_class = preg_replace(''/^([/w////]+////)?([^////]+)$/'', ''$2'', get_class($myobject));

Básicamente es regex para obtener cualquier combinación de caracteres o barras diagonales inversas hacia arriba y hasta la última barra invertida para luego devolver solo los caracteres que no sean la barra invertida hacia arriba y hasta el final de la cadena. Agregar el? después de la primera agrupación significa que si la coincidencia de patrón no existe, simplemente devuelve la cadena completa.


Una buena expresión regular antigua parece ser más rápida que la mayoría de los métodos mostrados anteriormente:

// both of the below calls will output: ShortClassName echo preg_replace(''/.*/////'', '''', ''ShortClassName''); echo preg_replace(''/.*/////'', '''', ''SomeNamespace/SomePath/ShortClassName'');

Esto funciona incluso cuando proporciona un nombre de clase corto o un nombre de clase completo (canónico).

Lo que hace la expresión regular es que consume todos los caracteres anteriores hasta que se encuentra el último separador (que también se consume). Entonces la cadena restante será el nombre corto de la clase.

Si desea utilizar un separador diferente (por ej., /), Simplemente use ese separador. Recuerde escapar de la barra diagonal inversa (es decir, /) y también del patrón char (es decir, /) en el patrón de entrada.


Yii forma

/yii/helpers/StringHelper::basename(get_class($model));

Yii usa este método en su generador de código Gii

Documentación de método

Este método es similar al nombre base de la función php (), excepto que tratará ambos / y / como separadores de directorios, independientemente del sistema operativo. Este método fue creado principalmente para trabajar en espacios de nombres php. Al trabajar con rutas de archivos reales, el nombre base de php () debería funcionar bien para usted. Nota: este método no tiene conocimiento del sistema de archivos real o de los componentes de ruta como "..".

Más información:

https://github.com/yiisoft/yii2/blob/master/framework/helpers/BaseStringHelper.php http://www.yiiframework.com/doc-2.0/yii-helpers-basestringhelper.html#basename()-detail


Yo uso esto:

basename(str_replace(''//', ''/'', get_class($object)));


(new /ReflectionClass($obj))->getShortName(); es la mejor solución en cuanto a rendimiento.

Tenía curiosidad de cuál de las soluciones proporcionadas es la más rápida, así que hice una pequeña prueba.

Resultados

Reflection: 1.967512512207 s ClassA Basename: 2.6840535163879 s ClassA Explode: 2.6507515668869 s ClassA

Código

namespace foo/bar/baz; class ClassA{ public function getClassExplode(){ $c = array_pop(explode(''//', get_class($this))); return $c; } public function getClassReflection(){ $c = (new /ReflectionClass($this))->getShortName(); return $c; } public function getClassBasename(){ $c = basename(str_replace(''//', ''/'', get_class($this))); return $c; } } $a = new ClassA(); $num = 100000; $rounds = 10; $res = array( "Reflection" => array(), "Basename" => array(), "Explode" => array(), ); for($r = 0; $r < $rounds; $r++){ $start = microtime(true); for($i = 0; $i < $num; $i++){ $a->getClassReflection(); } $end = microtime(true); $res["Reflection"][] = ($end-$start); $start = microtime(true); for($i = 0; $i < $num; $i++){ $a->getClassBasename(); } $end = microtime(true); $res["Basename"][] = ($end-$start); $start = microtime(true); for($i = 0; $i < $num; $i++){ $a->getClassExplode(); } $end = microtime(true); $res["Explode"][] = ($end-$start); } echo "Reflection: ".array_sum($res["Reflection"])/count($res["Reflection"])." s ".$a->getClassReflection()."/n"; echo "Basename: ".array_sum($res["Basename"])/count($res["Basename"])." s ".$a->getClassBasename()."/n"; echo "Explode: ".array_sum($res["Explode"])/count($res["Explode"])." s ".$a->getClassExplode()."/n";

Los resultados realmente me sorprendieron. Pensé que la solución de explosión sería la forma más rápida de hacerlo ...


$shortClassName = join('''',array_slice(explode(''//', $longClassName), -1));