So I created these two classes
//Quarter.php
namespace Resources;
class Quarter {
...
}
//Epoch.php
namespace Resources;
class Epoch {
public static function initFromType($value, $type) {
$class = "Quarter";
return new $class($value, $type);
}
}
Now this is a a very simplified version of both, but is enough to illustrate my question. The classes as they are shown here will not work as it will not find the Quarter class. To make it work I could change the $class variable to
$class = "\Resources\Quarter";
So my question is: Why do I need to use the namespace here when both classes are already members of the same namespace. The namespace is only needed when I put the classname in a variable so doing:
public static function initFromType($value, $type) {
return new Quarter($value, $type);
}
will work without problems. Why is this and is there any potential traps here I need to avoid?
Because strings can be passed around from one namespace to another. That makes name resolution ambiguous at best and easily introduces weird problems.
namespace Foo;
$class = 'Baz';
namespace Bar;
new $class; // what class will be instantiated?
A literal in a certain namespace does not have this problem:
namespace Foo;
new Baz; // can't be moved, it's unequivocally \Foo\Baz
Therefore, all "string class names" are always absolute and need to be written as FQN:
$class = 'Foo\Baz';
(Note: no leading \.)
You can use this as shorthand, sort of equivalent to a self-referential self in classes:
$class = __NAMESPACE__ . '\Baz';
Related
So I created these two classes
//Quarter.php
namespace Resources;
class Quarter {
...
}
//Epoch.php
namespace Resources;
class Epoch {
public static function initFromType($value, $type) {
$class = "Quarter";
return new $class($value, $type);
}
}
Now this is a a very simplified version of both, but is enough to illustrate my question. The classes as they are shown here will not work as it will not find the Quarter class. To make it work I could change the $class variable to
$class = "\Resources\Quarter";
So my question is: Why do I need to use the namespace here when both classes are already members of the same namespace. The namespace is only needed when I put the classname in a variable so doing:
public static function initFromType($value, $type) {
return new Quarter($value, $type);
}
will work without problems. Why is this and is there any potential traps here I need to avoid?
Because strings can be passed around from one namespace to another. That makes name resolution ambiguous at best and easily introduces weird problems.
namespace Foo;
$class = 'Baz';
namespace Bar;
new $class; // what class will be instantiated?
A literal in a certain namespace does not have this problem:
namespace Foo;
new Baz; // can't be moved, it's unequivocally \Foo\Baz
Therefore, all "string class names" are always absolute and need to be written as FQN:
$class = 'Foo\Baz';
(Note: no leading \.)
You can use this as shorthand, sort of equivalent to a self-referential self in classes:
$class = __NAMESPACE__ . '\Baz';
I am new to PHP. I am just trying to understand PHP namespace and stuck at following point of changing the class name, I am referring this article titled How to use PHP namespace. And was referring following code on that page.
<?php
namespace App\Lib1;
class MyClass {
public function WhoAmI() {
return __METHOD__;
}
}
$c = __NAMESPACE__ . '\\MyClass';
$m = new $c;
echo $m->WhoAmI(); // outputs: App\Lib1\MyClass::WhoAmI
?>
In the above code, I guess, they have created Object like,
$c = __NAMESPACE__ . '\\MyClass';
$m = new $c;
I guess object can be created simple in above case as,
$m = new MyClass();
And on the same page, in other code the object has been created using code,
$m = new namespace\MyClass;
Now, if I compare all three above, I am finding two method of creating class name 1. $m = new MyClass(); and 2. $m = namespace\MyClass;. I am wondering what is the difference and purpose of using $m = namespace\MyClass;? Very thank in Advance.
The biggest advantage of using namespaces in PHP is for when you have multiple classes with the same name. Without namespacing, a developer would need to worry that by creating a class MyClass, there are thousands of other third-party libraries that could also have a MyClass.
Let's say you wanted to create a class called DateTime. If you just wrote the function without a namespace, it would conflict with PHP's DateTime class. By declaring a namespace, you segragate your code.
<?php
$myObject = DateTime(); // This statement creates an object using PHP's implementation of DateTime
namespace Foo\DateTime;
use Foo\DateTime as DateTime;
$myObject = DateTime(); // This statement will use the custom class from the Foo\DateTime namespace
$myObject = PHP\DateTime(); // This statement will use PHP's class even though you're using the Foo\DateTime namespace
?>
The second advantage is for code conciseness. If you use the Foo\DateTime namespace shown above, you don't have to use the fully qualified name when you create custom objects from your class. This is useful when you have to create a large number of instances of your custom class.
When you use the namespace like so, you can write it as an alias instead of a fully qualified name:
use Foo\DateTime as DateTime;
$myObject = new DateTime();
Without using namespaces, you would need to create the object like so, if you wanted to use your DateTime class instead of PHP's DateTime class:
$myObject = Foo\DateTime();
I suppose there are no any essential differences between:
$m = new MyClass(); and $m = new namespace\MyClass;
It can help you to explicitly indicate which one of two classes with the same name you use:
<?php
namespace Foo;
class MyClass {
public function WhoAmI() {
return __METHOD__;
}
}
?>
<?php
namespace Bar;
class MyClass {
public function WhoAmI() {
return __METHOD__;
}
}
?>
<?php
namespace Bar;
use Foo;
$obj1 = new MyClass;
var_dump($obj1->WhoAmI()); //Bar\MyClass::WhoAmI
$obj2 = new namespace\MyClass;
var_dump($obj2->WhoAmI()); //Bar\MyClass::WhoAmI
$obj3 = new Foo\MyClass;
var_dump($obj3->WhoAmI()); //Foo\MyClass::WhoAmI
I think it is better to use aliasing in this case.
I'm creating a factory class in my project, where this class gets a Report Type as a string. This string has the name of the concrete class that implements a Report Interface.
The issue I'm having is that when I'm instantiating this class, I get a Class not found error.
Here follows the factory code.
namespace App\Term\Reports;
class Factory
{
public static function build($type)
{
$obj = new CableBySensor(); // Works!
// $type == 'CableBySensor'
$obj2 = new $type; // Class not found :(
// ... validates if the class exists ...
// ... and if it implements the Report Interface ...
// ... throw exception if class doesn't exist or doesn't implements interface
// ... then returns the corresponding object.
}
}
Both methods are virtually the same thing.
First: Why do I have to specify the full qualified name of the class in the string to make it work? The class CableBySensor resides in the same namespace as Factory.
This started giving me trouble because I also want to validate that the class being instantiated implements a ReportsInterface.
Second: How do I overcome this? Should I call the factory like this $myReport = Factory::build('App\Term\Reports\' . $className); or should I use the __NAMESPACE__ constant inside the Factory class such as this: $obj = new __NAMESPACE__ . '\' . $className?
Thank you.
Indifferently whether the factory approach is useful here or not,
the problem is with trying to instantiate from a dynamic variable.
Or as akhoondi at php.net pointed out:
One must note that when using a dynamic class name [...] the "current namespace" [...] is global namespace.
There are possibly 3 solutions:
pass the fully qualified class name to your factory method (arghh...)
$instance = Factory::build('Acme\CableBySensor');
Or, do a check in your build method and prefix the namespace if necessary (as suggested here) (sounds not so fool proof to me)
public static function build($type)
{
if ($type[0] !== '\\') {
$type = '\\' . __NAMESPACE__ . '\\' . $type;
}
$obj = new $type;
...
}
Or, if you have PHP 5.5+ why not use class name resolution via ::class?
Personally, I would go for that one whenever possible:
$instance = Factory::build(CableBySensor::class);
So I created these two classes
//Quarter.php
namespace Resources;
class Quarter {
...
}
//Epoch.php
namespace Resources;
class Epoch {
public static function initFromType($value, $type) {
$class = "Quarter";
return new $class($value, $type);
}
}
Now this is a a very simplified version of both, but is enough to illustrate my question. The classes as they are shown here will not work as it will not find the Quarter class. To make it work I could change the $class variable to
$class = "\Resources\Quarter";
So my question is: Why do I need to use the namespace here when both classes are already members of the same namespace. The namespace is only needed when I put the classname in a variable so doing:
public static function initFromType($value, $type) {
return new Quarter($value, $type);
}
will work without problems. Why is this and is there any potential traps here I need to avoid?
Because strings can be passed around from one namespace to another. That makes name resolution ambiguous at best and easily introduces weird problems.
namespace Foo;
$class = 'Baz';
namespace Bar;
new $class; // what class will be instantiated?
A literal in a certain namespace does not have this problem:
namespace Foo;
new Baz; // can't be moved, it's unequivocally \Foo\Baz
Therefore, all "string class names" are always absolute and need to be written as FQN:
$class = 'Foo\Baz';
(Note: no leading \.)
You can use this as shorthand, sort of equivalent to a self-referential self in classes:
$class = __NAMESPACE__ . '\Baz';
How can I retrieve a class namespace automatically?
The magic var __NAMESPACE__ is unreliable since in subclasses it's not correctly defined.
Example:
class Foo\bar\A -> __NAMESPACE__ === Foo\bar
class Ping\pong\B extends Foo\bar\A -> __NAMESPACE__ === Foo\bar (it should be Ping\pong)
ps: I noticed the same wrong behavior using __CLASS__, but I solved using get_called_class()... is there something like get_called_class_namespace()? How can I implement such function?
UPDATE:
I think the solution is in my own question, since I realized get_called_class() returns the fully qualified class name and thus I can extract the namespace from it :D
...Anyway if there is a more effective approach let me know ;)
The namespace of class Foo\Bar\A is Foo\Bar, so the __NAMESPACE__ is working very well. What you are looking for is probably namespaced classname that you could easily get by joining echo __NAMESPACE__ . '\\' . __CLASS__;.
Consider next example:
namespace Foo\Bar\FooBar;
use Ping\Pong\HongKong;
class A extends HongKong\B {
function __construct() {
echo __NAMESPACE__;
}
}
new A;
Will print out Foo\Bar\FooBar which is very correct...
And even if you then do
namespace Ping\Pong\HongKong;
use Foo\Bar\FooBar;
class B extends FooBar\A {
function __construct() {
new A;
}
}
it will echo Foo\Bar\FooBar, which again is very correct...
EDIT: If you need to get the namespace of the nested class within the main that is nesting it, simply use:
namespace Ping\Pong\HongKong;
use Foo\Bar\FooBar;
class B extends FooBar\A {
function __construct() {
$a = new A;
echo $a_ns = substr(get_class($a), 0, strrpos(get_class($a), '\\'));
}
}
In PHP 5.5, ::class is available which makes things 10X easier. E.g.
A::class
Use Reflection class.
$class_name = get_class($this);
$reflection_class = new \ReflectionClass($class_name);
$namespace = $reflection_class->getNamespaceName();