Completed
Push — master ( 706141...565d34 )
by Alexey
01:33
created

Packagist::createPackageVersion()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 55
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 55
rs 8.7752
c 0
b 0
f 0
cc 5
eloc 38
nc 4
nop 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * This file is part of the wow-apps/symfony-packagist project
4
 * https://github.com/wow-apps/symfony-packagist
5
 *
6
 * (c) 2017 WoW-Apps
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace WowApps\PackagistBundle\Service;
13
14
use WowApps\PackagistBundle\DTO\DownloadsStat;
15
use WowApps\PackagistBundle\DTO\GitHubStat;
16
use WowApps\PackagistBundle\DTO\Package;
17
use WowApps\PackagistBundle\DTO\PackageAuthor;
18
use WowApps\PackagistBundle\DTO\PackageDependency;
19
use WowApps\PackagistBundle\DTO\PackageDist;
20
use WowApps\PackagistBundle\DTO\PackageMaintainer;
21
use WowApps\PackagistBundle\DTO\PackageSource;
22
use WowApps\PackagistBundle\DTO\PackageVersion;
23
use WowApps\PackagistBundle\Exception\PackagistException;
24
25
/**
26
 * Class Packagist
27
 *
28
 * @author Alexey Samara <[email protected]>
29
 * @package wow-apps/symfony-packagist
30
 */
