Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
43.64% covered (danger)
43.64%
48 / 110
42.86% covered (danger)
42.86%
3 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
check
43.64% covered (danger)
43.64%
48 / 110
42.86% covered (danger)
42.86%
3 / 7
203.08
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 configure
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 execute
50.00% covered (danger)
50.00%
8 / 16
0.00% covered (danger)
0.00%
0 / 1
13.12
 check_ext
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
42
 check_core
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
5
 check_all_ext
0.00% covered (danger)
0.00%
0 / 35
0.00% covered (danger)
0.00%
0 / 1
56
 display_versions
93.33% covered (success)
93.33%
14 / 15
0.00% covered (danger)
0.00%
0 / 1
4.00
1<?php
2/**
3*
4* This file is part of the phpBB Forum Software package.
5*
6* @copyright (c) phpBB Limited <https://www.phpbb.com>
7* @license GNU General Public License, version 2 (GPL-2.0)
8*
9* For full copyright and license information, please see
10* the docs/CREDITS.txt file.
11*
12*/
13
14namespace phpbb\console\command\update;
15
16use phpbb\config\config;
17use phpbb\exception\exception_interface;
18use phpbb\language\language;
19use phpbb\user;
20use Symfony\Component\Console\Command\Command as symfony_command;
21use Symfony\Component\Console\Input\InputInterface;
22use Symfony\Component\Console\Input\InputArgument;
23use Symfony\Component\Console\Input\InputOption;
24use Symfony\Component\Console\Output\OutputInterface;
25use Symfony\Component\Console\Style\SymfonyStyle;
26use Symfony\Component\DependencyInjection\ContainerInterface;
27
28class check extends \phpbb\console\command\command
29{
30    /** @var config */
31    protected $config;
32
33    /** @var ContainerInterface */
34    protected $phpbb_container;
35
36    /**
37     * @var language
38     */
39    private $language;
40
41    /**
42    * Construct method
43    */
44    public function __construct(user $user, config $config, ContainerInterface $phpbb_container, language $language)
45    {
46        $this->config = $config;
47        $this->phpbb_container = $phpbb_container;
48        $this->language = $language;
49
50        $this->language->add_lang(array('acp/common', 'acp/extensions'));
51
52        parent::__construct($user);
53    }
54
55    /**
56    * Configures the service.
57    *
58    * Sets the name and description of the command.
59    *
60    * @return void
61    */
62    protected function configure()
63    {
64        $this
65            ->setName('update:check')
66            ->setDescription($this->language->lang('CLI_DESCRIPTION_UPDATE_CHECK'))
67            ->addArgument('ext-name', InputArgument::OPTIONAL, $this->language->lang('CLI_DESCRIPTION_UPDATE_CHECK_ARGUMENT_1'))
68            ->addOption('stability', null, InputOption::VALUE_REQUIRED, $this->language->lang('CLI_DESCRIPTION_UPDATE_CHECK_OPTION_STABILITY'))
69            ->addOption('cache', 'c', InputOption::VALUE_NONE, $this->language->lang('CLI_DESCRIPTION_UPDATE_CHECK_OPTION_CACHE'))
70        ;
71    }
72
73    /**
74    * Executes the command.
75    *
76    * Checks if an update is available.
77    * If at least one is available, a message is printed and if verbose mode is set the list of possible updates is printed.
78    * If their is none, nothing is printed unless verbose mode is set.
79    *
80    * @param InputInterface $input Input stream, used to get the options.
81    * @param OutputInterface $output Output stream, used to print messages.
82    * @return int 0 if the board is up to date, 1 if it is not and 2 if an error occurred.
83    * @throws \RuntimeException
84    */
85    protected function execute(InputInterface $input, OutputInterface $output)
86    {
87        $io = new SymfonyStyle($input, $output);
88
89        $recheck = true;
90        if ($input->getOption('cache'))
91        {
92            $recheck = false;
93        }
94
95        $stability = null;
96        if ($input->getOption('stability'))
97        {
98            $stability = $input->getOption('stability');
99            if (!($stability == 'stable') && !($stability == 'unstable'))
100            {
101                $io->error($this->language->lang('CLI_ERROR_INVALID_STABILITY', $stability));
102                return symfony_command::FAILURE;
103            }
104        }
105
106        $ext_name = $input->getArgument('ext-name');
107        if ($ext_name != null)
108        {
109            if ($ext_name == 'all')
110            {
111                return $this->check_all_ext($io, $stability, $recheck);
112            }
113            else
114            {
115                return $this->check_ext($input, $io, $stability, $recheck, $ext_name);
116            }
117        }
118        else
119        {
120            return $this->check_core($input, $io, $stability, $recheck);
121        }
122    }
123
124    /**
125     * Check if a given extension is up to date
126     *
127     * @param InputInterface    $input        Input stream, used to get the options.
128     * @param SymfonyStyle        $io            IO handler, for formatted and unified IO
129     * @param string            $stability    Force a given stability
130     * @param bool                $recheck    Disallow the use of the cache
131     * @param string            $ext_name    The extension name
132     * @return int
133     */
134    protected function check_ext(InputInterface $input, SymfonyStyle $io, $stability, $recheck, $ext_name)
135    {
136        try
137        {
138            $ext_manager = $this->phpbb_container->get('ext.manager');
139            $md_manager = $ext_manager->create_extension_metadata_manager($ext_name);
140            $updates_available = $ext_manager->version_check($md_manager, $recheck, false, $stability);
141
142            $metadata = $md_manager->get_metadata('all');
143            if ($input->getOption('verbose'))
144            {
145                $io->title($md_manager->get_metadata('display-name'));
146
147                $io->note($this->language->lang('CURRENT_VERSION') . $this->language->lang('COLON') . ' ' . $metadata['version']);
148            }
149
150            if (!empty($updates_available))
151            {
152                if ($input->getOption('verbose'))
153                {
154                    $io->caution($this->language->lang('NOT_UP_TO_DATE', $metadata['name']));
155
156                    $this->display_versions($io, $updates_available);
157                }
158
159                return symfony_command::FAILURE;
160            }
161            else
162            {
163                if ($input->getOption('verbose'))
164                {
165                    $io->success($this->language->lang('UPDATE_NOT_NEEDED'));
166                }
167
168                return symfony_command::SUCCESS;
169            }
170        }
171        catch (\RuntimeException $e)
172        {
173            $io->error($this->language->lang('EXTENSION_NOT_INSTALLED', $ext_name));
174
175            return symfony_command::FAILURE;
176        }
177    }
178
179    /**
180     * Check if the core is up to date
181     *
182     * @param InputInterface    $input        Input stream, used to get the options.
183     * @param SymfonyStyle        $io            IO handler, for formatted and unified IO
184     * @param string            $stability    Force a given stability
185     * @param bool                $recheck    Disallow the use of the cache
186     * @return int
187     */
188    protected function check_core(InputInterface $input, SymfonyStyle $io, $stability, $recheck)
189    {
190        $version_helper = $this->phpbb_container->get('version_helper');
191        $version_helper->force_stability($stability);
192
193        $updates_available = $version_helper->get_suggested_updates($recheck);
194
195        if ($input->getOption('verbose'))
196        {
197            $io->title('phpBB core');
198
199            $io->note( $this->language->lang('CURRENT_VERSION') . $this->language->lang('COLON') . ' ' . $this->config['version']);
200        }
201
202        if (!empty($updates_available))
203        {
204            $io->caution($this->language->lang('UPDATE_NEEDED'));
205
206            if ($input->getOption('verbose'))
207            {
208                $this->display_versions($io, $updates_available);
209            }
210
211            return symfony_command::FAILURE;
212        }
213        else
214        {
215            if ($input->getOption('verbose'))
216            {
217                $io->success($this->language->lang('UPDATE_NOT_NEEDED'));
218            }
219
220            return symfony_command::SUCCESS;
221        }
222    }
223
224    /**
225    * Check if all the available extensions are up to date
226    *
227    * @param SymfonyStyle    $io            IO handler, for formatted and unified IO
228    * @param string            $stability    Stability specifier string
229    * @param bool            $recheck    Disallow the use of the cache
230    * @return int
231    */
232    protected function check_all_ext(SymfonyStyle $io, $stability, $recheck)
233    {
234        /** @var \phpbb\extension\manager $ext_manager */
235        $ext_manager = $this->phpbb_container->get('ext.manager');
236
237        $rows = [];
238
239        foreach ($ext_manager->all_available() as $ext_name => $ext_path)
240        {
241            $row = [];
242            $row[] = sprintf("<info>%s</info>", $ext_name);
243            $md_manager = $ext_manager->create_extension_metadata_manager($ext_name);
244            try
245            {
246                $metadata = $md_manager->get_metadata('all');
247                if (isset($metadata['extra']['version-check']))
248                {
249                    try {
250                        $updates_available = $ext_manager->version_check($md_manager, $recheck, false, $stability);
251                        if (!empty($updates_available))
252                        {
253                            $versions = array_map(function($entry)
254                            {
255                                return $entry['current'];
256                            }, $updates_available);
257
258                            $row[] = sprintf("<comment>%s</comment>", $metadata['version']);
259                            $row[] = implode(', ', $versions);
260                        }
261                        else
262                        {
263                            $row[] = sprintf("<info>%s</info>", $metadata['version']);
264                            $row[] = '';
265                        }
266                    } catch (\RuntimeException $e) {
267                        $row[] = $metadata['version'];
268                        $row[] = '';
269                    }
270                }
271                else
272                {
273                    $row[] = $metadata['version'];
274                    $row[] = '';
275                }
276            }
277            catch (exception_interface $e)
278            {
279                $exception_message = call_user_func_array(array($this->user, 'lang'), array_merge(array($e->getMessage()), $e->get_parameters()));
280                $row[] = '<error>' . $exception_message . '</error>';
281            }
282            catch (\RuntimeException $e)
283            {
284                $row[] = '<error>' . $e->getMessage() . '</error>';
285            }
286
287            $rows[] = $row;
288        }
289
290        $io->table([
291            $this->language->lang('EXTENSION_NAME'),
292            $this->language->lang('CURRENT_VERSION'),
293            $this->language->lang('LATEST_VERSION'),
294        ], $rows);
295
296        return symfony_command::SUCCESS;
297    }
298
299    /**
300    * Display the details of the available updates
301    *
302    * @param SymfonyStyle    $io                    IO handler, for formatted and unified IO
303    * @param array            $updates_available    The list of the available updates
304    */
305    protected function display_versions(SymfonyStyle $io, $updates_available)
306    {
307        $io->section($this->language->lang('UPDATES_AVAILABLE'));
308
309        $rows = [];
310        foreach ($updates_available as $version_data)
311        {
312            $row = ['', '', ''];
313            $row[0] = $version_data['current'];
314
315            if (isset($version_data['announcement']))
316            {
317                $row[1] = $version_data['announcement'];
318            }
319
320            if (isset($version_data['download']))
321            {
322                $row[2] = $version_data['download'];
323            }
324
325            $rows[] = $row;
326        }
327
328        $io->table([
329            $this->language->lang('VERSION'),
330            $this->language->lang('ANNOUNCEMENT_TOPIC'),
331            $this->language->lang('DOWNLOAD_LATEST'),
332        ], $rows);
333    }
334}