##2.22.1 PHPUnit:自動化單元測試
目前核心框架單元測試的覆蓋率(高達90%以上!!!此處應該有掌聲~):

注意,此測試截圖并不是最新的,但我們一直都在致力堅持單元測試。
這一點,你可以在單元測試的代碼中找到證明。
##2.22.2 Phing:一鍵部署、快速發布
無論如何,都應該走自動化發布流程,避免人工地打包、上傳、解壓、改生產配置這些重復性的人工操作。
在自動化發布中,Phing是個不錯的嘗試。
以下是我某個項目中的使用配置,出于對項目的保護,部分數據已刪除,但仍然可以參考。
###(1)發布配置 - build.xml
主要的操作有:
+ 1. 備份當前代碼,并刪除一天前的備份
+ 2. 從SVN簽出最新的發布代碼
+ 3. 執行配置檢測腳本
+ 4. 代碼發布切換,并替換使用線上配置文件
+ 5. 移除單元測試初始化文件,以免誤執行
```javascript
<?xml version="1.0" encoding="UTF-8"?>
<!-- ============================================ -->
<!-- PhalApi -->
<!-- @dogstar 20141221 -->
<!-- ============================================ -->
<project name="demo.phalapi.com" default="build">
<property
name="backup_path"
value="/home/apps/backup/demo.phalapi.com"
override="true" />
<property
name="backup_prefix"
value="demo.phalapi.com_phing_backup_"
override="true" />
<property
name="svn_todir"
value="/home/apps/svn/demo.phalapi.com"
override="true" />
<!-- ============================================ -->
<!-- Target: prepare -->
<!-- ============================================ -->
<target name="prepare">
<mkdir dir="./Runtime" />
<mkdir dir="${svn_todir}" />
<mkdir dir="${backup_path}" />
</target>
<!-- ============================================ -->
<!-- Target: svn checkout -->
<!-- ============================================ -->
<target name="svncheckout">
<svncheckout
repositoryurl="svn://127.0.0.1/PhalApi/demo/release"
username="test"
password="123456"
nocache="true"
todir="${svn_todir}" />
</target>
<!-- ============================================ -->
<!-- Target: svn update -->
<!-- ============================================ -->
<target name="svnup" depends="svncheckout">
<svnupdate
repositoryurl="svn://127.0.0.1/PhalApi/demo/release"
username="test"
password="123456"
nocache="true"
todir="${svn_todir}" />
</target>
<!-- ============================================ -->
<!-- Target: check config -->
<!-- ============================================ -->
<target name="checkconfig">
<php expression="include('${svn_todir}/Tools/check_config.php')" />
<php
function="checkProd"
class="Tools_Check_Config"
returnProperty="checkProdErrorMsg" />
<fail msg="${checkProdErrorMsg}" if="checkProdErrorMsg" />
</target>
<!-- ============================================ -->
<!-- Target: backup -->
<!-- ============================================ -->
<target name="backup">
<php
expression="date('Ymd', strtotime('-1 day'))"
returnProperty="backup_version_yesterday" />
<php
expression="date('Ymd')"
returnProperty="backup_version_today" />
<delete>
<fileset dir="${backup_path}">
<exclude name="${backup_prefix}${backup_version_yesterday}*" />
<exclude name="${backup_prefix}${backup_version_today}*" />
</fileset>
</delete>
<php
expression="date('YmdHis')"
returnProperty="backup_version" />
<zip destfile="${backup_path}/${backup_prefix}${backup_version}.zip" basedir="." >
<fileset dir=".">
<include name="**/**" />
<exclude name="./Runtime" />
<exclude name="./Runtime/**" />
<exclude name="./.svn" />
<exclude name="./.svn/**" />
</fileset>
</zip>
<copy
file="${backup_path}/${backup_prefix}${backup_version}.zip"
tofile="${backup_path}/${backup_prefix}lastest.zip"
overwrite="true" />
</target>
<!-- ============================================ -->
<!-- Target: build -->
<!-- ============================================ -->
<target name="build" depends="prepare,svnup,checkconfig,backup">
<copy todir="." overwrite="true" >
<fileset dir="${svn_todir}">
<include name="**/**" />
<exclude name="${svn_todir}/Config/dbs.php" />
<exclude name="${svn_todir}/Config/sys.php" />
<exclude name="${svn_todir}/Test" />
<exclude name="${svn_todir}/Test/**" />
</fileset>
</copy>
<copy
file="./Config/dbs.php.prod"
tofile="./Config/dbs.php"
overwrite="true" />
<copy
file="./Config/sys.php.prod"
tofile="./Config/sys.php"
overwrite="true" />
<!-- 避免在生產環境執行PHPUnit,故把測試環境的初始文件移開 -->
<move
file="./Test/test_env.php"
tofile="./Test/test_env.php.bak"
overwrite="true"/>
</target>
</project>
```
###(2)回滾配置 - rollback.xml
主要操作:
+ 1. 回滾到上一個版本
```javascript
<?xml version="1.0" encoding="UTF-8"?>
<!-- ============================================ -->
<!-- PhalApi -->
<!-- @dogstar 20141222 冬至 -->
<!-- ============================================ -->
<project name="demo.phalapi.com" default="rollback">
<property
name="backup_path"
value="/home/apps/backup/demo.phalapi.com"
override="true" />
<property
name="backup_prefix"
value="demo.phalapi.com_phing_backup_"
override="true" />
<property
name="svn_todir"
value="./__svn__"
override="true" />
<!-- ============================================ -->
<!-- Target: rollback -->
<!-- ============================================ -->
<target name="rollback" >
<unzip file="${backup_path}/${backup_prefix}lastest.zip" todir="." >
<fileset dir=".">
<include name="*.zip"/>
</fileset>
</unzip>
</target>
</project>
```
##2.22.3 autobench:接口壓力測試與可視化圖表
可以通過以下的腳本來進行,使用示例:
```javascript
$ ./autobench.sh
Usage: ./autobench.sh <host> <uri>
- ./autobench.sh www.baidu.com /index.php
```
參數可以自行調整。
###(1)利用bench2graph生成可視化圖表
####純PHP訪問 - 入口歡迎接口

