Completed
Push — develop ( a2ba3f...6fbee4 )
by Tino
07:18
created

ControllerActionDispatcher::isValidPostRequest()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 6
nc 4
nop 1
dl 0
loc 10
rs 9.6111
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Copyright (c) by the ACP3 Developers.
5
 * See the LICENSE file at the top-level module directory for licensing details.
6
 */
7
8
namespace ACP3\Core\Application;
9
10
use ACP3\Core\Application\Event\ControllerActionAfterDispatchEvent;
11
use ACP3\Core\Application\Event\ControllerActionBeforeDispatchEvent;
12
use ACP3\Core\Controller\ActionInterface;
13
use ACP3\Core\Controller\Exception\ControllerActionNotFoundException;
14
use ACP3\Core\Http\RequestInterface;
15
use Psr\Container\ContainerInterface;
16
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
17
use Symfony\Component\HttpFoundation\Response;
18
use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface;
19
20
class ControllerActionDispatcher
21
{
22
    private const ACTION_METHOD_DEFAULT = 'execute';
23
    private const ACTION_METHOD_POST = 'executePost';
24
25
    /**
26
     * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
27
     */
28
    protected $eventDispatcher;
29
    /**
30
     * @var \ACP3\Core\Http\RequestInterface
31
     */
32
    protected $request;
33
    /**
34
     * @var \Psr\Container\ContainerInterface
35
     */
36
    protected $container;
37
    /**
38
     * @var ArgumentResolverInterface
39
     */
40
    protected $argumentResolver;
41
42
    /**
43
     * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher
44
     * @param \ACP3\Core\Http\RequestInterface                            $request
45
     * @param \Psr\Container\ContainerInterface                           $container
46
     * @param ArgumentResolverInterface                                   $argumentResolver
47
     */
48
    public function __construct(
49
        EventDispatcherInterface $eventDispatcher,
50
        RequestInterface $request,
51
        ContainerInterface $container,
52
        ArgumentResolverInterface $argumentResolver
53
    ) {
54
        $this->eventDispatcher = $eventDispatcher;
55
        $this->request = $request;
56
        $this->container = $container;
57
        $this->argumentResolver = $argumentResolver;
58
    }
59
60
    /**
61
     * @param string|null $serviceId
62
     * @param array       $arguments
63
     *
64
     * @return Response|string
65
     *
66
     * @throws ControllerActionNotFoundException
67
     * @throws \ACP3\Core\Controller\Exception\ResultNotExistsException
68
     * @throws \ReflectionException
69
     */
70
    public function dispatch(?string $serviceId = null, array $arguments = [])
71
    {
72
        if (empty($serviceId)) {
73
            $serviceId = $this->buildControllerServiceId();
74
        } else {
75
            $this->modifyRequest($serviceId, $arguments);
76
        }
77
78
        if ($this->container->has($serviceId)) {
79
            $this->eventDispatcher->dispatch(
80
                'core.application.controller_action_dispatcher.before_dispatch',
81
                new ControllerActionBeforeDispatchEvent($serviceId)
82
            );
83
84
            /** @var \ACP3\Core\Controller\ActionInterface $controller */
85
            $controller = $this->container->get($serviceId);
86
            $controller->preDispatch();
87
            $response = $controller->display($this->executeControllerAction($controller, $arguments));
88
89
            $this->eventDispatcher->dispatch(
90
                'core.application.controller_action_dispatcher.after_dispatch',
91
                new ControllerActionAfterDispatchEvent($serviceId, $response)
92
            );
93
94
            return $response;
95
        }
96
97
        throw new ControllerActionNotFoundException(
98
            'Service-Id ' . $serviceId . ' was not found!'
99
        );
100
    }
101
102
    /**
103
     * @return string
104
     */
105
    protected function buildControllerServiceId()
106
    {
107
        return $this->request->getModule()
108
            . '.controller.'
109
            . $this->request->getArea()
110
            . '.' . $this->request->getController()
111
            . '.' . $this->request->getAction();
112
    }
113
114
    protected function modifyRequest(string $serviceId, array $arguments)
115
    {
116
        list($module, , , $controller, $action) = \explode('.', $serviceId);
117
118
        $params = '';
119
        foreach ($arguments as $key => $value) {
120
            $params .= "/{$key}_{$value}";
121
        }
122
123
        $this->request->setPathInfo($module . '/' . $controller . '/' . $action . $params);
124
        $this->request->processQuery();
125
    }
126
127
    /**
128
     * @param \ACP3\Core\Controller\ActionInterface $controller
129
     * @param array                                 $arguments
130
     *
131
     * @return mixed
132
     *
133
     * @throws \ReflectionException
134
     */
135
    private function executeControllerAction(ActionInterface $controller, array $arguments)
136
    {
137
        $callable = $this->getCallable($controller);
138
139
        if (empty($arguments)) {
140
            $arguments = $this->argumentResolver->getArguments($this->request->getSymfonyRequest(), $callable);
141
        }
142
143
        return \call_user_func_array($callable, $arguments);
144
    }
145
146
    /**
147
     * @param ActionInterface $controller
148
     *
149
     * @return array
150
     *
151
     * @throws \ReflectionException
152
     */
153
    private function getCallable(ActionInterface $controller)
154
    {
155
        $callable = [$controller, self::ACTION_METHOD_DEFAULT];
156
        if ($this->isValidPostRequest($controller)) {
157
            $reflection = new \ReflectionMethod($controller, self::ACTION_METHOD_POST);
158
159
            if ($reflection->isPublic()) {
160
                $callable = [$controller, self::ACTION_METHOD_POST];
161
            }
162
        }
163
164
        return $callable;
165
    }
166
167
    private function isValidPostRequest(ActionInterface $controller): bool
168
    {
169
        if ($this->request->getSymfonyRequest()->isMethod('POST')
170
            && \method_exists($controller, self::ACTION_METHOD_POST)) {
171
            return $this->request->getPost()->has('submit')
172
                || $this->request->getPost()->has('continue')
173
                || \method_exists($controller, self::ACTION_METHOD_DEFAULT) === false;
174
        }
175
176
        return false;
177
    }
178
}
179