Get child class namespace from superclass in PHP

14,220

Solution 1

__NAMESPACE__ is a compile time constant, meaning that it is only useful at compile time. You can think of it as a macro which where inserted will replace itself with the current namespace. Hence, there is no way to get __NAMESPACE__ in a super class to refer to the namespace of a child class. You will have to resort to some kind of variable which is assigned in every child class, like you are already doing.

As an alternative, you can use reflection to get the namespace name of a class:

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

See the PHP manual for more (unfinished) documentation. Note that you'll need to be on PHP 5.3.0 or later to use reflection.

Solution 2

You can also do this in your getNamespace() method:

return get_class($this);

When called from childclass, the result will be:

MyNS\SubNS\childclass

If you don't want the class name on the end, just chop off everything from the last \ to the end.

Solution 3

In my case, I needed to create a method in parent class, that can call a static method with call_user_func() in some subclass. If you know the full class name, you can call_user_func() no problemo. The trick was to call a static method in the subclass' namespace.

So we have i.e.

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

We now need a method in AbstractParent. This method called from subclass Foo will be able to call Bar::doMe() by prepending its own namespace.

Here is how you do it with dynamic call:

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');

To make it work with a static call replace get_class($this) with get_called_class()

Solution 4

As of PHP 5.3 you can use get_called_class and some string functions to achieve this.

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

Solution 5

Hope this helps.

/* 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 />";
}

Edited in response to Artur Bodera to add 'call' functionality

Share:
14,220
Alex Weber
Author by

Alex Weber

Updated on June 17, 2022

Comments

  • Alex Weber
    Alex Weber about 2 years

    Assuming I have the following classes in different files:

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

    If I instantiate "childclass" and call getNamespace() it returns "MyNS".

    Is there any way to get the current namespace from the child class without redeclaring the method?

    I've resorted to creating a static $namespace variable in each class and referencing it using super::$namespace but that just doesn't feel very elegant.

  • Alex Weber
    Alex Weber over 14 years
    Thanks, that makes sense, I totally overlooked using reflection here!
  • Tek
    Tek over 9 years
    This should be the answer.
  • Andrew
    Andrew about 8 years
    This is the correct and best answer. Can't be more simple, and it works