Completed
Push — master ( b77584...412d8f )
by Tomasz
06:43
created

ServiceParamConverter::supports()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 6
ccs 0
cts 4
cp 0
rs 9.4286
cc 2
eloc 3
nc 2
nop 1
crap 6
1
<?php
2
3
/*
4
 * All rights reserved
5
 * Copyright 2015 Gendoria
6
 */
7
8
namespace Gendoria\ParamConverterBundle\Request\ParamConverter;
9
10
use InvalidArgumentException;
11
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
12
use Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterInterface;
13
use Symfony\Component\DependencyInjection\Container;
14
use Symfony\Component\HttpFoundation\Request;
15
16
/**
17
 * This param converter converts parameters based on service method call.
18
 * 
19
 * Param converter invocation:
20
 * 
21
 * `@ParamConverter("parameter_name", converter="service_param_converter", options={"service" = "service_id", "method" = "service_method", "arguments" = {"%requestParamName%", "@otherServiceId", "someParameter"})`
22
 * 
23
 * Using this converter you can inject virtually any parameter into a controller, as service method call is not restricted with anything.
24
 * You should be **very cautious**, though, when passing request parameters into a service method call, as it can potentially lead to injections of malicious code.
25
 * All of the parameters injected should be properly restricted (eg to integers) using routing configuration.
26
 * 
27
 * @author Tomasz Struczyński <[email protected]>
28
 */
29
class ServiceParamConverter implements ParamConverterInterface
30
{
31
    /**
32
     * Service container.
33
     * 
34
     * @var Container
35
     */
36
    private $container;
37
    
38
    /**
39
     * Class constructor.
40
     * 
41
     * @param Container $container Service container.
42
     */
43 7
    public function __construct(Container $container)
44
    {
45 7
        $this->container = $container;
46
    }
47
    
48
    /**
49
     * Apply param converter.
50
     * 
51
     * @param Request $request Request
52
     * @param ParamConverter $configuration Param converter configuration.
53
     * @return boolean
54
     * @throws InvalidArgumentException
55
     */
56 6
    public function apply(Request $request, ParamConverter $configuration)
57
    {
58
        $param = $configuration->getName();
59
        $options = $this->getOptions($configuration);
60
        
61 6
        foreach ($options['arguments'] as &$value) {
62 1
            $value = $this->parseArgument($value, $request);
63 3
        }
64
        
65
        $service = $this->container->get($options['service']);
66
        $return = call_user_func_array(array($service, $options['method']), $options['arguments']);
67
        
68
        if ($configuration->getClass() && (!is_object($return) || !is_a($return, $configuration->getClass()))) {
69 1
            return false;
70
        }
71
72
        $request->attributes->set($param, $return);
73
        
74 4
        return true;
75 5
    }
76
    
77
    /**
78
     * Parse single argument.
79
     * 
80
     * @param string $value
81
     * @param Request $request
82
     * @return mixed
83
     * @throws InvalidArgumentException
84
     */
85 4
    private function parseArgument($value, Request $request)
86
    {
87
        if (strpos($value, '%') === 0 && strrpos($value, '%') === strlen($value)-1) {
88
            return $request->get(substr($value, 1, strlen($value)-2));
89
        } elseif (strpos($value, '@') === 0) {
90
            if ($this->container->has(substr($value, 1)) === false) {
91
                throw new InvalidArgumentException("Unknown service requested: ".$value);
92
            }
93
            return $this->container->get(substr($value, 1));
94
        }
95 1
        return $value;
96 4
    }
97
98
    public function supports(ParamConverter $configuration)
99
    {
100
        $options = $this->getOptions($configuration);
101
        
102
        return $this->container->has($options['service']) && !empty($options['method']);
103
    }
104
    
105 6
    protected function getOptions(ParamConverter $configuration)
106
    {
107
        $options = $configuration->getOptions();
108
        if (!is_array($options)) {
109
            $options = array();
110
        }
111
        return array_replace(array(
112
            'service' => null,
113
            'method' => null,
114
            'arguments' => array(),
115
        ), $options);
116
    }    
117
}