GG资源网

WordPress架构简单剖析(wordpress进阶教程(三十六):文章浏览量统计-重复刷新不增加)

WordPress架构简单剖析

前言

最近在搭建自己的博客站点时, 选择了网站使用较多的WordPress, 随着慢慢的使用, 它灵活的插件和主题令我折服. 基本上任何想要实现的功能, 都可以在上面通过插件的形式进行添加. 无论是在访问前的缓存、访问后的统计、访问中的过滤、各种流程的修改等等, 几乎都能够以插件的形式进行修改. 我觉得这太酷了, 如果在我平常业务上能够将架构写成这样, 还有什么需求变化能难倒我?

基于这个原因, 我对WordPress进行了简单的分析, 这就是开源的好处嘛. 我从index.php文件一步步跟踪了整个请求的开始到结束. 因为能力有限, 这可能是最笨的办法了.

解析

执行流程#

index.php文件很简单, 就一句:

require __DIR__ . '/wp-blog-header.php';

而wp-blog-header.php文件呢, 也很简单:

if ( ! isset( $wp_did_header ) ) {
$wp_did_header = true;
require_once __DIR__ . '/wp-load.php';
wp();
require_once ABSPATH . WPINC . '/template-loader.php';
}

而这, 已经将WordPress的执行流程体现出来了.

1.防止重复加载#

! isset( $wp_did_header ) 判断, 是为了防止文件被重复加载的, 直接跳过

2.加载 库/主题/插件#

第二步引入了wp-load.php文件, 然后又引入了wp-config.php文件, 再然后又引入了wp-settings.php文件, 实际的加载过程, 就在wp-settings.php文件中. 此文件做了下面几件事

  1. 引入初始化文件
  2. 常量定义
  3. 引入库
  4. 加载插件
  5. 加载主题

到这里, 还没有针对当前页面数据的查询, 仅完成了初始化过程.

3.查询页面数据#

wp()函数是执行页面数据加载的方法, 会根据当前页面, 到数据库中查询需要显示的数据, 将需要展示的数据准备好.

4.页面展示#

最终引入的template-loader.php文件, 其作用是将数据进行可视化展示.

5.完成#

至此, 整个页面的展示流程就走完了. 按照这个步骤看下来, 整个流程还是比较清晰的.

但是还是没有回答最开始的问题啊, 它灵活在哪里呢? 上面只是简单描述了一下整体的加载流程, 但具体细节还没有提到.

页面展示#

WordPress加载页面的地方, 就是最后的template-loader.php这个文件了.

其根据当前页面, 加载不同的文件进行展示. 至于页面为什么这么灵活, 随便找个页面看一下就知道了. index.php:

拼图式生成页面. 可针对每一个位置进行定制, 并将其进行组装. 所以每个主题都有很高的灵活性, 可以自己设置页面, 也可以选择丢弃某些内容而不展示.

另外, HTML在加载页面的时候, 会对几个模板进行查找, 如在访问: 计算机是如何进行时间同步的 这篇文章的时候, get_single_template 方法会依次查找下面几个文件:

  • single-post-计算机是如何进行时间同步的.php
  • single-post-%e8%ae%a1%e7%ae%97%e6%9c%ba%e6%98%af%e5%a6%82%e4%bd%95%e8%bf%9b%e8%a1%8c%e6%97%b6%e9%97%b4%e5%90%8c%e6%ad%a5%e7%9a%84.php
  • single-post.php
  • single.php

若某个文件存在, 就会直接加载. 有没有悟到什么. 这玩意不就可以做缓存嘛. 但是, 不好意思, 在执行这步操作之前, 该查询的数据就已经查过了, 所以这个缓存加了等于没加, 没什么卵用.

钩子函数#

如果WordPress只是能够拼图式组装页面, 那还不够灵活, 因为只能对页面进行操作, 而无法影响执行流程. 对执行流程的影响, 就是它的各种钩子函数了. WordPress的钩子函数通过do_action和apply_filters两个方法进行调用,

