• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ICanBoogie / bind-symfony-dependency-injection / 4257104880

pending completion
4257104880

push

github

Olivier Laviale
Use PHPUnit 10

5 of 5 new or added lines in 3 files covered. (100.0%)

110 of 124 relevant lines covered (88.71%)

4.19 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.29
/lib/ContainerProxy.php
1
<?php
2

3
/*
4
 * This file is part of the ICanBoogie package.
5
 *
6
 * (c) Olivier Laviale <olivier.laviale@gmail.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11

12
namespace ICanBoogie\Binding\SymfonyDependencyInjection;
13

14
use ICanBoogie\Accessor\AccessorTrait;
15
use ICanBoogie\Application;
16
use ICanBoogie\Autoconfig\Autoconfig;
17
use ICanBoogie\Binding\SymfonyDependencyInjection\Extension\ApplicationExtension;
18
use olvlvl\SymfonyDependencyInjectionProxy\ProxyDumper;
19
use Psr\Container\ContainerInterface;
20
use Symfony\Component\Config\FileLocator;
21
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
22
use Symfony\Component\DependencyInjection\ContainerBuilder;
23
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
24
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
25
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
26

27
use function array_keys;
28
use function file_exists;
29
use function file_put_contents;
30
use function getcwd;
31
use function is_string;
32
use function is_subclass_of;
33

34
/**
35
 * Proxy to Symfony container.
36
 *
37
 * @property-read ContainerInterface $container
38
 */
39
final class ContainerProxy implements ContainerInterface
40
{
41
    use AccessorTrait;
42

43
    public const ALIAS_APP = ApplicationExtension::APP_SERVICE;
44
    public const ALIAS_CONTAINER = 'container';
45
    private const CONFIG_FILENAME = 'services.yml';
46

47
    private ContainerInterface $container;
48

49
    private function get_container(): ContainerInterface
50
    {
51
        return $this->container ??= $this->instantiate_container();
7✔
52
    }
53

54
    // @codeCoverageIgnoreStart
55

56
    public function __construct(
57
        private readonly Application $app,
58
        private readonly Config $config
59
    ) {
60
    }
61

62
    // @codeCoverageIgnoreEnd
63

64
    /**
65
     * Note: We need the proxy to be a callable to satisfy `ICanBoogie\Service\ServiceProvider`.
66
     */
67
    public function __invoke(string $id): object
68
    {
69
        return $this->get($id);
5✔
70
    }
71

72
    public function get(string $id): object
73
    {
74
        return match ($id) {
7✔
75
            self::ALIAS_APP, Application::class => $this->app,
7✔
76
            self::ALIAS_CONTAINER => $this->get_container(),
7✔
77
            default => $this->get_container()->get($id),
7✔
78
        };
7✔
79
    }
80

81
    public function has(string $id): bool
82
    {
83
        return match ($id) {
×
84
            self::ALIAS_APP, Application::class, self::ALIAS_CONTAINER => true,
×
85
            default => $this->get_container()->has($id),
×
86
        };
×
87
    }
88

89
    private function instantiate_container(): ContainerInterface
90
    {
91
        $app = $this->app;
1✔
92
        $class = 'ApplicationContainer';
1✔
93
        $pathname = ContainerPathname::from($app);
1✔
94

95
        if (!$this->config->use_caching || !file_exists($pathname)) {
1✔
96
            $builder = $this->create_container_builder();
1✔
97
            $builder->compile();
1✔
98
            $this->dump_container($builder, $pathname, $class);
1✔
99
        }
100

101
        require $pathname;
1✔
102

103
        /* @var $container \Symfony\Component\DependencyInjection\ContainerInterface */
104

105
        $container = new $class(); // @phpstan-ignore-line
1✔
106
        $container->set(Application::class, $app);
1✔
107

108
        return $container;
1✔
109
    }
110

111
    private function create_container_builder(): ContainerBuilder
112
    {
113
        $container = new ContainerBuilder();
1✔
114

115
        $this->apply_services($container);
1✔
116
        $this->apply_compiler_passes($container);
1✔
117
        $this->apply_extensions($container);
1✔
118

119
        return $container;
1✔
120
    }
121

122
    private function apply_compiler_passes(ContainerBuilder $builder): void
123
    {
124
        foreach ($this->compiler_passes() as $compiler_pass) {
1✔
125
            $compiler_pass->process($builder);
1✔
126
        }
127
    }
128

129
    private function apply_extensions(ContainerBuilder $container): void
130
    {
131
        foreach ($this->extensions() as $extension) {
1✔
132
            $container->registerExtension($extension);
1✔
133
            $container->loadFromExtension($extension->getAlias());
1✔
134
        }
135
    }
136

137
    /**
138
     * @return iterable<CompilerPassInterface>
139
     */
140
    private function compiler_passes(): iterable
141
    {
142
        foreach ($this->config->compiler_passes as $class) {
1✔
143
            yield new $class();
1✔
144
        }
145
    }
146

147
    /**
148
     * @return iterable<ExtensionInterface>
149
     */
150
    private function extensions(): iterable
151
    {
152
        $app = $this->app;
1✔
153

154
        foreach ($this->config->extensions as $constructor) {
1✔
155
            if (is_subclass_of($constructor, ExtensionWithFactory::class, true)) {
1✔
156
                yield $constructor::from($app);
1✔
157

158
                continue;
1✔
159
            }
160

161
            yield new $constructor();
×
162
        }
163
    }
164

165
    private function apply_services(ContainerBuilder $container): void
166
    {
167
        $collection = $this->collect_services();
1✔
168

169
        if (!$collection) {
1✔
170
            return; // @codeCoverageIgnore
171
        }
172

173
        $cwd = getcwd();
1✔
174
        assert(is_string($cwd));
175
        $loader = new YamlFileLoader($container, new FileLocator($cwd));
1✔
176

177
        foreach ($collection as $service_pathname) {
1✔
178
            $loader->load($service_pathname);
1✔
179
        }
180
    }
181

182
    /**
183
     * @return string[] Path names of the `services.yml` files collections.
184
     */
185
    private function collect_services(): array
186
    {
187
        $collection = [];
1✔
188

189
        foreach (array_keys($this->app->auto_config[Autoconfig::CONFIG_PATH]) as $path) {
1✔
190
            $pathname = $path . DIRECTORY_SEPARATOR . self::CONFIG_FILENAME;
1✔
191

192
            if (!file_exists($pathname)) {
1✔
193
                continue;
×
194
            }
195

196
            $collection[] = $pathname;
1✔
197
        }
198

199
        return $collection;
1✔
200
    }
201

202
    private function dump_container(ContainerBuilder $container, ContainerPathname $pathname, string $class): void
203
    {
204
        $dumper = new PhpDumper($container);
1✔
205
        $dumper->setProxyDumper(new ProxyDumper());
1✔
206

207
        file_put_contents($pathname, $dumper->dump([ 'class' => $class ]));
1✔
208
    }
209
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc