Completed
Push — master ( bc20b9...9515fd )
by Dmitry
02:03
created

Storage::writeLastId()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
ccs 0
cts 1
cp 0
cc 1
eloc 2
nc 1
nop 1
crap 2
1
<?php
2
3
/*
4
 * Asset Packagist
5
 *
6
 * @link      https://github.com/hiqdev/asset-packagist
7
 * @package   asset-packagist
8
 * @license   BSD-3-Clause
9
 * @copyright Copyright (c) 2016, HiQDev (http://hiqdev.com/)
10
 */
11
12
namespace hiqdev\assetpackagist\components;
13
14
use hiqdev\assetpackagist\helpers\Locker;
15
use hiqdev\assetpackagist\models\AssetPackage;
16
use Yii;
17
use yii\base\Component;
18
use yii\helpers\Json;
19
20
class Storage extends Component implements StorageInterface
21
{
22
    protected $_path;
23
    protected $_locker;
24
25 1
    /**
26
     * @inheritdoc
27 1
     */
28 1
    public function init()
29
    {
30
        $this->_path = Yii::getAlias('@storage', false);
31
    }
32
33
    protected function getLocker()
34
    {
35
        if ($this->_locker === null) {
36
            $this->_locker = Locker::getInstance($this->buildPath('lock')); // TODO: get rid of singleton
37
        }
38
39
        return $this->_locker;
40
    }
41
42
    /**
43
     * @inheritdoc
44
     */
45
    public function getNextId()
46
    {
47
        $this->getLocker()->lock();
48
        {
49
            $nextID = $this->readLastId() + 1;
50
            $this->writeLastId($nextID);
51
        }
52
        $this->getLocker()->release();
53
54
        return $nextID;
55
    }
56
57
    protected function readLastId()
58
    {
59
        $path = $this->getLastIdPath();
60
61
        return (file_exists($path) ? (int) file_get_contents($path) : 0) ?: 1000000;
62
    }
63
64
    protected function writeLastId($value)
65
    {
66
        file_put_contents($this->getLastIdPath(), $value);
67
    }
68
69
    protected function getLastIdPath()
70
    {
71
        return $this->buildPath('lastid');
72
    }
73
74
    /**
75
     * @inheritdoc
76
     */
77
    public function writePackage(AssetPackage $package)
78
    {
79
        $name = $package->getNormalName();
80
        $data = [
81
            'packages' => [
82
                $name => $package->getReleases(),
83
            ],
84
        ];
85
        $json = Json::encode($data);
86
        $hash = hash('sha256', $json);
87
        $path = $this->buildHashedPath($name, $hash);
88 View Code Duplication
        if (!file_exists($path)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
89
            $this->getLocker()->lock();
90
            {
91
                $this->mkdir(dirname($path));
92
                file_put_contents($path, $json);
93
                file_put_contents($this->buildHashedPath($name), $json);
94
                $this->writeProviderLatest($name, $hash);
95
            }
96
            $this->getLocker()->release();
97
        }
98
99
        return $hash;
100
    }
101
102
    /**
103
     * @inheritdoc
104
     */
105
    public function readPackage(AssetPackage $package)
106
    {
107
        $name = $package->getNormalName();
108
        $path = $this->buildHashedPath($name);
109
        if (!file_exists($path)) {
110
            return null;
111
        }
112
        $json = file_get_contents($path);
113
        $updateTime = filemtime($path);
114
        $hash = hash('sha256', $json);
115
        $data = Json::decode($json);
116
        $releases = isset($data['packages'][$name]) ? $data['packages'][$name] : [];
117
118
        return compact('hash', 'releases', 'updateTime');
119
    }
120
121
    protected function buildPath()
122
    {
123
        $args = func_get_args();
124
        array_unshift($args, $this->_path);
125
126
        return implode(DIRECTORY_SEPARATOR, $args);
127
    }
128
129
    protected function buildHashedPath($name, $hash = 'latest')
130
    {
131
        return $this->buildPath('p', $name, $hash . '.json');
132
    }
133
134
    protected function writeProviderLatest($name, $hash)
135
    {
136
        $latest_path = $this->buildHashedPath('provider-latest');
137
        if (file_exists($latest_path)) {
138
            $data = Json::decode(file_get_contents($latest_path) ?: '[]');
139
        }
140
        if (!isset($data) || !is_array($data)) {
141
            $data = [];
142
        }
143
        if (!isset($data['providers'])) {
144
            $data['providers'] = [];
145
        }
146
        $data['providers'][$name] = ['sha256' => $hash];
147
        $json = Json::encode($data);
148
        $hash = hash('sha256', $json);
149
        $path = $this->buildHashedPath('provider-latest', $hash);
150 View Code Duplication
        if (!file_exists($path)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
151
            $this->getLocker()->lock();
152
            {
153
                $this->mkdir(dirname($path));
154
                file_put_contents($path, $json);
155
                file_put_contents($latest_path, $json);
156
                $this->writePackagesJson($hash);
157
            }
158
            $this->getLocker()->release();
159
        }
160
161
        return $hash;
162
    }
163
164
    protected function writePackagesJson($hash)
165
    {
166
        $data = [
167
            'providers-url'     => '/p/%package%/%hash%.json',
168
            'provider-includes' => [
169
                'p/provider-latest/%hash%.json' => [
170
                    'sha256' => $hash,
171
                ],
172
            ],
173
        ];
174
        $this->getLocker()->lock();
175
        {
176
            file_put_contents($this->buildPath('packages.json'), Json::encode($data));
177
        }
178
        $this->getLocker()->release();
179
    }
180
181
    protected function mkdir($dir)
182
    {
183
        if (!file_exists($dir)) {
184
            mkdir($dir, 0777, true);
185
        }
186
    }
187
188
    public function readJson($path)
189
    {
190
        return Json::decode(file_get_contents($this->buildPath($path)));
191
    }
192
193
    protected function readPackagesJson()
194
    {
195
        $data = $this->readJson('packages.json');
196
197
        return $data['provider-includes'];
198
    }
199
200
    protected function readProvider($path)
201
    {
202
        $data = $this->readJson($path);
203
204
        return $data['providers'];
205
    }
206
207
    /**
208
     * @inheritdoc
209
     */
210
    public function listPackages()
211
    {
212
        $packages = [];
213
        $providers = $this->readPackagesJson();
214
        foreach ($providers as $path => $data) {
215
            $path = strtr($path, ['%hash%' => $data['sha256']]);
216
            $packages = array_merge($packages, $this->readProvider($path));
217
        }
218
219
        return $packages;
220
    }
221
}
222