看过方法add_action发现, 它就是简单的调用了add_filter方法. 也就是说这两个方法内部是同一个方法. 个人理解, do_action注重与流程的插入, 既向主流程中加入一段逻辑, 没有返回值. 而 apply_filters方法有返回值, 更注重对数据的处理吧.

在WordPress中, 随处可见各种钩子的调用, 初始化的时候、加载插件、插件加载完成、加载主题等等等等.

举个例子, 有一个缓存插件, 就是通过在添加init钩子函数, 将页面内容 echo之后, 直接执行die函数, 以达到快速返回的效果.

不过在查看源码的过程中, 有一个问题, 所有钩子函数的调用, 都是直接使用字符串调用的, 如 do_action('init'). 这种通用的变量, 不应该写个常量列表的么?

不过好在官方维护了一份钩子函数的列表, 列出了所有的钩子, 同时进行了说明并指出调用的具体地址. 需要的时候可以看一下. 我数了一下, 目前一共1470个钩子. https://developer.wordpress.org/reference/hooks/

可以说, WordPress就是通过各种钩子以及拼图式页面, 分别实现展示和流程的个性化定制. 而这个钩子函数倒也不是什么新鲜玩意, 接口的监听器、各种beforeAction afterAction等等, 在平常开发过程中也经常用到. 只是没有用到这么极致罢.

其他细节#

配置加载#

WordPress的配置是存储在MySQL中的, 而请求加载配置文件的方式是执行sql查询:

SELECT option_name, option_value FROM $wpdb->options WHERE autoload = 'yes'

直接将表中的所有配置, 一次性读出来, 而且, 取出来的数据还不少嘞, 给你个直观感受, 我将结果保存到txt文件, 文件大小1.4mb.

如果说这个查询可以增加缓存, 或者通过配置文件引入的话, 能够省去一些消耗. 但是, 如果想通过插件的方式修改配置读取, 不好意思, 这个不可以. 因为 配置的首次读取是在调用wp_not_installed()函数时, 而此时插件还没加载呢. 如果想修改的话, 貌似只能修改源码了,

在加载配置的时候, 在请求缓存中先读了一次:

故可以预先将配置放到请求缓存中. 在调用方法wp_start_object_cache()加载缓存之后, 立刻调用了wp_cache_add( 'alloptions', $alloptions, 'options' );方法, 可以将全局配置预先放到缓存中, 实验了一下, 确实可行. 如果追求性能极致的话, 可以考虑.

配置存储#

看到数据库配置表wp_options中启用插件的值时, 我完全摸不到头脑, 存储的内容是这样的:

a:7:{i:0;s:49:"easy-table-of-contents/easy-table-of-contents.php";i:1;s:47:"simple-yearly-archive/simple-yearly-archive.php";i:2;s:30:"wp-githuber-md/githuber-md.php";i:3;s:29:"wp-mail-smtp/wp_mail_smtp.php";i:5;s:27:"wp-super-cache/wp-cache.php";i:6;s:31:"wpdiscuz/class.WpdiscuzCore.php";i:7;s:32:"xml-sitemap-feed/xml-sitemap.php";}

这这这, 这是啥? 看不懂, 但又好像能看懂. 于是我追踪了这个值的解析, 就是下面这个函数:

解析后的数据是:

{
"0": "easy-table-of-contents/easy-table-of-contents.php",
"1": "simple-yearly-archive/simple-yearly-archive.php",
"2": "wp-githuber-md/githuber-md.php",
"3": "wp-mail-smtp/wp_mail_smtp.php",
"5": "wp-super-cache/wp-cache.php",
"6": "wpdiscuz/class.WpdiscuzCore.php",
"7": "xml-sitemap-feed/xml-sitemap.php"
}

是不是一下就懂了? 存储的是通过serialize函数进行对象序列化之后的值, 于是, 弱弱的问一下, 直接存json字符串不好么?

全局变量定义#

在WordPress中到处都充斥着各种全局变量. 我在查看缓存文件的时候, 看到了这段代码:

