鉴于以下类main
来自external library,因此我无法更改它:
A
这会导致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();
A
打印出来A::privateMethod
而不是来自B
的{{1}},因为后者对B::privateMethod
不可见,如所解释的那样A::test
3}}
我怎样才能以最干净的方式修改这个私有库方法的行为(例如,没有复制整个类并更改它的代码重复)?
答案 0 :(得分:4)
这是因为private
仅在类本身的范围内。我使用了protected
你会覆盖这个函数,因为protected
方法意味着它可用于子类。
答案 1 :(得分:1)
您可以使用ReflectionMethod::setAccessible()
更改类方法的辅助功能:
$myEmogrifier = new \Pelago\Emogrifier;
$reflectedMethod = new ReflectionMethod($myEmogrifier, 'getCssFromAllStyleNodes');
$reflectedMethod->setAccessible(true);
$argument = new \DOMXpath(new \DOMDocument);
$returnValue = $reflectedMethod->invoke($myEmogrifier, $argument);
考虑到此代码将是“脆弱的”,因为库的作者不会考虑库的用户依赖于私有函数的结果。简单地复制函数代码可能比弄乱库本身更好。
答案 2 :(得分:1)
您可以间接操纵行为。这是您感兴趣的片段。
$allCss = $this->css;
if ($this->isStyleBlocksParsingEnabled) {
$allCss .= $this->getCssFromAllStyleNodes($xpath);
}
查看类设置器,可以调用disableStyleBlocksParsing
来阻止调用该函数。
$allCss
变量直接来自$this->css
,只能通过setCss
方法进行修改。
所以你有两个选择:
isStyleBlocksParsingEnabled
为false且不可变,然后覆盖setCss
方法以执行您希望getCssFromAllStyleNodes
执行的操作。disableStyleBlocksParsing
,然后使用预处理文字致电setCss
。以下是第一个选项的示例:
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);
}
}
所以没有霰弹枪手术或需要反射。
说实话。我觉得甚至不太喜欢使用像这样具体的图书馆。我使用protected
几乎所有的私有方法。
答案 3 :(得分:0)
您可以通过ReflectionClass::setAccessible更改属性的硬编码visibility
这是ReflectionClass的一部分。
设置可访问的属性。例如,它可能允许受保护 和私人财产。
这很危险但在某些情况下你可以使用它。