Dynamic namespaced class with alias - php

SO,
I have an issue with dynamic object creation using namespaces.
Here's namespace code:
namespace Foo
{
class Bar
{
}
}
Now, I'm trying to create object of class Bar with:
include('namespace.php');
$sName = 'Bar';
$sClass = '\\Foo\\'.$sName;
$rObj = new $sClass; //correct object
and everything going well with that. But, now I want to use alias and doing something like:
include('namespace.php');
use Foo as Baz;
$sName = 'Bar';
$sClass0= '\\Foo\\'.$sName;
$sClass1= '\\Baz\\'.$sName;
$rObj = new $sClass0; //correct object
$rObj = new $sClass1; //Fatal error
And I'm unable to instantiate an object such way (and accessing via full name still works well).
So, my question is - is it possible to access the class via alias somehow, and, if yes, how? I've also tried to access when using $sClass1='Baz\\'.$sName - no success. Also, I've checked declared classes via get_declared_classes() function, it shows that I have only \Foo\Bar class (no reference to an alias).
I'm not sure if it matters, but I'm using PHP 5.5 version.

Only the parser uses your namespace aliases to canonicalize the class references inside each of your files.
In other words, it doesn't introduce some kind of global alias that other code can use. Once your script has been parsed, the alias is no longer used.
This behaviour is also described in the manual:
Importing is performed at compile-time, and so does not affect dynamic class, function or constant names.

Related

'use' directive while using ReflectionClass in symfony app

I have a class in my symfony 3 app with a method that should instantiate a Model class dynamically using the name that's passed in to the function, i.e.:
static function getInstance($modelName){
use $modelName;
$r = new \ReflectionClass($modelName);
return $r->newInstanceArgs();
}
But there's a syntax error on the use directive.
I've tried adding specific use statements for every class at the top of the file; and I've also tried using require with a fully qualified path to the corresponding PHP file - but neither approach has worked.
Please advise how to correctly do this.
What about this? use qualified class name
static function getInstance($modelName){
$r = new \ReflectionClass('AppBundle\Entity\\' . $modelName);
return $r->newInstanceArgs();
}
Also, per PHP USE Documentation:
Scoping rules for importing
The use keyword must be declared in the outermost scope of a file (the global scope) or inside namespace declarations. This is because the importing is done at compile time and not runtime, so it cannot be block scoped. The following example will show an illegal use of the use keyword:
Example #5 Illegal importing rule
namespace Languages;
function toGreenlandic() {
use Languages\Danish;
}

Instantiate a PHP stdClass in my namespace – fatal error

I want to write a small add on for an existing CMS. To do so, I need to extend a class from that CMS' code.
My code will be written inside its own namespace, while the CMS' code does not use namespacing, which basically means it exists inside the global namespace.
Inside my code, I create a new stdClass:
$var = new stdClass();
With that code is place, it always produces a fatal error:
Fatal error: Class 'MyNamespace\something\StdClass' not found in /some/rather/long/path/to/class.php on line 123
Creating the stdClass like this solves that problem:
$var = new \stdClass();
Since I am still pretty new to namespaces, I am not exactly sure what the problem here is?
My guess is that in the first example, the stdClass would be created in the namespace of my class. This actually means the constructor of a class called stdClass existing in my namespace would be called, but since that class does not exist, an error is thrown.
In the second example, I signalize that I want to instantiate the class called stdClass from the global namespace, which somehow suddenly makes sense.
If anyone could elaborate what is happening here I would be very happy.
You appear to understand the concept behind namespaces, and you are headed in the right direction on your analysis of what is happening.
When you are working inside of a namespace you are able to refer to names as unqualified, qualified, and fully qualified.
When you make a namespace you are telling PHP to organize (and resolve) the names of your classes, function, methods, etc. away from the same scope where the built-in PHP code lives along with any other code behind its own namespace. It is away to organize your code and avoid naming collisions among libraries and built-in PHP functions.
Here is a brief on how names get resolved:
If you are trying to resolve a name within the same namespace you can use the unqualified name. So for class \Foo\Bar\Baz you can use new Baz(); as long as you are in namespace \Foo\Bar.
If you are trying to resolve a name that is lower in the same parent namespace you can use the qualified name. So for class \Foo\Bar\Baz you would need to use new Bar\Baz(); if you were in namespace \Foo.
If you are trying to resolve a name that is not in your namespace or is in the global namespace (built-in PHP stuff) then you must use the fully qualified name. If you are in namespace \Foo\Bar and you want to make use of something like the mysqli class you would need to call it by its fully qualified name. e.g. new \mysqli() Your question above is a perfect example that illustrates this. Likewise, if you need to access a class in a totally different namespace you would also need the fully qualified name: new \Third\Party\AppClass();
So to summarize, you are right, the built-in stdClass does not exist in your namespace, therefore you need to access it by the fully qualified name. The reason you must do thing this way had to do with conforming to the rules PHP uses when resolving names.
If you ever need to find out what namespace you are in it will be in the __NAMESPACE__ constant.
In case you haven't already read it, here is the documentation on name resolution in PHP: http://php.net/manual/en/language.namespaces.rules.php
The code is evaluated in your namespace and stdClass doesn't exist there. You are effectively answering your own question with your guess.
Your guess is correct. Look at the comments in "class references" section of Example #1 on this page.
http://php.net/manual/en/language.namespaces.rules.php

