196 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			196 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
/**
 | 
						|
 * Add support for headless WP.
 | 
						|
 *
 | 
						|
 * @since      1.0.15
 | 
						|
 * @package    RankMath
 | 
						|
 * @subpackage RankMath\Rest
 | 
						|
 * @author     Rank Math <support@rankmath.com>
 | 
						|
 */
 | 
						|
 | 
						|
namespace RankMath\Rest;
 | 
						|
 | 
						|
use WP_Error;
 | 
						|
use WP_REST_Server;
 | 
						|
use WP_REST_Request;
 | 
						|
use WP_REST_Response;
 | 
						|
use WP_REST_Controller;
 | 
						|
use RankMath\Helpers\Url;
 | 
						|
use RankMath\Helper;
 | 
						|
use RankMath\Frontend\Frontend;
 | 
						|
 | 
						|
defined( 'ABSPATH' ) || exit;
 | 
						|
 | 
						|
/**
 | 
						|
 * Front class.
 | 
						|
 */
 | 
						|
class Headless extends WP_REST_Controller {
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Whether the request is for the homepage.
 | 
						|
	 *
 | 
						|
	 * @var boolean
 | 
						|
	 */
 | 
						|
	public $is_home = false;
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Constructor.
 | 
						|
	 */
 | 
						|
	public function __construct() {
 | 
						|
		$this->namespace = \RankMath\Rest\Rest_Helper::BASE;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Registers the routes for the objects of the controller.
 | 
						|
	 */
 | 
						|
	public function register_routes() {
 | 
						|
		if ( ! Helper::get_settings( 'general.headless_support' ) ) {
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		register_rest_route(
 | 
						|
			$this->namespace,
 | 
						|
			'/getHead',
 | 
						|
			[
 | 
						|
				'methods'             => WP_REST_Server::READABLE,
 | 
						|
				'callback'            => [ $this, 'get_head' ],
 | 
						|
				'permission_callback' => '__return_true',
 | 
						|
				'args'                => [
 | 
						|
					'url' => [
 | 
						|
						'type'              => 'string',
 | 
						|
						'required'          => true,
 | 
						|
						'description'       => esc_html__( 'URL to get HTML tags for.', 'rank-math' ),
 | 
						|
						'validate_callback' => [ $this, 'is_valid_url' ],
 | 
						|
					],
 | 
						|
				],
 | 
						|
			]
 | 
						|
		);
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Get all tags that go in the <head>. Useful for headless WP installations.
 | 
						|
	 *
 | 
						|
	 * @param WP_REST_Request $request Request object, should include the "url" parameter.
 | 
						|
	 *
 | 
						|
	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
 | 
						|
	 */
 | 
						|
	public function get_head( WP_REST_Request $request ) {
 | 
						|
		$resp = new WP_REST_Response();
 | 
						|
		$url  = $request->get_param( 'url' );
 | 
						|
 | 
						|
		$html = $this->get_html_head( $url );
 | 
						|
 | 
						|
		$resp->set_status( 200 );
 | 
						|
		$resp->set_data(
 | 
						|
			[
 | 
						|
				'success' => true,
 | 
						|
				'head'    => $html,
 | 
						|
 | 
						|
			]
 | 
						|
		);
 | 
						|
		return $resp;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Return Rank Math head HTML output for the given URL.
 | 
						|
	 *
 | 
						|
	 * @param  string $url Request URL.
 | 
						|
	 *
 | 
						|
	 * @return string
 | 
						|
	 */
 | 
						|
	private function get_html_head( $url ) {
 | 
						|
		$this->setup_post_head( $url );
 | 
						|
 | 
						|
		ob_start();
 | 
						|
		do_action( 'wp' );
 | 
						|
		do_action( 'rank_math/head' );
 | 
						|
		return ob_get_clean();
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Prepare head output for a URL.
 | 
						|
	 *
 | 
						|
	 * @param string $url Request URL.
 | 
						|
	 *
 | 
						|
	 * @return void
 | 
						|
	 */
 | 
						|
	private function setup_post_head( $url ) {
 | 
						|
		// Setup WordPress.
 | 
						|
		$_SERVER['REQUEST_URI'] = $this->generate_request_uri( $url );
 | 
						|
		remove_all_actions( 'wp' );
 | 
						|
		remove_all_actions( 'parse_request' );
 | 
						|
		wp();
 | 
						|
 | 
						|
		if ( $this->is_home ) {
 | 
						|
			$GLOBALS['wp_query']->is_home = true;
 | 
						|
		}
 | 
						|
 | 
						|
		remove_filter( 'option_rewrite_rules', [ $this, 'fix_query_notice' ] );
 | 
						|
		header( 'Content-Type: application/json; charset=UTF-8' );
 | 
						|
 | 
						|
		// Setup Rank Math.
 | 
						|
		rank_math()->variables->setup();
 | 
						|
		rank_math()->manager->load_modules();
 | 
						|
		new Frontend();
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Generate $_SERVER['REQUEST_URI'] value based on input URL.
 | 
						|
	 *
 | 
						|
	 * @param string $url Input URL.
 | 
						|
	 * @return string
 | 
						|
	 */
 | 
						|
	public function generate_request_uri( $url ) {
 | 
						|
		$quoted      = preg_quote( rtrim( home_url(), '/' ), '/' );
 | 
						|
		$request_uri = preg_replace( sprintf( '/^%s/i', $quoted ), '', rtrim( $url, '/' ) );
 | 
						|
		if ( empty( $request_uri ) ) {
 | 
						|
			$request_uri   = '/';
 | 
						|
			$this->is_home = true;
 | 
						|
			$front_page_id = get_option( 'page_on_front' );
 | 
						|
			if ( 'page' === get_option( 'show_on_front' ) && $front_page_id ) {
 | 
						|
				$this->is_home = false;
 | 
						|
				$request_uri   = get_post_field( 'post_name', $front_page_id );
 | 
						|
			}
 | 
						|
 | 
						|
			add_filter( 'option_rewrite_rules', [ $this, 'fix_query_notice' ] );
 | 
						|
		}
 | 
						|
 | 
						|
		return $request_uri;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Filter rewrite_rules to avoid a PHP notice.
 | 
						|
	 *
 | 
						|
	 * @param array $rules Original rules.
 | 
						|
	 * @return array
 | 
						|
	 */
 | 
						|
	public function fix_query_notice( $rules ) {
 | 
						|
		if ( ! is_array( $rules ) || isset( $rules['$'] ) ) {
 | 
						|
			return $rules;
 | 
						|
		}
 | 
						|
 | 
						|
		global $wp_rewrite;
 | 
						|
		$rules['$'] = $wp_rewrite->index;
 | 
						|
		return $rules;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Check if provided URL is valid and internal.
 | 
						|
	 *
 | 
						|
	 * @param string $url URL.
 | 
						|
	 *
 | 
						|
	 * @return boolean
 | 
						|
	 */
 | 
						|
	public function is_valid_url( $url ) {
 | 
						|
		$url = preg_replace_callback(
 | 
						|
			'/[^\x20-\x7f]/',
 | 
						|
			function( $match ) {
 | 
						|
				return rawurlencode( $match[0] );
 | 
						|
			},
 | 
						|
			$url
 | 
						|
		);
 | 
						|
 | 
						|
		return Url::is_url( $url ) && ! Url::is_external( $url );
 | 
						|
	}
 | 
						|
}
 |