####mysql訪問 - 事件獲取接口

####帶MC緩存的訪問 - 應用入口 - 帶緩存的身份token驗證

###(2)詳細的數據報表
```javascript
dem_req_rate req_rate_demo.phalapi.com con_rate_demo.phalapi.com min_rep_rate_demo.phalapi.com avg_rep_rate_demo.phalapi.com max_rep_rate_demo.phalapi.com stddev_rep_rate_demo.phalapi.com resp_time_demo.phalapi.com net_io_demo.phalapi.com errors_demo.phalapi.com
5 5.0 5.0 4.8 5.0 5.2 0.1 22.4 2.9 0
25 25.0 25.0 25.0 25.0 25.0 0.0 21.6 14.3 0
45 45.0 45.0 45.0 45.0 45.0 0.0 22.3 25.7 0
65 64.9 64.9 64.7 64.7 64.7 0.0 25.4 37.0 0
85 84.8 84.8 84.6 84.6 84.6 0.0 28.6 48.3 0
105 104.6 104.6 0.0 0.0 0.0 0.0 34.4 59.7 0
125 124.0 124.0 0.0 0.0 0.0 0.0 42.2 70.7 0
145 143.8 143.8 0.0 0.0 0.0 0.0 59.2 82.0 0
165 147.4 147.4 0.0 0.0 0.0 0.0 262.9 84.1 0
185 151.1 151.1 0.0 0.0 0.0 0.0 429.7 86.2 0
```
###(3)附腳本
```javascript
#!/bin/bash
if [ $# -eq 0 ]; then
echo "Usage: $0 <host> <uri>"
echo ""
echo " - $0 www.baidu.com /index.php"
echo ""
exit
fi
DM=$1
URL=$2
#--signle_host 只測單機
#--host1 測試主機地址
#--uri1 host1 測試URI
#--quiet 安靜模式
#--low_rate 測試時最低請求數(指 httperf)
#--hight_rate 測試時最高請求數
#--rate_step 每次測試請求數增加步長
#--num-call 每連接中發起聯接數,一般是1
#--num_conn 測試聯接數
#--file 測試結果輸出的 tsv文件
autobench \
--single_host \
--host1=$DM \
--port1=80 \
--uri1=$URL \
--low_rate=5 \
--high_rate=200 \
--rate_step=20 \
--num_call=1 \
--num_conn=500 \
--timeout=10 \
--file ./$DM.tsv
```
##2.22.4 xhprof:性能分析工具
xhprof是一個不錯的內部性能分析工具,這里不過多的講述此工具的特點和使用,但會以對PhalApi進行的一個性能測試展示它的分析效果。
###(1)測試的接口服務
http://api.phalapi.com /demo/?service=Default.Index&username=test
###(2)Overall Summary
```javascript
Total Incl. Wall Time (microsec): 7,873 microsecs
Total Incl. CPU (microsecs): 7,999 microsecs
Total Incl. MemUse (bytes): 304,456 bytes
Total Incl. PeakMemUse (bytes): 306,616 bytes
Number of Function Calls: 338
```
###(3)Top 10耗時
Function Name|Calls|Calls%|Incl. Wall Time(microsec)|IWall%(microsec)|Excl. Wall Time|EWall%
---|---|---|---|---|---|---
PhalApi_Loader::loadClass| 10| 3.00%| 3,020| 38.40%| 1,038| 13.20%
PhalApi_Loader::loadClass1| 4| 1.20%| 785| 10.00%| 398| 5.10%
run_init::Public/init.php| 1| 0.30%| 3,915| 49.70%| 327| 4.20%
file_exists| 18| 5.30%| 249| 3.20%| 249| 3.20%
main()| 1| 0.30%| 7,873| 100.00%| 248| 3.20%
load::zh_cn/common.php| 2| 0.60%| 237| 3.00%| 237| 3.00%
PhalApi_Loader::load| 9| 2.70%| 3,380| 42.90%| 226| 2.90%
PhalApi_DI::get| 9| 2.70%| 1,268| 16.10%| 223| 2.80%
PhalApi_Translator::addMessage| 2| 0.60%| 522| 6.60%| 136| 1.70%
DI| 15| 4.40%| 645| 8.20%| 134| 1.70%
class_exists| 11| 3.30%| 1,383| 17.60%| 130| 1.70%
load::Config/sys.php| 1| 0.30%| 120| 1.50%| 120| 1.50%
對應的圖表如下:

