PluginProbe
Code Engine – PHP Snippets, AI Functions & Automation for WordPress / 0.3.1
Code Engine – PHP Snippets, AI Functions & Automation for WordPress v0.3.1
0.5.6 0.5.5 0.5.4 0.5.3 0.5.2 0.5.1 0.5.0 0.4.9 0.4.8 0.4.7 0.4.6 trunk 0.0.1 0.0.2 0.2.8 0.2.9 0.3.0 0.3.1 0.3.2 0.3.3 0.3.4 0.3.5 0.3.6 0.3.7 0.3.8 All 32 releases
code-engine / classes / core.php

core.php in Code Engine – PHP Snippets, AI Functions & Automation for WordPress 0.3.1, at classes/core.php

833 lines 22.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 require_once ( MWCODE_PATH . '/vendor/autoload.php' );
4 use PhpParser\ParserFactory;
5 use PhpParser\NodeDumper;
6 use PhpParser\Error;
7
8 class Meow_MWCODE_Core
9 {
10 public $admin = null;
11 public $snippet = null;
12 public $is_rest = false;
13 public $is_cli = false;
14 public $site_url = null;
15 public $mwcode = null;
16
17 private $option_name = 'mwcode_options';
18
19 public function __construct() {
20 global $mwcode;
21
22 $this->site_url = get_site_url();
23 $this->is_rest = MeowCommon_Helpers::is_rest();
24 $this->is_cli = defined( 'WP_CLI' ) && WP_CLI;
25
26 // Snippets
27 $snippet = new Meow_MWCODE_Modules_Snippet( $this );
28 $this->snippet = $snippet;
29
30 // Create API before plugins_loaded
31 $this->mwcode = new Meow_MWCODE_API( $this, $snippet );
32 $mwcode = $this->mwcode;
33
34 // Add the shortcode for the "content" snippets
35 add_shortcode( 'code-engine', [ $this, 'content_shortcode' ] );
36
37 add_action( 'plugins_loaded', array( $this, 'init' ) );
38 }
39
40 function init() {
41 // Part of the core, settings and stuff
42 $this->admin = new Meow_MWCODE_Admin( $this );
43
44 // Only for REST
45 if ( $this->is_rest ) {
46 new Meow_MWCODE_Rest( $this, $this->admin, $this->snippet );
47 }
48 }
49
50
51 /**
52 *
53 * Roles & Access Rights
54 *
55 */
56 #region Roles & Access Rights
57 public function can_access_settings() {
58 return apply_filters( 'mwcode_allow_setup', current_user_can( 'manage_options' ) );
59 }
60
61 public function can_access_features() {
62 return apply_filters( 'mwcode_allow_usage', current_user_can( 'administrator' ) );
63 }
64
65 public function check_rest_nonce( $request ) {
66 $nonce = $request->get_header( 'X-WP-Nonce' );
67 return wp_verify_nonce( $nonce, 'wp_rest' );
68 }
69 #endregion
70
71 #region Options
72
73 function get_option( $option, $default = null ) {
74 $options = $this->get_all_options();
75 return $options[$option] ?? $default;
76 }
77
78 function list_options() {
79 return [
80 //Safemode
81 "safe_mode_status" => "on", // on, off, whitelist
82 "safe_mode_whitelist" => [],
83
84 //LOGS
85 "server_debug_mode" => false,
86
87 //UI
88 "ui_show_preview" => true,
89
90 //AI
91 "ai_suggestions" => false,
92 "ai_engine_status"=> false,
93 "ai_engine_message" => "",
94
95 //API
96 "api_endpoint" => false,
97 "api_token" => md5( time() . rand() ),
98 ];
99 }
100
101 function get_all_options( ) {
102 $options = get_option( $this->option_name, $this->list_options( ) );
103 $options = $this->sanitize_options( $options );
104
105 return $options;
106 }
107
108 function update_options( $options ) {
109 $current_options = get_option($this->option_name);
110
111 if ($current_options === $options) {
112 // $this->log('💾 The options are already the expected value.');
113 } else {
114 if ( !update_option( $this->option_name, $options, false ) ) {
115 $this->log( '💾 There was an issue updating the options.' );
116 }
117 }
118
119 $options = $this->sanitize_options( $options );
120 return $options;
121 }
122
123 function update_option( $option, $value ) {
124 $options = $this->get_all_options();
125 $options[$option] = $value;
126 return $this->update_options( $options );
127 }
128
129 function reset_options() {
130 if ( $this->get_all_options() === $this->list_options() ) {
131 return true;
132 }
133 return $this->update_options( $this->list_options() );
134 }
135
136 // Validate and keep the options clean and logical.
137 function sanitize_options( $options ) {
138 $options_modified = false;
139
140 // Make sure safe mode whitelist is an array
141 if ( ! is_array( $options['safe_mode_whitelist'] ) ) {
142 $options['safe_mode_whitelist'] = explode( ",", $options['safe_mode_whitelist'] );
143 $options_modified = true;
144 }
145
146 // Update AI Engine status
147 $options_modified = $this->updateAIEngineStatus( $options ) || $options_modified;
148
149 // Disable AI related features if AI Engine is not available
150 if ( ! $options['ai_engine_status'] && $options['ai_suggestions'] !== false ) {
151 $options['ai_suggestions'] = false;
152 $options_modified = true;
153 }
154
155 if ( $options_modified ) {
156 update_option( $this->option_name, $options, false );
157 }
158
159 return $options;
160 }
161
162 private function updateAIEngineStatus( &$options ) {
163 global $mwai;
164
165 if ( is_null( $mwai ) || ! isset( $mwai ) ) {
166 $options['ai_engine_status'] = false;
167 $options['ai_engine_message'] = 'AI Engine is not available.';
168 return true;
169 }
170
171 try {
172 $status = $mwai->checkStatus();
173
174 if ( $options['ai_engine_status'] != true || $options['ai_engine_message'] != $status ) {
175 $options['ai_engine_status'] = true;
176 $options['ai_engine_message'] = $status;
177 return true;
178 }
179 } catch ( Exception $e ) {
180 if ( $options['ai_engine_status'] != false || $options['ai_engine_message'] != $e->getMessage() ) {
181 $options['ai_engine_status'] = false;
182 $options['ai_engine_message'] = $e->getMessage();
183 return true;
184 }
185 }
186
187 return false;
188 }
189
190 // #endregion
191
192 #region Snippets
193
194 /**
195 * Get snippet.
196 *
197 * @param $id
198 * @return mixed
199 */
200 protected function get_snippet( $id ) {
201 if ( $this->snippet === null ) {
202 $this->snippet = new Meow_MWCODE_Modules_Snippet( $this );
203 }
204
205 return $this->snippet->select_one( $id );
206 }
207
208 function add_snippet( $params ) {
209
210 $response = [
211 "snippet" => null,
212 "result" => false,
213 ];
214
215 $this->snippet->validate( $params );
216
217 $params = $this->snippet->formatParamsForDatabase( $params );
218 $result = $this->snippet->insert( $params );
219 $snippet = $this->snippet->select_one( $result );
220
221 if( $result ) {
222 $params['id'] = (string)$result;
223
224 $this->snippet->create_or_update_function_snippet( $params );
225 $this->snippet->create_or_update_interval_snippet( $params );
226
227 $this->snippet->get_function_snippets_data( $snippet );
228 }
229
230 $response['snippet'] = $snippet;
231 $response['result'] = $result;
232
233 return $response;
234 }
235
236 private function sanitize_arg( $name, $value, $type = null) {
237 $real_type = gettype( $value );
238
239 if ( $name[0] !== '$' ) { $name = '$' . $name; }
240
241 if ( $type == null ) {
242 $type = $real_type;
243 }
244
245 if ( $type != 'array' && !empty( $value ) && !is_numeric( $value ) && $value[0] !== '"' && $value[strlen( $value ) - 1] !== '"' ) {
246 $value = '"' . esc_sql( $value ) . '"';
247 }
248
249 if ( $type === 'array' && $real_type === 'string' ) {
250 // We got a string like this: "["a", "b", "c"]" or "[ 1, 2, 3 ]"
251 // We need to convert it to an array
252 $value = str_replace( '"', '', $value );
253 $value = str_replace( '[', '', $value );
254 $value = str_replace( ']', '', $value );
255 $value = explode( ',', $value );
256 $value = array_map( 'trim', $value );
257 }
258
259 if ( $type === 'array' ) {
260 $value = json_encode( $value );
261 $value = str_replace( '\\', '', $value );
262 }
263
264 return [ $name, $value ];
265 }
266
267 function run_non_fn_snippet( $id, $code = null, $test = false ) {
268 // Retrieve the snippet code from the provided code or via the snippet ID.
269 if ( $code ) {
270 $snippet = [ 'code' => $code ];
271 } else {
272 $snippet = $this->get_snippet( $id );
273 }
274
275 // Remove any PHP opening tag.
276 $snippet['code'] = preg_replace( '/<\?php/', '', $snippet['code'], 1 );
277
278
279 if ( $test ) {
280 $snippet['code'] = preg_replace( '/echo\s+(.+?);/s', 'echo $1 . "\n";', $snippet['code'] );
281 }
282
283 $error = null;
284 $output = null;
285
286 try {
287 ob_start();
288 eval( $snippet['code'] );
289 $output = ob_get_clean();
290 } catch ( Throwable $e ) {
291 $snippet_id = $id ? " ( ID: $id )" : '(Content Gutenberg Block)';
292 $this->log( '🔴 Error executing the snippet ' . $snippet_id . ' : ' . $e->getMessage() );
293 ob_clean();
294 } finally {
295 restore_error_handler();
296 }
297
298 // If in test mode, return output as an array of lines with an 'error' key if needed.
299 if ( $test ) {
300 $output = explode( "\n", trim( $output ) );
301 if ( $error !== null ) {
302 $output['error'] = $error->getMessage();
303 }
304 } else {
305 if ( $error !== null ) {
306 throw $error;
307 }
308 }
309
310 return $output;
311 }
312
313 function run_snippet( $id, $args = [], $params = [] )
314 {
315 // Static array to track defined functions
316 static $defined_functions = array();
317
318 if ( $id ) { // If there is an ID, we get the snippet, if not we get the data from the params
319 $snippet = $this->get_snippet( $id );
320 $this->snippet->get_function_snippets_data( $snippet ); // adds the function data to the snippet
321
322 $params = [ // We set the params according to the snippet we fetched
323 'test' => false, // If we pass an ID to the function, we are not testing the snippet
324 // 'test' => $params['test'] ?? false if needed we can still use ID and test at the same time (should not happen)
325 'code' => $snippet['code'],
326 'name' => $snippet['functionName'],
327 'args' => $snippet['functionArgs'],
328 'values' => $snippet['functionArgsDict'] // Contains the default values of the arguments
329 ];
330 }
331
332 // Sanitize all the arguments if the option is enabled
333 if ( $this->get_option( 'sanitize_arguments', true ) ) {
334
335 if ( $args ) {
336 foreach ( $args as $name => $value ) {
337 list( $sanitizedName, $sanitizedValue ) = $this->sanitize_arg( $name, $value );
338 unset( $args[$name] );
339
340 $args[$sanitizedName] = $sanitizedValue;
341 }
342 }
343
344 foreach ( $params['values'] as $name => $value ) {
345
346 if( array_key_exists( 'input', $value) ) {
347 list( $sanitizedInputName, $sanitizedInputValue ) = $this->sanitize_arg( $name, $value['input'], $value['type'] );
348 $params['values'][$sanitizedInputName]['input'] = $sanitizedInputValue;
349 }
350
351 if( array_key_exists( 'default', $value) ) {
352 list( $sanitizedDefaultValueName, $sanitizedDefaultValue ) = $this->sanitize_arg( $name, $value['default'], $value['type'] );
353 $params['values'][$sanitizedDefaultValueName]['default'] = $sanitizedDefaultValue;
354 }
355 }
356
357 }
358
359 // Make sure the function is existing and is the one in the snippet
360 if ( empty( $params['code'] ) ) {
361 throw new Exception( 'Code Engine: The snippet code appears to be empty.' );
362 }
363
364 if ( empty( $params['name'] ) || ! str_contains( $params['code'], $params['name'] ) ) {
365 throw new Exception( "Code Engine: Function name does not match. The name should be {$params['name']}." );
366 }
367
368 // Overwrite the default values with the provided ones
369 if ( $args ) {
370 foreach ( $args as $name => $value ) {
371 $params['values'][$name]['input'] = $value;
372 }
373
374 $this->log( '⚡ Arguments provided: ' . json_encode( $args ) );
375 }
376
377 // Check if the function has already been defined
378 if ( !in_array( $params['name'], $defined_functions ) ) {
379
380 // If not, proceed with modification and definition
381 if ( $params['test'] ) { // Make sure the echo statement uses a line break
382 $params['code'] = preg_replace( '/echo\s+(.+?);/s', 'echo $1 . "\n";', $params['code'] );
383 } else { // Remove all echo statements
384 $params['code'] = preg_replace( '/echo\s+(.+?);/s', '', $params['code'] );
385 }
386
387 $params['code'] = "if (!function_exists('{$params['name']}')) {\n" . $params['code'] . "\n}\n";
388
389 // Add the function name to the array to avoid redefinition
390 $defined_functions[] = $params['name'];
391 } else {
392 // If already defined, just prepare to call the function without redefining it
393 $params['code'] = '';
394 }
395
396 // Prepare the code to be executed
397 $params['code'] .= "\n\$mwcode_result = {$params['name']}(";
398 foreach ( $params['args'] as $index => $arg ) {
399 $value = 'null'; // In case the argument is not provided it will be null
400
401 if ( array_key_exists( $arg, $params['values'] ) ) { // Avoid warnings if the argument is not provided
402
403 // If the argument is provided, use it, if not use the default value
404 if ( !empty( $params['values'][$arg]['input'] ) ) {
405 $value = $params['values'][$arg]['input'];
406
407 } else if ( !empty( $params['values'][$arg]['default'] ) ) {
408 $value = $params['values'][$arg]['default'];
409 }
410 }
411
412 $params['code'] .= "{$value}";
413 if ( $index < count( $params['args'] ) - 1 ) {
414 $params['code'] .= ', ';
415 }
416 }
417 $params['code'] .= ");\necho print_r(\$mwcode_result, true);";
418
419 $error = null;
420 $output = null;
421
422 try {
423 ob_start();
424 eval( $params['code'] );
425 $output = ob_get_clean();
426
427 if ( $params['test'] ){
428 $output = explode( "\n", $output );
429 }
430
431 } catch ( Throwable $e ) {
432 //$this->log('Code Engine: Error executing the function: ' . $e->getMessage());
433 $error = new Exception(' Error executing the function, ' . $e->getMessage());
434
435 ob_clean();
436 } finally {
437 restore_error_handler();
438 }
439
440 if ( $error !== null ) {
441 if( $params['test'] ){
442 $output['error'] = $error->getMessage();
443 } else {
444 throw $error;
445 }
446 }
447
448 return $output;
449 }
450
451
452 function parse_snippet( $code, $new_snippet = false ){
453 $parser = ( new ParserFactory( ) )->createForNewestSupportedVersion( );
454
455 if( !$this->snippet ){
456 $this->snippet = new Meow_MWCODE_Modules_Snippet( $this );
457 }
458
459 // First we check the function names are unique
460 $fn = $this->snippet->sanitize_and_check_functions( $code, $new_snippet );
461 if ( ! $fn['is_valid'] ) {
462
463 $lint = [
464 'line' => 1,
465 'attributes' => $fn['attributes'][0],
466 'raw_message' => implode(', ', $fn['errors'][0]),
467 'message' => implode(', ', $fn['errors'][0]),
468 ];
469
470 return $lint;
471 }
472
473 try {
474 $stmts = $parser->parse( $code );
475 $result = $stmts;
476 } catch ( PhpParser\Error $e ) {
477
478 $lint = [
479 'line' => $e->getStartLine(),
480 'attributes' => $e->getAttributes(),
481 'raw_message' => $e->getRawMessage(),
482 'message' => $e->getMessage(),
483 ];
484
485 return $lint;
486 }
487
488 return null;
489 }
490
491 public function get_js_functions_to_push() {
492 $functions = $this->snippet->get_functions();
493 $js_functions = [];
494 foreach ( $functions as &$function ) {
495 if ( !isset( $function['target'] ) ) {
496 $function['target'] = 'php';
497 }
498 if ( $function['target'] == 'js' ) {
499 $js_functions[] = $function;
500 }
501 }
502 $snippets = [];
503 foreach ( $js_functions as $function ) {
504 $snippet = $this->snippet->select_one( $function['snippetId'] );
505 $snippet['function_info'] = $function; // Add function info to snippet
506 $snippets[] = $snippet;
507 }
508
509 return $this->generate_js_functions_code( $snippets );
510 }
511
512 function generate_js_functions_code ($snippets ) {
513 $code = "";
514 foreach ( $snippets as $snippet ) {
515 $function_code = $snippet['code'];
516 $function_info = $snippet['function_info'];
517
518 // Extract function name and arguments
519 preg_match( '/(?:const|let|var)?\s*(\w+)\s*=\s*\((.*?)\)\s*=>/', $function_code, $matches );
520 $function_name = $matches[1] ?? $function_info['name'];
521 $function_args = $matches[2] ?? '';
522
523 // Prepare default values
524 $default_args = [];
525 foreach ( $function_info['args'] as $arg ) {
526 if ( isset( $arg['default'] ) && $arg['default'] !== '' ) {
527 $default_args[$arg['name']] = $arg['default'];
528 }
529 }
530
531 // Modify function to use default values
532 if ( !empty( $default_args ) ) {
533 $new_args = explode( ',', $function_args );
534 foreach ( $new_args as &$arg ) {
535 $arg = trim( $arg );
536 if ( isset( $default_args[$arg] ) ) {
537 $arg .= " = " . json_encode( $default_args[$arg] );
538 }
539 }
540 $new_args_string = implode( ', ', $new_args );
541 $function_code = preg_replace(
542 '/(\w+)\s*=\s*\((.*?)\)\s*=>/',
543 "$1 = ($new_args_string) =>",
544 $function_code
545 );
546 }
547
548 $code .= $function_code . "\n\n";
549 }
550
551 return $code;
552 }
553
554
555 /**
556 * [STATIC] Execute active snippets.
557 *
558 * @return array
559 */
560 public function execute_active_snippets() {
561
562 $blocked = false;
563 $page = isset( $_GET["page"] ) ? sanitize_text_field( $_GET["page"] ) : null;
564 if ( $page === 'mwcode_settings' || !Meow_MWCODE_Core::is_white_listed_rest() ) {
565 $blocked = true;
566 }
567
568 if ( empty( $this->snippet ) ) {
569 $this->snippet = new Meow_MWCODE_Modules_Snippet( $this );
570 }
571
572 $ts = $this->get_option( 'thrown_snippet', null );
573 if ( !empty( $ts ) ) {
574 $this->log( "⚠️ Your snippet \"{$ts['name']}\" has thrown a fatal error last time, so we disabled it. Please check the logs for more information." );
575 $this->snippet->force_disable( $ts['id'] );
576 $this->update_option( 'thrown_snippet', null );
577 }
578
579 $scope = is_admin() ? [ 'backend', 'persistent' ] : [ 'frontend', 'persistent' ];
580 // Get all active snippets
581
582
583
584 $snippets = $this->snippet->select(
585 null, // offset
586 -1, // limit
587 [
588 [ 'accessor' => 'active', 'value' => 1 ],
589 [ 'accessor' => 'scope', 'value' => $scope ],
590 ], // filter
591 [ 'accessor' => 'priority', 'by' => 'DESC' ] // sort
592 )['data'];
593
594
595 if ( empty( $snippets ) ) {
596 return;
597 }
598
599 $snippets = array_map( function ( $snippet ) use ( $blocked ) {
600 $snippet['code'] = preg_replace( '/<\?php/', '', $snippet['code'], 1 );
601 $snippet['blocked'] = $blocked;
602
603 // If the snippet must be executed only in the frontend, we bypass the block
604 if ( !is_admin() && $snippet['scope'] === 'frontend' ) {
605 $snippet['blocked'] = false;
606 }
607
608 return $snippet;
609 }, $snippets );
610
611
612
613 return $snippets;
614 }
615
616
617 #endregion
618
619 #reion Shortcodes
620
621 function content_shortcode( $atts ) {
622
623 $atts = shortcode_atts( array(
624 'id' => null,
625 'target' => null,
626 'code' => null,
627 ), $atts );
628
629 $id = $atts['id'];
630 $target = $atts['target'];
631 $code = $atts['code'];
632
633 // If the ID is null, it means it comes from a Guttenberg block
634 $is_block = empty( $id ) && !empty( $code );
635 if( $is_block ){
636
637 // Because the code from Blocks are sanitized, we need to replace the &quot; with "
638 $code = str_replace( '&quot;', '"', $code );
639
640 if ( $target === 'js' ) {
641 $output = '<script>' . $code . '</script>';
642 }
643
644 if ( $target === 'php' ) {
645 $output = $this->run_non_fn_snippet( null, $code );
646 }
647
648 return $output;
649 }
650
651 // If the ID is not null, it means it comes from a shortcode
652 if ( empty( $id ) && empty( $code ) ) {
653 return '<b>Code Engine:</b> Please provide a snippet ID.';
654 }
655
656 $snippet = $this->get_snippet( $id );
657
658 if ( empty( $snippet ) ) {
659 return '<b>Code Engine:</b> The snippet does not exist.';
660 }
661
662 //Check if the snippet scope is either content_php or content_js
663 $is_content_php = $snippet['scope'] === 'content_php';
664 $is_content_js = $snippet['scope'] === 'content_js';
665
666 if ( !$is_content_php && !$is_content_js ) {
667 return '<b>Code Engine:</b> The snippet is not a content snippet.';
668 }
669
670 //Check if the snippet is active
671 if ( !$snippet['active'] ) {
672 return '<b>Code Engine:</b> The snippet is not active.';
673 }
674
675 $output = '<b>Code Engine:</b> No output.';
676
677 if ( $is_content_js ) {
678 $output = '<script>' . $snippet['code'] . '</script>';
679 }
680
681 if ( $is_content_php ) {
682 $output = $this->run_non_fn_snippet( $id );
683 }
684
685 return $output;
686 }
687
688 #endregion
689
690 #region Logs
691
692 function get_logs() {
693 $log_file_path = $this->get_logs_path();
694
695 if ( !file_exists( $log_file_path ) ) {
696 return "Empty log file.";
697 }
698
699 $content = file_get_contents( $log_file_path );
700 $lines = explode( "\n", $content );
701 $lines = array_filter( $lines );
702 $lines = array_reverse( $lines );
703 $content = implode( "\n", $lines );
704 return $content;
705 }
706
707 function clear_logs() {
708 $logPath = $this->get_logs_path();
709 if ( file_exists( $logPath ) ) {
710 unlink( $logPath );
711 }
712
713 $options = $this->get_all_options();
714 $options['logs_path'] = null;
715 $this->update_options( $options );
716 }
717
718 function get_logs_path() {
719 $uploads_dir = wp_upload_dir();
720 $uploads_dir_path = trailingslashit( $uploads_dir['basedir'] );
721
722 $path = $this->get_option( 'logs_path' );
723
724 if ( $path && file_exists( $path ) ) {
725 // make sure the path is legal (within the uploads directory with the MWCODE_PREFIX and log extension)
726 if ( strpos( $path, $uploads_dir_path ) !== 0 || strpos( $path, MWCODE_PREFIX ) === false || substr( $path, -4 ) !== '.log' ) {
727 $path = null;
728 } else {
729 return $path;
730 }
731 }
732
733 if ( !$path ) {
734 $path = $uploads_dir_path . MWCODE_PREFIX . "_" . $this->random_ascii_chars() . ".log";
735 if ( !file_exists( $path ) ) {
736 touch( $path );
737 }
738 $options = $this->get_all_options();
739 $options['logs_path'] = $path;
740 $this->update_options( $options );
741 }
742
743 return $path;
744 }
745
746 function log( $data = null ) {
747 if ( !$this->get_option( 'server_debug_mode', false ) ) { return false; }
748 $log_file_path = $this->get_logs_path();
749 $fh = @fopen( $log_file_path, 'a' );
750 if ( !$fh ) { return false; }
751 $date = date( "Y-m-d H:i:s" );
752 if ( is_null( $data ) ) {
753 fwrite( $fh, "\n" );
754 }
755 else {
756 fwrite( $fh, "$date: {$data}\n" );
757 //$this->log( "[MWCODE] $data" );
758 }
759 fclose( $fh );
760 return true;
761 }
762
763 private function random_ascii_chars( $length = 8 ) {
764 $characters = array_merge( range( 'A', 'Z' ), range( 'a', 'z' ), range( '0', '9' ) );
765 $characters_length = count( $characters );
766 $random_string = '';
767
768 for ( $i = 0; $i < $length; $i++ ) {
769 $random_string .= $characters[rand(0, $characters_length - 1)];
770 }
771
772 return $random_string;
773 }
774
775 #endregion
776
777 #region Helpers
778
779 /**
780 * Check if the request is from a white-listed REST route.
781 *
782 * @return bool
783 */
784 public static function is_white_listed_rest() {
785 $authorized = false;
786 $white_listed = array(
787 'mwai/v1',
788 'mwai-ui/v1',
789 'media-file-renamer/v1',
790 'media-cleaner/v1',
791 'wplr/v1',
792 'code-engine/v1',
793 'wp/v2',
794 'meow-gallery/v1',
795 );
796
797 $white_listed = apply_filters( 'meow_mwcode_white_listed_rest', $white_listed );
798
799 $route = isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : null;
800 $requested_route = null;
801
802 if ( $route ) {
803 $route_parts = explode( '/wp-json/', $route );
804
805 if ( isset( $route_parts[1] ) ) {
806 $requested_route = trim( $route_parts[1], '/' );
807 foreach ( $white_listed as $white_listed_route ) {
808 if ( strpos( $requested_route, $white_listed_route ) === 0 ) {
809 $authorized = true;
810 $authorized = apply_filters( 'meow_mwcode_white_listed_rest_authorized', $authorized, $requested_route );
811 return $authorized;
812 }
813 }
814 }
815
816 if ( is_admin() ) {
817 $authorized = true;
818
819 $authorized = apply_filters( 'meow_mwcode_white_listed_rest_authorized', $authorized, $requested_route );
820 return $authorized;
821 }
822
823
824 }
825
826 $authorized = apply_filters( 'meow_mwcode_white_listed_rest_authorized', $authorized, $requested_route );
827 return $authorized;
828 }
829
830 #endregion
831 }
832
833 ?>