I have a base class with a protected method, a trait that makes that method public and an interface that requires that method as public. Boiled down example is this:
<?php
class b
{
protected function method() {echo 'base';}
}
trait t
{
public function method()
{
parent::method();
}
}
interface e
{
public function method();
}
class c extends b implements e
{
use t;
}
$c = new c();
$c->method();
This gives me a fatal error:
Fatal error: Access level to b::method() must be public (as in class e)
(it says class and not interface e, but whatever).
I tried to be explicit with use t {method as public;} but that makes no difference.
If i comment out the implements e bit from class c, i do see "base" printed on the console.
My PHP version is 5.5.9-1ubuntu4.11.
It's true that Traits have high precedence and Trait methods override inherited methods. But forget about Trait in your example. This error is all because of interface e and class b. When you are using interface then you are defining a contract with an interface. All methods declared in an interface must be public; this is the nature of an interface. - as the PHP documentation says and with protected function method() in class b you are breaking the contract.
Related
The PHP docs says the following about overriding trait properties:
If a trait defines a property then a class can not define a property
with the same name unless it is compatible (same visibility and
initial value), otherwise a fatal error is issued.
However, when you use a trait in an abstract class, then you can override the properties defined in the trait in a class extending that abstract class:
<?php
trait PropertyTrait
{
public $prop = 'default';
}
abstract class A
{
use PropertyTrait;
}
class B extends A
{
public $prop = 'overridden';
public function write()
{
echo $this->prop;
}
}
$b = new B();
$b->write(); // outputs "overridden"
Live demo
The code above works, but I can't find any reference about it in the documentation. Is this an intended feature?
Because for all intents and purposes B is not using PropertyTrait. That's used by A to compose the abstract class.
B has no visibility of what traits A is using. If you were to execute class_uses on B, you'd get an empty array. Docs, and example.
Since B is not using any traits, the class is free to override any inherited properties.
The fact that A is an abstract class has no bearing on this. The same behaviour would happen with any class that extended a class that was composed using traits.
I wonder if it is possible to not implement a method coming from an interface and let child class do it.
For example :
abstract class Foo implements Bar
{
public abstract methodFromBar();
}
And then :
class SubFoo extends Foo
{
public methodFromBar()
{
// Implementation...
}
}
The idea behind this is to simplify development and just specifying that the subclass extends from the main class instead of writing again that the subclass implements the interface.
You don't need to mention the interface method in the parent class at all. If it doesn't implement the interfaces listed, then PHP will require that the subclass fulfills the contract instead. This will work fine:
interface Bar
{
public function methodFromBar();
}
abstract class Foo implements Bar
{
}
class SubFoo extends Foo
{
public function methodFromBar()
{
echo 'Hello world';
}
}
$subFoo = (new SubFoo)->methodFromBar();
// Hello world
See https://eval.in/1016337
If the subclass does not implement the method, you'll receive a message along the lines of
Fatal error: Class SubFoo contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Bar::methodFromBar)
Whether or not you think it's a good idea for an abstract class to implement an interface is probably a discussion for another site.
If I have an interface and I want the functions in my interface to be implemented in my sub class but the parent class must implement the interface. And Sub class extends Parent Class.
Here is what I want.
interface MyInterface {
public function find();
}
class B implements MyInterface {
}
class A extends B {
}
But an error is thrown saying find() function must be added in class B.
Anyone who could enlighten me on what am I doing wrong? thanks!
Interface is a structure, which contains a set of fields and methods that have to be implemented in every class that implements this interface. If your B class implements MyInterface it means it has to implement all of its methods/fields.
interface MyInterface
{
public function find();
}
class B implements MyInterface {
public function find()
{
echo "Hello world";
}
}
Does A class have to implement this method too? I'll leave it as your homework.
I am studying the differences between Abstract and Interface and I read some sentence
saying
A child class can only extend a single abstract (or any other) class,
whereas an interface can extend or a class can implement multiple
other interfaces.
I understand when he says, “A child class can only extend a single abstract (or any other) class,” he means:
class first
{
public function Search()
{
return 'Hellow';
}
}
abstract class first2 extends first
{
}
class second extends first2
{
}
$ob = new second();
echo $ob->Search();
However, I didn’t understand the rest of his sentence, where he says, “whereas an interface can extend or a class can implement multiple other interfaces.”
Could someone please explain his last sentence and add a code example?
Thank you all and have a nice day.
You can implement more than one interface
interface C {
public function method1();
}
interface D {
public function method2();
}
class A implements C,D {
//implement from interface C
public function method1() {
}
//implement from interface D
public function method2() {
}
}
Here you will need implement methods from interface C and D. You can also extend interfaces within interfaces, like normal classes.
interface D extends C{}
It's useful when well you need some common methods. so you write "schema" into interface what methods you are expecting from base class to be implemented.
While abstract is single extended class, you canot create instance for it, only extend. It's useful when you want have some base class with common functionality or abstract methods what should be implemented later.
More you can always read at php.net - interfaces
I have a PHP object that consists of a set of classes. For sake of simplicity lets call it an object of class C that extends class B which in its turn extends class A. At some point in my code I want to clean up the object by calling its doCleanup() function which it inherits from interface I:
interface I { public function doCleanup(); }
class A implements I { ... }
class B extends A { ... }
class C extends B implements I { ... }
In the doCleanup function in class C I want to also execute any cleanup function in my parent classes (in this case, the doCleanup() in class A). However, for some objects I am not sure whether any of the parent classes actually implement interface I, so I am not sure whether I can simpley call parent::doCleanup().
My question therefore is if there is a way to check whether any of my ancestors implement the interface for example by using some sort of instanceof call?
You can do this nicely with get_parent_class and is_subclass_of (which works for interfaces as well as parent classes):
<?php
interface I {
public function doCleanup();
}
class A implements I {
public function doCleanup() {
echo "done cleanup\n";
}
}
class B extends A {}
class C extends B implements I {
public function doCleanup() {
if (is_subclass_of(get_parent_class($this), 'I')) {
parent::doCleanup();
}
}
}
$c = new C;
$c->doCleanup(); // outputs "done cleanup"
Since class C extends B, and B extends A, and A is required to implement doCleanup, then logically you can call parent::doCleanup() in C and it will work. If B does not implement it, it will be passed up to A, which must implement it. More accurately, B will run it, using A's implementation.
If you didn't know whether A implemented I or not, then it wouldn't necessarily be your responsibility to call it. If it were library code, for example, docs might tell you what you should do.