PHP 使用 Redis 来做队列服务

为客户端开发 Api,采用 PHP,我们使用了一个叫做 Swoole_framework 的框架,同时使用了 swoole 扩展。在开发接口时使用了框架提供的模型类,但是,服务端除了接口,不可避免还有一些后台脚本,比如每日任务初始化,用户分数结算,后台脚本直接 require 相关配置文件,然后手动 update 数据库数据。这两天做需求的时候就发现,有些业务逻辑在服务端 api 层需要实现,在后台脚本里面也要实现,而这两处的代码不能复用,因此同样地逻辑处理了两遍,随着后续功能的增加,重复的代码还会增加。上周因为比较急就先采用这种方法完成了代码,实现了功能,这两天测试联调,总感觉同样地逻辑在多个地方出现,不仅写代码时麻烦,测试时也麻烦,下午灵光一现想到可以使用任务队列来完成这个工作。

简单来说,就是在原先逻辑处理的地方,只是简单增加一个任务,不再进行具体的业务逻辑,后台脚本和 api 都如此,然后再单独跑一个脚本,拉取任务,进行业务逻辑处理。这个的思路好处很明显,同样地逻辑归到了一处,开发以及调试找错时会清晰很多。

需要实现一个任务队列,使用 redis ,简单封装了一个队列:

队列的一个特点就是先进先出(FIFO),很显然,先产生的任务需要被先处理,redis 的 List 可以保证这一点。

晚上将代码重新组织,用任务队列加单独脚本的方式实现了需要的业务功能,顿时感觉浑身舒畅了许多。

nginx php-fpm 捕获记录 php 的错误日志

请确认 php-fpm.conf 的如下配置为:

catch_workers_output = yes # 捕获进程的输出,这个值默认为 no
error_log = log/php-fpm.log # 这个是默认值,指定 php-fpm 输出 log 的文件路径

确认 php.ini 的如下配置为:

log_errors = On # 开启记录错误日志,这个值默认为 Off
error_reporting=E_ALL&~E_NOTICE # 默认就是这个值,php 的 error_reporting 级别

参考网址:http://www.nginx.cn/666.html

腾讯 PHP 笔试题,写一个 is_writable 函数的替代函数

今年三月份去腾讯面试过。笔试题有一题是,PHP 中的 is_writable 函数不可信,请写一个函数来替代这个函数。

拿到这个题目,我便想起之前读 CI 框架实现的时候看到 CI 里面写了个 is_really_writable 函数,当时没有注意,只看这个函数的功能,至于 CI 为什么要自己实现一个 is_writable 函数的功能没有去深究。当时,我的解题思路是这样的,既然 is_writable 不可信,那么我就实实在在的尝试去那个文件夹里进行写入,如果可以写则返回 true,否则返回 false,思路是对的,但代码并不是完全无误,有瑕疵。

回到家后我便开始搜索,同时重新仔细查看 CI 的那个函数实现以及说明,原来是因为在 Windows 服务器上如果一个文件夹有只读属性,is_writable 这个函数还是会返回 true,而实际上 php 是不能进行写入的,另外一种情况是,在 unix 服务器上,如果 safe_mode 选项打开,那么 is_writable 函数也是不可信的。

这个事情提醒我,当遇到一个问题时,一定要追查到底,搞明白它的原理以及那样做的理由。

附上 CI 里面 is_really_writable 函数的实现:

 

珍爱生命,拥抱 PHPUnit

在公司负责服务端 Api 开发,为安卓客户端提供相应的接口,在开发的过程中,免不了要测试完成的接口是否可用。最开始的测试手段是使用 POSTMAN 等工具发送请求。使用这类工具的流程一般是,先构造 url 请求接口进行登录,登录后拿到服务器返回的一个标识(可以看做是一个 session,但这个 session 的概念和传统的网站开发中的 session 概念不一样),在后续的接口调用中,都需要在请求 header 头里面加上那个标识。这个流程会比较繁琐,至少需要多次鼠标点击以及复制粘贴的过程才能完成。偶尔这样做还好,这种事情做多了便让我觉得烦躁无比。

了解了下 PHPUnit,先读了读官网的 manual,大体明白怎么写测试了就开始动手。

具体到我们的业务,首先写了一个 Client 类,这个类主要作用是模仿客户端发一个请求,当然这个类会进行一些简单的封装,比如 curl 请求的时候带上我们的标识(上文说的 session),还有其它的模拟安卓客户端的一些请求头。刚开始我在每个测试方法中都 new 一个 Client 对象,后来想想不对,既然是模仿客户端,而一个客户端永远是一个实例,每次 new 一个出来显然不符合现实逻辑,另外一个问题是,测试登录接口后,session 标识需要在后续的接口测试中反复使用。想到这,于是将 Client 类改为了单例实现,这样就能保证在多个测试方法的 Client 为同一个实例对象,并能共享 session 标识状态。

