: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
protected function render_hidden_fields() {
// Wrap in a form with an action and post_id for the submit.
'<form id="yoast-form" method="post" action="%1$s"><input type="hidden" name="action" value="wpseo_elementor_save" /><input type="hidden" id="post_ID" name="post_id" value="%2$s" />',
\esc_url( \admin_url( 'admin-ajax.php' ) ),
\esc_attr( $this->get_metabox_post()->ID )
\wp_nonce_field( 'wpseo_elementor_save', '_wpseo_elementor_nonce' );
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Reason: Meta_Fields_Presenter->present is considered safe.
echo new Meta_Fields_Presenter( $this->get_metabox_post(), 'general' );
if ( $this->is_advanced_metadata_enabled ) {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Reason: Meta_Fields_Presenter->present is considered safe.
echo new Meta_Fields_Presenter( $this->get_metabox_post(), 'advanced' );
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Reason: Meta_Fields_Presenter->present is considered safe.
echo new Meta_Fields_Presenter( $this->get_metabox_post(), 'schema', $this->get_metabox_post()->post_type );
if ( $this->social_is_enabled ) {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Reason: Meta_Fields_Presenter->present is considered safe.
echo new Meta_Fields_Presenter( $this->get_metabox_post(), 'social' );
'<input type="hidden" id="%1$s" name="%1$s" value="%2$s" />',
\esc_attr( WPSEO_Meta::$form_prefix . 'slug' ),
\esc_attr( $this->get_post_slug() )
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output should be escaped in the filter.
echo \apply_filters( 'wpseo_elementor_hidden_fields', '' );
* Returns the slug for the post being edited.
protected function get_post_slug() {
$post = $this->get_metabox_post();
// In case get_metabox_post returns null for whatever reason.
if ( ! $post instanceof WP_Post ) {
// Drafts might not have a post_name unless the slug has been manually changed.
// In this case we get it using get_sample_permalink.
if ( ! $post->post_name ) {
$sample = \get_sample_permalink( $post );
// Since get_sample_permalink runs through filters, ensure that it has the expected return value.
if ( \is_array( $sample ) && \count( $sample ) === 2 && \is_string( $sample[1] ) ) {
* Returns post in metabox context.
protected function get_metabox_post() {
if ( $this->post !== null ) {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
if ( isset( $_GET['post'] ) && \is_numeric( $_GET['post'] ) ) {
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized,WordPress.Security.NonceVerification.Recommended -- Reason: No sanitization needed because we cast to an integer,We are not processing form information.
$post = (int) \wp_unslash( $_GET['post'] );
if ( ! empty( $post ) ) {
$this->post = \get_post( $post );
if ( isset( $GLOBALS['post'] ) ) {
$this->post = $GLOBALS['post'];
* Passes variables to js for use with the post-scraper.
protected function get_metabox_script_data() {
if ( \is_object( $this->get_metabox_post() ) ) {
$permalink = \get_sample_permalink( $this->get_metabox_post()->ID );
$permalink = $permalink[0];
$post_formatter = new WPSEO_Metabox_Formatter(
new WPSEO_Post_Metabox_Formatter( $this->get_metabox_post(), [], $permalink )
$values = $post_formatter->get_values();
/** This filter is documented in admin/filters/class-cornerstone-filter.php. */
$post_types = \apply_filters( 'wpseo_cornerstone_post_types', \YoastSEO()->helpers->post_type->get_accessible_post_types() );
if ( $values['cornerstoneActive'] && ! \in_array( $this->get_metabox_post()->post_type, $post_types, true ) ) {
$values['cornerstoneActive'] = false;
$values['elementorMarkerStatus'] = $this->is_highlighting_available() ? 'enabled' : 'hidden';
* Checks whether the highlighting functionality is available for Elementor:
* - in Free it's always available (as an upsell).
* - in Premium it's available as long as the version is 21.8-RC0 or above.
* @return bool Whether the highlighting functionality is available.
private function is_highlighting_available() {
$is_premium = \YoastSEO()->helpers->product->is_premium();
$premium_version = \YoastSEO()->helpers->product->get_premium_version();
return ! $is_premium || \version_compare( $premium_version, '21.8-RC0', '>=' );
* Prepares the replace vars for localization.
* @return array Replace vars.
protected function get_replace_vars() {
$cached_replacement_vars = [];
foreach ( $vars_to_cache as $var ) {
$cached_replacement_vars[ $var ] = \wpseo_replace_vars( '%%' . $var . '%%', $this->get_metabox_post() );
// Merge custom replace variables with the WordPress ones.
return \array_merge( $cached_replacement_vars, $this->get_custom_replace_vars( $this->get_metabox_post() ) );
* Prepares the recommended replace vars for localization.
* @return array Recommended replacement variables.
protected function get_recommended_replace_vars() {
$recommended_replace_vars = new WPSEO_Admin_Recommended_Replace_Vars();
// What is recommended depends on the current context.
$post_type = $recommended_replace_vars->determine_for_post( $this->get_metabox_post() );
return $recommended_replace_vars->get_recommended_replacevars_for( $post_type );
* Returns the list of replace vars that should be hidden inside the editor.
* @return string[] The hidden replace vars.
protected function get_hidden_replace_vars() {
return ( new WPSEO_Replace_Vars() )->get_hidden_replace_vars();
* Gets the custom replace variables for custom taxonomies and fields.
* @param WP_Post $post The post to check for custom taxonomies and fields.
* @return array Array containing all the replacement variables.
protected function get_custom_replace_vars( $post ) {
'custom_fields' => $this->get_custom_fields_replace_vars( $post ),
'custom_taxonomies' => $this->get_custom_taxonomies_replace_vars( $post ),
* Gets the custom replace variables for custom taxonomies.
* @param WP_Post $post The post to check for custom taxonomies.
* @return array Array containing all the replacement variables.
protected function get_custom_taxonomies_replace_vars( $post ) {
$taxonomies = \get_object_taxonomies( $post, 'objects' );
$custom_replace_vars = [];
foreach ( $taxonomies as $taxonomy_name => $taxonomy ) {
if ( \is_string( $taxonomy ) ) { // If attachment, see https://core.trac.wordpress.org/ticket/37368 .
$taxonomy_name = $taxonomy;
$taxonomy = \get_taxonomy( $taxonomy_name );
if ( $taxonomy->_builtin && $taxonomy->public ) {
$custom_replace_vars[ $taxonomy_name ] = [
'name' => $taxonomy->name,
'description' => $taxonomy->description,
return $custom_replace_vars;
* Gets the custom replace variables for custom fields.
* @param WP_Post $post The post to check for custom fields.
* @return array Array containing all the replacement variables.
protected function get_custom_fields_replace_vars( $post ) {
$custom_replace_vars = [];
// If no post object is passed, return the empty custom_replace_vars array.
if ( ! \is_object( $post ) ) {
return $custom_replace_vars;
$custom_fields = \get_post_custom( $post->ID );
// Simply concatenate all fields containing replace vars so we can handle them all with a single regex find.
$replace_vars_fields = \implode(
\YoastSEO()->meta->for_post( $post->ID )->presentation->title,
\YoastSEO()->meta->for_post( $post->ID )->presentation->meta_description,
\preg_match_all( '/%%cf_([A-Za-z0-9_]+)%%/', $replace_vars_fields, $matches );
$fields_to_include = $matches[1];
foreach ( $custom_fields as $custom_field_name => $custom_field ) {
// Skip private custom fields.
if ( \substr( $custom_field_name, 0, 1 ) === '_' ) {
// Skip custom fields that are not used, new ones will be fetched dynamically.
if ( ! \in_array( $custom_field_name, $fields_to_include, true ) ) {
// Skip custom field values that are serialized.
if ( \is_serialized( $custom_field[0] ) ) {
$custom_replace_vars[ $custom_field_name ] = $custom_field[0];
return $custom_replace_vars;
* Determines the scope based on the post type.
* This can be used by the replacevar plugin to determine if a replacement needs to be executed.
* @return string String describing the current scope.
protected function determine_scope() {
if ( $this->get_metabox_post()->post_type === 'page' ) {
* Determines whether or not the current post type has registered taxonomies.
* @return bool Whether the current post type has taxonomies.
protected function current_post_type_has_taxonomies() {
$post_taxonomies = \get_object_taxonomies( $this->get_metabox_post()->post_type );
return ! empty( $post_taxonomies );
* Returns an array with shortcode tags for all registered shortcodes.
protected function get_valid_shortcode_tags() {
foreach ( $GLOBALS['shortcode_tags'] as $tag => $description ) {
$shortcode_tags[] = $tag;