How does the keyword "use" work in PHP and can I import classes with it?

I have a file with a class Resp. The path is:
C:\xampp\htdocs\One\Classes\Resp.php
And I have an index.php file in this directory:
C:\xampp\htdocs\Two\Http\index.php
In this index.php file I want to instantiate a class Resp.
$a = new Resp();
I know I can use require or include keywords to include the file with a class:
require("One\Classes\Resp.php"); // I've set the include_path correctly already ";C:\xampp\htdocs". It works.
$a = new Resp();
But I want to import classes without using require or include. I'm trying to understand how use keyword works. I tried theses steps but nothing works:
use One\Classes\Resp;
use xampp\htdocs\One\Classes\Resp;
use htdocs\One\Classes\Resp;
use One\Classes;
use htdocs\One\Classes; /* nothing works */
$a = new Resp();
It says:
Fatal error: Class 'One\Classes\Resp' not found in C:\xampp\htdocs\Two\Http\index.php
How does the keyword use work? Can I use it to import classes?
No, you can not import a class with the use keyword. You have to use include/require statement. Even if you use a PHP auto loader, still autoloader will have to use either include or require internally.
The Purpose of use keyword:
Consider a case where you have two classes with the same name; you'll find it strange, but when you are working with a big MVC structure, it happens. So if you have two classes with the same name, put them in different namespaces. Now consider when your auto loader is loading both classes (does by require), and you are about to use object of class. In this case, the compiler will get confused which class object to load among two. To help the compiler make a decision, you can use the use statement so that it can make a decision which one is going to be used on.
Nowadays major frameworks do use include or require via composer and psr
1) composer
2) PSR-4 autoloader
Going through them may help you further.
You can also use an alias to address an exact class. Suppose you've got two classes with the same name, say Mailer with two different namespaces:
namespace SMTP;
class Mailer{}
and
namespace Mailgun;
class Mailer{}
And if you want to use both Mailer classes at the same time then you can use an alias.
use SMTP\Mailer as SMTPMailer;
use Mailgun\Mailer as MailgunMailer;
Later in your code if you want to access those class objects then you can do the following:
$smtp_mailer = new SMTPMailer;
$mailgun_mailer = new MailgunMailer;
It will reference the original class.
Some may get confused that then of there are not Similar class names then there is no use of use keyword. Well, you can use __autoload($class) function which will be called automatically when use statement gets executed with the class to be used as an argument and this can help you to load the class at run-time on the fly as and when needed.
Refer this answer to know more about class autoloader.
use doesn't include anything. It just imports the specified namespace (or class) to the current scope
If you want the classes to be autoloaded - read about autoloading
Don’t overthink what a Namespace is.
Namespace is basically just a Class prefix (like directory in Operating System) to ensure the Class path uniqueness.
Also just to make things clear, the use statement is not doing anything only aliasing your Namespaces so you can use shortcuts or include Classes with the same name but different Namespace in the same file.
E.g:
// You can do this at the top of your Class
use Symfony\Component\Debug\Debug;
if ($_SERVER['APP_DEBUG']) {
// So you can utilize the Debug class it in an elegant way
Debug::enable();
// Instead of this ugly one
// \Symfony\Component\Debug\Debug::enable();
}
If you want to know how PHP Namespaces and autoloading (the old way as well as the new way with Composer) works, you can read the blog post I just wrote on this topic: https://enterprise-level-php.com/2017/12/25/the-magic-behind-autoloading-php-files-using-composer.html
You'll have to include/require the class anyway, otherwise PHP won't know about the namespace.
You don't necessary have to do it in the same file though. You can do it in a bootstrap file for example. (or use an autoloader, but that's not the topic actually)
The issue is most likely you will need to use an auto loader that will take the name of the class (break by '\' in this case) and map it to a directory structure.
You can check out this article on the autoloading functionality of PHP. There are many implementations of this type of functionality in frameworks already.
I've actually implemented one before. Here's a link.
I agree with Green, Symfony needs namespace, so why not use them ?
This is how an example controller class starts:
namespace Acme\DemoBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class WelcomeController extends Controller { ... }
Can I use it to import classes?
You can't do it like that besides the examples above. You can also use the keyword use inside classes to import traits, like this:
trait Stuff {
private $baz = 'baz';
public function bar() {
return $this->baz;
}
}
class Cls {
use Stuff; // import traits like this
}
$foo = new Cls;
echo $foo->bar(); // spits out 'baz'
The use keyword is for aliasing in PHP and it does not import the classes. This really helps
1) When you have classes with same name in different namespaces
2) Avoid using really long class name over and over again.
Using the keyword "use" is for shortening namespace literals. You can use both with aliasing and without it. Without aliasing you must use last part of full namespace.
<?php
use foo\bar\lastPart;
$obj=new lastPart\AnyClass(); //If there's not the line above, a fatal error will be encountered.
?>
Namespace is use to define the path to a specific file containing a class e.g.
namespace album/className;
class className{
//enter class properties and methods here
}
You can then include this specific class into another php file by using the keyword "use" like this:
use album/className;
class album extends classname {
//enter class properties and methods
}
NOTE: Do not use the path to the file containing the class to be implements, extends of use to instantiate an object but only use the namespace.

