210 lines
8.5 KiB
PHP
210 lines
8.5 KiB
PHP
<?php
|
|
/**
|
|
* Copyright (C) 2017-present, Meta, Inc.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; version 2 of the License.
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* @package FacebookPixelPlugin
|
|
*/
|
|
|
|
namespace FacebookPixelPlugin\Core;
|
|
|
|
use FacebookPixelPlugin\Core\FacebookServerSideEvent;
|
|
|
|
use FacebookPixelPlugin\FacebookAds\Object\ServerSide\Event;
|
|
use FacebookPixelPlugin\FacebookAds\Object\ServerSide\UserData;
|
|
use FacebookPixelPlugin\FacebookAds\Object\ServerSide\CustomData;
|
|
use FacebookPixelPlugin\FacebookAds\Object\ServerSide\Content;
|
|
|
|
defined( 'ABSPATH' ) || die( 'Direct access not allowed' );
|
|
|
|
/**
|
|
* Class ServerEventAsyncTask
|
|
*/
|
|
class ServerEventAsyncTask extends \WP_Async_Task {
|
|
/**
|
|
* The action to be performed by this task.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $action = 'send_server_events';
|
|
|
|
/**
|
|
* Converts the normalized user data to the keys used in UserData.
|
|
*
|
|
* Normalized user data is an array of key => value pairs
|
|
* where the key is a normalized version of the user data field name
|
|
* and the value is the value of the field.
|
|
*
|
|
* This function takes that array and converts it to
|
|
* the format used by UserData.
|
|
*
|
|
* @param array $user_data_normalized The normalized user data.
|
|
*
|
|
* @return array The converted user data.
|
|
*/
|
|
private function convert_user_data( $user_data_normalized ) {
|
|
$norm_key_to_key = array(
|
|
AAMSettingsFields::EMAIL => 'emails',
|
|
AAMSettingsFields::FIRST_NAME => 'first_names',
|
|
AAMSettingsFields::LAST_NAME => 'last_names',
|
|
AAMSettingsFields::GENDER => 'genders',
|
|
AAMSettingsFields::DATE_OF_BIRTH => 'dates_of_birth',
|
|
AAMSettingsFields::EXTERNAL_ID => 'external_ids',
|
|
AAMSettingsFields::PHONE => 'phones',
|
|
AAMSettingsFields::CITY => 'cities',
|
|
AAMSettingsFields::STATE => 'states',
|
|
AAMSettingsFields::ZIP_CODE => 'zip_codes',
|
|
AAMSettingsFields::COUNTRY => 'country_codes',
|
|
);
|
|
$user_data = array();
|
|
foreach ( $user_data_normalized as $norm_key => $field ) {
|
|
if ( isset( $norm_key_to_key[ $norm_key ] ) ) {
|
|
$user_data[ $norm_key_to_key[ $norm_key ] ] = $field;
|
|
} else {
|
|
$user_data[ $norm_key ] = $field;
|
|
}
|
|
}
|
|
return $user_data;
|
|
}
|
|
|
|
/**
|
|
* Converts an array representation of an event into an Event object.
|
|
*
|
|
* This function takes an array that represents an event and
|
|
* converts it into
|
|
* an instance of the `Event` class. If the array contains `user_data`, a
|
|
* `UserData` object is created and associated with the event. Similarly, if
|
|
* `custom_data` is present, a `CustomData` object is created and
|
|
* associated.
|
|
* This includes handling nested `contents` and additional custom properties
|
|
* like `fb_integration_tracking`.
|
|
*
|
|
* @param array $event_as_array The array representing the event.
|
|
* @return Event The constructed Event object.
|
|
*/
|
|
private function convert_array_to_event( $event_as_array ) {
|
|
$event = new Event( $event_as_array );
|
|
if ( isset( $event_as_array['user_data'] ) ) {
|
|
$user_data = new UserData(
|
|
$this->convert_user_data(
|
|
$event_as_array['user_data']
|
|
)
|
|
);
|
|
$event->setUserData( $user_data );
|
|
}
|
|
if ( isset( $event_as_array['custom_data'] ) ) {
|
|
$custom_data = new CustomData( $event_as_array['custom_data'] );
|
|
if ( isset( $event_as_array['custom_data']['contents'] ) ) {
|
|
$contents = array();
|
|
foreach (
|
|
$event_as_array['custom_data']['contents'] as $contents_as_array
|
|
) {
|
|
if ( isset( $contents_as_array['id'] ) ) {
|
|
$contents_as_array['product_id'] = $contents_as_array['id'];
|
|
}
|
|
$contents[] = new Content( $contents_as_array );
|
|
}
|
|
$custom_data->setContents( $contents );
|
|
}
|
|
if ( isset(
|
|
$event_as_array['custom_data']['fb_integration_tracking']
|
|
) ) {
|
|
$custom_data->addCustomProperty(
|
|
'fb_integration_tracking',
|
|
$event_as_array['custom_data']['fb_integration_tracking']
|
|
);
|
|
}
|
|
$event->setCustomData( $custom_data );
|
|
}
|
|
return $event;
|
|
}
|
|
|
|
/**
|
|
* Prepares event data for processing.
|
|
*
|
|
* This function takes an input array of events, normalizes each event,
|
|
* and encodes the data into a base64 JSON string. If the input data
|
|
* contains only a single event, it is wrapped in an array for consistency.
|
|
*
|
|
* @param array $data The input data containing events and the number
|
|
* of events. The format is expected to be an array
|
|
* where the first element is the event(s) and the
|
|
* second element is the number of events.
|
|
*
|
|
* @return array An associative array containing 'event_data', a base64
|
|
* encoded JSON string of the events, and 'num_events',
|
|
* the number of events processed.
|
|
*
|
|
* @throws \Exception If there was an preprocessing error.
|
|
*/
|
|
protected function prepare_data( $data ) {
|
|
try {
|
|
if ( ! empty( $data ) ) {
|
|
$num_events = $data[1];
|
|
$events = $data[0];
|
|
if ( 1 === $num_events ) {
|
|
$events = array( $events );
|
|
}
|
|
$events_as_array = array();
|
|
foreach ( $events as $event ) {
|
|
$events_as_array[] = $event->normalize();
|
|
}
|
|
return array(
|
|
'event_data' =>
|
|
base64_encode( // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
|
|
wp_json_encode( $events_as_array )
|
|
),
|
|
'num_events' => $data[1],
|
|
);
|
|
}
|
|
} catch ( \Exception $ex ) {
|
|
throw $ex;
|
|
}
|
|
|
|
return array();
|
|
}
|
|
|
|
/**
|
|
* Process the events sent via the AJAX action.
|
|
*
|
|
* This function decodes the JSON string sent in the $_POST['event_data']
|
|
* and processes the events as an array of Event objects.
|
|
*
|
|
* @see FacebookServerSideEvent::send()
|
|
*
|
|
* @throws \Exception If there was an preprocessing error.
|
|
*/
|
|
protected function run_action() {
|
|
$num_events = isset( $_POST['num_events'] ) ? // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
|
sanitize_text_field(
|
|
wp_unslash( $_POST['num_events'] ) // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
|
) : null;
|
|
if ( 0 === $num_events ) {
|
|
return;
|
|
}
|
|
$events_as_array = json_decode(
|
|
base64_decode( // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
|
isset( $_POST['event_data'] ) // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
|
? $_POST['event_data'] : null // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
|
),
|
|
true
|
|
);
|
|
if ( ! $events_as_array ) {
|
|
return;
|
|
}
|
|
$events = array();
|
|
foreach ( $events_as_array as $event_as_array ) {
|
|
$event = $this->convert_array_to_event( $event_as_array );
|
|
$events[] = $event;
|
|
}
|
|
FacebookServerSideEvent::send( $events );
|
|
}
|
|
}
|