xref: /web-bugs/src/Container/Container.php (revision 5cd2630a)
1<?php
2
3namespace App\Container;
4
5use App\Container\Exception\EntryNotFoundException;
6use App\Container\Exception\ContainerException;
7
8/**
9 * PSR-11 compatible dependency injection container.
10 */
11class Container implements ContainerInterface
12{
13    /**
14     * All defined services and parameters.
15     *
16     * @var array
17     */
18    public $entries = [];
19
20    /**
21     * Already retrieved items are stored for faster retrievals in the same run.
22     *
23     * @var array
24     */
25    private $store = [];
26
27    /**
28     * Services already created to prevent circular references.
29     *
30     * @var array
31     */
32    private $locks = [];
33
34    /**
35     * Class constructor.
36     */
37    public function __construct(array $configurations = [])
38    {
39        $this->entries = $configurations;
40    }
41
42    /**
43     * Set service.
44     */
45    public function set(string $key, $entry): void
46    {
47        $this->entries[$key] = $entry;
48    }
49
50    /**
51     * Get entry.
52     *
53     * @return mixed
54     */
55    public function get(string $id)
56    {
57        if (!$this->has($id)) {
58            throw new EntryNotFoundException($id.' entry not found.');
59        }
60
61        if (!isset($this->store[$id])) {
62            $this->store[$id] = $this->createEntry($id);
63        }
64
65        return $this->store[$id];
66    }
67
68    /**
69     * Check if entry is available in the container.
70     */
71    public function has(string $id): bool
72    {
73        return isset($this->entries[$id]);
74    }
75
76    /**
77     * Create new entry - service or configuration parameter.
78     *
79     * @return mixed
80     */
81    private function createEntry(string $id)
82    {
83        $entry = &$this->entries[$id];
84
85        // Entry is a configuration parameter.
86        if (!class_exists($id) && !is_callable($entry)) {
87            return $entry;
88        }
89
90        // Entry is a service.
91        if (class_exists($id) && !is_callable($entry)) {
92            throw new ContainerException($id.' entry must be callable.');
93        } elseif (class_exists($id) && isset($this->locks[$id])) {
94            throw new ContainerException($id.' entry contains a circular reference.');
95        }
96
97        $this->locks[$id] = true;
98
99        return $entry($this);
100    }
101}
102