Получить пространство имен дочерних классов из суперкласса в PHP
Предполагая, что у меня есть следующие классы в разных файлах:
<?php
namespace MyNS;
class superclass {
public function getNamespace(){
return __NAMESPACE__;
}
}
?>
<?php
namespace MyNS\SubNS;
class childclass extends superclass { }
?>
Если я создаю экземпляр "childclass" и вызываю getNamespace(), он возвращает "MyNS".
Есть ли способ получить текущее пространство имен из дочернего класса без повторного использования метода?
Я прибегал к созданию статической переменной пространства имен в каждом классе и ссылаюсь на нее с помощью super::$namespace
, но это просто не очень элегантно.
Ответы
Ответ 1
__NAMESPACE__
- это константа времени компиляции, что означает, что она полезна только во время компиляции. Вы можете думать о нем как о макросе, который, когда вставил, заменит себя текущим пространством имен. Следовательно, нет способа получить __NAMESPACE__
в суперклассе, чтобы ссылаться на пространство имен дочернего класса. Вам придётся прибегнуть к какой-то переменной, которая назначается в каждом дочернем классе, как вы уже делаете.
В качестве альтернативы вы можете использовать отражение, чтобы получить имя пространства имен класса:
$reflector = new ReflectionClass('A\\Foo'); // class Foo of namespace A
var_dump($reflector->getNamespaceName());
См. руководство по PHP для получения дополнительной (незавершенной) документации. Обратите внимание, что вам нужно быть на PHP 5.3.0 или новее, чтобы использовать отражение.
Ответ 2
Вы также можете сделать это в методе getNamespace():
return get_class($this);
При вызове из дочернего класса результатом будет:
MyNS\SubNS\childclass
Если вам не нужно имя класса в конце, просто отрубите все от последнего\до конца.
Ответ 3
В моем случае мне нужно было создать метод в родительском классе, который может вызвать статический метод с call_user_func()
в каком-то подклассе. Если вы знаете полное имя класса, вы можете call_user_func()
no problemo. Хитрость заключалась в вызове статического метода в пространстве имен подкласса.
Итак, мы имеем.
\MyTools\AbstractParent
\Something\Else\Foo extends \MyTools\AbstractParent
\Something\Else\Bar extends \MyTools\AbstractParent
Теперь нам нужен метод в AbstractParent
. Этот метод, вызванный из подкласса Foo
, сможет вызвать Bar::doMe()
путем добавления собственного пространства имен.
Вот как вы это делаете с динамическим вызовом:
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');
Чтобы заставить его работать со статическим вызовом, замените get_class($this)
на get_called_class()
Ответ 4
Как и в PHP 5.3, вы можете использовать get_called_class
и некоторые строковые функции для достижения этого.
substr(get_called_class(), 0, strrpos(get_called_class(), "\\"))
Ответ 5
Надеюсь, что это поможет.
/* 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 />";
}
Отредактировано в ответ на Artur Bodera, чтобы добавить функциональность "вызов"
Ответ 6
Вы также можете перезаписать метод getNamespace в своем дочернем классе с тем же кодом, что и в вашем суперклассе.
тогда вызов $this- > getNamespace() в другом методе в вашем суперклассе вернет пространство имен класса, соответствующего объекту.
<?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"