: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
$default_general_toggles = array(
'title' => et_builder_i18n( 'Admin Label' ),
$this->_add_settings_modal_toggles( 'general', $default_general_toggles );
$this->_add_settings_modal_toggles( 'custom_css', array(
'title' => et_builder_i18n( 'Visibility' ),
if ( ! isset( $i18n['toggles'] ) ) {
// phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
$i18n['toggles'] = array(
'scroll' => esc_html__( 'Scroll Effects', 'et_builder' ),
$this->_add_settings_modal_toggles( 'custom_css', array(
'scroll_effects' => array(
'title' => $i18n['toggles']['scroll'],
$this->main_tabs = $this->get_main_tabs();
$this->custom_css_tab = isset( $this->custom_css_tab ) ? $this->custom_css_tab : true;
self::$modules[ $this->slug ] = $this;
$post_types = ! empty( $this->post_types ) ? $this->post_types : et_builder_get_builder_post_types();
// all modules should be assigned for et_pb_layout post type to work in the library
if ( ! in_array( 'et_pb_layout', $post_types ) ) {
$post_types[] = 'et_pb_layout';
$this->post_types = apply_filters( 'et_builder_module_post_types', $post_types, $this->slug, $this->post_types );
foreach ( $this->post_types as $post_type ) {
if ( ! in_array( $post_type, $this->post_types ) ) {
$this->register_post_type( $post_type );
if ( ! isset( self::$_module_slugs_by_post_type[ $post_type ] ) ) {
self::$_module_slugs_by_post_type[ $post_type ] = array();
if ( ! in_array( $this->slug, self::$_module_slugs_by_post_type[ $post_type ] ) ) {
self::$_module_slugs_by_post_type[ $post_type ][] = $this->slug;
if ( isset( $this->additional_shortcode ) && ! in_array( $this->additional_shortcode, self::$_module_slugs_by_post_type[ $post_type ] ) ) {
self::$_module_slugs_by_post_type[ $post_type ][] = $this->additional_shortcode;
if ( isset( $this->additional_shortcode_slugs ) ) {
foreach ( $this->additional_shortcode_slugs as $additional_shortcode_slug ) {
if ( ! in_array( $additional_shortcode_slug, self::$_module_slugs_by_post_type[ $post_type ] ) ) {
self::$_module_slugs_by_post_type[ $post_type ][] = $additional_shortcode_slug;
if ( 'child' === $this->type ) {
self::$child_modules[ $post_type ][ $this->slug ] = $this;
if ( isset( $this->additional_shortcode_slugs ) ) {
foreach( $this->additional_shortcode_slugs as $additional_slug ) {
self::$child_modules[ $post_type ][ $additional_slug ] = $this;
self::$parent_modules[ $post_type ][ $this->slug ] = $this;
// WooCommerce modules data only deterimine whether a module is WooCommerce modules or not
// it doesn't need to be aware whether it is available in certain CPT or not.
if ( $this->_is_woocommerce_module ) {
self::$woocommerce_modules[] = $this->slug;
if ( ! isset( $this->no_render ) ) {
$shortcode_slugs = array( $this->slug );
if ( ! empty( $this->additional_shortcode_slugs ) ) {
$shortcode_slugs = array_merge( $shortcode_slugs, $this->additional_shortcode_slugs );
foreach ( $shortcode_slugs as $shortcode_slug ) {
add_shortcode( $shortcode_slug, array( $this, '_render' ) );
if ( isset( $this->additional_shortcode ) ) {
add_shortcode( $this->additional_shortcode, array( $this, 'additional_render' ) );
if ( isset( $this->icon ) ) {
self::$_->array_set( self::$module_icons, "{$this->slug}.icon", $this->icon );
if ( isset( $this->icon_path ) ) {
self::$_->array_set( self::$module_icons, "{$this->slug}.icon_path", $this->icon_path );
// Push module's help videos to all help videos array if there's any
if ( ! empty( $this->help_videos ) ) {
// Automatically add design tab and library tutorial. DRY
if ( 'et_pb_column' !== $this->slug ) {
// Adding next tabs (design & tab) helps
'name' => esc_html__( 'Design Settings and Advanced Module Settings', 'et_builder' ),
if ( in_array( $this->slug, array( 'et_pb_row', 'et_pb_row_inner' ) ) ) {
$next_tabs_help['name'] = esc_html__( 'Design Settings and Advanced Row Settings', 'et_builder' );
if ( 'et_pb_section' === $this->slug ) {
$next_tabs_help['name'] = esc_html__( 'Design Settings and Advanced Section Settings', 'et_builder' );
$this->help_videos[] = $next_tabs_help;
// Adding Divi Library helps
$this->help_videos[] = array(
'name' => esc_html__( 'Saving and loading from the library', 'et_builder' ),
self::$module_help_videos[ $this->slug ] = $this->help_videos;
// Push module slug if this module has content option. These modules' content option need
// to be autop-ed during saving process to avoid unstyled body content in Divi Builder Plugin due
// to content not having <p> tag because it doesn't wrapped by newline during saving process
if ( ! $this->use_raw_content && ! $this->child_slug && 'tiny_mce' === self::$_->array_get( $this->get_fields(), 'content.type' ) ) {
self::$has_content_modules[] = $this->slug;
public function __call( $name, $args ) {
$class = get_class( $this );
$message = "You're Doing It Wrong!";
$is_deprecated = array_key_exists( $name, self::$_deprecations['methods'] );
$old_method_exists = method_exists( $this, $name );
if ( $old_method_exists && ! $is_deprecated ) {
// Inaccessible method (protected or private) that isn't deprecated
et_debug( "{$message} Attempted to call {$class}::{$name}() from out of scope.", 4, false );
$message .= " {$class}::{$name}()";
if ( ! $is_deprecated ) {
$message .= " doesn't exist.";
$message .= " is deprecated.";
$new_method = self::$_deprecations['methods'][ $name ];
if ( ! is_string( $new_method ) ) {
// Default value for a method that has no replacement.
} else if ( method_exists( $this, $new_method ) && ! $old_method_exists ) {
$message .= " Use {$class}::{$new_method}() instead.";
// @phpcs:ignore Generic.PHP.ForbiddenFunctions.Found
$value = call_user_func_array( array( $this, $new_method ), $args );
} else if ( $old_method_exists && function_exists( $new_method ) ) {
// New method is a function
$message .= " Use {$new_method}() instead.";
// @phpcs:ignore Generic.PHP.ForbiddenFunctions.Found
$value = call_user_func_array( $new_method, $args );
} else if ( $old_method_exists ) {
// Ensure that our current caller is not the same as the method we're about to call.
// as that would cause an infinite recursion situation. It happens when a child class
// method which has been deprecated calls itself on the parent class (using parent::)
// This also happens when we use $this->__call() to call a deprecated method from its
// replacement method so that a deprecation notice will be output.
$trace = debug_backtrace();
self::$_->array_get( $trace, '1.function' ),
self::$_->array_get( $trace, '2.function' ),
if ( ! in_array( $name, $callers ) ) {
$message .= " Use {$class}::{$new_method}() instead.";
// @phpcs:ignore Generic.PHP.ForbiddenFunctions.Found
$value = call_user_func_array( array( $this, $name ), $args );
et_debug( $message, 4, false );
public function &__get( $name ) {
$class = get_class( $this );
$message = "You're Doing It Wrong!";
$is_deprecated = array_key_exists( $name, self::$_deprecations['properties'] );
if ( property_exists( $this, $name ) && ! $is_deprecated ) {
// Inaccessible property (protected or private) that isn't deprecated
et_debug( "{$message} Attempted to access {$class}::\${$name} from out of scope.", 4, false );
$message .= " {$class}::\${$name}";
if ( ! $is_deprecated ) {
$message .= " doesn't exist.";
$should_set_value = true;
$message .= " is deprecated.";
$new_prop = self::$_deprecations['properties'][ $name ];
if ( $new_prop && is_string( $new_prop ) && property_exists( $this, $new_prop ) ) {
$message .= " Use {$class}::\${$new_prop} instead.";
$value = &$this->$new_prop;
} else if ( ! is_string( $new_prop ) || ! $new_prop ) {
$should_set_value = true;
if ( isset( $should_set_value ) ) {
// Create the property so we can return a reference to it which allows it to be
// used like this: $this->name[] = 'something'
et_debug( $message, 4, false );
public function __isset( $name ) {
$prop_name = array_key_exists( $name, self::$_deprecations['properties'] ) ? self::$_deprecations['properties'][ $name ] : $name;
if ( ! $prop_name || ! is_string( $prop_name ) ) {
return property_exists( $this, $prop_name );
public function __set( $name, $value ) {
$class = get_class( $this );
$message = "You're Doing It Wrong!";
$is_deprecated = array_key_exists( $name, self::$_deprecations['properties'] );
$property_exists = property_exists( $this, $name );
$has_replacement = $property_exists && is_string( self::$_deprecations['properties'][ $name ] ) && self::$_deprecations['properties'][ $name ];
if ( $property_exists && ! $is_deprecated ) {
// Inaccessible property (protected or private) that isn't deprecated
et_debug( "{$message} Attempted to access {$class}::\${$name} from out of scope.", 4, false );
if ( ( ! $property_exists && ! $is_deprecated ) || ! $has_replacement ) {
// Always allow setting values for properties that are undeclared
if ( ! $is_deprecated ) {
$message = " {$class}::\${$name} is deprecated.";
$replacement = self::$_deprecations['properties'][ $name ];
if ( $replacement && is_string( $replacement ) ) {
$message .= " Use {$class}::\${$replacement} instead.";
$this->$replacement = $value;
// Unset deprecated property so next time it's updated we process it again
et_debug( $message, 4, false );
private static function _is_official_module( $class_name ) {
$reflection = new ReflectionClass( $class_name );
$is_official = self::$_->includes( $reflection->getFileName(), ET_BUILDER_DIR_RESOLVED_PATH );
} catch( Exception $err ) {
private static function _is_woocommerce_module( $class_name ) {
$reflection = new ReflectionClass( $class_name );
$is_woocommerce = self::$_->includes( $reflection->getFileName(), ET_BUILDER_DIR_RESOLVED_PATH . '/module/woocommerce' );
} catch( Exception $err ) {
protected function _set_advanced_fields_config() {
$this->advanced_fields = $this->get_advanced_fields_config();
// 3rd-Party Backwards Compatability
if ( isset( $this->advanced_fields['custom_margin_padding'] ) ) {
$this->advanced_fields['margin_padding'] = $this->advanced_fields['custom_margin_padding'];
unset( $this->advanced_fields['custom_margin_padding'] );
* Get whether third party post interference should be respected.
* Current use case is for plugins like Toolset that render a
* loop within a layout which renders another layout for
* each post - in this case we must NOT override the
* current post so the loop works as expected.
protected static function _should_respect_post_interference() {
$post = ET_Post_Stack::get();
return null !== $post && get_the_ID() !== $post->ID;
* Retrieve the main query post id.
* Accounts for third party interference with the current post.
* @return integer|boolean
protected static function _get_main_post_id() {
if ( self::_should_respect_post_interference() ) {
return ET_Post_Stack::get_main_post_id();
* Retrieve Post ID from 1 of 4 sources depending on which exists:
* - $_POST['current_page']['id']
public static function get_current_post_id() {
// Getting correct post id in computed_callback request.
if ( wp_doing_ajax() && $post_id = self::$_->array_get( $_POST, 'current_page.id' ) ) {
return absint( $post_id );
if ( wp_doing_ajax() && isset( $_POST['et_post_id'] ) ) {
return absint( $_POST['et_post_id'] );
if ( isset( $_POST['post'] ) ) {
return absint( $_POST['post'] );
return self::_get_main_post_id();
* Retrieve Post ID from 1 of 3 sources depending on which exists:
public static function get_current_post_id_reverse() {
$post_id = self::_get_main_post_id();
// try to get post id from get_post_ID()
if ( false !== $post_id ) {
// get the post ID if loading data for VB
return isset( $_POST['et_post_id'] ) ? absint( $_POST['et_post_id'] ) : false; // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
// fallback to $_GET['post'] to cover the BB data loading
return isset( $_GET['post'] ) ? absint( $_GET['post'] ) : false; // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
* Get the current ID depending on the current request.
public function get_the_ID() {
return self::get_current_post_id_reverse();
* Setup the advanced styles manager
* @since 4.0 Made public.
* Before the styles manager was implemented, the advanced styles were output inline in the footer.
* That resulted in them being the last styles parsed by the browser, thus giving them higher
* priority than other styles on the page. With the styles manager, the advanced styles are
* enqueued at the very end of the <head>. This is for backwards compatibility (to maintain
* the same priority for the styles as before).}}
public static function setup_advanced_styles_manager( $post_id = 0 ) {
if ( 0 === $post_id && et_core_page_resource_is_singular() ) {
// It doesn't matter if post id is 0 because we're going to force inline styles.
$post_id = et_core_page_resource_get_the_ID();
$queried_object = get_queried_object();
if ( is_object( $queried_object ) && property_exists( $queried_object , 'term_id' ) ) {
$term_id = $queried_object->term_id;
$post_id = ! empty( $term_id ) ? $term_id : $post_id;
$is_preview = is_preview() || is_et_pb_preview();
$forced_in_footer = $post_id && et_builder_setting_is_on( 'et_pb_css_in_footer', $post_id );
$forced_inline = ! $post_id || $is_preview || $forced_in_footer || et_builder_setting_is_off( 'et_pb_static_css_file', $post_id ) || et_core_is_safe_mode_active() || ET_GB_Block_Layout::is_layout_block_preview();
$unified_styles = ! $forced_inline && ! $forced_in_footer;
$resource_owner = $unified_styles ? 'core' : 'builder';
$resource_slug = $unified_styles ? 'unified' : 'module-design';
$resource_slug .= ! empty( $term_id ) ? '-term' : '';
$resource_slug .= empty( $term_id ) && $unified_styles && et_builder_post_is_of_custom_post_type( $post_id ) ? '-cpt' : '';
$resource_slug = et_theme_builder_decorate_page_resource_slug( $post_id, $resource_slug );
// If the post is password protected and a password has not been provided yet,
// no content (including any custom style) will be printed.
// When static css file option is enabled this will result in missing styles.
if ( ! $forced_inline && post_password_required( $post_id ? $post_id : null ) ) {
// Don't let previews cause existing saved static css files to be modified.
$resource_slug .= '-preview';
$manager = et_core_page_resource_get( $resource_owner, $resource_slug, $post_id, 40 );
if ( ! $forced_inline && ! $forced_in_footer && $manager->has_file() ) {
// This post currently has a fully configured styles manager.