35

I came to know about mixins.So my doubt is, is it possible to use mixins in php?If yes then how?

NewUser
  • 12,713
  • 39
  • 142
  • 236

5 Answers5

60

Use Trait introduced in PHP 5.4

<?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();
?>

which prints Hello World!

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

Jeanno
  • 782
  • 1
  • 8
  • 17
11

This answer is obsolete as of PHP 5.4. See Jeanno's answer for how to use traits.


It really depends on what level of mixins you want from PHP. PHP handles single-inheritance, and abstract classes, which can get you most of the way.

Of course the best part of mixins is that they're interchangeable snippets added to whatever class needs them.

To get around the multiple inheritance issue, you could use include to pull in snippets of code. You'll likely have to dump in some boilerplate code to get it to work properly in some cases, but it would certainly help towards keeping your programs DRY.

Example:

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

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

It's not as direct as being able to define a class as class Foo mixin Bar, but it should get you most of the way there. There are some drawbacks: you need to keep the same parameter names and return variable names, you'll need to pass other data that relies on context such as func_get_args_array or __FILE__.

zzzzBov
  • 174,988
  • 54
  • 320
  • 367
2

Mixins for PHP (PHP does not implement Mixins natively, but this library will help)

Rudu
  • 15,682
  • 4
  • 47
  • 63
  • Some interesting comments down there. Not to mention why the lucky stiff being the author. – Mchl Jul 29 '11 at 17:52
2

First google result for "php5 mixin": http://www.sitepoint.com/forums/php-application-design-147/ruby-like-mixins-php5-332491.html

First google result for "php mixin": http://www.advogato.org/article/470.html

Short answer: yes, but not natively (yet, evidently, as @mchl notes). Check those out.

Longer answer: if you're using runkit, checkout runkit_method_copy(): "Copies a method from class to another."

Problematic
  • 17,567
  • 10
  • 73
  • 85
  • It would be funny to see first google result fall in this post. Would it cause stackoverflow if someone point to another google result? Inception! – Ismael Sep 30 '14 at 18:09
  • 1
    Virtual -1: For "php5 mixin" as well as "php mixin", I get a completely different site. Seems like your answer meta-rotted. – Sebastian Mach Oct 29 '14 at 08:56
0

I based mixins functionality on the blog entry found at jansch.nl.

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"

EDIT:

  1. As PHP clone is shallow, added __clone support.
  2. Also, bear in mind that unset WON'T work (or at least I've not managed to make it work) within the mixin. So - doing something like unset($this->__self->someValue); won't unset the value on Node. Don't know why, as in theory it should work. Funny enough unset($this->__self->someValue); var_dump(isset($this->__self->someValue)); will produce correctly false, however accessing the value from Node scope (as Node->someValue) will still produce true. There's some strange voodoo there.
eithed
  • 3,933
  • 6
  • 40
  • 60