Passed
Push — claude/module-string-evaluatio... ( 60e4d7 )
by Akihito
02:21
created

ModuleJson::getBindings()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 11
c 1
b 0
f 0
dl 0
loc 21
rs 9.9
cc 4
nc 5
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Ray\Di;
6
7
use function gettype;
8
use function is_object;
9
use function is_scalar;
10
use function json_encode;
11
use function preg_replace;
12
use function serialize;
13
use function strrpos;
14
use function substr;
15
use function unserialize;
16
17
use const JSON_PRETTY_PRINT;
18
use const JSON_UNESCAPED_SLASHES;
19
use const JSON_UNESCAPED_UNICODE;
20
21
/**
22
 * @psalm-import-type PointcutList from Types
23
 * @psalm-type BindingType = 'class'|'provider'|'instance'
24
 * @psalm-type AopBindings = array<string, list<string>>
25
 * @psalm-type BindingEntry = array{interface: string, name: string, type: BindingType, to: mixed, aop?: AopBindings}
26
 * @psalm-type ModuleBindings = array{bindings: list<BindingEntry>}
27
 */
28
final class ModuleJson
29
{
30
    private const SCHEMA = 'https://ray-di.github.io/schemas/module.schema.json';
31
32
    /**
33
     * @param PointcutList $pointcuts
0 ignored issues
show
Bug introduced by
The type Ray\Di\PointcutList was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
34
     */
35
    public function __invoke(Container $container, array $pointcuts): string
36
    {
37
        $bindings = $this->getBindings($container, $pointcuts);
38
39
        return json_encode($bindings, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?: '{}';
40
    }
41
42
    /**
43
     * @param PointcutList $pointcuts
44
     *
45
     * @return ModuleBindings
0 ignored issues
show
Bug introduced by
The type Ray\Di\ModuleBindings was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
46
     */
47
    public function getBindings(Container $container, array $pointcuts): array
48
    {
49
        /** @psalm-suppress MixedAssignment */
50
        $container = unserialize(serialize($container), ['allowed_classes' => true]);
51
        /** @var Container $container */
52
        $collector = new AopCollector();
53
        $entries = [];
54
55
        foreach ($container->getContainer() as $dependencyIndex => $dependency) {
56
            $aopBindings = [];
57
            if ($dependency instanceof Dependency) {
58
                $aopBindings = $collector->collect($dependency, $pointcuts);
59
            }
60
61
            $entry = $this->createEntry($dependencyIndex, $dependency, $aopBindings);
62
            if ($entry !== null) {
63
                $entries[] = $entry;
64
            }
65
        }
66
67
        return ['bindings' => $entries];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array('bindings' => $entries) returns the type array<string,array|array<mixed,array>> which is incompatible with the documented return type Ray\Di\ModuleBindings.
Loading history...
68
    }
69
70
    /**
71
     * @param AopBindings $aopBindings
0 ignored issues
show
Bug introduced by
The type Ray\Di\AopBindings was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
72
     *
73
     * @return BindingEntry|null
0 ignored issues
show
Bug introduced by
The type Ray\Di\BindingEntry was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
74
     */
75
    private function createEntry(string $dependencyIndex, DependencyInterface $dependency, array $aopBindings): ?array
76
    {
77
        [$interface, $name] = $this->parseIndex($dependencyIndex);
78
79
        if ($dependency instanceof Dependency) {
80
            return $this->createDependencyEntry($interface, $name, $dependency, $aopBindings);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->createDepe...pendency, $aopBindings) returns the type array which is incompatible with the documented return type Ray\Di\BindingEntry|null.
Loading history...
81
        }
82
83
        if ($dependency instanceof DependencyProvider) {
84
            return $this->createProviderEntry($interface, $name, $dependency);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->createProv...ce, $name, $dependency) returns the type array<string,string> which is incompatible with the documented return type Ray\Di\BindingEntry|null.
Loading history...
85
        }
86
87
        if ($dependency instanceof Instance) {
88
            return $this->createInstanceEntry($interface, $name, $dependency);
89
        }
90
91
        return null;
92
    }
93
94
    /**
95
     * @return array{string, string}
0 ignored issues
show
Documentation Bug introduced by
The doc comment array{string, string} at position 2 could not be parsed: Expected ':' at position 2, but found 'string'.
Loading history...
96
     */
97
    private function parseIndex(string $index): array
98
    {
99
        $pos = strrpos($index, '-');
100
        if ($pos === false) {
101
            return [$index, Name::ANY];
102
        }
103
104
        return [substr($index, 0, $pos), substr($index, $pos + 1)];
105
    }
106
107
    /**
108
     * @param AopBindings $aopBindings
109
     *
110
     * @return BindingEntry
111
     */
112
    private function createDependencyEntry(string $interface, string $name, Dependency $dependency, array $aopBindings): array
113
    {
114
        $dependencyString = (string) $dependency;
115
        // Extract class name from "(dependency) ClassName" or "(dependency) ClassName (aop) ..."
116
        $className = preg_replace('/^\(dependency\) ([^\s]+).*$/', '$1', $dependencyString) ?? '';
117
118
        $entry = [
119
            'interface' => $interface,
120
            'name' => $name,
121
            'type' => 'class',
122
            'to' => $className,
123
        ];
124
125
        if ($aopBindings !== []) {
126
            $entry['aop'] = $aopBindings;
127
        }
128
129
        /** @var BindingEntry $entry */
130
        return $entry;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $entry returns the type Ray\Di\BindingEntry which is incompatible with the type-hinted return array.
Loading history...
131
    }
132
133
    /**
134
     * @return BindingEntry
135
     */
136
    private function createProviderEntry(string $interface, string $name, DependencyProvider $dependency): array
137
    {
138
        $providerString = (string) $dependency;
139
        // Extract provider class name from "(provider) (dependency) ProviderClassName"
140
        $providerClass = preg_replace('/^\(provider\) \(dependency\) ([^\s]+).*$/', '$1', $providerString) ?? '';
141
142
        return [
0 ignored issues
show
Bug Best Practice introduced by
The expression return array('interface'...'to' => $providerClass) returns the type array<string,string> which is incompatible with the documented return type Ray\Di\BindingEntry.
Loading history...
143
            'interface' => $interface,
144
            'name' => $name,
145
            'type' => 'provider',
146
            'to' => $providerClass,
147
        ];
148
    }
149
150
    /**
151
     * @return BindingEntry
152
     */
153
    private function createInstanceEntry(string $interface, string $name, Instance $dependency): array
154
    {
155
        $value = $dependency->value;
156
157
        return [
158
            'interface' => $interface,
159
            'name' => $name,
160
            'type' => 'instance',
161
            'to' => $this->formatValue($value),
162
        ];
163
    }
164
165
    /**
166
     * @param mixed $value
167
     *
168
     * @return mixed
169
     */
170
    private function formatValue($value)
171
    {
172
        if (is_scalar($value) || $value === null) {
173
            return $value;
174
        }
175
176
        if (is_object($value)) {
177
            return ['__class' => $value::class];
178
        }
179
180
        return ['__type' => gettype($value)];
181
    }
182
}
183