Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 106
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
installer
0.00% covered (danger)
0.00%
0 / 106
0.00% covered (danger)
0.00%
0 / 6
1122
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 set_modules
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 set_iohandler
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 set_purge_cache_before
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 run
0.00% covered (danger)
0.00%
0 / 95
0.00% covered (danger)
0.00%
0 / 1
812
 recover_progress
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
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\install;
15
16use phpbb\cache\driver\driver_interface;
17use phpbb\di\ordered_service_collection;
18use phpbb\install\exception\cannot_build_container_exception;
19use phpbb\install\exception\installer_config_not_writable_exception;
20use phpbb\install\exception\jump_to_restart_point_exception;
21use phpbb\install\exception\resource_limit_reached_exception;
22use phpbb\install\exception\user_interaction_required_exception;
23use phpbb\install\helper\config;
24use phpbb\install\helper\container_factory;
25use phpbb\install\helper\iohandler\ajax_iohandler;
26use phpbb\install\helper\iohandler\cli_iohandler;
27use phpbb\install\helper\iohandler\iohandler_interface;
28use phpbb\path_helper;
29
30class installer
31{
32    /**
33     * @var driver_interface
34     */
35    protected $cache;
36
37    /**
38     * @var container_factory
39     */
40    protected $container_factory;
41
42    /**
43     * @var config
44     */
45    protected $install_config;
46
47    /**
48     * @var ordered_service_collection|null
49     */
50    protected $installer_modules;
51
52    /**
53     * @var iohandler_interface
54     */
55    protected $iohandler;
56
57    /**
58     * @var string
59     */
60    protected $web_root;
61
62    /**
63     * Stores the number of steps that a given module has
64     *
65     * @var array
66     */
67    protected $module_step_count;
68
69    /**
70     * @var bool
71     */
72    protected $purge_cache_before;
73
74    /**
75     * Constructor
76     *
77     * @param driver_interface    $cache            Cache service
78     * @param config            $config            Installer config handler
79     * @param path_helper        $path_helper    Path helper
80     * @param container_factory    $container        Container
81     */
82    public function __construct(driver_interface $cache, config $config, path_helper $path_helper, container_factory $container)
83    {
84        $this->cache                = $cache;
85        $this->install_config        = $config;
86        $this->container_factory    = $container;
87        $this->installer_modules    = null;
88        $this->web_root                = $path_helper->get_web_root_path();
89        $this->purge_cache_before    = false;
90    }
91
92    /**
93     * Sets modules to execute
94     *
95     * Note: The installer will run modules in the order they are set in
96     * the array.
97     *
98     * @param ordered_service_collection    $modules    Service collection of module service names
99     */
100    public function set_modules(ordered_service_collection $modules)
101    {
102        $this->installer_modules = $modules;
103    }
104
105    /**
106     * Sets input-output handler objects
107     *
108     * @param iohandler_interface    $iohandler
109     */
110    public function set_iohandler(iohandler_interface $iohandler)
111    {
112        $this->iohandler = $iohandler;
113    }
114
115    /**
116     * Sets whether to purge cache before the installation process
117     *
118     * @param bool    $purge_cache_before
119     */
120    public function set_purge_cache_before($purge_cache_before)
121    {
122        $this->purge_cache_before = $purge_cache_before;
123    }
124
125    /**
126     * Run phpBB installer
127     */
128    public function run()
129    {
130        if ($this->iohandler instanceof ajax_iohandler)
131        {
132            $this->iohandler->acquire_lock();
133        }
134
135        // Load install progress
136        $this->install_config->load_config();
137
138        if (!$this->install_config->get('cache_purged_before', false) && $this->purge_cache_before)
139        {
140            /** @var driver_interface $cache */
141            $cache = $this->container_factory->get('cache.driver');
142            $cache->purge();
143            $this->install_config->set('cache_purged_before', true);
144        }
145
146        // Recover install progress
147        $module_index = $this->recover_progress();
148
149        // Variable used to check if the install process have been finished
150        $install_finished    = false;
151        $fail_cleanup        = false;
152        $send_refresh        = false;
153
154        // We are installing something, so the introduction stage can go now...
155        $this->install_config->set_finished_navigation_stage(array('install', 0, 'introduction'));
156        $this->iohandler->set_finished_stage_menu(array('install', 0, 'introduction'));
157
158        if ($this->install_config->get_task_progress_count() === 0)
159        {
160            // Count all tasks in the current installer modules
161            $step_count = 0;
162
163            /** @var \phpbb\install\module_interface $module */
164            foreach ($this->installer_modules as $name => $module)
165            {
166                $module_step_count = $module->get_step_count();
167                $step_count += $module_step_count;
168                $this->module_step_count[$name] = $module_step_count;
169            }
170
171            // Set task count
172            $this->install_config->set_task_progress_count($step_count);
173        }
174
175        // Set up progress information
176        $this->iohandler->set_task_count(
177            $this->install_config->get_task_progress_count()
178        );
179
180        try
181        {
182            /** @psalm-suppress InvalidTemplateParam */
183            $iterator = $this->installer_modules->getIterator();
184
185            if ($module_index < $iterator->count())
186            {
187                $iterator->seek($module_index);
188            }
189            else
190            {
191                $iterator->seek($module_index - 1);
192                $iterator->next();
193            }
194
195            while ($iterator->valid())
196            {
197                $module    = $iterator->current();
198                $name    = $iterator->key();
199
200                // Check if module should be executed
201                if (!$module->is_essential() && !$module->check_requirements())
202                {
203                    $this->install_config->set_finished_navigation_stage($module->get_navigation_stage_path());
204                    $this->iohandler->set_finished_stage_menu($module->get_navigation_stage_path());
205
206                    $this->iohandler->add_log_message(array(
207                        'SKIP_MODULE',
208                        $name,
209                    ));
210                    $this->install_config->increment_current_task_progress($this->module_step_count[$name]);
211                }
212                else
213                {
214                    // Set the correct stage in the navigation bar
215                    $this->install_config->set_active_navigation_stage($module->get_navigation_stage_path());
216                    $this->iohandler->set_active_stage_menu($module->get_navigation_stage_path());
217
218                    $this->iohandler->send_response();
219
220                    $module->run();
221
222                    $this->install_config->set_finished_navigation_stage($module->get_navigation_stage_path());
223                    $this->iohandler->set_finished_stage_menu($module->get_navigation_stage_path());
224                }
225
226                $module_index++;
227                $iterator->next();
228
229                // Save progress
230                $this->install_config->set_active_module($name, $module_index);
231
232                if ($iterator->valid() && ($this->install_config->get_time_remaining() <= 0 || $this->install_config->get_memory_remaining() <= 0))
233                {
234                    throw new resource_limit_reached_exception();
235                }
236            }
237
238            // Installation finished
239            $install_finished = true;
240
241            if ($this->iohandler instanceof cli_iohandler)
242            {
243                $this->iohandler->add_success_message('INSTALLER_FINISHED');
244            }
245            else
246            {
247                // Start session if not installing and get user object
248                // to allow redirecting to ACP
249                $user = $this->container_factory->get('user');
250                if (!isset($module) || !($module instanceof \phpbb\install\module\install_finish\module))
251                {
252                    $auth = $this->container_factory->get('auth');
253
254                    $user->session_begin();
255                    $auth->acl($user->data);
256                    $user->setup();
257                }
258
259                $phpbb_root_path = $this->container_factory->get_parameter('core.root_path');
260
261                $acp_url = append_sid($phpbb_root_path . 'adm/index.php', 'i=acp_help_phpbb&mode=help_phpbb', true, $user->session_id);
262                $this->iohandler->add_success_message('INSTALLER_FINISHED', array(
263                    'ACP_LINK',
264                    $acp_url,
265                ));
266            }
267        }
268        catch (user_interaction_required_exception $e)
269        {
270            $this->iohandler->send_response(true);
271        }
272        catch (resource_limit_reached_exception $e)
273        {
274            $send_refresh = true;
275        }
276        catch (jump_to_restart_point_exception $e)
277        {
278            $this->install_config->jump_to_restart_point($e->get_restart_point_name());
279            $send_refresh = true;
280        }
281        catch (\Exception $e)
282        {
283            $this->iohandler->add_error_message($e->getMessage());
284            $this->iohandler->send_response(true);
285            $fail_cleanup = true;
286        }
287
288        if ($this->iohandler instanceof ajax_iohandler)
289        {
290            $this->iohandler->release_lock();
291        }
292
293        if ($install_finished)
294        {
295            // Send install finished message
296            $this->iohandler->set_progress('INSTALLER_FINISHED', $this->install_config->get_task_progress_count());
297            $this->iohandler->send_response(true);
298        }
299        else if ($send_refresh)
300        {
301            $this->iohandler->request_refresh();
302            $this->iohandler->send_response(true);
303        }
304
305        // Save install progress
306        try
307        {
308            if ($install_finished || $fail_cleanup)
309            {
310                $this->install_config->clean_up_config_file();
311                $this->cache->purge();
312
313                try
314                {
315                    /** @var driver_interface $cache */
316                    $cache = $this->container_factory->get('cache.driver');
317                    $cache->purge();
318                }
319                catch (cannot_build_container_exception $e)
320                {
321                    // Do not do anything, this just means there is no config.php yet
322                }
323            }
324            else
325            {
326                $this->install_config->save_config();
327            }
328        }
329        catch (installer_config_not_writable_exception $e)
330        {
331            // It is allowed to fail this test during requirements testing
332            $progress_data = $this->install_config->get_progress_data();
333
334            if ($progress_data['last_task_module_name'] !== 'installer.module.requirements_install')
335            {
336                $this->iohandler->add_error_message('INSTALLER_CONFIG_NOT_WRITABLE');
337            }
338        }
339    }
340
341    /**
342     * Recover install progress
343     *
344     * @return int    Index of the next installer module to execute
345     */
346    protected function recover_progress(): int
347    {
348        $progress_array = $this->install_config->get_progress_data();
349        return (int) $progress_array['last_task_module_index'];
350    }
351}