Issues (109)

src/di/ModuleJson.php (10 issues)

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
    /**
31
     * @param PointcutList $pointcuts
0 ignored issues
show
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...
32
     */
33
    public function __invoke(Container $container, array $pointcuts): string
34
    {
35
        $bindings = $this->getBindings($container, $pointcuts);
36
37
        return json_encode($bindings, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?: '{}';
38
    }
39
40
    /**
41
     * @param PointcutList $pointcuts
42
     *
43
     * @return ModuleBindings
0 ignored issues
show
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...
44
     */
45
    public function getBindings(Container $container, array $pointcuts): array
46
    {
47
        /** @psalm-suppress MixedAssignment */
48
        $container = unserialize(serialize($container), ['allowed_classes' => true]);
49
        /** @var Container $container */
50
        $collector = new AopCollector();
51
        $entries = [];
52
53
        foreach ($container->getContainer() as $dependencyIndex => $dependency) {
54
            $aopBindings = [];
55
            if ($dependency instanceof Dependency) {
56
                $aopBindings = $collector->collect($dependency, $pointcuts);
57
            }
58
59
            $entry = $this->createEntry($dependencyIndex, $dependency, $aopBindings);
60
            if ($entry !== null) {
61
                $entries[] = $entry;
62
            }
63
        }
64
65
        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...,array<string,string>>> which is incompatible with the documented return type Ray\Di\ModuleBindings.
Loading history...
66
    }
67
68
    /**
69
     * @param AopBindings $aopBindings
0 ignored issues
show
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...
70
     *
71
     * @return BindingEntry|null
0 ignored issues
show
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...
72
     */
73
    private function createEntry(string $dependencyIndex, DependencyInterface $dependency, array $aopBindings): ?array
74
    {
75
        [$interface, $name] = $this->parseIndex($dependencyIndex);
76
77
        if ($dependency instanceof Dependency) {
78
            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<string,string> which is incompatible with the documented return type Ray\Di\BindingEntry|null.
Loading history...
79
        }
80
81
        if ($dependency instanceof DependencyProvider) {
82
            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...
83
        }
84
85
        if ($dependency instanceof Instance) {
86
            return $this->createInstanceEntry($interface, $name, $dependency);
87
        }
88
89
        return null;
90
    }
91
92
    /**
93
     * @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...
94
     */
95
    private function parseIndex(string $index): array
96
    {
97
        $pos = strrpos($index, '-');
98
        if ($pos === false) {
99
            return [$index, Name::ANY];
100
        }
101
102
        return [substr($index, 0, $pos), substr($index, $pos + 1)];
103
    }
104
105
    /**
106
     * @param AopBindings $aopBindings
107
     *
108
     * @return BindingEntry
109
     */
110
    private function createDependencyEntry(string $interface, string $name, Dependency $dependency, array $aopBindings): array
111
    {
112
        $dependencyString = (string) $dependency;
113
        // Extract class name from "(dependency) ClassName" or "(dependency) ClassName (aop) ..."
114
        $className = preg_replace('/^\(dependency\) ([^\s]+).*$/', '$1', $dependencyString) ?? '';
115
116
        $entry = [
117
            'interface' => $interface,
118
            'name' => $name,
119
            'type' => 'class',
120
            'to' => $className,
121
        ];
122
123
        if ($aopBindings !== []) {
124
            $entry['aop'] = $aopBindings;
125
        }
126
127
        return $entry;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $entry returns the type array<string,string> which is incompatible with the documented return type Ray\Di\BindingEntry.
Loading history...
128
    }
129
130
    /**
131
     * @return BindingEntry
132
     */
133
    private function createProviderEntry(string $interface, string $name, DependencyProvider $dependency): array
134
    {
135
        $providerString = (string) $dependency;
136
        // Extract provider class name from "(provider) (dependency) ProviderClassName"
137
        $providerClass = preg_replace('/^\(provider\) \(dependency\) ([^\s]+).*$/', '$1', $providerString) ?? '';
138
139
        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...
140
            'interface' => $interface,
141
            'name' => $name,
142
            'type' => 'provider',
143
            'to' => $providerClass,
144
        ];
145
    }
146
147
    /**
148
     * @return BindingEntry
149
     */
150
    private function createInstanceEntry(string $interface, string $name, Instance $dependency): array
151
    {
152
        $value = $dependency->value;
153
154
        return [
155
            'interface' => $interface,
156
            'name' => $name,
157
            'type' => 'instance',
158
            'to' => $this->formatValue($value),
159
        ];
160
    }
161
162
    /**
163
     * @param mixed $value
164
     *
165
     * @return mixed
166
     */
167
    private function formatValue($value)
168
    {
169
        if (is_scalar($value) || $value === null) {
170
            return $value;
171
        }
172
173
        if (is_object($value)) {
174
            return ['__class' => $value::class];
175
        }
176
177
        return ['__type' => gettype($value)];
178
    }
179
}
180