HEX
Server: LiteSpeed
System: Linux eko108.isimtescil.net 4.18.0-477.21.1.lve.1.el8.x86_64 #1 SMP Tue Sep 5 23:08:35 UTC 2023 x86_64
User: uyarreklamcomtr (11202)
PHP: 7.4.33
Disabled: opcache_get_status
Upload Files
File: /var/www/vhosts/uyarreklam.com.tr/httpdocs/WPCOMProxy.php.tar
uyarreklam.com.tr/httpdocs/wp-content/plugins/google-listings-and-ads/src/Integration/WPCOMProxy.php000064400000024123151550232250032542 0ustar00var/www/vhosts<?php
declare( strict_types=1 );

namespace Automattic\WooCommerce\GoogleListingsAndAds\Integration;

use Automattic\WooCommerce\GoogleListingsAndAds\DB\Query\ShippingTimeQuery;
use Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure\Registerable;
use Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure\Service;
use Automattic\WooCommerce\GoogleListingsAndAds\Value\ChannelVisibility;
use Automattic\WooCommerce\GoogleListingsAndAds\Options\OptionsAwareTrait;
use Automattic\WooCommerce\GoogleListingsAndAds\Options\OptionsInterface;
use Automattic\WooCommerce\GoogleListingsAndAds\Options\OptionsAwareInterface;
use Automattic\WooCommerce\GoogleListingsAndAds\Product\Attributes\AttributeManager;
use WC_Product;
use WP_REST_Response;
use WP_REST_Request;

defined( 'ABSPATH' ) || exit;

/**
 * Class WPCOMProxy
 *
 * Initializes the hooks to filter the data sent to the WPCOM proxy depending on the query parameter gla_syncable.
 *
 * @since 2.8.0
 *
 * @package Automattic\WooCommerce\GoogleListingsAndAds\Integration
 */
class WPCOMProxy implements Service, Registerable, OptionsAwareInterface {

	use OptionsAwareTrait;

	/**
	 * The ShippingTimeQuery object.
	 *
	 * @var ShippingTimeQuery
	 */
	protected $shipping_time_query;

	/**
	 * The AttributeManager object.
	 *
	 * @var AttributeManager
	 */
	protected $attribute_manager;

	/**
	 * The protected resources. Only items with visibility set to sync-and-show will be returned.
	 */
	protected const PROTECTED_RESOURCES = [
		'products',
		'coupons',
	];

	/**
	 * WPCOMProxy constructor.
	 *
	 * @param ShippingTimeQuery $shipping_time_query The ShippingTimeQuery object.
	 * @param AttributeManager  $attribute_manager   The AttributeManager object.
	 */
	public function __construct( ShippingTimeQuery $shipping_time_query, AttributeManager $attribute_manager ) {
		$this->shipping_time_query = $shipping_time_query;
		$this->attribute_manager   = $attribute_manager;
	}

	/**
	 * The meta key used to filter the items.
	 *
	 * @var string
	 */
	public const KEY_VISIBILITY = '_wc_gla_visibility';

	/**
	 * The Post types to be filtered.
	 *
	 * @var array
	 */
	public static $post_types_to_filter = [
		'product'           => [
			'meta_query' => [
				[
					'key'     => self::KEY_VISIBILITY,
					'value'   => ChannelVisibility::SYNC_AND_SHOW,
					'compare' => '=',
				],
			],
		],
		'shop_coupon'       => [
			'meta_query' => [
				[
					'key'     => self::KEY_VISIBILITY,
					'value'   => ChannelVisibility::SYNC_AND_SHOW,
					'compare' => '=',
				],
				[
					'key'     => 'customer_email',
					'compare' => 'NOT EXISTS',
				],
			],
		],
		'product_variation' => [
			'meta_query' => null,
		],
	];

