divineniiquaye/rade-di

View on GitHub
src/Definitions/TaggedLocator.php

Summary

Maintainability
A
3 hrs
Test Coverage
<?php

declare(strict_types=1);

/*
 * This file is part of DivineNii opensource projects.
 *
 * PHP version 7.4 and above required
 *
 * @author    Divine Niiquaye Ibok <divineibok@gmail.com>
 * @copyright 2021 DivineNii (https://divinenii.com/)
 * @license   https://opensource.org/licenses/BSD-3-Clause License
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Rade\DI\Definitions;

use Rade\DI\Container;

/**
 * Creates a lazy iterator by tag name.
 *
 * @author Divine Niiquaye Ibok <divineibok@gmail.com>
 */
class TaggedLocator
{
    private ?string $indexAttribute;

    /**
     * @param string            $tag                   The name of the tag identifying the target services
     * @param string|null       $indexAttribute        The name of the attribute that defines the key referencing each service in the tagged collection
     * @param bool              $needsIndexes          Whether indexes are required and should be generated when computing the map
     * @param array<int,string> $exclude               Services to exclude from the iterator
     */
    public function __construct(private string $tag, string $indexAttribute = null, bool $needsIndexes = false, private array $exclude = [])
    {
        if (null === $indexAttribute && $needsIndexes) {
            $indexAttribute = \preg_match('/[^.]++$/', $tag, $m) ? $m[0] : $tag;
        }

        $this->tag = $tag;
        $this->exclude = $exclude;
        $this->indexAttribute = $indexAttribute;
    }

    /**
     * @return array<int|string,Reference|Statement>
     */
    public function resolve(Container $container): array
    {
        $i = 0;
        $services = $refs = [];

        foreach ($container->tagged($this->tag) as $serviceId => $attributes) {
            if (\in_array($serviceId, $this->exclude, true)) {
                continue;
            }

            $index = $priority = null;

            if (\is_array($attributes)) {
                if (null !== $this->indexAttribute && isset($attributes[$this->indexAttribute])) {
                    $index = $attributes[$this->indexAttribute];
                }

                if (isset($attributes['priority'])) {
                    $priority = $attributes['priority'];
                }
            }

            $services[] = [$priority ?? 0, ++$i, $index, $serviceId];
        }

        \uasort($services, static fn ($a, $b) => $b[0] <=> $a[0] ?: $a[1] <=> $b[1]);

        foreach ($services as [, , $index, $serviceId]) {
            $reference = $container->has($serviceId) ? new Reference($serviceId) : new Statement($serviceId);

            if (null === $index) {
                $refs[] = $reference;
            } else {
                $refs[$index] = $reference;
            }
        }

        return $refs;
    }
}