ServiceArgumentResolver::escape()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 6
rs 9.4285
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
        $container = $container->has('test.service_container') ? $container->get('test.service_container') : $container;
74
75
        if ($service = $this->getService($container, $argument)) {
76
            return $service;
77
        }
78
79
        $resolvedParam = $this->replaceParameters($container, $argument);
80
81
        if (!is_string($resolvedParam)) {
82
            return $resolvedParam;
83
        }
84
85
        return $this->escape($resolvedParam);
86
    }
87
88
    /**
89
     * @param ContainerInterface $container
90
     * @param string $argument
91
     * @return object|null
92
     * @throws ServiceNotFoundException
93
     */
94
    private function getService(ContainerInterface $container, $argument)
95
    {
96
        if ($serviceName = $this->getServiceName($argument)) {
97
            return $container->get($serviceName);
98
        }
99
100
        return null;
101
    }
102
103
    /**
104
     * @param string $argument
105
     * @return string|null
106
     */
107
    private function getServiceName($argument)
108
    {
109
        if (preg_match('/^@[?]?([^@].*)$/', $argument, $matches)) {
110
            return $matches[1];
111
        }
112
113
        return null;
114
    }
115
116
    /**
117
     * Sanitise the container key given by the Behat config file,
118
     * and retrieve the parameter from the Container.
119
     *
120
     * First, check if the whole string is one substitution, if it is, then pull it from the container.
121
     *
122
     * Secondly, iterate over all substitutions in the string. Exception is thrown if referencing a
123
     * collection-type parameter when the key is not an entire substitution.
124
     *
125
     * This is to handle the case where we're given an argument which should return a
126
     * collection-type parameter from the container.
127
     *
128
     * @param  ContainerInterface $container
129
     * @param  string $argument
130
     * @throws InvalidArgumentException
131
     * @return mixed
132
     */
133
    private function replaceParameters(ContainerInterface $container, $argument)
134
    {
135
        if (preg_match('/^(?<!%)%([^%]+)%(?!%)$/', $argument, $matches)) {
136
            $replaced = $matches[1];
137
138
            if ($container->hasParameter($replaced)) {
139
                return $container->getParameter($replaced);
140
            }
141
142
            return $replaced;
143
        }
144
        
145
        return preg_replace_callback(
146
            '/(?<!%)%([^%]+)%(?!%)/',
147
            function ($matches) use ($container) {
148
                $parameter = $container->getParameter($matches[1]);
149
150
                if (is_array($parameter)) {
151
                    throw new InvalidArgumentException(
152
                        'Cannot reference a collection-type parameter with string interpolation.'
153
                    );
154
                }
155
156
                return $parameter;
157
            },
158
            $argument
159
        );
160
    }
161
162
    /**
163
     * @param string $argument
164
     * @return string
165
     */
166
    private function escape($argument)
167
    {
168
        $argument = preg_replace('/^@/', '', $argument);
169
170
        return str_replace('%%', '%', $argument);
171
    }
172
}
173