getting Classes from variables in namespace - php

I'm trying to update my project with namespaces.
Before using namespaces I could easily call a class via a variable like so:
<?php
$className = "Item";
$myClass = new $className();
?>
Now, when using namespaces (with use) I'd expect this to work:
(not working allways meens: 'Can't find class' or smth like this)
<?php
namespace myProject
use myProject\Models
$className = "Item";
$myClass = new $className(); // this class has the namespace myProject\Models
// this doesn't work! (can't find class)
$myClass = new Models\$className();
// nope, doesn't work either
$myClass = new myProject\Models\$className();
// nope
?>
but calling the same class directly - without a variable - IS working
$myClass = new Item();
What I have to do, to make it work is:
<?php
namespace myProject;
use myProject\Models; // unnessecary now
$className = "\\" . __NAMESPACE__ . "\\Models\\" . "Item";
$myClass = new $className();
?>
My question now is:
If I want to call a class via a variable, do I really have to include the whole namespace as a fully qualified name?
EDIT:
I found myself an answer here:
Dynamic namespaced class with alias
I'll keep that question anyway, because I could not find the right answer in first place.

Related

What is difference between naming Class name as MyClass and namespace\Myclass in PHP?

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.

PHP OOP - Autoinitiate objects

<?php function __autoload($class_name)
{
include_once 'inc/classes/class.' . $class_name . '.inc.php';
}
?>
Right now im using __autoload to automatic load up my classes whenever they are used. But i was thinking, why not automatic initiate the object themself as well, so you dont have to start the object in the pages itself, you could just call the properties of an class without starting the object.
But here i am stuck, i thought i could just do like the example below but its not working, objects are not starting.
<?php
function __autoload($class_name)
{
include_once 'inc/classes/class.' . $class_name . '.inc.php';
'$'.$class_name = new $class_name;
}
?>
This does not make sense. As you say, autoloading happens when the class is referenced.
How is it referenced? Let's see:
With $object = new TheClassName() - there you already have your instance. Why create another one automagically?
With static method calls, access to static properties or constants TheClassName::I_NEED_THIS_CONSTANT - why would I need an automagically created instance if I access a static method/property/constant?
With calls to class_exists() - why would I need an automagially created instance if I just want to check if the class exists?
Try this:
$$class_name = new $class_name();
You can also try using {}. In your example it will be:
${$class_name} = new $class_name;
With ${} you can create dynamic variables like that:
$i = '1';
${'tmp' . $i} = 'Hello world';
echo $tmp1; // Hello world
You should also use spl_autoload_register instead of __autoload (because it can be deprecated or removed in the future).

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

zend framework2 how does the autoload function work

recently I was learning zend framework 2, and there's a problem annoying me for a long time, things look like this:
<?php
namespace Album\Model;
// Add these import statements
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilterInterface;
class Album implements InputFilterAwareInterface
{
public $id;
public $artist;
public $title;
protected $inputFilter;
public function exchangeArray($data)
{
$this->id = (isset($data['id'])) ? $data['id'] : null;
$this->artist = (isset($data['artist'])) ? $data['artist'] : null;
$this->title = (isset($data['title'])) ? $data['title'] : null;
}
// Add content to these methods:
public function setInputFilter(InputFilterInterface $inputFilter)
{
throw new \Exception("Not used");
}
//....
?>
This code was a section of the "skeleton application" programme, which was a tutorial of ZF2. The first time I see the programme, I don't understand what's the usage of "namespace" and "use", because this two keyword doesn't exist in php5.2(also the same in the earlier edition), so I go to see the manual and try to understand it.I write a programme to simulate what really happens:
<?php
use script\lib\test;
$o = new test();
echo $o->getWelcome();
function __autoload( $className ) {
$classname = strtolower( $classname );
require_once( dirname( __FILE__ ) . '/' . $classname . '.php' );
}
?>
the programme above works well, of course I created two folders named script and lib, and there's a file named test.php.
Seems like every thing is clear, zend framework also has a autoload function, BUT when I noticed the codes in "skeleton application programme", there was a namespace in the beginning, so I adds the namespace to my programme too:
<?php
namespace test;
use script\lib\test;
$o = new test();
echo $o->getWelcome();
function __autoload( $className ) {
$classname = strtolower( $classname );
require_once( dirname( __FILE__ ) . '/' . $classname . '.php' );
}
?>
the page returned me inforamtion as following:
Fatal error: Class 'script\lib\test' not found in E:\wamp\www\test\test_29.php on line 6
I tried to change the namespace's name such as script\lib, script\lib\test...
but it's useless.
Any answer will be appreciated, thanks.
Now I will give you more details about this issue:
To understand the usage of "namespace" and "use", I looked over the materials on php.net:
http://php.net/manual/en/language.namespaces.importing.php
In this page, there was a section of code looks like this:
Example #1 importing/aliasing with the use operator
<?php
namespace foo;
use My\Full\Classname as Another;
// this is the same as use My\Full\NSname as NSname
use My\Full\NSname;
// importing a global class
use ArrayObject;
$obj = new namespace\Another; // instantiates object of class foo\Another
$obj = new Another; // instantiates object of class My\Full\Classname
NSname\subns\func(); // calls function My\Full\NSname\subns\func
$a = new ArrayObject(array(1)); // instantiates object of class ArrayObject
// without the "use ArrayObject" we would instantiate an object of class
?>
Now let's review the programme I write in the above:
<?php
namespace test;
use script\lib\test;
$o = new test();
echo $o->getWelcome();
function __autoload( $className ) {
$classname = strtolower( $classname );
require_once( dirname( __FILE__ ) . '/' . $classname . '.php' );
}
?>
It's the same, I'm trying to simulate that instance, if we don't use the autoload function:
<?php
namespace test;
use script\lib\test;
require_once 'script/lib/test.php';
$o = new test();
echo $o->getWelcome();
?>
It works well too, BUT when I use __autoload function to load the class file, there's something wrong.
I don't konw where's problem, OR any body tried to write an instance to put the "Example #1" into practice? I will wait for your answer.
I think you're misunderstanding what's going on here.
Namespaces allow you to, more or less, create "directories" for your classes. So you can create the \Foo class and the \Test\Foo class (where \ represents the "root" of your application).
The way autoloading works is that your files mirror your namespacing. So foo.php would be in the root of your autoloading but you would create /test/foo.php for \Test\Foo
The use keyword has two uses. One is to alias class files and the other is, in PHP 5.4 or later, to bring in a Trait into your current class.
Now, to your question. First, Let's look at your code
<?php
namespace test;
use script\lib\test;
$o = new test();
echo $o->getWelcome();
This is confusing. You declare a namespace (which you don't need to do here) but then you alias it to script\lib\test. PHP is now looking for a file called /script/lib/test.php, which your error message says doesn't exist. But you said the file does exist so let's look at that
public function getWelcome() {
return 'welcome';
}
This isn't a class. It's a function. For this example you need a complete class
<?php
namespace script\lib;
class test {
public function getWelcome() {
return 'welcome';
}
}
Lastly, let's talk autoloading. You don't need to use use with autoloading. Your autoloader should take care of that for you. You should, however, use spl_autoload_register(), as __autoload() is soon to be depreciated.
From ZF2 docu
Zend\Loader\StandardAutoloader is designed as a PSR-0-compliant autoloader. It assumes a 1:1 mapping of the namespace+classname to the filesystem, wherein namespace separators and underscores are translated to directory separators.
Read more about: PSR-0
So if you're using namespaces the classname that gets send to the autoloader doesn't look like test. It looks like YOUR_NAMESPACE\test. YOUR_NAMESPACE is the namespace that you defined in the class with namespace YOUR_NAMESPACE;
PSR-0 is a standard that says: Your namespace should reflect your filesystem. You only have to replace the backslashes with forward slashes. Or _ with / if you're using pseudo namespaces like in ZF1. (Album_Model_Album)
So output the $className that is sent to your autoloader and you will see..

Shorter way to instantiation PHP class

Say I have a class Core() where it will give me the instance of different classes depending on some initialization. Say that after initiating it, I get some class and then want to instantiate that. This is what I do:
$core = new Core();
// $core is further initiated
$api = $core->getClass(); // This returns, for instance class Library_MyClass
$class = new $api();
Is there a way to combine the last two steps into one? So for instance I say something like $class = new $core->getClass()()? Obviously what I wrote is wrong, but that is sort of what I want! Is that possible?
If this is some form of a factory you could do something like:
class Core
{
public function getClass()
{
return new Library_MyClass();
}
}
$core = new Core();
$class = $core->getClass();
However considering the name of the class Core I suspect you may be violating some SOLID principles here.
You might want to benefit from the use of dependency injection (considering a slight update applied to PeeHaa example)
class Core
{
public function getClass($api)
{
return new $api();
}
}
$core = new Core();
$apiInstance = $core->getClass('Library_MyClass');
This way you won't have to update the Core class whenever you would like it to provide you with an instance of another library class.

Categories