Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
26 / 26
100.00% covered (success)
100.00%
7 / 7
CRAP
100.00% covered (success)
100.00%
1 / 1
Registry
100.00% covered (success)
100.00%
26 / 26
100.00% covered (success)
100.00%
7 / 7
11
100.00% covered (success)
100.00%
1 / 1
 register
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 has
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 all
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 useContainer
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 create
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 withDefaults
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
1
 assertBlock
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3declare(strict_types=1);
4
5namespace Cosray\Block;
6
7use Celema\Wire\Creator;
8use Cosray\Config;
9use Cosray\Contract\Block;
10use Cosray\Exception\RuntimeException;
11use Cosray\Field\Owner;
12use Psr\Container\ContainerInterface as Container;
13
14/**
15 * The default offer list of block types — what a Blocks field without
16 * `#[Allows]` offers — and the factory building type instances for a
17 * render.
18 */
19final class Registry
20{
21    /** @var list<class-string<Block>> */
22    private array $types = [];
23
24    private ?Container $container = null;
25
26    /** @param class-string<Block> $class */
27    public function register(string $class): void
28    {
29        self::assertBlock($class);
30
31        if (!in_array($class, $this->types, true)) {
32            $this->types[] = $class;
33        }
34    }
35
36    /** @param class-string $class */
37    public function has(string $class): bool
38    {
39        return in_array($class, $this->types, true);
40    }
41
42    /** @return list<class-string<Block>> */
43    public function all(): array
44    {
45        return $this->types;
46    }
47
48    /** Block type constructors are autowired from this container. */
49    public function useContainer(Container $container): void
50    {
51        $this->container = $container;
52    }
53
54    /**
55     * A fresh instance with autowired constructor arguments, like an
56     * embedded class: block types are node-local helpers, never container
57     * services, and never receive a node.
58     *
59     * @param class-string<Block> $class
60     */
61    public function create(string $class, Owner $owner): Block
62    {
63        self::assertBlock($class);
64
65        if ($this->container?->has($class)) {
66            throw new RuntimeException("Block type '{$class}' must not be registered as a container service.");
67        }
68
69        $instance = new Creator($this->container)->create($class, predefinedTypes: [
70            Owner::class => $owner,
71            Config::class => $owner->config(),
72        ]);
73        assert($instance instanceof Block, 'The creator returns the requested class');
74
75        return $instance;
76    }
77
78    public static function withDefaults(): self
79    {
80        $registry = new self();
81        $registry->register(RichText::class);
82        $registry->register(Text::class);
83        $registry->register(Heading::class);
84        $registry->register(Image::class);
85        $registry->register(Images::class);
86        $registry->register(Video::class);
87        $registry->register(Youtube::class);
88        $registry->register(Iframe::class);
89
90        return $registry;
91    }
92
93    private static function assertBlock(string $class): void
94    {
95        if (!class_exists($class) || !is_a($class, Block::class, true)) {
96            throw new RuntimeException('Block types must implement ' . Block::class . "{$class}");
97        }
98    }
99}