CentOS + Apache2.4 + PHP5.6 FPM报503错误

Service Unavailable 503错误,很可能的原因是服务器过载。

错误提示:

The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.

服务器使用Apache2.4,fpm方式加载PHP,因此排查解决过程如下。

1. 查看PHP-FPM日志
#tail -f /alidata/server/php56/var/log/php-fpm.log
WARNING: [pool www] server reached pm.max_children setting (5), consider raising it

2. 修改PHP-FPM配置
#vi /alidata/server/php56/etc/php-fpm.conf
pm.max_children = 5
​修改为
pm.max_children = 100

计算依据参考:https://blog.csdn.net/solmyr_biti/article/details/53955141
pm.max_children = Total RAM dedicated to the web server / Max child process size – in my case it was 85MB
The server has 8GB of RAM, so:
pm.max_children = 6144MB / 85MB = 72

JS关闭网页window.close(兼容各类浏览器,包括微信浏览器)

function closePage()

{

var userAgent = navigator.userAgent;

if (userAgent.indexOf(“Firefox”) != -1 || userAgent.indexOf(“Chrome”) !=-1) {// Firefox或Chrome中关闭

  window.location.href = “about:blank”;

} else {

  window.opener = null;

  window.open(“”, “_self”);

  window.close();

}

if (WeixinJSBridge) {// 微信中关闭

WeixinJSBridge.call(‘closeWindow’);

}

}

Zend Framework exceeds the defined form size

程序上线很久了,一直没发生过此问题,突然报错。

由于代码没有动过,加上最近调整过服务器的PHP配置,因此猜测是PHP配置的问题。

果不其然。

post_max_size

upload_max_filesize

php.ini中的这两个配置项,由于其他项目上传大文件的需要,设置成了2048M。

经过反复修改此参数,发现不能超过2048M。

两个参数均设置为2000M,问题消失。

PHP gd库增加jpeg支持

wget http://www.ijg.org/files/jpegsrc.v9b.tar.gz
tar zxvf jpegsrc.v9b.tar.gz
cd jpeg-9b
./configure –prefix=/usr/local/jpeg –enable-shared –enable-static
make && make install

cd /alidata/server/temp/php-5.6.30/ext/gd
make clean
/alidata/server/php56/bin/phpize
./configure –with-php-config=/alidata/server/php56/bin/php-config –with-png-dir –with-freetype-dir=/usr/local/freetype.2.1.10 –with-jpeg-dir=/usr/local/jpeg –with-zlib-dir –with-gd
make && make install
/etc/init.d/php56-fpm restart

如果仍然不行,重新编译PHP。

如果重新编译仍然不行,那么就先不加gd选项编译PHP,然后再加上gd选项编译一次。

Centos6.6下编译安装最新apache

转载地址:http://www.linuxprobe.com/centos66-make-apache.html
安装编译环境
安装开发工具组
yum groupinstall 'Development tools'
仅安装需要的工具

因我的虚拟机是mini安装,配置aliyun的yum源家里小水管下载慢,就只装个gcc好了。

yum install gcc
下载源码包
下载最新版httpd

下载地址:http://httpd.apache.org/download.cgi#apache24

写此篇文章时最新版本为:httpd-2.4.27

在Centos6.6下编译安装最新apache在Centos6.6下编译安装最新apache

如果默认镜像下载太慢的话,可以在红框内选择其他国内镜像。

执行如下命令将源码包下载至Linux中并解压

wget https://mirrors.tuna.tsinghua.edu.cn/apache//httpd/httpd-2.4.27.tar.gz
tar -xf httpd-2.4.27.tar.gz
下载apr及apr-util

apr是Apache Portable Runtime的缩写,中文译为Apache可移植运行环境,是httpd所必须的,apr-util同理。

apr及apr-util下载地址:http://apr.apache.org/download.cgi

执行如下命令:

wget https://mirrors.tuna.tsinghua.edu.cn/apache//apr/apr-1.6.2.tar.gz
wget https://mirrors.tuna.tsinghua.edu.cn/apache//apr/apr-util-1.6.0.tar.gz
tar -xf apr-1.6.2.tar.gz
tar -xf apr-util-1.6.0.tar.gz

