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

ICanBoogie / bind-symfony-dependency-injection / 4267329321

pending completion
4267329321

push

github

Olivier Laviale
ContainerProxy constructor is now private

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

66 of 124 relevant lines covered (53.23%)

3.52 hits per line

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

10.17
/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\Application;
15
use ICanBoogie\Autoconfig\Autoconfig;
16
use ICanBoogie\Binding\SymfonyDependencyInjection\Extension\ApplicationExtension;
17
use olvlvl\SymfonyDependencyInjectionProxy\ProxyDumper;
18
use Psr\Container\ContainerInterface;
19
use Symfony\Component\Config\FileLocator;
20
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
21
use Symfony\Component\DependencyInjection\ContainerBuilder;
22
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
23
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
24
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
25

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

33
/**
34
 * Proxy to Symfony container.
35
 */
36
final class ContainerProxy implements ContainerInterface
37
{
38
    public const ALIAS_APP = ApplicationExtension::APP_SERVICE;
39
    public const ALIAS_CONTAINER = 'container';
40
    public const CONFIG_FILENAME = 'services.yml';
41

42
    /**
43
     * Creates a container proxy from the application instance.
44
     */
45
    public static function from(Application $app): self
46
    {
47
        return new self(
×
48
            $app,
×
49
            $app->config_for_class(Config::class)
×
50
        );
×
51
    }
52

53
    // @codeCoverageIgnoreStart
54

55
    public readonly ContainerInterface $container;
56

57
    private function __construct(
58
        private readonly Application $app,
59
        private readonly Config $config
60
    ) {
61
        $this->container = $this->instantiate_container();
62
    }
63

64
    // @codeCoverageIgnoreEnd
65

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

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

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

91
    private function instantiate_container(): ContainerInterface
92
    {
93
        $app = $this->app;
×
94
        $pathname = ContainerPathname::from($app);
×
95
        /** @var class-string<\Symfony\Component\DependencyInjection\ContainerInterface> $class */
96
        $class = 'ApplicationContainer';
×
97

98
        if (!$this->config->use_caching || !file_exists($pathname)) {
×
99
            $builder = $this->create_container_builder();
×
100
            $builder->compile();
×
101
            $this->dump_container($builder, $pathname, $class);
×
102
        }
103

104
        require $pathname;
×
105

106
        $container = new $class();
×
107
        $container->set(Application::class, $app);
×
108

109
        return $container;
×
110
    }
111

112
    private function create_container_builder(): ContainerBuilder
113
    {
114
        $container = new ContainerBuilder();
×
115

116
        $this->apply_services($container);
×
117
        $this->apply_compiler_passes($container);
×
118
        $this->apply_extensions($container);
×
119

120
        return $container;
×
121
    }
122

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

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

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

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

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

159
                continue;
×
160
            }
161

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

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

170
        if (!$collection) {
×
171
            return; // @codeCoverageIgnore
172
        }
173

174
        $cwd = getcwd();
×
175
        assert(is_string($cwd));
176
        $loader = new YamlFileLoader($container, new FileLocator($cwd));
×
177

178
        foreach ($collection as $service_pathname) {
×
179
            $loader->load($service_pathname);
×
180
        }
181
    }
182

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

190
        foreach (array_keys($this->app->autoconfig[Autoconfig::CONFIG_PATH]) as $path) {
×
191
            $pathname = $path . DIRECTORY_SEPARATOR . self::CONFIG_FILENAME;
×
192

193
            if (!file_exists($pathname)) {
×
194
                continue;
×
195
            }
196

197
            $collection[] = $pathname;
×
198
        }
199

200
        return $collection;
×
201
    }
202

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

208
        file_put_contents($pathname, $dumper->dump([ 'class' => $class ]));
×
209
    }
210
}
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