31
class Packagist
32
{
33
    const API_URL = 'https://packagist.org';
34
    const API_URL_LIST = self::API_URL . '/packages/list.json';
35
    const API_URL_SEARCH = self::API_URL . '/search.json';
36
    const API_URL_PACKAGE = self::API_URL . '/packages/%s.json';
37
    const API_RESULT_PER_PAGE = 15;
38
    const SUPPORTED_PACKAGE_TYPES = [
39
        'symfony-bundle',
40
        'wordpress-plugin',
41
        'typo3-cms-extension',
42
        'library',
43
        'project',
44
        'metapackage',
45
        'composer-plugin'
46
    ];
47
48
    /** @var ApiProvider */
49
    private $apiProvider;
50
51
    /**
52
     * Packagist constructor.
53
     *
54
     * @param ApiProvider $apiProvider
55
     */
56
    public function __construct(ApiProvider $apiProvider)
57
    {
58
        $this->apiProvider = $apiProvider;
59
    }
60
61
    /**
62
     * @param string|null $query
63
     * @param string|null $tag
64
     * @param string|null $type
65
     * @return \ArrayObject|Package[]
66
     */
67
    public function searchPackages($query = null, $tag = null, $type = null): \ArrayObject
68
    {
69
        $result = new \ArrayObject();
70
        $currentPage = 1;
71
        $request = self::API_URL_SEARCH;
72
        $attributes = [];
73
74
        if (empty($query)) {
75
            throw new PackagistException(PackagistException::E_EMPTY_SEARCH_QUERY);
76
        }
77
78
        $attributes[] = 'q=' . urlencode($query);
79
80
        if (!empty($tag)) {
81
            $attributes[] = 'tags=' . urlencode($tag);
82
        }
83
84
        if (!empty($type)) {
85
            $this->validatePackageType($type);
86
            $attributes[] = 'type=' . urlencode($type);
87
        }
88
89
        $request .= '?' . implode('&', $attributes);
90
91
        $response = $this->apiProvider->getAPIResponse($request);
92
        $this->validateResponse($response, 'results');
93
94
        if ($response['total'] == 0) {
95
            return $result;
96
        }
97
98
        $this->fillSearchResultObject($result, $response['results']);
99
100
        $totalPages = ceil((int) $response['total'] / self::API_RESULT_PER_PAGE);
101
102
        if ($totalPages === 1) {
103
            return $result;
104
        }
105
106
        do {
107
            ++$currentPage;
108
            $response = $this->apiProvider->getAPIResponse($request . '&page=' . $currentPage);
109
            $this->validateResponse($response, 'results');
110
111
            if ($response['total'] == 0) {
112
                break;
113
            }
114
115
            $this->fillSearchResultObject($result, $response['results']);
116
117
        } while ($currentPage < $totalPages);
118
119
        return $result;
120
    }
121
122
    /**
123
     * @param \ArrayObject $searchResultObject
124
     * @param array $searchResult
125
     */
126
    private function fillSearchResultObject(\ArrayObject &$searchResultObject, array $searchResult)
127
    {
128
        if (!empty($searchResult)) {
129
            foreach ($searchResult as $item) {
130
                $package = new Package();
131
                $package
132
                    ->setName($item['name'])
133
                    ->setDescription($item['description'])
134
                    ->setUrl($item['url'])
135
                    ->setRepository($item['repository'])
136
                    ->setDownloads(
137
                        new DownloadsStat($item['downloads'])
138
                    )
139
                    ->setFavers($item['favers'])
140
                ;
141
142
                $searchResultObject->offsetSet($package->getName(), $package);
143
            }
144
        }
145
    }
146
147
    /**
148
     * @param string|null $vendor
149
     * @param string|null $type
150
     * @return array
151
     */
152
    public function getPackageList($vendor = null, $type = null): array
153
    {
154
        $request = self::API_URL_LIST;
155
        $attributes = [];
156
        if (!empty($vendor)) {
157
            $attributes[] = 'vendor=' . urlencode($vendor);
158
        }
159
        if (!empty($type)) {
160
            $this->validatePackageType($type);
161
            $attributes[] = 'type=' . urlencode($type);
162
        }
163
        if (!empty($attributes)) {
164
            $request .= '?' . implode('&', $attributes);
165
        }
166
        $response = $this->apiProvider->getAPIResponse($request);
167
        $this->validateResponse($response, 'packageNames');
168
169
        return $response['packageNames'];
170
    }
171
172
    /**
173
     * @param string $packageName
174
     * @return Package
175
     */
176
    public function getPackage(string $packageName): Package
177
    {
178
        $request = sprintf(self::API_URL_PACKAGE, trim($packageName));
179
        $response = $this->apiProvider->getAPIResponse($request);
180
        $this->validateResponse($response, 'package');
181
182
        return $this->createPackageObject($response);
183
    }
184
185
    /**
186
     * @param array $packageNames
187
     * @return \ArrayObject|Package[]
188
     */
189
    public function getPackages(array $packageNames): \ArrayObject
190
    {
191
        $packages = new \ArrayObject();
192
193
        foreach ($packageNames as $packageName) {
194
            $package = $this->getPackage($packageName);
195
            $packages->offsetSet($packageName, $package);
196
        }
197
198
        return $packages;
199
    }
200
201
    /**
202
     * @param array $packageArray
203
     * @return Package
204
     */
205
    private function createPackageObject(array $packageArray): Package
206
    {
207
        $package = new Package();
208
209
        $package
210
            ->setName($packageArray['package']['name'] ?? '')
211
            ->setDescription($packageArray['package']['description'] ?? '')
212
            ->setTime($packageArray['package']['time'] ?? '')
213
            ->setMaintainers(new \ArrayObject())
214
            ->setVersions(new \ArrayObject())
215
            ->setType($packageArray['package']['type'] ?? '')
216
            ->setRepository($packageArray['package']['repository'] ?? '')
217
            ->setGithub(
218
                new GitHubStat(
219
                    (int) $packageArray['package']['github_stars'] ?? 0,
220
                    (int) $packageArray['package']['github_watchers'] ?? 0,
221
                    (int) $packageArray['package']['github_forks'] ?? 0,
222
                    (int) $packageArray['package']['github_open_issues'] ?? 0
223
                )
224
            )
225
            ->setLanguage($packageArray['package']['language'] ?? '')
226
            ->setDependents((int) $packageArray['package']['dependents'] ?? 0)
227
            ->setSuggesters((int) $packageArray['package']['suggesters'] ?? 0)
228
            ->setDownloads(
229
                new DownloadsStat(
230
                    (int) $packageArray['package']['downloads']['total'] ?? 0,
231
                    (int) $packageArray['package']['downloads']['monthly'] ?? 0,
232
                    (int) $packageArray['package']['downloads']['daily'] ?? 0
233
                )
234
            )
235
            ->setFavers((int) $packageArray['package']['favers'] ?? 0)
236
        ;
237
238
        if (!empty($packageArray['package']['maintainers'])) {
239
            foreach ($packageArray['package']['maintainers'] as $maintainer) {
240
                $package->getMaintainers()->append(
241
                    new PackageMaintainer(
242
                        $maintainer['name'] ?? '',
243
                        $maintainer['avatar_url'] ?? ''
244
                    )
245
                );
246
            }
247
        }
248
249
        $this->addPackageVersions($package, $packageArray);
250
251
        $package->setVersion($this->identifyPackageVersion($package));
252
253
        return $package;
254
    }
255
256
    /**
257
     * @param Package $package
258
     * @param array $packageArray
259
     */
260
    private function addPackageVersions(Package &$package, array $packageArray)
261
    {
262
        if (!empty($packageArray['package']['versions'])) {
263
            foreach ($packageArray['package']['versions'] as $version) {
264
                if (empty($version['version'])) {
265
                    continue;
266
                }
267
268
                $packageVersion = $this->createPackageVersion($version);
269
270
                $package->getVersions()->offsetSet($packageVersion->getVersion(), $packageVersion);
271
            }
272
        }
273
    }
274
275
    /**
276
     * @param array $version
277
     * @return PackageVersion
278
     */
279
    private function createPackageVersion(array  $version): PackageVersion
280
    {
281
        $packageVersion = new PackageVersion();
282
283
        $packageVersion
284
            ->setName($version['name'] ?? '')
285
            ->setDescription($version['description'] ?? '')
286
            ->setKeywords($version['keywords'] ?? [])
287
            ->setHomepage($version['homepage'] ?? '')
288
            ->setVersion($version['version'])
289
            ->setVersionNormalized($version['version_normalized'] ?? '')
290
            ->setLicense($version['license'][0] ?? '')
291
            ->setAuthors(new \ArrayObject())
292
            ->setSource(
293
                new PackageSource(
294
                    $version['source']['type'] ?? '',
295
                    $version['source']['url'] ?? '',
296
                    $version['source']['reference'] ?? ''
297
                )
298
            )
299
            ->setDist(
300
                new PackageDist(
301
                    $version['dist']['type'] ?? '',
302
                    $version['dist']['url'] ?? '',
303
                    $version['dist']['reference'] ?? '',
304
                    $version['dist']['shasum'] ?? ''
305
                )
306
            )
307
            ->setType($version['type'] ?? '')
308
            ->setTime($version['time'] ?? '')
309
            ->setAutoload($version['autoload'] ?? [])
310
            ->setRequire(new \ArrayObject())
311
        ;
312
313
        if (!empty($version['authors'])) {
314
            foreach ($version['authors'] as $author) {
315
                $packageVersion->getAuthors()->append(
316
                    new PackageAuthor(
317
                        $author['name'] ?? '',
318
                        $author['email'] ?? '',
319
                        $author['homepage'] ?? '',
320
                        $author['role'] ?? ''
321
                    )
322
                );
323
            }
324
        }
325
326
        if (!empty($version['require'])) {
327
            foreach ($version['require'] as $name => $ver) {
328
                $packageVersion->getRequire()->append(new PackageDependency($name, $ver));
329
            }
330
        }
331
332
        return $packageVersion;
333
    }
334
335
    /**
336
     * @param Package $package
337
     * @return string
338
     */
339
    private function identifyPackageVersion(Package $package): string
340
    {
341
        if (empty($package->getVersions())) {
342
            return '';
343
        }
344
345
        $currentVersion = '';
346
347
        foreach ($package->getVersions() as $version) {
348
            if (preg_match('/(dev)/i', $version->getVersion())) {
349
                continue;
350
            }
351
352
            if (preg_match('/(master)/i', $version->getVersion())) {
353
                continue;
354
            }
355
356
            if ((int) str_replace('.', '', $version->getVersion()) < (int) str_replace('.', '', $currentVersion)) {
357
                continue;
358
            }
359
360
            $currentVersion = $version->getVersion();
361
        }
362
363
        return $currentVersion;
364
    }
365
366
    /**
367
     * @param string $packageType
368
     * @throws PackagistException
369
     */
370
    private function validatePackageType(string $packageType)
371
    {
372
        if (empty($packageType) || !in_array($packageType, self::SUPPORTED_PACKAGE_TYPES)) {
373
            throw new PackagistException(
374
                PackagistException::E_UNSUPPORTED_PACKAGE_TYPE
375
            );
376
        }
377
    }
378
379
    /**
380
     * @param array $response
381
     * @param string
382
     * @return void
383
     * @throws PackagistException
384
     */
385
    private function validateResponse(array $response, string $searchKey = null)
386
    {
387
        if (isset($response['status']) && $response['status'] == 'error') {
388
            throw new PackagistException($response['message'] ?? PackagistException::E_UNKNOWN);
389
        }
390
391
        if (!empty($searchKey) && !isset($response[$searchKey])) {
392
            throw new PackagistException(
393
                PackagistException::E_RESPONSE_WITHOUT_NEEDED_KEY,
394
                ['needed_key' => $searchKey]
395
            );
396
        }
397
    }
398
}
399