但奇怪的是, 我全局搜索变量$wp_object_cache, 却没有找到定义的地方. 最终我一点一点找到了它定义的地方.

而这种功能风格到处都是, 如果想找到一个变量都有哪些地方使用了, 很不好找. 而且, 直接引用全局变量的方式, 也导致变量之后很难修改. 在源码中就看到了这么一个活生生的例子:

这种风格导致一个后果, 一个变量一旦定义, 就摘不掉了.

数据库查询记录#

在查看数据库查询的时候, 看到了这样的代码:

也就是说, 如果定义了SAVEQUERIES常量, 且为true, 那么就会将查询的sql记录下来. 在log_query方法中, 记录到了queries变量中.

这个操作对于数据库的调优还是比较方便的. 在配置文件中定义常量, 在最终拿到所有的sql及执行时间

总结

对于这种充斥着全局变量和钩子函数的内容, 阅读起来有一丢丢的疲惫, 经常看着看着就看丢了. 不过还是发现了很多有意思的地方.

本来是想看看它为什么这么灵活, 结果发现其实在平常的开发过程中已经用到了, 不过WordPress对一些内容的处理还是给了我一些启发.

比如这种拼图式的页面组成, 可以将页面的展示和数据处理分离. 而在开发接口的时候, 是不是也可以借鉴类似的思路. 这种方式有一个问题, 就是即使页面没有用到的数据, 在查询的时候也都查询出来了, 对于接口这种追求性能的情况, 肯定是不能忍受的. 或者可以将需要使用的数据让展示方给出配置? 不过这样的话, 耦合度又高了, 灵活度也下降了, 难搞.

不过最重要的是, 这破玩意就是我现在在用的呀, 不好好了解一下怎么行. 以后如果有定制化需求, 咱也不至于无从下手了.

wordpress进阶教程(三十六):文章浏览量统计-重复刷新不增加

有细心的网友发现本工作室的教程的浏览量统计,重复刷新不增加,留言问怎么实现的。

本工作室的这个统计代码还是很久很久很久以前,在某一个插件上面扒下来的代码,具体什么插件我也忘了,今天这篇教程也不详细解析代码了,直接来懒人模式。

