<?php
namespace Yoast\WP\Test_Helper;
use WPSEO_Utils;
/**
* Class to influence the Schema functionality of Yoast SEO (Premium).
*/
class Schema implements Integration {
/**
* Holds our option instance.
*
* @var Option
*/
private $option;
/**
* Class constructor.
*
* @param Option $option Our option array.
*/
public function __construct( Option $option ) {
$this->option = $option;
}
/**
* Adds the required hooks for this class.
*/
public function add_hooks() {
if ( $this->option->get( 'replace_schema_domain' ) === true ) {
\add_filter( 'wpseo_debug_json_data', [ $this, 'replace_domain' ] );
}
if ( $this->option->get( 'enable_schema_endpoint' ) === true ) {
\add_action( 'template_redirect', [ $this, 'send_json_ld' ] );
\add_action( 'init', [ $this, 'init_rewrite' ] );
}
switch ( $this->option->get( 'is_needed_breadcrumb' ) ) {
case 'show':
case 'hide':
\add_filter( 'wpseo_schema_needs_breadcrumb', [ $this, 'filter_is_needed_breadcrumb' ] );
break;
default:
\remove_filter( 'wpseo_schema_needs_breadcrumb', [ $this, 'filter_is_needed_breadcrumb' ] );
break;
}
switch ( $this->option->get( 'is_needed_webpage' ) ) {
case 'show':
case 'hide':
\add_filter( 'wpseo_schema_needs_webpage', [ $this, 'filter_is_needed_webpage' ] );
break;
default:
\remove_filter( 'wpseo_schema_needs_webpage', [ $this, 'filter_is_needed_webpage' ] );
break;
}
\add_action( 'admin_post_yoast_seo_test_schema', [ $this, 'handle_submit' ] );
}
/**
* Registers the schema endpoint if needed.
*/
public function init_rewrite() {
\add_rewrite_endpoint( 'schema', \EP_ALL );
}
/**
* Send the Yoast SEO Schema.
*/
public function send_json_ld() {
global $wp_query;
if ( ! isset( $wp_query->query_vars['schema'] ) ) {
return;
}
\header( 'Content-Type: application/ld+json' );
$url = \YoastSEO()->meta->for_current_page()->canonical;
if ( ! empty( $url ) ) {
\header( 'Link: <' . $url . '>; rel="canonical"' );
}
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- This is our self generated Schema, no need for escaping.
echo WPSEO_Utils::format_json_encode( \YoastSEO()->meta->for_current_page()->schema );
exit;
}
/**
* Retrieves the controls.
*
* @return string The HTML to use to render the controls.
*/
public function get_controls() {
$select_options = [
'none' => \esc_html__( 'Don\'t influence', 'yoast-test-helper' ),
'show' => \esc_html__( 'Always include', 'yoast-test-helper' ),
'hide' => \esc_html__( 'Never include', 'yoast-test-helper' ),
];
$output = Form_Presenter::create_select(
'is_needed_breadcrumb',
\esc_html__( 'Influence the Breadcrumb Graph piece: ', 'yoast-test-helper' ),
$select_options,
$this->option->get( 'is_needed_breadcrumb' )
);
$output .= Form_Presenter::create_select(
'is_needed_webpage',
\esc_html__( 'Influence the WebPage Graph piece: ', 'yoast-test-helper' ),
$select_options,
$this->option->get( 'is_needed_webpage' )
);
$output .= Form_Presenter::create_checkbox(
'replace_schema_domain',
\esc_html__( 'Replace .test domain name with example.com in Schema output.', 'yoast-test-helper' ),
$this->option->get( 'replace_schema_domain' )
);
$output .= Form_Presenter::create_checkbox(
'enable_schema_endpoint',
\sprintf(
/* translators: %1$ss is replaced by `<code>/schema/</code>`, %2$s is replaced by `<code>?schema</code>`. */
\esc_html__( 'Enable the Schema endpoint for every URL: suffix the URL with %1$s or %2$s to get the Schema for that URL, pretty printed.', 'yoast-test-helper' ),
'<code>/schema/</code>',
'<code>?schema</code>'
),
$this->option->get( 'enable_schema_endpoint' )
);
return Form_Presenter::get_html( \__( 'Schema', 'yoast-test-helper' ), 'yoast_seo_test_schema', $output );
}
/**
* Handles the form submit.
*
* @return void
*/
public function handle_submit() {
if ( \check_admin_referer( 'yoast_seo_test_schema' ) !== false ) {
$this->option->set( 'replace_schema_domain', isset( $_POST['replace_schema_domain'] ) );
$this->option->set( 'enable_schema_endpoint', isset( $_POST['enable_schema_endpoint'] ) );
}
if ( isset( $_POST['is_needed_breadcrumb'] ) ) {
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized,WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- validation is done in validate_submit.
$validated_is_needed_breadcrumb = $this->validate_submit( $_POST['is_needed_breadcrumb'] );
$this->option->set( 'is_needed_breadcrumb', $validated_is_needed_breadcrumb );
}
if ( isset( $_POST['is_needed_webpage'] ) ) {
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized,WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- validation is done in validate_submit.
$validated_is_needed_webpage = $this->validate_submit( $_POST['is_needed_webpage'] );
$this->option->set( 'is_needed_webpage', $validated_is_needed_webpage );
}
\wp_safe_redirect( \self_admin_url( 'tools.php?page=' . \apply_filters( 'Yoast\WP\Test_Helper\admin_page', '' ) ) );
}
/**
* Makes sure we only store data we know how to deal with.
*
* @param string $value The submitted value.
*
* @return string The validated submit value.
*/
private function validate_submit( $value ) {
$value = (string) $value;
if ( \in_array( $value, [ 'none', 'show', 'hide' ], true ) ) {
return $value;
}
return 'none';
}
/**
* Replaces your .test domain name with example.com in JSON output.
*
* @param array $data Data to replace the domain in.
*
* @return array Data to replace the domain in.
*/
public function replace_domain( $data ) {
$source = WPSEO_Utils::get_home_url();
$target = 'https://example.com';
if ( $source[ ( \strlen( $source ) - 1 ) ] === '/' ) {
$source = \substr( $source, 0, -1 );
}
return $this->array_value_str_replace( $source, $target, $data );
}
/**
* Returns the current breadcrumb option as boolean.
*
* @return bool
*/
public function filter_is_needed_breadcrumb() {
return $this->option->get( 'is_needed_breadcrumb' ) === 'show';
}
/**
* Returns the current webpage option as boolean.
*
* @return bool
*/
public function filter_is_needed_webpage() {
return $this->option->get( 'is_needed_webpage' ) === 'show';
}
/**
* Deep replaces strings in an array.
*
* @param string $needle The needle to replace.
* @param string $replacement The replacement.
* @param array $subject The array to replace in.
*
* @return array The array with needle replaced by replacement in strings.
*/
private function array_value_str_replace( $needle, $replacement, $subject ) {
if ( \is_array( $subject ) ) {
foreach ( $subject as $key => $value ) {
if ( \is_array( $value ) ) {
$subject[ $key ] = $this->array_value_str_replace( $needle, $replacement, $subject[ $key ] );
}
elseif ( \strpos( $value, $needle ) !== false ) {
$subject[ $key ] = \str_replace( $needle, $replacement, $value );
}
}
}
return $subject;
}
}