src/chippyash/Assembler/Assembler.php
<?php
/**
* Lightweight assembly builder pattern
*
* @author Ashley Kitson
* @copyright Ashley Kitson <ashley@zf4.biz>, 2015,2021 UK
* @licence BSD 3 Clause see LICENSE.MD
*/
declare(strict_types=1);
namespace Assembler;
/**
* A Class that assembles other things to give a result
*
*/
class Assembler
{
/**
* Names of introduced variables
*
* @var array
*/
protected $placeHolders = [];
/**
* Computed values
*
* @var array
*/
protected $values = [];
/**
* Singleton instance of an Assembler
* @see get()
*
* @var Assembler
*/
private static $singleton;
/**
* Static Assembler constructor
* Returns a new Assembler
*
* @param array $params Immutable parameters to send into the assembler
*
* @return static
*/
public static function create(array $params = []): Assembler
{
$assembler = new static();
if (!empty($params)) {
array_walk($params, function($v, $k) use ($assembler) {
$assembler->$k(function() use($v) {return $v;});
});
$assembler->assemble();
}
return $assembler;
}
/**
* Return Singleton instance of Assembler
*
* @param array $params Immutable parameters to send into the assembler
*
* @return Assembler
*/
public static function get(array $params = []): Assembler
{
if (empty(self::$singleton)) {
self::$singleton = static::create($params);
}
return self::$singleton;
}
/**
* Proxies variable names to functions
*
* You can overwrite an existing definition
*
* @param $method
* @param array $args
* @return $this
*/
public function __call($method, array $args): Assembler
{
if (count($args) == 0 || !$args[0] instanceof \Closure) {
throw new \RuntimeException('We expect to build the variable with a function');
}
$this->placeHolders[$method] = $args[0];
return $this;
}
/**
* Return an array of variables created else just a single value
* Usage:
* ->release('var1','varN')
* ->release('var1')
*
* Don't forget to call assemble() first if needed
*
* @param string $var1 Name of variable to return
* @param string $_ Next name of variable to retrieve - repeater
*
* @return mixed
*/
public function release($var1)
{
$flip = array_flip(func_get_args());
$intersect = array_intersect_key($this->values, $flip);
if (count(func_get_args()) > 1) {
array_walk($flip, function(&$v, $k) use ($intersect) {
$v = $intersect[$k];
});
return array_values($flip);
} else {
return array_pop($intersect);
}
}
/**
* Run the Assembly not returning any value.
* Assembly will not overwrite anything already assembled
*
* @return $this
*/
public function assemble(): Assembler
{
foreach ($this->placeHolders as $name => $placeholder) {
if (!isset($this->values[$name])) {
$this->values[$name] = $this->addVars($placeholder);
}
}
return $this;
}
/**
* Merge this assembly with another.
* Obeys the rules of array_merge
*
* @param Assembler $other
*
* @return $this
*/
public function merge(Assembler $other): Assembler
{
//N.B. reflection used so as to not expose values
//via some public method
$refl = new \ReflectionObject($other);
$reflValues = $refl->getProperty('values');
$reflValues->setAccessible(true);
$oValues = $reflValues->getValue($other);
if (count($oValues) == 0) {
return $this;
}
$this->values = array_merge($this->values, $oValues);
return $this;
}
/**
* Execute the function assigned to the value, binding in values created earlier
* in the assembly
*
* @param \Closure $func
*
* @return mixed
*/
protected function addVars(\Closure $func)
{
$args = (new \ReflectionFunction($func))->getParameters();
if (count($args) === 0) {
return $func();
}
$fArgs = array_map(function($key) {
return $this->values[$key];
},
array_map(function($arg) {
return $arg->getName();
},
$args
)
);
return call_user_func_array($func, $fArgs);
}
/**
* Don't allow direct construction
* @see create()
*/
protected function __construct(){}
}