代码之家  ›  专栏  ›  技术社区  ›  caesay

PHP中类似C的扩展方法?

  •  20
  • caesay  · 技术社区  · 14 年前

    我喜欢C#中的方法,您可以编写一个扩展方法,然后执行以下操作:

    string ourString = "hello";
    ourString.MyExtension("another");
    

    "hello".MyExtention("another");
    

    在PHP中有没有类似的行为?

    4 回复  |  直到 14 年前
        1
  •  10
  •   deceze    11 年前

    class MyString {
        ...
        function foo () { ... }
    }
    
    $str = new MyString('Bar');
    $str->foo('baz');
    

    这个 'Bar'->foo('baz') 在PHP中,如果不扩展核心引擎,语法是不可能实现的(这不是您想要了解的内容,至少不是为了这个目的:)。


    扩展一个对象的功能也没有什么比简单地编写一个接受原语的新函数更好的了。换句话说,PHP等价于

    "hello".MyExtention("another");
    

    my_extension("hello", "another");
    

        2
  •  5
  •   Roland Pihlakas    9 年前

    由于PHP5.4有 traits 可以作为扩展方法。

    <?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!
    

    一旦您将一个trait包含到一个类中,我们就可以用一个名称来调用它 Extension ,您可以添加所需的任何方法并将其定位到其他位置。在这个例子中 use Extension

        3
  •  4
  •   Dan Lugg    11 年前

    § 模拟扩展方法。

    给定接口和实现:

    interface FooInterface { 
        function sayHello();
        function sayWorld();
    }
    
    class Foo implements FooInterface { 
        public function sayHello() { 
            echo 'Hello';
        }
        public function sayWorld() {
            echo 'World';
        }
    }
    

    只要依赖于 Foo FooInterface ( 这就是我所说的理智 )你可以实现 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() 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中?不,绝对不是;但是这种方法提供的可扩展性将有助于更松散地解决问题。


    我想我会根据一些关于这个话题的聊天来详细说明:你永远不会复制它( 今天不行,明天也不行 )然而,在PHP中,我的答案的关键是设计模式。它们提供了一个机会,在你不一定能理解的情况下,将策略从一种语言移植到另一种语言( 通常,或者永远 )端口功能。

        4
  •  4
  •   Antônio Walter    8 年前

    我有另一个PHP实现>=就像Northborn设计公司解释的那样。

    我们只需要一个API来创建扩展,一个decorator来应用扩展。

    from MSDN ):

    public static int WordCount(this String str)
    {
        return str.Split(new char[] { ' ', '.', '?' }, 
                         StringSplitOptions.RemoveEmptyEntries).Length;
    }
    

    当然,我们在PHP中没有String对象,但是对于所有其他对象,我们可以为这类向导创建通用的decorator。

    让我们看看我的实现:

    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!';
        }
    }
    

    另外:对于界面爱好者,创建一个抽象扩展:

    <?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'
    

    赞成的意见:

    • 与PHP中的C#扩展方法非常相似;
    • 扩展的使用似乎是对象的一部分,但它只是修饰(也许这对快速开发的团队来说很有意思,但是如果涉及遗留问题,那么在将来回顾扩展对象的实现);

    欺骗:

    • 扩展必须声明给对象,它不只是像C#中那样的导入,您必须“修饰”所需的实例来为它提供扩展。
    • 无法将扩展实例作为扩展对象的实例直接测试,需要使用API测试更多代码;
    • 性能缺陷是因为使用了魔术方法(但是当需要性能时,我们会改变语言,重新创建核心,使用极简框架,如果需要的话使用汇编程序);

    下面是该Api的要点: https://gist.github.com/tennaito/9ab4331a4b837f836ccdee78ba58dff8