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

odan / Slim / 19937261397

04 Dec 2025 05:04PM UTC coverage: 97.317% (-1.5%) from 98.808%
19937261397

push

github

web-flow
Merge branch 'slimphp:5.x' into 5.x

158 of 171 new or added lines in 22 files covered. (92.4%)

4 existing lines in 2 files now uncovered.

943 of 969 relevant lines covered (97.32%)

34.03 hits per line

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

75.68
/Slim/Container/ContainerResolver.php
1
<?php
2

3
/**
4
 * Slim Framework (https://slimframework.com)
5
 *
6
 * @license https://github.com/slimphp/Slim/blob/5.x/LICENSE.md (MIT License)
7
 */
8

9
declare(strict_types=1);
10

11
namespace Slim\Container;
12

13
use Closure;
14
use Psr\Container\ContainerInterface;
15
use RuntimeException;
16
use Slim\Interfaces\ContainerResolverInterface;
17

18
use function is_array;
19

20
/**
21
 * Resolves identifiers into services or callables using a PSR-11 DI container.
22
 *
23
 * Supports:
24
 * - Service names (strings)
25
 * - Slim notation: "service:method"
26
 * - PHP notation: "Class::method"
27
 * - Arrays like ["service", "method"]
28
 * - Callables or objects directly
29
 *
30
 * Returned results can be:
31
 * - A callable
32
 * - An object fetched from the container
33
 * - A callable bound to the container (closures)
34
 *
35
 * This is used internally by Slim to resolve route callables, middleware, and
36
 * other handler definitions.
37
 */
38
final class ContainerResolver implements ContainerResolverInterface
39
{
40
    private ContainerInterface $container;
41

42
    /**
43
     * Regex matching Slim-style "service:method" callables.
44
     */
45
    private string $callablePattern = '!^([^\:]+)\:([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)$!';
46

47
    public function __construct(ContainerInterface $container)
48
    {
49
        $this->container = $container;
213✔
50
    }
51

52
    /**
53
     * {@inheritdoc}
54
     */
55
    public function resolve(callable|object|array|string $identifier): mixed
56
    {
57
        // Already an object, no further resolution needed
58
        if (is_object($identifier)) {
120✔
59
            return $identifier;
101✔
60
        }
61

62
        // Bind callable to container
63
        if (is_callable($identifier)) {
106✔
64
            return $this->bindToContainer($identifier);
3✔
65
        }
66

67
        // ClassName::methodName or Slim notation ClassName:methodName
68
        if (is_string($identifier) && preg_match($this->callablePattern, $identifier, $matches)) {
104✔
69
            $identifier = [$matches[1], $matches[2]];
8✔
70
        }
71

72
        // Resolve as a container entry name
73
        if (is_string($identifier)) {
104✔
74
            return $this->container->get($identifier);
96✔
75
        }
76

77
        // Array callable notation: ['service-id', 'method']
78
        // @phpstan-ignore-next-line
79
        if (is_string($identifier[0])) {
11✔
80
            // Replace the container entry name by the actual object
81
            $service = $this->container->get($identifier[0]);
10✔
82

83
            if (!is_object($service) && !is_string($service)) {
8✔
NEW
84
                throw new RuntimeException(
×
NEW
85
                    sprintf(
×
NEW
86
                        'Container entry "%s" must resolve to an object or class name.',
×
NEW
87
                        $identifier[0],
×
NEW
88
                    ),
×
NEW
89
                );
×
90
            }
91

92
            $method = (string)($identifier[1] ?? '');
8✔
93

94
            if (!method_exists($service, $method)) {
8✔
95
                throw new RuntimeException(sprintf('The method "%s" does not exist', $method));
2✔
96
            }
97

98
            return [$service, $method];
6✔
99
        }
100

101
        // @phpstan-ignore-next-line
102
        return $identifier;
1✔
103
    }
104

105
    /**
106
     * {@inheritdoc}
107
     */
108
    public function resolveCallable(callable|array|string $identifier): callable
109
    {
110
        if (is_string($identifier)) {
85✔
111
            $identifier = $this->resolve($identifier);
5✔
112
        }
113

114
        if (is_callable($identifier)) {
84✔
115
            return $this->bindToContainer($identifier);
84✔
116
        }
117

118
        // Unrecognized stuff, we let it fail
UNCOV
119
        throw new RuntimeException(
×
NEW
120
            sprintf('The definition "%s" is not a callable.', implode(':', (array)$identifier)),
×
UNCOV
121
        );
×
122
    }
123

124
    /**
125
     * Bind closures to the container to allow `$this` access.
126
     * @param callable $callable
127
     */
128
    private function bindToContainer(callable $callable): callable
129
    {
130
        if (is_array($callable) && $callable[0] instanceof Closure) {
86✔
131
            $callable = $callable[0];
1✔
132
        }
133

134
        if ($callable instanceof Closure) {
86✔
135
            $callable = $callable->bindTo($this->container) ?? throw new RuntimeException(
80✔
136
                'Unable to bind callable to DI container.',
80✔
137
            );
80✔
138
        }
139

140
        return $callable;
86✔
141
    }
142
}
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