如何利用缓存提高页面加载速度

互联网有一项著名的原则叫“8秒钟原则”,即首页的加载速度超过8秒,有70%的用户就会不耐烦,选择离开。

做网站或APP当然内容是王道,产品提供有价值的内容,才会吸引更多人使用;但如果加载过慢,纵然你的内容再好,也会有相当一部分用户流失。

相对于早期的“静态网页”,现在大家开发网站时都趋向于使用后端语言和数据库通信,为用户实时展现最新文章、最新评论的“动态网页”。这个理念是没错,可当用户访问量增多时,数据库通信将会严重拖慢页面加载速度。假设5秒钟之内有1000名用户访问了你网站的首页,如果你在这5秒没有更新任何内容的话,这1000人访问了1000次数据库,只为了得到一模一样的数据。999次数据库连接通信其实就是在浪费时间和资源。

“动态网页”和数据库的同步其实没必要精确到分和秒。服务器端如果有一个10分钟的缓存机制,将大大减少用户访问时的加载时间。据我的经验,有数据库通信,加载需要3秒钟的页面,使用缓存后可以缩短至600毫秒之内。

php里有一个很轻量、实用的工具叫phpfastcache,用起来简单的不要不要的,只需要$cache->get来获取缓存过的值,如果这个值不存在,那就去数据库里把它抓取出来,用$cache->set把值存入就好了。接下来的10分钟,所有用户访问都在服务器就可以解决,不用连数据库了。当然如果数据不会经常变化的话,缓存有效时间可以设置更久。

$results = $cache->get("identity_keyword");

if($results == null) {
    $results = cURL->get("http://www.youtube.com/api/json/url/keyword/page");
    // Write to Cache Save API Calls next time
    $cache->set("identity_keyword", $results, 60*10);
}

如何按相似标签(类别)的数量来排列相关文章?

如何在Wordpress网站的单页post或是custom post type(CPT) 中显示相关推荐?网上有很多教程教你怎么做。大体都是先得到当前post的类别或标签,然后到数据库里抓有相同标签的post出来。

对于一个小的、文章类型单一的博客,这种方法足够了,但对于结构复杂,同一篇post有多个标签时,我们要在有限的空间(一般不会超过10条)为用户提供最有价值的内容,就涉及优先级和排序的问题。如何判断一篇post和当前post的相关程度呢?

我能想到的最好办法就是按他们相同标签的数量来排序了。下面是我在名为商户(”business”)的CPT中显示相关商户推荐的例子。

首先,用wp_get_object_terms 得到当前商户的所有类别和标签数组,将其implode为string,然后运行query语句得出相关商户,按相同类别和标签数量多少来排序。

SELECT ID, COUNT(*) AS total_count, post_title  FROM `wp_posts` 
LEFT JOIN wp_term_relationships ON wp_posts.ID = wp_term_relationships.object_id
LEFT JOIN wp_term_taxonomy ON wp_term_relationships.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id
LEFT JOIN wp_terms ON wp_term_taxonomy.term_id = wp_terms.term_id
WHERE wp_posts.ID!=343131 AND wp_posts.post_type='business' AND wp_posts.post_status='publish' AND wp_terms.name IN ('中餐','粤港菜','茶餐厅简餐','奶茶甜点','果汁饮品','咖啡烘焙','早茶点心')
GROUP BY wp_posts.ID
ORDER BY total_count DESC

判断一个PHP数组为空时应该用empty还是count==0?

研究了一下,不管是用empty还是用count,运行效率方面没什么差别。如果数组为空,count返回0;但如果数组未定义,会收到php notice。所以要在count前面加isset判断。empty()不会有这个问题。

另外,空数组在PHP中相当于false,所以其实两个方程都不需要用,直接用数组本身做判断也可以。

<?php

    $arr=array();
    
    echo "The array is ", $arr ? 'full' : 'empty', ".\n";
//Output: The array is empty.


    $label = array(0 => 'empty', 1 => 'full');

    echo "The array is ", $label[(bool)$arr], ".\n";
//Output: The array is empty.

Use http_build_query to generate query string

When adding query data to my http requests, I used to url-encode all the parameters and combine them together as a single url-encoded string manually. Today I found a PHP function that just does the job perfectly.


<?php
$data = array('foo'=>'bar',
              'baz'=>'boom',
              'cow'=>'milk',
              'php'=>'hypertext processor');

echo http_build_query($data) . "\n";
//This will output: foo=bar&baz=boom&cow=milk&php=hypertext+processor
echo http_build_query($data, '', '&amp;');
//This will output: foo=bar&amp;baz=boom&amp;cow=milk&amp;php=hypertext+processor

?>

More details can be found here

如何得到两个日期中间所有日期的Array

如果我有两个日期的String,’2015-01-01′ 和 ‘2016-03-15’, 如何使用PHP将它们之间的所有日期用Array封装呢?PHP有一个DateInterval的Class就是做这个的。下面是我根据PHP官方文档写的function。

