Unrecognized namespace alias causing unexpected invocation of autoloader - php

I am attempting use a namespace alias to invoke static behavior on a class. The class has already been loaded, and I think I've got its alias defined properly. When I attempt to use that alias, however, php attempts (and fails) to autoload the class. I'm running php 5.3.3, and ZendFramework 1.8.4, all under CentOS 5.9.
Am I doing something wrong?
Here's are some (sanitized) code excerpts (my employer won't allow me to share the actual code, sorry).
<?php
// Filename = 'SomeAssembly/AbstractFoo.php'
namespace SomeAssembly;
use BaseAssembly\Base;
use SomeAssembly\ConcreteFoo;
require_once 'BaseAssembly/Base.php';
require_once 'SomeAssembly/ConcreteFoo.php';
class AbstractFoo extends Base {
// ...
static public function buildFooNamed_($aFooName) {
// ...
$declaredClasses = get_declared_classes();
$namespace = __NAMESPACE __;
$answer = $aFooName::create(); // Invokes autoloader!
return $answer;
}
}
?>
When I invoke AbstractFoo::buildFooNamed_('ConcreteFoo'), the autoloader is invoked. It fails because "ConcreteFoo" does not include the namespace.
When I set a breakpoint at "$answer = $aFooName::create", I see that $declaredClasses includes 'SomeAssembly\ConcreteFoo' and $namespace = 'SomeAssembly".
I think I declared the namespace alias "ConcreteFoo" in the line that reads "use SomeAssembly\ConcreteFoo".
Why isn't "ConcreteFoo" recognized as a namespace alias of a loaded class? If so, why is the autoloader being invoked?

It seems that my question has been answered in Dynamic namespaced class with alias.
From the manual:
Importing is performed at compile-time, and so does not affect dynamic class, function or constant names.
So I have to collect the fully-qualified class name myself. Oh well.

Related

spl_autoload_register couldn't get "use" namespace

#file1
spl_autoload_register(function($class){
require_once "{$class}.php";
});
new classes\Foo();
#file2
namespace classes;
class Foo implements toolInterface {
function __construct(){
echo __CLASS__;
}
public function tool(){
}
}
I have a problem with spl_autoload_register, above example works fine, but when I try to use use classes it will have fatal error, anyone know how to solve this problem?
//fatal error
spl_autoload_register(function($class){
require_once "{$class}.php";
});
use classes;
new Foo();
The ability to refer to an external fully qualified name with an alias
Note that for namespaced names (fully qualified namespace names
containing namespace separator, such as Foo\Bar as opposed to global
names that do not, such as FooBar), the leading backslash is
unnecessary and not recommended, as import names must be fully
qualified, and are not processed relative to the current namespace.
PHP: Using namespaces: Aliasing/Importing
So we need the fully qualified name
use classes\Foo;
new Foo();
If you log the $class variable provided to the closure you have you'll note that the FQCN (Fully Qualified Class Name) is given. In your case:
classes\Foo
You will need to ensure the file path is correct for that.
Or you could also use the de-facto standard - Composer - Don't mind the fact that the home page has a picture of a conductor.

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

Using namespaces in PHP

I am working on the AWS documentation which uses Guzzle framework. I have to deal with namespaces here and I am not able to get it working. I went through the docs and examples and understood that we can have packages for projects using namespaces.
I went ahead and tried a simple example, but unsuccessful. Here's the example: this is the index.php:
use My\Full\Classname as Another; //Also tried use My\Full\Classname
$obj = new Another; //with $obj = new Classname;
echo $obj->add();
I have Classname.php in the directory structure like this My->Full->Classname.php:
<?php
class Classname{
public static function add(){
return 2+2;
}
}
?>
I am trying to call the function in index.php but getting error:
Fatal error: Class 'Another' not found in C:\wamp\www\guzzleEx\index.php on line 19
which is the line where I instantiate the Classname object $obj = new Another;
What is the mistake i am making? Is there any INI that needs to be updated or any other config issue? How can I make the code working? If you use the normal include for Classname.php it works fine.
Namespaces need to be explicitly declared, they do not come from a certain directory structure.
So if you do not have a line that reads namespace My\Full; in front of your class Classname, then your class is not in any namespace, but in the root namespace.
Thus you cannot use it as \My\Full\Classname, but \Classname or even Classname directly.

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.

cascading use namespace in php with child classes

I am using an aliased and namespaced class in a parent class successfully but it doesn't seem to be available in the child class. The actual error is from the autoloader. The weird thing is that the function does work in the parent class and loads fine. How can I make a class brought in by use available in subclasses?
edit: the recipes are stateless -- would it make sense to make them singletons in Base and then reference them as members in the child class MyTest?
I have the two files:
Base.php:
namespace selenium;
use selenium\recipe\Cms as Cms;
class Base extends \PHPUnit_Framework_TestCase
{
public function __construct()
{
Cms::staticfunc(); //works fine
}
}
MyTest.php:
class MyTest extends \selenium\Base
{
public testMyTest()
{
Cms::staticfunc(); //errors here
}
}
From comment:
i was hoping for a way to cascade the use without duplicating that line among the 20 or so child classes
That is one of the biggest issues I have with PHP namespacing, that you have to call use for every file the current script needs access to. It's the same situation we used to face having to call require_once 20 times on some scripts in order to bring in the necessary libraries.
What I prefer to do is namespace my files (as they reside on the filesystem, like Zend Framework does) and use an autoloader to avoid the whole mess. I currently use ZF autoloader, which can be used outside of the framework, or you can also use the vanilla PHP implementation using SplAutoload.
-- Update --
I have a library which I have written over the last few years which is namespaced as Hobis_Api, and are located on the filesystem with the same convention; ~/projects/projects/dp/hobis/lib/Hobis/Api/*. In order to register the namespace with Zend_Loader I do the following:
// Be sure to set the include path to include the Zend and Hobis_Api files
// Not sure how your setup is, but would look something like:
set_include_path(get_include_path() . ':' . DIRNAME(__FILE__));
require_once 'Zend/Loader/Autoloader.php';
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->registerNamespace(
array(
'Hobis_Api_'
)
);
Normally the above code would go into some bootstrap file, which you can call from a centralized script in order to register the autoloader, once.
Now, if your include path is set correctly, anytime you reference Hobis_Api_* it will be autoloaded for you, so you don't need to call use or require_once, example usage:
// SomeScript.php
// Notice no requires
// I can make a call to Hobis_Api_Image without error
$image = Hobis_Api_Image;
$image->setHeight(400);

Categories