PluginProbe
Code Engine – PHP Snippets, AI Functions & Automation for WordPress / trunk
Code Engine – PHP Snippets, AI Functions & Automation for WordPress vtrunk
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
← All changes | classes/core.php +50 -54 0.5.3trunk View file →
@@ -362,35 +362,17 @@
362 362 'values' => $snippet['functionArgsDict'] // Contains the default values of the arguments
363 363 ];
364 364 }
365 365
366 - // Sanitize all the arguments if the option is enabled
367 - if ( $this->get_option( 'sanitize_arguments', true ) ) {
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.
368 374
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 -
393 375 // Make sure the function is existing and is the one in the snippet
394 376 if ( empty( $params['code'] ) ) {
395 377 throw new Exception( 'Code Engine: The snippet code appears to be empty.' );
396 378 }
@@ -398,12 +380,17 @@
398 380 if ( empty( $params['name'] ) || ! str_contains( $params['code'], $params['name'] ) ) {
399 381 throw new Exception( "Code Engine: Function name does not match. The name should be {$params['name']}." );
400 382 }
401 383
402 - // Overwrite the default values with the provided ones
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 = [];
403 390 if ( $args ) {
404 391 foreach ( $args as $name => $value ) {
405 - $params['values'][$name]['input'] = $value;
392 + $provided[ ltrim( $name, '$' ) ] = $value;
406 393 }
407 394
408 395 $this->log( '⚡ Arguments provided: ' . json_encode( $args ) );
409 396 }
@@ -436,49 +423,58 @@
436 423 // If already defined, just prepare to call the function without redefining it
437 424 $params['code'] = '';
438 425 }
439 426
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 - }
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'];
454 442 }
455 -
456 - $params['code'] .= "{$value}";
457 - if ( $index < count( $params['args'] ) - 1 ) {
458 - $params['code'] .= ', ';
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" ) ) );
459 448 }
449 + $callArgs[] = $value;
460 450 }
461 451
462 - $params['code'] .= ");\necho print_r(\$mwcode_result, true);";
463 -
464 452 $error = null;
465 453 $output = null;
466 -
454 +
467 455 try {
468 456 ob_start();
469 - eval( $params['code'] );
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 );
470 464 $output = ob_get_clean();
471 -
472 - if ( $params['test'] ){
465 +
466 + if ( $params['test'] ) {
473 467 $output = explode( "\n", $output );
474 468 }
475 -
469 +
476 470 } catch ( Throwable $e ) {
477 471 //$this->log('Code Engine: Error executing the function: ' . $e->getMessage());
478 472 $error = new Exception(' Error executing the function, ' . $e->getMessage());
479 473
480 - ob_clean();
474 + if ( ob_get_level() > 0 ) {
475 + ob_end_clean();
476 + }
481 477 } finally {
482 478 restore_error_handler();
483 479 }
484 480