Completed
Push — master ( 794c22...18e3f4 )
by
unknown
22:38
created

buildWidgetContextsFromArray()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 7
rs 10
1
<?php
2
3
/*
4
 * This file is part of the TYPO3 CMS project.
5
 *
6
 * It is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License, either version 2
8
 * of the License, or any later version.
9
 *
10
 * For the full copyright and license information, please read the
11
 * LICENSE.txt file that was distributed with this source code.
12
 *
13
 * The TYPO3 project - inspiring people to share!
14
 */
15
16
namespace TYPO3\CMS\Fluid\Core\Widget;
17
18
use TYPO3\CMS\Core\SingletonInterface;
19
use TYPO3\CMS\Core\Utility\GeneralUtility;
20
use TYPO3\CMS\Extbase\Security\Cryptography\HashService;
21
use TYPO3\CMS\Fluid\Core\Widget\Exception\WidgetContextNotFoundException;
22
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
23
24
/**
25
 * This object stores the WidgetContext for the currently active widgets
26
 * of the current user, to make sure the WidgetContext is available in
27
 * Widget AJAX requests.
28
 *
29
 * @internal This class is only used internally by the widget framework.
30
 */
31
class AjaxWidgetContextHolder implements SingletonInterface
32
{
33
    /**
34
     * An array $ajaxWidgetIdentifier => $widgetContext
35
     * which stores the widget context.
36
     *
37
     * @var WidgetContext[]
38
     */
39
    protected $widgetContexts = [];
40
41
    /**
42
     * @var string
43
     */
44
    protected $widgetContextsStorageKey = 'TYPO3\\CMS\\Fluid\\Core\\Widget\\AjaxWidgetContextHolder_widgetContexts';
45
46
    /**
47
     * @var HashService
48
     */
49
    protected $hashService;
50
51
    /**
52
     * Constructor
53
     */
54
    public function __construct()
55
    {
56
        $this->hashService = GeneralUtility::makeInstance(HashService::class);
57
        $this->loadWidgetContexts();
58
    }
59
60
    /**
61
     * Loads the widget contexts from the TYPO3 user session
62
     */
63
    protected function loadWidgetContexts()
64
    {
65
        if (isset($GLOBALS['TSFE']) && $GLOBALS['TSFE'] instanceof TypoScriptFrontendController) {
66
            $this->widgetContexts = $this->unserializeWithHmac($GLOBALS['TSFE']->fe_user->getKey('ses', $this->widgetContextsStorageKey));
67
        } else {
68
            $this->widgetContexts = isset($GLOBALS['BE_USER']->uc[$this->widgetContextsStorageKey]) ? $this->unserializeWithHmac($GLOBALS['BE_USER']->uc[$this->widgetContextsStorageKey]) : [];
69
            $GLOBALS['BE_USER']->writeUC();
70
        }
71
    }
72
73
    /**
74
     * Get the widget context for the given $ajaxWidgetId.
75
     *
76
     * @param string $ajaxWidgetId
77
     * @return WidgetContext
78
     */
79
    public function get($ajaxWidgetId)
80
    {
81
        if (!isset($this->widgetContexts[$ajaxWidgetId])) {
82
            throw new WidgetContextNotFoundException('No widget context was found for the Ajax Widget Identifier "' . $ajaxWidgetId . '". This only happens if AJAX URIs are called without including the widget on a page.', 1284793775);
83
        }
84
        return $this->widgetContexts[$ajaxWidgetId];
85
    }
86
87
    /**
88
     * Stores the WidgetContext inside the Context, and sets the
89
     * AjaxWidgetIdentifier inside the Widget Context correctly.
90
     *
91
     * @param WidgetContext $widgetContext
92
     */
93
    public function store(WidgetContext $widgetContext)
94
    {
95
        $ajaxWidgetId = md5(uniqid(random_int(0, mt_getrandmax()), true));
96
        $widgetContext->setAjaxWidgetIdentifier($ajaxWidgetId);
97
        $this->widgetContexts[$ajaxWidgetId] = $widgetContext;
98
        $this->storeWidgetContexts();
99
    }
100
101
    /**
102
     * Persists the widget contexts in the TYPO3 user session
103
     */
104
    protected function storeWidgetContexts()
105
    {
106
        if (isset($GLOBALS['TSFE']) && $GLOBALS['TSFE'] instanceof TypoScriptFrontendController) {
107
            $GLOBALS['TSFE']->fe_user->setKey('ses', $this->widgetContextsStorageKey, $this->serializeWithHmac($this->widgetContexts));
108
            $GLOBALS['TSFE']->fe_user->storeSessionData();
109
        } else {
110
            $GLOBALS['BE_USER']->uc[$this->widgetContextsStorageKey] = $this->serializeWithHmac($this->widgetContexts);
111
            $GLOBALS['BE_USER']->writeUC();
112
        }
113
    }
114
115
    protected function serializeWithHmac(array $widgetContexts): string
116
    {
117
        $data = serialize($widgetContexts);
118
        return $this->hashService->appendHmac($data);
119
    }
120
121
    protected function unserializeWithHmac(?string $data): array
122
    {
123
        if ($data === null || $data === '') {
124
            return [];
125
        }
126
        try {
127
            $data = $this->hashService->validateAndStripHmac($data);
128
            // widget contexts literally can contain everything, that why using
129
            // HMAC-signed `unserialize()` is the only option here unless Extbase
130
            // structures have been refactored further
131
            $widgetContexts = unserialize($data);
132
        } finally {
133
            return is_array($widgetContexts ?? null) ? $widgetContexts : [];
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $widgetContexts does not seem to be defined for all execution paths leading up to this point.
Loading history...
134
        }
135
    }
136
}
137