I'm curious about how use statements work in PHP. I was watching a tutorial and the code looked like this:
<?php
use Illuminate\Foundation\Testing\DatabaseTransactions;
class ConversationTest extends TestCase {
use DatabaseTransactions;
}
Why does the DatabaseTransactions item have to be declared twice?
The use DatabaseTransactions; statement refers the use of Traits.
Traits are a mechanism for code reuse.
A example from the php.net manual:
<?php
class Base {
public function sayHello() {
echo 'Hello ';
}
}
trait SayWorld {
public function sayHello() {
parent::sayHello();
echo 'World!';
}
}
class MyHelloWorld extends Base {
use SayWorld;
}
$o = new MyHelloWorld();
$o->sayHello();
?>
Related
Alright, I have a structure like this:
class Creature{
public function sayHi(){
echo "Hi";
}
}
class HumanType extends Creature(){
}
class Human extends HumanType{
}
class Human232 extends Human{
public function sayHi(){
echo "Hello, bro";
}
}
class Human457 extend Human{
}
$Human = new Human232($id);
echo $Human->sayHi(); //Hello, bro
$Human2 = new Human457($id);
echo $Human2->sayHi(); //Hi
//And then I have this still to be implemented
class HumanCategory576{
public function sayHi(){
echo "Hi from the category!";
}
}
I have of course many classes like:
Human457,
Human458,
Human459,
Human600,
Human601
And also:
HumanCategory576,
HumanCategory577,
HumanCategory578,
HumanCategory579,
HumanCategory580
What I want to do is implement HumanCategory576 in a way that sayHi() would print "Hi from the category!" only if the Human class which is (I suppose) inheriting it is not ovveriding the function, like Human457.
I hope I was clear enough.
How do I do? Thanks
I am assuming that it was your intention for class HumanCategory576 to extend another class.
You can use is_callable() to check if HumanCategory576 parent has the function by:
is_callable('parent::sayHi'){
parent::sayHi();
}else{
echo "Hi from the category!";
}
Please take a is_callable in PHP's documentation.
<?php
class TestClass extends TestClassParent {
/** #brief Object initialisation callback
#returns void */
public function __construct() {
# do initialisation
# ...
# if we have a parent
if(is_callable('parent::__construct')) {
# then bubble up
parent::__construct();
}
}
}
?>
I have the following code as a sample.
trait sampletrait{
function hello(){
echo "hello from trait";
}
}
class client{
use sampletrait;
function hello(){
echo "hello from class";
//From within here, how do I call traits hello() function also?
}
}
I could put all the details as to why this is necessary but I want to keep this question simple. Extending from the class client is not the answer here due to my particular situation.
Is it possible to have a trait have the same function name as the class using it, but call the traits function in addition to the classes function?
Currently it will only use the classes function (as it seems to override the traits)
You can do it this way :
class client{
use sampletrait {
hello as protected sampletrait_hello;
}
function hello(){
$this->sampletrait_hello();
echo "hello from class";
}
}
Edit :
Whops, forgot $this-> (thanks JasonBoss)
Edit 2 :
Just did some research on "renaming" trait functions.
If you are renaming a function but not overwriting another one (see the example), both functions will exist (php 7.1.4) :
trait T{
public function f(){
echo "T";
}
}
class C{
use T {
f as public f2;
}
}
$c = new C();
$c->f();
$c->f2();
You can only change the visibility :
trait T{
public function f(){
echo "T";
}
}
class C{
use T {
f as protected;
}
}
$c->f();// Won't work
Yes, You can do it this way also, you can use multiple function of a trait like this.
Try this code snippet here
<?php
ini_set('display_errors', 1);
trait sampletrait
{
function hello()
{
echo "hello from trait";
}
}
class client
{
use sampletrait
{
sampletrait::hello as trait_hello;//alias method
}
function hello()
{
$this->trait_hello();
echo "hello from class";
}
}
Let's say I have a simple class like this that uses a trait.
<?php namespace A\B;
use C\FooTrait;
class D {
use FooTrait;
}
And my trait looks like this.
<?php namespace C;
class FooTrait {
public function getBaseNamespace()
{
// code
}
}
My expected behavior would be the following:
<?php
$d = new D;
// Shoud be 'A\B';
$d->getBaseNamespace();
But so far I haven't been able to do this using the reflection API. Any clues?
This might be a bit more simple than using reflection.
If you are trying to determine it from within the trait method.
You can use:
public function getBaseNamespace()
{
return preg_replace('/(.+)\\\\[^\\\\]+/', '$1', __CLASS__);
}
My final implementation is the following.
<?php namespace Tools\Namespaces;
use ReflectionClass;
trait NamespaceTrait {
public function getBaseNamespace()
{
$reflection = new ReflectionClass(__CLASS__);
return $reflection->getNamespaceName().'\\';
}
}
The documentation says
The namespace keyword can be used to explicitly request an element from the current namespace or a sub-namespace. It is the namespace equivalent of the self operator for classes.
I need the equivalent of static instead, ie. if a class extends my class, the namespace of that.
This
return preg_replace('/.[^\\\\]+$/', '', get_class($object));
does it but it makes me sad.
Reflection provides an effective way of doing this via ReflectionObject->getNamespace(), check the following code:
namespace Foo {
class Bar {
public function getNamespace() {
return (new \ReflectionObject($this))->getNamespaceName();
}
}
}
namespace Baz {
use Foo\Bar as BaseClass;
class Bar extends BaseClass {}
}
namespace {
$bar1 = new Foo\Bar();
echo "ns1 is: ", $bar1->getNamespace(), '<br>';
$bar2 = new Baz\Bar();
echo "ns2 is: ", $bar2->getNamespace();
}
I don't think there's a "late static namespace" helper and you will indeed need to hack around.
e.g.
<?php
namespace ProjectFoo;
class Foo {
public static function ns() { echo __NAMESPACE__; }
public static function getNamespace() {
return static::ns();
}
}
namespace ProjectBar;
use ProjectFoo\Foo;
class Bar extends Foo {
public static function ns() { echo __NAMESPACE__; }
}
$foo = new Foo();
$foo::getNamespace();
print "\n";
$bar = new Bar();
$bar::getNamespace();
print "\n";
I think there is a problem in php's OOP implementation.
EDIT: Consider more illustrative example:
abstract class Animal {
public $name;
// public function Communicate(Animal $partner) {} // Works
public abstract function Communicate(Animal $partner); // Gives error
}
class Panda extends Animal {
public function Communicate(Panda $partner) {
echo "Hi {$partner->name} I'm a Panda";
}
}
class Human extends Animal {
public function Communicate(Human $partner) {
echo "Hi {$partner->name} I'm a Human";
}
}
$john = new Human(); $john->name = 'John';
$mary = new Human(); $mary->name = 'Mary';
$john->Communicate($mary); // should be ok
$zuzi = new Panda(); $zuzi->name = 'Zuzi';
$zuzi->Communicate($john); // should give error
The problem is that when Animal::Communicate is an abstract method, php tells that the following methods are illegal:
"public function Communicate(Panda $partner)"
"public function Communicate(Human $partner)"
but when Animal::Communicate is non-abstract but has zero-implementation Php thinks that these methods are legal. So in my opinion it's not right because we are doing override in both cases, and these both cases are equal, so it seems like it's a bug...
Older part of the post:
Please consider the following code:
Framework.php
namespace A
{
class Component { ... }
abstract class Decorator {
public abstract function Decorate(\A\Component $component);
}
}
Implementation.php
namespace B
{
class MyComponent extends \A\Component { ... }
}
MyDecorator.php
namespace A
{
class MyDecorator extends Decorator {
public function Decorate(\B\MyComponent $component) { ... }
}
}
The following code gives error in MyDecorator.php telling
Fatal error: Declaration of MyDecorator::Decorate() must be compatible with that of A\Decorator::Decorate() in MyDecorator.php on line ...
But when I change the Framework.php::Decorator class to the following implementation:
abstract class Decorator {
public function Decorate(\A\Component $component) {}
}
the problem disappears.
I'm not sure (haven't tested it ;), but you declare this abstract function:
public abstract function Decorate(\A\Component $component);
So you should implement this EXACTLY like that. But you did this:
public function Decorate(\B\MyComponent $component) { ... }
That's not the same. Could you try to change that to \A\Component?
To all comments: fact of the matter is that this piece of PHP "runs"
namespace A
{
class Component { }
abstract class Decorator {
public abstract function Decorate(\A\Component $component);
}
}
namespace B
{
class MyComponent extends \A\Component { }
}
namespace A
{
class MyDecorator extends Decorator {
public function Decorate(\A\Component $component) {}
}
}
And this doesn't:
<?php
namespace A
{
class Component { }
abstract class Decorator {
public abstract function Decorate(\A\Component $component);
}
}
namespace B
{
class MyComponent extends \A\Component { }
}
namespace A
{
class MyDecorator extends Decorator {
public function Decorate(\B\MyComponent $component) {}
}
}
?>
With this error: PHP Fatal error: Declaration of A\MyDecorator::Decorate() must be compatible with that of A\Decorator::Decorate() in line 18
Now you can discuss all you like about how that should or should not be, but that's the problem with the code.
so, to satisfy my own curiosity: this is illegal too:
<?php
class Component { }
abstract class Decorator {
public abstract function Decorate(Component $component);
}
class MyComponent extends Component { }
class MyDecorator extends Decorator {
public function Decorate(MyComponent $component) {}
}
?>
It's not the namespaces or anything. It just doesn't seem legal.
See http://bugs.php.net/bug.php?id=36601, this issues has been reported as a bug but was rejected because of laziness :D
It has nothing to do with it being abstract. It has to do with the type hinting. The two definitions of the method are not compatible because you explicitly set the argument to be of type \A\Component and then try to overload the method with \B\Component you cant do that because it changes the method signature. Any subsequent declaration of Decorate must use the same type hint as its parent declaration in order for the method signatures to be compatible.
This might assist someone, and am not late.
The best way to handle such is by using an interface.
Consider below;
<?php
interface Componentor{}
class Component implements Componentor { }
abstract class Decorator {
public abstract function Decorate(Componentor $component);
}
class MyComponent extends Component { }
class MyDecorator extends Decorator {
public function Decorate(Componentor $component) {}
}
?>
Usage;
<?php
$c=new Component();
//TODO ....blah blah blah...
$decor=new MyDecorator();
//TODO ....blah blah blah...
$decor->Decorate($c);
?>