phoole/event

View on GitHub
src/Provider.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php

/**
 * Phoole (PHP7.2+)
 *
 * @category  Library
 * @package   Phoole\Event
 * @copyright Copyright (c) 2019 Hong Zhang
 */
declare(strict_types=1);

namespace Phoole\Event;

use Phoole\Base\Reflect\ParameterTrait;
use Phoole\Base\Queue\UniquePriorityQueue;
use Psr\EventDispatcher\ListenerProviderInterface;

/**
 * Provider
 *
 * @package Phoole\Event
 */
class Provider implements ListenerProviderInterface
{
    use ParameterTrait;

    /**
     * event classes listened
     *
     * @var UniquePriorityQueue[]
     */
    protected $listened = [];

    /**
     * @param  object $event
     *   An event for which to return the relevant listeners.
     * @return iterable
     *   An iterable (array, iterator, or generator) of callables.  Each
     *   callable MUST be type-compatible with $event.
     */
    public function getListenersForEvent(object $event): iterable
    {
        $queue = new UniquePriorityQueue();
        foreach ($this->listened as $class => $q) {
            if (is_a($event, $class) && count($q)) {
                $queue = $queue->combine($q);
            }
        }
        return $queue;
    }

    /**
     * Attach a listener (with default priority 50) to the provider
     *
     * @param  callable $callable  MUST be type-compatible with $event.
     * @param  int      $priority  range 0 - 100, 0 means lower priority
     * @return $this
     * @throws \RuntimeException            reflection problem found
     * @throws \InvalidArgumentException    unknown type of callable found
     */
    public function attach(callable $callable, int $priority = 50)
    {
        $class = $this->getEventClass($callable);
        if (!isset($this->listened[$class])) {
            $this->listened[$class] = new UniquePriorityQueue();
        }
        $this->listened[$class]->insert($callable, $priority);
        return $this;
    }

    /**
     * Get callable's first argument EVENT class.
     *
     * @param  callable $callable
     * @return string                       classname or interface name
     * @throws \RuntimeException            reflection problem found
     * @throws \InvalidArgumentException    unknown type of callable found
     */
    protected function getEventClass(callable $callable): string
    {
        try {
            $params = $this->getCallableParameters($callable);
            $error = 'Listener must declare one object as event';

            // only one parameter allowed
            if (1 != count($params)) {
                throw new \InvalidArgumentException($error);
            }

            // must be object
            $type = $params[0]->getType()->getName();
            if (class_exists($type) || interface_exists($type)) {
                return $type;
            }
            throw new \InvalidArgumentException($error);
        } catch (\Throwable $e) {
            throw new \RuntimeException($e->getMessage());
        }
    }
}