223 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			223 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| 
 | |
| namespace EDD\Admin\Extensions;
 | |
| 
 | |
| class ExtensionsAPI {
 | |
| 
 | |
| 	/**
 | |
| 	 * Gets the product data from the EDD Products API.
 | |
| 	 *
 | |
| 	 * @since 2.11.4
 | |
| 	 * @param array $body    The body for the API request.
 | |
| 	 * @param int   $item_id The product ID, if querying a single product.
 | |
| 	 * @return false|array|ProductData
 | |
| 	 */
 | |
| 	public function get_product_data( $body = array(), $item_id = false ) {
 | |
| 		if ( empty( $body ) ) {
 | |
| 			if ( empty( $item_id ) ) {
 | |
| 				return false;
 | |
| 			}
 | |
| 			$body = $this->get_api_body( $item_id );
 | |
| 		}
 | |
| 		$key = $this->array_key_first( $body );
 | |
| 		// The option name is created from the first key/value pair of the API "body".
 | |
| 		$option_name = sanitize_key( "edd_extension_{$key}_{$body[ $key ]}_data" );
 | |
| 		$option      = get_option( $option_name );
 | |
| 		$is_stale    = $this->option_has_expired( $option );
 | |
| 
 | |
| 		// The ProductData class.
 | |
| 		$product_data = new ProductData();
 | |
| 
 | |
| 		// If the data is "fresh" and what we want exists, return it.
 | |
| 		if ( $option && ! $is_stale ) {
 | |
| 			if ( $item_id && ! empty( $option[ $item_id ] ) ) {
 | |
| 				return $product_data->fromArray( $option[ $item_id ] );
 | |
| 			} elseif ( ! empty( $option['timeout'] ) ) {
 | |
| 				unset( $option['timeout'] );
 | |
| 
 | |
| 				return $option;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// Get all of the product data.
 | |
| 		$all_product_data = $this->get_all_product_data();
 | |
| 
 | |
| 		// If no product data was retrieved, let the option sit for an hour.
 | |
| 		if ( empty( $all_product_data ) ) {
 | |
| 			$data = array(
 | |
| 				'timeout' => strtotime( '+1 hour', time() ),
 | |
| 			);
 | |
| 			if ( $option && $is_stale ) {
 | |
| 				$data = array_merge( $option, $data );
 | |
| 			}
 | |
| 			update_option(
 | |
| 				$option_name,
 | |
| 				$data,
 | |
| 				false
 | |
| 			);
 | |
| 
 | |
| 			if ( $item_id && ! empty( $option[ $item_id ] ) ) {
 | |
| 				return $product_data->fromArray( $option[ $item_id ] );
 | |
| 			}
 | |
| 			unset( $option['timeout'] );
 | |
| 
 | |
| 			return $option;
 | |
| 		}
 | |
| 
 | |
| 		$value = array(
 | |
| 			'timeout' => strtotime( '+1 week', time() ),
 | |
| 		);
 | |
| 		if ( $item_id && ! empty( $all_product_data->$item_id ) ) {
 | |
| 			$item              = $all_product_data->$item_id;
 | |
| 			$value[ $item_id ] = $this->get_item_data( $item );
 | |
| 		} elseif ( in_array( $key, array( 'category', 'tag' ), true ) ) {
 | |
| 			$term_id = $body[ $key ];
 | |
| 			foreach ( $all_product_data as $item_id => $item ) {
 | |
| 				if ( 'category' === $key && ( empty( $item->categories ) || ! in_array( $term_id, $item->categories, true ) ) ) {
 | |
| 					continue;
 | |
| 				} elseif ( 'tag' === $key && ( empty( $item->tags ) || ! in_array( $term_id, $item->tags, true ) ) ) {
 | |
| 					continue;
 | |
| 				}
 | |
| 				$value[ $item_id ] = $this->get_item_data( $item );
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		update_option( $option_name, $value, false );
 | |
| 		unset( $value['timeout'] );
 | |
| 
 | |
| 		return $item_id && ! empty( $value[ $item_id ] ) ? $product_data->fromArray( $value[ $item_id ] ) : $value;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Gets all of the product data, either from an option or an API request.
 | |
| 	 * If the option exists and has data, it will be an object.
 | |
| 	 *
 | |
| 	 * @since 2.11.4
 | |
| 	 * @return object|false
 | |
| 	 */
 | |
| 	private function get_all_product_data() {
 | |
| 		// Possibly all product data is in an option. If it is, return it.
 | |
| 		$all_product_data = get_option( 'edd_all_extension_data' );
 | |
| 		if ( $all_product_data && ! $this->option_has_expired( $all_product_data ) ) {
 | |
| 			return ! empty( $all_product_data['products'] ) ? $all_product_data['products'] : false;
 | |
| 		}
 | |
| 
 | |
| 		// Otherwise, query the API.
 | |
| 		$url     = add_query_arg(
 | |
| 			array(
 | |
| 				'edd_action' => 'extension_data',
 | |
| 			),
 | |
| 			$this->get_products_url()
 | |
| 		);
 | |
| 		$request = wp_remote_get(
 | |
| 			esc_url_raw( $url ),
 | |
| 			array(
 | |
| 				'timeout'   => 15,
 | |
| 				'sslverify' => true,
 | |
| 			)
 | |
| 		);
 | |
| 
 | |
| 		// If there was an API error, set option and return false.
 | |
| 		if ( is_wp_error( $request ) || ( 200 !== wp_remote_retrieve_response_code( $request ) ) ) {
 | |
| 			update_option(
 | |
| 				'edd_all_extension_data',
 | |
| 				array(
 | |
| 					'timeout' => strtotime( '+1 hour', time() ),
 | |
| 				),
 | |
| 				false
 | |
| 			);
 | |
| 
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		// Fresh data has been retrieved, so update the option with a four hour timeout.
 | |
| 		$all_product_data = json_decode( wp_remote_retrieve_body( $request ) );
 | |
| 		$data             = array(
 | |
| 			'timeout'  => strtotime( '+4 hours', time() ),
 | |
| 			'products' => $all_product_data,
 | |
| 		);
 | |
| 
 | |
| 		update_option( 'edd_all_extension_data', $data, false );
 | |
| 
 | |
| 		return $all_product_data;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Gets the product data as needed for the extension manager.
 | |
| 	 *
 | |
| 	 * @since 2.11.4
 | |
| 	 * @param object $item
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	private function get_item_data( $item ) {
 | |
| 		return array(
 | |
| 			'title'       => ! empty( $item->title ) ? $item->title : '',
 | |
| 			'slug'        => ! empty( $item->slug ) ? $item->slug : '',
 | |
| 			'image'       => ! empty( $item->image ) ? $item->image : '',
 | |
| 			'description' => ! empty( $item->excerpt ) ? $item->excerpt : '',
 | |
| 			'basename'    => ! empty( $item->custom_meta->basename ) ? $item->custom_meta->basename : '',
 | |
| 			'tab'         => ! empty( $item->custom_meta->settings_tab ) ? $item->custom_meta->settings_tab : '',
 | |
| 			'section'     => ! empty( $item->custom_meta->settings_section ) ? $item->custom_meta->settings_section : '',
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Gets the base url for the products remote request.
 | |
| 	 *
 | |
| 	 * @since 2.11.4
 | |
| 	 * @return string
 | |
| 	 */
 | |
| 	private function get_products_url() {
 | |
| 		if ( defined( 'EDD_PRODUCTS_URL' ) ) {
 | |
| 			return EDD_PRODUCTS_URL;
 | |
| 		}
 | |
| 
 | |
| 		return 'https://easydigitaldownloads.com/';
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Gets the default array for the body of the API request.
 | |
| 	 * A class may override this by setting an array to query a tag or category.
 | |
| 	 * Note that the first array key/value pair are used to create the option name.
 | |
| 	 *
 | |
| 	 * @since 2.11.4
 | |
| 	 * @param int $item_id The product ID.
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	private function get_api_body( $item_id ) {
 | |
| 		return array(
 | |
| 			'product' => $item_id,
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Gets the first key of an array.
 | |
| 	 * (Shims array_key_first for PHP < 7.3)
 | |
| 	 *
 | |
| 	 * @since 2.11.4
 | |
| 	 * @param array $array
 | |
| 	 * @return string|null
 | |
| 	 */
 | |
| 	private function array_key_first( array $array ) {
 | |
| 		if ( function_exists( 'array_key_first' ) ) {
 | |
| 			return array_key_first( $array );
 | |
| 		}
 | |
| 		foreach ( $array as $key => $unused ) {
 | |
| 			return $key;
 | |
| 		}
 | |
| 
 | |
| 		return null;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Checks whether a given option has "expired".
 | |
| 	 *
 | |
| 	 * @since 2.11.4
 | |
| 	 * @param array|false $option
 | |
| 	 * @return bool
 | |
| 	 */
 | |
| 	private function option_has_expired( $option ) {
 | |
| 		return empty( $option['timeout'] ) || time() > $option['timeout'];
 | |
| 	}
 | |
| }
 |