Are traits in PHP affected by namespaces? - php

From the PHP documentation:
only four types of code are affected by namespaces: classes, interfaces, functions and constants.
But, it seems to me that TRAITS are also affected:
namespace FOO;
trait fooFoo {}
namespace BAR;
class baz
{
use fooFoo; // Fatal error: Trait 'BAR\fooFoo' not found in
}
Am I wrong?

Yes, they are.
Import the trait with use outside the class for PSR-4 autoloading.
Then use the trait name inside the class.
namespace Example\Controllers;
use Example\Traits\MyTrait;
class Example {
use MyTrait;
// Do something
}
Or just use the Trait with full namespace:
namespace Example\Controllers;
class Example {
use \Example\Traits\MyTrait;
// Do something
}

I think they are affected as well. Look at some of the comments on the php.net page.
The first comment:
Note that the "use" operator for traits (inside a class) and the "use" operator for namespaces (outside the class) resolve names differently. "use" for namespaces always sees its arguments as absolute (starting at the global namespace):
<?php
namespace Foo\Bar;
use Foo\Test; // means \Foo\Test - the initial \ is optional
?>
On the other hand, "use" for traits respects the current namespace:
<?php
namespace Foo\Bar;
class SomeClass {
use Foo\Test; // means \Foo\Bar\Foo\Test
}
?>

In my experience if this piece of code you pasted resides in different files/folders and you use the spl_autoload_register function to load classes you need to do it like this:
//file is in FOO/FooFoo.php
namespace FOO;
trait fooFoo {}
//file is in BAR/baz.php
namespace BAR;
class baz
{
use \FOO\fooFoo; // note the backslash at the beginning, use must be in the class itself
}

The documentation also says "A Trait is similar to a class",
A trait is a special case of a class.
So what applied to a class also applied to a trait.

Related

Import namespace in PHP

I have just started learning a PHP framework and using namespace and use. I have gone through some resources trying to understand them (https://code.tutsplus.com/tutorials/namespacing-in-php--net-27203). I think I get what namespace is but I do not quite understand how namespace importing (use keyword) work.
I get that namespace works like below. I have a lib.php file:
<?php
namespace namespaceproj\lib1;
class lib1 {
private $var;
function __construct(){
$this->var='Run from lib';
echo $this->var;
}
}
I have an index.php file
<?php
namespace main ;
include "lib.php";
class lib1 {
private $var;
function __construct(){
$this->var='run from main';
echo $this->var;
}
}
$obj=new \namespaceproj\lib1\lib1(); // this refers to the class with namespace namespaceproj\lib1;
$obj = new lib1();// this refers to the class with namespace main .
All fine and good now I am trying to use the use keyword. I use the namespace\classname in the use keyword and can instantiate the class directly. I am guessing the namespace of the class becomes main when we use "use".
<?php
namespace main ;
include "lib.php";
class lib1 {
private $var;
function __construct(){
$this->var='run from main';
echo $this->var;
}
}
//Accessing class using use
use namespaceproj\lib1\lib1 as lib1inc ;
$obj=new lib1(); //This refers to class with main namespace
$obj=new lib1inc(); // This refers to class with namespaceproj\lib1; namespace
*/
Now is the part I am not able to understand. What happens if you do not want to import classes individually but import many classes in a namespace? The resource say you can use the use keyword to import namespaces. So I try to import the namespace of the lib file and try to instantiate the object. If I try something like this
use namespaceproj\lib1 ;
$obj=new libnew();
It gives me an error saying:
Cannot use namespaceproj\lib1 as lib1 because the name is already in use in C:\xampp\htdocs\AllTest\index.php on line 26
I was guessing the namespaces and all classes in that namespaces was getting imported to the main namespace and hence the error .
So I used the as keyword
use namespaceproj\lib1 as libnew;
$obj=new libnew();
This gives me an error:
Fatal error: Class 'namespaceproj\lib1' not found in C:\xampp\htdocs\AllTest\index.php on line 27.
My questions are:
Does the use keyword always expect a class even though I tell it to import a namespace ?
How do you import a namespace (and then instantiate multiple classes in that namespace) ?
For the above example how do you use both the classes, one in main namespace and the other in namespaceproj\lib1 namespace ?
use namespaceproj\lib1 ;
Here you are importing the namespace, not all of the classes inside of that namespace. Therefore, you're trying to create a symbol lib1 pointing towards the namespace namespaceproj\lib1. This fails because you already have a lib1 symbol as a class.
use namespaceproj\lib1 as libnew;
Again, namespaceproj\lib1 is a namespace, not a class. So now you have a symbol named libnew for the namespace and you can instantiate classes using this aliased namespace:
new libnew\lib1;
PHP7 introduced group import syntax, but it still requires you to declare which classes you are importing.
use some\namespace\{ClassA, ClassB, ClassC}

How to get the trait name with namespace inside itself?

I want to know if there is a way to get the traits namespace inside itself, I know that I can use self::class to get the classname, but inside a trait it gets the namespace of the class that is using the trait, I don't want to type it's name fixed like new ReflectionClass('trait')
Is there any function or const that can do this?
I'm a little bit confused by your question but if you need the fully qualified name from the trait then you may use __TRAIT__ magic constant and if you only need the namespace of the trait then you may use __NAMESPACE__. For example, declare a trait using a namespace:
namespace App\Http\Controllers\Traits;
trait Methods
{
public function getNamespace()
{
// Get fully qualified name of the trait
echo __TRAIT__; // App\Http\Controllers\Traits\Methods
echo PHP_EOL;
// Get namespace of the trait
echo __NAMESPACE__; // App\Http\Controllers\Traits
}
}
Now, declare a class using another namespace and use that trait inside this class:
namespace App\Http\Controllers;
use App\Http\Controllers\Traits\Methods;
class TraitController
{
use Methods;
public function index()
{
// Call the method declared in trait
$this->getNamespace();
}
}
(new TraitController)->index();
The predefined magic constants __TRAIT__ (since 5.4.0) and __NAMESPACE__ (since 5.3.0) is used so use which one is needed. Tested in php v-5.4.0. Check the demo here.
Also, if you want to get the fully qualified name of the trait from the class that is using it then you may use NameOfTheTrait::class (NameOfTheClass::class/NameOfTheInterface::class) but this is available since php v-5.5.
Also be careful when using self::class. The self::class will give the fully qualified name of the class where you've used it because the self always references the lexical scope (where it's physically used) since the scope of self is determined during the compile time so you may get unexpected results if you inherit a class where a self::class statement is used. In other words, if you call any static method from a child class then the calling context will be still the parent class if you use self in your parent class, in that case you need to use static instead of self. This is actually another topic so please read more on php manual about Late Static Binding.

How do I 'use' the IteratorAggregate interface, PHP can't find it?

I have a class that I want to implement IteratorAggregate. I declare:
class Email implements IteratorAggregate {
but when I rung the code I get:
Attempted to load interface "IteratorAggregate" from namespace
"Companyname\ConsoleBundle\Entity". Did you forget a "use" statement
for another namespace?
So what name space should I adding to implelement the 'IteratorAggregate' interface. None of the examples I've seen include any use or require statements for it and it should be included as standard in PHP5 so it's not an installation issue.
Any ideas what I'm doing wrong?
When you use your own namespace and you want to use a standard class, you have to spell it like this
class Email implements \IteratorAggregate {
To "use" classes from the global namespace, prefix them with a backslash:
use \IteratorAggregate;
class Email implements IteratorAggregate { ... }

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();

Categories