-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcommand.php
275 lines (229 loc) · 8.48 KB
/
command.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
<?php
namespace YaronMiro\WpProfile;
if ( ! class_exists( 'WP_CLI' ) ) {
return;
}
use WP_CLI;
use WP_CLI_Command;
use Symfony\Component\Yaml\Parser;
// We only need to manually require `autoload.php` if the command is installed
// from a local path (Usually on development). The command is been required by a
// config file: e.g. '~/.wp-cli/config.yml' | 'wp-cli.local.yml' | 'wp-cli.yml'.
// The`autoload.php` is automatically required when installing this command from
// the package index list via the `wp package install`
if ( file_exists( __DIR__ . '/vendor/autoload.php' ) ) {
require_once( __DIR__ . '/vendor/autoload.php' );
} else {
WP_CLI::error( 'Wp-profile command: Please, run composer install first' );
}
/**
*
* Base class for all the profile install sub commands.
*
*/
abstract class Installer extends WP_CLI_Command {
/**
* @var string $file_path the config file path.
*/
protected $file_path = '';
/**
* @var array $allowed_file_types config file types.
*/
private $allowed_file_types = array( 'yml', 'yaml' );
/**
* @var array $data_structure the file data structure.
*/
protected $data_structure = array();
/**
* @var array $data the data from the file.
*/
protected $data = array();
/**
* @var object $data_parser the data parser object.
*/
private $data_parser = null;
/**
* Install a profile component via a unique command.
*
* Each sub-command process is unique. Therefore it is an abstract method
* that must be implemented when a sub-command extends this class.
*/
public abstract function execute_command();
/**
* Validate that the dependencies have been loaded.
* Assert that the data_structure property was declared correctly.
*/
public function __construct() {
$this->load_dependencies();
$this->assert_data_structure();
}
/**
* Basic mandatory operations for a command execution.
*/
public function __invoke( $args, $assoc_args ) {
// Process the file path and validate it's existence.
$this->process_file_path( reset( $args ) );
// Get the data from the file.
$this->parse_data_from_file();
// Validate the file data structure.
$this->validate_data_structure();
// Install.
$this->execute_command();
}
/**
* Load the dependencies.
*
* If the dependencies can't be loaded, then exits the script with an
* error message.
*/
protected function load_dependencies() {
// Load the file data parser.
if ( ! class_exists('Symfony\Component\Yaml\Parser') || ! $this->data_parser = new Parser() ) {
WP_CLI::error( 'Can\'t execute the command due to missing dependencies' );
}
}
/**
* Assert that the `data_structure` property was declared as expected.
*
* e.g array (
* 'required => array(),
* 'not_required => array(),
* );
*/
protected function assert_data_structure() {
// Mandatory keys.
$keys = array(
'allowed_properties',
'required_properties',
);
$variables = array('@class' => get_class( $this ));
foreach ( $keys as $key ) {
if ( ! isset( $this->data_structure[$key] ) ) {
$variables['@key'] = $key;
WP_CLI::error( strtr('\'@class:\' data_structure array is missing a key definition: \'@key\'', $variables) );
}
}
}
/**
* Validate that the data does not contain any unknown properties.
*
* Compare the file data properties against the 'allowed data properties'.
* In case the arrays do not match then exits the script with an
* error message.
*/
protected function validate_data_allowed_properties() {
// Compare arrays properties.
if ( $unauthorised_properties = array_diff( Utils::get_array_keys( $this->data ), $this->data_structure['allowed_properties'] ) ) {
$variables = array(
'@file' => $this->file_path,
'@unauthorised_properties' => implode( ', ', $unauthorised_properties ),
'@prop' => count( $unauthorised_properties ) > 1 ? 'properties' : 'property',
);
WP_CLI::error( strtr( 'Unauthorised @prop: \'@unauthorised_properties\' on file: \'@file\'.' , $variables ) );
}
}
/**
* Validating the file data structure.
*
*/
protected function validate_data_structure() {
// Validate that the data does not contain any unknown properties.
$this->validate_data_allowed_properties();
$this->validate_data_required_properties($this->data_structure['required_properties'], $this->data);
}
/**
* Validate that the required properties are declared as expected.
*
* @param array $required_data
* An array that defines the required properties.
* @param $data
* The target data to validate against.
* @param null $main_property
* The data may have some property that have sub-properties. For example:
* The main property is 'user' and it has sub-properties: 'name' & `password`
* + ------------- +
* + user: +
* + name: +
* + password: +
* + ------------- +
*/
protected function validate_data_required_properties($required_data, $data, $main_property = null) {
// Iterate over the data properties and validate that the properties were
// declared as expected.
$error_message = 'Property: \'@property\' is required on file: \'@file\'.';
foreach ($required_data as $key => $value) {
$variables = array( '@file' => $this->file_path );
// In case it's a simple key value per property.
if ( ! is_array( $value ) && empty( $data[$value] ) ) {
$variables['@property'] = $main_property ? ( $main_property . ': ' . $value ) : $value;
WP_CLI::error( strtr( $error_message, $variables ) );
}
// In case it's an array (property with sub-properties).
/**
* Check if numeric array or associative array
*
*/
if ( is_array( $value ) ) {
// In case the main property is missing.
if ( empty( $data[$key] ) ) {
$variables['@property'] = $key;
WP_CLI::error( strtr( $error_message, $variables) );
}
// Validate the sub properties.
$this->validate_data_required_properties($required_data[$key], $data[$key], $key);
}
}
}
/**
* Parse the data from a given file.
*/
protected function parse_data_from_file() {
$this->data = $this->data_parser->parse( ( file_get_contents( $this->file_path ) ) );
}
/**
* Process the file path from relative to absolute & validate it's integrity.
*
* @param $relative_file_path
* string
*
* Wrong file type or the file does not exists then,
* exits the script with an error message.
* File was found then, store the absolute file path.
*/
private function process_file_path( $relative_file_path ) {
// Get the absolute file path.
$absolute_file_path = getcwd() . '/' . $relative_file_path;
// In case the file type is incorrect.
$file_extension = strtolower( pathinfo( $absolute_file_path, PATHINFO_EXTENSION ) );
if ( ! in_array( $file_extension , $this->allowed_file_types ) ) {
// Message placeholders.
$variables = array(
'@file_type' => $file_extension,
'@allowed_file_types' => implode( ', ', $this->allowed_file_types ),
);
// Error message.
$message = 'File type: \'@file_type\' is incorrect. allowed file type are: \'@allowed_file_types\'';
WP_CLI::error( strtr( $message, $variables ) );
}
// In case the file does not exists.
if ( ! file_exists( $absolute_file_path ) ) {
WP_CLI::error( strtr( 'File: \'@file\' was not found!', array( '@file' => $absolute_file_path ) ) );
}
// Store the absolute path.
$this->file_path = $absolute_file_path;
}
}
// Info command.
WP_CLI::add_command( 'profile-install info', __NAMESPACE__ . '\\Info', array( 'when' => 'before_wp_load' ) );
// Database command.
WP_CLI::add_command( 'profile-install db', __NAMESPACE__ . '\\Database', array( 'when' => 'before_wp_load' ) );
// Plugins command.
WP_CLI::add_command( 'profile-install plugins', __NAMESPACE__ . '\\Plugins', array( 'when' => 'before_wp_load' ) );
// Themes command.
WP_CLI::add_command( 'profile-install themes', __NAMESPACE__ . '\\Themes' );
// Options command.
WP_CLI::add_command( 'profile-install options', __NAMESPACE__ . '\\Options' );
// Core command.
WP_CLI::add_command( 'profile-install core', __NAMESPACE__ . '\\Core', array( 'when' => 'before_wp_load' ) );
// Site command.
WP_CLI::add_command( 'profile-install site', __NAMESPACE__ . '\\Site', array( 'when' => 'before_wp_load' ) );