| 1 |
<?php |
| 2 |
/** |
| 3 |
* Main class. |
| 4 |
* |
| 5 |
* @package Mihdan\ReCrawler |
| 6 |
*/ |
| 7 |
|
| 8 |
namespace Mihdan\ReCrawler; |
| 9 |
|
| 10 |
use Mihdan\ReCrawler\Logger\Logger; |
| 11 |
use Mihdan\ReCrawler\Views\WPOSA; |
| 12 |
use WP; |
| 13 |
use WP_Post; |
| 14 |
use WP_Comment; |
| 15 |
|
| 16 |
abstract class IndexNowAbstract implements SearchEngineInterface { |
| 17 |
|
| 18 |
/** |
| 19 |
* API key. |
| 20 |
* |
| 21 |
* @var string $api_key |
| 22 |
*/ |
| 23 |
private $api_key; |
| 24 |
|
| 25 |
/** |
| 26 |
* Logger instance. |
| 27 |
* |
| 28 |
* @var Logger $logger |
| 29 |
*/ |
| 30 |
private $logger; |
| 31 |
|
| 32 |
/** |
| 33 |
* WPOSA instance. |
| 34 |
* |
| 35 |
* @var WPOSA $wposa |
| 36 |
*/ |
| 37 |
private $wposa; |
| 38 |
|
| 39 |
/** |
| 40 |
* Site host name. |
| 41 |
* |
| 42 |
* @var string $host |
| 43 |
*/ |
| 44 |
private $host; |
| 45 |
|
| 46 |
/** |
| 47 |
* Post types. |
| 48 |
* |
| 49 |
* @var array[] |
| 50 |
*/ |
| 51 |
private $post_types; |
| 52 |
|
| 53 |
/** |
| 54 |
* Taxonomies. |
| 55 |
* |
| 56 |
* @var array[] |
| 57 |
*/ |
| 58 |
private $taxonomies; |
| 59 |
|
| 60 |
/** |
| 61 |
* IndexNowAbstract constructor. |
| 62 |
* |
| 63 |
* @param Logger $logger Logger instance. |
| 64 |
*/ |
| 65 |
public function __construct( Logger $logger, WPOSA $wposa ) { |
| 66 |
$this->logger = $logger; |
| 67 |
$this->wposa = $wposa; |
| 68 |
$this->host = apply_filters( 'recrawler/host', wp_parse_url( get_home_url(), PHP_URL_HOST ) ); |
| 69 |
$this->post_types = apply_filters( 'recrawler/post_types', (array) $this->wposa->get_option( 'post_types', 'general', [] ) ); |
| 70 |
$this->taxonomies = apply_filters( 'recrawler/taxonomies', (array) $this->wposa->get_option( 'taxonomies', 'general', [] ) ); |
| 71 |
$this->api_key = $this->wposa->get_option( 'api_key', 'index_now', Utils::generate_key() ); |
| 72 |
} |
| 73 |
|
| 74 |
public function setup_hooks() { |
| 75 |
if ( ! $this->is_enabled() ) { |
| 76 |
return false; |
| 77 |
} |
| 78 |
|
| 79 |
add_action( 'parse_request', [ $this, 'set_virtual_key_file' ] ); |
| 80 |
add_action( 'recrawler/post_added', [ $this, 'ping_on_post_update' ], 10, 2 ); |
| 81 |
add_action( 'recrawler/post_updated', [ $this, 'ping_on_post_update' ], 10, 2 ); |
| 82 |
|
| 83 |
if ( $this->is_ping_on_comment() ) { |
| 84 |
add_action( 'recrawler/comment_updated', [ $this, 'ping_on_insert_comment' ], 10, 2 ); |
| 85 |
} |
| 86 |
|
| 87 |
if ( $this->is_ping_on_term() ) { |
| 88 |
add_action( 'recrawler/term_updated', [ $this, 'ping_on_insert_term' ], 10, 2 ); |
| 89 |
} |
| 90 |
} |
| 91 |
|
| 92 |
abstract protected function get_api_url(): string; |
| 93 |
abstract protected function get_bot_useragent(): string; |
| 94 |
|
| 95 |
public function is_enabled(): bool { |
| 96 |
return $this->wposa->get_option( 'enable', 'index_now', 'on' ) === 'on'; |
| 97 |
} |
| 98 |
|
| 99 |
private function is_ping_on_comment(): bool { |
| 100 |
return $this->wposa->get_option( 'ping_on_comment', 'general', 'off' ) === 'on'; |
| 101 |
} |
| 102 |
|
| 103 |
private function is_ping_on_post_added(): bool { |
| 104 |
return $this->wposa->get_option( 'ping_on_post', 'general', 'on' ) === 'on'; |
| 105 |
} |
| 106 |
|
| 107 |
private function is_ping_on_post_updated(): bool { |
| 108 |
return $this->wposa->get_option( 'ping_on_post_updated', 'general', 'on' ) === 'on'; |
| 109 |
} |
| 110 |
|
| 111 |
private function is_ping_on_term(): bool { |
| 112 |
return $this->wposa->get_option( 'ping_on_term', 'general', 'off' ) === 'on'; |
| 113 |
} |
| 114 |
|
| 115 |
private function is_key_logging_enabled(): bool { |
| 116 |
return $this->wposa->get_option( 'key_logging', 'logs', 'on' ) === 'on'; |
| 117 |
} |
| 118 |
|
| 119 |
private function get_current_search_engine(): string { |
| 120 |
return $this->wposa->get_option( 'search_engine', 'index_now', 'yandex-index-now' ); |
| 121 |
} |
| 122 |
|
| 123 |
/** |
| 124 |
* Fires actions related to the transitioning of a post's status. |
| 125 |
* |
| 126 |
* @param int $post_id Post ID. |
| 127 |
* @param WP_Post $post Post data. |
| 128 |
* |
| 129 |
* @link https://yandex.ru/dev/webmaster/doc/dg/reference/host-recrawl-post.html |
| 130 |
*/ |
| 131 |
public function ping_on_post_update( int $post_id, WP_Post $post ) { |
| 132 |
$this->maybe_do_ping_post( $post_id ); |
| 133 |
} |
| 134 |
|
| 135 |
public function ping_on_insert_comment( int $post_id, WP_Comment $comment ) { |
| 136 |
$this->maybe_do_ping_post( $post_id ); |
| 137 |
} |
| 138 |
|
| 139 |
public function ping_on_insert_term( int $term_id, string $taxonomy ) { |
| 140 |
$this->maybe_do_ping_term( $term_id, $taxonomy ); |
| 141 |
} |
| 142 |
|
| 143 |
private function maybe_do_ping_post( int $post_id ) { |
| 144 |
|
| 145 |
if ( $this->get_current_search_engine() === $this->get_slug() ) { |
| 146 |
$this->push( [ get_permalink( $post_id ) ] ); |
| 147 |
} |
| 148 |
} |
| 149 |
|
| 150 |
private function maybe_do_ping_term( int $term_id, string $taxonomy ) { |
| 151 |
|
| 152 |
if ( ! in_array( $taxonomy, $this->get_taxonomies(), true ) ) { |
| 153 |
return; |
| 154 |
} |
| 155 |
|
| 156 |
if ( $this->get_current_search_engine() === $this->get_slug() ) { |
| 157 |
$this->push( [ get_term_link( $term_id, $taxonomy ) ] ); |
| 158 |
} |
| 159 |
} |
| 160 |
|
| 161 |
public function set_api_url( string $url ): bool { |
| 162 |
$this->api_url = $url; |
| 163 |
|
| 164 |
return true; |
| 165 |
} |
| 166 |
|
| 167 |
private function get_post_types(): array { |
| 168 |
return $this->post_types; |
| 169 |
} |
| 170 |
|
| 171 |
private function get_taxonomies(): array { |
| 172 |
return $this->taxonomies; |
| 173 |
} |
| 174 |
|
| 175 |
/** |
| 176 |
* Get host name. |
| 177 |
* |
| 178 |
* @return string |
| 179 |
*/ |
| 180 |
private function get_host() { |
| 181 |
return $this->host; |
| 182 |
} |
| 183 |
|
| 184 |
public function push( array $url_list ): bool { |
| 185 |
$args = array( |
| 186 |
'timeout' => 30, |
| 187 |
'body' => wp_json_encode( |
| 188 |
array( |
| 189 |
'host' => $this->get_host(), |
| 190 |
'key' => $this->get_api_key(), |
| 191 |
'keyLocation' => $this->get_api_key_location(), |
| 192 |
'urlList' => $url_list, |
| 193 |
) |
| 194 |
), |
| 195 |
'headers' => [ |
| 196 |
'Content-Type' => 'application/json', |
| 197 |
], |
| 198 |
); |
| 199 |
|
| 200 |
$response = wp_remote_post( $this->get_api_url(), $args ); |
| 201 |
|
| 202 |
$body = wp_remote_retrieve_body( $response ); |
| 203 |
$status_code = wp_remote_retrieve_response_code( $response ); |
| 204 |
$response_message = wp_remote_retrieve_response_message( $response ); |
| 205 |
|
| 206 |
if ( Utils::is_json( $body ) ) { |
| 207 |
$body = json_decode( $body, true ); |
| 208 |
} |
| 209 |
|
| 210 |
$data = [ |
| 211 |
'status_code' => $status_code, |
| 212 |
'search_engine' => $this->get_current_search_engine(), |
| 213 |
]; |
| 214 |
|
| 215 |
if ( Utils::is_response_code_success( $status_code ) ) { |
| 216 |
foreach ( $url_list as $url ) { |
| 217 |
$message = sprintf( '<a href="%s" target="_blank">%s</a> - OK', $url, $url ); |
| 218 |
$this->logger->info( $message, $data ); |
| 219 |
} |
| 220 |
} else { |
| 221 |
|
| 222 |
if ( ! empty( $body['message'] ) ) { |
| 223 |
$message = $body['message']; |
| 224 |
} elseif ( ! empty( $body ) ) { |
| 225 |
$message = print_r( $body, 1 ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r |
| 226 |
} elseif ( ! empty( $response_message ) ) { |
| 227 |
$message = $response_message; |
| 228 |
} else { |
| 229 |
$message = ''; |
| 230 |
} |
| 231 |
|
| 232 |
$this->logger->error( $message, $data ); |
| 233 |
} |
| 234 |
|
| 235 |
return true; |
| 236 |
} |
| 237 |
|
| 238 |
/** |
| 239 |
* Get API key. |
| 240 |
* |
| 241 |
* @return string |
| 242 |
*/ |
| 243 |
private function get_api_key() { |
| 244 |
return $this->api_key; |
| 245 |
} |
| 246 |
|
| 247 |
/** |
| 248 |
* Get virtual API key location. |
| 249 |
* |
| 250 |
* @return string |
| 251 |
*/ |
| 252 |
private function get_api_key_location(): string { |
| 253 |
return trailingslashit( get_home_url() ) . $this->get_api_key() . '.txt'; |
| 254 |
} |
| 255 |
|
| 256 |
/** |
| 257 |
* Set virtual key file. |
| 258 |
* |
| 259 |
* @param WP $wp WP instance. |
| 260 |
*/ |
| 261 |
public function set_virtual_key_file( WP $wp ) { |
| 262 |
if ( ! get_option( 'permalink_structure' ) ) { |
| 263 |
return; |
| 264 |
} |
| 265 |
|
| 266 |
$api_key = $this->get_api_key(); |
| 267 |
|
| 268 |
if ( $wp->request !== $api_key . '.txt' ) { |
| 269 |
return; |
| 270 |
} |
| 271 |
|
| 272 |
/** |
| 273 |
* A constant for disabling cache in popular plugins like: |
| 274 |
* - WP Super Cache |
| 275 |
* - WP Rocket |
| 276 |
*/ |
| 277 |
if ( ! defined( 'DONOTCACHEPAGE' ) ) { |
| 278 |
define( 'DONOTCACHEPAGE', true ); |
| 279 |
} |
| 280 |
|
| 281 |
if ( $this->is_key_logging_enabled() ) { |
| 282 |
$data = [ |
| 283 |
'search_engine' => $this->get_current_search_engine(), |
| 284 |
'direction' => 'incoming', |
| 285 |
]; |
| 286 |
|
| 287 |
$this->logger->info( __( 'Bot checked the key file', 'recrawler' ), $data ); |
| 288 |
} |
| 289 |
|
| 290 |
header( 'Content-Type: text/plain' ); |
| 291 |
header( 'X-Robots-Tag: noindex' ); |
| 292 |
status_header( 200 ); |
| 293 |
echo esc_html( $api_key ); |
| 294 |
die; |
| 295 |
} |
| 296 |
} |
| 297 |
|