chippyash/Builder-Pattern

View on GitHub
src/Chippyash/BuilderPattern/AbstractBuilder.php

Summary

Maintainability
A
45 mins
Test Coverage
<?php
/*
 * Builder Pattern Library
 *
 * @author Ashley Kitson <akitson@zf4.biz>
 * @copyright Ashley Kitson, UK, 2014
 * @licence GPL V3 or later : http://www.gnu.org/licenses/gpl.html
 * @link http://en.wikipedia.org/wiki/Builder_pattern
 */
namespace Chippyash\BuilderPattern;

use Chippyash\BuilderPattern\BuilderInterface;
use Chippyash\BuilderPattern\ModifiableInterface;
use Zend\EventManager\EventManagerAwareInterface;
use Zend\EventManager\ResponseCollection;

/**
 * Abstract builder pattern
 */
abstract class AbstractBuilder implements BuilderInterface, ModifiableInterface
{
    /**
     * Build items
     *
     * @var array
     */
    protected $buildItems = [];

    /**
     *
     * @var array
     */
    protected $dataObject;

    /**
     * Modification event manager
     * @var Zend\EventManager\EventManagerAwareInterface 
     */
    protected $modifier;
    
    /**
     * Constructor
     */
    public function __construct()
    {
        $this->setBuildItems();
    }

    /**
     * Build the data object
     *
     * @return boolean
     */
    public function build()
    {
        $this->dataObject = [];
        foreach ($this->buildItems as $name => $value) {
            if ($value instanceof BuilderInterface) {
                if (!$this->buildItems[$name]->build()) {
                    return false;
                }
                $this->dataObject[$name] = $this->buildItems[$name]->getResult();
            } elseif (is_callable($value)) {
                $this->dataObject[$name] = $value();
            } else {
                $this->dataObject[$name] = $this->buildItems[$name];
            }
        }

        return true;
    }

    /**
     * Return the generated data object
     *
     * @return array
     */
    public function getResult()
    {
        return $this->dataObject;
    }

    /**
     * Set a modifier for this builder
     * 
     * @param EventManagerAwareInterface $modifier
     * @return \Chippyash\BuilderPattern\AbstractModifiableBuilder
     */
    public function setModifier(EventManagerAwareInterface $modifier)
    {
        $this->modifier = $modifier;
        foreach ($this->buildItems as $buildItem) {
            if ($buildItem instanceof ModifiableInterface) {
                $buildItem->setModifier($modifier);
            }
        }
        
        return $this;
    }
    
    /**
     * Add a modification trigger for this builder
     * 
     * @param array $params
     * @param type $phase
     * @return ResponseCollection
     */
    public function modify(array $params = [], $phase = ModifiableInterface::PHASE_POST_BUILD)
    {
        if (!empty($this->modifier)) {
            return $this->modifier->getEventManager()
                    ->trigger($phase, $this, $params);
        }
        
        //return empty collection
        return new ResponseCollection();
    }
    
    /**
     * Get object value.  Throw exception of value does not exist
     *
     * @param string $key
     * @return mixed
     * @throws \InvalidArgumentException
     */
    public function __get($key)
    {
        $k = strtolower($key);
        //check for specialised getter
        $methodName = 'get' . ucfirst($k);
        if (method_exists($this, $methodName)) {
            return $this->$methodName();
        } elseif (array_key_exists($k, $this->buildItems)) {
            //return raw value
            return $this->buildItems[$k];
        } else {
            throw new \InvalidArgumentException("Unknown parameter: {$key}");
        }
    }

    /**
     * Set an object value.  Throws exception if key does not exist
     *
     * @param string $key
     * @param mixed $value
     * @return Chippyash\BuilderPattern\SDO\SDOInterface Fluent Interface
     * @throws \InvalidParameterException
     */
    public function __set($key, $value)
    {
        $k = strtolower($key);
        //check for specialised setter
        $methodName = 'set' . ucfirst($k);
        if (method_exists($this, $methodName)) {
            return $this->$methodName($value);
        } elseif (array_key_exists($k, $this->buildItems)) {
            //set the raw value if parameter exists
            $this->buildItems[$k] = $value;
            return $this;
        } else {
            throw new \InvalidArgumentException("Unknown parameter: {$key}");
        }
    }

    /**
     * Tests if an object parameter is set and contains a non null value.
     *
     * @param string $key
     * @return boolean
     */
    public function __isset($key)
    {
        $k = strtolower($key);
        //check for specialised discovery method
        $methodName = 'has' . ucfirst($k);
        if (method_exists($this, $methodName)) {
            return $this->$methodName();
        } elseif (array_key_exists($k, $this->buildItems) && !is_null($this->buildItems[$k])) {
            return true;
        }
        
        return false;
    }

    /**
     * Sets key value to null.
     *
     * @param string key
     * @return \Chippyash\BuilderPattern\DataBuilder\Builder\AbstractBuilder Fluent Interface
     * @throws InvalidArgumentException
     */
    public function __unset($key)
    {
        $k = strtolower($key);
        if (array_key_exists($k, $this->buildItems)) {
            $this->buildItems[$k] = null;
        } else {
            throw new \InvalidArgumentException("Unknown parameter: {$key}");
        }
        
        return $this;
    }
    
    /**
     * Proxy method calls
     * 
     * @param string $name
     * @param array $arguments
     * @return mixed
     * @throws \BadMethodCallException
     */
    public function __call($name, $arguments)
    {
        $prefix = strtolower(substr($name, 0, 3));
        $key = substr($name, 3);
        switch ($prefix) {
            case 'get':
                return $this->__get($key);
            case 'set':
                return $this->__set($key, $arguments[0]);
            case 'has':
                return $this->__isset($key);
            default:
                throw new \BadMethodCallException("Bad method name: {$name}");
        }
    }

    /**
     * Set up the build items that this builder will manage
     */
    abstract protected function setBuildItems();
}