Completed
Push — master ( d9951f...3fd094 )
by Dmitry
06:45
created

RemoteCartStorage::writeSession()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 2
1
<?php
2
3
namespace hipanel\modules\finance\cart\storage;
4
5
use hipanel\components\SettingsStorage;
6
use hipanel\helpers\StringHelper;
7
use Yii;
8
use yii\base\InvalidConfigException;
9
use yii\base\NotSupportedException;
10
use yii\caching\CacheInterface;
11
use yii\helpers\Json;
12
use yii\web\MultiFieldSession;
13
use yii\web\Session;
14
use yii\web\User;
15
16
/**
17
 * Class RemoteCartStorage
18
 *
19
 * @author Dmytro Naumenko <[email protected]>
20
 */
21
class RemoteCartStorage extends MultiFieldSession implements CartStorageInterface
22
{
23
    /**
24
     * @var string The cart name. Used to distinguish carts, if there are different carts stored.
25
     * E.g. site, panel and mobile-app cart.
26
     */
27
    public $sessionCartId;
28
29
    const CACHE_DURATION = 60 * 60; // 1 hour
30
    /**
31
     * @var array
32
     */
33
    protected $data = [];
34
    /**
35
     * @var array
36
     */
37
    protected $oldData = [];
38
    /**
39
     * @var SettingsStorage
40
     */
41
    private $settingsStorage;
42
    /**
43
     * @var CacheInterface
44
     */
45
    private $cache;
46
    /**
47
     * @var User
48
     */
49
    private $user;
50
51
    /**
52
     * @var Session
53
     */
54
    private $session;
55
56
    public function __construct(Session $session, User $user, SettingsStorage $settingsStorage, CacheInterface $cache, array $config = [])
57
    {
58
        $this->settingsStorage = $settingsStorage;
59
        $this->cache = $cache;
60
        $this->user = $user;
61
        $this->session = $session;
62
63
        parent::__construct($config);
64
    }
65
66
    public function init()
67
    {
68
        parent::init();
69
70
        if (empty($this->sessionCartId)) {
71
            throw new InvalidConfigException('Parameter "sessionCartId" must be set in RemoteCartStorage');
72
        }
73
    }
74
75
    protected function read()
76
    {
77
        try {
78
            $this->data = $this->cache->getOrSet($this->getCacheKey(), function () {
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->cache->getOrSet($..., self::CACHE_DURATION) of type * is incompatible with the declared type array of property $data.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
79
                $remoteCartData = $this->settingsStorage->getBounded($this->getStorageKey());
80
                $localCartData = $this->session[$this->sessionCartId] ?? null;
81
                if ($remoteCartData === [] && $localCartData === null) {
82
                    return $this->session;
83
                }
84
85
                return $this->mergedCartData($remoteCartData, $localCartData);
0 ignored issues
show
Documentation introduced by
$remoteCartData is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
86
            }, self::CACHE_DURATION);
87
        } catch (\Exception $exception) {
88
            Yii::error('Failed to read cart: ' . $exception->getMessage(), __METHOD__);
89
        }
90
    }
91
92
    /**
93
     * @param string $remoteData base64 encoded JSON of serialized remotely stored cart items.
94
     * @param string $localData local cart items array. Defaults to `null`, meaning no local data exists
95
     * @return array
96
     */
97
    private function mergedCartData($remoteData, $localData = null)
98
    {
99
        $decodedRemote = Json::decode(base64_decode($remoteData));
100
101
        $local = $localData ? unserialize($localData) : [];
102
        $remote = isset($decodedRemote[$this->sessionCartId])
103
            ? unserialize($decodedRemote[$this->sessionCartId])
104
            : [];
105
106
        return [$this->sessionCartId => serialize(array_merge($remote, $local))];
107
    }
108
109
    protected function getCacheKey()
110
    {
111
        return [__CLASS__, $this->user->getId(), session_id()];
112
    }
113
114
    protected function getStorageKey()
115
    {
116
        return StringHelper::basename(__CLASS__);
117
    }
118
119
    protected function write()
120
    {
121
        if ($this->data === $this->oldData) {
122
            return;
123
        }
124
125
        try {
126
            $this->cache->set($this->getCacheKey(), $this->data);
127
            $this->settingsStorage->setBounded($this->getStorageKey(), base64_encode(Json::encode($this->data)));
128
        } catch (\Exception $exception) {
129
            Yii::error('Failed to save cart: ' . $exception->getMessage(), __METHOD__);
130
        }
131
    }
132
133
    /**
134
     * @var bool
135
     */
136
    private $_isActive;
137
138
    public function getIsActive()
139
    {
140
        return $this->_isActive;
141
    }
142
143
    /** {@inheritdoc} */
144
    public function open()
145
    {
146
        if ($this->getIsActive()) {
147
            return;
148
        }
149
150
        $this->read();
151
        $this->oldData = $this->data;
152
        $this->_isActive = true;
153
        $this->registerShutdownFunction();
154
    }
155
156
    /** {@inheritdoc} */
157
    public function offsetExists($offset)
158
    {
159
        $this->open();
160
161
        return isset($this->data[$offset]);
162
    }
163
164
    /** {@inheritdoc} */
165
    public function offsetGet($offset)
166
    {
167
        $this->open();
168
169
        return $this->data[$offset] ?? null;
170
    }
171
172
    /** {@inheritdoc} */
173
    public function offsetSet($offset, $item)
174
    {
175
        $this->open();
176
177
        $this->data[$offset] = $item;
178
    }
179
180
    /** {@inheritdoc} */
181
    public function offsetUnset($offset)
182
    {
183
        $this->open();
184
        unset($this->data[$offset]);
185
    }
186
187
    /**
188
     * @throws NotSupportedException
189
     * @void
190
     */
191
    private function throwShouldNotBeCalledException()
192
    {
193
        throw new NotSupportedException('Remote cart storage extends yii\web\Session, but it is not a session actually. This method should be never called.');
194
    }
195
196
    /** {@inheritdoc} */
197
    public function regenerateID($deleteOldSession = false)
198
    {
199
        $this->throwShouldNotBeCalledException();
200
    }
201
202
    /** {@inheritdoc} */
203
    public function readSession($id)
204
    {
205
        return $this->throwShouldNotBeCalledException();
206
    }
207
208
    /** {@inheritdoc} */
209
    public function writeSession($id, $data)
210
    {
211
        return $this->throwShouldNotBeCalledException();
212
    }
213
214
    /** {@inheritdoc} */
215
    public function destroySession($id)
216
    {
217
        return $this->throwShouldNotBeCalledException();
218
    }
219
220
    /** {@inheritdoc} */
221
    public function gcSession($maxLifetime)
222
    {
223
        return $this->throwShouldNotBeCalledException();
224
    }
225
226
    private function registerShutdownFunction()
227
    {
228
        register_shutdown_function(\Closure::fromCallable([$this, 'write']));
229
    }
230
}
231