Object creation from class name not working within namespace - php

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

Related

php: class not found (dynamically created instance) [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';

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

laravel 5.1 - Dynamically create Class object based on string

i want to create object of class base on string which come from URL parameter.
for example :
http://localhost/CSWeb/api/search/Slideshare
in above URL Slideshare is parameter which get in apiController->indexAction.
slideshare.php class
<?php
namespace App\Http\API;
class slideshare
{
public function index()
{
return 'any data';
}
}
apiController.php
namespace App\Http\Controllers\API;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use Auth;
use App\Http\API\Slideshare;
class apiController extends Controller
{
public function index($source)
{
$controller= new $source;
return $controller->index();
// if i change code to $controller= new Slideshare; it works fine
}
}
laravel error when i use parameter string to create class object
FatalErrorException in apiController.php line 17: Class
'Slideshare' not found
if i change code to
$controller= new Slideshare; it works fine
Thank you in advance
When creating PHP objects with strings, you must provide the full qualified name of the class (a.k.a include the namespace). So, you should have something like this:
$className = 'App\\Http\\API\\' . $source;
$controller = new $className;
return $controller->index();
Another way to do it, if you are sure that the class you want to instantiate lives in the same namespace as your code, you can use:
$className = __NAMESPACE__ . '\\' . $source;
$controller = new $className;
return $controller->index();
A more elaborated way of achieving the same results is through the Factory Design Pattern. Basically you create a class that is responsible for instantiating elements, and you delegate the task of actually creating those objects to that class. Something along those lines:
class Factory {
function __construct ( $namespace = '' ) {
$this->namespace = $namespace;
}
public function make ( $source ) {
$name = $this->namespace . '\\' . $source;
if ( class_exists( $name ) ) {
return new $name();
}
}
}
$factory = new Factory( __NAMESPACE__ );
$controller = $factory->make( $source );
The advantage of this approach is that the responsability of creating the objects now lies in the Factory, and if you ever need to change it, maybe allow for aliases, add some additional security measures, or any other thing, you just need to change that code in one place, but as long as the class signature remains, your code keeps working.
An interesting tutorial on factories:
http://culttt.com/2014/03/19/factory-method-design-pattern/
Source:
http://nl3.php.net/manual/en/language.oop5.basic.php#language.oop5.basic.new
http://php.net/manual/en/language.namespaces.nsconstants.php

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

Get PHP class namespace dynamically

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

Categories