PluginProbe
Woody Code Snippets – Insert PHP, CSS, JS, and Header/Footer Scripts / 2.4.7
Woody Code Snippets – Insert PHP, CSS, JS, and Header/Footer Scripts v2.4.7
2.7.6 2.7.5 2.7.4 trunk 1.3 2.0.4 2.0.6 2.1.91 2.2.4 2.2.7 2.2.9 2.3.1 2.3.10 2.4.10 2.4.2 2.4.4 2.4.5 2.4.6 2.4.7 2.4.8 2.4.9 2.6.0 2.6.1 2.7.0 2.7.1 All 27 releases
insert-php / includes / class.execute.snippet.php

class.execute.snippet.php in Woody Code Snippets – Insert PHP, CSS, JS, and Header/Footer Scripts 2.4.7, at includes/class.execute.snippet.php

1,206 lines 36.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Execute snippet
4 *
5 * @author Artem Prihodko <webtemyk@yandex.ru>
6 * @copyright (c) 2020, CreativeMotion
7 * @version 2.4
8 */
9
10 // Exit if accessed directly
11 if ( ! defined( 'ABSPATH' ) ) {
12 exit;
13 }
14
15 class WINP_Execute_Snippet {
16
17 /**
18 * @var self
19 */
20 private static $instance;
21
22 /**
23 * @var array
24 */
25 public $snippets;
26
27 /**
28 * @var WINP_Insertion_Locations
29 */
30 public $snippets_locations;
31
32 public static function app() {
33 if ( self::$instance === null ) {
34 self::$instance = new self();
35 }
36
37 return self::$instance;
38 }
39
40 /**
41 * WINP_Execute_Snippet constructor.
42 */
43 public function __construct() {
44 self::$instance = $this;
45
46 if ( ! defined( 'WINP_UPLOAD_DIR' ) ) {
47 $dir = wp_upload_dir();
48 define( 'WINP_UPLOAD_DIR', $dir['basedir'] . '/winp-css-js' );
49 }
50
51 if ( ! defined( 'WINP_UPLOAD_URL' ) ) {
52 $dir = wp_upload_dir();
53 define( 'WINP_UPLOAD_URL', $dir['baseurl'] . '/winp-css-js' );
54 }
55 global $wpdb;
56
57 $sql = "SELECT {$wpdb->posts}.ID, {$wpdb->posts}.post_content, p2.meta_value as priority
58 FROM {$wpdb->posts}
59 INNER JOIN {$wpdb->postmeta} p1 ON ({$wpdb->posts}.ID = p1.post_id)
60 INNER JOIN {$wpdb->postmeta} p2 ON ({$wpdb->posts}.ID = p2.post_id)
61 INNER JOIN {$wpdb->postmeta} p3 ON ({$wpdb->posts}.ID = p3.post_id)
62 WHERE (( p1.meta_key = '" . WINP_Plugin::app()->getPrefix() . "snippet_scope' AND p1.meta_value = '%s')
63 AND
64 ( p3.meta_key = '" . WINP_Plugin::app()->getPrefix() . "snippet_activate' AND p3.meta_value = '1')
65 AND p2.meta_key = '" . WINP_Plugin::app()->getPrefix() . "snippet_priority' )
66 AND {$wpdb->posts}.post_type = '" . WINP_SNIPPETS_POST_TYPE . "'
67 AND ({$wpdb->posts}.post_status = 'publish')
68 ORDER BY CAST(priority AS UNSIGNED) %s";
69
70 $this->snippets['evrywhere'] = $wpdb->get_results( sprintf( $sql, 'evrywhere', 'DESC' ) );
71 $this->snippets['auto'] = $wpdb->get_results( sprintf( $sql, 'auto', 'ASC' ) );
72
73 global $winp_snippets_locations;
74 $this->snippets_locations = new WINP_Insertion_Locations();
75 }
76
77 /**
78 * Register hooks
79 */
80 public function registerHooks() {
81 add_action( 'plugins_loaded', [ $this, 'executeEverywhereSnippets' ], 1 );
82
83 if ( ! is_admin() ) { #issue PCS-45 fix bug with WPBPage Builder Frontend Editor
84 add_action( 'wp_head', [ $this, 'executeHeaderSnippets' ] );
85 add_action( 'wp_footer', [ $this, 'executeFooterSnippets' ] );
86 add_action( 'the_post', [ $this, 'executePostSnippets' ], 10, 2 );
87 add_filter( 'the_content', [ $this, 'executeContentSnippets' ] );
88 add_filter( 'the_excerpt', [ $this, 'executeExcerptSnippets' ] );
89 // Бесполезный �
90 ук, который вызывается на каждый комментарий. Если и�
91 много, увеличивается нагрузка
92 //add_filter( 'wp_list_comments_args', [ $this, 'executeListCommentsSnippets' ] );
93
94 //add_action( 'wp_head', [ $this, 'executeWoocommerceSnippets' ] );
95
96 if ( ! empty( $this->snippets_locations->getInsertion( 'custom' ) ) ) {
97 add_action( 'wp_head', [ $this, 'executeCustomSnippets' ] );
98 }
99 }
100 }
101
102 /**
103 * Execute the everywhere snippets once the plugins are loaded
104 */
105 public function executeEverywhereSnippets() {
106 echo $this->executeActiveSnippets( 'evrywhere' );
107 }
108
109 /**
110 * Execute the snippets in header of page once the plugins are loaded
111 */
112 public function executeHeaderSnippets() {
113 echo $this->executeActiveSnippets( 'auto', 'header' );
114 }
115
116 /**
117 * Execute the snippets in footer of page once the plugins are loaded
118 */
119 public function executeFooterSnippets() {
120 echo $this->executeActiveSnippets( 'auto', 'footer' );
121 }
122
123 /**
124 * Execute the snippets before post
125 *
126 * @param WP_Post $post
127 * @param WP_Query $query
128 */
129 public function executePostSnippets( $post, $query ) {
130 $content = '';
131
132 $post_type = ! empty( $post ) ? $post->post_type : get_post( $post->ID )->post_type;
133 if ( is_singular( [ $post_type ] ) ) {
134 if ( did_action( 'get_header' ) ) {
135 // Перед заголовком
136 $content = $this->executeActiveSnippets( 'auto', 'before_post' );
137 }
138 } else {
139 if ( $query->post_count > 0 ) {
140 if ( $query->post_count > 1 && $query->current_post > 0 && $query->post_count > $query->current_post ) {
141 // Между записями
142 $content = $this->executeActiveSnippets( 'auto', 'between_posts' );
143 }
144 // Перед записью
145 $content .= $this->executeActiveSnippets( 'auto', 'before_posts', '', $query );
146
147 // После записи
148 $content .= $this->executeActiveSnippets( 'auto', 'after_posts', '', $query );
149 }
150 }
151
152 echo $content;
153 }
154
155 /**
156 * Handle paragraph content
157 *
158 * @param $content
159 * @param $snippet_content
160 * @param $paragraph_number
161 * @param $type
162 *
163 * @return mixed
164 */
165 private function handleParagraphContent( $content, $snippet_content, $paragraph_number, $type = 'before' ) {
166 if ( 'before' == $type ) {
167 preg_match_all( '/<p(.*?)>/', $content, $matches );
168 } else {
169 preg_match_all( '/<\/p>/', $content, $matches );
170 }
171 $paragraphs = $matches[0];
172
173 if ( $paragraph_number == 0 ) {
174 $paragraph_number = 1;
175 }
176
177 if ( $content && $snippet_content && $paragraphs && $paragraph_number <= count( $paragraphs ) ) {
178 $offset = 0;
179 foreach ( $paragraphs as $paragraph_key => $paragraph ) {
180 $position = strpos( $content, $paragraph, $offset ); // Позиция тега параграфа
181 // Если указанный номер параграфа совпадает с текущим
182 if ( $paragraph_key + 1 == $paragraph_number ) {
183 if ( 'before' == $type ) {
184 $content = substr( $content, 0, $position ) . $snippet_content . substr( $content, $position );
185 } else {
186 $content = substr( $content, 0, $position + 4 ) . $snippet_content . substr( $content, $position + 4 );
187 }
188 break;
189 } else {
190 $offset = $position + 1;
191 }
192 }
193 }
194
195 return $content;
196 }
197
198 /**
199 * Handle posts content
200 *
201 * @param string $content
202 * @param string $snippet_content
203 * @param integer $post_number
204 * @param string $type
205 * @param object $query
206 *
207 * @return mixed
208 */
209 private function handlePostsContent( $content, $snippet_content, $post_number, $type, $query ) {
210 global $winp_after_post_content;
211 if ( $query->post_count > 0 ) {
212 if ( $post_number == 0 ) {
213 $post_number = 1;
214 }
215
216 if ( 'before' == $type && $query->current_post + 1 == $post_number ) {
217 return $snippet_content;
218 } elseif ( 'after' == $type ) {
219 // Номер поста совпадает
220 if ( $query->current_post == $post_number ) {
221 return $snippet_content;
222 // Если это последний пост и указанный номер поста больше общего количества постов,
223 // то нужно со�
224 ранить контент сниппета для вывода в конце данного поста
225 } elseif ( $query->current_post + 1 == $query->post_count && $post_number >= $query->post_count ) {
226 $winp_after_post_content[ $query->post->ID ] = $snippet_content;
227 }
228 }
229 }
230
231 return $content;
232 }
233
234 /**
235 * Execute the snippets page content
236 *
237 * @param $content
238 *
239 * @return mixed
240 */
241 public function executeContentSnippets( $content ) {
242 global $post, $winp_after_post_content;
243
244 $post_type = ! empty( $post ) ? $post->post_type : false;
245
246 if ( is_category() || is_archive() || is_tag() || is_tax() || is_search() ) {
247 // Перед коротким описанием
248 $content = $this->executeActiveSnippets( 'auto', 'before_excerpt' ) . $content;
249
250 // После короткого описания
251 $content .= $this->executeActiveSnippets( 'auto', 'after_excerpt' );
252 }
253
254 if ( is_singular( [ $post_type ] ) ) {
255 // Перед параграфом
256 $content = $this->executeActiveSnippets( 'auto', 'before_paragraph', $content );
257
258 // После параграфа
259 $content = $this->executeActiveSnippets( 'auto', 'after_paragraph', $content );
260
261 // После заголовка
262 $content = $this->executeActiveSnippets( 'auto', 'before_content' ) . $content;
263
264 // После текста
265 $content .= $this->executeActiveSnippets( 'auto', 'after_content' );
266
267 // После поста
268 $content .= $this->executeActiveSnippets( 'auto', 'after_post' );
269
270 if ( ! comments_open( $post->ID ) && ! get_comments_number( $post->ID ) ) {
271 remove_filter( 'wp_list_comments_args', [ $this, 'executeListCommentsSnippets' ] );
272 }
273 } elseif ( ! is_null( $post ) && isset( $winp_after_post_content[ $post->ID ] ) ) {
274 // После последнего поста в списке
275 $content .= $winp_after_post_content[ $post->ID ];
276 unset( $winp_after_post_content[ $post->ID ] );
277 }
278
279 return $content;
280 }
281
282 /**
283 * Execute the snippets page excerpt
284 *
285 * @param $excerpt
286 *
287 * @return mixed
288 */
289 public function executeExcerptSnippets( $excerpt ) {
290 if ( is_category() || is_archive() || is_tag() || is_tax() || is_search() ) {
291 // Перед коротким описанием
292 $excerpt = $this->executeActiveSnippets( 'auto', 'before_excerpt' ) . $excerpt;
293
294 // После короткого описания
295 $excerpt .= $this->executeActiveSnippets( 'auto', 'after_excerpt' );
296 }
297
298 return $excerpt;
299 }
300
301 /**
302 * Execute the list comments filter
303 *
304 * @param $args
305 *
306 * @return mixed
307 */
308 public function executeListCommentsSnippets( $args ) {
309 global $winp_wp_data;
310
311 $winp_wp_data['winp_comments_saved_end_callback'] = $args['end-callback'];
312 $args['end-callback'] = [ $this, 'executeCommentsSnippets' ];
313
314 return $args;
315 }
316
317 /**
318 * Execute the snippets after page comments
319 *
320 * @param $comment
321 * @param $args
322 * @param $depth
323 */
324 public function executeCommentsSnippets( $comment, $args, $depth ) {
325 global $winp_wp_data, $post;
326
327 if ( ! empty( $winp_wp_data['winp_comments_saved_end_callback'] ) ) {
328 echo call_user_func( $winp_wp_data['winp_comments_saved_end_callback'], $comment, $args, $depth );
329 }
330
331 $content = '';
332
333 $post_type = ! empty( $post ) ? $post->post_type : false;
334 if ( is_singular( [ $post_type ] ) ) {
335 // После комментариев
336 $content = $this->executeActiveSnippets( 'auto', 'after_post' );
337 }
338
339 echo $content;
340 }
341
342 /**
343 * Execute the custom snippets
344 *
345 * @since 2.4
346 */
347 public function executeCustomSnippets() {
348 $locations = $this->snippets_locations->getInsertion( 'custom' );
349 foreach ( $locations as $location => $data ) {
350 $this->executeActiveSnippets( 'auto', $location );
351 }
352 }
353
354 /**
355 * Execute Woocommerce actions/hooks
356 *
357 * @param $location
358 * @param $snippet_content
359 *
360 * @since 2.4
361 */
362 public function woocommerce_actions( $location, $snippet_content = '' ) {
363 $action = function () use ( $location, $snippet_content ) {
364 echo $snippet_content;
365 };
366
367 switch ( $location ) {
368 case 'woo_before_shop_loop':
369 add_filter( 'woocommerce_product_loop_start', function ( $content ) use ( $snippet_content ) {
370 return $snippet_content . $content;
371 } );
372 break;
373 case 'woo_after_shop_loop':
374 add_filter( 'woocommerce_product_loop_end', function ( $content ) use ( $snippet_content ) {
375 return $content . $snippet_content;
376 } );
377 break;
378 case 'woo_before_single_product':
379 add_action( 'woocommerce_before_single_product', $action, 10, 2 );
380 break;
381 case 'woo_after_single_product':
382 add_action( 'woocommerce_after_single_product', $action, 10, 2 );
383 break;
384 case 'woo_before_single_product_summary':
385 add_action( 'woocommerce_before_single_product_summary', $action, 10, 2 );
386 break;
387 case 'woo_after_single_product_summary':
388 add_action( 'woocommerce_after_single_product_summary', $action, 10, 2 );
389 break;
390 case 'woo_single_product_summary_title':
391 add_action( 'woocommerce_single_product_summary', $action, 6, 2 );
392 break;
393 case 'woo_single_product_summary_price':
394 add_action( 'woocommerce_single_product_summary', $action, 15, 2 );
395 break;
396 case 'woo_single_product_summary_excerpt':
397 add_action( 'woocommerce_single_product_summary', $action, 25, 2 );
398 break;
399 default:
400 break;
401 }
402 }
403
404 /**
405 * Execute Woocommerce actions/hooks
406 *
407 * @param $location
408 * @param $snippet_content
409 *
410 * @since 2.4
411 */
412 public function custom_actions( $location, $snippet_content = '' ) {
413 if ( ! empty( $this->snippets_locations->getLocation( $location ) ) ) {
414 /**
415 * Action for a custom location applied in 'wbcr/woody/add_custom_location' filter
416 *
417 * @param array $location Slug of the location.
418 * @param string $snippet_content Rendered snippet content
419 *
420 * @since 2.4
421 */
422 do_action( "wbcr/woody/do_custom_location/{$location}", $snippet_content );
423 }
424 }
425
426 /**
427 * Execute the snippets once the plugins are loaded
428 *
429 * @param string $scope
430 * @param string $location
431 * @param string $content
432 * @param array $custom_params
433 *
434 * @return string
435 */
436 public function executeActiveSnippets( $scope = 'evrywhere', $location = '', $content = '', $custom_params = [] ) {
437 /*
438 global $wpdb;
439
440 if ( $scope == 'evrywhere' ) {
441 $sort = 'DESC';
442 } else {
443 $sort = 'ASC';
444 }
445 $snippets = $wpdb->get_results( "SELECT {$wpdb->posts}.ID, {$wpdb->posts}.post_content, p2.meta_value as priority
446 FROM {$wpdb->posts}
447 INNER JOIN {$wpdb->postmeta} p1 ON ({$wpdb->posts}.ID = p1.post_id)
448 INNER JOIN {$wpdb->postmeta} p2 ON ({$wpdb->posts}.ID = p2.post_id)
449 INNER JOIN {$wpdb->postmeta} p3 ON ({$wpdb->posts}.ID = p3.post_id)
450 WHERE (( p1.meta_key = '" . WINP_Plugin::app()->getPrefix() . "snippet_scope' AND p1.meta_value = '{$scope}')
451 AND
452 ( p3.meta_key = '" . WINP_Plugin::app()->getPrefix() . "snippet_activate' AND p3.meta_value = '1')
453 AND p2.meta_key = '" . WINP_Plugin::app()->getPrefix() . "snippet_priority' )
454 AND {$wpdb->posts}.post_type = '" . WINP_SNIPPETS_POST_TYPE . "'
455 AND ({$wpdb->posts}.post_status = 'publish')
456 ORDER BY CAST(priority AS UNSIGNED) {$sort}" );
457 */
458 $snippets = $this->snippets[ $scope ] ?? [];
459
460 if ( empty( $snippets ) ) {
461 return $content;
462 }
463
464 foreach ( (array) $snippets as $snippet ) {
465 $id = (int) $snippet->ID;
466 //$is_active = (int) WINP_Helper::getMetaOption( $id, 'snippet_activate', 0 );
467 // Если это сниппет с автовставкой и выбранное место под�
468 одит под активный action
469 $avail_place = ( 'auto' == $scope ? $location == WINP_Helper::getMetaOption( $id, 'snippet_location', '' ) : true );
470 // Если условие отображения сниппета выполняется
471 $snippet_type = WINP_Helper::getMetaOption( $id, 'snippet_type', WINP_SNIPPET_TYPE_PHP );
472 $is_condition = $snippet_type != WINP_SNIPPET_TYPE_PHP ? $this->checkCondition( $id ) : true;
473
474 if ( $avail_place && $is_condition ) {
475 $post_id = (int) WINP_Plugin::app()->request->post( 'post_ID', 0 );
476
477 if ( isset( $_POST['wbcr_inp_snippet_scope'] ) && $post_id === $id && WINP_Plugin::app()->currentUserCan() ) {
478 return $content;
479 }
480
481 if ( WINP_Helper::is_safe_mode() ) {
482 return $content;
483 }
484
485 // WPML Compatibility
486 if ( defined( 'WPML_PLUGIN_FILE' ) ) {
487 $wpml_langs = WINP_Helper::getMetaOption( $id, 'snippet_wpml_lang', '' );
488 if ( $wpml_langs !== '' && defined( 'ICL_LANGUAGE_CODE' ) ) {
489 if ( ! in_array( ICL_LANGUAGE_CODE, explode( ',', $wpml_langs ) ) ) {
490 continue;
491 }
492 }
493 }
494
495 $snippet_code = WINP_Helper::get_snippet_code( $snippet );
496
497 /**
498 * Filter snippet code before execute
499 */
500 $snippet_code = apply_filters( 'wbcr/inp/execute_snippet/snippet_code', $snippet_code, $id );
501
502 if ( WINP_Plugin::app()->getOption( 'execute_shortcode' ) ) {
503 $snippet_code = do_shortcode( $snippet_code );
504 }
505
506 if ( $snippet_type === WINP_SNIPPET_TYPE_TEXT || $snippet_type === WINP_SNIPPET_TYPE_AD ) {
507 $snippet_content = '<div class="winp-text-snippet-container">' . $snippet_code . '</div>';
508 } elseif ( $snippet_type === WINP_SNIPPET_TYPE_CSS || $snippet_type === WINP_SNIPPET_TYPE_JS ) {
509 $snippet_content = self::getJsCssSnippetData( $id );
510 } elseif ( $snippet_type === WINP_SNIPPET_TYPE_HTML ) {
511 $snippet_content = $snippet_code;
512 } else {
513 $code = $this->prepareCode( $snippet_code, $id );
514 ob_start();
515 $this->executeSnippet( $code, $id, false );
516 $snippet_content = ob_get_contents();
517 ob_end_clean();
518 }
519
520 if ( 'auto' == $scope ) {
521 switch ( $location ) {
522 case 'before_paragraph': // Перед параграфом
523 $location_number = WINP_Helper::getMetaOption( $id, 'snippet_p_number', 0 );
524 $content = $this->handleParagraphContent( $content, $snippet_content, $location_number );
525 break;
526 case 'after_paragraph': // После параграфа
527 $location_number = WINP_Helper::getMetaOption( $id, 'snippet_p_number', 0 );
528 $content = $this->handleParagraphContent( $content, $snippet_content, $location_number, 'after' );
529 break;
530 case 'before_posts': // Перед записью
531 $location_number = WINP_Helper::getMetaOption( $id, 'snippet_p_number', 0 );
532 $content = $this->handlePostsContent( $content, $snippet_content, $location_number, 'before', $custom_params );
533 break;
534 case 'after_posts': // После записи
535 $location_number = WINP_Helper::getMetaOption( $id, 'snippet_p_number', 0 );
536 $content = $this->handlePostsContent( $content, $snippet_content, $location_number, 'after', $custom_params );
537 break;
538 default:
539 $content = $snippet_content . $content;
540 }
541
542 /**
543 * Action for woo actions
544 *
545 * @param array $location Slug of the location.
546 * @param string $snippet_content Rendered snippet content
547 *
548 * @since 2.4
549 */
550 do_action( 'wbcr/woody/do_woocommerce_actions', $location, $snippet_content );
551
552 //$this->woocommerce_actions( $location, $snippet_content );
553 $this->custom_actions( $location, $snippet_content );
554 } else {
555 $content = $snippet_content . $content;
556 }
557 }
558 }
559
560 return $content;
561 }
562
563 /**
564 * Get js or css snippet data
565 *
566 * @param $snippet_id
567 *
568 * @return mixed|string
569 */
570 public static function getJsCssSnippetData( $snippet_id ) {
571 $snippet_type = WINP_Helper::get_snippet_type( $snippet_id );
572
573 $linking = WINP_Helper::getMetaOption( $snippet_id, 'snippet_linking' );
574 $filetype = WINP_Helper::getMetaOption( $snippet_id, 'filetype', $snippet_type );
575
576 $file_name = $snippet_id . '.' . $filetype;
577 $slug = WINP_Helper::getMetaOption( $snippet_id, 'css_js_slug' );
578 if ( ! empty( $slug ) ) {
579 $file_name = $slug . '.' . $filetype;
580 }
581
582 if ( file_exists( WINP_UPLOAD_DIR . '/' . $file_name ) ) {
583 if ( 'inline' == $linking ) {
584 return file_get_contents( WINP_UPLOAD_DIR . '/' . $file_name );
585 }
586
587 if ( 'external' == $linking ) {
588 $file_name .= '?ver=' . WINP_Helper::getMetaOption( $snippet_id, 'css_js_version', time() );
589
590 if ( 'js' == $snippet_type ) {
591 return PHP_EOL . "<script type='text/javascript' src='" . WINP_UPLOAD_URL . '/' . $file_name . "'></script>" . PHP_EOL;
592 }
593
594 if ( 'css' == $snippet_type ) {
595 $short_filename = preg_replace( '@\.css\?ver=.*$@', '', $file_name );
596
597 return PHP_EOL . "<link rel='stylesheet' id='" . $short_filename . "-css' href='" . WINP_UPLOAD_URL . '/' . $file_name . "' type='text/css' media='all' />" . PHP_EOL;
598 }
599 }
600 }
601
602 return '';
603 }
604
605 /**
606 * Execute a snippet
607 *
608 * Code must NOT be escaped, as
609 * it will be executed directly
610 *
611 * @param string $code The snippet code to execute
612 * @param int $id The snippet ID
613 * @param bool $catch_output Whether to attempt to suppress the output of execution using buffers
614 *
615 * @return mixed The result of the code execution
616 */
617 public function executeSnippet( $code, $id = 0, $catch_output = true ) {
618 $id = (int) $id;
619
620 if ( ! $id || empty( $code ) ) {
621 return false;
622 }
623
624 if ( $catch_output ) {
625 ob_start();
626 }
627
628 $snippet = get_post( $id );
629
630 if ( empty( $snippet ) || $snippet->post_type !== WINP_SNIPPETS_POST_TYPE ) {
631 return false;
632 }
633
634 $snippet_type = WINP_Helper::getMetaOption( $id, 'snippet_type', true );
635
636 if ( $snippet_type == WINP_SNIPPET_TYPE_UNIVERSAL ) {
637 $result = eval( '?>' . $code . '<?php ' );
638 } elseif ( $snippet_type == WINP_SNIPPET_TYPE_PHP ) {
639 $result = eval( $code );
640 } else {
641 $result = ! empty( $code );
642 }
643
644 if ( $catch_output ) {
645 ob_end_clean();
646 }
647
648 return $result;
649 }
650
651 /**
652 * Get property value
653 *
654 * @param $value
655 * @param $property
656 *
657 * @return null
658 */
659 private function getPropertyValue( $value, $property ) {
660 if ( is_object( $value ) ) {
661 return $value->$property ?? null;
662 } elseif ( isset( $value[ $property ] ) ) {
663 return $value[ $property ];
664 }
665
666 return null;
667 }
668
669 /**
670 * Check conditional execution logic for the snippet
671 *
672 * @param $snippet_id
673 *
674 * @return bool
675 */
676 public function checkCondition( $snippet_id ) {
677 // Итоговый результат условий
678 $result = true;
679 // Получаем со�
680 ранённые параметры условий
681 $filters = get_post_meta( $snippet_id, WINP_Plugin::app()->getPrefix() . 'snippet_filters' );
682 // Если условия указаны
683 if ( ! ( empty( $filters ) || isset( $filters[0] ) && empty( $filters[0] ) ) ) {
684 foreach ( $filters[0] as $filter ) {
685 $conditions = $this->getPropertyValue( $filter, 'conditions' );
686 // Если условия пусты, то пропускаем цикл
687 if ( empty( $conditions ) ) {
688 continue;
689 }
690 // Промежуточный результат AND условий
691 $and_conditions = null;
692 // Про�
693 одим по AND условиям
694 foreach ( $conditions as $scope ) {
695 $scope_conditions = $this->getPropertyValue( $scope, 'conditions' );
696 // Если условия пусты, то пропускаем цикл
697 if ( empty( $scope_conditions ) ) {
698 continue;
699 }
700 // Промежуточный результат OR условий
701 $or_conditions = null;
702 // Про�
703 одим по OR условиям
704 foreach ( $scope_conditions as $condition ) {
705 $method_name = str_replace( '-', '_', $this->getPropertyValue( $condition, 'param' ) );
706 $operator = $this->getPropertyValue( $condition, 'operator' );
707 $value = $this->getPropertyValue( $condition, 'value' );
708 // Получаем результат OR условий
709 $or_conditions = is_null( $or_conditions ) ? $this->call_method( $method_name, $operator, $value ) : $or_conditions || $this->call_method( $method_name, $operator, $value );
710 }
711 // Получаем результат AND условий
712 $and_conditions = is_null( $and_conditions ) ? $or_conditions : $and_conditions && $or_conditions;
713 }
714 // Получаем результат блока условий
715 $result = $this->getPropertyValue( $filter, 'type' ) == 'showif' ? $and_conditions : ! $and_conditions;
716 }
717 }
718
719 return $result;
720 }
721
722 /**
723 * Call specified method
724 *
725 * @param $method_name
726 * @param $operator
727 * @param $value
728 *
729 * @return bool
730 */
731 private function call_method( $method_name, $operator, $value ) {
732 if ( method_exists( $this, $method_name ) ) {
733 return $this->$method_name( $operator, $value );
734 } else {
735 return apply_filters( 'wbcr/inp/execute/check_condition', false, $method_name, $operator, $value );
736 }
737 }
738
739 /**
740 * Retrieve the first error in a snippet's code
741 *
742 * @param int $snippet_id
743 *
744 * @return array|bool
745 */
746 public function getSnippetError( $snippet_id ) {
747 if ( ! intval( $snippet_id ) ) {
748 return false;
749 }
750
751 $snippet = get_post( $snippet_id );
752
753 if ( ! $snippet ) {
754 return false;
755 }
756
757 $snippet_code = WINP_Helper::get_snippet_code( $snippet );
758 $snippet_code = $this->prepareCode( $snippet_code, $snippet_id );
759
760 $result = $this->executeSnippet( $snippet_code, $snippet_id );
761
762 if ( false !== $result ) {
763 return false;
764 }
765
766 $error = error_get_last();
767
768 if ( is_null( $error ) ) {
769 return false;
770 }
771
772 return $error;
773 }
774
775 /**
776 * Prepare the code by removing php tags from beginning and end
777 *
778 * @param string $code
779 * @param integer $snippet_id
780 *
781 * @return string
782 */
783 public function prepareCode( $code, $snippet_id ) {
784 $snippet_type = WINP_Helper::get_snippet_type( $snippet_id );
785 if ( $snippet_type != WINP_SNIPPET_TYPE_UNIVERSAL && $snippet_type != WINP_SNIPPET_TYPE_CSS && $snippet_type != WINP_SNIPPET_TYPE_JS && $snippet_type != WINP_SNIPPET_TYPE_HTML ) {
786 /* Remove <?php and <? from beginning of snippet */
787 $code = preg_replace( '|^[\s]*<\?(php)?|', '', $code );
788
789 /* Remove ?> from end of snippet */
790 $code = preg_replace( '|\?>[\s]*$|', '', $code );
791 }
792
793 return $code;
794 }
795
796 /**
797 * Get current URL
798 *
799 * @return string
800 */
801 private function getCurrentUrl() {
802 $out = '';
803 $url = explode( '?', $_SERVER['REQUEST_URI'], 2 );
804 if ( isset( $url[0] ) ) {
805 $out = trim( $url[0], '/' );
806 }
807
808 return $out ? urldecode( $out ) : '/';
809 }
810
811 /**
812 * Get referer URL
813 *
814 * @return string
815 */
816 private function getRefererUrl() {
817 $out = '';
818 $url = explode( '?', str_replace( site_url(), '', $_SERVER['HTTP_REFERER'] ), 2 );
819 if ( isset( $url[0] ) ) {
820 $out = trim( $url[0], '/' );
821 }
822
823 return $out ? urldecode( $out ) : '/';
824 }
825
826 /**
827 * Check by operator
828 *
829 * @param $operation
830 * @param $first
831 * @param $second
832 * @param $third
833 *
834 * @return bool
835 */
836 public function checkByOperator( $operation, $first, $second, $third = false ) {
837 switch ( $operation ) {
838 case 'equals':
839 if ( is_array( $second ) ) {
840 return in_array( $first, $second );
841 } else {
842 return $first === $second;
843 }
844 case 'notequal':
845 if ( is_array( $second ) ) {
846 return ! in_array( $first, $second );
847 } else {
848 return $first !== $second;
849 }
850 case 'less':
851 case 'older':
852 return $first > $second;
853 case 'greater':
854 case 'younger':
855 return $first < $second;
856 case 'contains':
857 return strpos( $first, $second ) !== false;
858 case 'notcontain':
859 return strpos( $first, $second ) === false;
860 case 'between':
861 return $first < $second && $second < $third;
862
863 default:
864 return $first === $second;
865 }
866 }
867
868 /**
869 * A role of the user who views your website. The role "guest" is applied for unregistered users.
870 *
871 * @param string $operator
872 * @param string $value
873 *
874 * @return boolean
875 */
876 private function user_role( $operator, $value ) {
877 if ( ! is_user_logged_in() ) {
878 return $this->checkByOperator( $operator, $value, 'guest' );
879 } else {
880 $current_user = wp_get_current_user();
881 if ( ! ( $current_user instanceof WP_User ) ) {
882 return false;
883 }
884
885 return $this->checkByOperator( $operator, $value, $current_user->roles[0] );
886 }
887 }
888
889 /**
890 * Get timestamp
891 *
892 * @param $units
893 * @param $count
894 *
895 * @return integer
896 */
897 private function getTimestamp( $units, $count ) {
898 switch ( $units ) {
899 case 'seconds':
900 return $count;
901 case 'minutes':
902 return $count * MINUTE_IN_SECONDS;
903 case 'hours':
904 return $count * HOUR_IN_SECONDS;
905 case 'days':
906 return $count * DAY_IN_SECONDS;
907 case 'weeks':
908 return $count * WEEK_IN_SECONDS;
909 case 'months':
910 return $count * MONTH_IN_SECONDS;
911 case 'years':
912 return $count * YEAR_IN_SECONDS;
913
914 default:
915 return $count;
916 }
917 }
918
919 /**
920 * Get date timestamp
921 *
922 * @param $value
923 *
924 * @return integer
925 */
926 public function getDateTimestamp( $value ) {
927 if ( is_object( $value ) ) {
928 return ( current_time( 'timestamp' ) - $this->getTimestamp( $value->units, $value->unitsCount ) ) * 1000;
929 } else {
930 return $value;
931 }
932 }
933
934 /**
935 * The date when the user who views your website was registered.
936 * For unregistered users this date always equals to 1 Jan 1970.
937 *
938 * @param string $operator
939 * @param string $value
940 *
941 * @return boolean
942 */
943 private function user_registered( $operator, $value ) {
944 if ( ! is_user_logged_in() ) {
945 return false;
946 } else {
947 $user = wp_get_current_user();
948 $registered = strtotime( $user->data->user_registered ) * 1000;
949
950 if ( $operator == 'equals' || $operator == 'notequal' ) {
951 $registered = $registered / 1000;
952 $timestamp = round( $this->getDateTimestamp( $value ) / 1000 );
953
954 return $this->checkByOperator( $operator, date( 'Y-m-d', $timestamp ), date( 'Y-m-d', $registered ) );
955 } elseif ( $operator == 'between' ) {
956 $start_timestamp = $this->getDateTimestamp( $value->start );
957 $end_timestamp = $this->getDateTimestamp( $value->end );
958
959 return $this->checkByOperator( $operator, $start_timestamp, $registered, $end_timestamp );
960 } else {
961 $timestamp = $this->getDateTimestamp( $value );
962
963 return $this->checkByOperator( $operator, $timestamp, $registered );
964 }
965 }
966 }
967
968 /**
969 * Check the user views your website from mobile device or not
970 *
971 * @param string $operator
972 * @param string $value
973 *
974 * @return boolean
975 *
976 * @link https://stackoverflow.com/a/4117597
977 */
978 private function user_mobile( $operator, $value ) {
979 $useragent = $_SERVER['HTTP_USER_AGENT'];
980
981 if ( preg_match( '/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i', $useragent ) || preg_match( '/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i', substr( $useragent, 0, 4 ) ) ) {
982 return $operator === 'equals' && $value === 'yes' || $operator === 'notequal' && $value === 'no';
983 } else {
984 return $operator === 'notequal' && $value === 'yes' || $operator === 'equals' && $value === 'no';
985 }
986 }
987
988 /**
989 * Determines whether the user's browser has a cookie with a given name
990 *
991 * @param $operator
992 * @param $value
993 *
994 * @return boolean
995 */
996 private function user_cookie_name( $operator, $value ) {
997 if ( isset( $_COOKIE[ $value ] ) ) {
998 return $operator === 'equals';
999 } else {
1000 return $operator === 'notequal';
1001 }
1002 }
1003
1004 /**
1005 * A some selected page
1006 *
1007 * @param $operator
1008 * @param $value
1009 *
1010 * @return boolean
1011 */
1012 private function location_some_page( $operator, $value ) {
1013 $post_id = ( ! is_404() && ! is_search() && ! is_archive() && ! is_home() ) ? get_the_ID() : false;
1014
1015 switch ( $value ) {
1016 case 'base_web': // Basic - Entire Website
1017 $result = true;
1018 break;
1019 case 'base_sing': // Basic - All Singulars
1020 $result = is_singular();
1021 break;
1022 case 'base_arch': // Basic - All Archives
1023 $result = is_archive();
1024 break;
1025 case 'spec_404': // Special Pages - 404 Page
1026 $result = is_404();
1027 break;
1028 case 'spec_search': // Special Pages - Search Page
1029 $result = is_search();
1030 break;
1031 case 'spec_blog': // Special Pages - Blog / Posts Page
1032 $result = is_home();
1033 break;
1034 case 'spec_front': // Special Pages - Front Page
1035 $result = is_front_page();
1036 break;
1037 case 'spec_date': // Special Pages - Date Archive
1038 $result = is_date();
1039 break;
1040 case 'spec_auth': // Special Pages - Author Archive
1041 $result = is_author();
1042 break;
1043 case 'post_all': // Posts - All Posts
1044 case 'page_all': // Pages - All Pages
1045 $result = false;
1046 if ( false !== $post_id ) {
1047 $post_type = 'post_all' == $value ? 'post' : 'page';
1048 $result = $post_type == get_post_type( $post_id );
1049 }
1050 break;
1051 case 'post_arch': // Posts - All Posts Archive
1052 case 'page_arch': // Pages - All Pages Archive
1053 $result = false;
1054 if ( is_archive() ) {
1055 $post_type = 'post_arch' == $value ? 'post' : 'page';
1056 $result = $post_type == get_post_type();
1057 }
1058 break;
1059 case 'post_cat': // Posts - All Categories Archive
1060 case 'post_tag': // Posts - All Tags Archive
1061 $result = false;
1062 if ( is_archive() && 'post' == get_post_type() ) {
1063 $taxonomy = 'post_tag' == $value ? 'post_tag' : 'category';
1064 $obj = get_queried_object();
1065
1066 $current_taxonomy = '';
1067 if ( '' !== $obj && null !== $obj ) {
1068 $current_taxonomy = $obj->taxonomy;
1069 }
1070
1071 if ( $current_taxonomy == $taxonomy ) {
1072 $result = true;
1073 }
1074 }
1075 break;
1076
1077 default:
1078 $result = false;
1079 }
1080
1081 if ( WINP_Helper::is_woo_active() ) {
1082 switch ( $value ) {
1083 case 'woo_product':
1084 $result = is_product();
1085 break;
1086 case 'woo_arch':
1087 $result = is_shop();
1088 break;
1089 case 'woo_cart':
1090 $result = is_cart();
1091 break;
1092 case 'woo_checkout':
1093 $result = is_checkout();
1094 break;
1095 case 'woo_checkout_pay':
1096 $result = is_checkout_pay_page();
1097 break;
1098 case 'woo_cat':
1099 $result = is_product_category();
1100 break;
1101 case 'woo_tag':
1102 $result = is_product_tag();
1103 break;
1104 }
1105 }
1106
1107 return $this->checkByOperator( $operator, $result, true );
1108 }
1109
1110 /**
1111 * An URL of the current page where a user who views your website is located
1112 *
1113 * @param $operator
1114 * @param $value
1115 *
1116 * @return boolean
1117 */
1118 private function location_page( $operator, $value ) {
1119 $url = $this->getCurrentUrl();
1120
1121 return $url ? $this->checkByOperator( $operator, trim( $url, '/' ), trim( $value, '/' ) ) : false;
1122 }
1123
1124 /**
1125 * A referrer URL which has brought a user to the current page
1126 *
1127 * @param $operator
1128 * @param $value
1129 *
1130 * @return boolean
1131 */
1132 private function location_referrer( $operator, $value ) {
1133 $url = $this->getRefererUrl();
1134
1135 return $url ? $this->checkByOperator( $operator, trim( $url, '/' ), trim( $value, '/' ) ) : false;
1136 }
1137
1138 /**
1139 * A post type of the current page
1140 *
1141 * @param $operator
1142 * @param $value
1143 *
1144 * @return boolean
1145 */
1146 private function location_post_type( $operator, $value ) {
1147 if ( is_singular() ) {
1148 return $this->checkByOperator( $operator, $value, get_post_type() );
1149 }
1150
1151 return false;
1152 }
1153
1154 /**
1155 * A taxonomy page
1156 *
1157 * @param $operator
1158 * @param $value
1159 *
1160 * @return boolean
1161 * @since 2.2.8 The bug is fixed, the condition was not checked
1162 * for tachonomies, only posts.
1163 */
1164 private function location_taxonomy( $operator, $value ) {
1165 $term_id = null;
1166
1167 if ( is_tax() || is_tag() || is_category() ) {
1168 $term_id = get_queried_object()->term_id;
1169
1170 if ( $term_id ) {
1171 return $this->checkByOperator( $operator, intval( $value ), $term_id );
1172 }
1173 }
1174
1175 return false;
1176 }
1177
1178 /**
1179 * A taxonomy of the current page
1180 *
1181 * @param $operator
1182 * @param $value
1183 *
1184 * @return boolean
1185 * @since 2.4.0
1186 */
1187 private function page_taxonomy( $operator, $value ) {
1188 $term_id = null;
1189
1190 if ( is_singular() ) {
1191 $post_cat = get_the_category( get_the_ID() );
1192 if ( is_array( $post_cat ) ) {
1193 foreach ( $post_cat as $item ) {
1194 $term_id[] = $item->term_id;
1195 }
1196 }
1197 }
1198
1199 if ( $term_id ) {
1200 return $this->checkByOperator( $operator, intval( $value ), $term_id );
1201 }
1202
1203 return false;
1204 }
1205 }
1206