	/**
	 * Register all filters.
	 */
	public function register(): void {
		// Allow to filter by gla_syncable.
		add_filter(
			'woocommerce_rest_query_vars',
			function ( $valid_vars ) {
				$valid_vars[] = 'gla_syncable';
				return $valid_vars;
			}
		);

		$this->register_callbacks();

		foreach ( array_keys( self::$post_types_to_filter ) as $object_type ) {
			$this->register_object_types_filter( $object_type );
		}
	}

	/**
	 * Register the filters for a specific object type.
	 *
	 * @param string $object_type The object type.
	 */
	protected function register_object_types_filter( string $object_type ): void {
		add_filter(
			'woocommerce_rest_prepare_' . $object_type . '_object',
			[ $this, 'filter_response_by_syncable_item' ],
			PHP_INT_MAX, // Run this filter last to override any other response.
			3
		);

		add_filter(
			'woocommerce_rest_prepare_' . $object_type . '_object',
			[ $this, 'prepare_response' ],
			PHP_INT_MAX - 1,
			3
		);

		add_filter(
			'woocommerce_rest_' . $object_type . '_object_query',
			[ $this, 'filter_by_metaquery' ],
			10,
			2
		);
	}

	/**
	 * Register the callbacks.
	 */
	protected function register_callbacks() {
		add_filter(
			'rest_request_after_callbacks',
			/**
			 * Add the Google for WooCommerce and Ads settings to the settings/general response.
			 *
			 * @param WP_REST_Response|WP_HTTP_Response|WP_Error|mixed $response The response object.
			 * @param mixed                                             $handler  The handler.
			 * @param WP_REST_Request                                   $request  The request object.
			 */
			function ( $response, $handler, $request ) {
				if ( ! $this->is_gla_request( $request ) || ! $response instanceof WP_REST_Response ) {
					return $response;
				}

				$data = $response->get_data();

				if ( $request->get_route() === '/wc/v3/settings/general' ) {
					$data[] = [
						'id'    => 'gla_target_audience',
						'label' => 'Google for WooCommerce: Target Audience',
						'value' => $this->options->get( OptionsInterface::TARGET_AUDIENCE, [] ),
					];

					$data[] = [
						'id'    => 'gla_shipping_times',
						'label' => 'Google for WooCommerce: Shipping Times',
						'value' => $this->shipping_time_query->get_all_shipping_times(),
					];

					$data[] = [
						'id'    => 'gla_language',
						'label' => 'Google for WooCommerce: Store language',
						'value' => get_locale(),
					];

					$response->set_data( array_values( $data ) );
				}

				$response->set_data( $this->prepare_data( $response->get_data(), $request ) );
				return $response;
			},
			10,
			3
		);
	}

	/**
	 * Prepares the data converting the empty arrays in objects for consistency.
	 *
	 * @param array           $data The response data to parse
	 * @param WP_REST_Request $request The request object.
	 * @return mixed
	 */
	public function prepare_data( $data, $request ) {
		if ( ! is_array( $data ) ) {
			return $data;
		}

		foreach ( $data as $key => $value ) {
			if ( preg_match( '/^\/wc\/v3\/shipping\/zones\/\d+\/methods/', $request->get_route() ) && isset( $value['settings'] ) && empty( $value['settings'] ) ) {
				$data[ $key ]['settings'] = (object) $value['settings'];
			}
		}

		return $data;
	}

	/**
	 * Whether the request is coming from the WPCOM proxy.
	 *
	 * @param WP_REST_Request $request The request object.
	 *
	 * @return bool
	 */
	protected function is_gla_request( WP_REST_Request $request ): bool {
		// WPCOM proxy will set the gla_syncable to 1 if the request is coming from the proxy and it is the Google App.
		return $request->get_param( 'gla_syncable' ) === '1';
	}

	/**
	 * Get route pieces: resource and id, if present.
	 *
	 * @param WP_REST_Request $request The request object.
	 *
	 * @return array The route pieces.
	 */
	protected function get_route_pieces( WP_REST_Request $request ): array {
		$route   = $request->get_route();
		$pattern = '/(?P<resource>[\w]+)(?:\/(?P<id>[\d]+))?$/';
		preg_match( $pattern, $route, $matches );

		return $matches;
	}

