PluginProbe ʕ •ᴥ•ʔ
Post Views Counter / 1.1.4
Post Views Counter v1.1.4
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 / counter.php
post-views-counter / includes Last commit date
columns.php 10 years ago counter.php 10 years ago cron.php 10 years ago frontend.php 10 years ago functions.php 10 years ago query.php 10 years ago settings.php 10 years ago update.php 10 years ago widgets.php 10 years ago
counter.php
527 lines
1 <?php
2 // exit if accessed directly
3 if ( ! defined( 'ABSPATH' ) )
4 exit;
5
6 /**
7 * Post_Views_Counter_Counter class.
8 */
9 class Post_Views_Counter_Counter {
10
11 const GROUP = 'pvc';
12 const NAME_ALLKEYS = 'cached_key_names';
13 const CACHE_KEY_SEPARATOR = '.';
14
15 private $cookie = array(
16 'exists' => false,
17 'visited_posts' => array(),
18 'expiration' => 0
19 );
20
21 public function __construct() {
22 // actions
23 add_action( 'plugins_loaded', array( $this, 'check_cookie' ), 1 );
24 add_action( 'wp', array( $this, 'check_post' ) );
25 add_action( 'deleted_post', array( $this, 'delete_post_views' ) );
26 add_action( 'wp_ajax_pvc-check-post', array( $this, 'check_post_ajax' ) );
27 add_action( 'wp_ajax_nopriv_pvc-check-post', array( $this, 'check_post_ajax' ) );
28 }
29
30 /**
31 * Remove post views from database when post is deleted.
32 *
33 * @param int $post_id
34 */
35 public function delete_post_views( $post_id ) {
36 global $wpdb;
37
38 $wpdb->delete( $wpdb->prefix . 'post_views', array( 'id' => $post_id ), array( '%d' ) );
39 }
40
41 /**
42 *
43 * Get timestamp convertion.
44 *
45 * @param string $type
46 * @param int $number
47 * @param int $timestamp
48 * @return string
49 */
50 public function get_timestamp( $type, $number, $timestamp = true ) {
51 $converter = array(
52 'minutes' => 60,
53 'hours' => 3600,
54 'days' => 86400,
55 'weeks' => 604800,
56 'months' => 2592000,
57 'years' => 946080000
58 );
59
60 return ($timestamp ? current_time( 'timestamp', true ) : 0) + $number * $converter[$type];
61 }
62
63 /**
64 * Check whether to count visit via AJAX request.
65 */
66 public function check_post_ajax() {
67 if ( isset( $_POST['action'], $_POST['post_id'], $_POST['pvc_nonce'], $_POST['post_type'] ) && $_POST['action'] === 'pvc-check-post' && ($post_id = (int) $_POST['post_id']) > 0 && wp_verify_nonce( $_POST['pvc_nonce'], 'pvc-check-post' ) !== false && Post_Views_Counter()->options['general']['counter_mode'] === 'js' ) {
68 // get countable post types
69 $post_types = Post_Views_Counter()->options['general']['post_types_count'];
70
71 // get post type
72 $post_type = get_post_type( $post_id );
73
74 // whether to count this post type or not
75 if ( empty( $post_types ) || empty( $post_type ) || $post_type !== $_POST['post_type'] || ! in_array( $post_type, $post_types, true ) )
76 exit;
77
78 // get excluded ips
79 $excluded_ips = Post_Views_Counter()->options['general']['exclude_ips'];
80
81 // whether to count this ip or not
82 if ( ! empty( $excluded_ips ) && filter_var( preg_replace( '/[^0-9a-fA-F:., ]/', '', $_SERVER['REMOTE_ADDR'] ), FILTER_VALIDATE_IP ) && in_array( $_SERVER['REMOTE_ADDR'], $excluded_ips, true ) )
83 exit;
84
85 // get groups to check them faster
86 $groups = Post_Views_Counter()->options['general']['exclude']['groups'];
87
88 // whether to count this user
89 if ( is_user_logged_in() ) {
90 // exclude logged in users?
91 if ( in_array( 'users', $groups, true ) )
92 exit;
93 // exclude specific roles?
94 elseif ( in_array( 'roles', $groups, true ) && $this->is_user_role_excluded( Post_Views_Counter()->options['general']['exclude']['roles'] ) )
95 exit;
96 }
97 // exclude guests?
98 elseif ( in_array( 'guests', $groups, true ) )
99 exit;
100
101 // whether to count robots
102 if ( $this->is_robot() )
103 exit;
104
105 // cookie already existed?
106 if ( $this->cookie['exists'] ) {
107 // post already viewed but not expired?
108 if ( in_array( $post_id, array_keys( $this->cookie['visited_posts'] ), true ) && current_time( 'timestamp', true ) < $this->cookie['visited_posts'][$post_id] ) {
109 // updates cookie but do not count visit
110 $this->save_cookie( $post_id, $this->cookie, false );
111
112 exit;
113 } else
114 // updates cookie
115 $this->save_cookie( $post_id, $this->cookie );
116 } else {
117 // set new cookie
118 $this->save_cookie( $post_id );
119 }
120
121 // count visit
122 $this->count_visit( $post_id );
123 }
124
125 exit;
126 }
127
128 /**
129 * Check whether to count visit.
130 */
131 public function check_post() {
132 // do not count admin entries
133 if ( is_admin() )
134 return;
135
136 // do we use PHP as counter?
137 if ( Post_Views_Counter()->options['general']['counter_mode'] === 'php' ) {
138 $post_types = Post_Views_Counter()->options['general']['post_types_count'];
139
140 // whether to count this post type
141 if ( empty( $post_types ) || ! is_singular( $post_types ) )
142 return;
143
144 $ips = Post_Views_Counter()->options['general']['exclude_ips'];
145
146 // whether to count this ip
147 if ( ! empty( $ips ) && filter_var( preg_replace( '/[^0-9a-fA-F:., ]/', '', $_SERVER['REMOTE_ADDR'] ), FILTER_VALIDATE_IP ) && in_array( $_SERVER['REMOTE_ADDR'], $ips, true ) )
148 return;
149
150 // get groups to check them faster
151 $groups = Post_Views_Counter()->options['general']['exclude']['groups'];
152
153 // whether to count this user
154 if ( is_user_logged_in() ) {
155 // exclude logged in users?
156 if ( in_array( 'users', $groups, true ) )
157 return;
158 // exclude specific roles?
159 elseif ( in_array( 'roles', $groups, true ) && $this->is_user_role_excluded( Post_Views_Counter()->options['general']['exclude']['roles'] ) )
160 return;
161 }
162 // exclude guests?
163 elseif ( in_array( 'guests', $groups, true ) )
164 return;
165
166 // whether to count robots
167 if ( $this->is_robot() )
168 return;
169
170 // get post id
171 $id = get_the_ID();
172
173 // cookie already existed?
174 if ( $this->cookie['exists'] ) {
175 // post already viewed but not expired?
176 if ( in_array( $id, array_keys( $this->cookie['visited_posts'] ), true ) && current_time( 'timestamp', true ) < $this->cookie['visited_posts'][$id] ) {
177 // update cookie but do not count visit
178 $this->save_cookie( $id, $this->cookie, false );
179
180 return;
181 } else
182 // update cookie
183 $this->save_cookie( $id, $this->cookie );
184 } else
185 // set new cookie
186 $this->save_cookie( $id );
187
188 // count visit
189 $this->count_visit( $id );
190 }
191 }
192
193 /**
194 * Initialize cookie session.
195 */
196 public function check_cookie() {
197 // do not run in admin except for ajax requests
198 if ( is_admin() && ! (defined( 'DOING_AJAX' ) && DOING_AJAX) )
199 return;
200
201 // is cookie set?
202 if ( isset( $_COOKIE['pvc_visits'] ) && ! empty( $_COOKIE['pvc_visits'] ) ) {
203 $visited_posts = $expirations = array();
204
205 foreach ( $_COOKIE['pvc_visits'] as $content ) {
206 // is cookie valid?
207 if ( preg_match( '/^(([0-9]+b[0-9]+a?)+)$/', $content ) === 1 ) {
208 // get single id with expiration
209 $expiration_ids = explode( 'a', $content );
210
211 // check every expiration => id pair
212 foreach ( $expiration_ids as $pair ) {
213 $pair = explode( 'b', $pair );
214 $expirations[] = (int) $pair[0];
215 $visited_posts[(int) $pair[1]] = (int) $pair[0];
216 }
217 }
218 }
219
220 $this->cookie = array(
221 'exists' => true,
222 'visited_posts' => $visited_posts,
223 'expiration' => max( $expirations )
224 );
225 }
226 }
227
228 /**
229 * Save cookie function.
230 *
231 * @param int $id
232 * @param array $cookie
233 * @param bool $expired
234 */
235 private function save_cookie( $id, $cookie = array(), $expired = true ) {
236 $expiration = $this->get_timestamp( Post_Views_Counter()->options['general']['time_between_counts']['type'], Post_Views_Counter()->options['general']['time_between_counts']['number'] );
237
238 // is this a new cookie?
239 if ( empty( $cookie ) ) {
240 // set cookie
241 setcookie( 'pvc_visits[0]', $expiration . 'b' . $id, $expiration, COOKIEPATH, COOKIE_DOMAIN, (isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off' ? true : false ), true );
242 } else {
243 if ( $expired ) {
244 // add new id or chang expiration date if id already exists
245 $cookie['visited_posts'][$id] = $expiration;
246 }
247
248 // create copy for better foreach performance
249 $visited_posts_expirations = $cookie['visited_posts'];
250
251 // get current gmt time
252 $time = current_time( 'timestamp', true );
253
254 // check whether viewed id has expired - no need to keep it in cookie (less size)
255 foreach ( $visited_posts_expirations as $post_id => $post_expiration ) {
256 if ( $time > $post_expiration )
257 unset( $cookie['visited_posts'][$post_id] );
258 }
259
260 // set new last expiration date if needed
261 $cookie['expiration'] = max( $cookie['visited_posts'] );
262
263 $cookies = $imploded = array();
264
265 // create pairs
266 foreach ( $cookie['visited_posts'] as $id => $exp ) {
267 $imploded[] = $exp . 'b' . $id;
268 }
269
270 // split cookie into chunks (4000 bytes to make sure it is safe for every browser)
271 $chunks = str_split( implode( 'a', $imploded ), 4000 );
272
273 // more then one chunk?
274 if ( count( $chunks ) > 1 ) {
275 $last_id = '';
276
277 foreach ( $chunks as $chunk_id => $chunk ) {
278 // new chunk
279 $chunk_c = $last_id . $chunk;
280
281 // is it full-length chunk?
282 if ( strlen( $chunk ) === 4000 ) {
283 // get last part
284 $last_part = strrchr( $chunk_c, 'a' );
285
286 // get last id
287 $last_id = substr( $last_part, 1 );
288
289 // add new full-lenght chunk
290 $cookies[$chunk_id] = substr( $chunk_c, 0, strlen( $chunk_c ) - strlen( $last_part ) );
291 } else {
292 // add last chunk
293 $cookies[$chunk_id] = $chunk_c;
294 }
295 }
296 } else {
297 // only one chunk
298 $cookies[] = $chunks[0];
299 }
300
301 foreach ( $cookies as $key => $value ) {
302 // set cookie
303 setcookie( 'pvc_visits[' . $key . ']', $value, $cookie['expiration'], COOKIEPATH, COOKIE_DOMAIN, (isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off' ? true : false ), true );
304 }
305 }
306 }
307
308 /**
309 * Check if object cache is in use.
310 *
311 * @param bool $using
312 * @return bool
313 */
314 public function using_object_cache( $using = null ) {
315 $using = wp_using_ext_object_cache( $using );
316
317 if ( $using ) {
318 // check if explicitly disabled by flush_interval setting/option <= 0
319 $flush_interval_number = Post_Views_Counter()->options['general']['flush_interval']['number'];
320 $using = ( $flush_interval_number <= 0 ) ? false : true;
321 }
322
323 return $using;
324 }
325
326 /**
327 * Count visit function.
328 *
329 * @global object $wpdb
330 * @param int $id
331 * @return bool
332 */
333 private function count_visit( $id ) {
334 global $wpdb;
335
336 $cache_key_names = array();
337 $using_object_cache = $this->using_object_cache();
338
339 // get day, week, month and year
340 $date = explode( '-', date( 'W-d-m-Y', current_time( 'timestamp' ) ) );
341
342 foreach( array(
343 0 => $date[3] . $date[2] . $date[1], // day like 20140324
344 1 => $date[3] . $date[0], // week like 201439
345 2 => $date[3] . $date[2], // month like 201405
346 3 => $date[3], // year like 2014
347 4 => 'total' // total views
348 ) as $type => $period ) {
349 if ( $using_object_cache ) {
350 $cache_key = $id . self::CACHE_KEY_SEPARATOR . $type . self::CACHE_KEY_SEPARATOR . $period;
351 wp_cache_add( $cache_key, 0, self::GROUP );
352 wp_cache_incr( $cache_key, 1, self::GROUP );
353 $cache_key_names[] = $cache_key;
354 } else {
355 // hit the db directly
356 // @TODO: investigate queueing these queries on the 'shutdown' hook instead instead of running them instantly?
357 $this->db_insert( $id, $type, $period, 1 );
358 }
359 }
360
361 // update the list of cache keys to be flushed
362 if ( $using_object_cache && ! empty( $cache_key_names ) ) {
363 $this->update_cached_keys_list_if_needed( $cache_key_names );
364 }
365
366 do_action( 'pvc_after_count_visit', $id );
367
368 return true;
369 }
370
371 /**
372 * Update the single cache key which holds a list of all the cache keys
373 * that need to be flushed to the db.
374 *
375 * The value of that special cache key is a giant string containing key names separated with the `|` character.
376 * Each such key name then consists of 3 elements: $id, $type, $period (separated by a `.` character).
377 * Examples:
378 * 62053.0.20150327|62053.1.201513|62053.2.201503|62053.3.2015|62053.4.total|62180.0.20150327|62180.1.201513|62180.2.201503|62180.3.2015|62180.4.total
379 * A single key is `62053.0.20150327` and that key's data is: $id = 62053, $type = 0, $period = 20150327
380 *
381 * This data format proved more efficient (avoids the (un)serialization overhead completely + duplicates filtering is a string search now)
382 *
383 * @param array $key_names
384 */
385 private function update_cached_keys_list_if_needed( $key_names = array() ) {
386 $existing_list = wp_cache_get( self::NAME_ALLKEYS, self::GROUP );
387 if ( ! $existing_list ) {
388 $existing_list = '';
389 }
390
391 $list_modified = false;
392
393 // modify the list contents if/when needed
394 if ( empty( $existing_list ) ) {
395 // the simpler case of an empty initial list where we just
396 // transform the specified key names into a string
397 $existing_list = implode( '|', $key_names );
398 $list_modified = true;
399 } else {
400 // search each specified key name and append it if it's not found
401 foreach ( $key_names as $key_name ) {
402 if ( false === strpos( $existing_list, $key_name ) ) {
403 $existing_list .= '|' . $key_name;
404 $list_modified = true;
405 }
406 }
407 }
408
409 // save modified list back in cache
410 if ( $list_modified ) {
411 wp_cache_set( self::NAME_ALLKEYS, $existing_list, self::GROUP );
412 }
413 }
414
415 /**
416 * Flush views data stored in the persistent object cache into
417 * our custom table and clear the object cache keys when done.
418 *
419 * @global object $wpdb
420 * @return bool
421 */
422 public function flush_cache_to_db() {
423 global $wpdb;
424
425 $key_names = wp_cache_get( self::NAME_ALLKEYS, self::GROUP );
426
427 if ( ! $key_names ) {
428 $key_names = array();
429 } else {
430 // create an array out of a string that's stored in the cache
431 $key_names = explode( '|', $key_names );
432 }
433
434 foreach( $key_names as $key_name ) {
435 // get values stored within the key name itself
436 list( $id, $type, $period ) = explode( self::CACHE_KEY_SEPARATOR, $key_name );
437 // get the cached count value
438 $count = wp_cache_get( $key_name, self::GROUP );
439
440 // store cached value in the db
441 $this->db_insert( $id, $type, $period, $count );
442
443 // clear the cache key we just flushed
444 wp_cache_delete( $key_name, self::GROUP );
445 }
446
447 // delete the key holding the list itself after we've successfully flushed it
448 if ( ! empty( $key_names ) ) {
449 wp_cache_delete( self::NAME_ALLKEYS, self::GROUP );
450 }
451
452 return true;
453 }
454
455 /**
456 * Insert or update views count.
457 *
458 * @global object $wpdb
459 * @param int $id
460 * @param string $type
461 * @param string $period
462 * @param int $count
463 * @return bool
464 */
465 private function db_insert( $id, $type, $period, $count = 1 ) {
466 global $wpdb;
467
468 $count = (int) $count;
469
470 if ( ! $count ) {
471 $count = 1;
472 }
473
474 return $wpdb->query(
475 $wpdb->prepare( "
476 INSERT INTO " . $wpdb->prefix . "post_views (id, type, period, count)
477 VALUES (%d, %d, %s, %d)
478 ON DUPLICATE KEY UPDATE count = count + %d", $id, $type, $period, $count, $count
479 )
480 );
481 }
482
483 /**
484 * Check whether user has excluded roles.
485 *
486 * @param string $option
487 * @return bool
488 */
489 public function is_user_role_excluded( $option ) {
490 $user = wp_get_current_user();
491
492 if ( empty( $user ) )
493 return false;
494
495 $roles = (array) $user->roles;
496
497 if ( ! empty( $roles ) ) {
498 foreach ( $roles as $role ) {
499 if ( in_array( $role, $option, true ) )
500 return true;
501 }
502 }
503
504 return false;
505 }
506
507 /**
508 * Check whether visitor is a bot.
509 */
510 private function is_robot() {
511 if ( ! isset( $_SERVER['HTTP_USER_AGENT'] ) || (isset( $_SERVER['HTTP_USER_AGENT'] ) && trim( $_SERVER['HTTP_USER_AGENT'] ) === '') )
512 return false;
513
514 $robots = array(
515 'bot', 'b0t', 'Acme.Spider', 'Ahoy! The Homepage Finder', 'Alkaline', 'Anthill', 'Walhello appie', 'Arachnophilia', 'Arale', 'Araneo', 'ArchitextSpider', 'Aretha', 'ARIADNE', 'arks', 'AskJeeves', 'ASpider (Associative Spider)', 'ATN Worldwide', 'AURESYS', 'BackRub', 'Bay Spider', 'Big Brother', 'Bjaaland', 'BlackWidow', 'Die Blinde Kuh', 'Bloodhound', 'BSpider', 'CACTVS Chemistry Spider', 'Calif', 'Cassandra', 'Digimarc Marcspider/CGI', 'ChristCrawler.com', 'churl', 'cIeNcIaFiCcIoN.nEt', 'CMC/0.01', 'Collective', 'Combine System', 'Web Core / Roots', 'Cusco', 'CyberSpyder Link Test', 'CydralSpider', 'Desert Realm Spider', 'DeWeb(c) Katalog/Index', 'DienstSpider', 'Digger', 'Direct Hit Grabber', 'DownLoad Express', 'DWCP (Dridus\' Web Cataloging Project)', 'e-collector', 'EbiNess', 'Emacs-w3 Search Engine', 'ananzi', 'esculapio', 'Esther', 'Evliya Celebi', 'FastCrawler', 'Felix IDE', 'Wild Ferret Web Hopper #1, #2, #3', 'FetchRover', 'fido', 'KIT-Fireball', 'Fish search', 'Fouineur', 'Freecrawl', 'FunnelWeb', 'gammaSpider, FocusedCrawler', 'gazz', 'GCreep', 'GetURL', 'Golem', 'Grapnel/0.01 Experiment', 'Griffon', 'Gromit', 'Northern Light Gulliver', 'Harvest', 'havIndex', 'HI (HTML Index) Search', 'Hometown Spider Pro', 'ht://Dig', 'HTMLgobble', 'Hyper-Decontextualizer', 'IBM_Planetwide', 'Popular Iconoclast', 'Ingrid', 'Imagelock', 'IncyWincy', 'Informant', 'Infoseek Sidewinder', 'InfoSpiders', 'Inspector Web', 'IntelliAgent', 'Iron33', 'Israeli-search', 'JavaBee', 'JCrawler', 'Jeeves', 'JumpStation', 'image.kapsi.net', 'Katipo', 'KDD-Explorer', 'Kilroy', 'LabelGrabber', 'larbin', 'legs', 'Link Validator', 'LinkScan', 'LinkWalker', 'Lockon', 'logo.gif Crawler', 'Lycos', 'Mac WWWWorm', 'Magpie', 'marvin/infoseek', 'Mattie', 'MediaFox', 'MerzScope', 'NEC-MeshExplorer', 'MindCrawler', 'mnoGoSearch search engine software', 'moget', 'MOMspider', 'Monster', 'Motor', 'Muncher', 'Muninn', 'Muscat Ferret', 'Mwd.Search', 'Internet Shinchakubin', 'NDSpider', 'Nederland.zoek', 'NetCarta WebMap Engine', 'NetMechanic', 'NetScoop', 'newscan-online', 'NHSE Web Forager', 'Nomad', 'nzexplorer', 'ObjectsSearch', 'Occam', 'HKU WWW Octopus', 'OntoSpider', 'Openfind data gatherer', 'Orb Search', 'Pack Rat', 'PageBoy', 'ParaSite', 'Patric', 'pegasus', 'The Peregrinator', 'PerlCrawler 1.0', 'Phantom', 'PhpDig', 'PiltdownMan', 'Pioneer', 'html_analyzer', 'Portal Juice Spider', 'PGP Key Agent', 'PlumtreeWebAccessor', 'Poppi', 'PortalB Spider', 'GetterroboPlus Puu', 'Raven Search', 'RBSE Spider', 'RoadHouse Crawling System', 'ComputingSite Robi/1.0', 'RoboCrawl Spider', 'RoboFox', 'Robozilla', 'RuLeS', 'Scooter', 'Sleek', 'Search.Aus-AU.COM', 'SearchProcess', 'Senrigan', 'SG-Scout', 'ShagSeeker', 'Shai\'Hulud', 'Sift', 'Site Valet', 'SiteTech-Rover', 'Skymob.com', 'SLCrawler', 'Inktomi Slurp', 'Smart Spider', 'Snooper', 'Spanner', 'Speedy Spider', 'spider_monkey', 'Spiderline Crawler', 'SpiderMan', 'SpiderView(tm)', 'Site Searcher', 'Suke', 'suntek search engine', 'Sven', 'Sygol', 'TACH Black Widow', 'Tarantula', 'tarspider', 'Templeton', 'TeomaTechnologies', 'TITAN', 'TitIn', 'TLSpider', 'UCSD Crawl', 'UdmSearch', 'URL Check', 'URL Spider Pro', 'Valkyrie', 'Verticrawl', 'Victoria', 'vision-search', 'Voyager', 'W3M2', 'WallPaper (alias crawlpaper)', 'the World Wide Web Wanderer', 'w@pSpider by wap4.com', 'WebBandit Web Spider', 'WebCatcher', 'WebCopy', 'webfetcher', 'Webinator', 'weblayers', 'WebLinker', 'WebMirror', 'The Web Moose', 'WebQuest', 'Digimarc MarcSpider', 'WebReaper', 'webs', 'Websnarf', 'WebSpider', 'WebVac', 'webwalk', 'WebWalker', 'WebWatch', 'Wget', 'whatUseek Winona', 'Wired Digital', 'Weblog Monitor', 'w3mir', 'WebStolperer', 'The Web Wombat', 'The World Wide Web Worm', 'WWWC Ver 0.2.5', 'WebZinger', 'XGET'
516 );
517
518 foreach( $robots as $robot ) {
519 if ( stripos( $_SERVER['HTTP_USER_AGENT'], $robot ) !== false )
520 return true;
521 }
522
523 return false;
524 }
525
526 }
527