着手编译

首先编译apr
cd apr-1.6.2
./configure --prefix=/usr/local/apr  //指定安装位置
make && make install
编译apr-util
cd apr-util-1.6.0
./configure --with-apr=/usr/local/apr/ --prefix=/usr/local/apr-util  //指定apr所在目录及apr-util安装目录
make && make install

在这里会报一个错,如下所示

xml/apr_xml.c:35:19: error: expat.h: No such file or directory   //注意这一行,提示没有expat.h这个头文件
xml/apr_xml.c:66: error: expected specifier-qualifier-list before ‘XML_Parser’
......强行省略.....
xml/apr_xml.c: In function ‘do_parse’:
xml/apr_xml.c:434: error: ‘apr_xml_parser’ has no member named ‘xp’
.....不要看我......
xml/apr_xml.c:500: error: ‘apr_xml_parser’ has no member named ‘xp_err’
make[1]: *** [xml/apr_xml.lo] Error 1
make[1]: Leaving directory `/root/apr-util-1.6.0'
make: *** [all-recursive] Error 1

expat是解析XML的程序,是不是想立马执行yum install expat-devel呢?

然而我可以告诉你,然并卵!

不管是base库中的2.0版还是epel库中的2.1版在后面编译httpd的时候都会报错!

所以,我们去expat的官网去下载最新版的expat。

cd ~
wget https://nchc.dl.sourceforge.net/project/expat/expat/2.2.3/expat-2.2.3.tar.bz2
tar -xf expat-2.2.3.tar.bz2
cd expat-2.2.3
./configure --prefix=/usr/local/expat23
make && make install

现在可以重新编译apr-util了

cd ~
cd apr-util-1.6.0
./configure --with-apr=/usr/local/apr/ --with-expat=/usr/local/expat23/ --prefix=/usr/local/apr-util
make clean //因执行过一次编译了,保险起见把那些编译出来的东东删掉,虽然理论上并不影响
make && make install
编译httpd
cd ~
cd httpd-2.4.27
./configure --prefix=/alidata/server/httpd24 --sysconfdir=/alidata/server/httpd24/conf --with-mpm=prefork --enable-mpms-shared=all --enable-mods-shared=most --with-apr=/usr/local/apr --with-apr-util=/usr/local/apr-util/

到这一步会报错,提示我们没有装pcre,报错如下:

configure: error: pcre-config for libpcre not found. PCRE is required and available from http://pcre.org/

执行yum install pcre-devel即可

./configure --prefix=/alidata/server/httpd24 --sysconfdir=/alidata/server/httpd24/conf --with-mpm=prefork --enable-mpms-shared=all --enable-mods-shared=most --with-apr=/usr/local/apr --with-apr-util=/usr/local/apr-util/
make && make install

下面解释一下所用的选项:

  • –prefix=/usr/local/httpd24 :配置安装目录不用多说了吧?
  • –sysconfdir=/etc/httpd24:指定配置文件存放目录,默认存放到安装目录下
  • –with-mpm=prefork:默认mpm为prefork
  • –enable-mpms-shared=all:将所有mpm编译为.so模块
  • –enable-mods-shared=most:编译大多数模块为.so文件
  • –with-apr=/usr/local/apr:指定apr所在目录
  • –with-apr-util=/usr/local/apr-util/ : 指定apr-util所在目录

更多的./configure脚本选项请参考:http://httpd.apache.org/docs/2.4/programs/configure.html

测试新版httpd

首先将httpd2.4.27的bin目录加到PATH环境变量中以便执行命令~

echo "export PATH=$PATH:/usr/local/httpd24/bin" >> /etc/profile.d/httpd.sh
source /etc/rc.local

简单执行下面命令开启服务并测试:

apachectl start

打开浏览器–>访问192.168.123.233–>看到如下图说明安装成功

在Centos6.6下编译安装最新apache在Centos6.6下编译安装最新apache

上文所指报错

好吧,我没重现出错误情景….

如遇到类似如下报错,可自行编译expat

libaprutil-1.so: undefined reference to `XML_GetErrorCode'
新增编译时没有启用的模块
1. 进入apache源代码的modules/proxy目录
2. 运行如下命令自动编译、安装和修改httpd.conf文件,激活mod_proxy模块:
apachepath/bin/apxs -c -i -a mod_proxy.c proxy_util.c
apachepath/bin/apxs -c -i -a mod_proxy_http.c proxy_util.c
选项说明:
-c 执行编译操作
-i 安装操作,安装一个或多个动态共享对象到服务器的modules目录
-a 自动增加一个LoadModule行到httpd.conf文件,以激活此模块,若此行存在则启用之
-A 与-a类似,但是它增加的LoadModule行前有井号前缀(#)
-e 需要执行编辑操作,可与-a和-A选项配合使用,与-i操作类似,修改httpd.conf文件,但并不安装此模块
3. 重启apache。

PHP多版本共存:php5.6与php5.5共存

续:
php多版本共存,需要使用php-fpm来运行php方便,这样更方便配置和管理
准备工作:
创建PHP5.6的相关目录,不要与已安装的php5.5的目录相同

mkdir /alidata/server/php56  安装目录 
mkdir /alidata/server/php56/etc      配置文件存放目录

源码包下载:

wget http://cn2.php.net/distributions/php-5.6.30.tar.gz

解压,编译安装php

tar xvf php-5.6.30.tar.gz
cd php-5.6.30
所有配置参数与上一个php5.5的完全相同,只有安装的目录和配置文件目录不同。
./configure \
--prefix=/alidata/server/php56 \
--with-config-file-path=/alidata/server/php56/etc \
--with-apxs2=/alidata/server/httpd/bin/apxs \
--with-libdir=lib64
--enable-inline-optimization \
--disable-debug \
--disable-rpath \
--enable-shared \
--enable-opcache \
--enable-fpm \
--with-fpm-user=www \
--with-fpm-group=www \
--with-mysql=mysqlnd \
--with-mysqli=mysqlnd \
--with-pdo-mysql=mysqlnd \
--with-mssql=/usr/local/freetds \
--with-pdo-dblib=/usr/local/freetds \
--with-gettext \
--enable-mbstring \
--with-iconv \
--with-mhash \
--with-openssl \
--enable-bcmath \
--enable-soap \
--with-libxml-dir \
--enable-pcntl \
--enable-shmop \
--enable-sysvmsg \
--enable-sysvsem \
--enable-sysvshm \
--enable-sockets \
--with-curl \
--with-zlib \
--enable-zip \
--with-bz2 \
--with-gd \
--without-sqlite3 \
--without-pdo-sqlite \
--with-pear \
--with-mcrypt \
--with-freetype-dir=/usr/local/freetype.2.1.10 \
--with-jpeg-dir=/usr/local/jpeg.6

make && make install

若编译安装时遇到如下错误

/usr/local/src/php-5.3.10/ext/xmlrpc/libxmlrpc/encodings.c:73: undefined reference to `libiconv_open’ /usr/local/src/php-5.3.10/ext/xmlrpc/libxmlrpc/encodings.c:81: undefined reference to `libiconv’ /usr/local/src/php-5.3.10/ext/xmlrpc/libxmlrpc/encodings.c:101: undefined reference to `libiconv_close’ collect2: ld returned 1 exit status make: *** [sapi/fpm/php-fpm] 错误
在执行完 ./configure … 之后,修改下 Makefile,找到其中的
EXTRA_LIBS = -lcrypt -lz -lcrypt -lrt -lmysqlclient -lmcrypt -lldap -llber -lfreetype -lpng -lz -ljpeg -lcurl -lz -lrt -lm -ldl -lnsl -lrt -lxml2 -lz -lm -lssl -lcrypto -ldl -lz -lcurl -ldl -lgssapi_krb5 -lkrb5 -lk5crypto -lcom_err -lidn -lssl -lcrypto -lz -lxml2 -lz -lm -lssl -lcrypto -ldl -lz -lxml2 -lz -lm -lxml2 -lz -lm -lcrypt -lxml2 -lz -lm -lxml2 -lz -lm -lxml2 -lz -lm -lxml2 -lz -lm -lcrypt
在最后面添加 -liconv ,修改后如下
EXTRA_LIBS = -lcrypt -lz -lcrypt -lrt -lmysqlclient -lmcrypt -lldap -llber -lfreetype -lpng -lz -ljpeg -lcurl -lz -lrt -lm -ldl -lnsl -lrt -lxml2 -lz -lm -lssl -lcrypto -ldl -lz -lcurl -ldl -lgssapi_krb5 -lkrb5 -lk5crypto -lcom_err -lidn -lssl -lcrypto -lz -lxml2 -lz -lm -lssl -lcrypto -ldl -lz -lxml2 -lz -lm -lxml2 -lz -lm -lcrypt -lxml2 -lz -lm -lxml2 -lz -lm -lxml2 -lz -lm -lxml2 -lz -lm -lcrypt -liconv

配置php-fpm

拷贝配置文件,在安装目录里
cd /alidata/server/php56/etc
cp php-fpm.conf.default php-fpm.conf 
修改配置文件
vim php-fpm.conf
将listen = 127.0.0.1:9000 改为listen = 127.0.0.1:9056
与php5.59000端口区别开,防止端口冲突

拷贝服务脚本,在源码包里
# cp sapi/fpm/init.d.php-fpm /etc/init.d/php56-fpm
# chmod +x /etc/init.d/php56-fpm
# chkconfig --add php56-fpm

启动php-fpm

 service php56-fpm start
 监听端口为:9056

nginx 切换php版本只需修改nginx.conf即可
php5.5 php-fpm 监听端口为:9000
php5.6 php-fpm监听端口为:9056

vim /etc/nginx/nginx.conf
将fastcgi_pass   127.0.0.1:9000;修改为
  fastcgi_pass   127.0.0.1:9056;
  重启nginx,打开phpinfo

apache切换PHP版本:
由于之前是用php mod来运行php,php作为apache的一个模块来运行。如果想要使用多版本切换,就不能再用php mod来运行了,必须要用php-fpm运行,然后和Apache关联。

修改配置文件

vim /etc/httpd/httpd.conf

首先注释掉以下内容(这是用php mod与PHP关联的)

#LoadModule php5_module modules/libphp5.so
#<FilesMatch \.php$> 
   #SetHandler application/x-httpd-php
#</FilesMatch>

然后去掉以下两行的注释,开启代理功能

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so

再添加下面内容,使用php-fpm与PHP关联

<FilesMatch \.php$>
         SetHandler "proxy:fcgi://127.0.0.1:9056"
</FilesMatch>

重启apache服务,成功切换为php5.6
致此全部完成,nginx和apache全都可以正常切换PHP版本

WampServer死活连不上SQLServer的解决办法

下载了Wamp Server 2.5,连接MSSQLServer 2008 R2,一直提示“Fatal error: Call to undefined function sqlsrv_connect()”。

网上的办法全都试过也还是不管用(包括ext目录中添加dll、php.ini添加dll设置、ntwdblib.dll加入system32等等)。

如果遇到类似以上问题并且山穷水尽的同学,可以往下看。

首先在此感谢解决原作者:https://blog.csdn.net/xocom/article/details/45840725

下面开始说问题原因和方法。

原因:

经测试,微软最新发布的microsoft Drivers 3.1 for PHP for SQL Server仅支持32位的php5.5版本,在win7 64位系统中安装32位wamp环境可以正常开启扩展,但是安装了64位的wamp环境就不能开启扩展,网上查发现很多人反应64位的扩展源码编译不出来。

也就是说,微软发布的DLL扩展在64位的wamp中不能用

解决办法:

下载以下安装包,找到x64目录中对应的DLL,放到你的php/ext目录下。

地址一:http://oa.jnszkj.com/ueditor/php/upload/file/20180622/1529665745721557.zip

地址二:http://www.weka.cc/ueditor/php/upload/file/20180622/1529665745721557.zip

再次感谢原作者!感谢CSDN!感谢百度!微软就不谢了。

网页加载慢浏览器端分析方法

  1. 打开Chrome浏览器,访问要分析的网页
  2. 按下F12键,或在页面上点击右键选择“检查元素”
  3. 弹出的面板中,点击Network标签,选中里面的“Disable Cache”选项(目的是禁用缓存,否则不能体现首次访问加载所有资源的情况)
  4. 刷新网页,面板左下角会显示加载页面共请求了多少资源,共耗费多少时间,共使用了多少流量。并以表格形式显示出所有的网络资源请求,每一行一个,每行都包括请求该资源所耗费的时间、资源大小等信息。
  5. 下一步进行分析,主要从两个方面,一是请求时间,二是资源大小。
  6. 对于请求时间特别长的资源,可以看下是否该资源为国外资源,因为被墙导致访问时间长。
  7. 点击Size,按资源大小进行倒序排列,找到占用空间大的资源。

ERR: SOAP-ERROR: Parsing WSDL: Couldn’t load from XXX解决方案

原文链接:http://myitlife.blog.51cto.com/2289784/1529621

PHP在用SOAP协议做接口的时候,经常会碰到如下问题,不是不成功,而是偶尔不成功,实在让人费解!

ERR: SOAP-ERROR: Parsing WSDL: Couldn’t load from ‘http://www.xxxxx.com/member/member_sync.php?wsdl’ : failed to load external entity “http://www.xxxxx.com/member/member_sync.php?wsdl”

查找日志发现:

NOTIC: [2] SoapClient::SoapClient(): I/O warning : failed to load external entity “http://www.xxxxx.com/member/member_sync.php?wsdl” /home/wwwroot/default/xxxxx.com/App/Modules/Admin/Action/OrderAction.class.php 第 112 行.客户端$client = new SoapClient ( $url );创建SoapClient对象时出错!

网上查找很多资料,有开启selinux、关闭soap缓存、开启openssl等等答案,但尝试过后都不尽人意,现终极解决办法:

只需把php的客户端SoapClient的wsdl模式修改为non-wsdl模式,即可解决!

wsdl模式:

$url = “http://xxxxx.org/member/member_sync.php?wsdl”;

$client = new SoapClient ( $url );

non-wsdl模式:

$client = new SoapClient(null,array(‘location’=>’http://www.xxxxx.com/member/member_sync.php’,uri => ‘member_sync.php’));

Windows环境下安装phpunit

参考文章:http://blog.csdn.net/sunshinelyc/article/details/49834293#comments

进来由于工作需要,安装phpunit,在网上找了各种安装方法,pear添加phpunit通道安装,手动安装,各种失效,各种无语,皆因网上的资料都是年份比较久远的(也有可能本人不太了解phpunit的工作原理),导致安装了一个下午进展缓慢。最后在phpunit.de中看到了一篇文章,然后第二天再试着安装了一次,具体成功了(然后也对phpunit有了一个比较基础的了解)故此,记录一下本人安装phpunit的过程,方便自己和路人查阅,该文章仅作为本人查阅之用,故此希望网友看到了不要吐槽。。微笑

话不多说,直接开展。。。

首先,官网文档连接如下phpunit.de ,如果是由于年份久远导致无法连接的只能再找找资料了(再网上找资料的时候就碰到了一大推年份久远导致链接失效的情况。。。大哭,导致坑越来越深。。。)

安装步骤如下:

其实安装过程非常非常非常简单。。(注意用了三个非常,足以证明强调意义)

1、在官网下载phpunit.phar包,链接如下:phpunit.phar,下载和自己php版本对应的稳定包即可。(注意:这里是PHPUnit 的 PHP 档案包,它将 PHPUnit 所需要的所有必要组件(以及某些可选组件)捆绑在这个文件中)

2、创建一个文件夹,这里我在d盘创建了phpunit,路径为D:\phpunit,将该路径添加到path环境变量中,然后将下载好的phpunit.phar放到该文件夹中

3、打开一个cmd窗口,按照如下执行(每个人的cmd路径不一样,不要对号入座。。):

C:\Users\Administrator>d:

D:\>cd phpunit

D:\phpunit>echo @php “%~dp0phpunit.phar” %* > phpunit.cmd

D:\phpunit>exit

4、重新打开一个新的cmd窗口,在cmd中执行执行phpunit –version

C:\Users\Administrator>phpunit –version
PHPUnit 4.8.18 by Sebastian Bergmann and contributors.

这样子就表示你的phpunit已经安装完成了。。是不是超级简单。。不到半个小时就搞定的事情,我昨天研究了一个下午。。也是够蠢的。。。(不要局限于用pear安装。安装方法各种各样。结果达到就行。。),暂时介绍安装。后续继续补充phpunit的学习过程,毕竟这东西,才有一点点概念。至于怎么使用。原理是什么还是一头雾水。。

Zend Framework单元测试方法

Update: Things are all change in Zend Framework 1.8, so this may require some adaptation in that version.

If you’ve followed the Zend Framework quickstart tutorial like me, you may be wondering how to perform unit testing using it. The tutorial does set up the bootstrap file to allow for unit testing, but does not cover unit testing itself. The aim of this post is to  provide a quick guide to unit testing using the quickstart application, which will hopefully come in useful for a few people and myself when I inevitably forget this later on.

Step 0: Before Starting

First, you should download and set up the guestbook application from the Zend Framework website. Make sure you place the Zend library in the library folder and enable the read/write permissions for the sqlite database and the db directory for access by the web server.

You will also want to install pear and, with it, install PHPUnit:

pear channel-discover pear.phpunit.de

pear install phpunit/PHPUnit

Step 1: Create Test Folders

Create a folder called “tests” in the root folder of the application. Inside that create a folder called “controllers” and “models”. The  directory structure should be as follows:

zendquickstart
|-- application
|-- data
|-- library
|-- public
|-- scripts
`--tests
 |-- controllers
 `-- models

All your controller tests should go in the controllers folder, all your model tests should go in the models folder.

Step 2: Create Test Configuration

Create the file TestConfiguration.php in the tests folder. This file will initially set up the testing configuration, inside place the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?php
// Gets called when this file is included/required
TestConfiguration::setUp();

class TestConfiguration
{
    /**
    * Sets the environment up for testing
    */
    static function setUp()
    {
        // Set the environment constant to testing which will load the testing
        // configuration in app.ini by the bootstrap
        define('APPLICATION_ENVIRONMENT', 'testing');

        // Set the include path for locating the Zend library
        set_include_path(realpath(dirname(__FILE__)) . '/../library'
            . PATH_SEPARATOR . get_include_path());

        // Use Autoload so that we don't have to include/require every class
        require_once "Zend/Loader.php";
        Zend_Loader::registerAutoload();
    }

    static function setUpDatabase()
    {
        require '../application/bootstrap.php';

        $db = Zend_Registry::get('configuration')->database->params->dbname;

        // delete any pre-existing databases
        if(file_exists($db)) unlink($db);

        // run the database set up script to recreate the database
        require '../scripts/load.sqlite.php';
    }
}
?>

The setUp() function in this class will get called when the file is included or required. The APPLICATION_ENVIRONMENT constant is set to ‘testing’ so that the testing section of the app.ini file is read in the bootstrap, and the testing database is used. A testing database should be used so that testing does not interfere with the production or development databases, and no important  data is lost. The next step is to set the include path, which should contain the location of the Zend Framework library. The final step is to enable the Zend autoloader so that we don’t have to explicitly require/include the Zend libraries.

The setUpDatabase() function resets the database to a known state. When the tests are run we may wish to insert data to the database and this should be removed before every new test. We require the bootstrap file so we can get the database configuration and delete the database file, which is an easy way to reset an SQLite database. A different method will be necessary for other databases like MySQL or PostgreSQL. Finally, the setUpDatabase() function uses a script which comes with the quick start application to (re)create the database.

Step 3: Creating Controller Tests

To test the IndexController, create the file IndexControllerTest.php in the tests/controller directory. The following is a simple example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?php
// Set up the testing environment
require 'TestConfiguration.php';

class controllers_IndexControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
{
    // Bootstraps the application
    public $bootstrap = '../application/bootstrap.php';

    public function testHomePageIsASuccessfulRequest()
    {
        // Runs the test on /, the homepage
        $this->dispatch('/');

        // Tests there are no exceptions on the home page
        $this->assertFalse($this->response->isException());

        // Tests for redirection to the error handler
        $this->assertNotRedirect();
    }

    public function testHomePageDisplaysCorrectContent()
    {
        // Runs the test on /
        $this->dispatch('/');

        // Tests the page title is present
        $this->assertQueryContentContains(
            'div#header-logo',
            'ZF Quickstart Application'
        );

        // Tests the guestbook link is present
        $this->assertQueryContentContains('a', 'Guestbook');
    }
}
?>

This is a very basic controller test. First note that you should extend Zend_Test_PHPUnit_ControllerTestCase for controller tests. This class extends PHPUnit_Framework_TestCase itself, but adds some assertions and other things specific to Zend Framework.

The bootstrap instance variable must be set in order to test the controller, and in this case it points to the location of the bootstrap file.

The testHomePageIsASuccessfulRequest() test is used to test that the homepage functions correctly; it should not contain any exceptions or redirect to the error controller. If it does then there is a problem somewhere and the test fails. The testHomePageDisplaysCorrectContent() is used to test that the page title and the link to the guestbook is present.

Step 4: Creating Model Tests

To test the GuestBook model, create the file GuestBookTest.php in tests/models.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
require 'TestConfiguration.php';
require '../application/models/GuestBook.php';

class models_GuestBookTest extends PHPUnit_Framework_TestCase
{
    public function setUp()
    {
        // Reset database state
        TestConfiguration::setUpDatabase();
    }

    public function testFetchEntries()
    {
        // Instantiate the GuestBook model
        $guestBook = new Model_GuestBook();

        // Get all entries from the database
        $entries = $guestBook->fetchEntries();

        // Test that there are 2 entries in the guestbook
        $this->assertSame(2, count($entries));
    }
}
?>

Again, this is very basic. The setUp() function gets called by PHPUnit before the test is run, and it will reset the database. The testFetchEntries() function tests the fetchEntries() function in the model. Two rows in the guestbook table should be present, therefore there should be two elements in the array returned by fetchEntries().

Step 5: Edit load.sqlite.php

You may find it annoying to have to wait 5 seconds between model tests when the database is recreated. I worked around this problem by making a small change to the PHP script which loads the database schema:

1
2
3
4
5
6
7
8
9
<?php
if (APPLICATION_ENVIRONMENT != 'testing')
{
    echo 'Writing Database Guestbook in (control-c to cancel): ' . PHP_EOL;
    for ($x = 5; $x > 0; $x--) {
        echo $x . "\r"; sleep(1);
    }
}
?>

If the application environment is not testing then the timeout should not be displayed. You may prefer to simply remove the timeout altogether. Additionally, you will need to change the lines that locate the SQL files to include dirname(FILE):

1
2
3
4
<?php
$schemaSql = file_get_contents(dirname(__FILE__) . '/schema.sqlite.sql');
$dataSql = file_get_contents(dirname(__FILE__) . '/data.sqlite.sql');
?>

Step 6: Running the Tests

To run the tests, navigate to the tests directory in a terminal/CMD, and type phpunit controllers_IndexControllerTest and models_GuestBookTest to test the IndexController and GuestBook model, respectively.

phpunit controllers_IndexControllerTest

PHPUnit 3.3.14 by Sebastian Bergmann.

..

Time: 0 seconds

OK (2 tests, 4 assertions)

phpunit models_GuestBookTest

PHPUnit 3.3.14 by Sebastian Bergmann.

Database Created

Data Loaded.

.

Time: 0 seconds

OK (1 test, 1 assertion)

Conclusion

There you have it. This isn’t necessarily the only way or the best way to perform unit testing, this is merely the way I got it to work with the quickstart application having had no prior experience with PHPUnit. Any suggestions or improvements are welcome.

Credits

I based this on the Zend Framework manual and the book Zend Framework in Action.