第一步,在你主题的functions.php文件中添加如下代码(是统计计数、获取浏览数的一些函数)

  1. /***********文章统计*********/  
  2. function process_postviews() {   
  3.     global $user_ID$post;   
  4.     if(check_cookie($post))   
  5.         return;   
  6.     if(is_int($post)) {   
  7.         $post = get_post($post);   
  8.     }   
  9.     if(!wp_is_post_revision($post)) {   
  10.         if(is_single() || is_page()) {   
  11.             $id = intval($post->ID);   
  12.             //$post_views = get_post_custom($id);   
  13.             $post_views = get_post_meta($id,\'_check_count\',true);   
  14.             //统计所有人   
  15.             $should_count = true;   
  16.             //排除机器人   
  17.             $bots = array(\'Google Bot\' => \'googlebot\', \'Google Bot\' => \'google\', \'MSN\' => \'msnbot\', \'Alex\' => \'ia_archiver\', \'Lycos\' => \'lycos\', \'Ask Jeeves\' => \'jeeves\', \'Altavista\' => \'scooter\', \'AllTheWeb\' => \'fast-webcrawler\', \'Inktomi\' => \'slurp@inktomi\', \'Turnitin.com\' => \'turnitinbot\', \'Technorati\' => \'technorati\', \'Yahoo\' => \'yahoo\', \'Findexa\' => \'findexa\', \'NextLinks\' => \'findlinks\', \'Gais\' => \'gaisbo\', \'WiseNut\' => \'zyborg\', \'WhoisSource\' => \'surveybot\', \'Bloglines\' => \'bloglines\', \'BlogSearch\' => \'blogsearch\', \'PubSub\' => \'pubsub\', \'Syndic8\' => \'syndic8\', \'RadioUserland\' => \'userland\', \'Gigabot\' => \'gigabot\', \'Become.com\' => \'become.com\',\'Baidu Bot\'=>\'Baiduspider\');   
  18.             $useragent = $_SERVER[\'HTTP_USER_AGENT\'];   
  19.             foreach ($bots as $name => $lookfor) {   
  20.                 if (stristr($useragent$lookfor) !== false) {   
  21.                     $should_count = false;   
  22.                     break;   
  23.                 }   
  24.             }   
  25.             if($should_count) {   
  26.                 if(!update_post_meta($id, \'_check_count\', ($post_views+1))) {   
  27.                     add_post_meta($id, \'_check_count\', 1, true);   
  28.                 }   
  29.             }   
  30.         }   
  31.     }   
  32. }   
  33.   
  34. function check_cookie($post){   
  35.     $COOKNAME = \'ashuwp_view\';   
  36.     if(isset($_COOKIE[$COOKNAME]))   
  37.         $cookie = $_COOKIE[$COOKNAME];   
  38.     else  
  39.         return false;   
  40.     $id = $post->ID;   
  41.     if(empty($id)){   
  42.         return false;   
  43.     }   
  44.     if(!empty($cookie)){   
  45.         $list = explode(\'a\', $cookie);   
  46.         if(!empty($list) && in_array($id$list)){   
  47.             return true;   
  48.         }   
  49.     }   
  50.     return false;   
  51. }   
  52. ### Function: Display The Post Views   
  53. function the_views($display = true,$id) {   
  54.     $post_views = intval(get_post_meta($id,\'_check_count\',true));   
  55.     $output = number_format_i18n($post_views);   
  56.     if($display) {   
  57.         echo $output;   
  58.     } else {   
  59.         return $output;   
  60.     }   
  61. }   
  62.   
  63. ### Function: Display Total Views   
  64. if(!function_exists(\'get_totalviews\')) {   
  65.     function get_totalviews($display = true) {   
  66.         global $wpdb;   
  67.         $total_views = intval($wpdb->get_var(\"SELECT SUM(meta_value+0) FROM $wpdb->postmeta WHERE meta_key = \'_check_count\'\"));   
  68.         if($display) {   
  69.             echo number_format_i18n($total_views);   
  70.         } else {   
  71.             return $total_views;   
  72.         }   
  73.     }   
  74. }   
  75.   
  76. ### Function: Add Views Custom Fields   
  77. add_action(\'publish_post\', \'add_views_fields\');   
  78. add_action(\'publish_page\', \'add_views_fields\');   
  79. function add_views_fields($post_ID) {   
  80.     global $wpdb;   
  81.     if(!wp_is_post_revision($post_ID)) {   
  82.         add_post_meta($post_ID, \'_check_count\', 0, true);   
  83.     }   
  84. }   
  85. ### Function: Delete Views Custom Fields   
  86. add_action(\'delete_post\', \'delete_views_fields\');   
  87. function delete_views_fields($post_ID) {   
  88.     global $wpdb;   
  89.     if(!wp_is_post_revision($post_ID)) {   
  90.         delete_post_meta($post_ID, \'_check_count\');   
  91.     }   
  92. }  
由于网站搬家,部分链接失效,如无法下载,请联系站长!谢谢支持!
1. 带 [亲测] 说明源码已经被站长亲测过!
2. 下载后的源码请在24小时内删除,仅供学习用途!
3. 分享目的仅供大家学习和交流,请不要用于商业用途!
4. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!
5. 本站所有资源来源于站长上传和网络,如有侵权请邮件联系站长!
6. 没带 [亲测] 代表站长时间紧促,站长会保持每天更新 [亲测] 源码 !
7. 盗版ripro用户购买ripro美化无担保,若设置不成功/不生效我们不支持退款!
8. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
9. 如果你也有好源码或者教程,可以到审核区发布,分享有金币奖励和额外收入!
10.如果您购买了某个产品,而我们还没来得及更新,请联系站长或留言催更,谢谢理解 !
GG资源网 » WordPress架构简单剖析(wordpress进阶教程(三十六):文章浏览量统计-重复刷新不增加)

发表回复

CAPTCHAis initialing...