Can't get constant from dynamic class using namespaces

I'm not able to get a constant from a class which is defined by using a string variable and PHP 5.3. namespaces. Example:
use \Some\Foo\Bar;
$class = 'Bar';
echo $class::LOCATION;
where LOCATION is a properly defined constant. The error I get says class Bar is undefined.
If I instead do
$class = "\Some\Foo\Bar";
everything works fine.
Is there anyway to make the first example work?
Using $class::CONST to get a class constant, it is required that $class contains a fully qualified classname independent to the current namespace.
The use statement does not help here, even if you were in the namespace \Some\Foo, the following would not work:
namespace \Some\Foo;
$class = 'Bar';
echo $class::LOCATION;
As you have already written in your question, you have found a "solution" by providing that fully qualified classname:
use \Some\Foo\Bar;
$class = "\Some\Foo\Bar";
echo $class::LOCATION;
However, you dislike this. I don't know your specific problem with it, but generally it looks fine. If you want to resolve the full qualified classname of Bar within your current namespace, you can just instantiate an object of it and access the constant:
use \Some\Foo\Bar;
$class = new Bar;
echo $class::LOCATION;
At this stage you do not even need to care in which namespace you are.
If you however for some reason need to have a class named Bar in the global namespace, you can use class_alias to get it to work. Use it in replace of the use \Some\Foo\Bar to get your desired behaviour:
class_alias('\Some\Foo\Bar', 'Bar');
$class = 'Bar';
echo $class::LOCATION;
But anyway, I might not get your question, because the solution you already have looks fine to me. Maybe you can write what your specific problem is.

PHP namespace with Dynamic class name

Wondering if anyone else has encountered this problem when utilizing the new ability to namespace classes using PHP 5.3.
I am generating a dynamic class call utilizing a separate class for defining user types in my application. Basically the class definer takes an integer representation of types and interprets them, returning a string containing the classname to be called as the model for that user.
I have an object model for the user's type with that name defined in the global scope, but I have another object with the same name for the user's editor in the Editor namespace. For some reason, PHP won't allow me to make a namespaced dynamic call as follows.
$definition = Definer::defineProfile($_SESSION['user']->UserType);
new \Editor\$definition();
The identical syntax works for calling the global basic object model in the global namespace and I use it this way reliably throughout the application.
$definition = Definer::defineProfile($_SESSION['user']->UserType);
new $definition();
This will correctly call the dynamically desired class.
Is there a reason the two would behave differently, or has dynamic calling for namespaces not been implemented in this manor yet as this is a new feature? Is there another way to dynamically call a class from another namespace without explicitly placing its name in the code, but from within a variable?
Well, just spell out the namespace in the string:
$definition = Definer::defineProfile($_SESSION['user']->UserType);
$class = '\\Editor\\' . $definition;
$foo = new $class();
And if it's a child namespace (as indicated in the comments), simply prepend the namespace with __NAMESPACE__:
$class = __NAMESPACE__ . '\\Editor\\' . $definition;
So if the current namespace is \Foo\Bar, and $definition is "Baz", the resulting class would be \Foo\Bar\Editor\Baz

Categories