Strange behavior when overriding private methods
Inheriting/overriding private methods
In PHP, methods (including private ones) in the subclasses are either:
- Copied; the scope of the original function is maintained.
- Replaced ("overridden", if you want).
You can see this with this code:
<?php
class A {
//calling B::h, because static:: resolves to B::
function callH() { static::h(); }
private function h() { echo "in A::h"; }
}
class B extends A {
//not necessary; just to make explicit what's happening
function callH() { parent::callH(); }
}
$b = new B;
$b->callH();
Now if you override the private method, its new scope will not be A, it will be B, and the call will fail because A::callH()
runs in scope A
:
<?php
class A {
//calling B::h, because static:: resolves to B::
function callH() { static::h(); }
private function h() { echo "in A::h"; }
}
class B extends A {
private function h() { echo "in B::h"; }
}
$b = new B;
$b->callH(); //fatal error; call to private method B::h() from context 'A'
Calling methods
Here the rules are as follows:
- Look in the method table of the actual class of the object (in your case,
bar
).- If this yields a private method:
- If the scope where the method was defined is the same as the scope of the calling function and is the same as the class of the object, use it.
- Otherwise, look in the parent classes for a private method with the same scope as the one of the calling function and with the same name.
- If no method is found that satisfies one of the above requirements, fail.
- If this yields a public/protected method:
- If the scope of the method is marked as having changed, we may have overridden a private method with a public/protected method. So in that case, and if, additionally, there's a method with the same name that is private as is defined for the scope of the calling function, use that instead.
- Otherwise, use the found method.
- If this yields a private method:
Conclusion
- (Both private) For
bar->call()
, the scope ofcall
isfoo
. Calling$this->m()
elicits a lookup in the method table ofbar
form
, yielding a privatebar::m()
. However, the scope ofbar::m()
is different from the calling scope, whichfoo
. The methodfoo:m()
is found when traversing up the hierarchy and is used instead. - (Private in
foo
, public inbar
) The scope ofcall
is stillfoo
. The lookup yields a publicbar::m()
. However, its scope is marked as having changed, so a lookup is made in the function table of the calling scopefoo
for methodm()
. This yields a private methodfoo:m()
with the same scope as the calling scope, so it's used instead. - Nothing to see here, error because visibility was lowered.
- (Both public) The scope of
call
is stillfoo
. The lookup yields a publicbar::m()
. Its scope isn't marked as having changed (they're both public), sobar::m()
is used.
Is a private parent method overridden if child has an implementation of 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.
How to override private method from a library class
You can manipulate the behaviour indirectly. This is the snippet you are interested in.
$allCss = $this->css;
if ($this->isStyleBlocksParsingEnabled) {
$allCss .= $this->getCssFromAllStyleNodes($xpath);
}
Looking at the class setters, you can call disableStyleBlocksParsing
to prevent the function being called.
The $allCss
variable is taken straight from $this->css
, which is only modified by the setCss
method.
So you have two choices:
- Extend the class, make
isStyleBlocksParsingEnabled
false and immutable, then override thesetCss
method to do what you wantedgetCssFromAllStyleNodes
to do. - Call
disableStyleBlocksParsing
and and callsetCss
with preprocessed text.
Here is an example of the first option:
class MyEmogrifier extends Emogrifier
{
public function __construct($html = '', $css = '')
{
parent::__construct($html, $css);
$this->disableStyleBlocksParsing();
}
public function setCss($css)
{
// Preprocess CSS here.
parent::setCss($css);
}
}
So there is no shotgun surgery, or reflection needed.
To be honest though. I would feel much less inclined to even use a library as concrete as this one. I use protected
for nearly all of my would be private methods.
Weird Eclipse behaviour when tring to override a method
Other IDE's seeing it as parent method and giving you the correct error message.
Coming to eclipse (that's weird from eclipse side), since there is no @ovveride
annotation it is completely treating that as a new method in child, hence no error. If you place @ovveride
annotation in child, you see the same error as other IDE's.
It is the same reason that you getting woof woof
twice, as it is calling the same method from parent twice as it is assuming you are not calling the new method created from child.
To avoid the confusion it is always recommend that to use the @ovveride annotation while overriding methods.
Overridden methods and variables - Inconsistent behavior
Overriding a method with a weaker access-modifier is prohibited by the standard (§8.4.8.3):
The access modifier (§6.6) of an overriding or hiding method must provide at least as much access as the overridden or hidden method, as follows:
If the overridden or hidden method is public, then the overriding or hiding method must be public; otherwise, a compile-time error occurs.
If the overridden or hidden method is protected, then the overriding or hiding method must be protected or public; otherwise, a compile-time error occurs.
If the overridden or hidden method has default (package) access, then the overriding or hiding method must not be private; otherwise, a compile-time error occurs.
This ensures that any method provided by the base-class can also be called on derived classes within the same context.
Variables can't be overriden. Base.className
and Derived.className
are two distinct variables. Thus it is perfectly valid to have a variable with the same name and different access-modifier in Derived
.
I.e. this code will print false
:
class Base{
public String str = "hello";
}
class Derived extends Base{
private String str = "whatever";
public Derived(){
super.str = "abc";
str = "def";
}
void foo(){
System.out.println(str.equals(super.str));
}
}
public static void main(String[] args){
new Derived().foo();
}
The relevant jls-sections:
Field declarations (§8.3):
The scope and shadowing of a field declaration is specified in §6.3 and §6.4.
If the class declares a field with a certain name, then the declaration of that field is said to hide any and all accessible declarations of fields with the same name in superclasses, and superinterfaces of the class.
In this respect, hiding of fields differs from hiding of methods (§8.4.8.3), for there is no distinction drawn between static and non-static fields in field hiding whereas a distinction is drawn between static and non-static methods in method hiding.
A hidden field can be accessed by using a qualified name (§6.5.6.2) if it is static, or by using a field access expression that contains the keyword super (§15.11.2) or a cast to a superclass type.
In this respect, hiding of fields is similar to hiding of methods.
If a field declaration hides the declaration of another field, the two fields need not have the same type.
And Shadowing (§6.4.1):
A declaration d of a field or formal parameter named n shadows, throughout the scope of d, the declarations of any other variables named n that are in scope at the point where d occurs.
static and private method behavior when calling direct on object of child class sounds like overriding?
Polymorphism is not for static methods. Static methods are called with JVM instructions invokestatic, whereas polymorphism is achieved with invokevirtual. The calls to static methods are determined at compile time, and polymorphic methods are dynamically dispatched at runtime.
You can easily tweak your code so that A.staticMethod() is called, by just assigning new B() to a variable of type A.
public static void main(String[] args) {
new B().privateMethod();
A b = new B(); // change here.
b.staticMethod(); // A.staticMethod() is called here.
}
Strange behavior on overriding methods and calling them with shared_ptr
The call
(*entities[0]).resolveCollision(*entities[1]);
resolves to Entity::resolveCollision(Entity &other);
at compile time. By virtue of virtual
function dispatch mechanism, the call is dispatched to Person::resolveCollision(Entity &other);
at run time. However, the dynamic dispatch system does not change the call to Person::resolveCollision(Bullet &other);
based on the run time information of other
. That would require a double dispatch system, which is not part of the language.
Why is the wrong method called: Strange inheritance behaviour
I'm guessing that you expected m4.E
to be printed. But don't forget that overload resolution is performed at compile-time, not at execution time.
Neither C
nor B
have a method4(E)
method available, so the compiler resolves the calls to the method4(A)
method... which isn't overridden by E
. All the calls you have m4.C // why?
in the question are calls to a method with signature method4(A)
, called on an instance of E
. Now E
doesn't override method4(A)
, so it's left with the implementation in C
, which prints m4.C
.
Nothing strange going on here at all.
Related Topics
Zf2 + Doctrine 2 - Child-Level Discriminators with Class Table Inheritance
Environment Driven Database Settings in Laravel
Sort Array by Length and Then Alphabetically
How to Change Default Redirect Url of Laravel 5 Auth Filter
Is There a Function to Make a Copy of a PHP Array to Another
How to Design a Hierarchical Role Based Access Control System
Passing JavaScript Variables to PHP
What Http Status Code Is Supposed to Be Used to Tell the Client the Session Has Timed Out
PHP Merge Array(S) and Delete Double Values
PHP If Single or Double Equals
Fatal Error - Too Many Open Files
Laravel 5.1 Unknown Database Type Enum Requested
PHP 7: Missing Vcruntime140.Dll
JSON_Encode/JSON_Decode - Returns Stdclass Instead of Array in PHP
Laravel 5.2 - Pluck() Method Returns Array
Symfony Redirect with 2 Parameters
Problems with Lib-Icu Dependency When Installing Symfony 2.3.X via Composer