Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 61
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
Marketplace_Product_Installer
0.00% covered (danger)
0.00%
0 / 61
0.00% covered (danger)
0.00%
0 / 7
240
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 get_results
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 install
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0
 get_skip_plugins
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
12
 get_skip_themes
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
12
 install_dependencies
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
30
 run_command
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
6
1<?php // phpcs:ignore Squiz.Commenting.FileComment.Missing
2/**
3 * Abstract class that allows to install a marketplace product.
4 *
5 * @since 5.5.0
6 * @package WPCOM_Marketplace
7 */
8
9/**
10 * Abstract class that allows to install a marketplace product by generating and running the installation commands.
11 * This class implements methods that are common to all marketplace products.
12 */
13abstract class Marketplace_Product_Installer {
14
15    /**
16     * Command helper.
17     *
18     * @var Marketplace_Command_Helper
19     */
20    protected Marketplace_Command_Helper $command_helper;
21
22    /**
23     * Product software.
24     *
25     * @var Marketplace_Product_Software
26     */
27    protected Marketplace_Product_Software $product_software;
28
29    /**
30     * List of command results.
31     *
32     * @var array
33     */
34    protected array $results = array();
35
36    /**
37     * Marketplace_Product_Installer constructor.
38     *
39     * @param Marketplace_Command_Helper   $command_helper The command helper.
40     * @param Marketplace_Product_Software $product_software The product software.
41     */
42    public function __construct( Marketplace_Command_Helper $command_helper, Marketplace_Product_Software $product_software ) {
43        $this->command_helper   = $command_helper;
44        $this->product_software = $product_software;
45    }
46
47    /**
48     * Get the list of command results.
49     *
50     * @return array
51     */
52    public function get_results() {
53        return $this->results;
54    }
55
56    /**
57     * Install the product.
58     *
59     * This method generates and run the installation commands depending on the product type.
60     *
61     * @return WP_Error|bool
62     */
63    abstract public function install();
64
65    /**
66     * Get the list of plugins to skip when installing the product.
67     * To make sure that there are no 3rd party plugins that might interfere in the installation process,
68     * this method gets the list of installed plugins and filters out the ones that are dependencies of the product.
69     *
70     * @return array|WP_Error
71     */
72    protected function get_skip_plugins() {
73        if ( empty( $this->product_software->get_plugin_dependencies() ) ) {
74            return array();
75        }
76
77        $installed_plugins_names_command = $this->command_helper->get_installed_plugins_names_command();
78        $installed_plugins_names         = $this->run_command( $installed_plugins_names_command );
79        if ( is_wp_error( $installed_plugins_names ) ) {
80            return $installed_plugins_names;
81        }
82
83        $plugin_dependencies = $this->product_software->get_plugin_dependencies();
84        return array_filter(
85            explode( PHP_EOL, $installed_plugins_names->stdout ),
86            function ( $plugin_name ) use ( $plugin_dependencies ) {
87                return ! in_array( $plugin_name, $plugin_dependencies, true );
88            },
89        );
90    }
91
92    /**
93     * Get the list of themes to skip when installing the product.
94     * To make sure that there are no 3rd party themes that might interfere in the installation process,
95     * this method gets the list of installed themes and filters out the ones that are dependencies of the product.
96     *
97     * @return array|WP_Error
98     */
99    protected function get_skip_themes() {
100        if ( empty( $this->product_software->get_theme_dependencies() ) ) {
101            return array();
102        }
103
104        $installed_themes_names_command = $this->command_helper->get_installed_themes_names_command();
105        $installed_themes_names         = $this->run_command( $installed_themes_names_command );
106        if ( is_wp_error( $installed_themes_names ) ) {
107            return $installed_themes_names;
108        }
109
110        $theme_dependencies = $this->product_software->get_theme_dependencies();
111        return array_filter(
112            explode( PHP_EOL, $installed_themes_names->stdout ),
113            function ( $theme_name ) use ( $theme_dependencies ) {
114                return ! in_array( $theme_name, $theme_dependencies, true );
115            },
116        );
117    }
118
119    /**
120     * Install the product dependencies.
121     *
122     * This method installs the dependencies of the product.
123     */
124    protected function install_dependencies() {
125        $dependency_commands = $this->command_helper->generate_dependency_install_commands(
126            $this->product_software->get_plugin_dependencies(),
127            $this->product_software->get_theme_dependencies()
128        );
129
130        foreach ( $dependency_commands as $command ) {
131            $conditional_commands = explode( '||', $command );
132
133            foreach ( $conditional_commands as $conditional_command ) {
134                $dependency_installation = $this->run_command( $conditional_command );
135
136                // If the command failed, continue to the next command.
137                if ( is_wp_error( $dependency_installation ) ) {
138                    continue;
139                }
140
141                // If the command was successful, the remaining commands are not executed.
142                if ( $dependency_installation->return_code === 0 ) {
143                    continue 2;
144                }
145            }
146
147            // If all conditional commands failed, return an error.
148            return new WP_Error(
149                'plugin_installation_failed',
150                sprintf( '%s: Plugin installation failed. A dependency could not be installed ', $this->product_software->get_software_slug() ),
151                $this->results
152            );
153        }
154
155        return true;
156    }
157
158    /**
159     * Wrapper for WP_CLI::runcommand that logs the command and the result.
160     *
161     * @param string $command The command to run.
162     *
163     * @return WP_Error|object
164     */
165    protected function run_command( string $command ) {
166        $result = WP_CLI::runcommand(
167            $command,
168            array(
169                'return'     => 'all',
170                'parse'      => 'json',
171                'launch'     => true,
172                'exit_error' => false,
173            )
174        );
175
176        $this->results[] = $result;
177
178        if ( $result->return_code !== 0 ) {
179            return new WP_Error( 'command_failed', 'Command failed.', $result );
180        }
181
182        return $result;
183    }
184}