You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

400 lines
8.8 KiB
PHP

<?php
/**
* The SEO Analyzer result of each test.
*
* @since 1.0.24
* @package RankMath
* @subpackage RankMath\SEO_Analysis
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\SEO_Analysis;
use RankMath\Helper;
defined( 'ABSPATH' ) || exit;
/**
* Result class.
*/
class Result {
/**
* Result ID.
*
* @var string
*/
private $id;
/**
* Hold result data.
*
* @var array
*/
private $result;
/**
* Is sub-page.
*
* @var array
*/
private $is_subpage;
/**
* The Constructor.
*
* @param string $id Result id.
* @param object $data Result data.
* @param bool $is_subpage Is sub-page result.
*/
public function __construct( $id, $data, $is_subpage ) {
if ( is_a( $data, 'RankMath\\SEO_Analysis\\Result' ) ) {
$data = $data->result;
}
$this->id = $id;
$this->result = $data;
$this->is_subpage = $is_subpage;
}
/**
* Magic method: convert object to string.
*/
public function __toString() {
$kb_link = 'https://rankmath.com/kb/seo-analysis/';
if ( ! empty( $this->result['kb_link'] ) ) {
$kb_link = $this->result['kb_link'];
}
ob_start();
?>
<div class="row-title">
<?php $this->the_status(); ?>
<h3><?php echo esc_html( $this->result['title'] ); ?>
<?php if ( ! empty( $this->result['tooltip'] ) ) : ?>
<a href="<?php echo esc_url( $kb_link ); ?>" target="_blank" class="rank-math-tooltip"><em class="dashicons-before dashicons-editor-help"></em><span><?php echo esc_html( $this->result['tooltip'] ); ?></span></a>
<?php endif; ?>
</h3>
</div>
<div class="row-description">
<div class="row-content">
<?php if ( $this->has_fix() ) : ?>
<a href="#" class="button button-secondary button-small result-action"><?php esc_html_e( 'How to fix', 'rank-math' ); ?></a>
<?php endif; ?>
<?php echo wp_kses_post( $this->result['message'] ); ?>
<?php if ( $this->has_fix() ) : ?>
<div class="how-to-fix-wrapper">
<div class="analysis-test-how-to-fix">
<?php echo wp_kses_post( $this->result['fix'] ); ?>
<?php if ( ! preg_match( '#<\/a><\/p>$#i', trim( $this->result['fix'] ) ) ) : ?>
<p><a href="<?php echo esc_url( $kb_link ); ?>" target="_blank" class="analysis-read-more"><?php esc_html_e( 'Read more', 'rank-math' ); ?></a></p>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<div class="clear"></div>
<?php
if ( isset( $this->result['data'] ) && ! empty( $this->result['data'] ) ) {
$this->the_content();
}
?>
</div>
</div>
<?php
return ob_get_clean();
}
/**
* Get result ID.
*
* @return string
*/
public function get_id() {
return $this->id;
}
/**
* Get result category.
*
* @return string
*/
public function get_category() {
return is_array( $this->result ) && isset( $this->result['category'] ) ? $this->result['category'] : '';
}
/**
* Get result status.
*
* @return string
*/
public function get_status() {
return is_array( $this->result ) && isset( $this->result['status'] ) ? $this->result['status'] : '';
}
/**
* Has "how to fix" content.
*
* @return bool
*/
private function has_fix() {
return is_array( $this->result ) && in_array( $this->result['status'], [ 'fail', 'warning' ], true ) && ! empty( $this->result['fix'] );
}
/**
* Output test result status.
*/
private function the_status() {
if ( ! is_array( $this->result ) ) {
return;
}
$status = $this->result['status'];
if ( ! empty( $this->result['is_info'] ) ) {
$status = 'info';
}
$icons = [
'ok' => 'dashicons dashicons-yes',
'fail' => 'dashicons dashicons-no-alt',
'warning' => 'dashicons dashicons-warning',
'info' => 'dashicons',
];
$labels = [
'ok' => esc_html__( 'OK', 'rank-math' ),
'fail' => esc_html__( 'Failed', 'rank-math' ),
'warning' => esc_html__( 'Warning', 'rank-math' ),
'info' => esc_html__( 'Info', 'rank-math' ),
];
printf(
'<div class="status-icon status-%1$s %3$s" title="%2$s"></div>',
sanitize_html_class( $status ),
esc_attr( $labels[ $status ] ),
esc_attr( $icons[ $status ] )
);
}
/**
* Output test data.
*/
private function the_content() {
if ( ! is_array( $this->result ) ) {
return;
}
$data = $this->result['data'];
if ( 'common_keywords' === $this->id ) {
$this->the_tag_cloud( $data );
return;
}
if ( $this->is_list() || $this->is_reverse_heading() ) {
$this->the_list( $data );
return;
}
$explode = [ 'title_length', 'description_length', 'canonical' ];
if ( in_array( $this->id, $explode, true ) ) {
echo '<code class="full-width">' . wp_kses_post( join( ', ', (array) $data ) ) . '</code>';
return;
}
}
/**
* Render results list.
*
* @param array $data Keywords.
*/
private function the_list( $data ) {
$is_reverse_heading = $this->is_reverse_heading();
$html = '<ul class="info-list">';
foreach ( $data as $label => $text ) {
$text = is_array( $text ) ? join( ', ', $text ) : $text;
$html .= $is_reverse_heading ? '<li><strong>' . $label . ': </strong> ' . esc_html( $text ) . '</li>' :
'<li>' . esc_html( ( is_string( $label ) ? $label . ' (' . $text . ')' : $text ) ) . '</li>';
}
echo wp_kses_post( $html ) . '</ul>';
}
/**
* Check if result data should be rendered as a list or not.
*
* @return bool
*/
private function is_list() {
return in_array( $this->id, [ 'img_alt', 'minify_css', 'minify_js', 'active_plugins', 'h1_heading', 'h2_headings' ], true );
}
/**
* Check if result data should be rendered with reversed heading or not.
*
* @return bool
*/
private function is_reverse_heading() {
return in_array( $this->id, [ 'links_ratio', 'keywords_meta', 'page_objects' ], true );
}
/**
* Render tag cloud.
*
* @param array $data Keywords.
*/
private function the_tag_cloud( $data ) {
echo wp_kses_post( $this->get_tag_cloud( $data ) );
}
/**
* Get tag cloud HTML.
*
* @param array $data Keywords.
*/
private function get_tag_cloud( $data ) {
$font_size_max = 22;
$font_size_min = 10;
$max = max( $data );
$html = '<div class="wp-tag-cloud">';
foreach ( $data as $keyword => $occurrences ) {
$size = ( $occurrences / $max ) * ( $font_size_max - $font_size_min ) + $font_size_min;
$size = round( $size, 2 );
$html .= sprintf( '<span class="keyword-cloud-item" style="font-size: %.2fpx">%s</span> ', $size, htmlspecialchars( $keyword, ENT_QUOTES | ENT_SUBSTITUTE, 'utf-8' ) );
}
$html = rtrim( $html );
$html .= '</div>';
return apply_filters( 'rank_math/seo_analysis/tag_cloud_html', $html, $data );
}
/**
* Is test excluded.
*
* @return bool
*/
public function is_excluded() {
$exclude_tests = [
'active_plugins',
'active_theme',
'dirlist',
'libwww_perl_access',
'robots_txt',
'safe_browsing',
'xmlrpc',
// Local tests.
'comment_pagination',
'site_description',
'permalink_structure',
'cache_plugin',
'search_console',
'focus_keywords',
'post_titles',
];
return $this->is_subpage && in_array( $this->id, $exclude_tests, true );
}
/**
* Is test hidden.
*
* @return bool
*/
public function is_hidden() {
$always_hidden = [
'serp_preview',
'mobile_serp_preview',
];
// Hidden when not in advanced mode.
$hidden_tests = [
// Performance.
'image_header',
'minify_css',
'minify_js',
'page_objects',
'page_size',
'response_time',
// Security.
'directory_listing',
'safe_browsing',
'ssl',
'active_plugins',
'active_theme',
];
$is_hidden = in_array( $this->id, $always_hidden, true ) || ( ! Helper::is_advanced_mode() && in_array( $this->id, $hidden_tests, true ) );
return apply_filters( 'rank_math/seo_analysis/is_test_hidden', $is_hidden, $this->id );
}
/**
* Get test score.
*
* @return int
*/
public function get_score() {
$score = [
'h1_heading' => 5,
'h2_headings' => 2,
'img_alt' => 4,
'keywords_meta' => 5,
'links_ratio' => 3,
'title_length' => 4,
'permalink_structure' => 7,
'focus_keywords' => 3,
'post_titles' => 4,
// Advanced SEO.
'canonical' => 5,
'noindex' => 7,
'non_www' => 4,
'opengraph' => 2,
'robots_txt' => 3,
'schema' => 3,
'sitemaps' => 3,
'search_console' => 1,
// Performance.
'image_header' => 3,
'minify_css' => 2,
'minify_js' => 1,
'page_objects' => 2,
'page_size' => 3,
'response_time' => 3,
// Security.
'directory_listing' => 1,
'safe_browsing' => 8,
'ssl' => 7,
];
return isset( $score[ $this->id ] ) ? $score[ $this->id ] : 0;
}
/**
* Get test result data.
*
* @return array
*/
public function get_result() {
return $this->result;
}
}