PHP Namespace qualification - why do I have to fully qualify? - php

I am converting from c# to php and I'm having trouble transitioning in some places. Particularly namespaces. The problem I am having is I have to fully qualify every namespace when calling a class from another namespace. Is this normal?
<?php
namespace Lib\Things;
class TheThings
{
}
then in the other class
<?php
namespace App;
use Lib\Things;
class DoStuff
{
public function doStuff()
{
$things = new TheThings();
}
}
That doesn't work... I end up having to do
new Lib\Things\TheThings();
Or
<?php
namespace App;
use Lib\Things as T;
class DoStuff
{
public function doStuff()
{
$things = new T\TheThings();
}
}
I've also got this in my composer.json file
"psr-4": {
"App\\": "app/",
"Lib\\": "lib/"
}
Is that normal or am I doing something wrong here?

In the PHP manual the use keyword is referred to as importing or aliasing.
This means that
use Lib\Things;
and
use Lib\Things as Things;
are the same. This results that you don't have to use the fully qualified names to instantiate classes from a namespace, you can use only the alias of the imported namespace. So in your case, the following would have worked:
<?php
namespace App;
use Lib\Things;
// same as:
// use Lib\Things as Things; // explicit alias
class DoStuff
{
public function doStuff()
{
$things = new Things\TheThings();
}
}
(Note that this is the same as your second example, the only difference is that the alias is not explicitly set here to T (and defaults to Things).
To be able to use the class name without any namespace prefix, you will have to set the alias of the actual class:
<?php
namespace App;
use Lib\Things\TheThings;
// same as:
// use Lib\Things\TheThings as TheThings; // explicit alias
class DoStuff
{
public function doStuff()
{
$things = new TheThings();
}
}
In conclusion, if you start thinking about the use operator as setting an alias for a namespace or class (or other), you will get the hang of it.
PS 1:
Before PHP 7, if you wanted to import multiple classes from the same namespace, you had to write:
use some\namespace\ClassA;
use some\namespace\ClassB;
use some\namespace\ClassC as C;
From PHP 7.0 onwards, classes, functions and constants being imported from the same namespace can be grouped together in a single use statement:
use some\namespace\{ClassA, ClassB, ClassC as C};
PS 2:
composer helps in automagically including/loading the actual php files, based on some PSR* rule, it does not have any role in how namespaces work in bare PHP.

When you do:
$things = new TheThings();
Class is searched on the current namespace (App in your example), thats why not worked.
So, you need to specify full class namespace, so interpreter know which class are you refering, you could also have a namepsace2/TheThings and this/is/another/namespace/TheThings classes.
Just include full class namespace
use Lib\Things\TheThings;

Related

PHP 'USE' operator in classes and extended classes

I'm trying to understand how to use USE in base and extended classes. I have searched around but I don't think I have the correct terminology.
Let's say my base class looks like
namespace App\Classes;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\Legend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
class ExcelReport
{
public $spreadsheet;
public function __construct()
{
$this->spreadsheet = null;
}
}
and then I extended that class
namespace App\Classes;
class MonthlyExcelReport extends ExcelReport
{
public $id;
public function __construct(int $id)
{
parent::__construct();
$this->id = $id;
}
public function build()
{
$reader = IOFactory::createReader('Xlsx');
}
}
What do I have to do to get the call to IOFactory in the extended class to recognize that use PhpOffice\PhpSpreadsheet\IOFactory; is present in the base class?
I currently get this error Class 'App\Classes\Gap\IOFactory' not found and I don't want to have to repeat all of those use statements in the extended class.
TL;DR;
Namespace is there to allow you to have two classes named the same, but in a different namespace.
Imagine the class Animal\Bear\Claw and Machinery\Compactor\Claw, are things possible with namespaces, when we needed ugly class names like Animal_Bear_Claw and Machinery_Compactor_Claw before the introduction of namespaces in PHP.
Now when you instanciate or use those classes, you don't want to allways have to go in the extends and say
new \Animal\Bear\Claw();
You want to be able to say: "I am in the context of an Animal Factory Pattern and will basically act upon the classes under the Animal namespace, not the Machinery's ones"
So you go:
use Animal\Bear\Claw;
new Claw();
Or
use Animal\Bear;
new Bear\Claw();
Or even, with aliases
use Animal\Bear as MyTeddyBear;
new MyTeddyBear\Claw();
And so, inheriting another class from another containing uses, just does nothing, you'll have to repeat your uses, maybe simplifying them, and most likely, not add uses for class you actually do not use in the said class (did you know that good IDE does prompt you about unused use statement present in your classes and help you add the good ones to your use statements?).
The use statement are not like an include like you seems to believe it.
It is just there to say: "thanks to namespaces, I can have multiple classes with the same name, now the class I want to use is actually under the namespace defined by use"
You are not forced to state a full namespace either in your use statements.
For example:
namespace App\Classes;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class MonthlyExcelReport extends ExcelReport
{
public function __construct()
{
$reader = IOFactory::createReader('Xlsx');
$workSheet = new Worksheet();
}
}
Could be shortened this way:
namespace App\Classes;
use PhpOffice\PhpSpreadsheet; // This means "all the classes that I am going to use, if not in the same namespace as the current class (App\Classes) would come from the namespace PhpOffice\PhpSpreadsheet"
class MonthlyExcelReport extends ExcelReport
{
public function __construct()
{
$reader = PhpSpreadsheet\IOFactory::createReader('Xlsx');
$workSheet = new PhpSpreadsheet\Worksheet\Worksheet();
}
}
Further reading:
https://www.php.net/manual/en/language.namespaces.faq.php
https://www.php.net/manual/en/language.namespaces.rules.php
Use operator is used to "include" a class.
If you don't use "Use" operator, than you can include as "full path".
In your case:
$reader = IOFactory::createReader('Xlsx');
Should be:
$reader = PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx');

PHP prepend class names with sub-space for autoloading

I'm just wrapping my head around PHP namespaces and autoloading with composer. I have a question about the following code:
namespace APP\Controllers;
use APP;
use APP\Interfaces;
use APP\Lib;
class PageController
extends Lib\ActionController
implements Interfaces\ControllerInterface
{
//stuff
}
Why do I have to prepend the extends class with the sub-space with 'Lib\' when I already use the line 'use APP\Lib;'? Same goes for the interface. When I don't prepend I get an autoload error. I'm using composer to autoload and have this in my composer.json:
"autoload": {
"psr-4": {
"APP": "app/"
}
}
In app/ I have subfolders Lib, Interfaces and Controllers like so:
/app
/Controllers
/Interfaces
/Lib
I noticed that in other devs code they don't have to do this. I'm confused as to what I am doing wrong.
Thanks for the help.
You are including three namespaces:
use APP;
use APP\Interfaces;
use APP\Lib;
Now if you say just:
extends ActionController
PHP would not know if it is:
APP\ActionController or
APP\Interfaces\ActionController or
APP\Lib\ActionController
If you still wanted to extend it without Lib subspace you would need to do:
use APP\Lib\ActionController; first
use is only there to alias namespaces or class names to shorter names. It's there to avoid having to repeatedly address all classes by their fully qualified name all the time:
$a = new \Foo\Bar\Baz\Quurx();
$b = new \Foo\Bar\Baz\Quurx();
// shorter:
use Foo\Bar\Baz\Quurx;
$a = new Quurx();
$b = new Quurx();
use Foo\Bar is shorthand for use Foo\Bar as Bar. So, you're creating an alias Bar which really resolves to the full name \Foo\Bar. Since APP\Interfaces doesn't resolve to any particular interface in your case, just using implements Interfaces wouldn't mean anything. And if you just used implements ControllerInterface, it would be ambiguous which namespace that resolves to. \APP\Controllers\ControllerInterface? \APP\ControllerInterface? \APP\Lib\ControllerInterface? It's just not clear and cannot be resolved automatically.
So, what you're doing is you're shortening APP\Interfaces to just Interfaces, and then refer to APP\Interfaces\ControllerInterface by just using the shorter Interfaces\ControllerInterface. You could be doing this to make it even shorter:
use APP\Interfaces\ControllerInterface;
.. implements ControllerInterface ..

PHP use "use" of the interface over concrete classes

Have encountered an issue I can't seem to figure out now by myself.
Using Symfony autoload module.
Here's my factory:
namespace Core\Factories;
use \Core\Gateway;
class DatabaseAccessFactory {
// Define client type
const DEF = 'mongo';
public function createObject($type) {
switch($type) {
case self::DEF:
return new MongoClientGateway();
break;
default:
return false;
}
}
}
Example of /Core/Gateway/MongoClientGateway.php
<? namespace Core\Gateway;
class MongoClientGateway implements MongoDbGateway {
public function setUp(){
}
public function query(){
}
public function save(){
}
}
So, basically I'm using "use" keyword to load gateway namespace into my current one, and then I try to instantiate a class that is under \Core\Gateway namespace, but it says class is not found. Am I missing something?
You need to specifcy the class as well
use Core\Gateway\MongoClientGateway
or access the class with the namespace you used
new Gateway\MongoClientGateway
Btw, there's no need for the first "\" in use \Core\Gateway
It's use Foo\Bar, without leading backslash.
use Foo\Bar does not mean that every Class implicitly resolves to Foo\Bar\Class now. use Foo\Bar is shorthand for use Foo\Bar as Bar, so you can reference the namespace Foo\Bar using merely Bar. use is not "importing a namespace", it's aliasing a namespace to a shorter name.
Therefore you need to write Gateway\MongoClientGateway, or use Core\Gateway\MongoClientGateway explicitly if you want to be able to write just MongoClientGateway.
you used "use" wrong.
waht "use" does, is to tell your code where class comes from.
sample code:
use \my\namespace\className
new ClassName();
this will make the className accassible without a namespace.

Is there a shortcut for namespacing singleton classes

My current code is as following:
namespace Libraries;
class_alias('Libraries\ORM', 'ORM');
class ORM
{
public function __construct() {}
static public function someMethod()
{
// do something
}
}
I thought I could shortcut the namespace as you can see above, so I only needed to call the ORM::someMethod(); instead of \Libraries\ORM::someMethod();
(I am using the ORM class in another namespace, lets says 'Project')
Is this possible or what is the right solution?
I know that I could store the class in a global namespace, but then I still need to use the global slash like: \ORM::someMethod();.
Thanks!
Simply alias the classname when you are importing it:
namespace SomethingEntirelyDifferent;
use Libraries\ORM as ORM;
ORM::someMethod();

PHP namespaces and importation

I have a question about namespaces in PHP.
this code doesn't work :
<?php
namespace My\Functions\Printing;
class A {
public function __construct() {
echo __NAMESPACE__;
}
}
namespace My;
use My\Functions\Printing\A as A;
$obj=new namespace\A();
But this one work :
<?php
namespace My\Functions\Printing;
class A {
public function __construct() {
echo __NAMESPACE__;
}
}
namespace My;
use My\Functions\Printing\A as A;
$obj=new A();
I would like to get more information about the behavior of namespaces importation.
Why an imported class can't be accessible in the namespace where it is imported?
Probably you have a confused idea of using 'use'.
The keyword 'namespace' refers to the current namespace
namespace My\Functions\Printing;
class A {
public function __construct() {
echo __NAMESPACE__;
}
}
namespace My;
use My\Functions\Printing\A;
use My\Functions\Printing\A as myAlias;
$obj=new namespace\A(); // instance of \My\A (doesn't exist)
$obj2=new A(); // instance of \My\Functions\Printing\A
$obj3=new myAlias(); // instance of \My\Functions\Printing\A
As usual, see the documentation for complete details: php doc
I'm not sure but I think problem is in using namespace keyword.
http://www.php.net/manual/pl/language.namespaces.nsconstants.php
In first example you are in My namespace so namespace\A() == My\My\Functions\Printing\A()
My\Functions\Printing namespace look like
-- My\Functions\Printing
A
"My" namespace before importation look like:
-- My
"My" namespace after importation should look like (i thinks)
-- My
A
So why i can't acces My\A ?
I think the structure of namespace it's not changed an PHP compiler check in the "namespace imported area" before the "namespace structure".
Documentation specify namespaces look like filesystem, it's false.
If an importation of an other class is as "create a symbolic link" i must be able to acced it from the current namespace.
We can acced a symbolic link from the directory where it was created or from the absolute path but it's not possible to acced the imported class
with its new path. (\My\A)
Why ?

Categories