I start with an example.
Mold class
<?php
class Mold {
public $current;
function merge($first, $second) {
$this->current = $first . $second;
return $this;
}
function phone() {
$this->current = '<a href="tel:' . $this->current . '">' . $this->current . '</a>';
return $this;
}
function __toString() {
return $this->current;
}
}
function mold() {
return new Mold();
}
In action
This works as expected.
echo mold()->merge('sada', 'asdsa')->phone();
I have one or more classes with methods that wants to be available as well.
Plugin class
class MyPlugin {
function link() {
// Code
}
function currency($number) {
// Code
}
}
class MyPlugin2 {
// Other methods
}
Dream code
The exact change of events below may not make any sense. Anyway, what I intend to do is the following.
echo mold()->merge('sada', 'asdsa')->currency(45)-link();
mold()
which creates a new instance of the Mold
class.merge()
which is used from the Mold
class.currency()
or link()
method does not exist in the Mold
class. Instead they should be loaded from one of the plugins.Mold
class.Interesting ideas.
The one that solves the problem like I had in my mind was to use traits like @vivek_23 suggested in a comment.
Here is a complete example of how it works
<?php
// Core methods
class MoldCore {
public $current;
function merge($first, $second) {
$this->current = $first . $second;
return $this;
}
function phone() {
$this->current = '<a href="tel:' . $this->current . '">' . $this->current . '</a>';
return $this;
}
function __toString() {
return $this->current;
}
}
// Plugin 1
trait Plugin1 {
public function yaay() {
$this->current .= ' Plugin1 yaay';
return $this;
}
}
// Plugin 2
trait Plugin2 {
public function hello() {
$this->current = str_replace('Plugin1', 'Modified', $this->current);
return $this;
}
public function world() {
$this->current .= ' Plugin2 world';
return $this;
}
}
// Glue plugins with MoldCore
class Mold extends MoldCore {
use Plugin1, Plugin2;
}
$mold = new Mold();
echo $mold->merge('sada', 'asdsa')->phone()->yaay()->hello();
A concept that you might try to apply is the Composition Over Inheritance.
A solution could be to compose your current Mold
object and pass the two plugins instances so that it could use them on its methods, like so:
<?php
class Mold {
public $current;
public function __construct(Plugin1 $plugin1, Plugin2 $plugin2) {
$this->plugin1 = $plugin1;
$this->plugin2 = $plugin2;
}
public function merge($first, $second) {
$this->current = $first . $second;
return $this;
}
public function phone() {
$this->current = '<a href="tel:' . $this->current . '">' . $this->current . '</a>';
return $this;
}
public function __toString() {
return $this->current;
}
public function link() {
$this->plugin1->link();
return $this;
}
}
function mold() {
return new Mold(new Plugin1(), new Plugin2());
}
Another solution could be to create a MoldHandler
class of some sort, which has a Mold
object as a property together with the other plugins, which could be used inside of this directly.
public class MoldHanlder {
protected $mold;
protected $plugin1;
protected $plugin2;
public function __contruct($mold, $plugin1, $plugin2) {
$this->mold = $mold;
$this->plugin1 = $plugin1;
$this->plugin2 = $plugin2;
}
public function merge() {
$this->mold = $this->mold->merge();
return $this;
}
public function link() {
$foo = $this->plugin1->method();
return $this;
}
...
}
You could also just instantiate the classes inside the construct
, but if you are planning about writing unit tests, dependency injection is the way to go
I think that the first approach is way better because you avoid yourself to re-write some code of the Mold class, which is unnecessary
N.B. this is a really raw solution proposed just to let you better understand the idea I would opt for cheers
Technically you could use __call()
for this. Your "main" class could contain/track a set of plugin instances and any unknown method call could be looked up and delegated, if available in a plugin.
I wouldn't recommend this though. Fluent APIs are often brittle and can get inconvenient even with one class. Involving multiple classes in fluent calls might quickly to add up to a confusing mess. A mess which even IDE can't help you (or another person working with code) with since it has no clue about all the magic happening.
I would highly recommend to look into alternative patterns for an API like this.