I'm trying to trick PHP into taking a class from another namespace when trying to create a specific class.
I have two class called "page", the first is in the Core namespace:
namespace Core;
class Page {...}
The second inherits from Core\Page, but adds a few things. It is in the Addons namespace.
namespace Addons;
class Page extends \Core\Page{...}
The reason I want to do this is because I want to build my system with an easy addon engine. Whenever I want, I can add a line in an XML file that tells the autoloading function to take the class in the addon namespace instead of the core namespace.
However, when I try to do this :
spl_autoload_register('loadClass');
public function loadClass(string $className)
{
if (Addon_exist_and_is_registered($className))
{
require "/Addons/$className.php";
}
else
{
require "/Core/$className.php";
}
}
$page = new \Core\Page(); <-- error here
I get an error saying that the class \Core\Page cannot be found in the file Addons\Page.php. This is normal behaviour since the class is not in the same namespace and as such, the fully qualified name cannot find the right class.
Is it possible to trick PHP into thinking that a child class in another namespace is actually the right class? I tried this for the addons class;
namespace Core;
class Page extends \Core\Page{...}
But it breaks the inheritance as you cannot inherit yourself.
Ignore that the classes have the "same name". Because they don't. One class is called Core\Page, the other is called Addons\Page. Those are their names, their fully qualified names to be exact. It's as much a difference as Foo and Bar. If you tell PHP to instantiate Core\Page, then it's going to do that; you can't "trick" it into instantiating Addons\Page, since that's an entirely different class name.
Don't try to "trick" anyone, make your system actually extensible and explicitly allow overriding of class names:
$class = 'Core\Page';
if (...) {
$class = 'Addons\Page';
}
$page = new $class;
Related
I have trouble understanding the import behavior. Let me explain, I have a class B outside of any namespace (for some reason I have double autoload, a proprietary one, and the classic PSR-4, I guess this can have something to do with my issue, I'm ready to gather more information on this tidbit if necessary). In this class I import a class A from a namespace, and it turns out when in a method of my children class I can use the A class to instantiate an object, but when I try to tell class B to extend class A it says the FQN is not found.
<?php
use \App\Services\AbstractController as BaseController;
class BarController extends BaseController
{
public function test() {
$foo = new BaseController();
}
}
There I would have an error on the "class BarController extends BaseController" line saying BaseController can't be found, but if I delete the extends part, the test method will run smoothly with its instantiation (I did try to manipulate the object, it sure works well).
so I guess my question is, is there any difference on the treatment of a class to extend vs. using it to instantiate an object.
Thanks in advance for your thoughts, and I'm ready to answer any additional question.
I did manage to deal with the issue. The controller class was instantiated inside the proprietary autoloader but the method was called later on the process. I switched both autoloader order (ie. first psr-4 then proprietary one) in the init and it works well.
EDIT:
Yes the problem was using \ at the beginning of the use statement. As M1ke pointed out, use goes from the root element.
Original post
I think is a PHP question but it may be Drupal.
I'm working on a headless Drupal project where is using a class (which I call Entity Model) that uses a Drupal class called EntityFieldQuery.
Before a create or use this class I bootstrap Drupal using:
require_once DRUPAL_ROOT.'/includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
The entity model class is in the Models names space like so:
namespace Models;
use \EntityFieldQuery;
class EntityModel
{
.....
$query = new EntityFieldQuery();
$query->doSomething();
......
}
The EntityFieldQuery is found perfectly as I use the "\" because this class is out of the Models namespace.
The problem is when this class is created is uses other classes that don't use any namespace, and I have the following error:
class Models\InsertQuery not found in ....
Here is the class used by EntityFieldQuery that uses InsertQuery
class InsertQuery_mysql extends InsertQuery ...
I don't understand why InsertQuery_mysql is found but InsertQuery
I ended up adding a "\" in InsertQuery to fix the problem like so:
class InsertQuery_mysql extends \InsertQuery ...
Actually this class in a php file called query.inc that contains two defitinion classes (in the same file, I don't know this is a a problem too)
class InsertQuery_mysql extends InsertQuery
....
class TruncateQuery_mysql extends TruncateQuery
I thought that if I use "new \ClassName()" the "default namespace" inside this class would be "\" too and not the first called class's namespace.
I don't like to modify 3rd party libraries, is any way to avoid this? I guess is a architecture problem rather than a lack of definition if someone has a better idea, I appreciate.
Thanks!
EDIT2: Adding more info...
In order of execution.
index.php:
require_once 'vendor/autoload.php';
require_once DRUPAL_ROOT.'/includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
...
app/SiteController.php:
use Models\Campaign;
class SiteController {
...
$campaing = new Campaign();
...
app/Models/Campaing.php:
namespace Models;
class Campaign extends EntityModel {
...
app/Models/EntityModel.php:
namespace Models;
use \EntityFieldQuery; //<-- this should go without \ as I say in EDIT section
class EntityModel {
...
public function getAll() {
$query = new EntityFieldQuery(); //<--throwed Models\InsertQuery not found. It must have \ at the beginning of the class name.
To answer the base question (and pending further code) PHP namespaces are set by whichever namespace is declared in the file.
// Bar.php
namespace Foo;
class Bar {}
// some other file
use Foo\Bar;
$test = new Bar(); //works
// different file
namespace Foo;
$test = new Bar(); // works
// another file
require 'Bar.php';
// won't work because we are not in namespace "Foo"
$test = new Bar();
In your specific case it the use \EntityLoader should be use EntityLoader because you're exiting the namespace you want to be inside.
I read up quite a bit on namespaces in PHP and I'm still confused.
I have a class in a different folder that is under the namespace Entity (Class A).
I have another class in a different folder that is under the same namespace (Class B), and extends class A.
I get an error saying class A could not be found.
My main question is - do I have to include class A when I create a new instance of class B?
This is my code:
(Class A)
namespace Entity;
//Framework/Entity/BaseModel.php
class BaseModel {
//TODO: IMPLEMENT THIS
public function GetList() {
return null;
}
}
(Class B)
namespace Entity;
//Models/Points.php
class Points extends BaseModel{
public $Id = null;
}
(Main File)
require_once(dirname(dirname(__FILE__)) . '/Models/Points.php');
$points = new Points();
Namespaces have nothing to do with actually including files, those are two completely separate mechanisms. So, yes, you will still have to require_once the file that the class is defined in before you can use it.
Having said that, especially with namespaces, autoloaders are typically used so you don't have to write a ton of require code. If you organise your class files in folders exactly as their namespaces are, it's very easy to autoload their files. See http://php.net/manual/en/language.oop5.autoload.php and https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
Parent class definition must be know on definition of child class.
Even before you create instance of child class.
How otherwise PHP would know what to put inside class you create?
require_once('BaseModel.php')
namespace Entity;
//Models/Points.php
class Points extends BaseModel{
public $Id = null;
}
I am working on a php sdk rewrite project and the client wants to get PSR standards done. I am looking at the standards page here
https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
One thing what i am not able to understand, if i use name spaces in my class do i still need to use include or require or use. I mean the whole reason of autoload beats the purpose right ?
For example, say i have a class this way,
namespace Employee\Department;
Class Department
{
//code
}
and i have another class which uses this class by extending it,
namespace Employee\community;
Class Community extends Department
{
//code
}
so does the above code make it to psr-0 standard considering that i have an autoload function exactly thats on the link above.
The second example is going to assume Department is in the Community namespace so in this case you would need a use statement. Also both of your examples would use the namespace Employee not Employee\Whatever for example lets assume the following layout:
Employee/
Community.php
Community/
Manager.php
Department.php
Department/
Manager.php
Then we would see the class/namespaces like the following
namespace Employee;
class Department {
}
///////////
namespace Employee;
class Community extends Department {
}
/////////////
namespace Employee\Department;
class Manager {
}
/////////////
namespace Employee\Community;
use Employee\Department\Manager as BaseManager;
Class Manager extends BaseManager {
}
For your understanding, autoloading works by registering the autoload function in the autoload stack via spl_autoload_register; this allows the function to be invoked whenever a new Class() statement is executed (more info).
On the other hand, the FIG standard for autoloading, PSR-0, defines how a namespace will be translated into a filename by a PSR-0 autoloader function. For example, given the namespace Vendor\Foo, the autoloader will look for the file Vendor/Foo.php having the following code
namespace Vendor;
class Foo
{
public function do()
{
echo "Foo";
}
}
Therefore, following the mandatory requirements, a PSR-0 compliant namespace resolves to the correct PHP file which could otherwise have been included using a require or include.
If I read your intentions correctly, you just need the following namespace in both code snippets:
namespace Employee;
Of course, this is not a PSR-0 namespace because there is no vendor name (unless your vendor name is 'Employee'). Anyway, using this namespace in your two code snippets will work fine.
However, if you intended to keep them in separate namespaces, then the autoloader won't figure out Department in extends Department in the second snippet. You will have to either import the namespace or explicitly specify it as so:
namespace Employee\community;
class Community extends Employee\Department\Department
{
//code
}
I imagine that you did not expect the full class names from your snippets to be Employee\Department\Department, and that is why I first suggested keeping the same namespace for your purposes.
When using the pseudo namespacing pattern of PEAR and Zend, it is common to come across class heirarchies that look like this:
Zend/
Db.php
Db/
Expr.php
Where DB.php contains a class named Zend_Db and Expr.php contains a class named Zend_Db_Expr. However, when you try to convert the old 5.2 psuedo namespacing into PHP 5.3 namespacing you are presented with a case where a namespace and a class share a name. Since the use operator can import either a namespace or a classname this leads to ambiguity.
Here's an example of an app I'm working on converting:
App/
Core.php
Core/
Autoloader.php
Here the base directory and namespace are App. In the top level of the name space is a Core class:
namespace App;
class Core { }
In the Core directory are various other core classes, some of which use the main Core. Under the pseudo namespacing pattern, this isn't a problem. But in the real namespacing pattern it creates this situation:
namespace App\Core;
use App\Core as Core; // What is this importing? Namespace or class?
class Autoloader {
public function __construct(Core $core) {}
}
Is this defined? What is actually imported here?
Simply both. It is not real import, just a hint for compiler, that every encounter of this alias in class related operations should be expanded to this declaration. In php namespace is just part of class so just think of it like this
$alias = 'Zend_Db';
$zendDB = new $alias;
$aliasExpr = $alias . '_Expr';
$expr = new $aliasExpr;