2010-02-23 14 views
25

Suponiendo que tengo las siguientes clases en diferentes archivos:Obtener espacio de nombres de clase niño de superclase en PHP

<?php 
    namespace MyNS; 

    class superclass { 

     public function getNamespace(){ 
      return __NAMESPACE__; 
     } 
    } 
?> 

<?php 
    namespace MyNS\SubNS; 

    class childclass extends superclass { } 
?> 

Si instanciar "ChildClass" y llamar getNamespace() devuelve "MyNS".

¿Hay alguna manera de obtener el espacio de nombres actual de la clase secundaria sin volver a definir el método?

He recurrido a la creación de una variable estática de $ namespace en cada clase y haciendo referencia a ella usando super::$namespace, pero eso simplemente no me parece muy elegante.

+0

Aunque no se le solicite específicamente, también podría estar interesado en la palabra clave 'namespace', http://doc.php.net/language.namespaces.nsconstants, p. 'echo namespace \ MyClass :: myFunction();' – VolkerK

+0

¡Gracias, lo sé, pero desafortunadamente no funciona en esta situación! :) –

Respuesta

19

__NAMESPACE__ es una constante de tiempo de compilación, lo que significa que solo es útil en tiempo de compilación. Puedes pensar que es una macro que, cuando se inserte, se reemplazará por el espacio de nombres actual. Por lo tanto, no hay forma de obtener __NAMESPACE__ en una superclase para hacer referencia al espacio de nombres de una clase secundaria. Deberá recurrir a algún tipo de variable asignada en cada clase secundaria, como ya lo hace.

Como alternativa, se puede utilizar la reflexión para obtener el nombre del espacio de nombres de una clase:

$reflector = new ReflectionClass('A\\Foo'); // class Foo of namespace A 
var_dump($reflector->getNamespaceName()); 

Ver the PHP manual más documentación (sin terminar). Tenga en cuenta que necesitará estar en PHP 5.3.0 o posterior para usar la reflexión.

+0

Gracias, eso tiene sentido, ¡pasé por alto totalmente el uso de la reflexión aquí! –

+0

Muy útil. Gracias ! – Caio

12

