Passed
Push — master ( ce34db...aabc2a )
by
unknown
13:08
created

ComposerManifestProposalGenerator   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 81
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 16
eloc 33
c 1
b 0
f 0
dl 0
loc 81
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
B getComposerManifestProposal() 0 23 7
A isValidExtensionKey() 0 4 2
A getComposerManifestProposalFromTer() 0 20 6
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the TYPO3 CMS project.
7
 *
8
 * It is free software; you can redistribute it and/or modify it under
9
 * the terms of the GNU General Public License, either version 2
10
 * of the License, or any later version.
11
 *
12
 * For the full copyright and license information, please read the
13
 * LICENSE.txt file that was distributed with this source code.
14
 *
15
 * The TYPO3 project - inspiring people to share!
16
 */
17
18
namespace TYPO3\CMS\Extensionmanager\Service;
19
20
use GuzzleHttp\Exception\TransferException;
21
use TYPO3\CMS\Core\Core\Environment;
22
use TYPO3\CMS\Core\Http\RequestFactory;
23
use TYPO3\CMS\Core\Utility\GeneralUtility;
24
use TYPO3\CMS\Extensionmanager\Utility\EmConfUtility;
25
26
/**
27
 * Service for generating composer manifest proposals
28
 */
29
class ComposerManifestProposalGenerator
30
{
31
    private const TER_COMPOSER_ENDPOINT = 'https://extensions.typo3.org/composerize';
32
33
    /**
34
     * @var RequestFactory
35
     */
36
    protected $requestFactory;
37
38
    /**
39
     * @var EmConfUtility
40
     */
41
    protected $emConfUtility;
42
43
    public function __construct(RequestFactory $requestFactory, EmConfUtility $emConfUtility)
44
    {
45
        $this->requestFactory = $requestFactory;
46
        $this->emConfUtility = $emConfUtility;
47
    }
48
49
    /**
50
     * Return the generated composer manifest content
51
     *
52
     * @param string $extensionKey
53
     * @return string
54
     */
55
    public function getComposerManifestProposal(string $extensionKey): string
56
    {
57
        if (!$this->isValidExtensionKey($extensionKey)) {
58
            throw new \InvalidArgumentException('Extension key ' . $extensionKey . ' is not valid.', 1619446379);
59
        }
60
61
        $composerManifestPath = Environment::getExtensionsPath() . '/' . $extensionKey . '/composer.json';
62
63
        if (file_exists($composerManifestPath)) {
64
            $composerManifest = json_decode((file_get_contents($composerManifestPath) ?: ''), true) ?? [];
65
            if (!is_array($composerManifest) || $composerManifest === []) {
66
                return '';
67
            }
68
            $composerManifest['extra']['typo3/cms']['extension-key'] = $extensionKey;
69
        } else {
70
            try {
71
                $composerManifest = $this->getComposerManifestProposalFromTer($extensionKey);
72
            } catch (TransferException $e) {
73
                return '';
74
            }
75
        }
76
77
        return json_encode($composerManifest, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
78
    }
79
80
    /**
81
     * Calls the TER API endpoint by providing the extensions' ext_emconf
82
     * to receive a composer manifest proposal with resolved dependencies.
83
     */
84
    protected function getComposerManifestProposalFromTer(string $extensionKey): array
85
    {
86
        $emConf = $this->emConfUtility->includeEmConf($extensionKey, Environment::getExtensionsPath() . '/' . $extensionKey);
87
88
        if (!is_array($emConf) || $emConf === []) {
89
            return [];
90
        }
91
92
        $response = $this->requestFactory->request(
93
            self::TER_COMPOSER_ENDPOINT . '/' . $extensionKey,
94
            'post',
95
            ['json' => [$emConf]]
96
        );
97
98
        if ($response->getStatusCode() !== 200 || ($responseContent = $response->getBody()->getContents()) === '') {
99
            return [];
100
        }
101
102
        $composerManifest = json_decode($responseContent, true) ?? [];
103
        return is_array($composerManifest) ? $composerManifest : [];
104
    }
105
106
    protected function isValidExtensionKey(string $extensionKey): bool
107
    {
108
        return preg_match('/^[0-9a-z._\-]+$/i', $extensionKey)
109
            && GeneralUtility::isAllowedAbsPath(Environment::getExtensionsPath() . '/' . $extensionKey);
110
    }
111
}
112