是否有可能在PHP中使用mixins

时间:2011-07-29 17:43:24

标签: php mixins

我开始了解mixins。所以我怀疑是,是否有可能在php中使用mixins?如果是,那么如何?

5 个答案:

答案 0 :(得分:42)

使用PHP 5.4中引入的Trait

<?php
class Base {
    public function sayHello() {
        echo 'Hello ';
    }
}

trait SayWorld {
    public function sayHello() {
        parent::sayHello();
        echo 'World!';
    }
}

class MyHelloWorld extends Base {
    use SayWorld;
}

$o = new MyHelloWorld();
$o->sayHello();
?>

打印Hello World!

http://php.net/manual/en/language.oop5.traits.php

答案 1 :(得分:11)

从PHP 5.4开始,这个答案已经过时了。 See Jeanno's answer for how to use traits


这实际上取决于您希望从PHP中获得mixins的级别。 PHP处理单继承和抽象类,它们可以帮助您完成大部分工作。

当然,mixins最好的部分是它们是可互换的片段,可以添加到任何需要它们的课程中。

要解决多重继承问题,您可以使用include来提取代码片段。在某些情况下,您可能必须转储一些样​​板代码才能使其正常工作,但它肯定有助于保持程序干燥。

示例:

class Foo
{
  public function bar( $baz )
  {
    include('mixins/bar');
    return $result;
  }
}

class Fizz
{
  public function bar( $baz )
  {
    include('mixins/bar');
    return $result;
  }
}

它不像能够将类定义为class Foo mixin Bar一样直接,但它应该可以让你在那里大部分时间。存在一些缺点:您需要保留相同的参数名称并返回变量名称,您需要传递依赖于func_get_args_array__FILE__等上下文的其他数据。

答案 2 :(得分:2)

Mixins for PHP(PHP本身没有实现Mixins,但这个库会有所帮助)

答案 3 :(得分:2)

“php5 mixin”的第一个google结果:http://www.sitepoint.com/forums/php-application-design-147/ruby-like-mixins-php5-332491.html

“php mixin”的第一个google结果:http://www.advogato.org/article/470.html

简短的回答:是的,但不是本地的(但显然,正如@mchl所说)。看看那些。

更长的答案:如果您使用runkit,则结帐runkit_method_copy():“将某个方法从一个类复制到另一个。”

答案 4 :(得分:0)

我在jansch.nl上的博客条目中使用了mixins功能。

class Node
{
    protected $__decorator_lookup = array();

    public function __construct($classes = array())
    {              
        foreach($classes as $class)
        if (class_exists($class))
        {
            $decorator = new $class($this);
            $methods = get_class_methods($decorator);
            if (is_array($methods))
                foreach($methods as $method) 
                    $this->__decorator_lookup[strtolower($method)] = $decorator;
        }
        else
            trigger_error("Tried to inherit non-existant class", E_USER_ERROR);
    }

    public function __get($name)
    {
        switch($name)
        {
             default:
                if ($this->__decorator_lookup[strtolower($name)])
                    return $this->__call($name);
        }
    }

    public function __call($method, $args = array()) 
    {
        if(isset($this->__decorator_lookup[strtolower($method)]))
            return call_user_func_array(array($this->__decorator_lookup[strtolower($method)], $method), $args);
        else
            trigger_error("Call to undefined method " . get_class($this) . "::$method()", E_USER_ERROR);
    }

    public function __clone()
    {
        $temp = $this->decorators;
        $this->decorators = array();

        foreach($temp as $decorator)
        {
            $new = clone($decorator);
            $new->__self = $this;
            $this->decorators[] = $new;
        }
    }
}

class Decorator
{
    public $__self;

    public function __construct($__self)
    {
        $this->__self = $__self;
    }

    public function &__get($key)
    {
        return $this->__self->$key;
    }

    public function __call($method, $arguments)
    {
        return call_user_func_array(array($this->__self, $method), $arguments);
    }

    public function __set($key, $value)
    {
        $this->__self->$key = $value;
    }
}

class Pretty extends Decorator
{
    public function A()
    {
        echo "a";
    }

    public function B()
    {
        $this->b = "b";
    }
}

$a = new Node(array("Pretty"));

$a->A(); // outputs "a"
$a->B();

echo($a->b); // outputs "b"

编辑:

  1. 由于PHP克隆很浅,添加了__clone支持。
  2. 另外,请记住,在mixin中,unset不起作用(或者至少我没有设法使它工作)。因此 - 执行unset($this->__self->someValue);之类的操作不会取消Node上的值。不知道为什么,因为在理论上它应该工作。有趣的unset($this->__self->someValue); var_dump(isset($this->__self->someValue));会正确生成false,但访问Node范围(Node->someValue)的值仍然会产生true。那里有一些奇怪的伏都教。