Completed
Push — 5.4 ( 7ab161...b78a54 )
by Jeroen
06:30 queued 13s
created

VersionChecker::__construct()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3.009

Importance

Changes 0
Metric Value
dl 0
loc 16
ccs 9
cts 10
cp 0.9
rs 9.7333
c 0
b 0
f 0
cc 3
nc 2
nop 3
crap 3.009
1
<?php
2
3
namespace Kunstmaan\AdminBundle\Helper\VersionCheck;
4
5
use Doctrine\Common\Cache\Cache;
6
use Exception;
7
use GuzzleHttp\Client;
8
use Kunstmaan\AdminBundle\Helper\VersionCheck\Exception\ParseException;
9
use Symfony\Component\DependencyInjection\ContainerInterface;
10
use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface;
11
use Symfony\Contracts\Translation\TranslatorInterface;
12
13
/**
14
 * Version checker
15
 */
16
class VersionChecker
17
{
18
    /**
19
     * @var ContainerInterface
20
     */
21
    private $container;
22
23
    /**
24
     * @var Cache
25
     */
26
    private $cache;
27
28
    /**
29
     * @var string
30
     */
31
    private $webserviceUrl;
32
33
    /**
34
     * @var int
35
     */
36
    private $cacheTimeframe;
37
38
    /**
39
     * @var bool
40
     */
41
    private $enabled;
42
43
    /**
44
     * @var Client
45
     */
46
    private $client;
47
48
    /**
49
     * @var TranslatorInterface|LegacyTranslatorInterface
50
     */
51
    private $translator;
52
53
    /**
54
     * Constructor
55
     *
56
     * @param ContainerInterface $container
57
     * @param Cache              $cache
58
     */
59 7
    public function __construct(ContainerInterface $container, Cache $cache, $translator)
60
    {
61 7
        $this->container = $container;
62 7
        $this->cache = $cache;
63
64
        // NEXT_MAJOR Add "Symfony\Contracts\Translation\TranslatorInterface" typehint when sf <4.4 support is removed.
65 7
        if (!$translator instanceof TranslatorInterface && !$translator instanceof LegacyTranslatorInterface) {
66
            throw new \InvalidArgumentException(sprintf('The "$translator" parameter should be instance of "%s" or "%s"', Symfony\Contracts\Translation\TranslatorInterface::class, LegacyTranslatorInterface::class));
67
        }
68
69 7
        $this->translator = $translator;
70
71 7
        $this->webserviceUrl = $this->container->getParameter('version_checker.url');
72 7
        $this->cacheTimeframe = $this->container->getParameter('version_checker.timeframe');
73 7
        $this->enabled = $this->container->getParameter('version_checker.enabled');
74 7
    }
75
76
    /**
77
     * Check that the version check is enabled.
78
     *
79
     * @return bool
80
     */
81 7
    public function isEnabled()
82
    {
83 7
        return $this->enabled;
84
    }
85
86
    /**
87
     * Check if we recently did a version check, if not do one now.
88
     *
89
     * @throws ParseException
90
     */
91 1
    public function periodicallyCheck()
92
    {
93 1
        if (!$this->isEnabled()) {
94
            return;
95
        }
96
97 1
        $data = $this->cache->fetch('version_check');
98 1
        if (!\is_array($data)) {
99
            $this->check();
100
        }
101 1
    }
102
103
    /**
104
     * Get the version details via webservice.
105
     *
106
     * @return mixed a list of bundles if available
107
     *
108
     * @throws ParseException
109
     */
110 5
    public function check()
111
    {
112 5
        if (!$this->isEnabled()) {
113
            return;
114
        }
115
116 5
        $host = $this->container->get('request_stack')->getCurrentRequest()->getHttpHost();
117 5
        $console = realpath($this->container->get('kernel')->getProjectDir().'/bin/console');
118 5
        $installed = filectime($console);
119 5
        $bundles = $this->parseComposer();
120 2
        $title = $this->container->getParameter('kunstmaan_admin.website_title');
121
122 2
        $jsonData = json_encode(array(
123 2
            'host' => $host,
124 2
            'installed' => $installed,
125 2
            'bundles' => $bundles,
126 2
            'project' => $this->translator->trans($title),
127
        ));
128
129
        try {
130 2
            $client = $this->getClient();
131 2
            $response = $client->post($this->webserviceUrl, ['body' => $jsonData]);
132 1
            $contents = $response->getBody()->getContents();
133 1
            $data = json_decode($contents);
134
135 1
            if (null === $data) {
136
                return false;
137
            }
138
139
            // Save the result in the cache to make sure we don't do the check too often
140 1
            $this->cache->save('version_check', $data, $this->cacheTimeframe);
141
142 1
            return $data;
143 1
        } catch (Exception $e) {
144
            // We did not receive valid json
145 1
            return false;
146
        }
147
    }
148
149
    /**
150
     * @return Client
151
     */
152 1
    public function getClient()
153
    {
154 1
        if (!$this->client) {
155 1
            $this->client = new Client(array('connect_timeout' => 3, 'timeout' => 1));
156
        }
157
158 1
        return $this->client;
159
    }
160
161
    /**
162
     * @param Client $client
163
     */
164
    public function setClient($client)
165
    {
166
        $this->client = $client;
167
    }
168
169
    /**
170
     * Returns the absolute path to the composer.lock file.
171
     *
172
     * @return string
173
     */
174
    protected function getLockPath()
175
    {
176
        $kernel = $this->container->get('kernel');
177
        $rootPath = $kernel->getProjectDir();
178
179
        return $rootPath.'/composer.lock';
180
    }
181
182
    /**
183
     * Returns a list of composer packages.
184
     *
185
     * @return array
186
     *
187
     * @throws ParseException
188
     */
189 4
    protected function getPackages()
190
    {
191 4
        $translator = $this->container->get('translator');
192 4
        $errorMessage = $translator->trans('settings.version.error_parsing_composer');
193
194 4
        $composerPath = $this->getLockPath();
195 4
        if (!file_exists($composerPath)) {
196 1
            throw new ParseException($translator->trans('settings.version.composer_lock_not_found'));
197
        }
198
199 3
        $json = file_get_contents($composerPath);
200 3
        $result = json_decode($json, true);
201
202 3
        if (json_last_error() !== JSON_ERROR_NONE) {
203 1
            throw new ParseException($errorMessage.' (#'.json_last_error().')');
204
        }
205
206 2
        if (\array_key_exists('packages', $result) && \is_array($result['packages'])) {
207 1
            return $result['packages'];
208
        }
209
210
        // No package list in JSON structure
211 1
        throw new ParseException($errorMessage);
212
    }
213
214
    /**
215
     * Parse the composer.lock file to get the currently used versions of the kunstmaan bundles.
216
     *
217
     * @return array
218
     *
219
     * @throws ParseException
220
     */
221 4
    protected function parseComposer()
222
    {
223 4
        $bundles = array();
224 4
        foreach ($this->getPackages() as $package) {
225 1
            if (!strncmp($package['name'], 'kunstmaan/', \strlen('kunstmaan/'))) {
226 1
                $bundles[] = array(
227 1
                    'name' => $package['name'],
228 1
                    'version' => $package['version'],
229 1
                    'reference' => $package['source']['reference'],
230
                );
231
            }
232
        }
233
234 1
        return $bundles;
235
    }
236
}
237