PluginProbe ʕ •ᴥ•ʔ
Post Views Counter / 1.7.5
Post Views Counter v1.7.5
1.7.13 1.7.12 1.7.11 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.2.0 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3 1.3.1 1.3.10 1.3.11 1.3.12 1.3.13 1.3.2 1.3.2.1 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.7.0 1.7.1 1.7.10 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.7.7 1.7.8 1.7.9
post-views-counter / includes / functions.php
post-views-counter / includes Last commit date
class-admin.php 5 months ago class-columns-modal.php 5 months ago class-columns.php 5 months ago class-counter.php 5 months ago class-crawler-detect.php 5 months ago class-cron.php 5 months ago class-dashboard.php 5 months ago class-frontend.php 5 months ago class-functions.php 5 months ago class-import.php 5 months ago class-integration-gutenberg.php 5 months ago class-integrations.php 5 months ago class-query.php 5 months ago class-settings-api.php 5 months ago class-settings-display.php 5 months ago class-settings-general.php 5 months ago class-settings-integrations.php 5 months ago class-settings-other.php 5 months ago class-settings-reports.php 5 months ago class-settings.php 5 months ago class-toolbar.php 5 months ago class-traffic-signals.php 5 months ago class-update.php 5 months ago class-widgets.php 5 months ago functions.php 5 months ago
functions.php
924 lines
1 <?php
2 /**
3 * Post Views Counter pluggable template functions
4 *
5 * Override any of those functions by copying it to your theme or replace it via plugin
6 *
7 * @author Digital Factory
8 * @package Post Views Counter
9 * @since 1.0.0
10 */
11 // exit if accessed directly
12 if ( ! defined( 'ABSPATH' ) )
13 exit;
14
15 /**
16 * Get post views for a post or array of posts.
17 *
18 * @global object $wpdb
19 *
20 * @param int|array $post_id
21 * @param string $period
22 * @param array $args Optional arguments to override period parsing
23 *
24 * @return int
25 */
26 if ( ! function_exists( 'pvc_get_post_views' ) ) {
27 function pvc_get_post_views( $post_id = 0, $period = 'total', $args = [] ) {
28 global $wpdb;
29
30 // ensure args is an array
31 if ( ! is_array( $args ) ) {
32 $args = [];
33 }
34
35 // sanitize period
36 $period = sanitize_key( $period );
37
38 if ( empty( $post_id ) )
39 $post_id = get_the_ID();
40
41 if ( is_array( $post_id ) ) {
42 $numbers = array_filter( array_unique( array_map( 'intval', $post_id ) ) );
43 $post_id = implode( ',', $numbers );
44 } else if ( $post_id === 'all' ) {
45 $numbers = [];
46 } else {
47 $post_id = (int) $post_id;
48 $numbers = [ $post_id ];
49 }
50
51 // set where clause
52 $where = [ 'type' => 'type = 4' ];
53
54 // override type if explicitly provided in args
55 if ( isset( $args['type'] ) ) {
56 $where['type'] = 'type = ' . (int) $args['type'];
57 if ( ! isset( $args['content'] ) )
58 $where['content'] = 'content = 0';
59 } elseif ( isset( $args['period_type'] ) ) {
60 $period_type = sanitize_key( $args['period_type'] );
61 $type_map = [
62 'day' => 0,
63 'week' => 1,
64 'month' => 2,
65 'year' => 3,
66 'total' => 4
67 ];
68 if ( isset( $type_map[ $period_type ] ) ) {
69 $where['type'] = 'type = ' . $type_map[ $period_type ];
70 if ( ! isset( $args['content'] ) )
71 $where['content'] = 'content = 0';
72 }
73 }
74
75 // optional period range (e.g., for day-based ranges within a month)
76 $range_from = null;
77 $range_to = null;
78
79 if ( isset( $args['period_range'] ) && is_array( $args['period_range'] ) ) {
80 $range = array_values( $args['period_range'] );
81 if ( isset( $range[0], $range[1] ) ) {
82 $range_from = (int) $range[0];
83 $range_to = (int) $range[1];
84 }
85 } elseif ( isset( $args['period_from'], $args['period_to'] ) ) {
86 $range_from = (int) $args['period_from'];
87 $range_to = (int) $args['period_to'];
88 }
89
90 if ( $range_from && $range_to ) {
91 $range_min = min( $range_from, $range_to );
92 $range_max = max( $range_from, $range_to );
93
94 $where['period'] = 'CAST( period AS SIGNED ) <= ' . $range_max . ' AND CAST( period AS SIGNED ) >= ' . $range_min;
95
96 // default to day type when using explicit ranges and no type set
97 if ( ! isset( $where['type'] ) ) {
98 $where['type'] = 'type = 0';
99 if ( ! isset( $args['content'] ) )
100 $where['content'] = 'content = 0';
101 }
102 }
103
104 // update where clause
105 $where = apply_filters( 'pvc_get_post_views_period_where', $where, $period, $post_id, $args );
106
107 // updated where clause
108 $_where = [];
109
110 // sanitize where clause
111 foreach ( $where as $index => $value ) {
112 if ( $index === 'type' || $index === 'content' )
113 $_where[$index] = preg_replace( '/[^0-9]/', '', $value );
114 elseif ( $index === 'period' ) {
115 $values = preg_match_all( '/\d+/', $value, $matches );
116
117 // any values?
118 if ( $values !== false && $values > 0 )
119 $_where['period'] = $matches[0];
120 }
121 }
122
123 // get current number of ids
124 $ids_count = count( $numbers );
125
126 $where_clause = '';
127
128 // add post ids if needed
129 if ( $ids_count ) {
130 $where_clause .= " WHERE id IN (" . implode( ',', array_fill( 0, $ids_count, '%d' ) ) . ") AND";
131 } else {
132 $where_clause .= " WHERE";
133 }
134
135 // validate where clause
136 foreach( $_where as $index => $value ) {
137 if ( $index === 'type' ) {
138 $where_clause .= ' type = %d';
139 $numbers[] = (int) $value;
140 } elseif ( $index === 'content' ) {
141 $where_clause .= ' AND content = %d';
142 $numbers[] = (int) $value;
143 } elseif ( $index === 'period' ) {
144 $nop = count( $_where['period'] );
145
146 if ( $nop === 1 ) {
147 $where_clause .= ' AND CAST( period AS SIGNED ) = %d';
148 $numbers[] = (int) $_where['period'][0];
149 } elseif ( $nop === 2 ) {
150 $where_clause .= ' AND CAST( period AS SIGNED ) <= %d AND CAST( period AS SIGNED ) >= %d';
151 $numbers[] = (int) $_where['period'][0];
152 $numbers[] = (int) $_where['period'][1];
153 }
154 }
155 }
156
157 // prepare query
158 $query = $wpdb->prepare( "SELECT SUM(count) AS views FROM " . $wpdb->prefix . "post_views" . $where_clause, $numbers );
159
160 // calculate query hash
161 $query_hash = md5( $query );
162
163 // get cached data
164 $post_views = wp_cache_get( $query_hash, 'pvc-get_post_views' );
165
166 // cached data not found?
167 if ( $post_views === false ) {
168 // get post views
169 $post_views = (int) $wpdb->get_var( $query );
170
171 // set the cache expiration, 5 minutes by default
172 $expire = absint( apply_filters( 'pvc_object_cache_expire', 300 ) );
173
174 // add cached post views
175 wp_cache_add( $query_hash, $post_views, 'pvc-get_post_views', $expire );
176 }
177
178 return (int) apply_filters( 'pvc_get_post_views', $post_views, $post_id, $period, $args );
179 }
180 }
181
182 /**
183 * Get views query.
184 *
185 * @global object $wpdb
186 *
187 * @param array $args
188 *
189 * @return int|array
190 */
191 if ( ! function_exists( 'pvc_get_views' ) ) {
192 function pvc_get_views( $args = [] ) {
193 global $wpdb;
194
195 $range = [];
196 $defaults = [
197 'fields' => 'views',
198 'post_id' => '',
199 'post_type' => '',
200 'views_query' => [
201 'year' => '',
202 'month' => '',
203 'week' => '',
204 'day' => '',
205 'after' => '', // string or array
206 'before' => '', // string or array
207 'inclusive' => true
208 ]
209 ];
210
211 // merge default options with new arguments
212 $args = array_merge( $defaults, $args );
213
214 // check views query
215 if ( ! is_array( $args['views_query'] ) )
216 $args['views_query'] = $defaults['views_query'];
217
218 // merge views query too
219 $args['views_query'] = array_merge( $defaults['views_query'], $args['views_query'] );
220
221 // filter arguments
222 $args = apply_filters( 'pvc_get_views_args', $args );
223
224 // check post types
225 if ( is_string( $args['post_type'] ) )
226 $args['post_type'] = [ $args['post_type'] ];
227 elseif ( ! is_array( $args['post_type'] ) )
228 $args['post_type'] = [];
229
230 // get number of post types
231 $post_types_count = count( $args['post_type'] );
232
233 // check post ids
234 if ( is_array( $args['post_id'] ) && ! empty( $args['post_id'] ) )
235 $args['post_id'] = array_filter( array_unique( array_map( 'intval', $args['post_id'] ) ) );
236 elseif ( is_string( $args['post_id'] ) || is_numeric( $args['post_id'] ) ) {
237 $post_id = (int) $args['post_id'];
238
239 if ( $post_id === 0 )
240 $args['post_id'] = [];
241 else
242 $args['post_id'] = [ $post_id ];
243 } else
244 $args['post_id'] = [];
245
246 // get number of post ids
247 $post_ids_count = count( $args['post_id'] );
248
249 // placeholder for empty query data
250 $query_data = [ 1 ];
251
252 // set query data
253 if ( $post_ids_count === 0 && $post_types_count === 0 )
254 $query_data = [ 1 ];
255 elseif ( $post_ids_count === 0 )
256 $query_data = array_merge( $query_data, array_values( $args['post_type'] ) );
257 elseif ( $post_types_count === 0 )
258 $query_data = array_merge( $query_data, array_values( $args['post_id'] ) );
259 else
260 $query_data = array_merge( $query_data, array_values( $args['post_id'] ), array_values( $args['post_type'] ) );
261
262 // set where clause
263 $where = apply_filters( 'pvc_get_views_period_where', [], $args );
264
265 // check fields
266 if ( ! in_array( $args['fields'], [ 'views', 'date=>views' ], true ) )
267 $args['fields'] = $defaults['fields'];
268
269 $query_chunks = [];
270 $views_query = '';
271
272 // views query after/before parameters work only when fields == views
273 if ( $args['fields'] === 'views' ) {
274 // check views query inclusive
275 if ( ! isset( $args['views_query']['inclusive'] ) )
276 $args['views_query']['inclusive'] = $defaults['views_query']['inclusive'];
277 else
278 $args['views_query']['inclusive'] = (bool) $args['views_query']['inclusive'];
279
280 // check after and before dates
281 foreach ( [ 'after' => '>', 'before' => '<' ] as $date => $type ) {
282 $year_ = null;
283 $month_ = null;
284 $week_ = null;
285 $day_ = null;
286
287 // check views query date
288 if ( ! empty( $args['views_query'][$date] ) ) {
289 // is it a date array?
290 if ( is_array( $args['views_query'][$date] ) ) {
291 // check views query $date date year
292 if ( ! empty( $args['views_query'][$date]['year'] ) )
293 $year_ = str_pad( (int) $args['views_query'][$date]['year'], 4, 0, STR_PAD_LEFT );
294
295 // check views query date month
296 if ( ! empty( $args['views_query'][$date]['month'] ) )
297 $month_ = str_pad( (int) $args['views_query'][$date]['month'], 2, 0, STR_PAD_LEFT );
298
299 // check views query date week
300 if ( ! empty( $args['views_query'][$date]['week'] ) )
301 $week_ = str_pad( (int) $args['views_query'][$date]['week'], 2, 0, STR_PAD_LEFT );
302
303 // check views query date day
304 if ( ! empty( $args['views_query'][$date]['day'] ) )
305 $day_ = str_pad( (int) $args['views_query'][$date]['day'], 2, 0, STR_PAD_LEFT );
306 // is it a date string?
307 } elseif ( is_string( $args['views_query'][$date] ) ) {
308 $time_ = strtotime( $args['views_query'][$date] );
309
310 // valid datetime?
311 if ( $time_ !== false ) {
312 // week does not exists here, string dates are always treated as year + month + day
313 list( $day_, $month_, $year_ ) = explode( ' ', date( "d m Y", $time_ ) );
314 }
315 }
316
317 // valid date?
318 if ( ! ( $year_ === null && $month_ === null && $week_ === null && $day_ === null ) ) {
319 $query_chunks[] = [
320 'year' => $year_,
321 'month' => $month_,
322 'day' => $day_,
323 'week' => $week_,
324 'type' => $type . ( $args['views_query']['inclusive'] ? '=' : '' )
325 ];
326 }
327 }
328 }
329
330 if ( ! empty( $query_chunks ) ) {
331 $valid_dates = true;
332
333 // after and before?
334 if ( count( $query_chunks ) === 2 ) {
335 // before and after dates should be the same
336 foreach ( [ 'year', 'month', 'day', 'week' ] as $date_type ) {
337 if ( ! ( ( $query_chunks[0][$date_type] !== null && $query_chunks[1][$date_type] !== null ) || ( $query_chunks[0][$date_type] === null && $query_chunks[1][$date_type] === null ) ) )
338 $valid_dates = false;
339 }
340 }
341
342 if ( $valid_dates ) {
343 foreach ( $query_chunks as $chunk ) {
344 // year
345 if ( isset( $chunk['year'] ) ) {
346 // year, week
347 if ( isset( $chunk['week'] ) ) {
348 $where['type'] = 'pvc.type = 1';
349 $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( pvc.period AS SIGNED ) " . $chunk['type'] . " " . (int) ( $chunk['year'] . $chunk['week'] );
350 }
351 // year, month
352 elseif ( isset( $chunk['month'] ) ) {
353 // year, month, day
354 if ( isset( $chunk['day'] ) ) {
355 $where['type'] = 'pvc.type = 0';
356 $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( pvc.period AS SIGNED ) " . $chunk['type'] . " " . (int) ( $chunk['year'] . $chunk['month'] . $chunk['day'] );
357 }
358 // year, month
359 else {
360 $where['type'] = 'pvc.type = 2';
361 $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( pvc.period AS SIGNED ) " . $chunk['type'] . " " . (int) ( $chunk['year'] . $chunk['month'] );
362 }
363 // year
364 } else {
365 $where['type'] = 'pvc.type = 3';
366 $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( pvc.period AS SIGNED ) " . $chunk['type'] . " " . (int) ( $chunk['year'] );
367 }
368 // month
369 } elseif ( isset( $chunk['month'] ) ) {
370 // month, day
371 if ( isset( $chunk['day'] ) ) {
372 $where['type'] = 'pvc.type = 0';
373 $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( RIGHT( pvc.period, 4 ) AS SIGNED ) " . $chunk['type'] . " " . (int) ( $chunk['month'] . $chunk['day'] );
374 // month
375 } else {
376 $where['type'] = 'pvc.type = 2';
377 $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( RIGHT( pvc.period, 2 ) AS SIGNED ) " . $chunk['type'] . " " . (int) ( $chunk['month'] );
378 }
379 // week
380 } elseif ( isset( $chunk['week'] ) ) {
381 $where['type'] = 'pvc.type = 1';
382 $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( RIGHT( pvc.period, 2 ) AS SIGNED ) " . $chunk['type'] . " " . (int) ( $chunk['week'] );
383 }
384 // day
385 elseif ( isset( $chunk['day'] ) ) {
386 $where['type'] = 'pvc.type = 0';
387 $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( RIGHT( pvc.period, 2 ) AS SIGNED ) " . $chunk['type'] . " " . (int) ( $chunk['day'] );
388 }
389 }
390 }
391 }
392 }
393
394 $special_views_query = ( $views_query !== '' );
395
396 if ( $args['fields'] === 'date=>views' || $views_query === '' ) {
397 // check views query year
398 if ( ! empty( $args['views_query']['year'] ) )
399 $year = str_pad( (int) $args['views_query']['year'], 4, 0, STR_PAD_LEFT );
400
401 // check views query month
402 if ( ! empty( $args['views_query']['month'] ) )
403 $month = str_pad( (int) $args['views_query']['month'], 2, 0, STR_PAD_LEFT );
404
405 // check views query week
406 if ( ! empty( $args['views_query']['week'] ) )
407 $week = str_pad( (int) $args['views_query']['week'], 2, 0, STR_PAD_LEFT );
408
409 // check views query day
410 if ( ! empty( $args['views_query']['day'] ) )
411 $day = str_pad( (int) $args['views_query']['day'], 2, 0, STR_PAD_LEFT );
412
413 // year
414 if ( isset( $year ) ) {
415 // year, week
416 if ( isset( $week ) ) {
417 if ( $args['fields'] === 'date=>views' ) {
418 // create date based on week number
419 $date = new DateTime( $year . 'W' . $week );
420
421 // get monday
422 $monday = $date->format( 'd' );
423
424 // get month of monday
425 $monday_month = $date->format( 'm' );
426
427 // prepare range
428 for( $i = 1; $i <= 6; $i++ ) {
429 $range[(string) ( $date->format( 'Y' ) . $date->format( 'm' ) . $date->format( 'd' ) )] = 0;
430
431 $date->modify( '+1days' );
432 }
433
434 $range[(string) ( $date->format( 'Y' ) . $date->format( 'm' ) . $date->format( 'd' ) )] = 0;
435
436 // get month of sunday
437 $sunday_month = $date->format( 'm' );
438
439 $where['type'] = 'pvc.type = 0';
440 $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( pvc.period AS SIGNED ) >= " . (int) ( $year . $monday_month . $monday ) . " AND CAST( pvc.period AS SIGNED ) <= " . (int) ( $date->format( 'Y' ) . $sunday_month . $date->format( 'd' ) );
441 } else {
442 $where['type'] = 'pvc.type = 1';
443 $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( pvc.period AS SIGNED ) = " . (int) ( $year . $week );
444 }
445 // year, month
446 } elseif ( isset( $month ) ) {
447 // year, month, day
448 if ( isset( $day ) ) {
449 if ( $args['fields'] === 'date=>views' )
450 // prepare range
451 $range[(string) ( $year . $month . $day )] = 0;
452
453 $where['type'] = 'pvc.type = 0';
454 $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( pvc.period AS SIGNED ) = " . (int) ( $year . $month . $day );
455 // year, month
456 } else {
457 if ( $args['fields'] === 'date=>views' ) {
458 // create date
459 $date = new DateTime( $year . '-' . $month . '-01' );
460
461 // get last day
462 $last = $date->format( 't' );
463
464 // prepare range
465 for( $i = 1; $i <= $last; $i++ ) {
466 $range[(string) ( $year . $month . str_pad( $i, 2, 0, STR_PAD_LEFT ) )] = 0;
467 }
468
469 $where['type'] = 'pvc.type = 0';
470 $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( pvc.period AS SIGNED ) >= " . (int) ( $year . $month ) . "01 AND CAST( pvc.period AS SIGNED ) <= " . (int) ( $year . $month . $last );
471 } else {
472 $where['type'] = 'pvc.type = 2';
473 $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( pvc.period AS SIGNED ) = " . (int) ( $year . $month );
474 }
475 }
476 // year
477 } else {
478 if ( $args['fields'] === 'date=>views' ) {
479 // prepare range
480 for( $i = 1; $i <= 12; $i++ ) {
481 $range[(string) ( $year . str_pad( $i, 2, 0, STR_PAD_LEFT ) )] = 0;
482 }
483
484 // create date
485 $date = new DateTime( $year . '-12-01' );
486
487 $where['type'] = 'pvc.type = 2';
488 $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( pvc.period AS SIGNED ) >= " . (int) ( $year ) . "01 AND CAST( pvc.period AS SIGNED ) <= " . (int) ( $year ) . "12";
489 } else {
490 $where['type'] = 'pvc.type = 3';
491 $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( pvc.period AS SIGNED ) = " . (int) ( $year );
492 }
493 }
494 // month
495 } elseif ( isset( $month ) ) {
496 // month, day
497 if ( isset( $day ) ) {
498 $where['type'] = 'pvc.type = 0';
499 $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( RIGHT( pvc.period, 4 ) AS SIGNED ) = " . (int) ( $month . $day );
500 // month
501 } else {
502 $where['type'] = 'pvc.type = 2';
503 $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( RIGHT( pvc.period, 2 ) AS SIGNED ) = " . (int) ( $month );
504 }
505 // week
506 } elseif ( isset( $week ) ) {
507 $where['type'] = 'pvc.type = 1';
508 $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( RIGHT( pvc.period, 2 ) AS SIGNED ) = " . (int) ( $week );
509 // day
510 } elseif ( isset( $day ) ) {
511 $where['type'] = 'pvc.type = 0';
512 $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( RIGHT( pvc.period, 2 ) AS SIGNED ) = " . (int) ( $day );
513 }
514 }
515
516 // update where clause
517 $where['type'] = [ 'pvc.type = 4' ];
518
519 $query = $wpdb->prepare(
520 "SELECT " . ( $args['fields'] === 'date=>views' ? 'pvc.period, ' : '' ) . "SUM( COALESCE( pvc.count, 0 ) ) AS post_views
521 FROM " . $wpdb->prefix . "posts wpp
522 LEFT JOIN " . $wpdb->prefix . "post_views pvc ON pvc.id = wpp.ID AND 1 = %d" . ( $views_query !== '' ? ' ' . $views_query : ' AND ' . implode( ' AND ', $where ) ) . ( ! empty( $args['post_id'] ) ? ' AND pvc.id IN (' . implode( ',', array_fill( 0, $post_ids_count, '%d' ) ) . ')' : '' ) . "
523 " . ( ! empty( $args['post_type'] ) ? 'WHERE wpp.post_type IN (' . implode( ',', array_fill( 0, $post_types_count, '%s' ) ) . ')' : '' ) . "
524 " . ( $views_query !== '' && $special_views_query === false ? 'GROUP BY pvc.period HAVING post_views > 0' : '' ),
525 $query_data
526 );
527
528 $query = apply_filters( 'pvc_get_views_query_sql', $query, $args, $views_query );
529
530 // get cached data
531 $post_views = wp_cache_get( md5( $query ), 'pvc-get_views' );
532
533 // cached data not found?
534 if ( $post_views === false ) {
535 if ( $args['fields'] === 'date=>views' && ! empty( $range ) ) {
536 $results = $wpdb->get_results( $query );
537
538 if ( ! empty( $results ) ) {
539 foreach ( $results as $row ) {
540 $range[$row->period] = (int) $row->post_views;
541 }
542 }
543
544 $post_views = $range;
545 } else
546 $post_views = (int) $wpdb->get_var( $query );
547
548 // set the cache expiration, 5 minutes by default
549 $expire = absint( apply_filters( 'pvc_object_cache_expire', 300 ) );
550
551 wp_cache_add( md5( $query ), $post_views, 'pvc-get_views', $expire );
552 }
553
554 return apply_filters( 'pvc_get_views', $post_views );
555 }
556 }
557
558 /**
559 * Display post views for a given post.
560 *
561 * @param int $post_id
562 * @param bool $display
563 *
564 * @return string|void
565 */
566 if ( ! function_exists( 'pvc_post_views' ) ) {
567 function pvc_post_views( $post_id = 0, $display = true, $period = '' ) {
568 // get all data
569 $post_id = (int) ( empty( $post_id ) ? get_the_ID() : $post_id );
570
571 // get display options
572 $options = Post_Views_Counter()->options['display'];
573
574 // get post views
575 $views = pvc_get_post_views( $post_id, $period !== '' ? $period : $options['display_period'] );
576
577 // use number format?
578 $views = $options['use_format'] ? number_format_i18n( $views ) : $views;
579
580 // container class
581 $class = apply_filters( 'pvc_post_views_class', 'post-views content-post post-' . $post_id . ' entry-meta', $post_id );
582
583 // dynamic loading?
584 $class .= $options['dynamic_loading'] === true ? ' load-dynamic' : ' load-static';
585
586 // prepare display
587 $label = apply_filters( 'pvc_post_views_label', ( function_exists( 'icl_t' ) ? icl_t( 'Post Views Counter', 'Post Views Label', $options['label'] ) : $options['label'] ), $post_id );
588
589 // add dashicons class if needed
590 $icon_class = strpos( $options['icon_class'], 'dashicons' ) === false ? $options['icon_class'] : 'dashicons ' . $options['icon_class'];
591
592 // prepare icon output
593 $icon = apply_filters( 'pvc_post_views_icon', '<span class="post-views-icon ' . esc_attr( $icon_class ) . '"></span> ', $post_id );
594
595 // final views
596 $views = apply_filters( 'pvc_post_views_number_format', $views, $post_id );
597
598 $html = apply_filters(
599 'pvc_post_views_html',
600 '<div class="' . esc_attr( $class ) . '">
601 ' . ( $options['display_style']['icon'] ? $icon : '' )
602 . ( $options['display_style']['text'] ? '<span class="post-views-label">' . esc_html( $label ) . '</span> ' : '' )
603 . '<span class="post-views-count">' . $views . '</span>
604 </div>',
605 $post_id,
606 $views,
607 $label,
608 $icon
609 );
610
611 if ( $display )
612 echo $html;
613 else
614 return $html;
615 }
616 }
617
618 /**
619 * Get most viewed posts.
620 *
621 * @param array $args
622 *
623 * @return array
624 */
625 if ( ! function_exists( 'pvc_get_most_viewed_posts' ) ) {
626 function pvc_get_most_viewed_posts( $args = [] ) {
627 $args = array_merge(
628 [
629 'posts_per_page' => 10,
630 'order' => 'desc',
631 'post_type' => [ 'post' ],
632 'post_status' => [ 'publish' ],
633 'fields' => 'all',
634 'period' => 'total'
635 ],
636 $args
637 );
638
639 if ( ( is_array( $args['post_type'] ) && in_array( 'attachment', $args['post_type'], true ) ) || ( is_string( $args['post_type'] ) && $args['post_type'] === 'attachment' ) )
640 $args['post_status'][] = 'inherit';
641
642 $args = apply_filters( 'pvc_get_most_viewed_posts_args', $args );
643
644 // force to use filters
645 $args['suppress_filters'] = false;
646
647 // force to use post views as order
648 $args['orderby'] = 'post_views';
649
650 return apply_filters( 'pvc_get_most_viewed_posts', get_posts( $args ), $args );
651 }
652 }
653
654 /**
655 * Display a list of most viewed posts.
656 *
657 * @param array $args
658 * @param bool $display
659 *
660 * @return void|string
661 */
662 if ( ! function_exists( 'pvc_most_viewed_posts' ) ) {
663 function pvc_most_viewed_posts( $args = [], $display = true ) {
664 $defaults = [
665 'number_of_posts' => 5,
666 'post_type' => [ 'post' ],
667 'order' => 'desc',
668 'thumbnail_size' => 'thumbnail',
669 'list_type' => 'unordered',
670 'show_post_views' => true,
671 'show_post_thumbnail' => false,
672 'show_post_author' => false,
673 'show_post_excerpt' => false,
674 'no_posts_message' => __( 'No most viewed posts found.', 'post-views-counter' ),
675 'item_before' => '',
676 'item_after' => '',
677 'period' => 'total'
678 ];
679
680 $args = apply_filters( 'pvc_most_viewed_posts_args', wp_parse_args( $args, $defaults ) );
681
682 // get periods
683 $periods = apply_filters( 'pvc_display_period_options', [ 'total' => __( 'Total Views', 'post-views-counter' ) ] );
684
685 // sanitize arguments
686 $args['show_post_views'] = (bool) $args['show_post_views'];
687 $args['show_post_thumbnail'] = (bool) $args['show_post_thumbnail'];
688 $args['show_post_author'] = (bool) $args['show_post_author'];
689 $args['show_post_excerpt'] = (bool) $args['show_post_excerpt'];
690 $args['period'] = isset( $args['period'] ) && array_key_exists( $args['period'], $periods ) ? $args['period'] : $defaults['period'];
691 $args['post_type'] = isset( $args['post_type'] ) ? $args['post_type'] : $defaults['post_type'];
692
693 // no post types?
694 if ( empty( $args['post_type'] ) )
695 $html = $args['no_posts_message'];
696 else {
697 // get posts
698 $posts = pvc_get_most_viewed_posts( [
699 'posts_per_page' => isset( $args['number_of_posts'] ) ? (int) $args['number_of_posts'] : $defaults['number_of_posts'],
700 'order' => isset( $args['order'] ) ? $args['order'] : $defaults['order'],
701 'post_type' => $args['post_type'],
702 'period' => $args['period']
703 ] );
704
705 if ( ! empty( $posts ) ) {
706 $html = ( $args['list_type'] === 'unordered' ? '<ul>' : '<ol>' );
707
708 foreach ( $posts as $post ) {
709 setup_postdata( $post );
710
711 $html .= '<li>';
712 $html .= apply_filters( 'pvc_most_viewed_posts_item_before', $args['item_before'], $post );
713
714 if ( $args['show_post_thumbnail'] && has_post_thumbnail( $post->ID ) ) {
715 $html .= '<span class="post-thumbnail">' . get_the_post_thumbnail( $post->ID, $args['thumbnail_size'] ) . '</span>';
716 }
717
718 $html .= '<a class="post-title" href="' . get_permalink( $post->ID ) . '">' . get_the_title( $post->ID ) . '</a>' . ( $args['show_post_author'] ? ' <span class="author">(' . get_the_author_meta( 'display_name', $post->post_author ) . ')</span> ' : '' ) . ( $args['show_post_views'] ? ' <span class="count">(' . number_format_i18n( (int) ( property_exists( $post, 'post_views' ) ? $post->post_views : pvc_get_post_views( $post->ID, $args['period'] ) ) ) . ')</span>' : '' );
719
720 if ( $args['show_post_excerpt'] ) {
721 $excerpt = '';
722
723 if ( empty( $post->post_excerpt ) )
724 $text = $post->post_content;
725 else
726 $text = $post->post_excerpt;
727
728 if ( ! empty( $text ) )
729 $excerpt = wp_trim_words( str_replace( ']]>', ']]&gt;', strip_shortcodes( $text ) ), apply_filters( 'excerpt_length', 55 ), apply_filters( 'excerpt_more', ' ' . '[&hellip;]' ) );
730
731 if ( ! empty( $excerpt ) )
732 $html .= '<div class="post-excerpt">' . esc_html( $excerpt ) . '</div>';
733 }
734
735 $html .= apply_filters( 'pvc_most_viewed_posts_item_after', $args['item_after'], $post );
736 $html .= '</li>';
737 }
738
739 wp_reset_postdata();
740
741 $html .= ( $args['list_type'] === 'unordered' ? '</ul>' : '</ol>' );
742 } else
743 $html = $args['no_posts_message'];
744 }
745
746 $html = apply_filters( 'pvc_most_viewed_posts_html', $html, $args );
747
748 if ( $display )
749 echo $html;
750 else
751 return $html;
752 }
753 }
754
755 /**
756 * Update total number of post views for a post.
757 *
758 * @global object $wpdb
759 *
760 * @param int $post_id
761 * @param int $post_views
762 *
763 * @return bool|int
764 */
765 function pvc_update_post_views( $post_id = 0, $post_views = 0 ) {
766 global $wpdb;
767
768 // cast post ID
769 $post_id = (int) $post_id;
770
771 // get post
772 $post = get_post( $post_id );
773
774 // check if post exists
775 if ( empty( $post ) )
776 return false;
777
778 // cast number of views
779 $post_views = (int) $post_views;
780 $post_views = $post_views < 0 ? 0 : $post_views;
781
782 // change post views?
783 $post_views = apply_filters( 'pvc_update_post_views_count', $post_views, $post_id );
784
785 // insert or update database post views count
786 $wpdb->query( $wpdb->prepare( "INSERT INTO " . $wpdb->prefix . "post_views (id, type, period, count) VALUES (%d, %d, %s, %d) ON DUPLICATE KEY UPDATE count = %d", $post_id, 4, 'total', $post_views, $post_views ) );
787
788 // query fails only if it returns false
789 return apply_filters( 'pvc_update_post_views', $post_id );
790 }
791
792 /**
793 * View post manually function.
794 *
795 * By default this function has limitations. It works properly only between
796 * wp_loaded (minimum priority 10) and wp_head (maximum priority 6) actions and
797 * it can handle only one function execution per site request.
798 *
799 * To bypass these limitations there is a $bypass_content argument. It requires
800 * JavaScript or REST API as counter mode but it extends the ability to use
801 * pvc_view_post up to wp_print_footer_scripts (maximum priority 10) action. It
802 * also bypass one function execution limitation to allow multiple function
803 * calls during one site request. This also includes the correct saving of
804 * cookies.
805 *
806 * @since 1.2.0
807 *
808 * @param int $post_id
809 * @param bool $bypass_content
810 *
811 * @return bool
812 */
813 function pvc_view_post( $post_id = 0, $bypass_content = false ) {
814 // no post id?
815 if ( empty( $post_id ) ) {
816 // get current id
817 $post_id = get_the_ID();
818 } else {
819 // cast post id
820 $post_id = (int) $post_id;
821 }
822
823 // get post
824 $post = get_post( $post_id );
825
826 // invalid post?
827 if ( ! is_a( $post, 'WP_Post' ) )
828 return false;
829
830 // get main instance
831 $pvc = Post_Views_Counter();
832
833 if ( $bypass_content )
834 $pvc->counter->add_to_queue( $post_id );
835 else
836 $pvc->counter->check_post( $post_id );
837
838 return true;
839 }
840
841 /**
842 * Convert string to date.
843 *
844 * @param string $period
845 * @return DateTiem object
846 */
847 if ( ! function_exists( 'pvc_period2date' ) ) {
848 function pvc_period2date( $period ) {
849 $datetime = false;
850
851 // check requested period by string length
852 $length = is_string( $period ) ? strlen( $period ) : 0;
853
854 if ( $length ) {
855 switch ( $length ) {
856 // day
857 case 8:
858 $datetime = date_create_from_format( 'Ymd' , $period );
859 break;
860
861 // week
862 case 7:
863 // get year and week from string
864 $period_year = (int) substr( $period, 0, -3 );
865 $period_week = (int) substr( $period, 4, -1 );
866
867 $datetime = new DateTime();
868 $datetime->setISODate( $period_year, $period_week );
869 break;
870
871 // month
872 case 6:
873 $datetime = date_create_from_format( 'Ymd' , $period . '01' );
874 break;
875 // year
876 case 4:
877 $datetime = date_create_from_format( 'Y' , $period );
878 break;
879
880 default:
881 $datetime = new DateTime();
882 }
883 }
884
885 return apply_filters( 'pvc_period2date', $datetime, $period );
886 }
887 }
888
889 /**
890 * Convert period to timestamp.
891 *
892 * @param string $period
893 * @return int
894 */
895 if ( ! function_exists( 'pvc_period2timestamp' ) ) {
896 function pvc_period2timestamp( $period ) {
897 $period = preg_replace( '/[^a-z0-9_|]/', '', $period );
898
899 // default time
900 $timestamp = current_time( 'timestamp', false );
901
902 // whitelisted period?
903 if ( in_array( $period, [ 'this_week', 'this_year', 'this_month' ], true ) ) {
904 $timestamp = current_time( 'timestamp', false );
905 // backward compatibility
906 } else if ( preg_match( '/^([0-9]{2}\|[0-9]{4})$/', $period ) === 1 ) {
907 // month|year
908 $date = explode( '|', $period, 2 );
909
910 // get timestamp
911 $timestamp = strtotime( (string) $date[1] . '-' . (string) $date[0] . '-13' );
912 } else {
913 // convert string to DateTime()
914 $d = pvc_period2date( $period );
915
916 if ( $d ) {
917 $timestamp = $d->getTimestamp();
918 }
919 }
920
921 return $timestamp;
922 }
923 }
924