從中可以看出,主要的耗時途徑在于文件的加載,下面將進一步探討。
###(4)Top 1耗時深入

所加載的文件如下:

##2.22.5 Jenkis和Sonar:持續集成和靜態代碼分析
PhalApi從來不會隱藏自己的設計,當然,我們也不會隱藏我們內部的各個細節以及存在的問題。
但和其他隱藏了技術債務的框架相比,PhalApi敢于展示自己的靜態代碼分析報告,并且它也是做得相當出色的。
以下截圖來自 GIT@OSC 上的靜態代碼分析:

當你的項目很重要時,也可以使用Jenkis或者Sonar進行靜態代碼的分析。
不要個人主觀地覺得你的代碼風格寫得好,而是交由專業的分析工具進行解剖,并理解各個數據報表背后的含義,然后改進之。
##2.22.6 Git/SVN:更多的版本控制
##2.22.7 WIKI:團隊溝通與文檔交流
###(1)markdown接口模板
```
#3.2 接口文檔模板
##1、功能說明
_請在這里放置簡短的接口功能說明。_
##2、接口URL
/?service= _接口服務名稱_ + 公共參數(是否免登錄態?)
##3、接口參數 [跳轉](http://demo.phalapi.com/demo/checkApiParams.php?service=)
參數|必須|默認值|說明
---|---|---|---
user_id|1||用戶id
##4、返回結果
###返回字段
參數|類型|說明
---|---|---
data.username|string|用戶名
###結果示例
{
"ret": 200,
"data": {
.... //更多結果的說明
"msg": ""
},
"msg": ""
}
###請求示例
_請放置一個接口請求的鏈接。_
http://demo.phalapi.com/demo/?service=
```
##2.22.8 靜態代碼分析工具 - phpmetrics
以下是針對本框架核心代碼所做的分析報告:


