I've found something that appears to be a strange inheritance issue in PHP.
From the PHP manual:
Members declared protected can be accessed only within the class
itself and by inherited and parent classes.
To me this means:
A can access the protected members of B if A instanceof B or B instanceof A.
However, if both A and B extend Foo, and Foo has a protected constructor which is not overwritten in B, then I can create an instance of B from within A. This does not make sense to me, because A is not an instance of B and B is not an instance of A. I can also call the protected method $b->test() from within A, which executes the method implemented in B. (If B does not redeclare test() then the implementation in Foo is executed.) To me this is even more strange because I cannot create an instance of B from within A if B directly implements a protected constructor. It seems strange that I cannot access a protected constructor (also declared in the parent class) but accessing a protected method (also declared in the parent class) is no problem.
Note that I do get the expected behavior when I use a class C which does not extend Foo. If I try to instantiate B from within C, I get a fatal error because I'm trying to access a protected constructor. If I add a public constructor to B it is possible to instantiate it (which is expected) and I still cannot access the protected method test() (this is also expected behavior). I expect the same behavior when using A instead of C.
Sample code which explains again:
class Foo {
protected function __construct() {
echo('Constructing ' . get_called_class());
}
protected function test() {
echo('Hello world ' . __METHOD__);
}
}
class A extends Foo {
public function __construct() {
parent::__construct();
}
public function testB() {
// Both of these lines work
$b = new B();
$b->test();
}
}
class B extends Foo {
protected function test() {
echo('Hello world Again ' . __METHOD__);
}
}
class C {
public function __construct() {
}
public function testB() {
// Both of these lines cause fatal errors
$b = new B();
$b->test();
}
}
$a = new A();
$a->testB();
$c = new C();
$c->testB();
I'm probably not seeing something, but I can't find what. Could anyone explain the behavior to me?
You can access those methods because there is a declaration of them as protected in Foo, which is your parent and that gives you permission to access it. If you remove the declaration from the parent and declare the protected method in B you will get a Fatal Error.
This is reported as a bug in PHP https://bugs.php.net/bug.php?id=50892
There is no rationale about this, it has been reported 2 years ago: https://bugs.php.net/bug.php?id=52120
Related
In this example it first searches in B (because static:: resolves to B) and as it does not find it, it now searches in "A" and that is why it succeeds, right? or am I wrong?
class A {
private function foo() {
echo "success!\n";
}
public function test() {
$this->foo();
static::foo(); // Why does it execute method of "A" if static:: be resolved to "B"?
}
}
class B extends A {
/* foo() will be copied to B, hence its scope will still be A and
* the call be successful */
}
$b = new B();
$b->test();
OUTPUT:
success! success!
Late static binding allows you to refer to the called object's class instead of the class where the method is defined in. But it doesn't change how the scope is resolved, which means that the property/method visibility rules are obeyed as they would if you used self. When you use static, the name of the class will be resolved using runtime information.
For more information read late static bindings on php.net.
In your case when you have this:
public function test()
{
$this->foo();
static::foo();
}
It's actually the same as this when called on an instance of B:
public function test()
{
$this->foo();
B::foo();
}
Upon execution, PHP will start resolving the scope starting with B class. If B doesn't have a method with that name, it will check its parent, and so on. Your B class doesn't have method foo, but A does. So when you execute it, PHP will effectively try to execute something like this:
public function test()
{
$this->foo();
A::foo();
}
Now you can see that both lines are the same. The both call the same private method of A class. Yes, the method is private but it's called from within its own class.
static:: is late binding. If you aren't overriding then it works like same as $this. Classes are blue print of object there are no any Class A or B when you create Object. First it search override methods then public and protected methods of Object and last it search private methods of block.
class A {
private function foo() {
echo "success!\n";
}
public function test() {
$this->foo();
static::foo(); // Why does it execute method of "A" if static:: be resolved to "B"?
}
}
class B extends A {
public function foo() {
echo " b success!\n";
}
}
$b = new B();
$b->test();
Check This
success!
b success!
This question already has answers here:
Override method parameter with child interface as a new parameter
(2 answers)
Abstract function parameter type hint overriding in PHP 7
(2 answers)
Closed 3 years ago.
This code of dependency inversion should work fine but instead it gives error.
What am I doing wrong in here?
interface A { }
abstract class B implements A { }
class C extends B { }
abstract class D {
public function foo(A $a) { }
}
class E extends D {
public function foo(C $c) { }
}
The error is:
Warning: Declaration of E::foo(C $c) should be compatible with D::foo(A $a) in [...][...] on line 24
Surprisingly, doing the same for the constructor method works just fine:
interface A { }
abstract class B implements A { }
class C extends B { }
abstract class D {
public function __construct(A $a) { }
public function foo(A $a) { }
}
class E extends D {
public function __construct(C $c) { }
public function foo(A $c) { }
}
There are two questions here.
Why the first part raises a WARNING: the error is pretty explicit. Any time you override a method you need to respect the parent's signature, so a client application's expectations are met.
class Foo {}
class Bar extends Foo {}
class A {
public function test(Foo $foo) {}
}
class B extends A {
public function test(Bar $bar) {}
}
Now, since B extends A, and A::test() expects a Foo, a user might try to do this:
$a = new A();
$b = new B();
$foo = new Foo();
$bar = new Bar();
$b->test($bar);
$b->test($foo);
The first call to test() would work, but the second would fail (and fatally): a Foo is not a Bar.
The parameter type is more restrictive than the original, and thus breaks expectations from client applications. Note that if we had a legal declaration (B::test(Foo)), we could still call $b->test($bar) and would work as expected, so changing the signature is not really necessary.
In November 2019, When PHP 7.4 is released, contravariance will be supported for parameter types, and covariance will be supported for return types; so your example would still be invalid.
Finally, notice that the error raised is a WARNING, it won't crash the program immediately. But it is warning you that you are opening your application to behaviour that might crash it, and that's it's generally poor design (for the reasons explained above).
The second question: Why you do not get the same warning for the constructor? They do not raise this warning by design. If you check the docs, you'll find this:
Unlike with other methods, PHP will not generate an E_STRICT level error message when __construct() is overridden with different parameters than the parent __construct() method has.
Why constructors are treated differently? Because it is generally understood that constructors are not part of the "public API" of an object, and do not need to be subjected to the same constraints. The Liskov Substitution Principle deals with objects, not with classes; and before an class is instantiated there is yet no object. Thus, the constructor is out of scope.
You can read more about this here, for example.
TL;DR C is implementing A, but it can also inherit other methods from B which implementing A doesn't guarantee. This creates situation where two classes of same inherited type can have two methods with different signatures. A sub-class must accept all inputs that the parent class would accept, but it can accept additional inputs if it wants,
Let's extend your example.
Nothing crazy, just some example methods.
interface A
{
public function gotA();
}
abstract class B implements A
{
abstract public function gotB();
}
class C extends B
{
public function gotA()
{
// ...
}
public function gotB()
{
// ...
}
}
I can agree that C is indirectly implementing A, but it also contains methods from B, so the call in E::foo() would be perfectly fine.
abstract class D
{
public function foo(A $a)
{
$a->gotA();
}
}
class E extends D
{
public function foo(C $c)
{
$c->gotB();
}
}
Another example class implementing A.
class ExampleA implements A
{
public function gotA()
{
// ...
}
}
Now the fun part. Assume this function anywhere else in your code. It assumes D (expecting A in foo) as parameter.
But E is also instanceof D and can be passed to x, but it expects C in foo which ExampleA has nothing to do with.
function x(D $d)
{
$exampleA = new ExampleA();
$d->foo($exampleA);
}
$e = new E();
x($e);
To sum up, the contract for function x has been met and yet the function broke.
You ended up with two 'different' D types. The one expecting A and the one expecting C.
As for why it doesn't throw warning with __constructor I don't know. Need further investigation.
There are several questions about weird behaviour when a child has an implementation of a private parent method like in the following example:
class A {
private function toOverridePrivate() {
echo "private toOverridePrivate A\n";
}
public function callInA() {
$this->toOverridePrivate();
echo "\n";
}
}
class B extends A {
private function toOverridePrivate() {
echo "private toOverridePrivate B\n";
}
public function callInB() {
$this->toOverridePrivate();
echo "\n";
}
}
$a = new A;
$b = new B;
$a->callInA(); // private toOverridePrivate A
$b->callInA(); // private toOverridePrivate A
$b->callInB(); // private toOverridePrivate B
When calling $b->callPrintsInA(), As implementation of toOverridePrivate is called, because B::toOverridePrivate is not accessible from A.
In summary it can be said, that the method must be accessible from the scope it is called, therefore the following fails:
class A {
public function callInA() {
$this->toOverridePrivate();
echo "\n";
}
}
class B extends A {
private function toOverridePrivate() {
echo "private toOverridePrivate B\n";
}
}
$b = new B;
$b->callInA(); // ERROR: Call to private method B::toOverridePrivate() from context 'A'
In the last example toOverridePrivate is not accessible in the scope of A, despite the fact that $this is in fact an object of type B.
But one aspect which varies between answers is, if having another implementation of a private parent method in a child class in overriding. In the most popular question I could find "Strange behavior when overriding private methods" the accepted answer says it is overriding...
Now if you override the private method, its new scope will not be A, it will be B
...while the second most voted answer states that...
A private method is not overridable
... because it is unkown outside of its class scope.
The last statement is questionable, because declaring a private method final makes overriding it not possible with the exact message that it can not be overridden:
class A {
final private function someMethod() { }
}
class B extends A {
// Fatal error: Cannot override final method A::someMethod()
private function someMethod() { }
}
So...
Does a child class override a private parent method, if it has an implementation of that method? If this isn't overriding, what vocabulary would you use to describe it?
The child class simply has no knowledge of any private methods from parent class. Any such method in the scope of child class is undefined. It can't be overridden if it is not defined in the first place.
If the method is public it is visible to everyone and any code can call it. When the method is protected in is only known to the class itself and any of its descendants. When a method is private it is only known in the scope of this class. To any other class this method is simply undefined.
You can define another method with the same name in the child class, but it is not overriding anything. You could still call it overriding, but it would not make much sense. Overriding means that if you do not redefine the method in the child class it will call the definition from parent class. With private methods it is not possible because they are not accessible from the child class.
Consider this example:
class A {
private function toOverridePrivate() {
echo "private toOverridePrivate A\n";
}
}
class B extends A {
public function toOverridePrivate() {
parent::toOverridePrivate();
}
}
$o = new B;
$o->toOverridePrivate();
Fatal error: Uncaught Error: Call to private method A::toOverridePrivate() from context 'B'
Whether the class B defines method toOverridePrivate or not, it makes no difference, because the method toOverridePrivate from A is always inaccessible.
I have 2 classes declared like in the example below.
class A{
protected $process;
public function __construct() {
......
$this->process=new B();
}
public function do_something(){
....
}
}
class B{
// content not important
// I need to call do_something from class A
}
My question is, how can I call from class B the method do_something() from class A? Is it possible?
From your example it is impossible for instance of B to know that it is instantiated and stored by an instance of class A. You need to create that connection explicitly in some way.
I didn't think this would even work, but apparently you can pass instance of A to B before A is even done with its constructor:
class A {
protected $process;
public function __construct() {
$this->process = new B( $this );
}
public function do_something() {
var_dump( 'do_something' );
}
public function test() {
$this->process->test();
}
}
class B {
public function __construct( A $a ) {
$this->a = $a;
}
public function test() {
$this->a->do_something();
}
}
$a = new A();
$a->test(); // do_something
It's hard to give an advice on what the best approach for your particular case would be, as we don't know what either A or B does.
There's a few ways to achieve this. One way would be to make B and extension of A - thereby allowing all methods of the class A to be callable on the object B. Another way is to create a new object of A inside B and call that method. Or you can pass
Here's an example where B is extended from A. By doing this, all properties and methods of A can be called on B, unless overwritten in B.
class A {
public function doSomething(){
echo "doSomething() called in A";
}
}
class B extends A {
public function someMethod() {
$this->doSomething();
}
}
$b = new B();
$b->someMethod();
The above would output doSomething() called in A.
Or, you can create an object A and call that method inside B.
class B {
public function someMethod() {
$a = new A();
$a->do_something();
}
}
$b = new B();
$b->someMethod();
After reading all the answers and doing some research i think that the best method for me was the use of Traits
"Traits are a mechanism for code reuse in single inheritance languages such as PHP. A Trait is intended to reduce some limitations of single inheritance by enabling a developer to reuse sets of methods freely in several independent classes living in different class hierarchies."
So i declared a Trait with the method do_something and call that method from Both class A and Class B
Thanks
I'm learning the differences between self, static and this and ran across this example from php.net:
<?php
class A {
private function foo() {
echo "success!\n";
}
public function test() {
$this->foo();
static::foo();
}
}
class B extends A {
/* foo() will be copied to B, hence its scope will still be A and
* the call be successful */
}
class C extends A {
private function foo() {
/* original method is replaced; the scope of the new one is C */
}
}
$b = new B();
$b->test();
$c = new C();
$c->test(); //fails
?>
The result is
success!
success!
success!
Fatal error: Call to private method C::foo() from context 'A' in /tmp/test.php on line 9
My question is this: when creating the new object C, shouldn't the call $this->foo(); call the newly replaced private function foo inside class C (and return an error since it's private)?
You should use protected rather than public to get this behaviour.
This is covered in a lot of detail here: What is the difference between public, private, and protected?
A quick excerpt:
public scope to make that variable/function available from anywhere, other classes and instances of the object.
private scope when you want your variable/function to be visible in its own class only.
protected scope when you want to make your variable/function visible in all classes that extend current class including the parent class.