Reputation: 2869
Given are the following classes with class A
coming from an external library so that I can not change it:
class A {
public function test () {
$this->privateMethod();
}
private function privateMethod () {
echo('A');
}
}
class B extends A {
private function privateMethod () {
echo('B');
}
}
$b = new B();
$b->test();
This results in A
being printed out by A::privateMethod
instead of B
from B::privateMethod
, because the latter is not visible to A::test
as explained here.
How else can I modify the behavior of this private library method in the cleanest possible way (e.g. without code duplication from copying the whole class and changing it)?
Upvotes: 1
Views: 7940
Reputation: 12236
That is because private
is only in the scope of the class itself. I you had used protected
you would've overridden the function, because a protected
method means it's available for child classes.
Upvotes: 3
Reputation: 6946
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:
isStyleBlocksParsingEnabled
false and immutable, then override the setCss
method to do what you wanted getCssFromAllStyleNodes
to do.disableStyleBlocksParsing
and and call setCss
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.
Upvotes: 1
Reputation: 2788
You can change hardcode visibility
of property via ReflectionClass::setAccessible
this is a part of ReflectionClass.
Sets a property to be accessible. For example, it may allow protected and private properties to be accessed.
It is dangerous but in some cases you can use it.
Upvotes: 0
Reputation: 2715
You can change accessibility of a class method using ReflectionMethod::setAccessible()
:
$myEmogrifier = new \Pelago\Emogrifier;
$reflectedMethod = new ReflectionMethod($myEmogrifier, 'getCssFromAllStyleNodes');
$reflectedMethod->setAccessible(true);
$argument = new \DOMXpath(new \DOMDocument);
$returnValue = $reflectedMethod->invoke($myEmogrifier, $argument);
Take into account that this code will be 'fragile', since the author of the library will not take into account that a user of the library is relying on the result of a private function. It may be better to simply duplicate the function's code yourself than messing with the library itself.
Upvotes: 3