Initializing object with name vs variable - php

(Symfony framework is used) I am trying to initialize object like this
$token = new $provider; where $provider is Dropbox and I am getting this exception
Attempted to load class "Dropbox" from the global namespace.
Did you forget a "use" statement?
But if I change the code to $token = new Dropbox; then it works, where is the problem ?

Even if you have declared the Dropbox class in a use statement, when creating a class in that method, you need to have the FQDN of the class as the string.
Example;
$provider = '\MyBundle\Classes\Provider\Dropbox';
$token = new $provider;

Worth reading http://php.net/manual/en/language.namespaces.dynamic.php
[...] there is no difference between a qualified and a fully qualified
Name inside a dynamic class name, function name, or constant name [...]
When you reference a classname by a string, you need to define it with fully-qualified namespace.
Prepend it with namespace where the class belong,
$class = 'Provider\Namespace\' . $provider;
$class = new $class;
Don't also forget to escape the backslash if you prefer double quote
$class = "Provider\\Namespace\\$provider";
$class = new $class;
If it does exists within the same namespace, simply prepend php predefined constant
$class = __NAMESPACE__ . '\' . $provider;

Related

Creating objects dynamically in PHP [duplicate]

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

Object creation from class name not working within namespace

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

namespace instantiation through variable

namespace user;
use robot\r;
$namespace = 'r\someClass';
$class = new $namespace(); // does not work
$namespace = '\robot\r\someClass';
$class = new $namespace(); // does work
Why doesn't this work as expected?
The reason I am using a variable is b/c "someClass" isn't known ahead of time.
So the code looks like this:
if ( $class == 'someClass' )
{
$namespace = 'r\someClass';
}
elseif ( $class == 'someOtherClass' )
{
$namespace = 'r\someOtherClass';
}
$class = new $namespace();
This is easy to work around, but I don't understand why:
$class = new r\someClass() will work
and $class = new $namespace() will not work.
Updated:
When you use dynamic class name, you have to include the namespace name.
So the below will work:
namespace user;
use robot\r; // use is not necessary when you use dynamic class name.
$namespace = 'robot\r\someClass'; // for a dynamic class name, namespace is required.
$class = new $namespace();
Note the leading slash is not necessary, because there is no difference between a qualified and a fully qualified Name inside a dynamic class name, function name, or constant name.
Check the document here.
Should be able to use #xdazz answer, but you can also alias the namespace. The reason it is failing is you have to full quality namespace path.
use robot\r as r;
$classname = 'r\someClass';
without the
as r
part you have to fully qualify the path later.
I stumbled upon the same issue a few minutes ago - should've looked in here ealier :)
unfortunately i can't comment yet so here's a tiny hint that you can also use the __NAMESPACE__ constant instead of retyping the whole qualified namespace when your class is relative to the current namespace...
In my case i have a small factory method:
public function getService($name)
{
$className = __NAMESPACE__ . '\Service\\' . $name;
return new $className();
}

Using namespaces with classes created from a variable

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

Create new instance of object based on variable

I'm trying to create an instance of an object but the object class name is set by hand.
in config.php:
define('DIRECTORY', 'RaptorDirectory');
in the class file:
$this->directory = new DIRECTORY; // <--- how do I use the constant there?
I'm doing this because DIRECTORY might change to a different class (e.g., LDAPDirectory)
You can't use a constant there but you can use a variable, eg
$class = DIRECTORY;
$this->directory = new $class;

Categories