With PHP 7+ being rolled out on many web hosting servers these days, there is an increased push to using Namespacing in PHP coding. This is also very evident in the PHP Standard Recommendations (PSR-0 [deprecated] and PSR-4 [active]) for Auto-loading defined by the PHP Framework Interop Group (PHP-FIG) that everyone seems to be following these days. One of the things that can be observed with the increase in Namespace using is the need to remember or query the fully qualified name of a class or object. This can be tricky at first glance, but evidently, since PHP 5.5, a very useful feature was added to easily get the fully qualified name of a class. This can come in very handy especially if you are creating your own PHP library or implementing a design pattern like a Factory pattern. This feature is called “Class Name Resolution” and it can be achieved by suffixing “::class” at the end of a class name, like this: ClassName::class. To demonstrate the value of this feature, I will show some example code, and also mention some useful functions that do the similar function for objects.
Using ::class feature
The double-colon class feature is very useful when you are trying to create objects dynamically, and it is also useful for checking for the existence of classes and interfaces. You only understand the value of this when you start to extensively use namespaces in your code, especially when you are creating a library or API. It often can become a hassle to remember and correctly type all those Fully Qualified Names for classes when you need an object. Of course, you have the use keyword that comes into play when creating objects. But it is often very helpful to use a Factory design pattern to quickly and seamlessly create objects that you need. The following is some very simple code that you can run to get an idea of how you can use the double-colon class feature:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
<?php /** * Note: I am using namespaces in an single file for demonstration purposes only! */ namespace NS\Patterns { class Factory { protected static $classes = []; public static function create($name) { if (!array_key_exists($name, static::$classes)) { throw new \Exception("Unregistered class name!"); } $obj = new static::$classes[$name]; echo get_class($obj); return $obj; } public static function register($name, $fqn) { if (!array_key_exists($name, static::$classes)) { static::$classes[$name] = $fqn; } } } } namespace NS\Topic\Category { class Example {} class Child extends Example {} class Sibling extends Example {} class GrandChild extends Child {} } namespace { //Global Scope use NS\Patterns\Factory; use NS\Topic\Category\Example; //Use Case 1: checking class existance echo "<br>"; if (class_exists("Example")) { echo "1) true<br>"; } else { echo "1) false<br>"; } if (class_exists("NS\Topic\Category\Example")) { echo "2) true<br>"; } else { echo "2) false<br>"; } if (class_exists(Example::class)) { echo "3) true<br>"; } else { echo "3) false<br>"; } //Use Case 2: Using in Factory design pattern Factory::register("eg", Example::class); $dynamicObject1 = Factory::create("eg"); $dynamicObject2 = Factory::create("ex"); } ?> |
If you run the code in the example above, you will get the following result:
1 2 3 4 5 |
1) false 2) true 3) true NS\Topic\Category\Example Fatal error: Uncaught Exception: Unregistered class name! |
As you can see by looking at the code, you can specify the Fully Qualified Name (FQN) of the class without having the actually type it out. The only point at which you type it out is at the use keyword. So, in the case of this example, you can specify the FQN for a class in one file. Register the FQN with a Factory. Then, instantiate an object in any other script via the Factory without ever having to specify the FQN again. I think that that feature is really cool.
static::class versus __CLASS__ (and Late Static Binding)
Another very interesting use of double-colon class feature is for finding out the name of the actual class executed rather than the inherited class. The __CLASS__ magic constant is typically used the find out the name of a class. However, this magic constant only accurately works for naming the base or parent class and not a derived class. You would have to override the function using the __CLASS__ magic constant in the derived class if you want to accurately get the classes FQN. However, with the double-colon class feature, we can accurately retrieve the FQN for the class by using it on the static keyword: static::class.
By adding double-colon class to the end of the static keyword (static::class), we are making use of a PHP feature called “Late Static Binding”, which was introduced in PHP 5.3. Essentially, by using the static keyword, with the double-colon class, the class name will not be resolved where the method is defined, but rather it will be resolved from the class that was called at runtime. Here is a modified example to show how this works:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
<?php namespace NS\Topic\Category { class Example { public function getClassName(){ return __CLASS__; } public function getLateClassName() { return static::class; } } class Child extends Example {} class Sibling extends Example {} class GrandChild extends Child {} } namespace { //Global Scope use NS\Patterns\Factory; use NS\Topic\Category\Example; use NS\Topic\Category\Child; use NS\Topic\Category\Sibling; use NS\Topic\Category\GrandChild; $a = new Example(); $b = new Child(); $c = new Sibling(); $d = new GrandChild(); echo $a->getClassName(); echo "<br>"; echo $a->getLateClassName(); echo "<br>"; echo $b->getClassName(); echo "<br>"; echo $b->getLateClassName(); echo "<br>"; echo $c->getClassName(); echo "<br>"; echo $c->getLateClassName(); echo "<br>"; echo $d->getClassName(); echo "<br>"; echo $d->getLateClassName(); echo "<br>"; } ?> |
The result of the code above is as follows:
1 2 3 4 5 6 7 8 |
NS\Topic\Category\Example NS\Topic\Category\Example NS\Topic\Category\Example NS\Topic\Category\Child NS\Topic\Category\Example NS\Topic\Category\Sibling NS\Topic\Category\Example NS\Topic\Category\GrandChild |
The results reveal that using static::class gets the classname of the derived class every time.
One interesting tip to note is that using the __CLASS__ magic constant is equivalent to using self::class. They produce the same result. You can replace __CLASS__ with self::class in example 2 above, and you will see that the results are the same.
Equivalent functions for objects
Lastly, I just want to point out some equivalent functions that you can use to find out class names:
- get_called_class() – This function gets the late static binding class name. It is equivalent to using static::class.
- get_class() – This function when used without a parameter, inside a class, will give the class name of the class where the method is defined. It works the same way as using the __CLASS__ magic constant.
- get_class($this) – This function when used with the $this keyword will provide the class name of the derived class from which the object was instantiated. It is equivalent to using static::class. Of course, putting any object variable as the parameter instead of $this, will give you the FQN of the object’s class.
- class_exists(ClassName::class) – Use this function with ClassName::class to determine if the class has been defined.
- is_subclass_of($object, ClassName::class) – Use this function to find out if an object is the child of the specified class name or if it implements it.
References
- PSR-4: Autoloader – PHP-FIG [https://www.php-fig.org/psr/psr-4]
- Class Name Resolution – PHP.net [https://www.php.net/manual/en/migration55.new-features.php#migration55.new-features.class-name]
- Magic Constants – PHP.net [https://www.php.net/manual/en/language.constants.predefined.php]
- Late Static Bindings – PHP.net [https://www.php.net/manual/en/language.oop5.late-static-bindings.php]
- The Basics – PHP.net [https://www.php.net/manual/en/language.oop5.basic.php#language.oop5.basic.class.class]
- Namespaces and dynamic language features – PHP.net [https://www.php.net/manual/en/language.namespaces.dynamic.php]