Passed
Push — master ( 1a80b6...1aad82 )
by Christian
26:28 queued 12:48
created

StoreApiProxyController::setupStoreApiRequest()   B

Complexity

Conditions 7
Paths 14

Size

Total Lines 38
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 7
eloc 20
c 2
b 0
f 0
nc 14
nop 2
dl 0
loc 38
rs 8.6666
1
<?php declare(strict_types=1);
2
3
namespace Shopware\Storefront\Controller;
4
5
use Shopware\Core\Framework\Routing\Annotation\RouteScope;
6
use Shopware\Core\Framework\Routing\Exception\InvalidRequestParameterException;
7
use Shopware\Core\Framework\Routing\Exception\MissingRequestParameterException;
8
use Shopware\Core\PlatformRequest;
9
use Shopware\Core\SalesChannelRequest;
10
use Shopware\Core\System\SalesChannel\SalesChannelContext;
11
use Symfony\Component\HttpFoundation\Request;
12
use Symfony\Component\HttpFoundation\RequestStack;
13
use Symfony\Component\HttpFoundation\Response;
14
use Symfony\Component\HttpKernel\HttpKernelInterface;
15
use Symfony\Component\HttpKernel\KernelInterface;
16
use Symfony\Component\Routing\Annotation\Route;
17
18
/**
19
 * @RouteScope(scopes={"storefront"})
20
 */
21
class StoreApiProxyController
22
{
23
    public const INHERIT_ATTRIBUTES = [
24
        SalesChannelRequest::ATTRIBUTE_DOMAIN_LOCALE,
25
        SalesChannelRequest::ATTRIBUTE_DOMAIN_SNIPPET_SET_ID,
26
        SalesChannelRequest::ATTRIBUTE_DOMAIN_CURRENCY_ID,
27
        SalesChannelRequest::ATTRIBUTE_DOMAIN_ID,
28
        SalesChannelRequest::ATTRIBUTE_THEME_ID,
29
        SalesChannelRequest::ATTRIBUTE_THEME_NAME,
30
        SalesChannelRequest::ATTRIBUTE_THEME_BASE_NAME,
31
        PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT,
32
        PlatformRequest::ATTRIBUTE_CONTEXT_OBJECT,
33
    ];
34
35
    /**
36
     * @var KernelInterface
37
     */
38
    private $kernel;
39
40
    /**
41
     * @var RequestStack
42
     */
43
    private $requestStack;
44
45
    public function __construct(KernelInterface $kernel, RequestStack $requestStack)
46
    {
47
        $this->kernel = $kernel;
48
        $this->requestStack = $requestStack;
49
    }
50
51
    /**
52
     * @Route("/_proxy/store-api", name="frontend.store-api.proxy", defaults={"XmlHttpRequest"=true})
53
     */
54
    public function proxy(Request $request, SalesChannelContext $context): Response
55
    {
56
        $storeApiRequest = $this->setupStoreApiRequest($request, $context);
57
58
        return $this->wrapInStoreApiRoute($storeApiRequest, function () use ($storeApiRequest): Response {
59
            return $this->kernel->handle($storeApiRequest, HttpKernelInterface::SUB_REQUEST);
60
        });
61
    }
62
63
    private function setupStoreApiRequest(Request $request, SalesChannelContext $context): Request
64
    {
65
        if (!$request->query->has('path')) {
66
            throw new MissingRequestParameterException('path');
67
        }
68
69
        $url = parse_url($request->query->get('path'));
70
71
        if ($url === false) {
72
            throw new InvalidRequestParameterException('path');
73
        }
74
75
        $query = null;
76
        if (isset($url['query'])) {
77
            parse_str($url['query'], $query);
78
        }
79
80
        $server = array_merge($request->server->all(), ['REQUEST_URI' => $url['path'] ?? '']);
81
        $subRequest = $request->duplicate($query, null, [], null, null, $server);
82
83
        $subRequest->headers->set(PlatformRequest::HEADER_ACCESS_KEY, $context->getSalesChannel()->getAccessKey());
84
        $subRequest->headers->set(PlatformRequest::HEADER_CONTEXT_TOKEN, $context->getToken());
85
86
        $subRequest->attributes->set(PlatformRequest::ATTRIBUTE_OAUTH_CLIENT_ID, $context->getSalesChannel()->getAccessKey());
87
88
        foreach (self::INHERIT_ATTRIBUTES as $inheritAttribute) {
89
            if ($request->attributes->has($inheritAttribute)) {
90
                $subRequest->attributes->set($inheritAttribute, $request->attributes->get($inheritAttribute));
91
            }
92
        }
93
94
        $subRequest->attributes->set(SalesChannelRequest::ATTRIBUTE_IS_SALES_CHANNEL_REQUEST, true);
95
96
        if ($request->hasSession()) {
97
            $subRequest->setSession($request->getSession());
98
        }
99
100
        return $subRequest;
101
    }
102
103
    private function wrapInStoreApiRoute(Request $request, callable $call): Response
104
    {
105
        $requestStackBackup = $this->clearRequestStackWithBackup($this->requestStack);
106
        $this->requestStack->push($request);
107
108
        try {
109
            return $call();
110
        } finally {
111
            $this->restoreRequestStack($this->requestStack, $requestStackBackup);
112
        }
113
    }
114
115
    private function clearRequestStackWithBackup(RequestStack $requestStack): array
116
    {
117
        $requestStackBackup = [];
118
119
        while ($requestStack->getMasterRequest()) {
120
            $requestStackBackup[] = $requestStack->pop();
121
        }
122
123
        return $requestStackBackup;
124
    }
125
126
    private function restoreRequestStack(RequestStack $requestStack, array $requestStackBackup): void
127
    {
128
        $this->clearRequestStackWithBackup($requestStack);
129
130
        foreach ($requestStackBackup as $backedUpRequest) {
131
            $requestStack->push($backedUpRequest);
132
        }
133
    }
134
}
135