RebelCode/rcmod-eddbk-session-generator

View on GitHub
src/Generator/AbstractIteratorRule.php

Summary

Maintainability
A
45 mins
Test Coverage
<?php

namespace RebelCode\EddBookings\Sessions\Generator;

use Dhii\Exception\CreateInvalidArgumentExceptionCapableTrait;
use Dhii\Exception\CreateRuntimeExceptionCapableTrait;
use Dhii\I18n\StringTranslatingTrait;
use Dhii\Iterator\CreateIterationCapableTrait;
use Dhii\Iterator\CreateIteratorExceptionCapableTrait;
use Dhii\Iterator\IterationAwareTrait;
use Dhii\Iterator\IteratorInterface;
use Dhii\Iterator\IteratorTrait;
use Dhii\Time\PeriodInterface;
use Dhii\Util\Normalization\NormalizeArrayCapableTrait;
use Dhii\Util\Normalization\NormalizeIntCapableTrait;
use Dhii\Util\Normalization\NormalizeStringCapableTrait;
use Exception as RootException;
use RebelCode\EddBookings\Sessions\Time\CreatePeriodCapableFactoryTrait;
use RebelCode\EddBookings\Sessions\Time\PeriodFactoryAwareTrait;
use RebelCode\EddBookings\Sessions\Time\PeriodFactoryInterface;
use RebelCode\Time\EndAwareTrait;
use RebelCode\Time\NormalizeTimestampCapableTrait;
use RebelCode\Time\StartAwareTrait;
use stdClass;
use Traversable;

/**
 * Abstract functionality for a session generator rule.
 *
 * @since [*next-version*]
 */
abstract class AbstractIteratorRule implements PeriodInterface, IteratorInterface
{
    /* @since [*next-version*] */
    use IteratorTrait;

    /* @since [*next-version*] */
    use IterationAwareTrait;

    /* @since [*next-version*] */
    use StartAwareTrait;

    /* @since [*next-version*] */
    use EndAwareTrait;

    /* @since [*next-version*] */
    use RepeatFreqAwareTrait;

    /* @since [*next-version*] */
    use RepeatEndAwareTrait;

    /* @since [*next-version*] */
    use ExcludedDatesAwareTrait;

    /* @since [*next-version*] */
    use PeriodFactoryAwareTrait;

    /* @since [*next-version*] */
    use CreatePeriodCapableFactoryTrait;

    /* @since [*next-version*] */
    use NormalizeTimestampCapableTrait;

    /* @since [*next-version*] */
    use NormalizeIntCapableTrait;

    /* @since [*next-version*] */
    use NormalizeStringCapableTrait;

    /* @since [*next-version*] */
    use NormalizeArrayCapableTrait;

    /* @since [*next-version*] */
    use CreateInvalidArgumentExceptionCapableTrait;

    /* @since [*next-version*] */
    use CreateRuntimeExceptionCapableTrait;

    /* @since [*next-version*] */
    use CreateIterationCapableTrait;

    /* @since [*next-version*] */
    use CreateIteratorExceptionCapableTrait;

    /* @since [*next-version*] */
    use StringTranslatingTrait;

    /**
     * The current iteration occurrence start timestamp.
     *
     * @since [*next-version*]
     *
     * @var int
     */
    protected $current;

    /**
     * The timestamp at which to stop iteration.
     *
     * @since [*next-version*]
     *
     * @var int
     */
    protected $iterationEnd;

    /**
     * Initializes the session generator rule.
     *
     * @since [*next-version*]
     *
     * @param PeriodFactoryInterface|null $periodFactory The period factory instance, if any.
     * @param int|null                    $start         The start timestamp fo the rule.
     * @param int|null                    $end           The end timestamp fo the rule.
     * @param int|null                    $repeatFreq    How frequently the rule repeats.
     * @param int|null                    $repeatEnd     The timestamp when the repetition ends.
     * @param array|stdClass|Traversable  $excludeDates  The list of dates to exclude.
     */
    protected function _initRule(
        $periodFactory,
        $start,
        $end,
        $repeatFreq = null,
        $repeatEnd = null,
        $excludeDates = []
    ) {
        $this->_setPeriodFactory($periodFactory);
        $this->_setStart($start);
        $this->_setEnd($end);
        $this->_setRepeatFreq($repeatFreq);
        $this->_setRepeatEnd($repeatEnd);
        $this->_setExcludedDates($excludeDates);
    }

