Skip to content

Generic PHP Function data source / query runner. #673

Description

@mattheu

I have been exploring implementing this plugin within an existing project where we have data stored in a custom table in the WordPress database. We have some PHP functions to retrieve this data and I was wondering what the best way to retrieve this data would be?

I've put together a rough implementation using a custom query runner and it's working pretty well. It allows me to just specify any PHP function so I can have full control over retrieving and preparing the data.

Is there a better way to do this? Is there something in this that would be worth contributing back?

/**
 * Remote Data Blocks PHP Function Query Runner.
 *
 * Custom Query Runner for PHP Function Data Sources
 */

namespace RemoteDataBlocks\Config\PhpFunctionQueryRunner;

use RemoteDataBlocks\Config\QueryRunner\QueryRunner;
use RemoteDataBlocks\Config\Query\HttpQueryInterface;
use WP_Error;
use Exception;
use Error;

/**
 * Custom Query Runner for PHP Function Data Sources
 *
 * This query runner extends QueryRunner and overrides just the HTTP request part
 * to call a PHP function instead.
 */
class PhpFunctionQueryRunner extends QueryRunner {
	/**
	 * Override to bypass HTTP request validation for PHP functions
	 *
	 * @param HttpQueryInterface $query The query
	 * @param array $input_variables Input variables
	 * @return array Request details (simplified for PHP functions)
	 */
	protected function get_request_details( HttpQueryInterface $query, array $input_variables ): array|WP_Error {
		// PHP function passed as endpoint.    
		$function_name = $query->get_endpoint( $input_variables );

		return [
			'method' => 'PHP',
			'options' => $input_variables,
			'origin' => 'php-function',
			'uri' => $function_name,
		];
	}

	/**
	 * Override to execute PHP function instead of HTTP request
	 *
	 * @param array $request_details Not used for PHP functions
	 * @param array $input_variables Input variables to pass to the function
	 * @return array|WP_Error Response data
	 */
	protected function get_raw_response_data( array $request_details, array $input_variables ): array|WP_Error {
		// Extract function name from the URI (which comes from endpoint)
		$function_name = $request_details['uri'] ?? null;

		if ( ! $function_name ) {
			return new WP_Error( 'missing-php-function', 'No function name specified.' );
		}

		if ( ! is_callable( $function_name ) ) {
			return new WP_Error(
				'invalid-php-function',
				sprintf( 'PHP function "%s" is not callable. Make sure the function exists and is properly namespaced.', $function_name )
			);
		}

		try {
			// Call the PHP function with input variables
			$result = call_user_func( $function_name, $input_variables );

			// Ensure the result is an array
			if ( ! is_array( $result ) ) {
				return new WP_Error(
					'invalid-function-return',
					'PHP function must return an array. Got: ' . gettype( $result )
				);
			}

			// Return in the same format as the parent class expects
			return [
				'input_variables' => $input_variables,
				'metadata' => [
					'age' => 0, // No cache age for PHP functions
					'status_code' => 200, // Mock status code
				],
				'response_data' => $result,
			];

		} catch ( Exception $e ) {
			return new WP_Error( 'php-function-execution-error', $e->getMessage() );
		} catch ( Error $e ) {
			return new WP_Error( 'php-function-fatal-error', $e->getMessage() );
		}
	}
}

Example Usage

$data_source = [
	'display_name' => 'PHP Function Data Source',
	// Endpoint is a callable PHP function
	'endpoint' => __NAMESPACE__ . '\\get_data',
];

$query = [
	'data_source' => $data_source,
	'display_name' => 'Test PHP Function Data Query',
	'input_schema' => [
		// ...
	],
	'output_schema' => [
		'is_collection' => false,
		'type' => [
			// ...
		],
	],
	'query_runner' => new RemoteDataBlocks\Config\PhpFunctionQueryRunner\PhpFunctionQueryRunner(),
	'cache_ttl' => 300,
];

register_remote_data_block( [
	'title' => 'Degree Finder Institution Data',
	'render_query' => [
		'query' => $query,
	],
] );

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions