This repository has been archived by the owner on Jun 29, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 32
/
Copy pathBootstrap.php
350 lines (324 loc) · 10.3 KB
/
Bootstrap.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
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
<?php
/**
* {{The Plugin Name}}
*
* @package {{the-plugin-name}}
* @author {{author_name}} <{{author_email}}>
* @copyright {{author_copyright}}
* @license {{author_license}}
* @link {{author_url}}
*/
declare( strict_types = 1 );
namespace ThePluginName;
use ThePluginName\Common\Abstracts\Base;
use ThePluginName\Common\Traits\Requester;
use ThePluginName\Common\Utils\Errors;
use ThePluginName\Config\Classes;
use ThePluginName\Config\I18n;
use ThePluginName\Config\Requirements;
/**
* Bootstrap the plugin
*
* @since 1.0.0
*/
final class Bootstrap extends Base {
/**
* Determine what we're requesting
*
* @see Requester
*/
use Requester;
/**
* Used to debug the Bootstrap class; this will print a visualised array
* of the classes that are loaded with the total execution time if set true
*
* @var array
*/
public $bootstrap = [ 'debug' => false ];
/**
* List of class to init
*
* @var array : classes
*/
public $class_list = [];
/**
* Composer autoload file list
*
* @var Composer\Autoload\ClassLoader
*/
public $composer;
/**
* Requirements class object
*
* @var Requirements
*/
protected $requirements;
/**
* I18n class object
*
* @var I18n
*/
protected $i18n;
/**
* Bootstrap constructor that
* - Checks compatibility/plugin requirements
* - Defines the locale for this plugin for internationalization
* - Load the classes via Composer's class loader and initialize them on type of request
*
* @param \Composer\Autoload\ClassLoader $composer Composer autoload output.
* @throws \Exception
* @since 1.0.0
*/
public function __construct( $composer ) {
parent::__construct();
$this->startExecutionTimer();
$this->checkRequirements();
$this->setLocale();
$this->getClassLoader( $composer );
$this->loadClasses( Classes::get() );
$this->debugger();
}
/**
* Check plugin requirements
*
* @since 1.0.0
*/
public function checkRequirements() {
$set_timer = microtime( true );
$this->requirements = new Requirements();
$this->requirements->check();
$this->bootstrap['check_requirements'] = $this->stopExecutionTimer( $set_timer );
}
/**
* Define the locale for this plugin for internationalization.
*
* @since 1.0.0
*/
public function setLocale() {
$set_timer = microtime( true );
$this->i18n = new I18n();
$this->i18n->load();
$this->bootstrap['set_locale'] = $this->stopExecutionTimer( $set_timer );
}
/**
* Get the class loader from Composer
*
* @param $composer
* @since 1.0.0
*/
public function getClassLoader( $composer ) {
$this->composer = $composer;
}
/**
* Initialize the requested classes
*
* @param $classes : The loaded classes.
* @since 1.0.0
*/
public function loadClasses( $classes ) {
$set_timer = microtime( true );
foreach ( $classes as $class ) {
if ( isset( $class['on_request'] ) && is_array( $class['on_request'] )
) {
foreach ( $class['on_request'] as $on_request ) {
if ( ! $this->request( $on_request ) ) {
continue;
}
}
} elseif ( isset( $class['on_request'] ) && ! $this->request( $class['on_request'] )
) {
continue;
}
$this->getClasses( $class['init'] );
}
$this->initClasses();
$this->bootstrap['initialized_classes']['timer'] = $this->stopExecutionTimer( $set_timer, 'Total execution time of initialized classes' );
}
/**
* Init the classes
*
* @since 1.0.0
*/
public function initClasses() {
$this->class_list = \apply_filters( 'the_plugin_name_initialized_classes', $this->class_list );
foreach ( $this->class_list as $class ) {
try {
$set_timer = microtime( true );
// phpcs:ignore NeutronStandard.Functions.VariableFunctions.VariableFunction
$this->bootstrap['initialized_classes'][ $class ] = new $class();
$this->bootstrap['initialized_classes'][ $class ]->init();
$this->bootstrap['initialized_classes'][ $class ] = $this->stopExecutionTimer( $set_timer );
} catch ( \Throwable $err ) {
\do_action( 'the_plugin_name_class_initialize_failed', $err, $class );
Errors::wpDie(
sprintf( /* translators: %s: php class namespace */
__( 'Could not load class "%s". The "init" method is probably missing or try a `composer dumpautoload -o` to refresh the autoloader.',
'the-plugin-name-text-domain'
), $class
),
__( 'Plugin initialize failed', 'the-plugin-name-text-domain' ),
__FILE__, $err
);
}
}
}
/**
* Get classes based on the directory automatically using the Composer autoload
*
* @param string $namespace Class name to find.
* @return array Return the classes.
* @since 1.0.0
*/
public function getClasses( string $namespace ): array {
$namespace = $this->plugin->namespace() . '\\' . $namespace;
if ( is_object( $this->composer ) !== false ) {
$classmap = $this->composer->getClassMap();
// First we're going to try to load the classes via Composer's Autoload
// which will improve the performance. This is only possible if the Autoloader
// has been optimized.
if ( isset( $classmap[ $this->plugin->namespace() . '\\Bootstrap' ] ) ) {
if ( ! isset( $this->bootstrap['initialized_classes']['load_by'] ) ) {
$this->bootstrap['initialized_classes']['load_by'] = 'Autoloader';
}
$classes = array_keys( $classmap );
foreach ( $classes as $class ) {
if ( 0 !== strncmp( (string) $class, $namespace, strlen( $namespace ) ) ) {
continue;
}
$this->class_list[] = $class;
}
return $this->class_list;
}
}
// If the composer.json file is updated then Autoloader is not optimized and we
// can't load classes via the Autoloader. The `composer dumpautoload -o` command needs to
// to be called; in the mean time we're going to load the classes differently which will
// be a bit slower. The plugin needs to be optimized before production-release
// Errors::writeLog(
// [
// 'title' => __( '{{The Plugin Name}} classes are not being loaded by Composer\'s Autoloader' ),
// 'message' => __( 'Try a `composer dumpautoload -o` to optimize the autoloader that will improve the performance on autoloading itself.' )
// ]
//);
return $this->getByExtraction( $namespace );
}
/**
* Get classes by file extraction, will only run if autoload fails
*
* @param $namespace
* @return array
* @since 1.0.0
*/
public function getByExtraction( $namespace ): array {
if ( ! isset( $this->bootstrap['initialized_classes']['load_by'] ) ) {
$this->bootstrap['initialized_classes']['load_by'] = 'Extraction; Try a `composer dumpautoload -o` to optimize the autoloader.';
}
$find_all_classes = [];
foreach ( $this->filesFromThisDir() as $file ) {
$file_data = [
// phpcs:disable
// file_get_contents() is only discouraged by PHPCS for remote files
'tokens' => token_get_all( file_get_contents( $file->getRealPath() ) ),
// phpcs:enable
'namespace' => '',
];
$find_all_classes = array_merge( $find_all_classes, $this->extractClasses( $file_data ) );
}
$this->classBelongsTo( $find_all_classes, $namespace . '\\' );
return $this->class_list;
}
/**
* Extract class from file, will only run if autoload fails
*
* @param $file_data
* @param array $classes
* @return array
* @since 1.0.0
*/
public function extractClasses( $file_data, $classes = [] ): array {
for ( $index = 0; isset( $file_data['tokens'][ $index ] ); $index++ ) {
if ( ! isset( $file_data['tokens'][ $index ][0] ) ) {
continue;
}
if ( T_NAMESPACE === $file_data['tokens'][ $index ][0] ) {
$index += 2; // Skip namespace keyword and whitespace
while ( isset( $file_data['tokens'][ $index ] ) && is_array( $file_data['tokens'][ $index ] ) ) {
$file_data['namespace'] .= $file_data['tokens'][ $index++ ][1];
}
}
if ( T_CLASS === $file_data['tokens'][ $index ][0] && T_WHITESPACE === $file_data['tokens'][ $index + 1 ][0] && T_STRING === $file_data['tokens'][ $index + 2 ][0] ) {
$index += 2; // Skip class keyword and whitespace
// So it only works with 1 class per file (which should be psr-4 compliant)
$classes[] = $file_data['namespace'] . '\\' . $file_data['tokens'][ $index ][1];
break;
}
}
return $classes;
}
/**
* Get all files from current dir, will only run if autoload fails
*
* @return mixed
* @since 1.0.0
*/
public function filesFromThisDir(): \RegexIterator {
$files = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator( __DIR__ ) );
$files = new \RegexIterator( $files, '/\.php$/' );
return $files;
}
/**
* Checks if class belongs to namespace, will only run if autoload fails
*
* @param $classes
* @param $namespace
* @since 1.0.0
*/
public function classBelongsTo( $classes, $namespace ) {
foreach ( $classes as $class ) {
if ( strpos( $class, $namespace ) === 0 ) {
$this->class_list[] = $class;
}
}
}
/**
* Start the execution timer of the plugin
*
* @since 1.0.0
*/
public function startExecutionTimer() {
if ( $this->bootstrap['debug'] === true ) {
$this->bootstrap['execution_time']['start'] = microtime( true );
}
}
/**
* @param $timer
* @param string $tag
* @return string
* @since 1.0.0
*/
public function stopExecutionTimer( $timer, $tag = 'Execution time' ): string {
if ( $this->bootstrap['debug'] === true ) {
return 'Elapsed: ' . ( microtime( true ) - $this->bootstrap['execution_time']['start'] ) . ' | ' . $tag . ': ' . ( microtime( true ) - $timer );
} else {
return '';
}
}
/**
* Visual presentation of the classes that are loaded
*/
public function debugger() {
if ( $this->bootstrap['debug'] === true ) {
$this->bootstrap['execution_time'] =
'Total execution time in seconds: ' . ( microtime( true ) - $this->bootstrap['execution_time']['start'] );
add_action( 'shutdown', function () {
ini_set( 'highlight.comment', '#969896; font-style: italic' );
ini_set( 'highlight.default', '#FFFFFF' );
ini_set( 'highlight.html', '#D16568; font-size: 13px; padding: 0; display: block;' );
ini_set( 'highlight.keyword', '#7FA3BC; font-weight: bold; padding:0;' );
ini_set( 'highlight.string', '#F2C47E' );
$output = highlight_string( "<?php\n\n" . var_export( $this->bootstrap, true ), true );
echo "<div style=\"background-color: #1C1E21; padding:5px; position: fixed; z-index:9999; bottom:0;\">{$output}</div>";
} );
}
}
}