    /**
     * {@inheritdoc}
     *
     * @since [*next-version*]
     */
    public function getStart()
    {
        return $this->_getStart();
    }

    /**
     * {@inheritdoc}
     *
     * @since [*next-version*]
     */
    public function getEnd()
    {
        return $this->_getEnd();
    }

    /**
     * {@inheritdoc}
     *
     * @since [*next-version*]
     */
    public function getDuration()
    {
        return abs($this->_getEnd() - $this->_getStart());
    }

    /**
     * {@inheritdoc}
     *
     * @since [*next-version*]
     */
    public function rewind()
    {
        $this->_rewind();
    }

    /**
     * {@inheritdoc}
     *
     * @since [*next-version*]
     */
    public function next()
    {
        $this->_next();
    }

    /**
     * {@inheritdoc}
     *
     * @since [*next-version*]
     */
    public function valid()
    {
        return $this->_valid();
    }

    /**
     * {@inheritdoc}
     *
     * @since [*next-version*]
     */
    public function current()
    {
        return $this->_value();
    }

    /**
     * {@inheritdoc}
     *
     * @since [*next-version*]
     */
    public function key()
    {
        return $this->_key();
    }

    /**
     * {@inheritdoc}
     *
     * @since [*next-version*]
     */
    public function getIteration()
    {
        return $this->_getIteration();
    }

    /**
     * The timestamp at which to stop iteration.
     *
     * @since [*next-version*]
     *
     * @return int
     */
    protected function _getIterationEnd()
    {
        // Calculate if necessary
        if ($this->iterationEnd === null) {
            $ruleEnd   = $this->_getEnd();
            $repeatEnd = $this->_getRepeatEnd();
            // If no repetition, use rule's end timestamp as the end of iteration
            $this->iterationEnd = ($repeatEnd === null) ? $ruleEnd : $repeatEnd;
        }

        return $this->iterationEnd;
    }

    /**
     * {@inheritdoc}
     *
     * @since [*next-version*]
     */
    protected function _reset()
    {
        // Reset cursor to this rule's period start timestamp
        $this->current = $this->_getStart();

        // Yield this rule as the first period
        return $this->_createIteration($this->current, $this);
    }

    /**
     * {@inheritdoc}
     *
     * @since [*next-version*]
     */
    protected function _loop()
    {
        // Check if not a repeating rule, and if so, stop
        if (intval($this->_getRepeatFreq()) === 0) {
            return $this->_createIteration(null, null);
        }

        // Retrieves the next occurrence
        $this->current = $this->_getNextOccurrence($this->current);

        // Check if reached end or not
        if ($this->current >= $this->_getIterationEnd()) {
            return $this->_createIteration(null, null);
        }

        // If the occurrence is excluded, recurse to calculate the next one
        if ($this->_isExcluded($this->current)) {
            return $this->_loop();
        }

        // Calculate the end timestamp for this occurrence
        $end = $this->current + $this->getDuration();

        // Create the period instance to yield
        $period = $this->_createPeriod($this->current, $end);

        return $this->_createIteration($this->current, $period);
    }

    /**
     * Checks whether a timestamp is excluded.
     *
     * @since [*next-version*]
     *
     * @param int $timestamp The occurrence to check.
     *
     * @return bool True if excluded, false if not.
     */
    protected function _isExcluded($timestamp)
    {
        foreach ($this->_getExcludedDates() as $_excludeDate) {
            $_excludeStart = $_excludeDate;
            $_excludeEnd   = $_excludeStart + (24 * 60 * 60);

            if ($timestamp >= $_excludeStart && $timestamp < $_excludeEnd) {
                return true;
            }
        }

        return false;
    }

    /**
     * {@inheritdoc}
     *
     * @since [*next-version*]
     */
    protected function _throwIteratorException(
        $message = null,
        $code = null,
        RootException $previous = null
    ) {
        throw $this->_createIteratorException($message, $code, $previous, $this);
    }

    /**
     * Retrieves the next occurrence start timestamp.
     *
     * @since [*next-version*]
     *
     * @param int $timestamp The start timestamp from which to get the next occurrence.
     *
     * @return int The start timestamp of the next occurrence.
     */
    abstract protected function _getNextOccurrence($timestamp);
}