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

ICanBoogie / bind-symfony-dependency-injection / 4264642802

pending completion
4264642802

push

github

Olivier Laviale
Dump the container when the proxy is created

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

66 of 123 relevant lines covered (53.66%)

3.55 hits per line

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

10.91
/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
    private const CONFIG_FILENAME = 'services.yml';
41

42
    public readonly ContainerInterface $container;
43

44
    // @codeCoverageIgnoreStart
45

46
    public function __construct(
47
        private readonly Application $app,
48
        private readonly Config $config
49
    ) {
50
        $this->container = $this->instantiate_container();
51
    }
52

53
    // @codeCoverageIgnoreEnd
54

55
    /**
56
     * Note: We need the proxy to be a callable to satisfy `ICanBoogie\Service\ServiceProvider`.
57
     */
58
    public function __invoke(string $id): object
59
    {
60
        return $this->get($id);
5✔
61
    }
62

63
    public function get(string $id): object
64
    {
65
        return match ($id) {
7✔
66
            self::ALIAS_APP, Application::class => $this->app,
7✔
67
            self::ALIAS_CONTAINER => $this->container,
7✔
68
            default => $this->container->get($id),
7✔
69
        };
7✔
70
    }
71

72
    public function has(string $id): bool
73
    {
74
        return match ($id) {
×
75
            self::ALIAS_APP, Application::class, self::ALIAS_CONTAINER => true,
×
76
            default => $this->container->has($id),
×
77
        };
×
78
    }
79

80
    private function instantiate_container(): ContainerInterface
81
    {
82
        $app = $this->app;
×
83
        $pathname = ContainerPathname::from($app);
×
84
        /** @var class-string<\Symfony\Component\DependencyInjection\ContainerInterface> $class */
85
        $class = 'ApplicationContainer';
×
86

87
        if (!$this->config->use_caching || !file_exists($pathname)) {
×
88
            $builder = $this->create_container_builder();
×
89
            $builder->compile();
×
90
            $this->dump_container($builder, $pathname, $class);
×
91
        }
92

93
        require $pathname;
×
94

95
        $container = new $class();
×
96
        $container->set(Application::class, $app);
×
97

98
        return $container;
×
99
    }
100

101
    private function create_container_builder(): ContainerBuilder
102
    {
103
        $container = new ContainerBuilder();
×
104

105
        $this->apply_services($container);
×
106
        $this->apply_compiler_passes($container);
×
107
        $this->apply_extensions($container);
×
108

109
        return $container;
×
110
    }
111

112
    private function apply_compiler_passes(ContainerBuilder $builder): void
113
    {
114
        foreach ($this->compiler_passes() as $compiler_pass) {
×
115
            $compiler_pass->process($builder);
×
116
        }
117
    }
118

119
    private function apply_extensions(ContainerBuilder $container): void
120
    {
121
        foreach ($this->extensions() as $extension) {
×
122
            $container->registerExtension($extension);
×
123
            $container->loadFromExtension($extension->getAlias());
×
124
        }
125
    }
126

127
    /**
128
     * @return iterable<CompilerPassInterface>
129
     */
130
    private function compiler_passes(): iterable
131
    {
132
        foreach ($this->config->compiler_passes as $class) {
×
133
            yield new $class();
×
134
        }
135
    }
136

137
    /**
138
     * @return iterable<ExtensionInterface>
139
     */
140
    private function extensions(): iterable
141
    {
142
        $app = $this->app;
×
143

144
        foreach ($this->config->extensions as $constructor) {
×
145
            if (is_subclass_of($constructor, ExtensionWithFactory::class)) {
×
146
                yield $constructor::from($app);
×
147

148
                continue;
×
149
            }
150

151
            yield new $constructor();
×
152
        }
153
    }
154

155
    private function apply_services(ContainerBuilder $container): void
156
    {
157
        $collection = $this->collect_services();
×
158

159
        if (!$collection) {
×
160
            return; // @codeCoverageIgnore
161
        }
162

163
        $cwd = getcwd();
×
164
        assert(is_string($cwd));
165
        $loader = new YamlFileLoader($container, new FileLocator($cwd));
×
166

167
        foreach ($collection as $service_pathname) {
×
168
            $loader->load($service_pathname);
×
169
        }
170
    }
171

172
    /**
173
     * @return string[] Path names of the `services.yml` files collections.
174
     */
175
    private function collect_services(): array
176
    {
177
        $collection = [];
×
178

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

182
            if (!file_exists($pathname)) {
×
183
                continue;
×
184
            }
185

186
            $collection[] = $pathname;
×
187
        }
188

189
        return $collection;
×
190
    }
191

192
    private function dump_container(ContainerBuilder $container, ContainerPathname $pathname, string $class): void
193
    {
194
        $dumper = new PhpDumper($container);
×
195
        $dumper->setProxyDumper(new ProxyDumper());
×
196

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