	/**
	 * Filter response by syncable item.
	 *
	 * @param WP_REST_Response $response The response object.
	 * @param mixed            $item     The item.
	 * @param WP_REST_Request  $request  The request object.
	 *
	 * @return WP_REST_Response The response object updated.
	 */
	public function filter_response_by_syncable_item( $response, $item, WP_REST_Request $request ): WP_REST_Response {
		if ( ! $this->is_gla_request( $request ) ) {
			return $response;
		}

		$pieces = $this->get_route_pieces( $request );

		if ( ! isset( $pieces['id'] ) || ! isset( $pieces['resource'] ) || ! in_array( $pieces['resource'], self::PROTECTED_RESOURCES, true ) ) {
			return $response;
		}

		$meta_data = $response->get_data()['meta_data'] ?? [];

		foreach ( $meta_data as $meta ) {
			if ( $meta->key === self::KEY_VISIBILITY && $meta->value === ChannelVisibility::SYNC_AND_SHOW ) {
				return $response;
			}
		}

		return new WP_REST_Response(
			[
				'code'    => 'gla_rest_item_no_syncable',
				'message' => 'Item not syncable',
				'data'    => [
					'status' => '403',
				],
			],
			403
		);
	}

	/**
	 * Query items with specific args for example where _wc_gla_visibility is set to sync-and-show.
	 *
	 * @param array           $args    The query args.
	 * @param WP_REST_Request $request The request object.
	 *
	 * @return array The query args updated.
	 * */
	public function filter_by_metaquery( array $args, WP_REST_Request $request ): array {
		if ( ! $this->is_gla_request( $request ) ) {
			return $args;
		}

		$post_type         = $args['post_type'];
		$post_type_filters = self::$post_types_to_filter[ $post_type ];

		if ( ! isset( $post_type_filters['meta_query'] ) || ! is_array( $post_type_filters['meta_query'] ) ) {
			return $args;
		}

		$args['meta_query'] = [ ...$args['meta_query'] ?? [], ...$post_type_filters['meta_query'] ];

		return $args;
	}

	/**
	 * Prepares the response when the request is coming from the WPCOM proxy:
	 *
	 * Filter all the private metadata and returns only the public metadata and those prefixed with _wc_gla
	 * For WooCommerce products, it will add the attribute mapping values.
	 *
	 * @param WP_REST_Response $response The response object.
	 * @param mixed            $item     The item.
	 * @param WP_REST_Request  $request  The request object.
	 *
	 * @return WP_REST_Response The response object updated.
	 */
	public function prepare_response( WP_REST_Response $response, $item, WP_REST_Request $request ): WP_REST_Response {
		if ( ! $this->is_gla_request( $request ) ) {
			return $response;
		}

		$data     = $response->get_data();
		$resource = $this->get_route_pieces( $request )['resource'] ?? null;

		if ( $item instanceof WC_Product && ( $resource === 'products' || $resource === 'variations' ) ) {
			$attr = $this->attribute_manager->get_all_aggregated_values( $item );
			// In case of empty array, convert to object to keep the response consistent.
			$data['gla_attributes'] = (object) $attr;

			// Force types and prevent user type change for fields as Google has strict type requirements.
			$data['price']         = strval( $data['price'] ?? null );
			$data['regular_price'] = strval( $data['regular_price'] ?? null );
			$data['sale_price']    = strval( $data['sale_price'] ?? null );
		}

		foreach ( $data['meta_data'] ?? [] as $key => $meta ) {
			if ( str_starts_with( $meta->key, '_' ) && ! str_starts_with( $meta->key, '_wc_gla' ) ) {
				unset( $data['meta_data'][ $key ] );
			}
		}

		$data['meta_data'] = array_values( $data['meta_data'] );

		$response->set_data( $data );

		return $response;
	}
}