balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

manager.php (9487B)


      1 <?php
      2 
      3 namespace Elementor\Data;
      4 
      5 use Elementor\Core\Base\Module as BaseModule;
      6 use Elementor\Data\Base\Processor;
      7 
      8 if ( ! defined( 'ABSPATH' ) ) {
      9 	exit; // Exit if accessed directly
     10 }
     11 
     12 class Manager extends BaseModule {
     13 
     14 	const ROOT_NAMESPACE = 'elementor';
     15 
     16 	const REST_BASE = '';
     17 
     18 	const VERSION = '1';
     19 
     20 	/**
     21 	 * @var \WP_REST_Server
     22 	 */
     23 	private $server;
     24 
     25 	/**
     26 	 * @var boolean
     27 	 */
     28 	private $is_internal = false;
     29 
     30 	/**
     31 	 * @var array
     32 	 */
     33 	private $cache = [];
     34 
     35 	/**
     36 	 * Loaded controllers.
     37 	 *
     38 	 * @var \Elementor\Data\Base\Controller[]
     39 	 */
     40 	public $controllers = [];
     41 
     42 	/**
     43 	 * Loaded command(s) format.
     44 	 *
     45 	 * @var string[]
     46 	 */
     47 	public $command_formats = [];
     48 
     49 	/**
     50 	 * Fix issue with 'Potentially polymorphic call. The code may be inoperable depending on the actual class instance passed as the argument.'.
     51 	 *
     52 	 * @return \Elementor\Core\Base\Module|\Elementor\Data\Manager
     53 	 */
     54 	public static function instance() {
     55 		return ( parent::instance() );
     56 	}
     57 
     58 	public function __construct() {
     59 		add_action( 'rest_api_init', [ $this, 'register_rest_error_handler' ] );
     60 	}
     61 
     62 	public function get_name() {
     63 		return 'data-manager';
     64 	}
     65 
     66 	/**
     67 	 * @return \Elementor\Data\Base\Controller[]
     68 	 */
     69 	public function get_controllers() {
     70 		return $this->controllers;
     71 	}
     72 
     73 	private function get_cache( $key ) {
     74 		return self::get_items( $this->cache, $key );
     75 	}
     76 
     77 	private function set_cache( $key, $value ) {
     78 		$this->cache[ $key ] = $value;
     79 	}
     80 
     81 	/**
     82 	 * Register controller.
     83 	 *
     84 	 * @param string $controller_class_name
     85 	 *
     86 	 * @return \Elementor\Data\Base\Controller
     87 	 */
     88 	public function register_controller( $controller_class_name ) {
     89 		$controller_instance = new $controller_class_name();
     90 
     91 		return $this->register_controller_instance( $controller_instance );
     92 	}
     93 
     94 	/**
     95 	 * Register controller instance.
     96 	 *
     97 	 * @param \Elementor\Data\Base\Controller $controller_instance
     98 	 *
     99 	 * @return \Elementor\Data\Base\Controller
    100 	 */
    101 	public function register_controller_instance( $controller_instance ) {
    102 		// TODO: Validate instance.
    103 
    104 		$this->controllers[ $controller_instance->get_name() ] = $controller_instance;
    105 
    106 		return $controller_instance;
    107 	}
    108 
    109 	/**
    110 	 * Register endpoint format.
    111 	 *
    112 	 * @param string $command
    113 	 * @param string $format
    114 	 *
    115 	 */
    116 	public function register_endpoint_format( $command, $format ) {
    117 		$this->command_formats[ $command ] = rtrim( $format, '/' );
    118 	}
    119 
    120 	public function register_rest_error_handler() {
    121 		// TODO: Remove - Find better solution.
    122 		return;
    123 
    124 		if ( ! $this->is_internal() ) {
    125 			$logger_manager = \Elementor\Core\Logger\Manager::instance();
    126 
    127 			set_error_handler( [ $logger_manager, 'rest_error_handler' ], E_ALL );
    128 		}
    129 	}
    130 
    131 	/**
    132 	 * Find controller instance.
    133 	 *
    134 	 * By given command name.
    135 	 *
    136 	 * @param string $command
    137 	 *
    138 	 * @return false|\Elementor\Data\Base\Controller
    139 	 */
    140 	public function find_controller_instance( $command ) {
    141 		$command_parts = explode( '/', $command );
    142 		$assumed_command_parts = [];
    143 
    144 		foreach ( $command_parts as $command_part ) {
    145 			$assumed_command_parts [] = $command_part;
    146 
    147 			foreach ( $this->controllers as $controller_name => $controller ) {
    148 				$assumed_command = implode( '/', $assumed_command_parts );
    149 
    150 				if ( $assumed_command === $controller_name ) {
    151 					return $controller;
    152 				}
    153 			}
    154 		}
    155 
    156 		return false;
    157 	}
    158 
    159 	/**
    160 	 * Command extract args.
    161 	 *
    162 	 * @param string $command
    163 	 * @param array $args
    164 	 *
    165 	 * @return \stdClass
    166 	 */
    167 	public function command_extract_args( $command, $args = [] ) {
    168 		$result = new \stdClass();
    169 		$result->command = $command;
    170 		$result->args = $args;
    171 
    172 		if ( false !== strpos( $command, '?' ) ) {
    173 			$command_parts = explode( '?', $command );
    174 			$pure_command = $command_parts[0];
    175 			$query_string = $command_parts[1];
    176 
    177 			parse_str( $query_string, $temp );
    178 
    179 			$result->command = rtrim( $pure_command, '/' );
    180 			$result->args = array_merge( $args, $temp );
    181 		}
    182 
    183 		return $result;
    184 	}
    185 
    186 	/**
    187 	 * Command to endpoint.
    188 	 *
    189 	 * Format is required otherwise $command will returned.
    190 	 *
    191 	 * @param string $command
    192 	 * @param string $format
    193 	 * @param array  $args
    194 	 *
    195 	 * @return string endpoint
    196 	 */
    197 	public function command_to_endpoint( $command, $format, $args ) {
    198 		$endpoint = $command;
    199 
    200 		if ( $format ) {
    201 			$formatted = $format;
    202 
    203 			array_walk( $args, function ( $val, $key ) use ( &$formatted ) {
    204 				$formatted = str_replace( '{' . $key . '}', $val, $formatted );
    205 			} );
    206 
    207 			// Remove remaining format if not requested via `$args`.
    208 			if ( strstr( $formatted, '/{' ) ) {
    209 				/**
    210 				 * Example:
    211 				 * $command = 'example/documents';
    212 				 * $format = 'example/documents/{document_id}/elements/{element_id}';
    213 				 * $formatted = 'example/documents/1618/elements/{element_id}';
    214 				 * Result:
    215 				 * $formatted = 'example/documents/1618/elements';
    216 				 */
    217 				$formatted = substr( $formatted, 0, strpos( $formatted, '/{' ) );
    218 			}
    219 
    220 			$endpoint = $formatted;
    221 		}
    222 
    223 		return $endpoint;
    224 	}
    225 
    226 	/**
    227 	 * Run server.
    228 	 *
    229 	 * Init WordPress reset api.
    230 	 *
    231 	 * @return \WP_REST_Server
    232 	 */
    233 	public function run_server() {
    234 		/**
    235 		 * If run_server() called means, that rest api is simulated from the backend.
    236 		 */
    237 		$this->is_internal = true;
    238 
    239 		if ( ! $this->server ) {
    240 			// Remove all 'rest_api_init' actions.
    241 			remove_all_actions( 'rest_api_init' );
    242 
    243 			// Call custom reset api loader.
    244 			do_action( 'elementor_rest_api_before_init' );
    245 
    246 			$this->server = rest_get_server(); // Init API.
    247 		}
    248 
    249 		return $this->server;
    250 	}
    251 
    252 	/**
    253 	 * Kill server.
    254 	 *
    255 	 * Free server and controllers.
    256 	 */
    257 	public function kill_server() {
    258 		global $wp_rest_server;
    259 
    260 		$this->controllers = [];
    261 		$this->command_formats = [];
    262 		$this->server = false;
    263 		$this->is_internal = false;
    264 		$this->cache = [];
    265 		$wp_rest_server = false;
    266 	}
    267 
    268 	/**
    269 	 * Run processor.
    270 	 *
    271 	 * @param \Elementor\Data\Base\Processor $processor
    272 	 * @param array                          $data
    273 	 *
    274 	 * @return mixed
    275 	 */
    276 	public function run_processor( $processor, $data ) {
    277 		if ( call_user_func_array( [ $processor, 'get_conditions' ], $data ) ) {
    278 			return call_user_func_array( [ $processor, 'apply' ], $data );
    279 		}
    280 
    281 		return null;
    282 	}
    283 
    284 	/**
    285 	 * Run processors.
    286 	 *
    287 	 * Filter them by class.
    288 	 *
    289 	 * @param \Elementor\Data\Base\Processor[] $processors
    290 	 * @param string $filter_by_class
    291 	 * @param array $data
    292 	 *
    293 	 * @return false|array
    294 	 */
    295 	public function run_processors( $processors, $filter_by_class, $data ) {
    296 		foreach ( $processors as $processor ) {
    297 			if ( $processor instanceof $filter_by_class ) {
    298 				if ( Processor\Before::class === $filter_by_class ) {
    299 					$this->run_processor( $processor, $data );
    300 				} elseif ( Processor\After::class === $filter_by_class ) {
    301 					$result = $this->run_processor( $processor, $data );
    302 					if ( $result ) {
    303 						$data[1] = $result;
    304 					}
    305 				} else {
    306 					// TODO: error
    307 					break;
    308 				}
    309 			}
    310 		}
    311 
    312 		return isset( $data[1] ) ? $data[1] : false;
    313 	}
    314 
    315 	/**
    316 	 * Run request.
    317 	 *
    318 	 * Simulate rest API from within the backend.
    319 	 * Use args as query.
    320 	 *
    321 	 * @param string $endpoint
    322 	 * @param array $args
    323 	 * @param string $method
    324 	 *
    325 	 * @return \WP_REST_Response
    326 	 */
    327 	private function run_request( $endpoint, $args, $method ) {
    328 		$this->run_server();
    329 
    330 		$endpoint = '/' . self::ROOT_NAMESPACE . '/v' . self::VERSION . '/' . $endpoint;
    331 
    332 		// Run reset api.
    333 		$request = new \WP_REST_Request( $method, $endpoint );
    334 
    335 		if ( 'GET' === $method ) {
    336 			$request->set_query_params( $args );
    337 		} else {
    338 			$request->set_body_params( $args );
    339 		}
    340 
    341 		return rest_do_request( $request );
    342 	}
    343 
    344 	/**
    345 	 * Run endpoint.
    346 	 *
    347 	 * Wrapper for `$this->run_request` return `$response->getData()` instead of `$response`.
    348 	 *
    349 	 * @param string $endpoint
    350 	 * @param array $args
    351 	 * @param string $method
    352 	 *
    353 	 * @return array
    354 	 */
    355 	public function run_endpoint( $endpoint, $args = [], $method = 'GET' ) {
    356 		$response = $this->run_request( $endpoint, $args, $method );
    357 
    358 		return $response->get_data();
    359 	}
    360 
    361 	/**
    362 	 * Run ( simulated reset api ).
    363 	 *
    364 	 * Do:
    365 	 * Init reset server.
    366 	 * Run before processors.
    367 	 * Run command as reset api endpoint from internal.
    368 	 * Run after processors.
    369 	 *
    370 	 * @param string $command
    371 	 * @param array  $args
    372 	 * @param string $method
    373 	 *
    374 	 * @return array|false processed result
    375 	 */
    376 	public function run( $command, $args = [], $method = 'GET' ) {
    377 		$key = crc32( $command . '-' . wp_json_encode( $args ) . '-' . $method );
    378 		$cache = $this->get_cache( $key );
    379 
    380 		if ( $cache ) {
    381 			return $cache;
    382 		}
    383 
    384 		$this->run_server();
    385 
    386 		$controller_instance = $this->find_controller_instance( $command );
    387 
    388 		if ( ! $controller_instance ) {
    389 			$this->set_cache( $key, [] );
    390 			return [];
    391 		}
    392 
    393 		$extracted_command = $this->command_extract_args( $command, $args );
    394 		$command = $extracted_command->command;
    395 		$args = $extracted_command->args;
    396 
    397 		$format = isset( $this->command_formats[ $command ] ) ? $this->command_formats[ $command ] : false;
    398 
    399 		$command_processors = $controller_instance->get_processors( $command );
    400 
    401 		$endpoint = $this->command_to_endpoint( $command, $format, $args );
    402 
    403 		$this->run_processors( $command_processors, Processor\Before::class, [ $args ] );
    404 
    405 		$response = $this->run_request( $endpoint, $args, $method );
    406 		$result = $response->get_data();
    407 
    408 		if ( $response->is_error() ) {
    409 			$this->set_cache( $key, [] );
    410 			return [];
    411 		}
    412 
    413 		$result = $this->run_processors( $command_processors, Processor\After::class, [ $args, $result ] );
    414 
    415 		$this->set_cache( $key, $result );
    416 
    417 		return $result;
    418 	}
    419 
    420 	public function is_internal() {
    421 		return $this->is_internal;
    422 	}
    423 }