PS:從報告的評估可以看出,我們的框架明顯具有相當 高的可維護性。
- 歡迎使用PhalApi!
- 接口,從簡單開始!
- [1.1]-下載與安裝
- [1.2]-創建一個自己的項目
- [1.3]-在線體驗
- [1.4]-文檔、幫助和官網
- [1.10]-對PhalApi框架的抉擇
- [1.11]-快速入門(backup)
- [1.12]-參數規則:接口參數規則配置
- [1.13]-統一的接口請求方式:_sevice=XXX.XXX
- [1.14]-統一的返回格式和結構:ret-data-msg
- [1.15]-數據庫操作:基于NotORM的使用及優化
- [1.16]-配置讀取:內外網環境配置的完美切換
- [1.17]-日記紀錄:簡化版的日記接口
- [1.18]-快速函數:人性化的關懷
- [1.19]-DI服務速查:各資源服務一覽表
- [1.20]-DB操作:數據庫基本操作速查
- [1.21]-類的自動加載:遵循PEAR包的命名規范
- [1.22]-簽名驗證:自定義簽名規則
- [1.23]-請求和響應:GET和POST兩者皆可得及超越JSON格式返回
- [1.24]-緩存策略:更靈活地可配置化的多級緩存
- [1.25]-國際化翻譯:為走向國際化提前做好翻譯準備
- [1.26]-數據安全:數據對稱加密方案
- [1.27]-精益開發:更富表現力的Model層和重量級數據獲取的應對方案
- [1.28]-COOKIE:對COOKIE原生態的支持及記憶加密升級版
- [1.29]-開放與封閉:多入口和統一初始化
- [1.30]-保持的力量:接口開發最佳實踐
- [1.31]-新型計劃任務:以接口形式實現的計劃任務
- [2.11]-核心思想:DI依賴注入-讓資源更可控
- [2.12]-海量數據:可配置的分庫分表
- [2.13]-接口調試:在線SQL語句查看與性能優化
- [2.14]-測試驅動開發:意圖導向編程下的接口開發
- [2.15]-演進:新型計劃任務續篇
- [2.16]-領域驅動設計:應對復雜領域業務的Domain層
- [2.17]-微服務:Api接口服務層
- [2.18]-定制化:資源服務的再實現
- [2.19]-擴展庫:可重用的擴展類庫
- [2.20]-約定編程:架構明顯的編程風格
- [2.21]-服務器統一部署方案簡明版:CentOs---Nginx---php-fpm---MySql-[--Memcached]
- [2.22]-更多工具:精益項目和團隊建設
- [3.1]-擴展類庫:微信開發
- [3.2]-擴展類庫:代理模式下phprpc協議的輕松支持
- [3.3]-擴展類庫:基于PHPMailer的郵件發送
- [3.4]-擴展類庫:優酷開放平臺接口調用
- [3.5]-擴展類庫:七牛云存儲接口調用
- [3.6]-擴展類庫:新型計劃任務
- [3.8]-擴展類庫:用戶、會話和第三方登錄集成
- [3.9]-擴展類庫:swoole支持下的長鏈接和異步任務實現
- [3.11]-擴展類庫:基于FastRoute的快速路由
- [4.2]-開發實戰2:模擬優酷開放平臺接口項目開發
- [4.3]-開發實戰3:一個簡單的小型項目開發(奔跑吧兄弟投票活動)
- [5.1]-架構與思想:PhalApi核心設計和思想解讀
- [5.2]-雜談:扯一些PhalApi的前世和今生
- [5.3]-框架總結:術語表和PHP開發建議
- [5.4]-許可
- [5.5]-聯系和加入我們
- [5.6]-更新日記
- [5.8]-致框架貢獻者:加入PhalApi開源指南
- [6.1]-基于接口查詢語言的SDK包
- [6.2]-SDK包(JAVA版)
- [6.3]-SDK包(PHP版)
- [6.4]-SDK包(Objective-C版)
- [6.5]-SDK包(javascript版)
- [6.6]-SDK包(Ruby版)
- [8.1]-PhalApi視頻教程
- 附錄1:接口文檔參考模板