PluginProbe
Code Engine – PHP Snippets, AI Functions & Automation for WordPress / 0.5.3
Code Engine – PHP Snippets, AI Functions & Automation for WordPress v0.5.3
0.5.7 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 All 33 releases
← All changes | classes/core.php +55 -51 trunk0.5.3 View file →
@@ -362,17 +362,35 @@
362 362 'values' => $snippet['functionArgsDict'] // Contains the default values of the arguments
363 363 ];
364 364 }
365 365
366 - // Arguments used to be sanitized into PHP-literal strings here (quoting,
367 - // esc_sql, var_export) so they could be concatenated into a string of PHP and
368 - // eval-ed. That is gone: the function is now called with call_user_func_array
369 - // (see below), so values are passed as data and need no literal-formatting.
370 - // The old formatting also prefixed argument keys with "$" via sanitize_arg,
371 - // which stored the provided value under "$name" while the call read "name", so
372 - // provided arguments never reached the function. Passing the raw values through
373 - // fixes both issues at once.
366 + // Sanitize all the arguments if the option is enabled
367 + if ( $this->get_option( 'sanitize_arguments', true ) ) {
374 368
369 + if ( $args ) {
370 + foreach ( $args as $name => $value ) {
371 + list( $sanitizedName, $sanitizedValue ) = $this->sanitize_arg( $name, $value );
372 + unset( $args[$name] );
373 +
374 + $args[$sanitizedName] = $sanitizedValue;
375 + }
376 + }
377 +
378 + foreach ( $params['values'] as $name => $value ) {
379 +
380 + if( array_key_exists( 'input', $value) ) {
381 + list( $sanitizedInputName, $sanitizedInputValue ) = $this->sanitize_arg( $name, $value['input'], $value['type'] );
382 + $params['values'][$sanitizedInputName]['input'] = $sanitizedInputValue;
383 + }
384 +
385 + if( array_key_exists( 'default', $value) ) {
386 + list( $sanitizedDefaultValueName, $sanitizedDefaultValue ) = $this->sanitize_arg( $name, $value['default'], $value['type'] );
387 + $params['values'][$sanitizedDefaultValueName]['default'] = $sanitizedDefaultValue;
388 + }
389 + }
390 +
391 + }
392 +
375 393 // Make sure the function is existing and is the one in the snippet
376 394 if ( empty( $params['code'] ) ) {
377 395 throw new Exception( 'Code Engine: The snippet code appears to be empty.' );
378 396 }
@@ -380,17 +398,12 @@
380 398 if ( empty( $params['name'] ) || ! str_contains( $params['code'], $params['name'] ) ) {
381 399 throw new Exception( "Code Engine: Function name does not match. The name should be {$params['name']}." );
382 400 }
383 401
384 - // Collect the provided values, keyed by their normalized (dollar-less) name.
385 - // Incoming keys come from the AI/MCP schema, where register_function_tools()
386 - // strips a leading "$" from the declared name. The stored arg names can still
387 - // carry the "$", so we normalize both sides before matching below. Without this
388 - // a value provided as "style" never binds to an argument declared "$style".
389 - $provided = [];
402 + // Overwrite the default values with the provided ones
390 403 if ( $args ) {
391 404 foreach ( $args as $name => $value ) {
392 - $provided[ ltrim( $name, '$' ) ] = $value;
405 + $params['values'][$name]['input'] = $value;
393 406 }
394 407
395 408 $this->log( '⚡ Arguments provided: ' . json_encode( $args ) );
396 409 }
@@ -423,58 +436,49 @@
423 436 // If already defined, just prepare to call the function without redefining it
424 437 $params['code'] = '';
425 438 }
426 439
427 - // Resolve the arguments as REAL PHP values, in the function's declared order.
428 - // The previous version concatenated each value into a string of PHP and eval-ed
429 - // the call, which broke on any string or edge-case value with a parse error
430 - // ("syntax error, unexpected token ')'"). call_user_func_array passes them as
431 - // data, so no value can ever corrupt the call syntax.
432 - $callArgs = [];
433 - foreach ( $params['args'] as $arg ) {
434 - $key = ltrim( $arg, '$' ); // Match the normalized name the caller sent.
435 - $value = null; // Not provided and no default -> null.
436 - // array_key_exists, not !empty: a legitimately provided 0, "0", "" or false
437 - // must reach the function instead of silently falling back to the default.
438 - if ( array_key_exists( $key, $provided ) ) {
439 - $value = $provided[ $key ];
440 - } else if ( isset( $params['values'][$arg]['default'] ) && $params['values'][$arg]['default'] !== '' ) {
441 - $value = $params['values'][$arg]['default'];
440 + // Prepare the code to be executed
441 + $params['code'] .= "\n\$mwcode_result = {$params['name']}(";
442 + foreach ( $params['args'] as $index => $arg ) {
443 + $value = 'null'; // In case the argument is not provided it will be null
444 +
445 + if ( array_key_exists( $arg, $params['values'] ) ) { // Avoid warnings if the argument is not provided
446 +
447 + // If the argument is provided, use it, if not use the default value
448 + if ( !empty( $params['values'][$arg]['input'] ) ) {
449 + $value = $params['values'][$arg]['input'];
450 +
451 + } else if ( !empty( $params['values'][$arg]['default'] ) ) {
452 + $value = $params['values'][$arg]['default'];
453 + }
442 454 }
443 - // An array-typed argument can arrive as a string like "[1, 2, 3]"; turn it
444 - // into a real array so the function receives what its signature expects.
445 - if ( ( $params['values'][$arg]['type'] ?? null ) === 'array' && is_string( $value ) ) {
446 - $decoded = json_decode( $value, true );
447 - $value = is_array( $decoded ) ? $decoded : array_map( 'trim', explode( ',', trim( $value, "[] \t\n\r" ) ) );
455 +
456 + $params['code'] .= "{$value}";
457 + if ( $index < count( $params['args'] ) - 1 ) {
458 + $params['code'] .= ', ';
448 459 }
449 - $callArgs[] = $value;
450 460 }
451 461
462 + $params['code'] .= ");\necho print_r(\$mwcode_result, true);";
463 +
452 464 $error = null;
453 465 $output = null;
454 -
466 +
455 467 try {
456 468 ob_start();
457 - // $params['code'] holds the function definition (empty if it was already
458 - // defined earlier this request). Declare it, then invoke it as data.
459 - if ( $params['code'] !== '' ) {
460 - eval( $params['code'] );
461 - }
462 - $mwcode_result = call_user_func_array( $params['name'], $callArgs );
463 - echo print_r( $mwcode_result, true );
469 + eval( $params['code'] );
464 470 $output = ob_get_clean();
465 -
466 - if ( $params['test'] ) {
471 +
472 + if ( $params['test'] ){
467 473 $output = explode( "\n", $output );
468 474 }
469 -
475 +
470 476 } catch ( Throwable $e ) {
471 477 //$this->log('Code Engine: Error executing the function: ' . $e->getMessage());
472 478 $error = new Exception(' Error executing the function, ' . $e->getMessage());
473 479
474 - if ( ob_get_level() > 0 ) {
475 - ob_end_clean();
476 - }
480 + ob_clean();
477 481 } finally {
478 482 restore_error_handler();
479 483 }
480 484
@@ -724,9 +728,9 @@
724 728 */
725 729 public function execute_active_snippets() {
726 730
727 731 $blocked = false;
728 - $page = isset( $_GET["page"] ) ? sanitize_text_field( $_GET["page"] ) : '';
732 + $page = isset( $_GET["page"] ) ? sanitize_text_field( $_GET["page"] ) : null;
729 733
730 734
731 735 if ( $page === 'mwcode_settings' ) {
732 736 // If we blocks global snippets like nonce_life filter, we would block the settings page so let's remove the block for this page