iT邦幫忙

DAY 17
5

逐步提昇PHP技術能力系列 第 17

逐步提昇PHP技術能力 - 開發工具 : 使用phing來讓專案建置過程自動化

phing是(Phing is Not GNU make)的簡寫...(那make怎麼沒放進去XD),這是一個模仿apache ant的專案建置工具。只要寫好專案建置腳本(build.xml),就可以透過命令列的方式來建構專案。

不過...php是腳本語言,並不需要使用編譯器,那phing還能做什麼?其實一個完整的專案過程,還有很多的工作,這些都可以整合到腳本中,讓這些過程都可以透過命令列驅動並執行。同時每個建置階段,都可以設定相依性。這樣只要指示phing要執行哪一個步驟,phing也會把這個步驟前需要執行的工作也都一併完成。
參考:
http://www.phing.info/

之前試用過幾個工具,可以用來做單元測試、整合測試、檢測程式碼品質等等,其實這些工作都可以放進專案流程。另外在開發中,通常還會需要做幾項工作:

  1. Javascript、CSS的測試、程式碼品質檢測、混淆、最小化等等。另外在使用RequireJS時,還可以依照設定的相依性,產出合併並且最佳化的Javascript檔。
  2. 圖檔最佳化,例如在可接受的範圍內,降低圖檔的品質以減少他的size
  3. 如果有使用ionCube這類的原始碼加密工具,那在成品釋出前,還需要進行加密
  4. 版本釋出時,可能需要打包成zip/tgz等壓縮檔,或是可執行的phar檔
  5. 版本釋出時,也可能需要把程式deploy到production機器上

另外,即使不使用額外的伺服器,只要能透過cron定時檢測版本庫,然後驅動建構、測試的流程,而且測試結果都可以轉成html格式透過網頁伺服器來檢視的話,這其實就是一種持續整合了。

好了,不提那麼多,先來安裝phing。

phing也支援用composer的方式來安裝,同樣改一下composer.json:

{
    "require":{
    },
    "require-dev": {
        "phpunit/phpunit":"~3.7.5",
        "phpunit/php-invoker":"~1.1.0",
        "phpunit/phpunit-selenium":"~1.3.2",
        "pdepend/pdepend":"~1.1",
        "phpmd/phpmd":"~1.5",
        "phing/phing":"~2.6.1"
    }
}

然後執行composer update。執行完以後,把phing link到專案目錄,這樣執行會比較方便

> ln -s vendor/phing/phing/bin/phing

然後執行一下./phing看看:

Feng-Hsu-Pingteki-MacBook-Air:2-3a fillano$ ./phing 
Buildfile: build.xml does not exist!

嗯,所以在執行前,需要寫一個建構的腳本,預設的名字是build.xml(這跟ant一樣)...那就先來看看怎麼寫這個腳本。

跟ant一樣,build.xml的組織結構是:

  1. project,也就是root node
  2. 每個project中可以有多個target
  3. 每個target中,可以有多個task
  4. 每個target之間,可以設定依賴關係
  5. 可以用外部的properties檔來設定專案的屬性
  6. 共用的types,可以被許多task使用

先來寫一個hello world好了:

<?xml version="1.0" encoding="UTF-8"?>
<project name="2-3a" default="main">
	<target name="main">
		<echo msg="Hello Phing." />
	</target>
</project>

執行一下看看:

Feng-Hsu-Pingteki-MacBook-Air:2-3a fillano$ ./phing 
Buildfile: /Users/fillano/builds/ironman6/2-3a/build.xml

2-3a > main:

     [echo] Hello Phing.

BUILD FINISHED

Total time: 0.2400 seconds

其中:

  1. project element的tag name就是project
  2. target element的tag name就是target
  3. task有很多種,名字都不一樣,可以使用不同的內建type作為子元素。上例中的echo就是一個task,他的作用就是顯示訊息。

在target加上depends屬性,就可以指定依賴關係,例如我想要加一個target叫做prepare,會在執行main之前執行:

<?xml version="1.0" encoding="UTF-8"?>
<project name="2-3a" default="main">
	<target name="main" depends="prepare">
		<echo msg="Hello Phing." />
	</target>
	<target name="prepare">
		<echo msg="I'm prepared for main." />
	</target>
