: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
'companyOrPersonMessage' => \apply_filters( 'wpseo_knowledge_graph_setting_msg', '' ),
'currentUserId' => \get_current_user_id(),
'canCreateUsers' => \current_user_can( 'create_users' ),
'canCreatePages' => \current_user_can( 'edit_pages' ),
'canEditUsers' => \current_user_can( 'edit_users' ),
'canManageOptions' => \current_user_can( 'manage_options' ),
'userLocale' => \str_replace( '_', '-', \get_user_locale() ),
'pluginUrl' => \plugins_url( '', \WPSEO_FILE ),
'showForceRewriteTitlesSetting' => ! \current_theme_supports( 'title-tag' ) && ! ( \function_exists( 'wp_is_block_theme' ) && \wp_is_block_theme() ),
'upsellSettings' => $this->get_upsell_settings(),
'siteRepresentsPerson' => $this->get_site_represents_person( $settings ),
'siteBasicsPolicies' => $this->get_site_basics_policies( $settings ),
* Retrieves the currently represented person.
* @param array $settings The settings.
* @return array The currently represented person's ID and name.
protected function get_site_represents_person( $settings ) {
if ( isset( $settings['wpseo_titles']['company_or_person_user_id'] ) ) {
$person['id'] = $settings['wpseo_titles']['company_or_person_user_id'];
$user = \get_userdata( $person['id'] );
if ( $user instanceof WP_User ) {
$person['name'] = $user->get( 'display_name' );
* @param array $settings The settings.
* @return array The policy data.
private function get_site_basics_policies( $settings ) {
$policies = $this->maybe_add_policy( $policies, $settings['wpseo_titles']['publishing_principles_id'], 'publishing_principles_id' );
$policies = $this->maybe_add_policy( $policies, $settings['wpseo_titles']['ownership_funding_info_id'], 'ownership_funding_info_id' );
$policies = $this->maybe_add_policy( $policies, $settings['wpseo_titles']['actionable_feedback_policy_id'], 'actionable_feedback_policy_id' );
$policies = $this->maybe_add_policy( $policies, $settings['wpseo_titles']['corrections_policy_id'], 'corrections_policy_id' );
$policies = $this->maybe_add_policy( $policies, $settings['wpseo_titles']['ethics_policy_id'], 'ethics_policy_id' );
$policies = $this->maybe_add_policy( $policies, $settings['wpseo_titles']['diversity_policy_id'], 'diversity_policy_id' );
$policies = $this->maybe_add_policy( $policies, $settings['wpseo_titles']['diversity_staffing_report_id'], 'diversity_staffing_report_id' );
* Adds policy data if it is present.
* @param array $policies The existing policy data.
* @param int $policy The policy id to check.
* @param string $key The option key name.
* @return array<int,string> The policy data.
private function maybe_add_policy( $policies, $policy, $key ) {
'name' => \__( 'None', 'wordpress-seo' ),
if ( isset( $policy ) && \is_int( $policy ) ) {
$policy_array['id'] = $policy;
$post = \get_post( $policy );
if ( $post instanceof WP_Post ) {
if ( $post->post_status !== 'publish' || $post->post_password !== '' ) {
$policy_array['name'] = $post->post_title;
$policies[ $key ] = $policy_array;
* Returns settings for the Call to Buy (CTB) buttons.
* @return array<string> The array of CTB settings.
public function get_upsell_settings() {
'actionId' => 'load-nfd-ctb',
'premiumCtbId' => 'f6a84663-465f-4cb5-8ba5-f7a6d72224b2',
* Retrieves the default setting values.
* These default values are currently being used in the UI for dummy fields.
* Dummy fields should not expose or reflect the actual data.
* @return array The default setting values.
protected function get_default_setting_values() {
foreach ( WPSEO_Options::$options as $option_name => $instance ) {
if ( \in_array( $option_name, self::ALLOWED_OPTION_GROUPS, true ) ) {
$option_instance = WPSEO_Options::get_option_instance( $option_name );
$defaults[ $option_name ] = ( $option_instance ) ? $option_instance->get_defaults() : [];
foreach ( self::WP_OPTIONS as $option_name ) {
$defaults[ $option_name ] = '';
// Remove disallowed settings.
foreach ( self::DISALLOWED_SETTINGS as $option_name => $disallowed_settings ) {
foreach ( $disallowed_settings as $disallowed_setting ) {
unset( $defaults[ $option_name ][ $disallowed_setting ] );
if ( \defined( 'WPSEO_LOCAL_FILE' ) ) {
$defaults = $this->get_defaults_from_local_seo( $defaults );
* Retrieves the organization schema values from Local SEO for defaults in Site representation fields.
* Specifically for the org-vat-id, org-tax-id, org-email and org-phone options.
* @param array<string|int|bool> $defaults The settings defaults.
* @return array<string|int|bool> The settings defaults with local seo overides.
protected function get_defaults_from_local_seo( $defaults ) {
$local_options = \get_option( 'wpseo_local' );
$multiple_locations = $local_options['use_multiple_locations'];
$same_organization = $local_options['multiple_locations_same_organization'];
$shared_info = $local_options['multiple_locations_shared_business_info'];
if ( $multiple_locations !== 'on' || ( $multiple_locations === 'on' && $same_organization === 'on' && $shared_info === 'on' ) ) {
$defaults['wpseo_titles']['org-vat-id'] = $local_options['location_vat_id'];
$defaults['wpseo_titles']['org-tax-id'] = $local_options['location_tax_id'];
$defaults['wpseo_titles']['org-email'] = $local_options['location_email'];
$defaults['wpseo_titles']['org-phone'] = $local_options['location_phone'];
if ( \wpseo_has_primary_location() ) {
$primary_location = $local_options['multiple_locations_primary_location'];
'is_overridden' => '_wpseo_is_overridden_business_phone',
'value' => '_wpseo_business_phone',
'is_overridden' => '_wpseo_is_overridden_business_email',
'value' => '_wpseo_business_email',
'is_overridden' => '_wpseo_is_overridden_business_tax_id',
'value' => '_wpseo_business_tax_id',
'is_overridden' => '_wpseo_is_overridden_business_vat_id',
'value' => '_wpseo_business_vat_id',
foreach ( $location_keys as $key => $meta_keys ) {
$is_overridden = ( $shared_info === 'on' ) ? \get_post_meta( $primary_location, $meta_keys['is_overridden'], true ) : false;
if ( $is_overridden === 'on' || $shared_info !== 'on' ) {
$post_meta_value = \get_post_meta( $primary_location, $meta_keys['value'], true );
$defaults['wpseo_titles'][ $key ] = ( $post_meta_value ) ? $post_meta_value : '';
* Retrieves the settings and their values.
* @param array $default_setting_values The default setting values.
* @return array The settings.
protected function get_settings( $default_setting_values ) {
foreach ( WPSEO_Options::$options as $option_name => $instance ) {
if ( \in_array( $option_name, self::ALLOWED_OPTION_GROUPS, true ) ) {
$settings[ $option_name ] = \array_merge( $default_setting_values[ $option_name ], WPSEO_Options::get_option( $option_name ) );
foreach ( self::WP_OPTIONS as $option_name ) {
$settings[ $option_name ] = \get_option( $option_name );
// Remove disallowed settings.
foreach ( self::DISALLOWED_SETTINGS as $option_name => $disallowed_settings ) {
foreach ( $disallowed_settings as $disallowed_setting ) {
unset( $settings[ $option_name ][ $disallowed_setting ] );
* Transforms setting values.
* @param array $settings The settings.
* @return array The settings.
protected function transform_settings( $settings ) {
if ( isset( $settings['wpseo_titles']['breadcrumbs-sep'] ) ) {
* The breadcrumbs separator default value is the HTML entity `»`.
* Which does not get decoded in our JS, while it did in our Yoast form. Decode it here as an exception.
$settings['wpseo_titles']['breadcrumbs-sep'] = \html_entity_decode(
$settings['wpseo_titles']['breadcrumbs-sep'],
( \ENT_NOQUOTES | \ENT_HTML5 ),
* Decode some WP options.
$settings['blogdescription'] = \html_entity_decode(
$settings['blogdescription'],
( \ENT_NOQUOTES | \ENT_HTML5 ),
* Retrieves the disabled settings.
* @param array $settings The settings.
* @return array The settings.
protected function get_disabled_settings( $settings ) {
$site_language = $this->language_helper->get_language();
foreach ( WPSEO_Options::$options as $option_name => $instance ) {
if ( ! \in_array( $option_name, self::ALLOWED_OPTION_GROUPS, true ) ) {
$disabled_settings[ $option_name ] = [];
$option_instance = WPSEO_Options::get_option_instance( $option_name );
if ( $option_instance === false ) {
foreach ( $settings[ $option_name ] as $setting_name => $setting_value ) {
if ( $option_instance->is_disabled( $setting_name ) ) {
$disabled_settings[ $option_name ][ $setting_name ] = 'network';
// Remove disabled on multisite settings.
foreach ( self::DISABLED_ON_MULTISITE_SETTINGS as $option_name => $disabled_ms_settings ) {
if ( \array_key_exists( $option_name, $disabled_settings ) ) {
foreach ( $disabled_ms_settings as $disabled_ms_setting ) {
$disabled_settings[ $option_name ][ $disabled_ms_setting ] = 'multisite';
if ( \array_key_exists( 'wpseo', $disabled_settings ) && ! $this->language_helper->has_inclusive_language_support( $site_language ) ) {
$disabled_settings['wpseo']['inclusive_language_analysis_active'] = 'language';
return $disabled_settings;
* Retrieves the replacement variables.
* @return array The replacement variables.
protected function get_replacement_variables() {
$recommended_replace_vars = new WPSEO_Admin_Recommended_Replace_Vars();
$specific_replace_vars = new WPSEO_Admin_Editor_Specific_Replace_Vars();
$replacement_variables = $this->replace_vars->get_replacement_variables_with_labels();
'variables' => $replacement_variables,
'recommended' => $recommended_replace_vars->get_recommended_replacevars(),
'specific' => $specific_replace_vars->get(),
'shared' => $specific_replace_vars->get_generic( $replacement_variables ),
* @param array $post_types The post types.
* @return array The schema.
protected function get_schema( array $post_types ) {
foreach ( $this->schema_types->get_article_type_options() as $article_type ) {
$schema['articleTypes'][ $article_type['value'] ] = [
'label' => $article_type['name'],
'value' => $article_type['value'],
foreach ( $this->schema_types->get_page_type_options() as $page_type ) {
$schema['pageTypes'][ $page_type['value'] ] = [
'label' => $page_type['name'],
'value' => $page_type['value'],
$schema['articleTypeDefaults'] = [];
$schema['pageTypeDefaults'] = [];
foreach ( $post_types as $name => $post_type ) {
$schema['articleTypeDefaults'][ $name ] = WPSEO_Options::get_default( 'wpseo_titles', "schema-article-type-$name" );
$schema['pageTypeDefaults'][ $name ] = WPSEO_Options::get_default( 'wpseo_titles', "schema-page-type-$name" );
* Transforms the post types, to represent them.
* @param WP_Post_Type[] $post_types The WP_Post_Type array to transform.
* @return array The post types.
protected function transform_post_types( $post_types ) {
$new_post_types = $this->options->get( 'new_post_types', [] );
foreach ( $post_types as $post_type ) {
$transformed[ $post_type->name ] = [
'name' => $post_type->name,
'route' => $this->get_route( $post_type->name, $post_type->rewrite, $post_type->rest_base ),
'label' => $post_type->label,
'singularLabel' => $post_type->labels->singular_name,
'hasArchive' => $this->post_type_helper->has_archive( $post_type ),
'hasSchemaArticleType' => $this->article_helper->is_article_post_type( $post_type->name ),
'menuPosition' => $post_type->menu_position,
'isNew' => \in_array( $post_type->name, $new_post_types, true ),
\uasort( $transformed, [ $this, 'compare_post_types' ] );
* Compares two post types.
* @param array $a The first post type.
* @param array $b The second post type.
protected function compare_post_types( $a, $b ) {
if ( $a['menuPosition'] === null && $b['menuPosition'] !== null ) {
if ( $a['menuPosition'] !== null && $b['menuPosition'] === null ) {
if ( $a['menuPosition'] === null && $b['menuPosition'] === null ) {
// No position specified, order alphabetically by label.
return \strnatcmp( $a['label'], $b['label'] );
return ( ( $a['menuPosition'] < $b['menuPosition'] ) ? -1 : 1 );
* Transforms the taxonomies, to represent them.
* @param WP_Taxonomy[] $taxonomies The WP_Taxonomy array to transform.
* @param string[] $post_type_names The post type names.
* @return array The taxonomies.
protected function transform_taxonomies( $taxonomies, $post_type_names ) {
$new_taxonomies = $this->options->get( 'new_taxonomies', [] );
foreach ( $taxonomies as $taxonomy ) {
$transformed[ $taxonomy->name ] = [
'name' => $taxonomy->name,
'route' => $this->get_route( $taxonomy->name, $taxonomy->rewrite, $taxonomy->rest_base ),
'label' => $taxonomy->label,
'showUi' => $taxonomy->show_ui,
'singularLabel' => $taxonomy->labels->singular_name,
'postTypes' => \array_filter(
static function ( $object_type ) use ( $post_type_names ) {
return \in_array( $object_type, $post_type_names, true );
'isNew' => \in_array( $taxonomy->name, $new_taxonomies, true ),
static function ( $a, $b ) {
return \strnatcmp( $a['label'], $b['label'] );
* Gets the route from a name, rewrite and rest_base.
* @param string $name The name.
* @param array $rewrite The rewrite data.
* @param string $rest_base The rest base.
* @return string The route.
protected function get_route( $name, $rewrite, $rest_base ) {
if ( isset( $rewrite['slug'] ) ) {
$route = $rewrite['slug'];
if ( ! empty( $rest_base ) ) {
// Always strip leading slashes.
while ( \substr( $route, 0, 1 ) === '/' ) {
$route = \substr( $route, 1 );
return \rawurlencode( $route );
* Retrieves the fallbacks.
* @return array The fallbacks.
protected function get_fallbacks() {
$site_logo_id = \get_option( 'site_logo' );
$site_logo_id = \get_theme_mod( 'custom_logo' );
'siteLogoId' => $site_logo_id,