Create new instance of object based on variable - php

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;

Related

How to see if a class is an instance of another class without creating either class?

I have two classes.
I want to check if a class is an instance of another class without initializing either of them.
With the function is_subclass_of and when using instanceOf you have to initialize the sub class.
<?php
$classPath = 'app/foo/bar';
$subClassPath = 'app/foo/foo'; // Inherits from $classPath
// This is what I want to do but doesn't work.
echo $subClassPath instanceOf $classPath;
// Works
echo (new $subClassPath()) instanceOf $classPath;
I found the problem.
is_subclass_of requires your slashes to go the other way.
$classPath = 'app\foo\bar';
$subClassPath = 'app\foo\foo'; // Inherits from $classPath
// Works
echo is_subclass_of($subClassPath $classPath);

Initializing object with name vs variable

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

How can I name a fully-qualified type using a variable?

If I have a class name in $name, how can I create an object of type \folder\$name? Ideally I'd like to interpolate the $name so that I can create the object with just a single line of code.
The following doesn't seem to work:
$obj = new \folder\$name();
The problem is youre trying to use a variable as part of a FQCN. You cannot do that. The FQCN can be a variable itself like:
$fqcn = '\folder\classname';
$obj = new $fqcn();
Or you can delcare the namespace at the top of the file:
namespace folder;
$fqcn = 'classname';
$obj = new $fqcn;
Or if the file belongs to another namespace you can use the class to "localize" it:
namespace mynamespace;
use folder\classname;
$fqcn = 'classname';
$obj = new $fqcn();
A more concrete example of something i assume to be similar to what you are trying to do:
namespace App\WebCrawler;
// any local uses of the File class actually refer to
// \App\StorageFile instead of \App\WebCrawler\File
use App\Storage\File;
// if we didnt use the "as" keyword here we would have a conflict
// because the final component of our storage and cache have the same name
use App\Cache\File as FileCache;
class Client {
// the FQCN of this class is \App\WebCrawler\Client
protected $httpClient;
protected $storage;
protected $cache
static protected $clients = array(
'symfony' => '\Symfony\Component\HttpKernel\Client',
'zend' => '\Zend_Http_Client',
);
public function __construct($client = 'symfony') {
if (!isset(self::$clients[$client])) {
throw new Exception("Client \"$client\" is not registered.");
}
// this would be the FQCN referenced by the array element
$this->httpClient = new self::$clients[$client]();
// because of the use statement up top this FQCN would be
// \App\Storage\File
$this->storage = new File();
// because of the use statement this FQCN would be
// \App\Cache\File
$this->cache = new FileCache();
}
public static function registerHttpClient($name, $fqcn) {
self::$clients[$name] = $fqcn;
}
}
You can read in more detail here: http://php.net/manual/en/language.namespaces.dynamic.php
Shouldn't it be
new \folder\$arr[0];
and not
new \folder\$arr[0]();
Also, I'm not too familiar with PHP, and I've never see this syntax before. What I suggest is:
namespace \folder;
$obj = new $arr[0];
I'm not sure if you can do it with just one line and without namespaces.

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

Getting file paths of subclasses

To get the name of a subclass, I use get_called_class().
What should I use to get the file path of a subclass?
Use Reflection, specifically ReflectionClass::getFileName(). How you instantiate your ReflectionClass depends entirely on the current scope, eg
// globally
$reflectionClass = new ReflectionClass('SubClassName');
// within the sub class
$reflectionClass = new ReflectionClass(__CLASS__);
// within either sub or parent class in a static method
$reflectionClass = new ReflectionClass(get_called_class());
// within either sub or parent class, provided the instance is a sub class
$reflectionClass = new RelfectionObject($this);
// filename
$fn = $reflectionClass->getFileName();
// what I assume you mean by "path"
$path = dirname($fn);

Categories