PluginProbe
Core Framework / trunk
Core Framework vtrunk
2.0.2 2.0.1 2.0.0 1.10.4 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.3.10 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.1.1 1.5.2 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.7.0 1.7.1 1.8.0 All 32 releases
core-framework / wp / Scaffold.php

Scaffold.php in Core Framework trunk, at wp/Scaffold.php

353 lines 9.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * CoreFramework
5 *
6 * @package CoreFramework
7 * @author Core Framework <hello@coreframework.com>
8 * @copyright 2023 Core Framework
9 * @license MIT
10 * @link https://coreframework.com
11 */
12
13 declare(strict_types=1);
14
15 namespace CoreFramework;
16
17 use Composer\Autoload\ClassLoader;
18 use CoreFramework\Common\Abstracts\Base;
19 use CoreFramework\Common\Traits\Requester;
20 use CoreFramework\Common\Utils\Errors;
21 use CoreFramework\Config\Classes;
22 use CoreFramework\Config\Requirements;
23
24 /**
25 * Scaffold the plugin
26 *
27 * @since 0.0.0
28 */
29 final class Scaffold extends Base {
30
31 /**
32 * Determine what we're requesting
33 *
34 * @see Requester
35 */
36 use Requester;
37
38 /**
39 * Used to debug the Scaffold class; this will print a visualised array
40 * of the classes that are loaded with the total execution time if set true
41 *
42 * @var array
43 */
44 public $scaffold = array( 'debug' => false );
45
46 /**
47 * List of class to init
48 *
49 * @var array : classes
50 */
51 public $class_list = array();
52
53 /**
54 * Composer autoload file list
55 *
56 * @var ClassLoader
57 */
58 public $composer;
59
60 /**
61 * Requirements class object
62 *
63 * @var Requirements
64 */
65 protected $requirements;
66
67 /**
68 * Scaffold constructor that
69 * - Checks compatibility/plugin requirements
70 * - Defines the locale for this plugin for internationalization
71 * - Load the classes via Composer's class loader and initialize them on type of request
72 *
73 * @param ClassLoader $composer Composer autoload output.
74 * @throws \Exception
75 * @since 0.0.0
76 */
77 public function __construct( $composer ) {
78 parent::__construct();
79 $this->startExecutionTimer();
80 $this->checkRequirements();
81 $this->getClassLoader( $composer );
82 $this->loadClasses( Classes::get() );
83 $this->debugger();
84 }
85
86 /**
87 * Check plugin requirements
88 *
89 * @since 0.0.0
90 */
91 public function checkRequirements(): void {
92 $set_timer = microtime( true );
93 $this->requirements = new Requirements();
94 $this->requirements->check();
95 $this->scaffold['check_requirements'] = $this->stopExecutionTimer( $set_timer );
96 }
97
98 /**
99 * Get the class loader from Composer
100 *
101 * @param $composer
102 * @since 0.0.0
103 */
104 public function getClassLoader( $composer ): void {
105 $this->composer = $composer;
106 }
107
108 /**
109 * Initialize the requested classes
110 *
111 * @param $classes : The loaded classes.
112 * @since 0.0.0
113 */
114 public function loadClasses( $classes ): void {
115 $set_timer = microtime( true );
116 foreach ( $classes as $class ) {
117 if ( isset( $class['on_request'] ) && is_array( $class['on_request'] ) ) {
118 foreach ( $class['on_request'] as $on_request ) {
119 if ( ! $this->request( $on_request ) ) {
120 continue;
121 }
122 }
123 } elseif ( isset( $class['on_request'] ) && ! $this->request( $class['on_request'] ) ) {
124 continue;
125 }
126
127 $this->getClasses( $class['init'] );
128 }
129
130 $this->initClasses();
131 $this->scaffold['initialized_classes']['timer'] = $this->stopExecutionTimer(
132 $set_timer,
133 'Total execution time of initialized classes'
134 );
135 }
136
137 /**
138 * Init the classes
139 *
140 * @since 0.0.0
141 */
142 public function initClasses(): void {
143 $this->class_list = \apply_filters( 'core_framework_initialized_classes', $this->class_list );
144 foreach ( $this->class_list as $class ) {
145 try {
146 $set_timer = microtime( true );
147 $this->scaffold['initialized_classes'][ $class ] = new $class();
148 $this->scaffold['initialized_classes'][ $class ]->init();
149 $this->scaffold['initialized_classes'][ $class ] = $this->stopExecutionTimer( $set_timer );
150 } catch ( \Throwable $err ) {
151 \do_action( 'core_framework_class_initialize_failed', $err, $class );
152 Errors::wpDie(
153 sprintf(
154 /* translators: %s: php class namespace */
155 \__(
156 'Could not load class "%s". The "init" method is probably missing or try a `composer dumpautoload -o` to refresh the autoloader.',
157 'core-framework'
158 ),
159 $class
160 ),
161 \__( 'Plugin initialize failed', 'core-framework' ),
162 __FILE__,
163 $err
164 );
165 }
166 }
167 }
168
169 /**
170 * Get classes based on the directory automatically using the Composer autoload
171 *
172 * @param string $namespace Class name to find.
173 * @return array Return the classes.
174 * @since 0.0.0
175 */
176 public function getClasses( string $namespace ): array {
177 $namespace = $this->plugin->namespace() . '\\' . $namespace;
178 if ( is_object( $this->composer ) !== false ) {
179 $classmap = $this->composer->getClassMap();
180
181 // First we're going to try to load the classes via Composer's Autoload
182 // which will improve the performance. This is only possible if the Autoloader
183 // has been optimized.
184 if ( isset( $classmap[ $this->plugin->namespace() . '\\Scaffold' ] ) ) {
185 if ( ! isset( $this->scaffold['initialized_classes']['load_by'] ) ) {
186 $this->scaffold['initialized_classes']['load_by'] = 'Autoloader';
187 }
188
189 $classes = array_keys( $classmap );
190 foreach ( $classes as $class ) {
191 if ( 0 !== strncmp( $class, $namespace, strlen( $namespace ) ) ) {
192 continue;
193 }
194
195 if ( ! empty( $exclude ) && $class === $namespace . $exclude ) {
196 continue;
197 }
198
199 $this->class_list[] = $class;
200 }
201
202 return $this->class_list;
203 }
204 }
205
206 // If the composer.json file is updated then Autoloader is not optimized and we
207 // can't load classes via the Autoloader. The `composer dumpautoload -o` command needs to
208 // to be called; in the mean time we're going to load the classes differently which will
209 // be a bit slower. The plugin needs to be optimized before production-release
210 Errors::writeLog(
211 array(
212 'title' => __( "Core Framework classes are not being loaded by Composer's Autoloader", 'core-framework' ),
213 'message' => __(
214 'Try a `composer dumpautoload -o` to optimize the autoloader that will improve the performance on autoloading itself.',
215 'core-framework'
216 ),
217 )
218 );
219 return $this->getByExtraction( $namespace );
220 }
221
222 /**
223 * Get classes by file extraction, will only run if autoload fails
224 *
225 * @param $namespace
226 * @since 0.0.0
227 */
228 public function getByExtraction( $namespace ): array {
229 if ( ! isset( $this->scaffold['initialized_classes']['load_by'] ) ) {
230 $this->scaffold['initialized_classes']['load_by'] =
231 'Extraction; Try a `composer dumpautoload -o` to optimize the autoloader.';
232 }
233
234 $find_all_classes = array();
235 foreach ( $this->filesFromThisDir() as $file ) {
236 $file_data = array(
237 // file_get_contents() is only discouraged by PHPCS for remote files
238 'tokens' => token_get_all( file_get_contents( $file->getRealPath() ) ),
239 'namespace' => '',
240 );
241 $find_all_classes = array_merge( $find_all_classes, $this->extractClasses( $file_data ) );
242 }
243
244 $this->classBelongsTo( $find_all_classes, $namespace . '\\' );
245 return $this->class_list;
246 }
247
248 /**
249 * Extract class from file, will only run if autoload fails
250 *
251 * @param $file_data
252 * @param array $classes
253 * @since 0.0.0
254 */
255 public function extractClasses( $file_data, $classes = array() ): array {
256 for ( $index = 0; isset( $file_data['tokens'][ $index ] ); $index++ ) {
257 if ( ! isset( $file_data['tokens'][ $index ][0] ) ) {
258 continue;
259 }
260
261 if ( T_NAMESPACE === $file_data['tokens'][ $index ][0] ) {
262 $index += 2; // Skip namespace keyword and whitespace
263 while ( isset( $file_data['tokens'][ $index ] ) && is_array( $file_data['tokens'][ $index ] ) ) {
264 $file_data['namespace'] .= $file_data['tokens'][ $index++ ][1];
265 }
266 }
267
268 if (
269 T_CLASS === $file_data['tokens'][ $index ][0] &&
270 T_WHITESPACE === $file_data['tokens'][ $index + 1 ][0] &&
271 T_STRING === $file_data['tokens'][ $index + 2 ][0]
272 ) {
273 $index += 2; // Skip class keyword and whitespace
274 // So it only works with 1 class per file (which should be psr-4 compliant)
275 $classes[] = $file_data['namespace'] . '\\' . $file_data['tokens'][ $index ][1];
276 break;
277 }
278 }
279
280 return $classes;
281 }
282
283 /**
284 * Get all files from current dir, will only run if autoload fails
285 *
286 * @return mixed
287 * @since 0.0.0
288 */
289 public function filesFromThisDir(): \RegexIterator {
290 $files = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator( __DIR__ ) );
291 $files = new \RegexIterator( $files, '/\.php$/' );
292 return $files;
293 }
294
295 /**
296 * Checks if class belongs to namespace, will only run if autoload fails
297 *
298 * @param $classes
299 * @param $namespace
300 * @since 0.0.0
301 */
302 public function classBelongsTo( $classes, $namespace ): void {
303 foreach ( $classes as $class ) {
304 if ( strpos( $class, (string) $namespace ) === 0 ) {
305 $this->class_list[] = $class;
306 }
307 }
308 }
309
310 /**
311 * Start the execution timer of the plugin
312 *
313 * @since 0.0.0
314 */
315 public function startExecutionTimer(): void {
316 if ( $this->scaffold['debug'] === true ) {
317 $this->scaffold['execution_time']['start'] = microtime( true );
318 }
319 }
320
321 /**
322 * @param $timer
323 * @param string $tag
324 * @since 0.0.0
325 */
326 public function stopExecutionTimer( $timer, $tag = 'Execution time' ): string {
327 if ( $this->scaffold['debug'] === true ) {
328 return 'Elapsed: ' .
329 ( microtime( true ) - $this->scaffold['execution_time']['start'] ) .
330 ' | ' .
331 $tag .
332 ': ' .
333 ( microtime( true ) - $timer );
334 } else {
335 return '';
336 }
337 }
338
339 /**
340 * Visual presentation of the classes that are loaded
341 */
342 public function debugger(): void {
343 if ( true !== $this->scaffold['debug'] ) {
344 return;
345 }
346
347 $this->scaffold['execution_time'] =
348 'Total execution time in seconds: ' . ( microtime( true ) - $this->scaffold['execution_time']['start'] );
349
350 \do_action( 'core_framework_debug_data', $this->scaffold );
351 }
352 }
353