Since I've just jumped from another language to PHP, I would like to ask you what would be a correct way of adding an extension to a class. My config right now is as follows:
class A extends B {
use C;
public function a(){
}
}
I need to add some additional functions to class A, but logically divide them from it, so I though I would be using a trait C.
So my question is - can I use function a() in my trait C? I know I can define abstract function a() in trait C, however I believe that wouldn't be a good practice at all. Maybe I can somehow inject this into a trait or is it a bad practice as well?
It is possible, the choice is a discretion of the developer, the best solution is based on experience.
trait C {
public function hello() {
parent::a();
echo 'World!';
}
}
class B {
function a() {
echo "hello";
}
}
class A extends B{
use C;
}
(new A())->hello(); // helloWorld!
Let's say:
Class B {
public function a () {
//Does something
}
}
Class A extends B {
//We got access to B's public function. If you want to execute B's a and add some more content, then
public function a() {
parent::a();
//Some more content
}
}
Traits were developed due to PHP's nature of not allowing to inherit from multiple classes (Single inheritance). By creating a trait and apply it to a class, you know inherit it's methods
http://php.net/manual/en/language.oop5.traits.php
It really comes down to your needs.
Q: Do you want to create an interface and make other classes implement certain methods?
A: Create an interface
Q: Do you want to create an abstract class with some implementations and allow other classes to use them? A: Create an abstract class
Q: Do you want a class to inherit from two other classes and they've got different functionalities? A: Create a trait
The best approach is to use the tools at your disposal in order to output your desired result and keeping it organized
Related
What happens if I use a class in two different traits, and both have a method with the same name but different implementations of this method?
Short answer
(c) AbraCadaver
Long answer
Say you have a class Foo that uses traits A and B:
class Foo {
use A, B;
}
Where both traits have a method with a similar name, but different implementation (the implementation doesn't matter, really):
trait A {
public function bar() {
return true;
}
}
trait B {
public function bar() {
return false;
}
Traits work by extending the class horizontally. Simply put - just adding any new contents to the class. And all works fine till there's any doubling in trait methods and properties. Then you have yourself a fatal error if this conflict is not explicitly resolved.
The sweet part is, you can resolve this conflict by specifying which method from which trait to use:
class Foo {
use A, B {
B::bar insteadof A;
}
}
You can also save the other method from oblivion by using alias for it:
class Foo {
use A, B {
B::bar insteadof A;
A::bar as barOfA;
}
}
The manual has traits farely well documented, go check it out.
few days ago i was on a job interview, i was asked a question like this one:
There is an abstract class A with two methods foo and bar, from
it generated derived class C, which was implement only the method
foo. What changes need to take place in the script, in order to make
it work, while the implementation and interface classes A and C
should not be changed
abstract class A {
abstract public function foo();
abstract public function bar();
}
class C extends A {
public function foo() {
// some code
}
}
i said: okay we can simple add one method to our C class
public function bar() {
//
}
they said this is ok, but what if you can't add this method and you can't change abstract class A (and its methods).
And there are two options, either my interviewer are fool or i am fool and missing something.
I have read php.net documentation about abstract classes and i do not see any other solution.(ofcourse i can make class A not abstract or remove abstract modifier from bar method but i am not allowed to do that);
Help me please, because this question don't let me sleep!
You Need to declare Class C is Abstract.
Is there any chance there is an equivalent of Objective C Categories in PHP?
If all you want to do is break a huge class definition up over multiple files, and it's your class, then you can do that with traits. Just define some of the methods in a trait in a different file and use it in your class:
trait FooBar_ExtraMethods {
function foo () { return 'qux'; }
}
class FooBar {
use FooBar__ExtraMethods;
function bar () { return 'baz'; }
}
But if you want to add methods to somebody else's class, then there's simply no way to do that with vanilla PHP. Your choices are:
Live with the fact that this isn't possible and just write a function that takes instances of the class instead of extending it.
Use an extension to add the functionality into the language. Right now, the only contender is Dmitry Zenovich and Sara Golemon's Runkit. Zenovich's fork currently seems to be better-maintained and generally superior to Golemon's (although Golemon's is the one hosted on PECL), and the instructions below use Zenovich's fork.
Suppose I have an existing user-defined class Foo...
class Foo {
function methodA($arg) { return 2*$arg; }
}
I can add a method to it like this:
runkit_method_add('Foo', 'methodC', function ($arg) {
return 5 * $this->methodA($arg);
});
and call it like an ordinary method:
$f = new Foo;
echo $f->methodC(2); // 20
A couple of caveats:
You can't add methods to built-in classes. So if you want to use this to extend library-defined classes, you're good, but if you want to extend built-in classes like DateTime, you're out of luck.
This isn't remotely idiomatic - indeed, Runkit's tagline is "For all those things you.... probably shouldn't have been doing anyway.....". I leave it to your judgement whether to let that deter you from doing it.
It looks like a trait.
trait CarMaintenance{
public function needsOilChange(){}
public function changeOil(){}
public function rotateTires(){}
public function jumpBatteryUsingCar(){}
}
class Car {
use CarMaintenance;
public function startEngine() {}
public function drive() {}
public function turnLeft() {}
public function turnRight() {}
}
But traits could be reused in other class and could have their own hierarchy tree.
I was just playing around with traits to see what its purpose was and I noticed it allows a class to make use of multiple trait classes whereas a class extending from an abstract can extend from one abstract at a time
Here is my example:
trait parentClass
{
public function sayHello()
{
return 'parentClass sayHell() printed';
}
}
trait parentSecondClass
{
public function sayGoodbye()
{
return 'parentClass sayGoodbye() printed';
}
}
class People
{
use parentClass;
use parentSecondClass;
}
$o = new People();
echo $o->sayHello() . '<br/>';
echo $o->sayGoodbye();
Is this the general use of traits?
Generally traits are used for abstract functionality that might be needed by many classes that are not part of the same class hierarchy. You could use a trait to keep this functionality in a single location and apply it across a number of classes.
This doesn't remove the usefulness of abstract classes, but it does potentially remove the need for static utility classes one might typically put these type of functions in.
Let's have this class
class A {
protected static function ident() { return "I am A"; }
public static function say() { return "I say '".self::ident()."'!"; }
}
Then I need to extend class A and override the ident() like this
class B extends A {
protected static function ident() { return "I am B"; }
}
Now when B::say(); is called, the result is I say 'I am A'. Is there any technique how to force it to produce I say 'I am B' without overriding the say() method? (Please don't ask me why to do this, just trust me it is reasonable in my project)
I believe it is possible via abstract class or interface, but I can not figure out how. If it is impossible in PHP, is there any language (except Haskell) which implements this feature?
Since PHP 5.3 late static bindings are available. You shouled take a look at that.
http://php.net/manual/en/language.oop5.late-static-bindings.php
say() is a static method, this kind of method belong to the class, not to an instance, it's not really inherited. If you want to create your own method you have to "override it" (but again it's not overriding).