Completed
Push — master ( 319b56...e8ca6b )
by Samuel
14s
created

ServiceArgumentResolver::resolveArgument()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 24
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 12
nc 5
nop 1
dl 0
loc 24
rs 8.5125
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the Behat Symfony2Extension
5
 *
6
 * (c) Konstantin Kudryashov <[email protected]>
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace Behat\Symfony2Extension\Context\Argument;
13
14
use Behat\Behat\Context\Argument\ArgumentResolver;
15
use ReflectionClass;
16
use Symfony\Component\DependencyInjection\ContainerInterface;
17
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
18
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
19
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
20
use Symfony\Component\HttpKernel\KernelInterface;
21
22
/**
23
 * Resolves service arguments using the application container.
24
 *
25
 * @author Konstantin Kudryashov <[email protected]>
26
 */
27
final class ServiceArgumentResolver implements ArgumentResolver
28
{
29
    private $kernel;
30
31
    /**
32
     * Initializes resolver.
33
     *
34
     * @param KernelInterface $kernel
35
     */
36
    public function __construct(KernelInterface $kernel)
37
    {
38
        $this->kernel = $kernel;
39
    }
40
41
    /**
42
     * {@inheritdoc}
43
     */
44
    public function resolveArguments(ReflectionClass $classReflection, array $arguments)
45
    {
46
        $newArguments = array();
47
48
        foreach ($arguments as $key => $argument) {
49
            $newArguments[$key] = $this->resolveArgument($argument);
50
        }
51
52
        return $newArguments;
53
    }
54
55
    /**
56
     * Resolves single argument using container.
57
     *
58
     * @param string $argument
59
     *
60
     * @return mixed
61
     */
62
    private function resolveArgument($argument)
63
    {
64
        if (is_array($argument)) {
65
            return array_map(array($this, 'resolveArgument'), $argument);
66
        }
67
68
        if (!is_string($argument)) {
69
            return $argument;
70
        }
71
72
        $container = $this->kernel->getContainer();
73
74
        if ($service = $this->getService($container, $argument)) {
75
            return $service;
76
        }
77
78
        $resolvedParam = $this->replaceParameters($container, $argument);
79
80
        if (!is_string($resolvedParam)) {
81
            return $resolvedParam;
82
        }
83
84
        return $this->escape($resolvedParam);
85
    }
86
87
    /**
88
     * @param ContainerInterface $container
89
     * @param string $argument
90
     * @return object|null
91
     * @throws ServiceNotFoundException
92
     */
93
    private function getService(ContainerInterface $container, $argument)
94
    {
95
        if ($serviceName = $this->getServiceName($argument)) {
96
            return $container->get($serviceName);
97
        }
98
99
        return null;
100
    }
101
102
    /**
103
     * @param string $argument
104
     * @return string|null
105
     */
106
    private function getServiceName($argument)
107
    {
108
        if (preg_match('/^@[?]?([^@].*)$/', $argument, $matches)) {
109
            return $matches[1];
110
        }
111
112
        return null;
113
    }
114
115
    /**
116
     * Sanitise the container key given by the Behat config file,
117
     * and retrieve the parameter from the Container.
118
     *
119
     * First, check if the whole string is one substitution, if it is, then pull it from the container.
120
     *
121
     * Secondly, iterate over all substitutions in the string. Exception is thrown if referencing a
122
     * collection-type parameter when the key is not an entire substitution.
123
     *
124
     * This is to handle the case where we're given an argument which should return a
125
     * collection-type parameter from the container.
126
     *
127
     * @param  ContainerInterface $container
128
     * @param  string $argument
129
     * @throws InvalidArgumentException
130
     * @return mixed
131
     */
132
    private function replaceParameters(ContainerInterface $container, $argument)
133
    {
134
        if (preg_match('/^(?<!%)%([^%]+)%(?!%)$/', $argument, $matches)) {
135
            $replaced = $matches[1];
136
137
            if ($container->hasParameter($replaced)) {
138
                return $container->getParameter($replaced);
139
            }
140
141
            return $replaced;
142
        }
143
        
144
        return preg_replace_callback(
145
            '/(?<!%)%([^%]+)%(?!%)/',
146
            function ($matches) use ($container) {
147
                $parameter = $container->getParameter($matches[1]);
148
149
                if (is_array($parameter)) {
150
                    throw new InvalidArgumentException(
151
                        'Cannot reference a collection-type parameter with string interpolation.'
152
                    );
153
                }
154
155
                return $parameter;
156
            },
157
            $argument
158
        );
159
    }
160
161
    /**
162
     * @param string $argument
163
     * @return string
164
     */
165
    private function escape($argument)
166
    {
167
        $argument = preg_replace('/^@/', '', $argument);
168
169
        return str_replace('%%', '%', $argument);
170
    }
171
}
172