PluginProbe
ReCrawler / 0.2.0
ReCrawler v0.2.0
1.1.0 1.0.2 1.0.1 1.0.0 0.3.4 0.3.3 0.1.1 0.1.2 0.1.2.1 0.1.2.2 0.1.3 0.1.4 0.1.5 0.2.0 0.3.0 0.3.1 0.3.2 trunk 0.1.0 0.1.0.1
recrawler / src / IndexNowAbstract.php

IndexNowAbstract.php in ReCrawler 0.2.0, at src/IndexNowAbstract.php

297 lines 6.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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