</project>

執行結果:

Feng-Hsu-Pingteki-MacBook-Air:2-3a fillano$ ./phing 
Buildfile: /Users/fillano/builds/ironman6/2-3a/build.xml

2-3a > prepare:

     [echo] I'm prepared for main.

2-3a > main:

     [echo] Hello Phing.

BUILD FINISHED

Total time: 0.2418 seconds

再來用比較真實的專案流程做例子。假設專案流程是這樣:

  1. 執行單元測試
  2. 將檔案複製到build目錄
  3. 將build目錄中的檔案,壓縮成2-3a.zip,放到dist目錄

來把幾個東西組合一下:

<?xml version="1.0" encoding="UTF-8"?>
<project name="2-3a" default="dist">
	<target name="dist" depends="build">
		<zip destfile="dist/2-3a.zip">
			<fileset dir="builds">
				<include name="**/*.php" />
			</fileset>
		</zip>
	</target>
	<target name="build" depends="test">
		<copy todir="builds">
			<fileset dir="src">
				<include name="**.php" />
			</fileset>
		</copy>
	</target>
	<target name="test" depends="clear">
		<phpunit>
			<formatter todir="reports" type="xml" outfile="phpunit_report.xml" />
			<batchtest>
				<fileset dir="tests">
					<include name="TestScormTimeUtils.php" />
				</fileset>
			</batchtest>
		</phpunit>
	</target>
	<target name="clear">
		<delete>
			<fileset dir="builds">
				<include name="**.*" />
			</fileset>
		</delete>
		<delete>
			<fileset dir="reports">
				<include name="**.*" />
			</fileset>
		</delete>
		<delete>
			<fileset dir="dist">
				<include name="**.*" />
			</fileset>
		</delete>
	</target>
</project>

預設的目標是dist,他會把builds目錄中的php檔壓縮成2-3a.zip,放在dist目錄。在執行dist前,會先執行build,他會把src目錄中的php檔複製到builds目錄。在執行build之前,會先執行test。test會執行phpunit,使用tests/TestScormTimeUtils.php作為測試目標,然後把測試報告以xml格式存放在reports/phpunit_report.xml。在執行test之前,會先執行clear,把reports、builds、dist目錄清空。

執行結果:

Feng-Hsu-Pingteki-MacBook-Air:2-3a fillano$ ./phing
Buildfile: /Users/fillano/builds/ironman6/2-3a/build.xml

2-3a > clear:

   [delete] Deleting 1 files from builds
   [delete] Deleting 1 files from reports
   [delete] Deleting 1 files from dist

2-3a > test:


2-3a > build:

     [copy] Copying 1 file to /Users/fillano/builds/ironman6/2-3a/builds

2-3a > dist:

      [zip] Building zip: /Users/fillano/builds/ironman6/2-3a/dist/2-3a.zip

BUILD FINISHED

Total time: 0.3412 seconds

只要下一個命令,就可以完成複雜建構過程,使用phing真的可以節省很多時間。

執行完畢後,專案目錄檔案就會有這些:

Feng-Hsu-Pingteki-MacBook-Air:2-3a fillano$ tree src tests builds reports dist
src
└── ScormTimeUtils.php
tests
├── TestScormTimeUtils.php
└── TestSelenium.php
builds
└── ScormTimeUtils.php
reports
└── phpunit_report.xml
dist
└── 2-3a.zip

其中builds、reports、dist目錄中的檔案,都是建構過程中產生的。

phing網站上的文件相當完整,而且內建許多的task,也可以自定task, type等。使用時,可以根據建置的需求選擇需要的task,如果不夠用,也可以考慮自己寫。不過他抄ant真的很徹底XD


上一篇
逐步提昇PHP技術能力 - 開發工具 : 使用pdepend / phpmd 等工具檢測程式碼品質
下一篇
逐步提昇PHP技術能力 - 開發工具 : 使用phing來建置土砲CI
系列文
逐步提昇PHP技術能力30

2 則留言

0

我要留言

立即登入留言