Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 53
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
Landing_Page_Dispatcher
0.00% covered (danger)
0.00%
0 / 53
0.00% covered (danger)
0.00%
0 / 3
182
0.00% covered (danger)
0.00%
0 / 1
 init
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 dispatch
0.00% covered (danger)
0.00%
0 / 37
0.00% covered (danger)
0.00%
0 / 1
90
 log
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2/**
3 * Bridges the Woo opt-in hook to WPCOM, which in turn enqueues the
4 * landing-page creation job in DSP.
5 *
6 * Listens for `jetpack_blaze_woo_product_promote_requested` and POSTs
7 * a minimal payload to a WPCOM internal endpoint via the canonical
8 * `wpcom_json_api_request_as_blog` signed channel.
9 *
10 * The WPCOM endpoint itself ships separately (Phase 2 — see PR roadmap).
11 * Until that endpoint lands the call will 404; the dispatcher logs and
12 * swallows the error so it doesn't break the merchant publish flow.
13 *
14 * @package automattic/jetpack-blaze
15 */
16
17namespace Automattic\Jetpack\Blaze;
18
19use Automattic\Jetpack\Connection\Client as Jetpack_Connection_Client;
20use Automattic\Jetpack\Connection\Manager as Jetpack_Connection;
21use WP_Post;
22
23/**
24 * Dispatcher for Blaze landing-page jobs.
25 */
26class Landing_Page_Dispatcher {
27
28    /**
29     * Path suffix appended to `/sites/{blog_id}/` when calling WPCOM.
30     * The WPCOM endpoint is site-specific, so the blog id segment is required.
31     *
32     * @var string
33     */
34    const ENDPOINT_PATH_SUFFIX = '/blaze/landing-pages/enqueue';
35    const API_VERSION          = '2';
36
37    /**
38     * Wire hooks.
39     *
40     * @return void
41     */
42    public static function init() {
43        add_action(
44            'jetpack_blaze_woo_product_promote_requested',
45            array( __CLASS__, 'dispatch' ),
46            10,
47            2
48        );
49    }
50
51    /**
52     * Enqueue a landing-page job for the given product.
53     *
54     * @param int     $product_id Product ID.
55     * @param WP_Post $product    Product post.
56     * @return void
57     */
58    public static function dispatch( $product_id, $product ) {
59        $payload = array(
60            'mode'       => 'woocommerce',
61            'product_id' => (int) $product_id,
62            'title'      => $product instanceof WP_Post ? (string) $product->post_title : '',
63        );
64
65        /**
66         * Filter the payload sent to WPCOM when enqueueing a Blaze
67         * landing-page job.
68         *
69         * @since $$next-version$$
70         *
71         * @param array $payload    Payload that will be JSON-encoded.
72         * @param int   $product_id Product ID.
73         */
74        $payload = (array) apply_filters( 'jetpack_blaze_landing_dispatch_payload', $payload, (int) $product_id );
75
76        $blog_id = Jetpack_Connection::get_site_id();
77        if ( is_wp_error( $blog_id ) || ! is_numeric( $blog_id ) ) {
78            self::log(
79                'no_blog_id',
80                is_wp_error( $blog_id ) ? $blog_id->get_error_message() : 'not numeric',
81                $product_id
82            );
83            return;
84        }
85        $path = '/sites/' . (int) $blog_id . self::ENDPOINT_PATH_SUFFIX;
86
87        $response = Jetpack_Connection_Client::wpcom_json_api_request_as_blog(
88            $path,
89            self::API_VERSION,
90            array(
91                'method'  => 'POST',
92                'headers' => array( 'Content-Type' => 'application/json' ),
93            ),
94            wp_json_encode( $payload, JSON_UNESCAPED_SLASHES ),
95            'wpcom'
96        );
97
98        if ( is_wp_error( $response ) ) {
99            self::log( 'wp_error', $response->get_error_message(), $product_id );
100            return;
101        }
102
103        $code = (int) wp_remote_retrieve_response_code( $response );
104        if ( $code >= 200 && $code < 300 ) {
105            return;
106        }
107
108        $body = (string) wp_remote_retrieve_body( $response );
109        self::log(
110            'http_' . $code,
111            '' === $body ? '(empty body)' : $body,
112            $product_id
113        );
114    }
115
116    /**
117     * Log a dispatch failure. Uses error_log because Jetpack-blaze does
118     * not currently ship a structured logger, and we want this visible in
119     * `WP_DEBUG_LOG` without crashing the publish request.
120     *
121     * @param string $reason     Short reason code.
122     * @param string $detail     Detail string.
123     * @param int    $product_id Product ID.
124     * @return void
125     */
126    private static function log( $reason, $detail, $product_id ) {
127        if ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG ) {
128            return;
129        }
130        // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Best-effort debug logging guarded by WP_DEBUG.
131        error_log(
132            sprintf(
133                '[jetpack-blaze] landing-page dispatch failed (%s) for product %d: %s',
134                $reason,
135                (int) $product_id,
136                $detail
137            )
138        );
139    }
140}