[英]C#-like extension methods in PHP?
我喜欢在C#中编写扩展方法的方式,然后执行以下操作:
string ourString = "hello";
ourString.MyExtension("another");
甚至
"hello".MyExtention("another");
有没有办法在PHP中有类似的行为?
如果您将所有字符串重新实现为对象,则可以。
class MyString {
...
function foo () { ... }
}
$str = new MyString('Bar');
$str->foo('baz');
但你真的不想这样做。 PHP本身并不是面向对象的语言,字符串只是原始类型而没有方法。
没有扩展核心引擎(这不是你想要进入的东西,至少不是为了这个目的:),在PHP中不可能实现'Bar'->foo('baz')
语法。
也没有任何关于扩展对象的功能,这使得它本身比简单地编写接受基元的新函数更好。 换句话说,PHP相当于
"hello".MyExtention("another");
是
my_extension("hello", "another");
出于所有目的和目的,它具有相同的功能,只是不同的语法。
远离PHP中非对象原语的问题,当处理PHP中的实际类时,如果您的环境是理智的,那么您可以装饰给定的类来排序§模拟扩展方法。
给定一个接口和实现:
interface FooInterface {
function sayHello();
function sayWorld();
}
class Foo implements FooInterface {
public function sayHello() {
echo 'Hello';
}
public function sayWorld() {
echo 'World';
}
}
只要对Foo
任何依赖实际上依赖于接口FooInterface
( 这就是我所说的理智 )你就可以自己实现FooInterface
作为Foo
的包装,必要时将调用转发给Foo
,并根据需要添加其他方法:
class DecoratedFoo implements FooInterface {
private $foo;
public function __construct(FooInterface $foo) {
$this->foo = $foo;
}
public function sayHello() {
$this->foo->sayHello();
}
public function sayWorld() {
$this->foo->sayWorld();
}
public function sayDanke() {
echo 'Danke';
}
public function sayShoen() {
echo 'Shoen';
}
}
方法sayHello()
和sayWorld()
被修补到包含Foo
对象,但是我们添加了sayDanke()
和sayShoen()
。
下列:
function acceptsFooInterface(FooInterface $foo) {
$foo->sayHello();
$foo->sayWorld();
}
$foo = new Foo();
acceptsFooInterface($foo);
按预期工作,产生HelloWorld
; 但这样做:
$decoratedFoo = new DecoratedFoo($foo);
acceptsFooInterface($decoratedFoo);
$decoratedFoo->sayDanke();
$decoratedFoo->sayShoen();
哪个结果是HelloWorldDankeShoen
。
这是装饰图案中潜在的有限使用; 您可以修改已实现方法的行为,或者根本不转发它们( 但是,我们希望在此示例中通过原始类定义维护预期的行为 )
这是一个在PHP中实现扩展方法( 按照C# )的一对一解决方案吗? 不,绝对不是; 但这种方法提供的可扩展性将有助于更松散地解决问题。
§想象我会根据主题的一些聊天对话进行详细说明:你永远不会在PHP中复制它( 不是今天,也可能不是tommorow ),但我的答案中的关键是设计模式。 当您不一定( 通常或曾经 )端口功能时,它们提供了将策略从一种语言移植到另一种语言的机会。
从PHP 5.4开始,有一些特性可以用作扩展方法。
例:
<?php
trait HelloWorld {
public function sayHelloWorld() {
echo 'Hello World';
}
}
class MyHelloWorld {
use HelloWorld;
public function sayExclamationMark() {
echo '!';
}
}
$o = new MyHelloWorld();
$o->sayHelloWorld();
$o->sayExclamationMark();
?>
结果:
Hello World!
一旦你将一个特征包含在一个类中,我们可以使用名称Extension
调用它,你可以添加你想要的任何方法并在别处找到它们。 然后在该示例中, use Extension
成为可扩展类的一次性装饰。
我在PHP> = 5.3.0中有另一个实现,它就像Northborn Design解释的装饰器一样。
我们所需要的只是一个用于创建扩展的API和一个应用扩展的装饰器。
我们必须记住,在C#扩展方法上,它们不会破坏对象扩展的封装,并且它们不会修改对象(没有意义,相反,实现该方法会更有效)。 扩展方法是纯静态的,它们接收对象的实例,如下例所示(C#, 来自MSDN ):
public static int WordCount(this String str)
{
return str.Split(new char[] { ' ', '.', '?' },
StringSplitOptions.RemoveEmptyEntries).Length;
}
当然我们在PHP中没有String对象,但是对于所有其他对象,我们可以为这种魔法创建通用装饰器。
让我们看看我的实现:
API:
<?php
namespace Pattern\Extension;
/**
* API for extension methods in PHP (like C#).
*/
class Extension
{
/**
* Apply extension to an instance.
*
* @param object $instance
* @return \Pattern\Extension\ExtensionWrapper
*/
public function __invoke($instance)
{
return Extension::apply($instance);
}
/**
* Apply extension to an instance.
*
* @param object $instance
* @return \Pattern\Extension\ExtensionWrapper
*/
public static function apply($instance)
{
return new ExtensionWrapper($instance, \get_called_class());
}
/**
* @param mixed $instance
* @return boolean
*/
public static function isExtensible($instance)
{
return ($instance instanceof Extensible);
}
}
?>
装饰者:
<?php
namespace Pattern\Extension;
/**
* Demarcate decorators that resolve the extension.
*/
interface Extensible
{
/**
* Verify the instance of the holded object.
*
* @param string $className
* @return bool true if the instance is of the type $className, false otherwise.
*/
public function holdsInstanceOf($className);
/**
* Returns the wrapped object.
* If the wrapped object is a Extensible the returns the unwrap of it and so on.
*
* @return mixed
*/
public function unwrap();
/**
* Magic method for the extension methods.
*
* @param string $name
* @param array $args
* @return mixed
*/
public function __call($name, array $args);
}
?>
通用实现:
<?php
namespace Pattern\Extension;
/**
* Generic version for the Extensible Interface.
*/
final class ExtensionWrapper implements Extensible
{
/**
* @var mixed
*/
private $that;
/**
* @var Extension
*/
private $extension;
/**
* @param object $instance
* @param string | Extension $extensionClass
* @throws \InvalidArgumentException
*/
public function __construct($instance, $extensionClass)
{
if (!\is_object($instance)) {
throw new \InvalidArgumentException('ExtensionWrapper works only with objects.');
}
$this->that = $instance;
$this->extension = $extensionClass;
}
/**
* {@inheritDoc}
* @see \Pattern\Extension\Extensible::__call()
*/
public function __call($name, array $args)
{
$call = null;
if (\method_exists($this->extension, '_'.$name)) {
// this is for abstract default interface implementation
\array_unshift($args, $this->unwrap());
$call = array($this->extension, '_'.$name);
} elseif (\method_exists($this->extension, $name)) {
// this is for real implementations
\array_unshift($args, $this->unwrap());
$call = array($this->extension, $name);
} else {
// this is for real call on object
$call = array($this->that, $name);
}
return \call_user_func_array($call, $args);
}
/**
* {@inheritDoc}
* @see \Pattern\Extension\Extensible::unwrap()
*/
public function unwrap()
{
return (Extension::isExtensible($this->that) ? $this->that->unwrap() : $this->that);
}
/**
* {@inheritDoc}
* @see \Pattern\Extension\Extensible::holdsInstanceof()
*/
public function holdsInstanceOf($className)
{
return \is_a($this->unwrap(), $className);
}
}
?>
使用:
假设存在第三方类:
class ThirdPartyHello
{
public function sayHello()
{
return "Hello";
}
}
创建扩展程序:
use Pattern\Extension\Extension;
class HelloWorldExtension extends Extension
{
public static function sayHelloWorld(ThirdPartyHello $that)
{
return $that->sayHello().' World!';
}
}
另外:对于Interface Lovers,创建一个Abstract Extension:
<?php
interface HelloInterfaceExtension
{
public function sayHelloFromInterface();
}
?>
<?php
use Pattern\Extension\Extension;
abstract class AbstractHelloExtension extends Extension implements HelloInterfaceExtension
{
public static function _sayHelloFromInterface(ThirdPartyOrLegacyClass $that)
{
return $that->sayHello(). ' from Hello Interface';
}
}
?>
然后使用它:
////////////////////////////
// You can hide this snippet in a Dependency Injection method
$thatClass = new ThirdPartyHello();
/** @var ThirdPartyHello|HelloWorldExtension $extension */
$extension = HelloWorldExtension::apply($thatClass);
//////////////////////////////////////////
$extension->sayHello(); // returns 'Hello'
$extension->sayHelloWorld(); // returns 'Hello World!'
//////////////////////////////////////////
// Abstract extension
$thatClass = new ThirdPartyHello();
/** @var ThirdPartyHello|HelloInterfaceExtension $extension */
$extension = AbstractHelloExtension::apply($instance);
$extension->sayHello(); // returns 'Hello'
$extension->sayHelloFromInterface(); // returns 'Hello from Hello Interface'
优点:
缺点:
这里是Api的要点: https : //gist.github.com/tennaito/9ab4331a4b837f836ccdee78ba58dff8
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.