在 PHP 中是否可以使用 mixins?

36

我了解到混入(mixins)。我的疑问是,是否可以在PHP中使用混入?如果是的话,该如何使用?


8
PHP 5.4 将引入 Traits 特性(对于许多情况而言,它将与 Mixin 一样好用)。 - Mchl
5个回答

61

使用 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/zh/language.oop5.traits.php


11

截至 PHP 5.4,此答案已过时。请查看Jeanno的回答以了解如何使用 traits。


取决于您希望从PHP获得什么级别的混合(mixins)。PHP处理单一继承和抽象类,这可以让您实现大部分功能。

当然,混入最好的部分是它们是可互换的代码片段,可以添加到任何需要它们的类中。

要解决多重继承问题,您可以使用include来导入代码片段。在某些情况下,您可能需要添加一些样板代码才能使其正常工作,但这肯定会有助于保持程序DRY(不重复)。

示例:

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__


9
这个答案已过时,请参考PHP 5.4和traits。 - HappyDeveloper
请参考这个答案来了解traits。 - Matt

2

PHP Mixins(PHP本身不支持Mixins,但这个库可以帮助实现)


那里有一些有趣的评论。更不用说为什么幸运的家伙会是作者了。 - Mchl

2

看到第一个谷歌搜索结果在这篇文章中失败会很有趣。如果有人指向另一个谷歌搜索结果,会引起堆栈溢出吗?像梦中梦一样! - Ismael
1
虚拟-1:对于“php5 mixin”和“php mixin”,我得到了完全不同的网站。看起来你的答案元数据已经腐烂了。 - Sebastian Mach

0

我基于在jansch.nl发现的博客条目来实现了Mixin功能。

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。这里有一些奇怪的巫术。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接