php 程序设计代码
讲到
PHP
的全名就蛮有趣的,它是一个巢状的缩写名称,
"PHP: Hypertext Preprocessor"
,打开缩写还是缩写。
PHP
是一种
HTML
内嵌式的语言
(
类似
IIS
上的
ASP)
。而
PHP
独特的语法混合了
C
、
Java
、
Perl
以及
PHP
式的新语法。它可以比
CGI
或者
Perl
更快速的执行动态网页。
PHP
最初是在西元
1994
年
Rasmus Lerdorf
开始计画发展。在
1995
年以
Personal Home Page Tools (PHP Tools)
开始对外发表第一个版本。在这早期的版本中,提供了访客留言本、访客计数器等简单的功能。随后在新的成员加入开发行列之后,在
1995
年中,第二版的
PHP
问市。第二版定名为
PHP/FI(Form Interpreter)
。
PHP/FI
并加入了
mSQL
的支援,自此奠定了
PHP
在动态网页开发上的影响力。在
1996
年底,有一万五千个
Web
站台使用
PHP/FI
;在
1997
年中,使用
PHP/FI
的
Web
站台成长到超过五万个。而在
1997
年中,开始了第三版的开发计划,开发小组加入了
Zeev Suraski
及
Andi Gutmans
,而第三版就定名为
PHP3
。
PHP3
跟
Apache
伺服器紧密结合的特性;加上它不断的更新及加入新的功能;并且它几乎支援所有主流与非主流资料库;再以它能高速的执行效率,使得
PHP
在
1999
年中的使用站台超过了十五万
!!
它的原始码完全公开,在
Open Source
意识抬头的今天,它更是这方面的中流砥柱。不断地有新的函式库加入,以及不停地更新的活力,使得
PHP
无论在
UNIX
或是
Win32
的平台上都可以有更多新的功能。它提供丰富的函式,使得在程式设计方面有着更好的支援。
PHP
的第四代
Zend
核心引擎已经进入测试阶段。整个剧本程式的核心大幅更动,让程式的执行速度,满足更快的要求。在最佳化之后的效率,已较传统
CGI
或者
ASP
等程式有更好的表现。而且还有更强的新功能、更丰富的函式馆。无论您接不接受,
PHP
都将在
Web CGI
的领域上,掀起巅覆性的革命。对于一位专业的
Web Master
而言,它将也是必修课程之一。
PHP
在资料库方面的丰富支援,也是它迅速窜红的原因之一,它支援下列的资料库或是资料表:
·
dbm
filePro
Informix InterBase
mSQL
Microsoft SQL Server
MySQL
Solid
Sybase
ODBC
Oracle 8
Oracle
PostgreSQL
而在 Internet 上它也支援了相当多的通讯协定 (protocol),包括了与电子邮件相关的 IMAP, POP3;网管系统 SNMP;网路新闻 NNTP;帐号共用 NIS;全球资讯网 HTTP 及 Apache 伺服器;目录协定 LDAP 以及其它网路的相关函式。除此之外,用 PHP 写出来的 Web 后端 CGI 程式,可很轻易的移植到不同的作业平台上。例如,先以 Linux 架的网站,在系统负荷过高时,可以快速地将整个系统移到 SUN 工作站上,不用重新编译 CGI 程式。面对快速发展的
程式介面
PHP
ASP
CGI
NSAPI
ISAPI
作业系统
均可
Win32
均可
均可
Win32
Web
伺服器
数种
IIS
均可
Netscape Server
IIS
执行效率
快
快
慢
极快
极快
稳定性
佳
中等
最高
差
差
开发时间
短
短
中等
长
长
修改时间
短
短
中等
长
长
程式语言
PHP
VB
不限
C/C++
C/Delphi
网页结合
佳
佳
差
差
差
学习门槛
低
低
高
极高
高
函式支援
多
少
不定
中等
少
系统安全
佳
极差
最佳
佳
尚可
使用站台
超多
多
多
极少
少
改版速度
快
慢
无
慢
慢
其中的 PHP 可用在数种 Web 伺服器上;传统 CGI 就不限是哪种作业系统或 Web 伺服器平台;NSAPI 一定要在 Netscape 的伺服器 (如 Netscape Enterprise Server 或 FastTrack Server) 上才可以执行,但可支援多种作业系统 (UNIX 或 Win32);ASP 及 ISAPI 只在 IIS 上有完整的功能。在稳定性上,由于 NSAPI 或 ISAPI 是动态连结的方式,因此在执行若出现问题,会使得 Web 伺服器一起瘫痪。而 ASP 在吾人实际应用经验上,隔阵子就会使系统不稳定,需要重开作业系统。PHP 在许多的站台使用上,不但长期使用都没有问题,而且程式的稳定性也不错。当然最稳的还是传统 CGI 程式,因为它是由作业系统负责控制,不会因 CGI 程式的错误导致 Web 伺服器的不稳定。在开发及维护时间上,PHP 及 ASP 都有不错的表现。而 NSAPI 及 ISAPI 则需要长时间的开发过程,在稳定上线后,这二种介面反倒是效率最佳的方法。传统的 CGI 程式则要视开发工具语言而定了,用 Perl 或是 shell script 不需要编译的过程,直接就可以执行,若用 Delphi 或 VC/BCB 甚至用组合语言等都要经过编译才能执行,至于用 VB 来写传统 CGI,唉....。要比较和网页结合的能力,PH和 ASP 是并驾齐驱的,其它的方式就不能内嵌 HTML 语法了。而这也是影响开发时间的因素之一。就系统安全性而言,ASP 是最差的,在没有经过微软的 IIS Service Pack 处理过,使用 ::$DATA 就可以看到 ASP 的原始码,这真是叫人不敢领教。当然,传统 CGI 的程式,由于是由作业系统直接管理,要破解的难度最高,骇客必须由作业系统下手,而不能由 Web 伺服器下手。PHP 在许多商业及非商业使用时,也没有听过有什么安全的问题。在新增功能及改版方面,传统的 CGI 由于不受任何语言限制,没有这方面的问题。PHP 是最有活力的,数天至数周就有一个新版本出现,每次的新版,就代表更多的功能及修正更多的错误。其它的 ASP、NSAPI、ISAPI 就视它的 Web 伺服器改版速度了,ASP 要等到 IIS 5.0 出现时才会有 ASP 3.0,也就是要等到 Windows 2000 正式上市。综言之,在 Web 的后端 CGI 程式,就像鱼与熊掌一般,没有高效能又开发方便的选择。不过相信 PHP 是处于开发容易、效能亦不错的平衡点上。
PHP 与其它 CGI 的比较/
Internet,这是长期规划的最好选择。
在加入其它的模组之后,提供了更多样的支援如下:
英文拼字检查
BC 高精确度计算
西洋历法
PDF 档案格式
Hyperwave 伺服器
图形处理
编码与解码功能
杂凑处理
WDDX 功能
qmail 与 vmailmgr 系统
压缩档案处理
XML 解析
除此之外,一般语言有的数学运算、时间处理、档案系统、字串处理、行程处理等功能,它一样都不缺。再加上它是免费的系统,使得成本与效益比,几乎等于无限大!
环境需求与准备工作
在安装
PHP
做为
WWW
伺服器的一部份时,我们可以考虑用
UNIX
作业系统;或者是
Windows NT/95
等
Win32 API
的平台。当然,大部份的人都会使用
UNIX
来当作
PHP
的执行平台
(
在
Windows NT
的使用者大多数都会选择
IIS + ASP)
,因此,本书的所有内容以及范例程式都是在
UNIX
上为主。实际上,
Linux + Apache + PHP
应是最经济的选择,因为这样的组合几乎是不用钱的,成本与效益比这也是最好的选择。而许多成功站台的经验,更是采用这种组合最好的背书。
Linux 作业系统方面,您可以选择各式的 Linux 套件,包括 Slackware Linux、RedHat、OpenLinux、SuSE....等等,反正这方面的软体在坊间也是很容易而且很便宜就可以买到。对学生而言,也可以去各大 FTP 站下载完整的系统安装。
Apache 伺服器则是目前最多 WWW 网站所采用伺服器。您可以至 http://www.apache.org 下载最新版的程式及相关文件,若您觉得从国外下载要很久的话,也可以用它的 Mirror 站台下载。/PHP 则可以去它的官方网站 http://www.php.net 下载所需要的程式。
虽然目前 WindowsNT 或者 Windows98 等 Win32 的作业平台也能安装 PHP 及 Apache 伺服器,不过这似乎没什么道理,因为 PHP 和 Apache 在 UNIX下可以跑得更快更好。当然,若想使用商业化的作业平台,SUN、IBM、HP、DEC、SGI、NEC 等公司都有提供相关的 UNIX 或者是 WindowsNT 的作业平台。加上高安全性调整过后的 Apache 伺服器:Stronghold 或是其它支援 SSL 的 Apache 版本。这种组合,相信能满足商业化的需求。而 PHP 就扮演着快速方便的 CGI 角色,让客户对站台的服务品质更加满意。快速设定及安装
以下是基本的安装动作,作业环境是
UNIX
系列的作业平台。在安装之前,要先下载
apache_1.3.x.tar.gz
及
php-3.0.x.tar.gz
二个档案。可以将这二个档案放在
/usr/src
中再开始执行以下的动作。而下面每个行号后的动作都是整串字连在一起的,在这可能会分行而造成误会。
1.
gzip -d -c apache_1.3.x.tar.gz | tar xvf -
2.
gzip -d -c php-3.0.x.tar.gz | tar xvf -
3.
cd apache_1.3.x
4.
./configure --prefix=/www
5.
cd ../php-3.0.x
6.
./configure --with-mysql --with-apache=../apache_1.3.x --enable-track-vars
7.
make
8.
make install
9.
cd ../apache_1.3.x
10.
./configure --prefix=/www --activate-module=src/modules/php3/libphp3.a
11.
make
12.
make install
第一、二行利用 gzip 及 tar 加上管道功能,将压缩档解压还原。然后在 Apache 的原始档目录中执行环境设定,--prefix 选项指示 Apache 的安装目录路径。之后进入 PHP3 的原始档目录中,若没有 MySQL 资料库,则可省略 --with-mysql 的选项,重要的是一定要加入 --with-apache 选项,而且 Apache 原始档的路径要正确。设定完 PHP3 之后就编译、安装到 Apache 的原始档目录中。之后在 Apache 原始档目录中再加入 PHP 的模组档。在编译及安装 Apache 之后就初步完成了。之后就是要设定 Apache 才能让 Web Server 顺利运作。
值得注意的是,PHP 要和任何资料库连线,都要在执行这些动作之前先将资料库设好,并确定 Web Server 上可以顺利存取资料库系统。若有需要其它的一些 PHP 外部模组也要先设定好这些模组。
13.
cd ../php-3.0.x
14.
cp php3.ini-dist /usr/local/lib/php3.ini
之后将 php3.ini 放在指定的目录,若有需要,也可以手动修改 php3.ini 档档,以符合使用的要求。
在 Apache 伺服器的设定方面,要在 Apache 的设定档 httpd.conf 或 srm.conf 档案中加入下面的字串。告诉 Apache 伺服器,副档名 php3 是一个特殊的程式档案。当然副档名可以设成别的副档名,还有一些站台将 php 的程式副档名设为 phtml 也是不错的选择,反正这就要看 Webmaster 的规划了。
AddType application/x-httpd-php3 .php3/
在 PHP 4.x 版的方法大致和 PHP 3.0.x 版相同,不同的地方在于 PHP 4.x 的目录名称及编译后的模组放置目录不同。此外,内定的副档名也由 .php3 变成了 .php。当然在安装前还是要先下载 PHP 4.x 的程式才行。
1.
gzip -dc apache_1.3.x.tar.gz | tar xvf -
2.
gzip -dc php-4.0.x.tar.gz | tar xvf -
3.
cd apache_1.3.x
4.
./configure --prefix=/www
5.
cd ../php-4.0.x
6.
./configure --with-mysql --with-apache=../apache_1.3.x --enable-track-vars
7.
make
8.
make install
9.
cd ../apache_1.3.x
10.
./configure --prefix=/www --activate-module=src/modules/php4/libphp4.a
11.
make
12.
make install
13.
cd ../php-4.0.x
14.
cp php.ini-dist /usr/local/lib/php.ini
在 httpd.conf 或 srm.conf 加入
AddType application/x-httpd-php .php
AddType application/x-httpd-php-source .phps
而 PHP 4.x 版中对 Apache 伺服器加入了新的环境变数设定项。
php_value [PHP directive name] [value]
php_flag [PHP directive name] [On|Off]
php_admin_value [PHP directive name] [value]
php_admin_flag [PHP directive name] [On|Off]
在 PHP 3.0.x 版中,有些目录可能会有 .htaccess 的档案,使用 PHP 4.0.x 版的系统,必须将这个档案拿掉,可以使用改名字的方式或者直接删除。
13.
当一切设定好了之后,重新执行 Apache 伺服器。在 Apache 目录下有 bin 或是 sbin 的目录,其中会有 apachectl 的 shell 程式,输入 apachectl restart 就可以重新启动 Apache 伺服器了。赶快试看看 hello, world 程式吧!
PHP 的编译设定细部选项
在细部选项上,除了上述的安装简述外,也可以在编译时加入其它的选项。
阿帕契模组
语法
:
--with-apache=DIR
说明
:
用本选项可以让
PHP
以阿帕契的模组方式使用,
DIR
的字串可以是
/usr/local/apache
或其它安装阿帕契的目录
范例
:
--with-apache=/var/lib/apache
fhttpd
伺服器模组
语法
:
--with-fhttpd=DIR
说明
:
若使用
fttpd
伺服器,可以使用本指令编译
PHP
。用模组的方式配合
fttpd
伺服器,可以有较好的效能。
Adabas D
资料库
语法
:
--with-adabas=DIR
说明
:
资料库系统为
Adabas D
资料库时需要加本选项。关于
Adabas D
资料库的细节,可以参考
http://www.adabas.com
。
范例
:
--with-adabas=/usr/local/adabasd
dBase
资料表
语法
:
--with-dbase
说明
:
只要加本选项,不用其它的参数或函式库,
PHP
就会让系统有存取
dBase
资料表的功能。
filePro
资料库
语法
:
--with-filepro
说明
:
不用指定资料库路径及其它函式库等,可以读取
filePro
资料库
(
唯读
)
。
mSQL
资料库
语法
:
--with-msql=DIR
说明
:
提供存取
mSQL
资料库。更多的细节请参考
mSQL
的网站
http://www.hughes.com.au
。
范例
:
--with-msql=/usr/local/Hughes
MySQL
资料库
语法
:
--with-mysql=DIR
说明
:
提供存取
MySQL
资料库。更多的细节请参考
MySQL
的网站
http://www.tcx.se
。
范例
:
--with-mysql=/usr/local/mysql
iODBC
资料库装置
语法
:
--with-iodbc=DIR
说明
:
提供
ODBC
资料库装置,用来存取后端资料库。更多的细节请参考
iODBC
的网站
http://www.iodbc.org
。
范例
:
--with-iodbc=/usr/local/iodbc
OpenLink ODBC
资料库装置
语法
:
--with-openlink=DIR
说明
:
使用
OpenLink ODBC
资料库装置,用来存取后端资料库。更多的细节请参考
OpenLink ODBC
的网站
http://www.openlinksw.com
。
范例
:
--with-openlink=/usr/local/openlink
Oracle
资料库
语法
:
--with-oracle=DIR
说明
:
使用
Oracle
资料库。
Oracle
的版本要在
7.3
版以上。您也可以在
PHP
程式中使用环境变数
ORACLE_HOME
来指定
Oracle
的路径。更多有关
Oracle
的资讯请参考
Oracle
的网站
http://www.oracle.com
。
范例
:
--with-oracle=/export/app/oracle/product/7.3.2
PostgreSQL
资料库
语法
:
--with-pgsql=DIR
说明
:
使用
PostgreSQL
资料库。更多有关
PostgreSQL
的资讯请参考
PostgreSQL
的网站
http://www.postgreSQL.org
或台湾的
Mirror
站
http://postgresql.ccit.edu.tw
。
范例
:
--with-pgsql=/usr/local/pgsql
Solid
资料库
语法
:
--with-solid=DIR
说明
:
使用
Solid
资料库。更多有关
Solid
的资讯请参考
Solid
的网站
http://www.solidtech.com
。
范例
:
--with-solid=/usr/local/solid
Sybase
资料库
语法
:
--with-sybase=DIR
说明
:
使用
Sybase
资料库。更多有关
Sybase
的资讯请参考
Sybase
的网站
http://www.sybase.com
。
范例
:
--with-sybase=/home/sybase
Sybase-CT
资料库
语法
:
--with-sybase-ct=DIR
说明
:
使用
Sybase-CT
资料库。
范例
:
--with-sybase-ct=/home/sybase
Velocis
资料库
语法
:
--with-velocis=DIR
说明
:
使用
Velocis
资料库。有关
Velocis
资料库的进一步资料请参考
Raima
公司的网站
http://www.raima.com
。
范例
:
--with-velocis=/usr/local/velocis
自订
ODBC
资料库驱动程式
语法
:
--with-custom-odbc=DIR
说明
:
使用自订的
ODBC
函式库。当然,在使用本方式时要指定
CUSTOM_ODBC_LIBS
及
CFLAGS
变数。例如在
QNX
机器上使用
Sybase SQL Anywhere
时可能要设定系统环境变数
CFLAGS=-DODBC_QNX
、
LDFLAGS=-lunix
及
CUSTOM_ODBC_LIBS="-ldblib -lodbc"
,并要在
PHP
设定加入
--with-custom-odbc=/usr/lib/sqlany50
范例
:
--with-custom-odbc=/usr/local/odbc
不使用
ODBC
资料库驱动程式
语法
:
--disable-unified-odbc
说明
:
使用本选项将使所有的
ODBC
资料库驱动程式不作用。本选项不用指定路径,而受本选项影响的选项有
--with-iodbc
、
--with-solid
、
--with-adabas
、
--with-velocis
及
--with-custom-odbc
。
LDAP
目录协定
语法
:
--with-ldap=DIR
说明
:
若要使用目录协定
(Lightweight Directory Access Protocol, LDAP)
则必须要打开本选项。有关
LDAP
的细节,可以参考
RFC
文件的
RFC1777
及
RFC1778
。
范例
:
--with-ldap=/usr/local/ldap.
mcrypt
编码函式库
语法
:
--with-mcrypt=DIR
说明
:
当安装了
mcrypt
函式库后,可在编译
PHP
时加入本选项,让程式可以使用编解码功能。
范例
:
--with-mcrypt=/usr/local/include
Sys V
信号
语法
:
--enable-sysvsem
说明
:
要使用
SysV
的信号
(semaphores)
机制,则要打开本选项。
XML
支援
语法
:
--with-xml
说明
:
打开本选项可以支援
James Clark's
写的
XML
剖析程式库。
维护模式
语法
:
--enable-maintainer-mode
说明
:
本选项一般不会打开,除非是
PHP
开发人员比较有用。
常规表示程式库
语法
:
--with-system-regex
说明
:
若您需要额外的常规表示功能,可以加入本选项。
PHP
设定档
语法
:
--with-config-file-path=DIR
说明
:
用来指定
php3.ini
或
php4.ini
的路径,供
PHP
初始化时使用。
范例
:
--with-config-file-path=/usr/local/lib
PHP
执行路径
语法
:
--with-exec-dir=DIR
说明
:
有时为了系统的安全性考量,会指定
PHP
程式一定要在哪个目录执行。
范例
:
--with-exec-dir=/usr/local/bin
除错模式
语法
:
--enable-debug
说明
:
本选项一般不会使用,除非在开发
PHP
程式时比较有用。它可以显示额外的错误讯息。
安全模式
语法
:
--enable-safe-mode
说明
:
内定值是打开的,可以对系统安全提供比较多的保护。
变数追踪
语法
:
--enable-track-vars
说明
:
让
PHP
能
追踪
HTTP_GET_VARS
、
HTTP_POST_VARS
及
HTTP_COOKIE_VARS
三个变数,一般是打开的。
自动加引入字元
语法
:
--enable-magic-quotes
说明
:
可让程式在执行时自动加入反斜线的引入字元。
开启除错器
语法
:
--enable-debugger
说明
:
打开内建的
PHP
除错器。目前本功能还在实验阶段,尚未成熟。
取消路径
(discard path)
语法
:
--enable-discard-path
说明
:
打开这个选项,使用者就不能透过浏览器读取
.htaccess
等和系统安全相关的档案。
高精确度数学函式
语法
:
--enable-bcmath
说明
:
打开高精确度函式。必须要先安装本函式库,本选项方有效。
强迫
CGI
重导
语法
:
--enable-force-cgi-redirect
范例
:
若使用
CGI VERSION
模式来执行
PHP
的设,打开本选项会增加安全性。例如使用者读
http://my.host/cgi-bin/php/secret/doc.html
遇到比较了解
PHP
系统的骇客级使用者可能会自已输入以下网址
http://my.host/secret/doc.html
来读取相关资讯。若
PHP
和
Apache
编译在一起,让
PHP
变成
Apache
的一部份,则不需要加入本选项。
不使用短的标记
语法
:
--disable-short-tags
说明
:
设定本选项后,
PHP
的程式就不能使用短的
.... ?>
标记,一定要用
的长标记。
引入远端档案
语法
:
--enable-url-includes
说明
:
设定本选项可让
PHP
程式可以引入
(include)
远端的
HTTP
或
FTP
伺服器中的档案。
关闭语法效果
语法
:
--disable-syntax-hl
说明
:
使用本选项会关闭
PHP
语法的彩色效果。
函式库路径
语法
:
CPPFLAGS=-IDIR
及
LDFLAGS=-LDIR
说明
:
若
PHP
在安全或编译所需的函式库在特别的路径,需要加入本选项,
LDFLAGS
表示函式库的路径,
CPPFLAGS
表示标头档的路径。
范例:
LDFLAGS=-L/my/lib/dir CPPFLAGS=-I/my/include/dir ./configure
php.ini
或
php3.ini
是
PHP
在启动时会读取的设定档。该档的存放路径为
/usr/local/lib/
。在
PHP 3.x
版的设定档为
php3.ini
;而在
PHP 4.x
版改为
php.ini
。若
PHP
安装成伺服器的模组,则在
Web
伺服器启动执行时会读取,之后就不再读取,因此更动设定的话要重新启动
Web
伺服器。若使用独立
PHP CGI
方式,则每次都读一次。
要看目前的系统设定,可以用 phpinfo() 看到。以下为选项设定
php_value name value
设定变数名称和值。本选项需
PHP 4.x
版之后方可使用。
php_flag name on|off
设定旗标布林变数选项。本选项需
PHP 4.x
版之后方可使用。
php_admin_value name value
设定
Apache
的设定档变数。原来在
.htaccess
的设定改到这个选项设定。本选项需
PHP 4.x
版之后方可使用。
php_admin_flag name on|off
设定布林变数当旗标。本选项需
PHP 4.x
版之后方可使用。
asp_tags boolean
设定
PHP
程式是否剖析以
ASP Script
语法
<% %>
的标记
(tag)
。本选项在
PHP 3.0.4
之后才可使用。更多的细节可以参考
嵌入方法
的部份。
auto_append_file string
设定本选项可以让指定的档案附加在原
PHP
程式后面自动执行。若
PHP
程式中有用到
exit()
函式,则指定的档案不会执行。参数
string
即为指定自动执行的档案。
auto_prepend_file string
设定本选项可以让指定的档案在原
PHP
程式之前自动执行。参数
string
即为指定自动执行的档案。
cgi_ext string
本选项设定
CGI
程式的副档名。
display_errors boolean
本选项设定是否要将执行的错误讯息显示在使用者的浏览器上。
doc_root string
设定伺服器的文件及
PHP
档案放置的根目录。
engine boolean
本选项需要使用阿帕契的模组方式使用
PHP
。可设定是否要使用
PHP
引擎。在
httpd.conf
中加入
php3_engine on/off
亦可设定某目录或某虚拟站台是否要用
PHP
解译器。
error_log string
本选项用来设定错误记录档。在
UNIX
系统上参数
string
即为档名。
Windows NT
会将记录记在事件检视器的日志之中。
Windows 95/98
则不支援系统记录。
error_reporting integer
本选项用来设定系统记录的等级。参数
integer
即为等级的级数旗标,可以加在一起,内定值为
7
,见下表
级数
说明
1
一般的错误
2
一般的警告
4
剖析错误
8
非关键的警告
open_basedir string
本选项用来设定提供
PHP
存取的最底层目录。从该指定目录之下的档案或目录,
PHP
程式方可存取。使用本选项主要是为了系统安全性的考量。参数
string
即为欲限制的最底层目录节点。值得注意的是在
UNIX
系统中,若该节点之下的档案或目录有符号链结
(symbolic link)
,则可能会让使用本选项的目的打折,因此在目录的设计上考量,也是
Webmaster
的重要任务。内定值是所有档案均可存取。在
PHP 3.0.7
版之后,可以设定多个底层目录。
gpc_order string
设定
GET/POST/COOKIE
三种模组的顺序及规则。参数
string
即为欲设定的规则,例如:设定成
"GP"
表示忽视
cookie
,并在存取方法
(method)
相同时,以
POST
取代
GET
。
ignore_user_abort string
内定值为
Off
。用来设定当传输未完成时,使用者端断线是要继续处理。
include_path string
为
PHP
程式中
require
、
include
及
fopen_with_path
等函式的档案搜寻路径。在
UNIX
或
Windows
中的斜线方向不同。
log_errors boolean
指定程式错误时是否要记录在
log
档中。
magic_quotes_gpc boolean
设定
GET/POST/COOKIE
三种模组的特殊字元,包含单引号、双引号、反斜线、及空字元
(NUL)
是否要自动加入反斜线当溢出字元。
magic_quotes_runtime boolean
设定传回资料是否自动加入反斜线当溢出字元。
magic_quotes_sybase boolean
设定
sybase
资料中单引号要自动加上反斜线当溢出字元。
max_execution_time integer
设定程式最久执行时间。单位是秒。
memory_limit integer
设定程式最多使用多少记忆体。
short_open_tag boolean
设定是否使用短的
PHP
标记
( ?>)
。若不使用,则必须要用
作为程式的开头,若要使程式合乎
XML
的标准则要关闭本功能。
track_errors boolean
打开本选项可使最后的错误讯息跟着全域变数
$php_errormsg
。
track_vars boolean
打开本选项可让使用者输入的字串自动剖析到变数之中,而不用自己处理。
upload_tmp_dir string
指定上传档案暂存路径。
user_dir string
指定使用者自家目录的路径。
warn_plus_overloading boolean
若打开本选项,字串间就只能用英文的句号
(.)
连结,而不能用加号
(+)
连结字串。
SMTP string
在
Windows
系列作业系统中,用来指定
SMTP
伺服器,供
mail
函式使用。参数
string
为
SMTP
伺服器名字。
sendmail_from string
设定
"From: "
字串供
Windows
系列作业系统使用
mail
函式。
sendmail_path string
设定
sendmail
程式的放置路径。例如
/usr/sbin/sendmail
。
safe_mode boolean
设定
PHP
在安全模式下执行。
safe_mode_exec_dir string
设定安全模式程式执行的路径。
debugger.host string
指定远端除错的伺服器名称,可以是
IP
或
Domain Name
。
debugger.port string
设定远端除错伺服器的埠号
(port)
。
debugger.enabled boolean
设定是否可以为除错模式。
enable_dl boolean
本选项要使用阿帕契模组
(Apache module)
的方式才有效。用来设定
PHP
的
dl()
函式可否作用。当系统处于安全模式
(safe-mode)
时,本选项
enable
也无法使用
dl()
函式。
extension_dir string
设定动态函式的路径。
extension string
PHP
启动时所要载入的动态扩充功能。
mysql.allow_persistent boolean
设定是否允许
MySQL
资料库长期连线
(persistent connections)
,会影响
mysql_pconnect()
函式。
mysql.max_persistent integer
设定每个处理程序最多保持几个
MySQL
长期连线。
mysql.max_links integer
设定每个处理程式最多开几个
MySQL
连线,包括长期连线。
msql.allow_persistent boolean
设定是否允许
mSQL
资料库长期连线
(persistent connections)
,会影响
msql_pconnect()
函式。
msql.max_persistent integer
设定每个处理程序最多保持几个
mSQL
长期连线。
msql.max_links integer
设定每个处理程式最多开几个
mSQL
连线,包括长期连线。
pgsql.allow_persistent boolean
设定是否允许
Postgres
资料库长期连线
(persistent connections)
,会影响
pg_pConnect()
函式。
pgsql.max_persistent integer
设定每个处理程序最多保持几个
Postgres
长期连线。
pgsql.max_links integer
设定每个处理程式最多开几个
Postgres
连线,包括长期连线。
sybase.allow_persistent boolean
设定是否允许
Sybase
资料库长期连线
(persistent connections)
,会影响
sybase_pconnect()
函式。
sybase.max_persistent integer
设定每个处理程序最多保持几个
Sybase
长期连线。
sybase.max_links integer
设定每个处理程式最多开几个
Sybase
连线,包括长期连线。
sybct.allow_persistent boolean
设定是否允许
Sybase-CT
资料库长期连线
(persistent connections)
,内定值是开启的。
sybct.max_persistent integer
设定每个处理程序最多保持几个
Sybase-CT
长期连线。内定值为
-1
表示无限制。
sybct.max_links integer
设定每个处理程式最多开几个
Sybase-CT
连线,包括长期连线。内定值为
-1
,表示没有限制。
sybct.min_server_severity integer
设定
Sybase-CT
伺服器错误报告的最少笔数。内定值为
10
。
sybct.min_client_severity integer
设定
Sybase-CT
客户端错误报告的最少笔数。内定值为
10
。
sybct.login_timeout integer
设定
Sybase-CT
最久可以使用的登入时间。内定值为
1
分钟。
sybct.timeout integer
设定
Sybase-CT
的
query
操作时间限制。内定值为无限制。
sybct.hostname string
设定
Sybase-CT
可连线机器名称。内定值不设限
ifx.allow_persistent boolean
设定是否允许
Informix
资料库长期连线
(persistent connections)
,会影响
ifx_pconnect()
函式。
ifx.max_persistent integer
设定每个处理程序最多保持几个
Informix
长期连线。
ifx.max_links integer
设定每个处理程式最多开几个
Informix
连线,包括长期连线。
ifx.default_host string
设定
Informix
内定连线的伺服器名称,供
ifx_connect()
或
ifx_pconnect()
函式使用。
ifx.default_user string
设定
Informix
内定连线的使用者帐号,供
ifx_connect()
或
ifx_pconnect()
函式使用。
ifx.default_password string
设定
Informix
内定连线的使用者密码,供
ifx_connect()
或
ifx_pconnect()
函式使用。
ifx.blobinfile boolean
设定
Informix
长位元物件模式,
0
表在记忆体;
1
表在档案中。亦可以在
PHP
程式中使用
ifx_blobinfile_mode()
函式来修改。
ifx.textasvarchar boolean
设定
Informix
文字模式内定值,
0
表传回
blob
的代码;
1
表传回
varchar
字串。亦可在
PHP
程式中使用
ifx_textasvarchar()
函式来修改设定。
ifx.byteasvarchar boolean
设定
Informix
位元组模式内定值,
0
表传回
blob
的代码;
1
表传回
varchar
字串。亦可在
PHP
程式中使用
ifx_byteasvarchar()
来修改设定。
ifx.charasvarchar boolean
设定
Informix
传回字串的字尾空格是否要自动去除。
ifx.nullformat boolean
设定
NULL
栏位的传回方式,
true
表示传回字串
NULL
,
false
表格传回字串
""
。亦可在
PHP
程式中以
ifx_nullformat()
修改。
bcmath.scale integer
设定
BC
高精确度函式库的小数点位数。
browscap string
设定浏览器的开档能力名。
uodbc.default_db string
设定
ODBC
内定连线的资料库名称,供
odbc_connect()
或
odbc_pconnect()
函式使用。
uodbc.default_user string
设定
ODBC
内定连线的使用者帐号,供
odbc_connect()
或
odbc_pconnect()
函式使用。
uodbc.default_pw string
设定
ODBC
内定连线的使用者密码,供
odbc_connect()
或
odbc_pconnect()
函式使用。
uodbc.allow_persistent boolean
设定是否允许
ODBC
资料库长期连线
(persistent connections)
,会影响
odbc_pconnect()
函式。
uodbc.max_persistent integer
设定每个处理程序最多保持几个
ODBC
长期连线。
uodbc.max_links integer
设定每个处理程式最多开几个
ODBC
连线,包括长期连线。
以下和 session 有关的设定值,都在 PHP 4.x 以上的版本方支援。在 php.ini 的设定档中。
session.save_handler
定义
session
储存资料的档案名称。内定值为
files
。
session.save_path
定义
session
储存资料的档案路径。内定值为
/tmp
。
session.name
设定
session
所使用的
cookie
名称。内定值为
PHPSESSID
。
session.auto_start
设定
session
是否自动开启。内定值为
0 (
否
)
。
session.lifetime
设定
cookie
送到浏览器后的保存时间,单位为秒。内定值为
0
,表示直到浏览器关闭。
session.serialize_handler
定义连续
/
解连续资料的标头,本功能只有
WDDX
模组或
PHP
内部使用。内定值为
php
。
session.gc_probability
设定每笔要求回应时的废物蒐集
(gc, garbage collection)
处理机率。内定值为
1
。
session.gc_maxlifetime
设定废物被清除前的存活秒数。
session.extern_referer_check
决定参照到客户端的
Session
代码是否要删除。有时在安全或其它考量时,会设定不删除。内定值为
0
。
session.entropy_file
设定
session
代码建立时,使用外部高熵值资源或档案来建立,例如
UNIX
系统上的
/dev/random
或
/dev/urandom
。
session.entropy_length
设定
session
从高熵值资源读取的位元组数。内定值为
0
。
session.use_cookies
设定是否要将 session 变成 cookie 存在使用者端。内定值为 1,表是开启本功能。
若只会用
Frontpage
、
Dreamweaver
的所见即所得编辑模式来写网页,而完全不懂
HTML
的语法,恐怕要先下点功夫了解
HTML
语法,才能顺利的写
PHP
程式了。若早就极度了解
HTML
幕后排版语言的语法,那么应该可以马上开始
PHP
的程式写作了。在
PHP
这种后端伺服器的程式语言,下苦功去学习
HTML
是写作的必备条件。
对熟悉 Windows 系列作业平台的使用者,有套软体倒是在开发 PHP 程式上是蛮方便的,就是 PHP Editor,若对这软体有兴趣,不妨到 http://www.soysal.com/PHPEd 下载最新的版本。同时还需要下载 PHP 程式 Win32 的版本。安装妥 PHP Win32 版后,在 PHP Editor 中设定好 PHP Win32 的路径,就可以让您轻松地开发 PHP 程式了。至于在写作 PHP 程式的工作平台上,有没有 Web 伺服器倒是不重要,因为在 PHP Editor 的环境中写好的程式,按下执行的键后,由 PHP Editor 直接将所写的 PHP 程式送给 PHP Win32,并将执行结果处理后,呈现在使用者的面前。
若您和笔者一样,对 Linux/UNIX 很偏执,或许可以尝试使用 Bluefish: 蓝鱼 PHP 写作工具,只要装好 Linux 及 X-Window 之后就可以使用了。要使用中文的话,需要在 X-Window 加入中文的字型及输入法,要方便的话,使用 RedHat 加上 CLE 中文支援就好了。
假使您已是老 Linux/UNIX 了,也不喜欢使用这些专门的开发工具。相信您对 vi 或是 Emacs 一定相当熟悉,那么就在装妥 Web 伺服器和 PHP 程式后,直接用 vi 或 Emacs 写 PHP 程式就可以了,并且可以直接看到执行的真正结果,和资料库或是其它的伺服程式连结,也不会有什么问题。这种「所写即所得」的方式,岂不痛快!!
语法
在
K&R
二教授的经典名著
The C Programming Language
一书中的
"hello, world"
几乎已经变成了所有程式语言的第一个范例。因此,在这儿也用
PHP
来写个最基本的
"hello, world"
程式。
这十行程式在 PHP 中不需经过编译等复杂的过程,只要将它放在设定好可执行 PHP 语法的伺服器中,将它存成档案 helloworld.php 好了。在用户的浏览器端,只要在 Location: 输入 http://some.hostname/helloworld.php,就可以在浏览器上看到 hello, world 字串出现。
我们可以看到,这个程式只有三行有用,其它六行都是标准的 HTML 语法。而它在传回浏览器时和 JavaScript 或 VBScript 完全不一样,PHP 的程式没有传到浏览器,只在浏览器上看到短短的几个字 "hello, world"。First program echo "hello, world/n" ; ?>
在第六行及第八行,分别是 PHP 的开始及结束的嵌入符号。第七行才是伺服器端执行的程式。在这个例子中,"/n" 和 C 语言的表示都一模一样,代表换行的意思。在第一章也有介绍过 PHP 是混合多种语言而成,而 C 正是含量最多的语言。在一个叙述式结束后,要加上分号代表结束。
嵌入方法
要将
Homepage
中放入
PHP
,有以下数种做法
echo (
"
这是一个
PHP
语言的嵌入范例
/n"
);
?>
1.
echo(
"
这是第二个
PHP
语言的嵌入范例
/n"
);
?>
2.
3.
<% echo ("
这是类似
ASP
嵌入语法的
PHP
范例
"); %>
其中第一种及第二种是最常用的二个方法,在小于符号加上问号后,可以加也可以不加
php
三个字,之后就是
PHP
的程式码。在程式码结束后,加入问号大于二符号就可以了。第三种方法对熟悉
Netscape
伺服器产品的
Webmaster
人员而言,有相当的亲切感,它是类似
JavaScript
的写作方式。而对于从
Windows NT
平台的
ASP
投向
PHP
的使用者来说,第四种方法似曾相似,只要用
PHP 3.0.4
版本以后的伺服器都可以用小于百分比的符号开始,以百分比大于结束
PHP
的区段,但想用第四种方法的使用者别忘了在
php.ini
加入
asp_tags
或是在编译
PHP
时加入
--enable-asp-tags
的选项,才能使第四种方法有效。建议少用第四种方法,当
PHP
与
ASP
原始码混在一起时就麻烦了。
其实,在写作 PHP 的程式最好的方法,就是先处理好纯 HTML 格式的 Homepage 文件之后,再将需要变数或其它处理的地方改成 PHP 程式。这种方法,可以让您在开发上达到事半功倍的效果。
引用档案
PHP
最吸引人的特色之一大概就是它的引用档案了。用这个方法可以将常用的功能写成一个函式,放在档案之中,然后引用之后就可以呼叫这个函式了。
引用档案的方法有二种:require 及 include。二种方式提供不同的使用弹性。
require 的使用方法如
require("MyRequireFile.php");
。这个函式通常放在 PHP 程式的最前面,PHP 程式在执行前,就会先读入 require 所指定引入的档案,使它变成 PHP 程式网页的一部份。常用的函式,亦可以这个方法将它引入网页中。
include 使用方法如
include("MyIncludeFile.php");
。这个函式一般是放在流程控制的处理区段中。PHP 程式网页在读到 include 的档案时,才将它读进来。这种方式,可以把程式执行时的流程简单化。
在 PHP 的官方网站中,每页都可以看到原始程式,更是处处看到这二种引入档案的方法。
程式注解
在
PHP
的程式中,加入注解的方法很灵活。可以使用
C
语言、
C++
语言或者是
UNIX
的
Shell
语言的注解方式,而且也可以混合使用。这可以让每个写
PHP
网页程式的
Webmaster
或
Programmer
发展出属于自己的写作风格。
echo
"这是第一种例子。/n"
;
// 本例是 C++ 语法的注解 /* 本例采用多行的 注解方式 */
echo
"这是第二种例子。/n"
; echo
"这是第三种例子。/n"
;
# 本例使用 UNIX Shell 语法注解
?>
不过在使用多行注解时请注意,不能让注解陷入巢状回圈当中,否则会引起错误。
/*
echo "这是错误的示范。/n"; /* 巢状注解会引起问题 */
*/
?>
常数型态
PHP
在常数中定义了以下一些常数。
__FILE__
这个内定常数是 PHP 程式档名。若引用档案 (include 或 require)则在引用档案内的该常数为引用档案名,而不是引用它的档案名。
__LINE__
这个内定常数是 PHP 程式行数。若引用档案 (include 或 require)则在引用档案内的该常数为引用档案的行,而不是引用它的档案行。
PHP_VERSION
这个内建常数是 PHP 程式的版本,如 '3.0.8-dev'。
PHP_OS
这个内建常数指执行 PHP 剖析器的作业系统名称,如 'Linux'。
TRUE
这个常数就是真值 (true)。
FALSE
这个常数就是伪值 (false)。
E_ERROR
这个常数指到最近的错误处。
E_WARNING
这个常数指到最近的警告处。
E_PARSE
本常式为剖析语法有潜在问题处。
E_NOTICE
这个常式为发生不寻常但不一定是错误处。例如存取一个不存在的变数。
这些 E_ 开头形式的常数,可以参考 error_reporting() 函式,有更多的相关说明。
当然在程式写作时,以上的内定常数是不够用。define() 的功能可以让我们自行定义所需要的常数。见下例
define
(
"COPYRIGHT"
,
"Copyright © 2000, netleader.126.com"
); echo
COPYRIGHT
;
?>
变数型态
PHP
的变数型态不多,有以下五种:
string
integer
double
array
object
string
即为字串变数,无论是单一字元或数千字的字串都是使用这个变数型态。值得注意的是要指定字串给字串变数,要在头尾加上双引号
(
例如
: "
这是字串
")
。在欲让字串换行时,可使用溢出字元,也就是反斜线加上指定的符号,若是
/x
加上二位数字,如
/xFE
即表十六进位字元,详见下表:
符号
意义
/"
双引号
//
反斜线
/n
换行
/r
送出
CR
/t
跳位
(TAB)
integer 为整数型别。在 32 位元的作业系统中,它的有效范围是 -2,147,483,648 到 +2,147,483,647。要使用 16 进位整数可以在面加 0x。
double 为浮点数型别。在 32 位元的作业系统中,它效范围是 1.7E-308 到 1.7E+308。
array 为阵列变数,可以是二维、三维或者多维阵列,其中的元素也很自由,可以是 string、integer 或者 double,甚至是 array。
object 为物件变数,目前在 PHP 中的物件不多,若论及物件,Microsoft 的 ASP 物件仍然较 PHP 的内定物件多,相信这有赖大家的努力。不过话又说回来,Web CGI 程式要求的是效率,以完全物件导向的方式,恐怕使用者在浏览时也会因为程式执行速度慢而很不耐烦吧。
要使用变数,只要在英文字串前面加个钱号 $ 即可,目前变数名称仍不能使用中文。至于变数的大小写是不一样的,对开发 PHP 程式的 TEAM 来说,最好使用相同的变数使用风格,以免届时因为变数大小的问题,花许多无谓的时间去找寻问题点,那就麻烦了。
以下为变数的使用范例:
$mystring
=
"我是字串"
;
$WilsonPeng
=
"真是认真的作者"
;
$NewLine
=
"换行了/n"
;
$int1
=
38
;
$int2
=
49
;
$hexint
=
0x10
;
$float1
=
1.732
;
$float2
=
1.4E+2
;
$MyArray1
= array(
"子"
,
"丑"
,
"寅"
,
"卯"
);
$MyArray2
= array(
"地支"
=> array(
"子"
,
"丑"
,
"寅"
,
"卯"
),
"生肖"
=> array(
"鼠"
,
"牛"
,
"虎"
,
"兔"
),
"数字"
=> array(
1
,
2
,
3
,
4
) );
物件的使用上就比较麻烦了,要先宣告类别,甚至必须先要方法,方可使用物件,如下例:
class
foo
{ function
do_foo
() { echo
"Doing foo."
; } }
$bar
= new
foo
;
$bar
->
do_foo
();
更多有关物件的讨论,参考 物件
此外,还有布林值 (boolean),通常 1 即为 true,0 为 false。
在变数之间,若要转换型态,在 PHP 程式中是很自由的,直接就拿来用了,不必经过特殊的转换函式。当然,浮点数转成整数就有点牵强了,不过可以将浮点数转成字变数的使用
就像大部份的结构化程式,有所谓的全域变数与区域变数,PHP 在这方面也是有相同的处理方式。
在 PHP 的程式执行时,系统会在记忆体中保留一块全域变数的区域。实际运用时,可以透过 $GLOBALS["变数名称"] 将需要的变数取出。在使用者自定的函式或程序中,就可以用 $GLOBALS 阵列取出需要的变数。当然别忘了 PHP 的变数有分大小写,搞错了大小写可是叫一百年,变数也不会出来。
$GLOBALS 阵列是 PHP 程式中比较特殊的变数,不必宣告,系统会自动匹配相关的变数在里面。在函式中,也不必管 $GLOBALS 阵列是否已经做全域宣告,就可以直接使用了。
和 $GLOBALS 变数类似的还有 $php_errormsg 字串变数。若 PHP 的设定档 (php.ini/php3.ini) 中的 track_errors 选项打开的话,会有全域变数 $php_errormsg 可以看到错误的讯息。
在 PHP 中,全域变数的有效范围 (scope) 仅限于主要程式中,不会影响到函式中同名的变数,也就是全域变数与区域变数互不侵犯。若要变数能通透到函式中,就要用到 $GLOBALS 阵列或是使用 global 宣告。
例如,在自行开发的函式中,要取得目前执行 PHP 程式页面的档名,就可以用 $GLOBALS["PHP_SELF"] 取出 $PHP_SELF 的值。
// 本程式使用 $GLOBALS 阵列
function myfunc() {
echo $GLOBALS["PHP_SELF"];
}
myfunc();
?>
下面是错误的示范,请勿模仿,上面的才是正确的示范。
// 这是错误的示范
function errfunc() {
echo $PHP_SELF;
}
errfunc();
?>
但是这个错误的示范要是改成下例就没问题了
// 本程式使用全域宣告
function myfunc() {
global $PHP_SELF;
echo $PHP_SELF;
}
myfunc();
?>
在变数前面加上 global 的名称,就是宣告该变数为全域变数。用这种方式,就不需使用 $GLOBALS 阵列,也能让变数进入自行开发的函式中。
接下来先看静态变数的例子
// 静态变数的例子
function myfunc() {
static $mystr;
$mystr.="哈";
echo $mystr." /n";
}
myfunc(); // 哈
myfunc(); // 哈哈
myfunc(); // 哈哈哈
?>
函式在执行时所产生的变数,在函式结束时就消失了,有时因为程式的需要,函式在回圈中,当不希望变数在每次执行完函式就消失的话,静态变数 (static variable) 就派上用场了。上例中,使用 $mystr 变数之前,先在变数前面加上 static,即表示变数 $mystr 是静态变数,当每次执行 myfunc() 函式时,$mystr 的值会一直增加,每执行一次就多一个哈字。若将 static 的静态变数宣告拿掉,就没办法累加哈字了。
// 不是静态变数的例子 (错误的)
function myfunc() {
$mystr.="哈";
echo $mystr." /n";
}
myfunc(); // 哈
myfunc(); // 哈
myfunc(); // 哈
?>
再看一个比较实际的例子,它是处理表格的颜色,让隔行的颜色不同。
function TdBackColor() {
static $ColorStr;
if ($ColorStr=="808080") {
$ColorStr="c0c0c0";
} else {
$ColorStr="808080";
}
return($ColorStr);
}
echo "/n";
for ($i=0; $i<10; $i++) {
$ColorStr=TdBackColor();
echo "这是第".$i."行 /n";
}
echo "
";
?>
PHP 的变数使用技巧上,最令人觉得不可思议的则是变数的变数 (variable variable)。这是充分利用 PHP 特性玩出的特殊技巧
$a = "Hello";
$$a = "world";
echo "$a, $hello"; // Hello, world
echo "$a, {$a}"; // 也是 Hello, world
?>
function myCallbackFunction()
{
print("Hello from callback");
}
function myFunction($callback)
{
$callback();
}
// call to myFunction passing callback
// function as parameter
myFunction("myCallbackFunction");
?>
至于使用者在 FORM 中输入的资料,要怎么处理呢?要是在 PHP 的设定档中,track_vars 设为 On 时,直接使用变数名字就好了。如下例,next.php 在执行时,系统会自动产生二个变数 $username 及 $sex,直接使用就好了,比起传统的 CGI 要自己剖析,PHP 实在是太神奇了。
姓名:
性别:
由于 PHP 许多语法都是 C 语言的翻版,故 PHP 在使用变数时,随时都可以使用新的变数,只要在使用前将变数初始化就好了,不必像 Pascal 语言那样严谨,所有要使用的变数都要事先宣告。这当然有好处与坏处:好处是使用方便、自由;坏处就是常常因这些自由而付出相当大的代价除错。在程式码短的 PHP 程式当然不成问题,当程式在数百数千行,甚至 include 或 require 好几层之后,问题就浮现出来了。无论如何,保持良好的写作习惯才是避免浪费青春的方法。串,也是很好处理。运算符号
运算符号可以用来处理数字、字串及其它需要比较运算的条件。PHP 的运算符号和 C 语言的运算符号与很类似,对于有经验的程式设计人员,应可以很顺利的掌握 PHP 的运算符号。
不同的运算符号,其实还是有优先顺序,就像小时候在学数学时,老师会教:先乘除、后加减。在 PHP 的运算优先顺序可以参考下面的表格,在混合式的情形下,愈往下表示优先权愈高。至于符号代表的意义,散见于以下的章节中。
运算符号 结合规则
, 左至右
or 左至右
xor 左至右
and 左至右
.= &= |= /= %= ^= = += -= *= 左至右
? : 左至右
|| 左至右
&& 左至右
| 左至右
^ 左至右
& 左至右
== != 不限
< <= >= > 不限
<< >> 左至右
+ - . 左至右
* / % 左至右
! ~ ++ -- @ 右至左
[] 右至左
就像先哲说的:物有本末、事有终始,知所先后,则近道矣,在运算时只要照着运算优先顺序写出来的程式,应该不会发生结果和预期不同的情形。在写作时多注意细节,可以减少除错的痛苦!算术运算
算术运算
(arithmetic operators) 符号,就是用来处理四则运算的符号,这是最简单,也最常用的符号,尤其是数字的处理,几乎都会使用到算术运算符号。
符号 意义
+ 加法运算
- 减法运算
* 乘法运算
/ 除法运算
% 取余数
++ 累加
-- 递减
以下为简单的算术运算范例
$a = 8;
$b = 2;
$c = 3;
echo $a+$b." /n";
echo $a-$b." /n";
echo $a*$b." /n";
echo $a/$b." /n";
echo $a%$c." /n";
$a++;
echo $a." /n";
$c--;
echo $c;
?>
字串运算
---------------------------------------------------------------------
字串运算 (string operator) 的运算符号只有一个,就是英文的句号 .。它可以将字串连接起来,变成合并的新字串。
以下是字串运算的例子
$a = "PHP 4";
$b = "功能强大";
echo $a.": ".$b;
?> 设定运算
设定运算 (assignment operator) 有时会让人搞得一头雾水,不过它可以让程式更精简,增加程式的执行效率。
符号 意义
= 将右边的值连到左边
+= 将右边的值加到左边
-= 将右边的值减到左边
*= 将左边的值乘以右边
/= 将左边的值除以右边
%= 将左边的值对右边取余数
.= 将右边的字串加到左边
$a = 5;
$a += 2; // 即 $a = $a + 2;
echo $a." /n";
$b = "哇";
$b .= "哈"; // $b = "哇哈";
$b .= "哈"; // $b = "哇哈哈";
echo "$b /n";
?>
位元运算 -----------------------------------------------------------------PHP 的位元运算子 (bitwise operators) 共有六个,提供数字做一些快速而低阶的运算。欲了解更多有关位元运算的资讯,可以参考离散数学方面的书籍。
符号
意义
&
且
(And)
|
或
(Or)
^
互斥
(Xor)
<<
向左移位
>>
向右移位
~
取
1
的补数
逻辑运算 -------------------------------------------------------------------------逻辑运算 (logical operators) 通常用来测试真假值。最常见到的逻辑运算就是回圈的处理,用来判断是否该离开回圈或继续执行回圈内的指令。
符号 意义
< 小于
> 大于
<= 小于或等于
>= 大于或等于
== 等于
!= 不等于
&& 而且 (And)
and 而且 (And)
|| 或者 (Or)
or 或者 (Or)
xor 互斥 (Xor)
! 不 (Not)
$a = 5;
if ($a != 5) {
echo "$a 不是 5";
} else {
echo "$a 是 5";
}
?>
--------------------------------------------------------------------------------
其它运算符号
--------------------------------------------------------------------------------
除了上述的运算符号之外,还有一些运算符号难以归类。
符号 意义
$ 变数
& 变数的指标 (加在变数前)
@ 不显示错误讯息 (加在函数前)
-> 物件的方法或者属性
=> 阵列的元素值
? : 三元运算子
其中比较特殊的是三元运算子 ? :,以下例来解释
(expr1) ? (expr2) : (expr3);
若 expr1 的运算结果为 true,则执行 expr2;否则执行 expr3。实际上它有点类以 if...else 回圈,但可以让程式较精简有效率。
流程控制 ------------------------------------------------------------------------由于 PHP 的大部份语法都是承袭了 C 语言的语法,因此,在流程控制方面,也是有着和 C 语言极类似的回圈。PHP 的回圈不像 ASP 般可以使用 goto 的 BASIC 语法,PHP 是结构化的程式语言,流程的设计上有一定的规定,而不能用 BASIC 的观念来乱跳到别的区段中。PHP 的语法没有像 C 语言的 main(){} 区段,其实整个 PHP 主页面 (就是浏览器输入的 URL) 就是 main(){} 区段,这点和其它的解译程式,如 Prel、Python、Shell Script 倒是很像。在流程的区段分隔符号上,都是使用 { 当作区段的开头,用 } 当作结尾,和 C 语言相同。不过 C 可以定义 begin 当开头、end 当结尾 (像 Pascal),而 PHP 中不能做这种特殊的定义。而 PHP 语法中在每道指令结束时都要加上分号 ;,但是在区段结尾符号 } 后面不用加上分号结束。
if..else 回圈 ------------------------------------------------------------------if..else 回圈有三种构造
第一种是只有用到 if 条件,当作单纯的判断。解释成 "若发生了某事则怎样处理"。语法如下:
if (expr) {
statement
}
其中的 expr 为判断的条件,通常都是用逻辑运算符号 (logical operators) 当判断的条件。而 statement 为符合条件的执行区段程式,若程式只有一行,可以省略大括号 {}。
范例:本例省略大括号。
if (date("D") == "Sat") echo "周末了,狂欢去";
?>
范例:本例的执行区段有三行,不可省略大括号。
if (file_exists("/usr/local/lib/php3.ini")) {
readfile("/usr/local/lib/php3.ini");
echo "/n";
}
?>
第二种是除了 if 之外,加上了 else 的条件,可解释成 "若发生了某事则怎样处理,否则该如何解决"。语法如下
if (expr) {
statement1
} else {
statement2
}
范例:上面的例子来修改成更完整的处理。其中的 else 由于只有一行执行的指令,因此不用加上大括号。
$f="/usr/local/lib/php3.ini";
if (file_exists($f)) {
readfile($f);
echo "/n";
} else echo "很抱歉,找不到 $f";
?>
第三种就是巢状的 if..else 回圈,通常用在多种决策判断时。它将数个 if..else 拿来合并运用处理。
直接看下面的例子
if ($a > $b) {
echo "a 比 b 大";
} elseif ($a == $b) {
echo "a 等于 b";
} else {
echo "a 比 b 小";
}
?>
上例只用二层的 if..else 回圈,用来比较 a 和 b 二个变数。实际要使用这种巢状 if..else 回圈时,请小心使用,因为太多层的回圈容易使设计的逻辑出问题,或者少打了大括号等,都会造成程式出现莫名其妙的问题。
do..while 回圈 -------------------------------------------------------------------------------do..while 是重复叙述的回圈,可以分成二种模式。
最单纯的就是只有 while 的回圈。用来在指定的条件内,不断地重覆指定的动作。语法如下
while (expr) {
statement
}
其中的 expr 为判断的条件,通常都是用逻辑运算符号 (logical operators) 当判断的条件。而 statement 为符合条件的执行区段程式,若程式只有一行,可以省略大括号 {}。
下例很有趣,要电脑的浏览器出现十次 "以后不敢了" 的字串,前面并加上数字,表示说了第几次不敢了。(感觉好像是 Web Server 做错事被处罚)
$i = 1;
while ($i <= 10) {
print $i++;
echo ". 以后不敢了 /n";
}
?>
while 可以不用大括号来包住执行区段,而使用冒号加上 endwhile。见下例
$i = 1;
while ($i <= 10):
print $i++;
echo ". 以后不敢了 /n";
endwhile;
?>
另外一种 do..while 回圈则先执行,再判断是否要继续执行,也就是说回圈至少执行一次,有点像是先斩后奏的方法。这种的回圈,和单用 while 是不同的 (单用 while 是先判断再处理)。若读者熟 Pascal 语言的话,会发现 do..while 回圈像是 Pascal 的 repeat..until 回圈。
do..whilte 的语法如下
do {
statement
} while (expr);
--------------------------------------------------------------------- for 回圈 --------------------------------------------------------------------for 回圈就单纯只有一种,没有变化,它的语法如下
for (expr1; expr2; expr3) {
statement
}
其中的 expr1 为条件的初始值。expr2 为判断的条件,通常都是用逻辑运算符号 (logical operators) 当判断的条件。expr3 为执行 statement 后要执行的部份,用来改变条件,供下次的回圈判断,如加一..等等。而 statement 为符合条件的执行区段程式,若程式只有一行,可以省略大括号 {}。
下例是用 for 回圈写的 "以后不敢了" 的例子,可以拿来和用 while 回圈的比较。
for ($i=1; $i<=10; $i++) {
echo "$i. 以后不敢了 /n";
}
?>
从上例中,可以很明显的看到,用 for 和用 while 的不同。实际应用上,若回圈有初始值,且都要累加(或累减),则使用 for 回圈比用 while 回圈好。例如将资料从资料库取出,可能用 for 回圈会比用 while 回圈适合--------------------------------------------------------------------------------switch 回圈--------------------------------------------------------------------switch 回圈,通常处理复合式的条件判断,每个子条件,都是 case 指令区段。在实作上若使用许多类似的 if 指令,可以将它综合成 switch 回圈。
语法如下
switch (expr) {
case expr1:
statement1;
break;
case expr2:
statement2;
break;
:
:
default:
statementN;
break;
}
其中的 expr 条件,通常为变数名称。而 case 后的 exprN,通常表示变数值。冒号后则为符合该条件要执行的区段。注意要用 break 跳离回圈。
switch (date("D")) {
case "Mon":
echo "今天星期一";
break;
case "Tue":
echo "今天星期二";
break;
case "Wed":
echo "今天星期三";
break;
case "Thu":
echo "今天星期四";
break;
case "Fri":
echo "今天星期五";
break;
default:
echo "今天放假";
break;
}
?>
很明显的,上述的例子用 if 回圈就很麻烦了。当然在设计时,要将出现机率最大的条件放在最前面,最少出现的条件放在最后面,可以增加程式的执行效率。上例由于每天出现的机率相同,所以不用注意条件的顺序。
其它的流程控制
除了上面的流程控制指令之外,尚有 break 及 continue 二个流程控制指令。
break 用来跳出目前执行的回圈,如下例
$i = 0;
while ($i < 10) {
if ($arr[$i] == "stop") {
break;
}
$i++;
}
?>
continue 即刻停止目前执行回圈,并回到回圈的条件判断处,见下例
while (list($key,$value) = each($arr)) {
if ($key % 2) { // 略过偶数
continue;
}
do_something_odd ($value);
}
?>
而 BASIC 常用的 goto 在 C 及 Borland Pascal 中或许可以使用。但在 PHP 中,由于它的 Web Server Script 特性以及结构化的组成,并不能在 PHP 中使用 goto 回圈指令? -------------------------------------------------------------------- 函式 ------------在 PHP 中,允许程式设计者将常用的流程或者变数等元件,组织成一个固定的格式。也就是说使用者可以自行组合函式或者是物件。
PHP 中的函式 (function) 和 C 语言一样,包括有传回值的及无传回值的,不像 Pascal 分成函式 (function) 和程序 (procedure) 那么复杂。
在函式的名称上,PHP 对于大小写的管制很松散。可以在定义函式时写成大写的名字,而在使用时使用小写的名字。总之,对函式而言,不用管大小写,只要注意名称没有重复就好了。
以下就是函式的使用语法
function myfunc($arg_1, $arg_2, ..., $arg_n) {
// 执行一些动作
return $retval;
}
在使用时,在自定的函式名称前要加入 function 的保留字,表示这是定义使用者自定函式。之后的 myfunc 可以是任何的英文字母开头的字串,字串除了开头不能是数字或是底线,在第一个字母后可以是阿拉伯数字或者是底线,当然其它的符号或是中文字不能当函式名。
$arg_1 到 $arg_n 为函式使用的参数,参数之间使用逗号隔开。在参数后的大括号 {},即为整个函式的区段。函式若有传回值,使用 return 可将值传回。而参数可以事先定义初始值或内定值。有定义内定值的参数在使用函式时可以省略,但一定要放在没有设定内定值参数的后面,否则 PHP 在解析函式时,会出现错误。
另外就是参数的形态,只要参数是 PHP 支援的变数型态都可以使用,无论是阵列、字串、或是整数....等等。传回值也是一样。
下面即为使用内定值及不用内定值的例子
function myfunc1($arg_1, $arg_2, $arg_3="我是内定字串") {
echo $arg_1+$arg_2;
}
myfunc(3, 4); // 参数 $arg_3 省略。
myfunc(6, 6, "不用内定值") // 输入参数 $arg_3。
?>
参数的值,通常使用传值的方式输入,有时在特别的需求时,可以使用传址的方式,传入参数的指标。方法就是在参数的前面加上 & 符号即可。如下例
function myfunc2(&$argstr) {
$argstr=ereg_replace("/", "-", $argstr);
}
$today="2000/01/01";
myfunc2($today);
echo $today; // 2000-01-01;
?>------------------------------------------------------------------------------物件使用者定义的物件,也是学好 PHP 所必备的条件之一。而 PHP 的物件,和其它的物件导向语言比较起来,还算蛮单纯的。PHP 只有类别 (class)、方法 (method)、属性、以及单一继承 (extensions) 等。对不习惯使用 C++、Java、Delphi 等物件导向语言来开发程式的使用者,不妨先阅读一下有关物件导向观念的书,相信可以带来许多的收获。下面的范例是手推车的物件。可以看到,使用 class 表示它是一个物件类别。在类别中的 function,例如 add_item 则表示该物件的一个方法。方法可以封装物件的实际处理情形,让该物件自己能依封装好的方法来执行一些动作。程式中的 $this 物件变数也和 $GLOBALS 及 $php_errormsg 二个变数一样,在 PHP 中属于特殊的变数。$this 变数只用在物件类别中,表示物件的本身。
// 程式名: cart.inc
class Cart {
var $items; // 手推车物件
// 本方法加入 $num 件物品到手推车中 (加到 $artnr 变数)
function add_item ($artnr, $num) {
$this->items[$artnr] += $num;
}
// 本方法从手推车减少 $num 件物品 (从 $artnr 变数减掉)
function remove_item ($artnr, $num) {
if ($this->items[$artnr] > $num) {
$this->items[$artnr] -= $num;
return true;
} else {
return false;
}
}
}
?>
要使用手推车可以用类似下例的方式。可以先将每个物件存成 Include 档案,再将它 require 或 include 进来。在宣告变数 $cart 时,要使用 new 的保留字,表示 $cart 使用 Cart 物件。使用 -> 符号,表示执行物件的方法。
require("cart.inc");
$cart = new Cart;
$cart->add_item("10", 1);
?>
之后再设计有记名的手推车。记名手推车从手推车遗传下来,因此手推车拥有的方法及属性,记名手推车也有,而记名手推车比手推车增加了名字的方法 (或许该称属性较恰当)。
从下例中可以看到,子物件 Named_Cart 使用 extends 来继承其父物件 Cart。虽然 Named_Cart 物件中没有增加物品及减少物品的方法,不过由于遗传的特性,父物件有的东西它都有。
// 程式名: named_cart.inc
require("cart.inc");
class Named_Cart extends Cart {
var $owner;
function set_owner ($name) {
$this->owner = $name;
}
}
?>
要使用记名手推车物件,请看下面的范例。当然这不算太好的设计,每个子物件都一直 require 它的父物件,会造成伺服器在 I/O 上面的负担。在实作时,可以将整个系列的物件在同一个程式档案中,从最早的袓先物件到最后的子孙物件,也方便日后修正。
require("named_cart.inc");
$ncart = new Named_Cart; // 建立物件变数
$ncart->set_owner ("CyberRidder"); // 设定物件的记名属性
echo $ncart->owner; // 显示物件的记名属性
$ncart->add_item ("10", 1); // 从父物件遗传的方法也可使用
?>
因此,在 PHP 中使用了 extends 保留字,加上良好的系统分析与完整的 CRC 卡片 (详见物件导向相关书籍) 设计之后,PHP 可变成拥有强大物件能力的 CGI 语言。
PHP 由于是剧本语言 (Script),因此程式原始码可见,在软体工程中的元件黑箱并不会在目前的 PHP 版本中出现,也就是说,全部的物件其实没有隐藏起它的内容。对于软体业者而言,没有办法保护所谓的软体 IC,站在开放社群而言,反而有原始码是件好事,至于孰是孰非,就很难判定了,不过目前 PHP 还是 Open Source 社群的一份子,或许日后 Zend 引擎可以做到物件封装的功能也不一定。
函式库及函式
本章中的函式涵盖 PHP 3.0.11 版前的所有函式。
函式的格式如下:
函式库名称
函式名称
函式功能简述。
语法: 传回型态 函式名称(型态 参数1, 型态 参数2....);
传回值: 变数型态
函式种类: 例如: 资料库, 网路..等等
内容说明
函式说明详述内容
使用范例
函式的范例 (本栏可能省略)
参考
小节索引 函式名称, 小节索引 函式名称... (本栏可能省略)
以下为 PHP 的相关函式库
Adabas D 资料库连结函式库
阿帕契伺服器专用函式库
阵列处理函式库
拼字检查函式库
BC 高精确度函式库
历法函式库
ClibPDF 函式库
日期与时间函式库
DBA 函式库
dBase 格式资料表函式库
dbm 类资料库函式库
目录管理函式库
动态连结函式库
程式执行功能函式库
FDF 函式库
filePro 资料库函式库
档案系统函式库
HTTP 相关函式库
Hyperwave 伺服器函式库
图形处理函式库
IMAP 电子邮件系统函式库
PHP 选项及相关资讯函式库
Informix 资料库函式库
InterBase 资料库函式库
LDAP 目录协定函式库
电子邮件函式库
数学运算函式库
mcrypt 编码函式库
mhash 杂凑函式库
杂项函式库
mSQL 资料库函式库
SQL Server 资料库函式库
MySQL 资料库函式库
Sybase 资料库函式库
网路函式库
NIS 函式库
ODBC 资料库连结函式库
Oracle 8 资料库函式库
Oracle 资料库函式库
Perl 相容语法函式库
PDF 格式档案函式库
PostgreSQL 资料库函式库
常规表示法函式库
信号与共享记忆体函式库
Solid 资料库连结函式库
SNMP 网管函式库
字串处理函式库
URL 处理函式库
变数处理函式库
Vmailmgr 邮件处理函式库
WDDX 函式库
压缩档案函式库
XML 剖析函式库
Session 函式库
GNU 记录函式库
FTP 档案传输函式库
MCAL 模组日历存取函式库
范例程式 --------------------------首先要说明的是,这几个范例程式都是不同性质的程式,每个程式在 Webmaster 学科 (若 Webmaster 也算是一门学科的话),都是不同的主题,也是成为专业人士所必须要研究的,也因此几乎所有和 CGI 有关的书都会提到这些主题。
当然从专业的角度而言,以下的程式实在太简单了,许多专业的 CGI 套件提供了完整而严谨的解决方案。在这儿只是要展现出 PHP 的程式实作,并真正与网站的系统相结合。至于真正要拿这些范例来赚钱,可能还有一段距离。实际上,可以在网路上找一些现成的套件,倒是比开发还容易。
以下就是这些主题
访客计数器: 探讨如何存取档案。
使用者认证: 探讨 PHP 与 MySQL 资料库的结合。并提供网页权限的架构。
聊天室: 探讨程式的共用变数档及用外部指令操作档案。
留言版: 探讨 PHP 与 Oracle 资料库的结合
意见信箱: 探讨 PHP 如何执行外部程式。
广告轮播: 探讨 PHP 所提供档案上传的功能。
访客计数器
访客计数器是让 Web 访客知道该网页或者网站的人气指数最直接的方法。尤其是想利用网页赚钱的人,访客人数是找广告商最好的说词。当然可以将网站来访人数写成统计报表,但总是感觉直接看到比较真实,到底眼见为凭。
在上图中,访客计数器的流程如下
第一位使用者浏览某页。
伺服器程式从资料库或档案中读取该页被浏览次数。
将次数加一储存,并将它送回第一位使用者。
第二位使用者浏览某页。
伺服器程式从资料库或档案中读取该页被浏览次数。
将次数再加一储存,并将它送回第二位使用者。
PHP 在没有特殊的访客计数器函式,但是我们可以用 PHP 的强大功能自已写一个访客计数器函式
以下的函式是访客计数器的原型,是由 David W. Bettis 所提供,并经过作者少许修改。
访客计数器 原型
/*
simple access counter for php3
(c)1998 David W. Bettis
medify by Wilson Peng
*/
$counterFile = "/tmp/counter.txt";
function displayCounter($counterFile) {
$fp = fopen($counterFile,"rw");
$num = fgets($fp,5);
$num += 1;
print "您是第 "."$num"." 位无聊份子";
exec( "rm -rf $counterFile");
exec( "echo $num > $counterFile");
}
if (!file_exists($counterFile)) {
exec( "echo 0 > $counterFile");
}
displayCounter($counterFile);
?>
Copyright © 1998 David W. Bettis
在读取到本页时,PHP 程式先找寻 /tmp/counter.txt 档案是否存在,若不存在,则建立一个 counter.txt 档案,然后将 0 写入档案。然后读取 counter.txt 档案的内容,也就是纯文字档,再将内文的数字存入 $num 变数中。在 $num 的变数出现在浏览器前,还有经过加一的动作,让使用者可以增加。当然,如果想灌水,就在加一动作时加二或者加三,不过自欺是无用的。最后将访客人数再回存 /tmp/counter.txt 就一切 OK。
当然,每一页都要这样写,岂不麻烦到了极点。这时,我们可以利用 PHP 提供的 require() 功能,将计数器整理成一个函式,酱子在使用上就方便多多了。
首先要先将 Apache 的设定档 (httpd.conf) 加入 PHP include 档案的路径。例如要设所有的 include 档都在 http://abcdefghijk.com.tw/include 中,可以在 httpd.conf 加入下面的例子
php3_include_path .:./include:../include
别忘了重新启动 Apache 伺服器,新增的 include 路径才有效。
./apachectl restart
再来就在伺服器的 .../include 目录中放入以下的档案,档名存成 counter.inc
下面就是 MyCounter() 函式。为了让读者方便了解,程式中的变数 $counterFile、$fp 及 $num 保持和 David W. Bettis 所设定的计数器中的变数功能相同。
//---------------------------
// 访客计数器函式 MyCounter()
// Author: Wilson Peng
// Copyright (C) 1999
//---------------------------
function MyCounter() {
$counterFile="/tmp".$GLOBALS["PHP_SELF"];
if (!file_exists($counterFile)) {
if (!file_exists(dirname($counterFile))) {
mkdir(dirname($counterFile), 0700);
}
exec("echo 0 > $counterFile");
}
$fp = fopen($counterFile,"rw");
$num = fgets($fp,5);
$num += 1;
print "$num";
echo $counterFile;
exec("rm -rf $counterFile");
exec("echo $num > $counterFile");
}
?>
Copyright © 1999, Wilson Peng
当然,要用的话要加 Homepage 中嵌入 MyCounter() 函式,就可以使用了
require("counter.inc");
?>
访客计数器 最终版
您是第 MyCounter(); ?> 位参观者
Copyright © 1999, Wilson Peng
要用这个 MyCounter() 函式,先在 Homepage 的开头处加入 require() 函式,引入 MyCounter() 函式成为该 Homepage 的一部份。之后再将 MyCounter(); ?> 字串放在需要计数器的地方就可以了。
function MyCounter() {
:
:
}
在建立函式时,需要用上面的格式。在自订函式名称前加入 function 字串。
每页有用到 MyCounter() 的 Homepage 都会在 /tmp 之后加入该页的路径,这可以用 $PHP_SELF 变数达成。
$counterFile="/tmp".$GLOBALS["PHP_SELF"];
当然,若您要将 /tmp 改成别的目录也可以,不然在 SUN 等伺服器,要是 reboot,/tmp 中的东西都没了,要重新开始再计数了。若您不知要使用什么目录,建议使用 /var/log/counter 这个目录,和其它的 log 等变动资料放在一起。
if (!file_exists($counterFile)) {
if (!file_exists(dirname($counterFile))) {
mkdir(dirname($counterFile), 0700);
}
exec("echo 0 > $counterFile");
}
这五行主要是检查 $counterFile 是否存在,若档案不存在则看目录是否存在,决定要不要建立目录。之后就建立档案,并写入 0。
$fp = fopen($counterFile,"rw");
$num = fgets($fp,5);
$num += 1;
print "$num";
echo $counterFile;
这五行就是打开计数器存放的档案,并将它累加后的结果送到浏览器端。
exec("rm -rf $counterFile");
exec("echo $num > $counterFile");
最后将计数器档案删除,再重新建立一个。就完成了这个以档案为基础的纯文字计数器。
使用者认证
在专业的 Web 站台上,常常会需要使用者的帐号及密码,也就是身份确认的动作。早期的 NCSA httpd 伺服器并没有提供这项使用者确认的功能,Webmaster 只能用手工打造一个身份确认的 CGI 程式。
自 CERN httpd 之后的 Web 伺服器大部份都提供了使用者身份确认的功能。仅管每套 Web 伺服器的设定都不太相同,但在设定上都大同小异。
以下就是 Apache 伺服器上的使用者身份确认的设定。
AuthType Basic
AuthName MyMember
AuthUserFile /usr/local/MyMember.txt
Options Includes ExecCGI
require valid-user
在这个例子中,当使用者在看 MyMember 目录下所有的档案,包括图片档案及其它各式档案时,都需要使用者的帐号密码确认。而使用者的帐号及密码档都存在于 /usr/local/MyMember.txt 之中。
这个帐号密码档 /usr/local/MyMember.txt 的样子可能如下例。其中冒号前的字串是使用者帐号,冒号之后的字串是经过不可还原加密的密码,编码一般都是使用传统的 DES 编码,密码的头二个字是类似种子的字元 (salt),本例中都是 3P。每行代表一位使用者。当然 Webmaster 要自行控制重覆帐号的情形。比较特殊是在 Win32 系统上架 Apache 的情形,冒号后的密码不可加密,因为 Win32 没有提供这方面的编码 API,因此使用者密码以明码的方式存在
john1234:3PWudBlJMiwro
queenwan:3PFNVLNPN9W0M
noname00:3PEsXaJx5pk7E
wilson49:3PjoWb0EnaG22
rootboot:3PIt0snI6.84E
sun_moon:3PvymMeNOc.x.
nobody38:3PbskPKwV94hw
在 Apache 1.3.6 版上,可以用 ~apache/bin/htpasswd 来产生单笔的帐号及密码,但对于需要大笔资料的商业站台,可能就需要自行写程式来处理了。UNIX 上需要呼叫 crypt() 来处理编码。
在专业的 Web 站台上,常常会需要使用者的帐号及密码,也就是身份确认的动作。早期的 NCSA httpd 伺服器并没有提供这项使用者确认的功能,Webmaster 只能用手工打造一个身份确认的 CGI 程式。
自 CERN httpd 之后的 Web 伺服器大部份都提供了使用者身份确认的功能。仅管每套 Web 伺服器的设定都不太相同,但在设定上都大同小异。
以下就是 Apache 伺服器上的使用者身份确认的设定。
AuthType Basic
AuthName MyMember
AuthUserFile /usr/local/MyMember.txt
Options Includes ExecCGI
require valid-user
在这个例子中,当使用者在看 MyMember 目录下所有的档案,包括图片档案及其它各式档案时,都需要使用者的帐号密码确认。而使用者的帐号及密码档都存在于 /usr/local/MyMember.txt 之中。
这个帐号密码档 /usr/local/MyMember.txt 的样子可能如下例。其中冒号前的字串是使用者帐号,冒号之后的字串是经过不可还原加密的密码,编码一般都是使用传统的 DES 编码,密码的头二个字是类似种子的字元 (salt),本例中都是 3P。每行代表一位使用者。当然 Webmaster 要自行控制重覆帐号的情形。比较特殊是在 Win32 系统上架 Apache 的情形,冒号后的密码不可加密,因为 Win32 没有提供这方面的编码 API,因此使用者密码以明码的方式存在。
john1234:3PWudBlJMiwro
queenwan:3PFNVLNPN9W0M
noname00:3PEsXaJx5pk7E
wilson49:3PjoWb0EnaG22
rootboot:3PIt0snI6.84E
sun_moon:3PvymMeNOc.x.
nobody38:3PbskPKwV94hw
在 Apache 1.3.6 版上,可以用 ~apache/bin/htpasswd 来产生单笔的帐号及密码,但对于需要大笔资料的商业站台,可能就需要自行写程式来处理了。UNIX 上需要呼叫 crypt() 来处理编码。 在一切都设定好了之后,连线时就会在浏览器出现查核密码的视窗,如上图就是 SEEDNet 的 MySEED 网站的使用者查核机制。在输入了帐号及密码后,浏览器会将它用 BASE64 编码后,传到伺服器端。当然 BASE64 只是编码不是加密,因此在网路上这种传输的安全性仍然不高,还是有可能被中间的刽客截下,再将 BASE64 还原,这也是整个使用者认证中最美中不足的地方,或许日后支援摘要认证 (Digest) 及使用 MD5 编码后,可以解决这种问题。之后每一页仍然需要帐号及密码,只不过浏览器会帮你主动送出,不用再输入帐号密码了。这方面浏览器会保留到被关闭为止,下次重执行浏览器仍需输入第一次。在使用者数量少时,使用上述的方法轻松又省事。但是在使用者有数万人,甚至数十万人时,会发生整个伺服器的效率都被搜寻帐号密码下拖垮,可能读取一页需要数十秒到数分钟。这种情形再使用伺服器提供的密码查核机制就不太明智了。在 Netscape Enterprise Server 上可能就可以使用 NSAPI 来开发自己的查核方式,在 IIS 上也可以用 ISAPI 过滤器开发。写 C/C++ 程式呼叫 NSAPI/ISAPI 总是很累,在 PHP 上有了另外的选择,这也是本节的主题。PHP 的 HTTP 相关函式库提供了 header() 的函式。许多 Web 伺服器与客户端的互动,都可以使用这个函式来变戏法。例如在某个 PHP 页面最开始处,也就是第一行或第二行,加入以下的程式,可以将使用者重导到作者的网页。
header("Location: http://wilson.gs");
exit;
?>
当然,在上述程式之后的 HTML 文字或者是 PHP 程式都永远不会出现在使用者端了。同样的道理,我们就用 header() 来变使用者认证的把戏。可以在 PHP 的最开头送出字串到使用者端,就会在使用者端出现下图的视窗。
Header("WWW-Authenticate: Basic realm=/"Member/"");
Header("HTTP/1.0 401 Unauthorized");
?>
在程式中字串 realm=/"Member/" 中的 Member 字样出现在图中,当然若使用中文字取代,浏览器端也会出现中文字,如上面的 MySEED 图。若 Web 站台使用者还有其它语文,如英文或日文,送出中文的 realm 字串似乎就比较不合适。无论如何,这都要视站台的性质及使用者定位而决定。
当然这还是很粗糙,因为除了送出视窗后,就没有下文了,帐号输入正确也好,输入错误也罢,都不会有任何的结果。我们需要再更进阶的程式来处理。在后端的使用认证上,考虑使用资料库作为储存帐号及密码的后端,在这种架构可以容纳许多的使用者,管它一万个使用者还是十万个使用者。若您的站已有数十万个使用者帐号,那么恭喜您,您的站算是世界级的大站了。MySQL 是个不错的选择,许多站台,甚至是商业化的站台都用它来做后端的资料库。当然您要架真正的商业站台,钱不是问题的话,那可以使用口碑最广的 Oracle 资料库系列。要在 PHP 中使用任何资料库,都要先将资料库的伺服器端及客户端设定好,之后才编译 PHP 及 Apache 系统。准备好 MySQL 及 PHP 之后,先在 MySQL 中加入新的资料库,本例是加入 mymember,用别的名字当然也可以。MySQL 要加入资料库 (Database) 很容易,只要在 MySQL 存放 Database 的地方 mkdir 就可以了。例如在 UNIX Shell 下打hahaha:/usr/local/mysql/data# mkdir mymember在建立了资料库之后,尚需要建立资料表格 (Table) 方能使用。设定的表格如下,可以将它储在 /tmp/memberauth.sql 中CREATE TABLE MemberAuth (
Serial mediumint(9) NOT NULL auto_increment,
Username char(8) NOT NULL,
Password char(8) NOT NULL,
Enable char(1) DEFAULT '0' NOT NULL,
PRIMARY KEY (Serial)
);
档案 memberauth.sql 先看看 memberauth.sql 的这些栏位。Serial 是个自动增加的整数栏位,每输入一笔资料,就会自动加一,这当然不能是空的栏位,于是就用 NOT NULL 了。第二个栏位是 Username,代表使用者的帐号,为了统一以及适应各系统起见,设定成八个字,当然这个栏位也不能是空的。Password 是第三个栏位,为使用者的密码。第四个栏位 Enable 做为帐号是否有效的旗标,设计上 0 表示无用,1 表可用,日后还可加入其它值做不同的用途。设计好了资料表之后,就要将资料表加入资料库了。由于常要使用 MySQL 资料库,可以到 http://www.phpwizard.net/phpMyAdmin 下载 phpMyAdmin,使用浏览器操作及管理 MySQL,轻松又方便。若使用这套 phpMyAdmin 可以在它的使用者介面上输入 memberauth.sql 加入 MySQL 中。或者也可以在 UNIX Shell 下输入下式,也是有同样的效果。
mysql mymember < /tmp/memberauth.sql
在准备好了之后,就可以输入使用者帐号及密码在 memberauth 资料表中了。当然还是使用 phpMyAdmin 方便,用 mysql 程式就要一笔笔的 INSERT 了。接着进入了设计函式的阶段了。
//---------------------------
// 使用者认证函式 auth.inc
// Author: Wilson Peng
// Copyright (C) 1999
//---------------------------
$error401 = "/home/phpdocs/error/401.php";
if ($PHP_AUTH_PW=="") {
Header("WWW-Authenticate: Basic realm=/"超金卡会员/"");
Header("HTTP/1.0 401 Unauthorized");
include($error401);
exit;
} else
{
$db_id = mysql_pconnect("localhost", "myid", "mypw");
$result = mysql_db_query("mymember","select password, enable from MemberAuth where username='$PHP_AUTH_USER'");
$row = mysql_fetch_array($result);
$MemberPasswd = $row[0];
$MemberEnable = $row[1];
if ($MemberEnable==0) {
echo "您的帐号被停用了";
exit;
}
if ($PHP_AUTH_PW!=$MemberPasswd) {
Header("WWW-Authenticate: Basic realm=/"超金卡会员/"");
Header("HTTP/1.0 401 Unauthorized");
include($error401);
exit;
}
}
?>
Copyright (C) 1999, Wilson Peng
要使用这个 auth.inc,要在每个 PHP 的第一行加入
require("auth.inc"); ?> 。在加入本程式的 PHP 档案都会检查帐号密码,图片等就不会检查,比起使用 Web 伺服器功能的某目录下全都检查,PHP 显得有弹性多了。
$error401 = "/home/phpdocs/error/401.php";
这行表示在使用者按下取消,或检查失败时,要显示给使用者看的档案。
if ($PHP_AUTH_PW=="") {
Header("WWW-Authenticate: Basic realm=/"超金卡会员/"");
Header("HTTP/1.0 401 Unauthorized");
include($error401);
exit;
} else {
到 else 之前,若没有传入密码,则送出输入密码的视窗。其中的 $PHP_AUTH_USER、$PHP_AUTH_PW 是 PHP 中特殊的变数,分别代表使用者确认的帐号及密码。上面的程式也是利用这二个变数来处理使用者认证。
$db_id = mysql_pconnect("localhost", "myid", "mypw");
$result = mysql_db_query("mymember","select password, enable from MemberAuth where username='$PHP_AUTH_USER'");
$row = mysql_fetch_array($result);
$MemberPasswd = $row[0];
$MemberEnable = $row[1];
若使用者有输入帐号及密码,则向资料库查询。同时查核该使用者是否仍可使用
if ($MemberEnable==0) {
echo "您的帐号被停用了";
exit;
}
上四行程式为帐号被停用的情形。
if ($PHP_AUTH_PW!=$MemberPasswd) {
Header("WWW-Authenticate: Basic realm=/"超金卡会员/"");
Header("HTTP/1.0 401 Unauthorized");
include($error401);
exit;
}
密码错误则再次向使用者要求输入帐号及密码。
在实际使用时,可以视需要加入的网页再加入 auth.inc 这个档案,就不用连看张图形也要查一次密码,降低伺服器和使用者二端的资源。当然,和 MySQL 的连系上,可以使用 mysql_pconnect() 一直和 MySQL 伺服器连线。或是使用 mysql_connect() 每次重新连线,用这个函式要记得早点使用 mysql_close() 将资料库关闭。下面的程式 auth1.inc 是另一版本的认证程式,就是开启连线后马上关闭,释放资源的例子。
//---------------------------
// 使用者认证函式-1 auth1.inc
// Author: Wilson Peng
// Copyright (C) 1999
//---------------------------
$error401 = "/home/phpdocs/error/401.php";
if ($PHP_AUTH_PW=="") {
Header("WWW-Authenticate: Basic realm=/"超金卡会员/"");
Header("HTTP/1.0 401 Unauthorized");
include($error401);
exit;
} else {
$db_id = mysql_connect("localhost", "myid", "mypw");
$result = mysql_db_query("mymember","select password, enable from MemberAuth where username='$PHP_AUTH_USER'");
$row = mysql_fetch_array($result);
$MemberPasswd = $row[0];
$MemberEnable = $row[1];
mysql_close($db_id);
if ($MemberEnable==0) {
echo "您的帐号被停用了";
exit;
}
if ($PHP_AUTH_PW!=$MemberPasswd) {
Header("WWW-Authenticate: Basic realm=/"超金卡会员/"");
Header("HTTP/1.0 401 Unauthorized");
include($error401);
exit;
}
}
?>
Copyright (C) 1999, Wilson Peng
在实际应用时,可以在资料库中加入更多功能,如使用者分组 (CUG) 的功能;或是加入时间栏位,可做到期检查。其中的变化,端赖设计者的巧思了。
聊天室 聊天室,是 Web 站上打发无聊人士的秘密武器。同时,站长或其它人员也可以在这儿杀时间。甚至发生一段轰轰烈烈的网路恋情呢,就算没有,起码可以增加打字的速度。聊天室,其实就是多人共同使用的 CGI 程式。程式将每个人输入的字串,依系统接收完成的时间整理过后,再送给各个使用者。而 Web 聊天室和 BBS 的聊天室不同的地方是 BBS 聊天室可以每收到一句话,就马上分送给每位在聊天室的网路使用者;Web 由于 CGI 程式不能像 BBS 的 telnet 一直连线,Web CGI 必须以最快的速度将资讯送出,然后结束连线。会形成这种情形,就是因为 Web 聊天室还是使用 HTTP 传输协定,在 HTTP 实作的版本,无论是 0.9、1.0 或是 1.1 版都不能长期占据网路连线的 Port。为了解决资料无法马上传输的问题,及更新讯息的问题,Netscape 在 3.0 版浏览器之后使用了新的技术,而 Internet Explorer 也实作了这些 Netscape 研发出来的技术。Netscape 将它分成 Server Push 及 Client Pull 二种技术。Server Push 由 Web 伺服器将资料以多重 MIME 编码,送给使用者端,目前较少网站使用这种方式;而 Client Pull 则利用了 HTML 的 meta 标签,并利用 http-equiv="Refresh" 的属性,表示资料要重新载入,至于载入时间,则利用 content 属性来达成。 标签通常都放在 .. 的区段中,以便让浏览器可以仅早准备更新使用者端的网页。下面为 meta 和 PHP 合用的例子,设定为每十五秒重新载入一次 如果不用 Server Push 或是 Client Pull 来做聊天室,是否有其它的方法,让 Web 的浏览器能聊天呢?答案是肯定的。可以使用 Java 或是 ActiveX (限 IE4、5) 来做甚至自行开发专属的 Browser Plug-in 程式 (如奇摩的聊天室),不过这就和 PHP 没有关系了,不是我们要的重点除此之外,由于定期更新所有网友的留言,为了怕写了一半因为 refresh 而被清掉尚未写好的字串,因此将聊天室以 frame 的页框技术来做是有必要的。下例就是聊天室的主程式。
聊天室
本聊天室需使用页框,您的浏览器无法使用
程式中以 frame 带出二支 PHP 程式,建议将它们放在同一目录之中,例如 /chatroom,以便日后维护。另外,为了 list.php 及 post.php 可以使用相同的变数,下例将共通的变路路径放在 env.inc 中,可以将它放在 /chatroom 或是 Web 伺服器 (如 Apache) 的 PHP include 设定路径中。
// 档名: env.inc
$tempdir="/tmp/";
$chatfile="/tmp/abc";
?>
聊天室的后端可以设计的很简单,单纯的使用档案来做,也可以弄个资料库,将聊天的内容丢入,若是真的很在意系统效率,或许可以考虑使用 UNIX 的行程通讯 IPC 了。本节即将使用者留言的内容放入档案中,在这儿的例子大部份都使用 UNIX/Linux 的外部指令。若系统无该指令 (或称程式),请自行安装相关程式。实际上将资料丢入档案中会比使用资料库还快,若还很在乎速度,可以在 UNIX 机器中装上 RAM Disk,再将档案的存取路径都设在该 RAM Disk 上,保证存取速度能满足严苛的要求。在有些以高速度搜寻引擎为号召的网站,甚至将整个资料库资料都放到 RAM Disk 中,马上让系统速度提高十倍百倍,而且 RAM 的价格和其它解决方案相比的话还算很便宜。若使用 Windows NT,那就没办法了,看微软什么时候提供,或者用 Third Party 的产品了。有些使用者可能对 UNIX 还不是很熟,在这儿先简介会用到的指令:/
touch: 建立新档案,或修改旧档的最后更新日期。
echo 加上二个大于符号: 将字串显示转向到指定的地方。
tail: 显示档案最后数行的资料,内定值为十行,可使用减号加数字,修改欲显示的行数
下面为送出及处理留言字串的程式,程式用到 env.inc 的档案。
// 档名: post.php
require("env.inc");
if (($chatuser!="") and ($chattext!="")) {
$chatstr="".date("h:i:s")." -".$chatuser." : ".$chattext;
$cmdstr="echo /"".$chatstr."/" >> ".$chatfile;
if (!file_exists($chatfile)) passthru("touch ".$chatfile);
passthru($cmdstr);
}
?>
程式先检查是否有输入字串,若无匿名及发言内容字串则显示发言的表单 (Form),若有资料则将字串及当时时间存入档案中 (利用 UNIX 的转向指令)。当然,为了防止错误,先检查是否有档案可存档,若没有则先 touch 该档,例中的档案就是 /tmp/abc。
// 档名: list.php
require("env.inc");
if (!file_exists($chatfile)) {
echo "尚未开张";
exit;
}
$uniqfile=$tempdir.uniqid(rand());
$shellcmd="/usr/bin/tail -50 ".$chatfile. " > ".$uniqfile;
passthru($shellcmd);
$chatfilearray=file($uniqfile);
$j=count($chatfilearray);
for ($i=1; $i<=$j; $i++) {
echo $chatfilearray[$j-$i]." /n";
}
unlink($uniqfile);
?>