<?php
namespace App\Support;
use IteratorAggregate;
class ClassMap implements IteratorAggregate
{
protected $baseClass;
protected $classesList = [];
protected $instances;
protected $knowledge = [];
/**
* Execute a callback over each class names.
*
* @param \Closure $callback
* @return array
*/
public function each(\Closure $callback)
{
return $this->isInstantiated()
? array_map($callback, $this->classesList, $this->instances)
: array_map($callback, $this->classesList);
}
/**
* Apply a callback to every instance of classes.
*
* @param \Closure $callback
*/
public function walk(\Closure $callback)
{
$this->instantiate();
array_walk($this->instances, $callback);
}
/**
* Reduce the collection of instances to a single value.
*
* @param \Closure $callback
* @param mixed $initial
* @return mixed
*/
public function reduce(\Closure $callback, $initial = null)
{
$this->instantiate();
return array_reduce($this->instances, $callback, $initial);
}
/**
* Filters classes or instances using a callback function.
*
* @param \Closure $callback
* @return $this
*/
public function filter(\Closure $callback)
{
$filtered = $this->isInstantiated()
? array_filter($this->instances, $callback)
: array_filter($this->classesList, $callback);
$object = clone $this;
$object->instances = $object->isInstantiated() ? array_intersect_key($object->instances, $filtered) : null;
$object->classesList = array_intersect_key($object->classesList, $filtered);
foreach ($this->knowledge as $key => $item) {
$object->knowledge[$key] = array_intersect_key($item, $filtered);
}
return $object;
}
/**
* Make instance for all classes.
*/
public function instantiate()
{
if (!$this->isInstantiated()) {
$args = func_get_args();
$this->instances = $this->each(function ($class) use ($args) {
return (new \ReflectionClass($class))->newInstanceArgs($args);
});
}
return $this;
}
/**
* Get list of values by parameter.
*
* @param string $parameter
* @param \Closure $callback
* @return mixed
*/
public function getListOf($parameter, \Closure $callback = null)
{
if (!array_key_exists($parameter, $this->knowledge)) {
$callback = $callback ?: function ($class, $obj = null) use ($parameter) {
return call_user_func([null !== $obj ? $obj : $class, $parameter]);
};
$this->knowledge[$parameter] = $this->each($callback);
}
return $this->knowledge[$parameter];
}
/**
* Get list of constants values by constant.
*
* @param string $const
* @return mixed
*/
public function getListOfConstant($const)
{
if (!array_key_exists($const, $this->knowledge)) {
$this->knowledge[$const] = $this->each(function ($class) use ($const) {
return defined($class . '::' . $const) ? constant($class . '::' . $const) : null;
});
}
return $this->knowledge[$const];
}
/**
* Get an instance of a class.
*
* @param string $class
* @return mixed
*/
public function getInstance($class)
{
if ($class === null) {
return null;
}
$this->instantiate();
return array_get(array_combine($this->classesList, $this->instances), $class, null);
}
/**
* Get a class by return value of a parameter.
*
* @param string $parameter
* @param mixed $val
* @return string
*/
public function getClassBy($parameter, $val)
{
return array_get(array_combine($this->getListOf($parameter), $this->classesList), $val, $this->baseClass);
}
/**
* Get an instance of a class by return value of a parameter.
*
* @param string $parameter
* @param mixed $val
* @return mixed
*/
public function getInstanceBy($parameter, $val)
{
$class = $this->getClassBy($parameter, $val);
return $this->getInstance($class);
}
/**
* Terminate classes instances created.
*
* @return bool
*/
public function isInstantiated()
{
return null !== $this->instances;
}
/**
* {@inheritdoc}
*/
public function getIterator()
{
$array = $this->isInstantiated()
? array_combine($this->classesList, $this->instances)
: array_fill_keys($this->classesList, null);
return new \ArrayIterator($array);
}
/**
* @return int
*/
public function count()
{
return $this->isInstantiated()
? count($this->instances)
: count($this->classesList);
}
}