: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
defined('ABSPATH') || exit;
abstract class Builder_Optin_Service {
public static abstract function get_id():string;
public static abstract function get_label(): string;
* Module options, displayed in the Optin module form
public static abstract function get_settings():array;
* Provider options that are not unique to each module
* These are displayed in the Builder settings page
public static abstract function get_global_options():array;
* Retrieves the $fields_args from module and determines if there is valid form to show.
public static abstract function validate_data(array $fields_args):string;
protected static function init():void{}
* Checks whether this service is available.
public static function is_available():bool {
* Creates or returns an instance of this class.
public static function run():void {
add_action('wp_ajax_tb_optin_clear_cache', array(__CLASS__, 'ajax_clear_cache'));
add_action('wp_ajax_tb_optin_subscribe', array(__CLASS__, 'ajax_subscribe'));
add_action('wp_ajax_nopriv_tb_optin_subscribe', array(__CLASS__, 'ajax_subscribe'));
* Initialize data providers for the module
* Other plugins or themes can extend or add to this list
* by using the "builder_optin_services" filter.
private static function init_providers($type = 'all'):array {
'mailchimp' => 'Builder_Optin_Service_MailChimp',
'activecampaign' => 'Builder_Optin_Service_ActiveCampaign',
'convertkit' => 'Builder_Optin_Service_ConvertKit',
'getresponse' => 'Builder_Optin_Service_GetResponse',
'mailerlite' => 'Builder_Optin_Service_MailerLite',
'newsletter' => 'Builder_Optin_Service_Newsletter',
$providersList = apply_filters('builder_optin_services',$coreList);
if ( ! isset( $providersList[ $type ] ) ) {
$providersList = array( $type => $providersList[ $type ] );
$dir = __DIR__ .DIRECTORY_SEPARATOR;
foreach ( $providersList as $id => $provider ) {
$path=isset($coreList[$id])?($dir . $id):$id;
include_once( $path . '.php' );
if ( class_exists( $provider,false ) && $provider::is_available() ) {
$providers[ $id ] = $provider;
* Helper function to retrieve list of providers or provider instance
public static function get_providers(string $id='all',bool $is_v75=false) {
$providers= self::init_providers($id);
if(!isset($providers[ $id ])){
$class=$providers[ $id ];
return $is_v75===true?$class:new $class();
* Handles the Ajax request for subscription form
* Hooked to wp_ajax_tb_optin_subscribe
public static function ajax_subscribe() {
if (!isset($_POST['tb_optin_provider'], $_POST['tb_optin_fname'], $_POST['tb_optin_lname'], $_POST['tb_optin_email'], $_POST['tb_post_id'], $_POST['tb_element_id'])) {
wp_send_json_error(array('error' => __('Required fields are empty.', 'themify')));
$module = Themify_Builder_Component_Module::get_element_settings( $_POST['tb_post_id'], $_POST['tb_element_id'] );
if ( isset( $module['captcha'] ) ) {
$provider = $module['captcha'] === 'on' ? 'recaptcha' : 'hcaptcha';
$provider === 'recaptcha' && ! empty( $_POST['g-recaptcha-response'] ) ? $_POST['g-recaptcha-response']
: ( $provider === 'hcaptcha' && ! empty( $_POST['h-captcha-response'] ) ? $_POST['h-captcha-response'] : null );
$result = Themify_Builder_Model::validate_captcha( $provider, $response );
if ( is_wp_error( $result ) ) {
wp_send_json_error( [ 'error' => $result->get_error_message() ] );
wp_send_json_error( [ 'error' => __( 'Empty Captcha response.', 'themify' ) ] );
foreach ($_POST as $key => $value) {
// remove "tb_optin_" prefix from the $_POST data
$key = preg_replace('/^tb_optin_/', '', $key);
$data[$key] = sanitize_text_field(trim($value));
if ($provider = self::get_providers($data['provider'],true)) {
$result = $provider::subscribe($data);
if (is_wp_error($result)) {
wp_send_json_error(array('error' => $result->get_error_message()));
wp_send_json_success(array(
/* send name and email in GET, these may come useful when building the page that the visitor will be redirected to */
'redirect' => add_query_arg(array(
'fname' => $data['fname'],
'lname' => $data['lname'],
'email' => $data['email'],
wp_send_json_error(array('error' => __('Unknown provider.', 'themify')));
public static function ajax_clear_cache() {
check_ajax_referer('tf_nonce', 'nonce');
if (current_user_can('manage_options') && Themify_Access_Role::check_access_backend()) {
$providers = self::get_providers('all',true);
foreach ($providers as $id => $instance) {
$instance::clear_cache();
* Returns the value of a setting
protected static function get($id, $default = null):?string {
if ($value = themify_builder_get("setting-{$id}", "setting-{$id}")) {
protected static function get_api_key():?string {
return static::get(static::get_id() . '_key');
* Action to perform when Clear Cache is requested
public static function clear_cache():void {
$key = static::get_api_key();
delete_transient('tb_optin_' . $id . '_' . md5($key));
Themify_Storage::delete('tb_optin_' . $id . '_' . $key);
protected static function get_lists(?string $key = '') {
$key = static::get_api_key();
return sprintf(__('%s API Key is missing.', 'themify'), static::get_label());
$cache_key = 'tb_optin_' . $id . '_' . $key;
if (false === ( $lists = Themify_Storage::get($cache_key) )) {
delete_transient('tb_optin_' . $id . '_' . md5($key));
if (is_wp_error(( $data = static::request_list()))) {
return $data->get_error_message();
Themify_Storage::set($cache_key, $data, MONTH_IN_SECONDS);
return json_decode($lists, true);
* Subscribe visitor to the mailing list
* @param $args array( 'fname', 'lname', 'email' )
* it also includes options from get_options() method with their values.
public static function subscribe(array $args) {
Builder_Optin_Service::run();