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.
Related
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');
My question is in three parts:
Does putting in a use statement trigger the autoloader immediately, or does it wait until the class is used? (lazy-loading)
If autoloading isn't done in a lazy-load fashion, could that negatively affect performance?
Which pattern is best to follow, and why? PhpStorm shows "Unnecessary fully qualified name..." as a code issue when the use statement isn't employed.
Here's an example class definition for a Laravel controller with a use statement:
namespace App\Http\Controllers;
use Carbon\Carbon;
class FooController extends Controller
{
/**
* This action uses the Carbon class
*/
public function bar1()
{
return view('foo.bar1', ['now' => new Carbon()]);
}
/**
* This action does not use the Carbon class
*/
public function bar2()
{
return view('foo.bar2');
}
}
The same class without the use statement:
namespace App\Http\Controllers;
class FooController extends Controller
{
/**
* This action uses the Carbon class
*/
public function bar1()
{
return view('foo.bar1', ['now' => new \Carbon\Carbon()]);
}
/**
* This action does not use the Carbon class
*/
public function bar2()
{
return view('foo.bar2');
}
}
1) The class is autoloaded when you perform a new Class() statement.
2) see 1)
3) Which pattern is best to follow and why?:
I'd recommend to use use because you might get into a situation where you have really long namespaces and your code will become unreadable.
From the php docs:
This example attempts to load the classes MyClass1 and MyClass2 from
the files MyClass1.php and MyClass2.php respectively.
<?php
spl_autoload_register(function ($class_name) {
include $class_name . '.php';
});
$obj = new MyClass1();
$obj2 = new MyClass2();
?>
Namespaces are only an additional feature to organize classes.
EDIT: As #IMSoP pointed out in the comments, new is not the only time the autoloader is triggered. Accessing a class constant, static method, or static property will also trigger it, as will running class_exists.
The use statement can be thought of like a C pre-processing macro, if you're familiar with those: it rewrites the current file at compile time to let you write a short name for a long class, function, or constant name. It doesn't trigger autoloading, as it doesn't care if a class exists or not.
For instance, if you write use Foo\Bar\Baz as X, then everywhere that X is mentioned as a class name, the PHP compiler rewrites that to mention Foo\Bar\Baz instead. Only when code mentioning the class (e.g. new X, X::FOO, X::doSomething()) is actually run does it see if there really is a class Foo\Bar\Baz, and trigger the autoloader as necessary.
The common form use Foo\Bar\Baz is just shorthand for use Foo\Bar\Baz as Baz, assigning the alias Baz to the class name Foo\Bar\Baz.
As the manual points out the alias is only processed at compile time, so dynamic lookups will not use it. In the example above, class_exists('X') will return false, but you can use class_exists(X::class) to expand out the alias - the compiler will automatically substitute the full class name as a string, so at run-time, the expression will be class_exists('\Foo\Bar\Baz').
Whether use statements make your code better is therefore entirely a matter of style: the intent is that your code will be more readable without the long fully-qualified class names, but it will make no difference to how the code actually runs.
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;
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();
I have a few PHP classes which are defined like this:
module1Model.php
namespace App\Modules\module1\models;
class module1Model
{
//... methods etc.
}
module2Model.php
namespace App\Modules\module2\models;
class module2Model
{
//... methods etc.
}
In some other class i want to use those namespaces. So i placed the following lines at the top of my page:
someClass.php
namespace App\Modules\someClass\controllers;
use App\Modules\module1\models; // Works
use App\Modules\module2\models; // Fails
use Core\Logger\TextLogger; // Works
class somceClass
{
//... methods etc.
}
So in someClass i'm trying to use both module1 and module2 models. Which are technically not in the same namespace. Just partially. But why is it giving me an error about it then..??
And what would be a good way to solve this problem? I know i can add this to the second line:
use App\Modules\module2\models as module2; // Now works
But i don't really like to do it that way, since i didn't do it like that in all my other classes (using the as keyword). That feels so inconsistent.
Is there any other way to make this work..? Or am i really forced to use the as keyword in all my use declarations...? :-(
I guess you want to use the model classes, not their whole namespace. If so, then the following just works:
use App\Modules\module1\models\module1Model;
use App\Modules\module2\models\module2Model;
Example:
namespace App\Modules\module1\models {
class module1Model
{
//... methods etc.
}
}
namespace App\Modules\module2\models {
class module2Model
{
//... methods etc.
}
}
namespace App\Modules\someClass\controllers {
use App\Modules\module1\models\module1Model;
use App\Modules\module2\models\module2Model;
use Core\Logger\TextLogger;
class somceClass
{
//... methods etc.
}
}