如何覆盖库中的私有方法 class
How to override private method from a library class
给定以下 classes,其中 class A
来自 external library,因此我无法更改它:
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
从 B::privateMethod
打印出来,因为后者对 A::test
不可见,如前所述here.
我还能如何以最干净的方式修改此私有库方法的行为(例如,无需通过复制整个 class 和更改它来复制代码)?
那是因为private
只在class本身的范围内。如果您使用了 protected
,您会覆盖该函数,因为 protected
方法意味着它可用于子 classes.
您可以使用 ReflectionMethod::setAccessible()
:
更改 class 方法的可访问性
$myEmogrifier = new \Pelago\Emogrifier;
$reflectedMethod = new ReflectionMethod($myEmogrifier, 'getCssFromAllStyleNodes');
$reflectedMethod->setAccessible(true);
$argument = new \DOMXpath(new \DOMDocument);
$returnValue = $reflectedMethod->invoke($myEmogrifier, $argument);
考虑到此代码将是 'fragile',因为库的作者不会考虑库的用户依赖于私有函数的结果。自己简单地复制函数的代码可能比弄乱库本身更好。
您可以间接操纵行为。这是您感兴趣的片段。
$allCss = $this->css;
if ($this->isStyleBlocksParsingEnabled) {
$allCss .= $this->getCssFromAllStyleNodes($xpath);
}
查看 class 设置器,您可以调用 disableStyleBlocksParsing
以防止调用该函数。
$allCss
变量直接取自$this->css
,仅通过setCss
方法修改
所以你有两个选择:
- 扩展 class,使
isStyleBlocksParsingEnabled
为假且不可变,然后重写 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
用于几乎所有的私有方法。
您可以通过 ReflectionClass::setAccessible 更改 属性 的硬编码 visibility
这是 ReflectionClass.
的一部分
Sets a property to be accessible. For example, it may allow protected
and private properties to be accessed.
这很危险但在某些情况下你可以使用它。
给定以下 classes,其中 class A
来自 external library,因此我无法更改它:
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
从 B::privateMethod
打印出来,因为后者对 A::test
不可见,如前所述here.
我还能如何以最干净的方式修改此私有库方法的行为(例如,无需通过复制整个 class 和更改它来复制代码)?
那是因为private
只在class本身的范围内。如果您使用了 protected
,您会覆盖该函数,因为 protected
方法意味着它可用于子 classes.
您可以使用 ReflectionMethod::setAccessible()
:
$myEmogrifier = new \Pelago\Emogrifier;
$reflectedMethod = new ReflectionMethod($myEmogrifier, 'getCssFromAllStyleNodes');
$reflectedMethod->setAccessible(true);
$argument = new \DOMXpath(new \DOMDocument);
$returnValue = $reflectedMethod->invoke($myEmogrifier, $argument);
考虑到此代码将是 'fragile',因为库的作者不会考虑库的用户依赖于私有函数的结果。自己简单地复制函数的代码可能比弄乱库本身更好。
您可以间接操纵行为。这是您感兴趣的片段。
$allCss = $this->css;
if ($this->isStyleBlocksParsingEnabled) {
$allCss .= $this->getCssFromAllStyleNodes($xpath);
}
查看 class 设置器,您可以调用 disableStyleBlocksParsing
以防止调用该函数。
$allCss
变量直接取自$this->css
,仅通过setCss
方法修改
所以你有两个选择:
- 扩展 class,使
isStyleBlocksParsingEnabled
为假且不可变,然后重写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
用于几乎所有的私有方法。
您可以通过 ReflectionClass::setAccessible 更改 属性 的硬编码 visibility
这是 ReflectionClass.
Sets a property to be accessible. For example, it may allow protected and private properties to be accessed.
这很危险但在某些情况下你可以使用它。