kahlan/kahlan

View on GitHub
src/Plugin/Stub/Method.php

Summary

Maintainability
A
1 hr
Test Coverage
B
88%
<?php
namespace Kahlan\Plugin\Stub;

use Kahlan\Plugin\Call\Message;
use Closure;
use Exception;

class Method extends Message
{
    /**
     * Index value in the `Method::$_substitutes` array.
     *
     * @var integer
     */
    protected $_substituteIndex = 0;

    /**
     * Return values.
     *
     * @var array
     */
    protected $_substitutes = null;

    /**
     * Index value in the `Method::$_returns/Method::$_closures` array.
     *
     * @var integer
     */
    protected $_returnIndex = 0;

    /**
     * Stub implementation.
     *
     * @var Closure
     */
    protected $_closures = [];

    /**
     * Return values.
     *
     * @var array
     */
    protected $_returns = [];

    /**
     * The method return value.
     *
     * @var mixed
     */
    protected $_return = null;

    /**
     * Boolean indicating if a method must act as a replacement.
     *
     * @var array
     */
    protected $_actsAsAReplacement = false;

    /**
     * The Constructor.
     *
     * @param array $config The options array, possible options are:
     *                      - `'closures'`: the closures to execute for this stub.
     *                      - `'args'`:    the arguments required for exectuting this stub.
     *                      - `'static'`:  the type of call required for exectuting this stub.
     *                      - `'returns'`: the returns values for this stub (used only if
     *                        the `'closure'` option is missing).
     */
    public function __construct($config = [])
    {
        $defaults = ['closures' => [], 'args' => null, 'returns' => [], 'static' => false];
        $config += $defaults;

        parent::__construct($config);
        $this->_name = ltrim($this->_name, '\\');
        $this->_closures = (array) $config['closures'];
        $this->_returns = (array) $config['returns'];
    }

    /**
     * Runs the stub.
     *
     * @param  string $self   The context from which the stub need to be executed.
     * @param  array  $args   The call arguments array.
     * @return mixed          The returned stub result.
     */
    public function __invoke($args = [], $self = null)
    {
        if ($this->_closures) {
            if (isset($this->_closures[$this->_returnIndex])) {
                $closure = $this->_closures[$this->_returnIndex++];
            } else {
                $closure = end($this->_closures);
            }
            if (is_string($self)) {
                $closure = $closure->bindTo(null, $self);
            } elseif ($self) {
                $closure = $closure->bindTo($self, get_class($self));
            }
            $this->_return = call_user_func_array($closure, $args);
        } elseif (array_key_exists($this->_returnIndex, $this->_returns)) {
            $this->_return = $this->_returns[$this->_returnIndex++];
        } else {
            $this->_return = $this->_returns ? end($this->_returns) : null;
        }
        return $this->_return;
    }

    /**
     * Return a compatible callable.
     *
     * @return callable The callable.
     */
    public function closure()
    {
        if ($this->_closures) {
            if (isset($this->_closures[$this->_returnIndex])) {
                $closure = $this->_closures[$this->_returnIndex++];
            } else {
                $closure = end($this->_closures);
            }
            return $closure;
        } elseif (array_key_exists($this->_returnIndex, $this->_returns)) {
            $this->_return = $this->_returns[$this->_returnIndex++];
        } else {
            $this->_return = $this->_returns ? end($this->_returns) : null;
        }
        return function () {
            return $this->_return;
        };
    }

    /**
     * Set return values.
     *
     * @param mixed ... <0,n> Return value(s).
     */
    public function toBe()
    {
        if ($this->reference()) {
            $this->_substitutes = func_get_args();
        } else {
            $this->_actsAsAReplacement = true;
            if (func_num_args() !== 1) {
                throw new Exception("Only one hard method substitution is allowed through `toBe()`.");
            }
            $this->_closures = func_get_args();
        }
    }

    /**
     * Set the stub logic.
     */
    public function andRun()
    {
        if ($this->_returns) {
            throw new Exception("Some return value(s) has already been set.");
        }
        $closures = func_get_args();
        foreach ($closures as $closure) {
            if (!is_callable($closure)) {
                throw new Exception("The passed parameter is not callable.");
            }
        }
        $this->_closures = $closures;
    }

    /**
     * Set return values.
     *
     * @param mixed ... <0,n> Return value(s).
     */
    public function andReturn()
    {
        if ($this->_closures) {
            throw new Exception("Some closure(s) has already been set.");
        }
        $this->_returns = func_get_args();
    }

    /**
     * Get the actual return value.
     *
     * @return mixed
     */
    public function actualReturn()
    {
        return $this->_return;
    }


    /**
     * Get the method substitute.
     *
     * @return mixed
     */
    public function substitute()
    {
        if (isset($this->_substitutes[$this->_substituteIndex])) {
            return $this->_substitutes[$this->_substituteIndex++];
        }
        return $this->_substitutes ? end($this->_substitutes) : null;
    }

    /**
     * Check if the stub is a lazy stub
     *
     * @return boolean
     */
    public function actsAsAReplacement()
    {
        return $this->_actsAsAReplacement;
    }
}