在参与了几个大型PHP项目,写了很多PHP代码以后,我发现很多工具可以提高代码质量,简化发布,使得做为PHP开发人员的生涯变得轻松许多。许多这样的工具可能已经为大家所用。但是,由于一些人甚至没有注意到这些工具的存在,我会从此开始,好了,不说废话,下面是我认为所有PHP程序员应该知道的工具列表。
Phing - 一个项目构建系统
Phing 是一个基于Apache ANT 的项目构建系统。它的名字是一个拗口的语句首字母缩写 - PHing Is Not GNU make 。Phing可以做传统构建系统比如 GNU make 能做的任何事情,同时没有陡峭的学习曲线。
在phing(以及其他的构建系统)背后的思想是评估一系列相关性,然后执行一系列PHP类去正确的安装和配置一个应用系统。构建过程用一个简单的XML配置文件控制。Out of the box, phing内置可以执行一系列替换(比如:在你的开发版本和产品版本间改变include路径)、执行SQL语句、移动和复制文件、运行外部脚本等等。通过扩展包含在发行包中的“task”类,你也可以创造自己的定制任务。
对任何需要在超过一台服务器上部署大型PHP应用的人来世,Phing是一个无价之宝。同时我发现它对简单的PHP脚本也是很有用的。
Xdebug - 调试和分析工具
Xdebug 是帮助你调试和分析脚本的一个PHP扩展。Xdebug中最有用的特性是在激活后显示的新注意信息、警告信息和错误信息。如果一个脚本不能正常运行, Xdebug将会输出关于错误信息的完整堆跟踪,会附带函数名称、参数值、源文件和行号。这对于厌倦了PHP默认只带的可怜的错误报告功能的程序员是一个值得欢迎的特性。
这个扩展有很多更先进的功能,允许开发人员进行代码覆盖率分析,收集分析信息以及交互式地调试脚本。profiling functionality 功能尤其有用。分析器使用一个普通的输出文件格式,允许你使用象 KCacheGrind 这样的工具快速发现你代码中的瓶颈。对任何严肃的开发人员而言,一个好的分析器是基本工具,它使得你妥善优化你的代码,同时避免过早优化带来的危害。
PHPUnit - 单元测试框架
PHPUnit 是一个轻量级的PHP测试框架。它是在PHP5下面的 JUnit 3.8.1 完整移植,是xUnit 测试框架家族的一员(它们基于软件模式 先锋 Kent Beck 的设计)。
单元测试是几个现代敏捷开发方法的基础,使得PHPUnit成为许多大型PHP项目的关键工具。这个工具也可以被前面讨论的Xdebug扩展用来生成代码覆盖率报告 ,并且可以与phing集成来自动测试。
Propel - ORM(对象关系映射)框架
Propel是一个PHP5下面的对象关系映射(ORM)框架,它起源于Apache Torque项目。它提供了一个复杂但是易用的数据库抽象层,使得你可以在PHP中象使用普通类和对象一样得使用数据库实体。Propel允许你使用一种简单的XML格式定义你的数据库,这个XML文件被用来映射数据库,生成应用中使用的静态类。
Propel被内嵌于流行的Symfony PHP框架 (还有其他的),它使得代码更灵活、模块化以及可移植。这个项目有The project has 出色的文档,以及很棒的支持社区。
phpMyAdmin / phpPgAdmin - 基于web的数据库管理系统
历史有点长可是还是很有用,phpMyAdmin是对于任何数据库(对应PostgreSQL和SQLite是phpPgAdmin 和 phpSQLiteAdmin)最有用的管理工具之一。它有助于做每一件事情 - 从为了调试应用去创建和删除数据库到做备份。安装它通常是我在一个LAMP服务器上面安装完 Apache, PHP和MySQL后做的第一件事情。如果你使用MySQL,不知何故还没听说过它,那么现在就安装 。
其他工具
还有很多优秀的工具用以符合各种需要,帮助PHP开发者创建一个丰富的开发环境 — 我希望我可以说到所有这些工具。我发现对自己有用的工具有PHP Beautifier, Spyc, Creole和Smarty。我确信还有很多我忘记或者没听说过的有用工具。因此,如果你知道某个我遗漏的PHP开发伟大工具,请留言让我(以及所有人)知道!
Phing是一个基于Apache Ant的项目代码构建系统.你可以用她做传统的构建系统能做的任何事情,比如GNU make,并且Phing使用简单的XML构建文件和可扩展的”任务”使之成为易于使用和极具可扩展性的框架.
Phing源自Binarycloud的一个子项目.Binaryclound是一个高度工程化的框架,为了在企业环境中使用而设计.Binarycloud广泛使用XML来存储关于项目的元数据(配置,节点,窗口小部件,站点结构,等等).
因为Binarycloud是为PHP构建的,在每一个页面请求上执行XML处理和转换是不切实际的.Phing用于”编译”XML元数据为可被PHP引擎处理的数组形式.
当然,XML”编译”只是Binarycloud使用Phing构建系统的许多方法中的一个.Phing构建系统使你能够:
1. 从单一的源代码树构建多语言页面.
2. 在单个XML文件中聚集元数据(Metadata)并且用多个不同的XSLT生成几个文件.(译者注:在XSLT2.0规范中,消除了1.0规范只能有一个输入一个输出的限制,现在我们可以做到单一输入来生成多个输出文件)
因此,Phing开始开发了,Phing是一个借助于Ant的思想用PHP写成的构建系统.第一版同时设计和开发,因此不是十分稳定.系统很快暴露出了它的限制并且需要一个更好的Phing系统.因此衍生了Phing2的雏形.
Phing当前的开发集中于Phing2,它涉及许多功能增强,bug修正,并且最值得注意的转变是用PHP5的抽象类,接口,和try/catch/throw异常处理来重写了原来Phing的基础代码.
Phing怎样工作
Phing使用包含一组项目构建描述的XML构建文件.构建文件由一些运行实际的命令的目标组成(比如复制文件,删除目录,执行数据库查询,等等).因此使用Phing,第一步要编写构建文件,然后运行Phing,在构建文件中定义的要执行的目标.
phing -f mybuildfile.xml mytarget
键入phing –h可以查看命令参数的描述
默认情况下,Phing将会查找一个叫build.xml的文件.(除非构建文件的名称不是build.xml,否则不必为Phing指定构建文件的名称) 并且,如果没有指定任何目标那么Phing将执行在<project>标签中设置的默认目标(default属性所指定的).
设置Phing
要使用Phing你必须安装PHP5.0.0b1和以上的版本并且加上 –with-libxml2,和—with-xsl选项.
自2.0.0b1版之后,你可以可以下载PEAR可安装包和完全的Phing分发包.如果你希望修改Phing,我们建议你下载完整的Phing发行包, 这样你可以创建你自己的PEAR包.如果你知识简单地使用Phing作为项目的需要或构建其他的包,下载并且安装PEAR包.
PEAR 安装
安装Phing最简易的方法是使用pear安装工具.
C:/> pear install http://phing.info/pear/phing-current.tgz
PEAR安装工具将检查包的依赖性,并把Phing的执行脚本放到PHP的安装目录下面(pear命令脚本同时也在这个目录下面)
下载完整的发行包及非pear安装方法见
http://phing.info/docs/guidehttps://files.jxasp.com/image/2.2.0/chapters/Setup.html
调用Phing
命令行
phing [targetname]
XML和Phing
构建文件有如下基本结构:
1. 文档序言(document prolog)
2. 根元素<project>
3. 几个类型元素(<property>,<fileset>,<patternset>)
4. 包含一个或几个内建的或用户自定义的任务元素(例如:<javac>,<tar>)
编写一个简单的构建文件<?xml version="1.0" encoding="utf-8" ?>
<project name="FooBar" default="dist" basedir=".">
<!-- 创建文件夹 -->
<target name="prepare">
<echo msg="Preparing build..." />
<mkdir dir="./pub" />
</target>
<!-- 复制文件 -->
<target name="build" depends="prepare">
<echo>Building...</echo>
<copy todir="./pub/" >
<fileset dir="./src/">
</fileset>
</copy>
</target>
<!-- 打包 -->
<target name="dist" depends="build" if="">
<echo message="Creating archive..." />
<tar destfile="./phing.tar" compression="tar">
<fileset dir="./pub">
</fileset>
</tar>
</target>
<!-- 删除,清理 -->
<target name="clean">
<echo msg="Cleaning up..."/>
<delete dir="./pub" includeemptydirs="true" verbose="true" failοnerrοr="true" />
</target>
</project>
复制代码project 元素
文件序言之后紧跟着的第一个元素的是<project>,此元素是一个包含其他元素的容器,并且有以下属性:
<project>的属性:
name 项目的名称
basedir 项目的根目录,如果未指定将使用当前目录
default 在调用构建文件的时候,如果没有指定要执行的目标,
将执行在此定义的项目默认目标
description 项目的描述
target 元素
一个目标可依赖其他的目标.你可能有一个在构建树中安装文件的目标,例如,创建一个tar.tgz分发包的目标.
Target元素的属性:
属性 含义 要求
name 目标的名称 Yes
depends 此目标依赖的逗号分隔列表 No
if 为了使此目标得以执行而必须设置的属性的名称 No
unless 为了使此目标得以执行而禁止设置的属性的名称
property 元素
属性是在构建文件使用的基本的变量,可以在构建文件中通过 PropertyTask任务设置或在Phing外部通过命令行传递.通过命令行传递的属性总是会覆盖在构建文件中设置的属性,也就是说命令行是最后传递给Phing的,自然会覆盖先前在构建文件中设置的属性.
属性有一个名称和一个唯一的值.属性可以作为任务属性的值.这是通过在”${”和”}”包含属性名称来设置的.例如:
<property name="sourcedir" value="/src/data/"/>
<echo msg="${sourcedir}"/>
这里在运行时${sourcedir}将被展开为/src/java
或者在命名行中:
phing –Dsourcedir=”/backup/data/” –f mybuild.xml
内建属性
Phing给你提供了访问系统属性的能力,好像它已经通过<property>定义过的一样.例如,${php.version}展开为php的版本.
http://phing.info/docs/guidehttps://files.jxasp.com/image/2.2.0/chapters/appendixes/AppendixA- 有完整的内建属性的列表
一个复杂的例子<?xml version="1.0" ?>
<project name="testsite" basedir="." default="main">
<property file="./mybuild.properties"/>
<property name="builddir" value="${projectdir}/pub" override="true" />
<property name="srcdir" value="${projectdir}/src" override="true" />
<!-- Fileset for all files -->
<fileset dir="${srcdir}" id="allfiles">
<include name="*" />
<exclude name="*.txt" />
</fileset>
<!-- Main Target -->
<target name="main" description="main target">
<copy todir="${builddir}">
<fileset refid="allfiles" />
</copy>
</target>
<!-- Rebuild -->
<target name="rebuild" description="rebuilds this package">
<delete file="${builddir}/*.*" />
<phingcall target="main"/>
</target>
</project>
复制代码注意:
第一个propery元素仅包含file属性,这里file属性的值是一个相对的或绝对的指向属性文件的路径(属性文件:后缀名为 .properties,其中包含的键值对(key/value)的集合。
构建文件中的<fileset>标签.它定义一个文件集合,例如一组多个文件组成的集合.你还可以用<fileset>元素的子元素<include>和<exclude>包含和排除<fileset>文件模式指定的文件集合
另外<fileset>标签有id属性,通过id属性可以在其他地方引用.就像直接包含一样,通过引用实现了代码的复用
在第二个目标中使用了<phingcall>标签来调用了PhingTask任务
属性文件格式
属性文件定义属性.属性以键/值对的形式保存,并且只包含纯文本,属性文件的后缀名称一般是.properties,构建文件的默认属性文件是build.properties
# Property files contain key/value pairs
projectdir=/data1/
projectname = myproject
一、安装xdebug模块
1、去www.xdebug.org下载相应版本php的模块文件,保存下载后的文件到php的ext目录,可以自己修改文件的名称,如保存成:php_xdebug.dll
2、修改php.ini,增加如下信息
[Xdebug]
zend_extension_ts="c:/webserver/php5/ext/php_xdebug.dll"
xdebug.auto_trace=on
xdebug.collect_params=on
xdebug.collect_return=on
xdebug.trace_output_dir="c:/webserver/php5/debuginfo"
xdebug.profiler_enable=on
xdebug.profiler_output_dir="c:/webserver/php5/debuginfo"
参数解释:
zend_extension_ts="c:/webserver/php5/ext/php_xdebug.dll"
;加载xdebug模块。这里不能用extension=php_xdebug.dll的方式加载,必须要以zend的方式加载,否则安装上后,phpinfo是显示不出xdebug这个项的。
xdebug.auto_trace=on;
;自动打开“监测函数调用过程”的功模。该功能可以在你指定的目录中将函数调用的监测信息以文件的形式输出。此配置项的默认值为off。
xdebug.collect_params=on;
;打开收集“函数参数”的功能。将函数调用的参数值列入函数过程调用的监测信息中。此配置项的默认值为off。
xdebug.collect_return=on
;打开收集“函数返回值”的功能。将函数的返回值列入函数过程调用的监测信息中。此配置项的默认值为off。
xdebug.trace_output_dir=”c:/Temp/xdebug”
;设定函数调用监测信息的输出文件的路径。
xdebug.profiler_enable=on
;打开效能监测器。
xdebug.profiler_output_dir=”c:/Temp/xdebug”;
;设定效能监测信息输出文件的路径。
还有一些更为具体的参数设定,详见:http://www.xdebug.org/docs-settings.php
3、重启apache
这样,在本地运行php的时候,会在所设定的目录里产生一些调试信息的文件:
* 函数调用过程监测信息文件的文件名格式:trace.××××××.xt。这个文件可以直接查看,里面包含了函数运行的时间,函数调用的参数值,返回值,所在的文件和位置等信息。内容格式还是相对直观的。
* 效能监测文件的文件名格式:cachegrind.out.××××××××。
这个文件也可以直接查看,不过信息格式不易被人类所理解,
所以我们需要接下来的一个软件。
二、安装wincachegrind
由于效能监测文件:cachegrind.out.××××××××文件的内容不易被人类所理解,所以我们需要一个工具来读取它。windows下就有一款这样的软件:wincachegrind。
1、到http://sourceforge.net/projects/wincachegrind/下载安装wincachegrind
2、安装运行后,点击Tools->options,设定你的working folder(php.ini里xdebug.profiler_output_dir的值)
这样就可以比较直观的查看效能监测文件的信息了。
什么是PHPUnit
PHPUnit是一个轻量级的PHP测试框架。它是在PHP5下面对JUnit3系列版本的完整移植,是xUnit测试框架家族的一员(它们都基于模式先锋Kent Beck的设计)
单元测试是几个现代敏捷开发方法的基础,使得PHPUnit成为许多大型PHP项目的关键工具。这个工具也可以被Xdebug扩展用来生成代码覆盖率报告 ,并且可以与phing集成来自动测试,最合它还可以和Selenium整合来完成大型的自动化集成测试。
如何部署PHPUnit
方法一 使用Pear
运行 pear channel-discover pear.phpunit.de;
pear install phpunit/PHPUnit
方法二 手动安装
1 从http://pear.phpunit.de/get/下载软件包并解压 2 把解压后的目录加入php.ini中的include_path
3 将脚本pear-phpunit改名为phpunit
4 将phpunit脚本中的@php_bin@替换成php可执行脚本的路径
5 为phpunit脚本增加可执行权限并加入$PATH
6 将PHPUnit/Runner/Version.php中的@package_version@替换成3位
PHPUnit版本号
下面的例子用来测试sizeof函数工作的正确性<?php
require_once 'PHPUnit/Framework.php';
class ArrayTest extends PHPUnit_Framework_TestCase {
public function testNewArrayIsEmpty() {
/*Create the Array fixture*/
$fixture = array();
/* Assert that the size of the Array * fixture is 0*/
$this->assertEquals(0, sizeof($fixture));
}
public function testArrayContainsAnElement() {
/* Create the Array fixture*/
$fixture = array();
/*Add an element to the Array * fixture*/
$fixture[] = 'Element';
/*Assert that the size of the * Array fixture is 1*/
$this->assertEquals(1, sizeof($fixture));
}
}
?>
复制代码要点:
1 编写的测试用例是一个php脚本
2 require_once 'PHPUnit/Framework.php'是必须的,另外,你需要在测
试用例脚本中包含你需要测试的代码
3 测试用例的主体必须写在类中,类名必须和文件名保持一致,还必须是
PHPUnit_Framework_TestCase的子类
4 每一个测试用例都是一个public的成员函数,必须以test开头
5 程序的输出使用assert*系列函数来进行验证
该用例需要在Shell下键入phpunit ArrayTest.php来运行。结果如下示:
[username@machine xx]$ phpunit ArrayTest.php
PHPUnit 3.1.3 by Sebastian Bergmann.
..
Time: 0 seconds
OK (2 tests)
结果中最重要的用红色标出的结果,点号 代表一个用例通过(即assert系列函数都通过)。如果将上面的测试用例testArrayContainsAnElement用例assertEquals函数中的1改为0,则运行结果为:
PHPUnit 3.1.3 by Sebastian Bergmann.
.F
Time: 0 seconds
There was 1 failure:
1) testArrayContainsAnElement(ArrayTest)
Failed asserting that <integer:1> matches expected value <integer:0>.
/home/wiki/apache/htdocs1.5.0/ArrayTest.php:29
FAILURES!
Tests: 2, Failures: 1.
可以很明显看出结果由原来的 .. 变为 .F ,F表示第二个测试用例未通
过,并且具体与哪一条验证不符都在后有详细说明。
除了F外,一个测试用例还有I(未完成),S (跳过),E (错误)三种状态,详见http://www.phpunit.de/pocket_guide/3.2/en/textui.html
PHPUnit如何保证测试的准确性
测试用例之间必须保证他们之间是不互相影响的,即这些测试用例无论以任何顺序执行,他们的结果都应该一样。PHPUnit提供了两个可供重写的函数来满足此要求。
成员函数setUp在每一个测试用例开始之前执行,用来创建用于测试的环境。tearDown则在每个测试用例结束时调用,用于还原测试用例对环境带来的影响。
上示的测试用例中$fixture = array()就可以放在setUp中完成
PHPUnit还支持哪些高级功能
结合XDebug生成代码覆盖率报告
如何测试你的测试用例设计,答案是代码覆盖率。代码覆盖率即当你的
一套测试用例执行完毕时,有多少比例的代码分支被覆盖到。
PHPUnit的代码覆盖率报告需要另一个优秀的Extension——XDebug
(http://www.xdebug.org)支持。当执行完测试用例后,得出的结果类似
下图:
图中绿色高亮的行代表测试用例有覆盖,红色相反。而左边的数字代表
此行代码被执行过几次。
代码覆盖率报告对指导我们编写测试用例将有极大的帮助,详见
http://www.phpunit.de/pocket_guide/3.2/en/code-coverage-analysis.html 结合Phing完成自动化部署
Phing(http://www.phing.info/trac/)是一个基于Apache Ant的加
快PHP项目部署的工具。当在一个新环境部署PHP项目时,代码的正确性
不能得到保证,这就需要在部署之前做针对性的测试,只有测试通过整个
过程才能继续。Phing使用XML文件定义部署过程,其中测试部分动作可
以使用PHPUnit完成,下面是一个
示例性的部署文件:<?xml version="1.0"?>
<project name="BankAccount" basedir="." default="test">
<target name="test">
<phpunit haltonfailure="true" printsummary="true">
<batchtest>
<fileset dir=".">
<include name="*Test.php"/>
</fileset>
</batchtest>
</phpunit>
</target>
</project>
复制代码其中需要部署的模块名为BankAccount,动作为test,使用测试工具
为phpunit,所有的测试用例文件为*Test.php。
如果一切正常,使用Phing部署后可以得到类似下面的结果:
phing
Buildfile: /home/sb/build.xml
BankAccount > test:
[phpunit] Tests run: 4, Failures: 0, Errors: 0, Time elapsed: 0.00067 sec
BUILD FINISHED
Total time: 0.0960 seconds
当然,如果build失败则你应该根据测试失败的提示来debug你的程序。
结合Selenium做大型自动化集成测试
前述的测试都是单元测试,测试的输入都是基于变量和类,而非基于用
户行为,例如浏览器的点击。在软件工程定义中,集成测试是在单元测试之
后的更高级别的测试,用PHPUnit作基于浏览器的自动化集成测试需要
Selenium的支持。Selenium(http://www.openqa.org/selenium/)是一
个完整基于浏览器做集成测试的框架。它通过在页面中嵌入js来模拟用户
行为。Selenium的经典架构如图:
可以看出,我们需要一个额外的Selenium Server来负责转发HTTP请求和执行用户动作。除此之外,剩下的就是在利用Selenium RC For PHP编写测试用例。PHPUnit集成了Selenium的API接口,通过直接调用Selenium的API即可完成测试动作和结果验证,一个典型的测试如下:<?php
set_include_path(get_include_path().PATH_SEPARATOR.'./PEAR/');
require_once 'Testing/Selenium.php';
require_once 'PHPUnit/Framework/TestCase.php';
class GoogleTest extends PHPUnit_Framework_TestCase {
private $selenium;
public function setUp() {
$this->selenium = new Testing_Selenium("*firefox", "http://www.google.com");
$this->selenium->start();
}
public function tearDown() {
$this->selenium->stop();
}
public function testGoogle() {
$this->selenium->open("/");
$this->selenium->type("q", "hello world");
$this->selenium->click("btnG");
$this->selenium->waitForPageToLoad(10000);
$this->assertRegExp("/Google Search/", $this->selenium->getTitle());
}
}
?>
复制代码可以看出,测试用例书写方法并没有很大的区别。在setUp中用指定浏览器打开www.google.com,然后在表单q中提交查询hello world并点击,最后等待页面load完毕后校验新的url中是否包含/Google Search/。
当然,Selenium还提供了更为丰富的API,例如验证某个表单的值等等。
PHPUnit的发展如何
PHPUnit已经受到了Zend官方和社区的大力支持。最新版本3.1.8已于9月初发布,不仅修正bug,还对mock object,日志和测试用例框架生成做了更进一步的支持。同时,它也将更容易和PHP的其他优秀工具进行整合来完成更为复杂的任务,详见
http://www.phpunit.de/wiki/ChangeLog#PHPUnit3.1.802-Sep-2007
PHPUnit能给我们带来什么
单元测试在现代软件开发过程中占据着愈发重要的地位,尤其是敏捷开发。所以,高质量的单元测试是保证项目质量的基础。
单元测试也为以后的开发提供支缓。就算是开发后期,我们也可以轻松的增加功能或更改程序结构,而不用担心这个过程中会破坏重要的东西。而且它为代码的重构提供了保障。这样,我们就可以更自由的对程序进行改进。
同时,编写单元测试将使我们从调用者观察、思考。特别是先写测试(test-first),迫使我们把程序设计成易于调用和可测试的,即迫使我们解除软件中的耦合。
然后,单元测试是一种无价的文档,它是展示函数或类如何使用的最佳文档。这份文档是可编译、可运行的,并且它保持最新,永远与代码同步。
最后,自动化的单元测试避免了代码出现回归,编写完成之后,可以随时随地的快速运行测试。即使在利用
如何更高效的利用PHPUnit进行单元测试
在用PHPUnit做单元测试时,我们可以借鉴先写测试和测试驱动编码的思想,将代码编写的更加模块化,减少耦合,并且以完成实际功能为目标。这样的代码将会有更高的可测性,会大大提高我们的测试效率
RD如何利用PHPUnit
1 利用PHPUnit进行Debug,完成单元自测
2 向scm提交测试用例代码并保持同步
3 获得测试用例的代码覆盖率并规定一个阈值(75%?),低于阈值的测试用例不能通过单元测试
4 通过QA的反馈改进RD的测试用例
5 结合Phing在部署新环境时利用PHPUnit验证代码在新环境下的正确性
6 结合Selenium做大规模的自动化集成测试,提高提测代码质量
另外,按照软件工程定义的测试用例设计经验,测试代码应该是程序代码量的1.2-1.5倍。虽然初期编写有一定的代价,但和带来的效用还是没有可比性的
QA如何利用PHPUnit
1 通过阅读RD的测试用例设计来更进一步了解项目设计和功能
2 通过QA的角度向RD反馈测试用例设计中的缺陷
4 自行编写测试用例进行测试
1.3概述
目录结构
注意事项:
1:使用相应数据库的PDO库,如:php_pdo_mysql.dll
2: 使用相应的DSN: 如mysql的DSN: mysql:host=localhost;dbname=bookstore
3: 在保存对象到数据库后,不能直接得到数据对象key
生成 PHP 类文件的工作主要在 peopel-generator 目录下进行。在正确安装完成之后,peopel-generator 的目录结构应该如下:
propel-generator
|– classes
| +– propel
| |– engine
| | |– database
| | | |– model
| | | +– transform
| | +– sql
| +– phing
|– dtd
|– projects
| +– bookstore
|– templates
+– test
|– classes
| +– propel
+– etc
classes:Propel 所用到的所有类文件;
dtd:对应于数据库描述文件(schema.xml)的 DTD 文件--DTD 是 XML 的基础,感兴趣的可以到 W3 School 学习;
projects:此目录用于存放各个项目的文件,每个项目对应于一个子目录,Propel 自带有 bookstore 项目作为样例。Propel 会从这些项目的目录中读取 schema 和配置文件,然后将生成的文件放入输出目录 build(如:projects/bookstore/build)中;
templates:存放着用于创建 PHP 文件的模板及 SQL 文件的数据模型;
test:PHPUnit2 测试用例。
创建数据库描述文件
首先需要在 projects 目录下创建项目目录(假设项目名为“infor96”),然后在此目录下创建数据库描述文件 schema.xml,内容如下(这里只对一个数据表进行试验测试):
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<database name="infor96" defaultIdMethod="native">
<table name="user" description="User Table">
<column
name="uid"
required="true"
primaryKey="true"
type="VARCHAR"
size="16"
description="User Id"/>
<column
name="pwd"
required="true"
type="VARCHAR"
size="16"
description="User's Password"/>
<column
name="name"
required="true"
type="VARCHAR"
size="10"
description="User's True Name"/>
</table>
</database>
复制代码可以清楚的看到这个项目的数据库名为“infor96”,有一个表,表名为“user”,这个表有 3 个字段:uid、pwd、name,如果有多个表的话,在 <database> …. </database> 之间加入多个 <table> 即可。其他标签属性比较简单,type 是字段类型,size 是大小,primaryKey 是“主键”。
设置创建属性
在项目目录(projects/infor96/)下创建文件 build.properties,最为简单的内容是:
# 项目名
propel.project = infor96
# 数据库类型
propel.database = mysql
设置运行时属性
在项目目录下创建 runtime-conf.xml 文件,内容为:
<?xml version="1.0" encoding="ISO-8859-1"?>
<config>
<log>
<ident>propel-infor96</ident>
<level>7</level>
</log>
<propel>
<datasources default="infor96">
<datasource id="infor96">
<adapter>mysql</adapter>
<connection>
<phptype>mysql</phptype>
<hostspec>localhost</hostspec>
<database>infor96</database>
<username>infor96</username>
<password>infor96</password>
</connection>
</datasource>
</datasources>
</propel>
</config>
复制代码可以看出,这个文件其实就是设置运行的时候需要用到的连接数据库参数,数据库名、用户、密码等。当生成 PHP 类文件时,这个文件会被自动转换成 projects/infor96/build/conf/runtime-conf.php,此文件路径在 Propel 初始化方法 Propel :: init() 中当参数传递。
生成 PHP 类文件
一切准备就绪,开始生成 PHP 类文件的工作了:
$ cd /usr/local/propel-generator
$ cp build.properties-sample build.properties
$ phing -Dproject=infor96
自动生成工作由 phing 来完成,如果没有安装 phing 的话,可以通过运行下边命令来安装:
pear install http://phing.info/pear/phing-current.tgz
在 phing 生成 PHP 类文件的过程中,可以看到很多输出:
Buildfile: /usr/local/propel-generator/build.xml
propel-project-builder > projectcheck:
propel-project-builder > configure:
[echo] Loading project-specific props from ./projects/infor96/build.properties
propel-project-builder > main:
[phing] Calling Buildfile '/usr/local/propel-generator/build-propel.xml' with target 'main'
propel > main:
[phingcall] Calling Buildfile '/usr/local/propel-generator/build-propel.xml' with target 'sql'
propel > check-run-only-on-schema-change:
propel > sql-check:
propel > sql:
[echo] +————————————————————————+
[echo] | |
[echo] | Generating SQL for YOUR Propel project! |
[echo] | |
[echo] +————————————————————————+
[phingcall] Calling Buildfile '/usr/local/propel-generator/build-propel.xml' with target 'sql-template'
propel > sql-template:
[propel-sql] Processing: schema.xml
[propel-sql] Target database type: mysql
[propel-sql] Target package:
[propel-sql] Using template path: /usr/local/propel-generator/templates
[propel-sql] Output directory: /usr/local/propel-generator/projects/infor96/build/sql
[propel-sql] Generating SQL tables for database: infor96
[propel-sql] Writing to SQL file: /usr/local/propel-generator/projects/infor96/build/sql/schema.sql
[propel-sql] + user
[phingcall] Calling Buildfile '/usr/local/propel-generator/build-propel.xml' with target 'om'
propel > check-run-only-on-schema-change:
propel > om-check:
propel > om:
[echo] +————————————————————————+
[echo] | |
[echo] | Generating Peer-based Object Model for |
[echo] | YOUR Propel project! |
[echo] | |
[echo] +————————————————————————+
[phingcall] Calling Buildfile '/usr/local/propel-generator/build-propel.xml' with target 'om-template'
propel > om-template:
[propel-om] Target database type: mysql
[propel-om] Target package: infor96
[propel-om] Using template path: /usr/local/propel-generator/templates
[propel-om] Output directory: /usr/local/propel-generator/projects/infor96/build/classes
[propel-om] Processing: schema.xml
[propel-om] Processing Datamodel : schema.xml
[propel-om] - processing database : infor96
[propel-om] + user
[propel-om] -> BaseUserPeer
[propel-om] -> BaseUser
[propel-om] -> UserMapBuilder
[phingcall] Calling Buildfile '/usr/local/propel-generator/build-propel.xml' with target 'convert-props'
propel > convert-props:
[echo] +———————————————————————+
[echo] | |
[echo] | Converting project properties file to an |
[echo] | array dump for run-time performance. |
[echo] | |
[echo] +———————————————————————+
[capsule] Using templatePath: /usr/local/propel-generator/templates
[capsule] Generating to file /usr/local/propel-generator/projects/infor96/build/conf/infor96-conf.php
[capsule] Parsing control template: conf/Control.tpl
BUILD FINISHED
Total time: 3.0861 seconds
生成的文件均位于 projects/infor96/build/ 目录下,PHP 类文件在 projects/infor96/build/classes/infor96/ 目录下。这些类文件中包含了很多属性和方法,可以看到,Propel 对每一个字段都提供了 get/set 方法,如:getUid() 和 setUid()。现在可以写个 PHP 调用这些类,然后通过这些类的方法去操作数据库了。这就是当初 Hibernate 的设想,像操作对象那样去操作数据库。在你写 PHP 代码中,不会出现 SQL 语句,因为这些 SQL 语句已经完全被封装在类文件中了,而这些类文件是由 Propel 自动生成的。下面是一个使用示例:
<?php
require_once 'propel/Propel.php';
// 首先需要将生成的 projects/infor96/build/ 整个项目目录移到需要用到的地方,当然可以不放到 WEB 发布目录下
// 下边传递的这个配置文件就是由运行时配置文件 runtime-conf.xml 对应生成的
Propel::init('/var/www/infor96/conf/runtime-conf.php');
include_once 'infor96/User.php';
//=======================================================
// INSERT 示例
//=======================================================
$user = new User();
$user->setUid("Nio");
$user->setPwd("test");
$user->setName("Krazy Nio");
// 将设置的数据信息保存到数据库中,实际后台是执行了 INSERT 操作
$user->save();
//=======================================================
// SELECT 示例
//=======================================================
$c = new Criteria();
$c->add(UserPeer::UID, "io%", Criteria::LIKE);
$c->setLimit(10); // 设置选择的记录数,相当于 MySQL 中的 LIMIT
$users = UserPeer::doSelect($c);
if ($users) {
echo '<p>找到了如何条件的用户!</p>';
foreach($users as $user)
echo '<br/>帐号:' . $user->getUid() . ",姓名:" . $user->getName();
} //end if
?>
复制代码嗯,今天的实践总结就写到这里吧,至少已经明白了原理及实现过程。
phpMyAdmin就不再写了,相信所有了解PHP的应该知道。