eloquent / pops
PHP 对象代理系统。
Requires
- php: ^7.4 || ^8
Requires (Dev)
README
不再维护
此包不再维护。更多信息请参阅 此声明。
Pops
PHP 对象代理系统。
安装和文档
- 作为 Composer 包 eloquent/pops 提供。
什么是 Pops?
Pops 是一个将 PHP 对象包装在其他对象中以便修改其行为的系统。尽可能模仿其包装的对象的 Pops 代理。它将方法调用传递下去并返回底层结果,并允许透明访问属性(包括设置和获取)。
Pops 是 Liberator 的底层系统。
创建代理
让我们编写一个简单的代理,它将所有内容转换为 uppercase。这里有一个类
class Confusion { public function wat() { return "What is this? I don't even..."; } public $derp = 'Has anyone really been far even as decided to use even?'; }
这里是我们的代理
use Eloquent\Pops\ProxyObject; class UppercaseProxyObject extends ProxyObject { public function popsCall($method, array &$arguments) { return strtoupper(parent::popsCall($method, $arguments)); } public function __get($property) { return strtoupper(parent::__get($property)); } }
我们在这里使用 popsCall()
而不是 __call()
,以规避 PHP 有关通过引用传递参数的限制。有关更深入的解释,请参阅 通过引用参数调用方法。
现在,当我们通过代理和正常方式访问 wat()
和 $derp
时,我们可以看到效果
$confusion = new Confusion; $proxy = new UppercaseProxyObject($confusion); echo $confusion->wat(); // outputs "What is this? I don't even..." echo $proxy->wat(); // outputs "WHAT IS THIS? I DON'T EVEN..." echo $confusion->derp; // outputs 'Has anyone really been far even as decided to use even?' echo $proxy->derp; // outputs 'HAS ANYONE REALLY BEEN FAR EVEN AS DECIDED TO USE EVEN?'
递归代理
Pops 代理可以递归地应用于任何值。当设计输出转义器(类似于 Symfony)时,这很有用。以下是一个如何为转义 HTML 输出创建此类系统的示例
namespace OutputEscaper; use Eloquent\Pops\Proxy; /** * Escapes output for use in HTML. */ class OutputEscaperProxy extends Proxy { /** * Get the array proxy class. * * @return string The array proxy class. */ protected static function proxyArrayClass() { return __NAMESPACE__ . '\OutputEscaperProxyArray'; } /** * Get the class proxy class. * * @return string The class proxy class. */ protected static function proxyClassClass() { return __NAMESPACE__ . '\OutputEscaperProxyClass'; } /** * Get the object proxy class. * * @return string The object proxy class. */ protected static function proxyObjectClass() { return __NAMESPACE__ . '\OutputEscaperProxyObject'; } /** * Get the proxy class for primitive values. * * @return string The proxy class for primitive values. */ protected static function proxyPrimitiveClass() { return __NAMESPACE__ . '\OutputEscaperProxyPrimitive'; } }
namespace OutputEscaper; use Eloquent\Pops\ProxyArray; /** * Wraps an array to escape any sub-values for use in HTML. */ class OutputEscaperProxyArray extends ProxyArray { /** * Get the proxy class. * * @return string The proxy class. */ protected static function popsProxyClass() { return __NAMESPACE__ . '\OutputEscaperProxy'; } }
namespace OutputEscaper; use Eloquent\Pops\ProxyClass; /** * Wraps a class to escape any sub-values for use in HTML. */ class OutputEscaperProxyClass extends ProxyClass { /** * Get the proxy class. * * @return string The proxy class. */ protected static function popsProxyClass() { return __NAMESPACE__ . '\OutputEscaperProxy'; } }
namespace OutputEscaper; use Eloquent\Pops\ProxyObject; /** * Wraps an object to escape any sub-values for use in HTML. */ class OutputEscaperProxyObject extends ProxyObject { /** * Get the proxy class. * * @return string The proxy class. */ protected static function popsProxyClass() { return __NAMESPACE__ . '\OutputEscaperProxy'; } }
namespace OutputEscaper; use Eloquent\Pops\ProxyPrimitive; /** * Wraps a primitive to escape its value for use in HTML. */ class OutputEscaperProxyPrimitive extends ProxyPrimitive { /** * Get the HTML-escaped version of this primitive. * * @return string The HTML-secaped version of this primitive. */ public function __toString() { return htmlspecialchars( strval($this->popsValue()), ENT_QUOTES, 'UTF-8' ); } }
现在可以如此使用输出转义器
use OutputEscaper\OutputEscaperProxy; use Eloquent\Pops\Safe\SafeProxy; $list = new ArrayIterator( array( 'foo', 'bar', '<script>alert(document.cookie);</script>', SafeProxy::proxy('<em>ooh...</em>'), ) ); $proxy = OutputEscaperProxy::proxy($list, true); echo "<ul>\n"; foreach ($proxy as $item) { printf("<li>%s</li>\n", $item); } echo "</ul>\n";
这将输出
<ul> <li>foo</li> <li>bar</li> <li><script>alert(document.cookie);</script></li> <li><em>ooh...</em></li> </ul>
请注意,上面的示例 不应 在生产中使用。输出转义是一个复杂的问题,不应轻率对待。
排除递归中的值
请注意,在上面的示例中,最后一个列表项被包装在一个 Safe 代理中。当 Pops 应用其代理时,它将跳过以这种方式标记为安全的内容。
通过引用参数调用方法
由于 PHP 的限制,必须以特殊方式调用具有引用参数的方法。
为了进一步解释,假设我们之前的类还有一个接受引用的方法
class Confusion { public function butWho(&$wasPhone) { $wasPhone = 'Hello? Yes this is dog.'; } }
由于 $wasPhone
参数是通过引用传递的,此方法不能以常规方式代理。通过 Pops 代理调用上述 butWho() 方法的正确方式如下
use Eloquent\Pops\Proxy; $proxy = Proxy::proxy(new Confusion); $wasPhone = null; $arguments = array(&$wasPhone); $proxy->popsCall('butWho', $arguments); echo $wasPhone; // outputs 'Hello? Yes this is dog.'
请注意,必须有一个变量用于$wasPhone
参数,以及必须有一个变量用于参数本身。这两个都不能直接作为值传递。参数还必须包含对$wasPhone
参数的引用。