function get_date_array($start_date, $end_date){
    $date_array = array();
    $start_date = new DateTime($start_date);
    $end_date = new DateTime($end_date);
    //$end_date = $end_date->modify(' +1 day ');//如果要包括end_date的话
    $interval = new DateInterval('P1D'); 
    $daterange = new DatePeriod($start_date, $interval ,$end_date);
    foreach($daterange as $date){
         array_push($date_array, $date->format("Y-m-d"));
    }
    return $date_array;
}

PHP:如何过滤一个Array

$new_array = array('2015-01-01', '2015-12-01', '2016-01-01','2016-05-05','2016-07-01');

给出上面这样一个array,如何过滤它,得出2015-06-01之后的值呢?可以用array_filter 这个function。下面是使用的例子

$filtered_array = array_filter($new_array, 'my_filter_function');
function my_filter_function($var){
    return strtotime($var)>=strtotime('2015-06-01');
}

PHP:json_encode返回的可不一定是json object

用PHP做移动端数据接口,以json_encode返回json object数据,今天安卓端的同事跟我反映收到一个空的Array,导致客户端崩溃。空的Array在json_encode的时候难道不会被转为json object么?

查了一下还真是这样。如果不加任何选项,空的Array默认返回的还是空Array,想要全部转为json object,必须加JSON_FORCE_OBJECT选项作为第二参数。

下面是php官网摘录的具体选项的用法

<?php
$a = array('<foo>',"'bar'",'"baz"','&blong&', "xc3xa9");

echo "Normal: ",  json_encode($a), "n";
echo "Tags: ",    json_encode($a, JSON_HEX_TAG), "n";
echo "Apos: ",    json_encode($a, JSON_HEX_APOS), "n";
echo "Quot: ",    json_encode($a, JSON_HEX_QUOT), "n";
echo "Amp: ",     json_encode($a, JSON_HEX_AMP), "n";
echo "Unicode: ", json_encode($a, JSON_UNESCAPED_UNICODE), "n";
echo "All: ",     json_encode($a, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP | JSON_UNESCAPED_UNICODE), "nn";

$b = array();

echo "Empty array output as array: ", json_encode($b), "n";
echo "Empty array output as object: ", json_encode($b, JSON_FORCE_OBJECT), "nn";

$c = array(array(1,2,3));

echo "Non-associative array output as array: ", json_encode($c), "n";
echo "Non-associative array output as object: ", json_encode($c, JSON_FORCE_OBJECT), "nn";

$d = array('foo' => 'bar', 'baz' => 'long');

echo "Associative array always output as object: ", json_encode($d), "n";
echo "Associative array always output as object: ", json_encode($d, JSON_FORCE_OBJECT), "nn";
?>

下面是对应的output


Normal: ["","'bar'",""baz"","&blong&","u00e9"]
Tags: ["u003Cfoou003E","'bar'",""baz"","&blong&","u00e9"]
Apos: ["","u0027baru0027",""baz"","&blong&","u00e9"]
Quot: ["","'bar'","u0022bazu0022","&blong&","u00e9"]
Amp: ["","'bar'",""baz"","u0026blongu0026","u00e9"]
Unicode: ["","'bar'",""baz"","&blong&","é"]
All: ["u003Cfoou003E","u0027baru0027","u0022bazu0022","u0026blongu0026","é"]

Empty array output as array: []
Empty array output as object: {}

Non-associative array output as array: [[1,2,3]]
Non-associative array output as object: {"0":{"0":1,"1":2,"2":3}}

Associative array always output as object: {"foo":"bar","baz":"long"}
Associative array always output as object: {"foo":"bar","baz":"long"}

我觉得这个是一个很好的PHP面试题。

wp_signon之后$current_user仍然为空的解决办法

最近在和国内的团队开发温哥华知道的安卓版本,有个问题困扰了我挺久。今天查出了原因,也学了一点小东西。

由于这是一款新闻资讯应用,开发后端时就借用了WordPress的框架。一来方便小编写文章,二来WordPress出色的模块如标签系统、用户系统、评论系统等,节省不少开发时间。

国内的团队反映用户登录后进行操作时,总是遇到用户未登录或登录过时的错误。使用同样的数据发送请求,我们这边却没有任何问题。

登录使用wp_signon(),var_dump($current_user)时就是已登录用户的详情。国内团队反应$current_user是空。我在使用wp_set_current_user()将登录用户设为$current_user后,问题就解决了。

才怪!哪有那么容易…今天温哥华这边快下班了,国内刚起床的弟兄们试了一下,登录是成功了,但用户进行登录后的操作时依然提示登录过期。

在公司使用Mac机,由于使用Postman未能重现问题,我回家用自己的PC下载了他们使用的Fiddler,终于知道问题所在:应该是cookie的问题。用户登录后,服务器会返回一个wordpress_logged_in_******的cookie,我在手动添加cookie发送新的请求时,问题迎刃而解。和国内团队沟通,才知道他们在客户端并没有保存cookie。

客户端保存cookie信息,每次用户进行登录后相关的操作时,后端可以通过WordPress function wp_set_auth_cookie() 来延长cookie有效期,这样用户也不用一再重复登录了。

同时为iOS和安卓提供后端服务支持,这也让我学了不少东西。iOS下运行正常的接口到安卓时,还得进行部分调整。以后再做类似的事情时也就少走一些弯路了。