下面是 Client 的实现及一个简单的测试用例文件示例:

要测试新的接口,只需要增加新的方法就好了,多次测试多次运行单元测试命令就 OK 了,可以节约大量时间啊。

另外,我发现在单元测试类里面的不同方法好像是单独运行的,比如下面的这个代码:

输出结果为 1 2 ,并不是想象中的 1 3,这是为什么呢?

Ubuntu Server 14.04 编译安装 PHP

每次编译安装 PHP 过程中出现了一些错误,都是直接谷歌相应的解决方案。刚才又在 Linode 上编译了一遍最新的 PHP,记录过程如下,以做备忘。

首先安装 build-essential 包:

进入 php 源码目录,我的 configure 命令选项如下:

配置过程中,会出现若干错误,提示各种头文件找不到,下面分别给出错误提示和对应的需要安装的包:

直到没有错误为止。最后:

至此编译安装完成。

 

PHP 的 print_r 函数并不是仅能输出数组或对象

大家都知道 PHP 中,print_r 函数能够打印输出数组,有些公司还会这样的笔试题:print 和 print_r 的区别,一般的回答便是:print 输出字符串,print_r 输出数组。前两天 coding 时偶然发现给 print_r 传了字符串,竟然正常输出,没有报 warning 和 notice,我就有点奇怪了,print_r 不是只能输出数组和对象的吗,怎么传个字符串也正常输出了。于是看手册,手册说

print_r() displays information about a variable in a way that’s readable by humans. … If given a stringinteger or float, the value itself will be printed. If given an array, values will be presented in a format that shows keys and elements. Similar notation is used for objects.

并没有任何地方说明 print_r 只能用于输出数组和对象,对于普通的 string integer float 型参数直接输出,相当于 print。

结论?结论就是完全没有理由使用 print 这个函数了。

configure: error: Cannot locate header file libintl.h 错误的解决方法

MAC OS 上编译 PHP 时,在 configure 配置阶段出现如题所示错误。找不到 libintl.h 头文件。

解决方法如下:

1. 安装 gettext:

2. 编辑 configure 文件:

将:

更改为:

3. 重新运行 ./configure 即可

深入理解 PHP 之 count 函数

曾有一次面试时面试官问我

会返回多少。当时我一下子懵了,不带这样玩的啊,count 函数不是用来计算数组元素个数的么,你传个字符串进去是几个意思啊。然后我满脸疑问并不自信的回答,是 3 吗,因为我觉得 PHP 的字符串也有时候可以表现得像数组,比如你可以用下标来取到字符串中对应位置的字符。面试官说是 1,然后我说没这样用过,一般都是用 count 来计算数组元素个数,面试官说,这就说明你代码量不够了。

写了简单文件,我们来看看给 count 函数传入非数组的参数时会返回什么:

上面代码输出如下:

可以看到 count 在作用于非数组的变量时,除了 null,其它都返回 1,为什么会这样呢,我们来看看 PHP 有关于 count 函数的 C 源代码:

源码很明显,如果传入的参数为 null,则返回 0,如果传入的数组,则计算数组元素个数,如果是对象,则按对象进行相应处理(这个过程我不是太明白),重点在于最后的 default,也就是说其它任何类型都返回 1.

完。

PHP 中 array_replace 和 array_merge 区别

PHP 5.3.0 新增了一个函数 array_replace,和 array_merge 作用很相似。

先看下面代码示例:

上面代码输出为:

很明显,array_replace 和 array_merge 的区别表现在处理数字索引的数据时,array_merge 会认为是不同的索引,不会进行覆盖,而 array_replace 则进行了覆盖。实际上 array_replace 和数组的 + 法运算的处理比较类似,但是参数的顺序得调换一下位置,下面的代码输出一致:

 

没注意运算符优先级(漏掉括号)导致的一个 BUG

昨天新上线的一个项目,换了 $_SESSION 的处理方式。代码中有这样一条 SQL

线上代码运行过程不停的 warning ,说 SQL 语句执行出错,出错语句为 “-1440”,找了很长时间都没找到这个 BUG,后来终于找到那个 SQL  语句:

为什么会是这样呢,因为字符串链接符 . 的优先级比运算符 – 要高,前面那一串先和 time() 返回的数字连成一个字符串,然后再与 1440 做减法运算,前面的一串字符串强制转换为数字 0,然后减 1440,就得到了 -1400,然后错误就出现啦。找到问题就好解决了,加上括号便 OK。

话说这类错误很不好发现啊,项目里面那么多地方运行 SQL 查询,哪知道是哪里出错的呢。

看来有必要在 Log 里面里面记录函数调用栈,这样才能知道是哪里出错,能快速定位 BUG。