También puede hacer esto en su getNamespace) método (:

return get_class($this); 

cuando se llama desde ChildClass, el resultado será:

MyNS\SubNS\childclass 

Si no desea que el nombre de la clase de Al final, solo corte todo, desde el último hasta el final.

4

En mi caso, necesitaba crear un método en la clase principal, que puede llamar a un método estático con call_user_func() en alguna subclase. Si conoce el nombre completo de la clase, puede call_user_func() no tener problema. El truco era llamar a un método estático en el espacio de nombres de la subclase.

Así que tenemos es decir

\MyTools\AbstractParent 
\Something\Else\Foo extends \MyTools\AbstractParent 
\Something\Else\Bar extends \MyTools\AbstractParent 

Ahora necesitamos un método en el AbstractParent. Este método llamado desde la subclase Foo podrá llamar al Bar::doMe() anteponiendo su propio espacio de nombres.

Aquí es cómo lo haces con llamada dinámica:

namespace MyTools; 
abstract class AbstractParent { 
    public static method doMe(){} 

    public function callSomethingStaticInClass($class){ 
     // current namespace IS NOT MyTools 
     // so you cannot use __NAMESPACE__ 
     $currentClass = get_class($this); 
     $refl = new ReflectionClass($currentClass); 
     $namespace = $refl->getNamespaceName(); 

     // now we know what the subclass namespace is... 
     // so we prefix the short class name 
     $class = $namespace . '\\' . $class; 
     $method = 'doMe'; 

     return call_user_func(array($class, $method)); 
    } 

}; 

namespace Something\Else; 
class Foo extends AbstractParent { } 
class Bar extends AbstractParent { } 

$foo = new Foo(); 
$foo->callSomethingStaticInClass('Bar'); 

para que funcione con una llamada estática reemplazar get_class($this) con get_called_class()

+0

Esta debería ser la respuesta. – Tek

1

Espero que esto ayude.

/* First Namespace */ 
namespace MyNS { 
    class superclass { 
     /* Functions to get the current namespace 
     * If $object is null then return the 
     * namespace of the class where the 
     * method exists, if not null, return 
     * the namespace of the class called. 
     */ 
     public static function get_namespace($object = null) { 
      if($object !== null) { 
       $tmp = (($object != "self") && (get_called_class() != get_class($object))) ? get_class($object) : get_called_class(); 
       $tmparr = explode("\\", $tmp); 
       $class = array_pop($tmparr); 
       return join("\\", $tmparr); 
      } else { 
       return __NAMESPACE__; 
      } 
     } 
     public static function get_current_namespace() { 
      return self::get_namespace(self); 
     } 

     public function call_static_method($class_name, $method_name, $arguments = array()) { 
      $class = "\\" . $this->get_namespace($this) . "\\{$class_name}"; 
      if(method_exists($class, $method_name)) { 
       if(count($arguments) > 0) return $class::$method_name($arguments); 
       return $class::$method_name(); 
      } 
      return "Method ({$method_name}) Does not exist in class ({$class})"; 
     } 

     public function call_user_method($object, $method_name, $arguments = array()) { 
      if(method_exists($object, $method_name)) { 
       if(count($arguments) > 0) return $object->$method_name($arguments); 
       return $object->$method_name(); 
      } 
     } 
    } 

    class superclass2 extends superclass { 
     public static function foo() { 
      return "superclass2 foo"; 
     } 
     public function bar() { 
      return "superclass2 bar"; 
     } 
    } 
} 

/* Second Namespace */ 
namespace MyNS\SubNS { 
    class childclass extends \MyNS\superclass { } 

    class childclass2 extends \MyNS\superclass { 
     public static function foo() { 
      return "childclass2 foo"; 
     } 
     public function bar() { 
      return "childclass2 bar"; 
     } 
    } 
} 

/* Back to Root Namespace */ 
namespace { 
    /* Returns 'MyNS' */ 
    echo \MyNS\superclass::get_namespace() . "<br />"; 
    echo \MyNS\SubNS\childclass::get_namespace() . "<br />"; 

    /* Returns 'MyNS' */ 
    echo \MyNS\superclass::get_current_namespace() . "<br />"; 

    /* Returns 'MyNS\SubNS' */ 
    echo \MyNS\SubNS\childclass::get_current_namespace() . "<br />"; 


    /* Or this way */ 


    $super = new \MyNS\superclass(); 
    $child = new \MyNS\SubNS\childclass(); 

    /* Returns 'MyNS' */ 
    echo $super->get_namespace() . "<br />"; 
    echo $child->get_namespace() . "<br />"; 

    /* Returns 'MyNS' */ 
    echo $super->get_namespace($super) . "<br />"; 

    /* Returns 'MyNS\SubNS' */ 
    echo $child->get_namespace($child) . "<br />"; 

    /* Returns 'superclass2 foo' */ 
    echo $super->call_static_method("superclass2", "foo") . "<br />"; 

    /* Returns 'superclass2 bar' */ 
    $super2 = new \MyNS\superclass2(); 
    echo $super->call_user_method($super2, "bar") . "<br />"; 

    /* Returns 'superclass2 foo' */ 
    echo $child->call_static_method("childclass2", "foo") . "<br />"; 

    /* Returns 'superclass2 bar' */ 
    $child2 = new \MyNS\SubNS\childclass2(); 
    echo $child->call_user_method($child2, "bar") . "<br />"; 
} 

Editado en respuesta a Artur Bodera añadir 'llamar' funcionalidad

3

A partir de PHP 5.3 se puede utilizar get_called_class y algunas funciones de cadena para lograrlo.

substr(get_called_class(), 0, strrpos(get_called_class(), "\\"))

+0

Esta es la respuesta correcta y la mejor. No puede ser más simple, y funciona – Andrew

0

También puede sobrescribir el método getNamespace en su clase hija con el mismo código que en su superclase.

luego, al invocar $ this-> getNamespace() en otro método en su superclase devolverá el espacio de nombres de la clase correspondiente al objeto.

<?php 
namespace MyNS; 

class superclass { 

    public function getNamespace(){ 
     return __NAMESPACE__; 
    } 

    public function foo() { 
     echo $this->getNamespace(); 
    } 
} 
?> 

<?php 
namespace MyNS\SubNS; 

class childclass extends \MyNS\superclass { 
    public function getNamespace(){ 
     return __NAMESPACE__; 
    } 
} 
?> 

A = new MyNS\superclass(); 
B = new MyNS\subNS\childclass(); 

A->foo() will display "MyNS" 
B->foo() will display "MyNS\SubNS" 
Cuestiones relacionadas