Archive for PHP知识累计

php curl详解

// April 4th, 2010 // 2 Comments » // PHP基础知识累积

目前为目最全的CURL中文说明了,采集用到,原出处不详…
PHP中的CURL函数库(Client URL Library Function)
curl_close — 关闭一个curl会话
curl_copy_handle — 拷贝一个curl连接资源的所有内容和参数
curl_errno — 返回一个包含当前会话错误信息的数字编号
curl_error — 返回一个包含当前会话错误信息的字符串
curl_exec — 执行一个curl会话
curl_getinfo — 获取一个curl连接资源句柄的信息
curl_init — 初始化一个curl会话
curl_multi_add_handle — 向curl批处理会话中添加单独的curl句柄资源
curl_multi_close — 关闭一个批处理句柄资源
curl_multi_exec — 解析一个curl批处理句柄
curl_multi_getcontent — 返回获取的输出的文本流
curl_multi_info_read — 获取当前解析的curl的相关传输信息
curl_multi_init — 初始化一个curl批处理句柄资源
curl_multi_remove_handle — 移除curl批处理句柄资源中的某个句柄资源
curl_multi_select — Get all the sockets associated with the cURL extension, which can then be “selected”
curl_setopt_array — 以数组的形式为一个curl设置会话参数
curl_setopt — 为一个curl设置会话参数
curl_version — 获取curl相关的版本信息
curl_init()函数的作用初始化一个curl会话,curl_init()函数唯一的一个参数是可选的,表示一个url地址。
curl_exec()函数的作用是执行一个curl会话,唯一的参数是curl_init()函数返回的句柄。
curl_close()函数的作用是关闭一个curl会话,唯一的参数是curl_init()函数返回的句柄。
$ch = curl_init(“http://www.baidu.com/“);
curl_exec($ch);
curl_close($ch);
?>
curl_version()函数的作用是获取curl相关的版本信息,curl_version()函数有一个参数,不清楚是做什么的
print_r(curl_version())
?>
curl_getinfo()函数的作用是获取一个curl连接资源句柄的信息,curl_getinfo()函数有两个参数,第一个参数是curl的资源句柄,第二个参数是下面一些常量:
$ch = curl_init(“http://www.baidu.com/“);
print_r(curl_getinfo($ch));
?>
可选的常量包括:
CURLINFO_EFFECTIVE_URL
最后一个有效的url地址
CURLINFO_HTTP_CODE
最后一个收到的HTTP代码
CURLINFO_FILETIME
远程获取文档的时间,如果无法获取,则返回值为“-1”
CURLINFO_TOTAL_TIME
最后一次传输所消耗的时间
CURLINFO_NAMELOOKUP_TIME
名称解析所消耗的时间
CURLINFO_CONNECT_TIME
建立连接所消耗的时间
CURLINFO_PRETRANSFER_TIME
从建立连接到准备传输所使用的时间
CURLINFO_STARTTRANSFER_TIME
从建立连接到传输开始所使用的时间
CURLINFO_REDIRECT_TIME
在事务传输开始前重定向所使用的时间
CURLINFO_SIZE_UPLOAD
上传数据量的总值
CURLINFO_SIZE_DOWNLOAD
下载数据量的总值
CURLINFO_SPEED_DOWNLOAD
平均下载速度
CURLINFO_SPEED_UPLOAD
平均上传速度
CURLINFO_HEADER_SIZE
header部分的大小
CURLINFO_HEADER_OUT
发送请求的字符串
CURLINFO_REQUEST_SIZE
在HTTP请求中有问题的请求的大小
CURLINFO_SSL_VERIFYRESULT
Result of SSL certification verification requested by setting CURLOPT_SSL_VERIFYPEER
CURLINFO_CONTENT_LENGTH_DOWNLOAD
从Content-Length: field中读取的下载内容长度
CURLINFO_CONTENT_LENGTH_UPLOAD
上传内容大小的说明
CURLINFO_CONTENT_TYPE
下载内容的“Content-type”值,NULL表示服务器没有发送有效的“Content-Type: header”
curl_setopt()函数的作用是为一个curl设置会话参数。curl_setopt_array()函数的作用是以数组的形式为一个curl设置会话参数。
$ch = curl_init();
$fp = fopen(“example_homepage.txt“, “w“);
curl_setopt($ch, CURLOPT_FILE, $fp);
$options = array(
CURLOPT_URL => ‘http://www.baidu.com/‘,
CURLOPT_HEADER => false
);
curl_setopt_array($ch, $options);
curl_exec($ch);
curl_close($ch);
fclose($fp);
?>
可设置的参数有:
CURLOPT_AUTOREFERER
自动设置header中的referer信息
CURLOPT_BINARYTRANSFER
在启用CURLOPT_RETURNTRANSFER时候将获取数据返回
CURLOPT_COOKIESESSION
启用时curl会仅仅传递一个session cookie,忽略其他的cookie,默认状况下curl会将所有的cookie返回给服务端。session cookie是指那些用来判断服务器端的session是否有效而存在的cookie。
CURLOPT_CRLF
启用时将Unix的换行符转换成回车换行符。
CURLOPT_DNS_USE_GLOBAL_CACHE
启用时会启用一个全局的DNS缓存,此项为线程安全的,并且默认为true。
CURLOPT_FAILONERROR
显示HTTP状态码,默认行为是忽略编号小于等于400的HTTP信息
CURLOPT_FILETIME
启用时会尝试修改远程文档中的信息。结果信息会通过curl_getinfo()函数的CURLINFO_FILETIME选项返回。
CURLOPT_FOLLOWLOCATION
启用时会将服务器服务器返回的“Location:”放在header中递归的返回给服务器,使用CURLOPT_MAXREDIRS可以限定递归返回的数量。
CURLOPT_FORBID_REUSE
在完成交互以后强迫断开连接,不能重用。
CURLOPT_FRESH_CONNECT
强制获取一个新的连接,替代缓存中的连接。
CURLOPT_FTP_USE_EPRT
TRUE to use EPRT (and LPRT) when doing active FTP downloads. Use FALSE to disable EPRT and LPRT and use PORT only.
Added in PHP 5.0.0.
CURLOPT_FTP_USE_EPSV
TRUE to first try an EPSV command for FTP transfers before reverting back to PASV. Set to FALSE to disable EPSV.
CURLOPT_FTPAPPEND
TRUE to append to the remote file instead of overwriting it.
CURLOPT_FTPASCII
An alias of CURLOPT_TRANSFERTEXT. Use that instead.
CURLOPT_FTPLISTONLY
TRUE to only list the names of an FTP directory.
CURLOPT_HEADER
启用时会将头文件的信息作为数据流输出。
CURLOPT_HTTPGET
启用时会设置HTTP的method为GET,因为GET是默认是,所以只在被修改的情况下使用。
CURLOPT_HTTPPROXYTUNNEL
启用时会通过HTTP代理来传输。
CURLOPT_MUTE
讲curl函数中所有修改过的参数恢复默认值。
CURLOPT_NETRC
在连接建立以后,访问~/.netrc文件获取用户名和密码信息连接远程站点。
CURLOPT_NOBODY
启用时将不对HTML中的body部分进行输出。
CURLOPT_NOPROGRESS
启用时关闭curl传输的进度条,此项的默认设置为true
CURLOPT_NOSIGNAL
启用时忽略所有的curl传递给php进行的信号。在SAPI多线程传输时此项被默认打开。
CURLOPT_POST
启用时会发送一个常规的POST请求,类型为:application/x-www-form-urlencoded,就像表单提交的一样。
CURLOPT_PUT
启用时允许HTTP发送文件,必须同时设置CURLOPT_INFILE和CURLOPT_INFILESIZE
CURLOPT_RETURNTRANSFER
讲curl_exec()获取的信息以文件流的形式返回,而不是直接输出。
CURLOPT_SSL_VERIFYPEER
FALSE to stop cURL from verifying the peer’s certificate. Alternate certificates to verify against can be specified with the CURLOPT_CAINFO option or a certificate directory can be specified with the CURLOPT_CAPATH option. CURLOPT_SSL_VERIFYHOST may also need to be TRUE or FALSE if CURLOPT_SSL_VERIFYPEER is disabled (it defaults to 2). TRUE by default as of cURL 7.10. Default bundle installed as of cURL 7.10.
CURLOPT_TRANSFERTEXT
TRUE to use ASCII mode for FTP transfers. For LDAP, it retrieves data in plain text instead of HTML. On Windows systems, it will not set STDOUT to binary mode.
CURLOPT_UNRESTRICTED_AUTH
在使用CURLOPT_FOLLOWLOCATION产生的header中的多个locations中持续追加用户名和密码信息,即使域名已发生改变。
CURLOPT_UPLOAD
启用时允许文件传输
CURLOPT_VERBOSE
启用时会汇报所有的信息,存放在STDERR或指定的CURLOPT_STDERR中
CURLOPT_BUFFERSIZE
每次获取的数据中读入缓存的大小,这个值每次都会被填满。
CURLOPT_CLOSEPOLICY
不是CURLCLOSEPOLICY_LEAST_RECENTLY_USED就是CURLCLOSEPOLICY_OLDEST,还存在另外三个,但是curl暂时还不支持。.
CURLOPT_CONNECTTIMEOUT
在发起连接前等待的时间,如果设置为0,则不等待。
CURLOPT_DNS_CACHE_TIMEOUT
设置在内存中保存DNS信息的时间,默认为120秒。
CURLOPT_FTPSSLAUTH
The FTP authentication method (when is activated): CURLFTPAUTH_SSL (try SSL first), CURLFTPAUTH_TLS (try TLS first), or CURLFTPAUTH_DEFAULT (let cURL decide).
CURLOPT_HTTP_VERSION
设置curl使用的HTTP协议,CURL_HTTP_VERSION_NONE(让curl自己判断),CURL_HTTP_VERSION_1_0(HTTP/1.0),CURL_HTTP_VERSION_1_1(HTTP/1.1)
CURLOPT_HTTPAUTH
使用的HTTP验证方法,可选的值 有:CURLAUTH_BASIC,CURLAUTH_DIGEST,CURLAUTH_GSSNEGOTIATE,CURLAUTH_NTLM,CURLAUTH_ANY,CURLAUTH_ANYSAFE, 可以使用“|”操作符分隔多个值,curl让服务器选择一个支持最好的值,CURLAUTH_ANY等价于CURLAUTH_BASIC | CURLAUTH_DIGEST | CURLAUTH_GSSNEGOTIATE | CURLAUTH_NTLM,CURLAUTH_ANYSAFE等价于CURLAUTH_DIGEST | CURLAUTH_GSSNEGOTIATE | CURLAUTH_NTLM
CURLOPT_INFILESIZE
设定上传文件的大小
CURLOPT_LOW_SPEED_LIMIT
当传输速度小于CURLOPT_LOW_SPEED_LIMIT时,PHP会根据CURLOPT_LOW_SPEED_TIME来判断是否因太慢而取消传输。
CURLOPT_LOW_SPEED_TIME
The number of seconds the transfer should be below CURLOPT_LOW_SPEED_LIMIT for PHP to consider the transfer too slow and abort.
当传输速度小于CURLOPT_LOW_SPEED_LIMIT时,PHP会根据CURLOPT_LOW_SPEED_TIME来判断是否因太慢而取消传输。
CURLOPT_MAXCONNECTS
允许的最大连接数量,超过是会通过CURLOPT_CLOSEPOLICY决定应该停止哪些连接
CURLOPT_MAXREDIRS
指定最多的HTTP重定向的数量,这个选项是和CURLOPT_FOLLOWLOCATION一起使用的。
CURLOPT_PORT
一个可选的用来指定连接端口的量
CURLOPT_PROXYAUTH
The HTTP authentication method(s) to use for the proxy connection. Use the same bitmasks as described in CURLOPT_HTTPAUTH. For proxy authentication, only CURLAUTH_BASIC and CURLAUTH_NTLM are currently supported.
CURLOPT_PROXYPORT
The port number of the proxy to connect to. This port number can also be set in CURLOPT_PROXY.
CURLOPT_PROXYTYPE
Either CURLPROXY_HTTP (default) or CURLPROXY_SOCKS5.
CURLOPT_RESUME_FROM
在恢复传输时传递一个字节偏移量(用来断点续传)
CURLOPT_SSL_VERIFYHOST
1 to check the existence of a common name in the SSL peer certificate.
2 to check the existence of a common name and also verify that it matches the hostname provided.
CURLOPT_SSLVERSION
The SSL version (2 or 3) to use. By default PHP will try to determine this itself, although in some cases this must be set manually.
CURLOPT_TIMECONDITION
如果在CURLOPT_TIMEVALUE指定的某个时间以后被编辑过,则使用CURL_TIMECOND_IFMODSINCE返回页面,如果没有被修 改过,并且CURLOPT_HEADER为true,则返回一个”304 Not Modified”的header,CURLOPT_HEADER为false,则使用CURL_TIMECOND_ISUNMODSINCE,默认值为 CURL_TIMECOND_IFMODSINCE
CURLOPT_TIMEOUT
设置curl允许执行的最长秒数
CURLOPT_TIMEVALUE
设置一个CURLOPT_TIMECONDITION使用的时间戳,在默认状态下使用的是CURL_TIMECOND_IFMODSINCE
CURLOPT_CAINFO
The name of a file holding one or more certificates to verify the peer with. This only makes sense when used in combination with CURLOPT_SSL_VERIFYPEER.
CURLOPT_CAPATH
A directory that holds multiple CA certificates. Use this option alongside CURLOPT_SSL_VERIFYPEER.
CURLOPT_COOKIE
设定HTTP请求中“Set-Cookie:”部分的内容。
CURLOPT_COOKIEFILE
包含cookie信息的文件名称,这个cookie文件可以是Netscape格式或者HTTP风格的header信息。
CURLOPT_COOKIEJAR
连接关闭以后,存放cookie信息的文件名称
CURLOPT_CUSTOMREQUEST
A custom request method to use instead of “GET” or “HEAD” when doing a HTTP request. This is useful for doing “DELETE” or other, more obscure HTTP requests. Valid values are things like “GET”, “POST”, “CONNECT” and so on; i.e. Do not enter a whole HTTP request line here. For instance, entering “GET /index.html HTTP/1.0\r\n\r\n” would be incorrect.
Note: Don’t do this without making sure the server supports the custom request method first.
CURLOPT_EGBSOCKET
Like CURLOPT_RANDOM_FILE, except a filename to an Entropy Gathering Daemon socket.
CURLOPT_ENCODING
header中“Accept-Encoding: ”部分的内容,支持的编码格式为:”identity”,”deflate”,”gzip”。如果设置为空字符串,则表示支持所有的编码格式
CURLOPT_FTPPORT
The value which will be used to get the IP address to use for the FTP “POST” instruction. The “POST” instruction tells the remote server to connect to our specified IP address. The string may be a plain IP address, a hostname, a network interface name (under Unix), or just a plain ‘-’ to use the systems default IP address.
CURLOPT_INTERFACE
在外部网络接口中使用的名称,可以是一个接口名,IP或者主机名。
CURLOPT_KRB4LEVEL
KRB4(Kerberos 4)安全级别的设置,可以是一下几个值之一:”clear”,”safe”,”confidential”,”private”。默认的值 为”private”,设置为null的时候表示禁用KRB4,现在KRB4安全仅能在FTP传输中使用。
CURLOPT_POSTFIELDS
在HTTP中的“POST”操作。如果要传送一个文件,需要一个@开头的文件名
CURLOPT_PROXY
设置通过的HTTP代理服务器
CURLOPT_PROXYUSERPWD
连接到代理服务器的,格式为“[username]:[password]”的用户名和密码。
CURLOPT_RANDOM_FILE
设定存放SSL用到的随机数种子的文件名称
CURLOPT_RANGE
设置HTTP传输范围,可以用“X-Y”的形式设置一个传输区间,如果有多个HTTP传输,则使用逗号分隔多个值,形如:”X-Y,N-M”。
CURLOPT_REFERER
设置header中”Referer: ” 部分的值。
CURLOPT_SSL_CIPHER_LIST
A list of ciphers to use for SSL. For example, RC4-SHA and TLSv1 are valid cipher lists.
CURLOPT_SSLCERT
传递一个包含PEM格式证书的字符串。
CURLOPT_SSLCERTPASSWD
传递一个包含使用CURLOPT_SSLCERT证书必需的密码。
CURLOPT_SSLCERTTYPE
The format of the certificate. Supported formats are “PEM” (default), “DER”, and “ENG”.
CURLOPT_SSLENGINE
The identifier for the crypto engine of the private SSL key specified in CURLOPT_SSLKEY.
CURLOPT_SSLENGINE_DEFAULT
The identifier for the crypto engine used for asymmetric crypto operations.
CURLOPT_SSLKEY
The name of a file containing a private SSL key.
CURLOPT_SSLKEYPASSWD
The secret password needed to use the private SSL key specified in CURLOPT_SSLKEY.
Note: Since this option contains a sensitive password, remember to keep the PHP script it is contained within safe.
CURLOPT_SSLKEYTYPE
The key type of the private SSL key specified in CURLOPT_SSLKEY. Supported key types are “PEM” (default), “DER”, and “ENG”.
CURLOPT_URL
需要获取的URL地址,也可以在PHP的curl_init()函数中设置。
CURLOPT_USERAGENT
在HTTP请求中包含一个”user-agent”头的字符串。
CURLOPT_USERPWD
传递一个连接中需要的用户名和密码,格式为:“[username]:[password]”。
CURLOPT_HTTP200ALIASES
设置不再以error的形式来处理HTTP 200的响应,格式为一个数组。
CURLOPT_HTTPHEADER
设置一个header中传输内容的数组。
CURLOPT_POSTQUOTE
An array of FTP commands to execute on the server after the FTP request has been performed.
CURLOPT_QUOTE
An array of FTP commands to execute on the server prior to the FTP request.
CURLOPT_FILE
设置输出文件的位置,值是一个资源类型,默认为STDOUT (浏览器)。
CURLOPT_INFILE
在上传文件的时候需要读取的文件地址,值是一个资源类型。
CURLOPT_STDERR
设置一个错误输出地址,值是一个资源类型,取代默认的STDERR。
CURLOPT_WRITEHEADER
设置header部分内容的写入的文件地址,值是一个资源类型。
CURLOPT_HEADERFUNCTION
设置一个回调函数,这个函数有两个参数,第一个是curl的资源句柄,第二个是输出的header数据。header数据的输出必须依赖这个函数,返回已写入的数据大小。
CURLOPT_PASSWDFUNCTION
设置一个回调函数,有三个参数,第一个是curl的资源句柄,第二个是一个密码提示符,第三个参数是密码长度允许的最大值。返回密码的值。
CURLOPT_READFUNCTION
设置一个回调函数,有两个参数,第一个是curl的资源句柄,第二个是读取到的数据。数据读取必须依赖这个函数。返回读取数据的大小,比如0或者EOF。
CURLOPT_WRITEFUNCTION
设置一个回调函数,有两个参数,第一个是curl的资源句柄,第二个是写入的数据。数据写入必须依赖这个函数。返回精确的已写入数据的大小
curl_copy_handle()函数的作用是拷贝一个curl连接资源的所有内容和参数
$ch = curl_init(“http://www.baidu.com/“);
$another = curl_copy_handle($ch);
curl_exec($another);
curl_close($another);
?>
curl_error()函数的作用是返回一个包含当前会话错误信息的字符串。
curl_errno()函数的作用是返回一个包含当前会话错误信息的数字编号。
curl_multi_init()函数的作用是初始化一个curl批处理句柄资源。
curl_multi_add_handle()函数的作用是向curl批处理会话中添加单独的curl句柄资源。curl_multi_add_handle()函数有两个参数,第一个参数表示一个curl批处理句柄资源,第二个参数表示一个单独的curl句柄资源。
curl_multi_exec()函数的作用是解析一个curl批处理句柄,curl_multi_exec()函数有两个参数,第一个参数表示一个批处理句柄资源,第二个参数是一个引用值的参数,表示剩余需要处理的单个的curl句柄资源数量。
curl_multi_remove_handle()函数表示移除curl批处理句柄资源中的某个句柄资源,curl_multi_remove_handle()函数有两个参数,第一个参数表示一个curl批处理句柄资源,第二个参数表示一个单独的curl句柄资源。
curl_multi_close()函数的作用是关闭一个批处理句柄资源。
$ch1 = curl_init();
$ch2 = curl_init();
curl_setopt($ch1, CURLOPT_URL, “http://www.baidu.com/“);
curl_setopt($ch1, CURLOPT_HEADER, 0);
curl_setopt($ch2, CURLOPT_URL, “http://www.google.com/“);
curl_setopt($ch2, CURLOPT_HEADER, 0);
$mh = curl_multi_init();
curl_multi_add_handle($mh,$ch1);
curl_multi_add_handle($mh,$ch2);
do {
curl_multi_exec($mh,$flag);
} while ($flag > 0);
curl_multi_remove_handle($mh,$ch1);
curl_multi_remove_handle($mh,$ch2);
curl_multi_close($mh);
?>
curl_multi_getcontent()函数的作用是在设置了CURLOPT_RETURNTRANSFER的情况下,返回获取的输出的文本流。
curl_multi_info_read()函数的作用是获取当前解析的curl的相关传输信息。
curl_multi_select()
Get all the sockets associated with the cURL extension, which can then be “selected”

目前为目最全的CURL中文说明了,采集用到,原出处不详…
PHP中的CURL函数库(Client URL Library Function)
curl_close — 关闭一个curl会话curl_copy_handle — 拷贝一个curl连接资源的所有内容和参数curl_errno — 返回一个包含当前会话错误信息的数字编号curl_error — 返回一个包含当前会话错误信息的字符串curl_exec — 执行一个curl会话curl_getinfo — 获取一个curl连接资源句柄的信息curl_init — 初始化一个curl会话curl_multi_add_handle — 向curl批处理会话中添加单独的curl句柄资源curl_multi_close — 关闭一个批处理句柄资源curl_multi_exec — 解析一个curl批处理句柄curl_multi_getcontent — 返回获取的输出的文本流curl_multi_info_read — 获取当前解析的curl的相关传输信息curl_multi_init — 初始化一个curl批处理句柄资源curl_multi_remove_handle — 移除curl批处理句柄资源中的某个句柄资源curl_multi_select — Get all the sockets associated with the cURL extension, which can then be “selected”curl_setopt_array — 以数组的形式为一个curl设置会话参数curl_setopt — 为一个curl设置会话参数curl_version — 获取curl相关的版本信息
curl_init()函数的作用初始化一个curl会话,curl_init()函数唯一的一个参数是可选的,表示一个url地址。curl_exec()函数的作用是执行一个curl会话,唯一的参数是curl_init()函数返回的句柄。curl_close()函数的作用是关闭一个curl会话,唯一的参数是curl_init()函数返回的句柄。

$ch = curl_init(“http://www.baidu.com/“);curl_exec($ch);curl_close($ch);?>curl_version()函数的作用是获取curl相关的版本信息,curl_version()函数有一个参数,不清楚是做什么的

print_r(curl_version())?>curl_getinfo()函数的作用是获取一个curl连接资源句柄的信息,curl_getinfo()函数有两个参数,第一个参数是curl的资源句柄,第二个参数是下面一些常量:

$ch = curl_init(“http://www.baidu.com/“);print_r(curl_getinfo($ch));?>可选的常量包括:
CURLINFO_EFFECTIVE_URL最后一个有效的url地址
CURLINFO_HTTP_CODE最后一个收到的HTTP代码
CURLINFO_FILETIME远程获取文档的时间,如果无法获取,则返回值为“-1”
CURLINFO_TOTAL_TIME最后一次传输所消耗的时间
CURLINFO_NAMELOOKUP_TIME名称解析所消耗的时间
CURLINFO_CONNECT_TIME建立连接所消耗的时间
CURLINFO_PRETRANSFER_TIME从建立连接到准备传输所使用的时间
CURLINFO_STARTTRANSFER_TIME从建立连接到传输开始所使用的时间
CURLINFO_REDIRECT_TIME在事务传输开始前重定向所使用的时间
CURLINFO_SIZE_UPLOAD上传数据量的总值
CURLINFO_SIZE_DOWNLOAD下载数据量的总值
CURLINFO_SPEED_DOWNLOAD平均下载速度
CURLINFO_SPEED_UPLOAD平均上传速度
CURLINFO_HEADER_SIZEheader部分的大小
CURLINFO_HEADER_OUT发送请求的字符串
CURLINFO_REQUEST_SIZE在HTTP请求中有问题的请求的大小
CURLINFO_SSL_VERIFYRESULTResult of SSL certification verification requested by setting CURLOPT_SSL_VERIFYPEER
CURLINFO_CONTENT_LENGTH_DOWNLOAD从Content-Length: field中读取的下载内容长度
CURLINFO_CONTENT_LENGTH_UPLOAD上传内容大小的说明
CURLINFO_CONTENT_TYPE下载内容的“Content-type”值,NULL表示服务器没有发送有效的“Content-Type: header”
curl_setopt()函数的作用是为一个curl设置会话参数。curl_setopt_array()函数的作用是以数组的形式为一个curl设置会话参数。

$ch = curl_init();$fp = fopen(“example_homepage.txt“, “w“);curl_setopt($ch, CURLOPT_FILE, $fp);$options = array(CURLOPT_URL => ‘http://www.baidu.com/‘,CURLOPT_HEADER => false);curl_setopt_array($ch, $options);curl_exec($ch);curl_close($ch);fclose($fp);?>可设置的参数有:
CURLOPT_AUTOREFERER自动设置header中的referer信息
CURLOPT_BINARYTRANSFER在启用CURLOPT_RETURNTRANSFER时候将获取数据返回
CURLOPT_COOKIESESSION启用时curl会仅仅传递一个session cookie,忽略其他的cookie,默认状况下curl会将所有的cookie返回给服务端。session cookie是指那些用来判断服务器端的session是否有效而存在的cookie。
CURLOPT_CRLF启用时将Unix的换行符转换成回车换行符。
CURLOPT_DNS_USE_GLOBAL_CACHE启用时会启用一个全局的DNS缓存,此项为线程安全的,并且默认为true。
CURLOPT_FAILONERROR显示HTTP状态码,默认行为是忽略编号小于等于400的HTTP信息
CURLOPT_FILETIME启用时会尝试修改远程文档中的信息。结果信息会通过curl_getinfo()函数的CURLINFO_FILETIME选项返回。
CURLOPT_FOLLOWLOCATION启用时会将服务器服务器返回的“Location:”放在header中递归的返回给服务器,使用CURLOPT_MAXREDIRS可以限定递归返回的数量。
CURLOPT_FORBID_REUSE在完成交互以后强迫断开连接,不能重用。
CURLOPT_FRESH_CONNECT强制获取一个新的连接,替代缓存中的连接。
CURLOPT_FTP_USE_EPRTTRUE to use EPRT (and LPRT) when doing active FTP downloads. Use FALSE to disable EPRT and LPRT and use PORT only.Added in PHP 5.0.0.
CURLOPT_FTP_USE_EPSVTRUE to first try an EPSV command for FTP transfers before reverting back to PASV. Set to FALSE to disable EPSV.
CURLOPT_FTPAPPENDTRUE to append to the remote file instead of overwriting it.
CURLOPT_FTPASCIIAn alias of CURLOPT_TRANSFERTEXT. Use that instead.
CURLOPT_FTPLISTONLYTRUE to only list the names of an FTP directory.
CURLOPT_HEADER启用时会将头文件的信息作为数据流输出。
CURLOPT_HTTPGET启用时会设置HTTP的method为GET,因为GET是默认是,所以只在被修改的情况下使用。
CURLOPT_HTTPPROXYTUNNEL启用时会通过HTTP代理来传输。
CURLOPT_MUTE讲curl函数中所有修改过的参数恢复默认值。
CURLOPT_NETRC在连接建立以后,访问~/.netrc文件获取用户名和密码信息连接远程站点。
CURLOPT_NOBODY启用时将不对HTML中的body部分进行输出。
CURLOPT_NOPROGRESS启用时关闭curl传输的进度条,此项的默认设置为true
CURLOPT_NOSIGNAL启用时忽略所有的curl传递给php进行的信号。在SAPI多线程传输时此项被默认打开。
CURLOPT_POST启用时会发送一个常规的POST请求,类型为:application/x-www-form-urlencoded,就像表单提交的一样。
CURLOPT_PUT启用时允许HTTP发送文件,必须同时设置CURLOPT_INFILE和CURLOPT_INFILESIZE
CURLOPT_RETURNTRANSFER讲curl_exec()获取的信息以文件流的形式返回,而不是直接输出。
CURLOPT_SSL_VERIFYPEERFALSE to stop cURL from verifying the peer’s certificate. Alternate certificates to verify against can be specified with the CURLOPT_CAINFO option or a certificate directory can be specified with the CURLOPT_CAPATH option. CURLOPT_SSL_VERIFYHOST may also need to be TRUE or FALSE if CURLOPT_SSL_VERIFYPEER is disabled (it defaults to 2). TRUE by default as of cURL 7.10. Default bundle installed as of cURL 7.10.
CURLOPT_TRANSFERTEXTTRUE to use ASCII mode for FTP transfers. For LDAP, it retrieves data in plain text instead of HTML. On Windows systems, it will not set STDOUT to binary mode.
CURLOPT_UNRESTRICTED_AUTH在使用CURLOPT_FOLLOWLOCATION产生的header中的多个locations中持续追加用户名和密码信息,即使域名已发生改变。
CURLOPT_UPLOAD启用时允许文件传输
CURLOPT_VERBOSE启用时会汇报所有的信息,存放在STDERR或指定的CURLOPT_STDERR中
CURLOPT_BUFFERSIZE每次获取的数据中读入缓存的大小,这个值每次都会被填满。
CURLOPT_CLOSEPOLICY不是CURLCLOSEPOLICY_LEAST_RECENTLY_USED就是CURLCLOSEPOLICY_OLDEST,还存在另外三个,但是curl暂时还不支持。.
CURLOPT_CONNECTTIMEOUT在发起连接前等待的时间,如果设置为0,则不等待。
CURLOPT_DNS_CACHE_TIMEOUT设置在内存中保存DNS信息的时间,默认为120秒。
CURLOPT_FTPSSLAUTHThe FTP authentication method (when is activated): CURLFTPAUTH_SSL (try SSL first), CURLFTPAUTH_TLS (try TLS first), or CURLFTPAUTH_DEFAULT (let cURL decide).
CURLOPT_HTTP_VERSION设置curl使用的HTTP协议,CURL_HTTP_VERSION_NONE(让curl自己判断),CURL_HTTP_VERSION_1_0(HTTP/1.0),CURL_HTTP_VERSION_1_1(HTTP/1.1)
CURLOPT_HTTPAUTH使用的HTTP验证方法,可选的值 有:CURLAUTH_BASIC,CURLAUTH_DIGEST,CURLAUTH_GSSNEGOTIATE,CURLAUTH_NTLM,CURLAUTH_ANY,CURLAUTH_ANYSAFE, 可以使用“|”操作符分隔多个值,curl让服务器选择一个支持最好的值,CURLAUTH_ANY等价于CURLAUTH_BASIC | CURLAUTH_DIGEST | CURLAUTH_GSSNEGOTIATE | CURLAUTH_NTLM,CURLAUTH_ANYSAFE等价于CURLAUTH_DIGEST | CURLAUTH_GSSNEGOTIATE | CURLAUTH_NTLM
CURLOPT_INFILESIZE设定上传文件的大小
CURLOPT_LOW_SPEED_LIMIT当传输速度小于CURLOPT_LOW_SPEED_LIMIT时,PHP会根据CURLOPT_LOW_SPEED_TIME来判断是否因太慢而取消传输。
CURLOPT_LOW_SPEED_TIMEThe number of seconds the transfer should be below CURLOPT_LOW_SPEED_LIMIT for PHP to consider the transfer too slow and abort.当传输速度小于CURLOPT_LOW_SPEED_LIMIT时,PHP会根据CURLOPT_LOW_SPEED_TIME来判断是否因太慢而取消传输。
CURLOPT_MAXCONNECTS允许的最大连接数量,超过是会通过CURLOPT_CLOSEPOLICY决定应该停止哪些连接
CURLOPT_MAXREDIRS指定最多的HTTP重定向的数量,这个选项是和CURLOPT_FOLLOWLOCATION一起使用的。
CURLOPT_PORT一个可选的用来指定连接端口的量
CURLOPT_PROXYAUTHThe HTTP authentication method(s) to use for the proxy connection. Use the same bitmasks as described in CURLOPT_HTTPAUTH. For proxy authentication, only CURLAUTH_BASIC and CURLAUTH_NTLM are currently supported.
CURLOPT_PROXYPORTThe port number of the proxy to connect to. This port number can also be set in CURLOPT_PROXY.
CURLOPT_PROXYTYPEEither CURLPROXY_HTTP (default) or CURLPROXY_SOCKS5.
CURLOPT_RESUME_FROM在恢复传输时传递一个字节偏移量(用来断点续传)
CURLOPT_SSL_VERIFYHOST1 to check the existence of a common name in the SSL peer certificate.2 to check the existence of a common name and also verify that it matches the hostname provided.
CURLOPT_SSLVERSIONThe SSL version (2 or 3) to use. By default PHP will try to determine this itself, although in some cases this must be set manually.
CURLOPT_TIMECONDITION如果在CURLOPT_TIMEVALUE指定的某个时间以后被编辑过,则使用CURL_TIMECOND_IFMODSINCE返回页面,如果没有被修 改过,并且CURLOPT_HEADER为true,则返回一个”304 Not Modified”的header,CURLOPT_HEADER为false,则使用CURL_TIMECOND_ISUNMODSINCE,默认值为 CURL_TIMECOND_IFMODSINCE
CURLOPT_TIMEOUT设置curl允许执行的最长秒数
CURLOPT_TIMEVALUE设置一个CURLOPT_TIMECONDITION使用的时间戳,在默认状态下使用的是CURL_TIMECOND_IFMODSINCE
CURLOPT_CAINFOThe name of a file holding one or more certificates to verify the peer with. This only makes sense when used in combination with CURLOPT_SSL_VERIFYPEER.
CURLOPT_CAPATHA directory that holds multiple CA certificates. Use this option alongside CURLOPT_SSL_VERIFYPEER.
CURLOPT_COOKIE设定HTTP请求中“Set-Cookie:”部分的内容。
CURLOPT_COOKIEFILE包含cookie信息的文件名称,这个cookie文件可以是Netscape格式或者HTTP风格的header信息。
CURLOPT_COOKIEJAR连接关闭以后,存放cookie信息的文件名称
CURLOPT_CUSTOMREQUESTA custom request method to use instead of “GET” or “HEAD” when doing a HTTP request. This is useful for doing “DELETE” or other, more obscure HTTP requests. Valid values are things like “GET”, “POST”, “CONNECT” and so on; i.e. Do not enter a whole HTTP request line here. For instance, entering “GET /index.html HTTP/1.0\r\n\r\n” would be incorrect.Note: Don’t do this without making sure the server supports the custom request method first.
CURLOPT_EGBSOCKETLike CURLOPT_RANDOM_FILE, except a filename to an Entropy Gathering Daemon socket.
CURLOPT_ENCODINGheader中“Accept-Encoding: ”部分的内容,支持的编码格式为:”identity”,”deflate”,”gzip”。如果设置为空字符串,则表示支持所有的编码格式
CURLOPT_FTPPORTThe value which will be used to get the IP address to use for the FTP “POST” instruction. The “POST” instruction tells the remote server to connect to our specified IP address. The string may be a plain IP address, a hostname, a network interface name (under Unix), or just a plain ‘-’ to use the systems default IP address.
CURLOPT_INTERFACE在外部网络接口中使用的名称,可以是一个接口名,IP或者主机名。
CURLOPT_KRB4LEVELKRB4(Kerberos 4)安全级别的设置,可以是一下几个值之一:”clear”,”safe”,”confidential”,”private”。默认的值 为”private”,设置为null的时候表示禁用KRB4,现在KRB4安全仅能在FTP传输中使用。
CURLOPT_POSTFIELDS在HTTP中的“POST”操作。如果要传送一个文件,需要一个@开头的文件名
CURLOPT_PROXY设置通过的HTTP代理服务器
CURLOPT_PROXYUSERPWD连接到代理服务器的,格式为“[username]:[password]”的用户名和密码。
CURLOPT_RANDOM_FILE设定存放SSL用到的随机数种子的文件名称
CURLOPT_RANGE设置HTTP传输范围,可以用“X-Y”的形式设置一个传输区间,如果有多个HTTP传输,则使用逗号分隔多个值,形如:”X-Y,N-M”。
CURLOPT_REFERER设置header中”Referer: ” 部分的值。
CURLOPT_SSL_CIPHER_LISTA list of ciphers to use for SSL. For example, RC4-SHA and TLSv1 are valid cipher lists.
CURLOPT_SSLCERT传递一个包含PEM格式证书的字符串。
CURLOPT_SSLCERTPASSWD传递一个包含使用CURLOPT_SSLCERT证书必需的密码。
CURLOPT_SSLCERTTYPEThe format of the certificate. Supported formats are “PEM” (default), “DER”, and “ENG”.
CURLOPT_SSLENGINEThe identifier for the crypto engine of the private SSL key specified in CURLOPT_SSLKEY.
CURLOPT_SSLENGINE_DEFAULTThe identifier for the crypto engine used for asymmetric crypto operations.
CURLOPT_SSLKEYThe name of a file containing a private SSL key.
CURLOPT_SSLKEYPASSWDThe secret password needed to use the private SSL key specified in CURLOPT_SSLKEY.Note: Since this option contains a sensitive password, remember to keep the PHP script it is contained within safe.
CURLOPT_SSLKEYTYPEThe key type of the private SSL key specified in CURLOPT_SSLKEY. Supported key types are “PEM” (default), “DER”, and “ENG”.
CURLOPT_URL需要获取的URL地址,也可以在PHP的curl_init()函数中设置。
CURLOPT_USERAGENT在HTTP请求中包含一个”user-agent”头的字符串。
CURLOPT_USERPWD传递一个连接中需要的用户名和密码,格式为:“[username]:[password]”。
CURLOPT_HTTP200ALIASES设置不再以error的形式来处理HTTP 200的响应,格式为一个数组。
CURLOPT_HTTPHEADER设置一个header中传输内容的数组。
CURLOPT_POSTQUOTEAn array of FTP commands to execute on the server after the FTP request has been performed.
CURLOPT_QUOTEAn array of FTP commands to execute on the server prior to the FTP request.
CURLOPT_FILE设置输出文件的位置,值是一个资源类型,默认为STDOUT (浏览器)。
CURLOPT_INFILE在上传文件的时候需要读取的文件地址,值是一个资源类型。
CURLOPT_STDERR设置一个错误输出地址,值是一个资源类型,取代默认的STDERR。
CURLOPT_WRITEHEADER设置header部分内容的写入的文件地址,值是一个资源类型。
CURLOPT_HEADERFUNCTION设置一个回调函数,这个函数有两个参数,第一个是curl的资源句柄,第二个是输出的header数据。header数据的输出必须依赖这个函数,返回已写入的数据大小。
CURLOPT_PASSWDFUNCTION设置一个回调函数,有三个参数,第一个是curl的资源句柄,第二个是一个密码提示符,第三个参数是密码长度允许的最大值。返回密码的值。
CURLOPT_READFUNCTION设置一个回调函数,有两个参数,第一个是curl的资源句柄,第二个是读取到的数据。数据读取必须依赖这个函数。返回读取数据的大小,比如0或者EOF。
CURLOPT_WRITEFUNCTION设置一个回调函数,有两个参数,第一个是curl的资源句柄,第二个是写入的数据。数据写入必须依赖这个函数。返回精确的已写入数据的大小
curl_copy_handle()函数的作用是拷贝一个curl连接资源的所有内容和参数

$ch = curl_init(“http://www.baidu.com/“);$another = curl_copy_handle($ch);curl_exec($another);curl_close($another);?>curl_error()函数的作用是返回一个包含当前会话错误信息的字符串。curl_errno()函数的作用是返回一个包含当前会话错误信息的数字编号。
curl_multi_init()函数的作用是初始化一个curl批处理句柄资源。curl_multi_add_handle()函数的作用是向curl批处理会话中添加单独的curl句柄资源。curl_multi_add_handle()函数有两个参数,第一个参数表示一个curl批处理句柄资源,第二个参数表示一个单独的curl句柄资源。curl_multi_exec()函数的作用是解析一个curl批处理句柄,curl_multi_exec()函数有两个参数,第一个参数表示一个批处理句柄资源,第二个参数是一个引用值的参数,表示剩余需要处理的单个的curl句柄资源数量。curl_multi_remove_handle()函数表示移除curl批处理句柄资源中的某个句柄资源,curl_multi_remove_handle()函数有两个参数,第一个参数表示一个curl批处理句柄资源,第二个参数表示一个单独的curl句柄资源。curl_multi_close()函数的作用是关闭一个批处理句柄资源。

$ch1 = curl_init();$ch2 = curl_init();curl_setopt($ch1, CURLOPT_URL, “http://www.baidu.com/“);curl_setopt($ch1, CURLOPT_HEADER, 0);curl_setopt($ch2, CURLOPT_URL, “http://www.google.com/“);curl_setopt($ch2, CURLOPT_HEADER, 0);$mh = curl_multi_init();curl_multi_add_handle($mh,$ch1);curl_multi_add_handle($mh,$ch2);do {curl_multi_exec($mh,$flag);} while ($flag > 0);curl_multi_remove_handle($mh,$ch1);curl_multi_remove_handle($mh,$ch2);curl_multi_close($mh);?>curl_multi_getcontent()函数的作用是在设置了CURLOPT_RETURNTRANSFER的情况下,返回获取的输出的文本流。
curl_multi_info_read()函数的作用是获取当前解析的curl的相关传输信息。
curl_multi_select()Get all the sockets associated with the cURL extension, which can then be “selected”

[整理] 页面跳转代码

// January 14th, 2010 // No Comments » // PHP基础知识累积, javascript and CSS

这年头,真是好记性不如烂笔头。学的快,忘的快。刚才工作中要用到页面跳转,却又记不清楚了。
故特意整理了一下,用做以后参考。
第一篇: JavaScript 跳转
方法一:
<script language=”javascript”>
window.location = “http://www.baidu.com”;
</script>
方法二:
<script language=”javascript”>
document.location = “http://www.baidu.com”;
</script>
方法三: (带进度条)
<html>
<head>
<meta http-equiv=”Content-Language” content=”zh-cn”>
<meta HTTP-EQUIV=”Content-Type” CONTENT=”text/html; charset=gb2312″>
<title>跳转到baidu.com</title>
</head>
<body>
<form name=loading>
<P align=center><FONT face=Arial color=#0066ff size=2>loading…</FONT>
<INPUT style=”PADDING-RIGHT: 0px; PADDING-LEFT: 0px; FONT-WEIGHT: bolder; PADDING-BOTTOM: 0px; COLOR: #0066ff; BORDER-TOP-style: none; PADDING-TOP: 0px; FONT-FAMILY: Arial; BORDER-RIGHT-style: none; BORDER-LEFT-style: none; BACKGROUND-COLOR: white; BORDER-BOTTOM-style: none”
size=46 name=chart>
<BR>
<INPUT style=”BORDER-RIGHT: medium none; BORDER-TOP: medium none; BORDER-LEFT: medium none; COLOR: #0066ff; BORDER-BOTTOM: medium none; TEXT-ALIGN: center” size=47 name=percent>
<script language=”javascript”>
var bar=0
var line=”||”
var amount=”||”
count()
function count(){
bar=bar+2
amount =amount + line
document.loading.chart.value=amount
document.loading.percent.value=bar+”%”
if (bar<99){
setTimeout(“count()”,100);
}else{
window.location = “http://www.baidu.com/”;
}
}
</script>
</P>
</form>
</body>
</html>
第二篇: 页面跳转
<head>
<meta http-equiv=”refresh” content=”10; url=http://www.baidu.com”>
</head>
第三篇: 动态页面跳转
方法一: PHP 跳转
<?php
header(“location: http://www.baidu.com”);
?>
方法二: ASP 跳转
<%
response.redirect “http://www.baidu.com”
%>
FYI:
<%
Dim ID1
Dim ID2
dim str
ID1 = Request(“forumID”)
ID2 = Request(“threadID”)
str=”/blog/threadview.asp?forumID=”& ID1 &”&threadID=” & ID2
response.redirect str
%>

这年头,真是好记性不如烂笔头。学的快,忘的快。刚才工作中要用到页面跳转,却又记不清楚了。
故特意整理了一下,用做以后参考。
第一篇: JavaScript 跳转
方法一:
<script language=”javascript”>    window.location = “http://www.baidu.com”;</script>
方法二:
<script language=”javascript”>    document.location = “http://www.baidu.com”;</script>
方法三: (带进度条)
<html><head><meta http-equiv=”Content-Language” content=”zh-cn”><meta HTTP-EQUIV=”Content-Type” CONTENT=”text/html; charset=gb2312″><title>跳转到baidu.com</title></head><body><form name=loading><P align=center><FONT face=Arial color=#0066ff size=2>loading…</FONT><INPUT style=”PADDING-RIGHT: 0px; PADDING-LEFT: 0px; FONT-WEIGHT: bolder; PADDING-BOTTOM: 0px; COLOR: #0066ff; BORDER-TOP-style: none; PADDING-TOP: 0px; FONT-FAMILY: Arial; BORDER-RIGHT-style: none; BORDER-LEFT-style: none; BACKGROUND-COLOR: white; BORDER-BOTTOM-style: none”size=46 name=chart><BR><INPUT style=”BORDER-RIGHT: medium none; BORDER-TOP: medium none; BORDER-LEFT: medium none; COLOR: #0066ff; BORDER-BOTTOM: medium none; TEXT-ALIGN: center” size=47 name=percent><script language=”javascript”>var bar=0var line=”||”var amount=”||”count()function count(){    bar=bar+2    amount =amount + line    document.loading.chart.value=amount    document.loading.percent.value=bar+”%”    if (bar<99){        setTimeout(“count()”,100);    }else{        window.location = “http://www.baidu.com/”;    }}</script></P></form></body></html>
第二篇: 页面跳转
<head><meta http-equiv=”refresh” content=”10; url=http://www.baidu.com”></head>
第三篇: 动态页面跳转
方法一: PHP 跳转
<?phpheader(“location: http://www.baidu.com”);?>
方法二: ASP 跳转
<%response.redirect “http://www.baidu.com”%>
FYI:<%Dim ID1Dim ID2dim strID1 = Request(“forumID”)ID2 = Request(“threadID”)str=”/blog/threadview.asp?forumID=”& ID1 &”&threadID=” & ID2response.redirect str%>

Zend_Db_Table Relationships 翻译和自己的注释

// January 10th, 2010 // No Comments » // Zend Framework 累积

下面是在读Zend手册9.8 Zend_Db_Table Relationships时,对重要的地方的摘录,翻译,还有很多自己的解释。由于时间有限,就不全部翻译了:

介绍:

在RDBMS中,表之间有着各种关系,有一多对应,多多对应等等。
Zend框架提供了一些方法来方便我们实现这些关系。

定义关系:

下面是本文用的例子的关系定义:

<?php
class Accounts extends Zend_Db_Table_Abstract
{
protected $_name            = ‘accounts’;
protected $_dependentTables = array(‘Bugs’);
}

class
Products extends Zend_Db_Table_Abstract
{
protected $_name            = ‘products’;
protected $_dependentTables = array(‘BugsProducts’);
}

class
Bugs extends Zend_Db_Table_Abstract
{
protected $_name            = ‘bugs’;

protected
$_dependentTables = array(‘BugsProducts’);

protected
$_referenceMap    = array(
‘Reporter’ => array(
‘columns’           => ‘reported_by’,
‘refTableClass’     => ‘Accounts’,
‘refColumns’        => ‘account_name’
),
‘Engineer’ => array(
‘columns’           => ‘assigned_to’,
‘refTableClass’     => ‘Accounts’,
‘refColumns’        => ‘account_name’
),
‘Verifier’ => array(
‘columns’           => array(‘verified_by’),
‘refTableClass’     => ‘Accounts’,
‘refColumns’        => array(‘account_name’)
)
);
}

class
BugsProducts extends Zend_Db_Table_Abstract
{
protected $_name = ‘bugs_products’;

protected
$_referenceMap    = array(
‘Bug’ => array(
‘columns’           => array(‘bug_id’),
‘refTableClass’     => ‘Bugs’,
‘refColumns’        => array(‘bug_id’)
),
‘Product’ => array(
‘columns’           => array(‘product_id’),
‘refTableClass’     => ‘Products’,
‘refColumns’        => array(‘product_id’)
)
);

}

我们看到例子中定义了四个类:Accounts,Products,Bugs,BugsProducts。其中Accounts,Products和Bugs是三个实体表,而BugsProducts是关系表。
我们再来分析一下这三个实体,一个Account有多个Bug,他们之间是一对多的关系,而Bug和Product是多对多的关系。
$_dependentTables是一个与该对象关联的对象名,这里注意,要写对象名而不是关联的数据库名。
$_referenceMap 数组用来定义和其他表的关系,在这里可以设置和那些表有关系,有什么样的关系。第一个设置的是Rule Key,也就是上面例子的’Reporter’, ‘Engineer’之类的。Rule Key的作用其实就是一个关系的名字,并不需要和其他数据库表名或者其他对象名的名字一样。只是为了标记的,在后面的时候,我们可以看到这个Rule Key的作用。

每一个Rule下面都有如下的一些定义:(没有特殊说明,都以如上’Reporter’关系进行说明)

* columns=> 设置和别的表关联的字段名,如上的’report_by’就是数据库中表Bugs的report_by字段。这里只有一个字段,也可以设置多个字段。
* refTableClass=>用于设置与这个表发生关系的表。这里要注意,一定使用目标表的对象的名字而不是表名字,例子中就和’Account’对象发生了关联。
* refColumns =>设置发生联系的表的字段。可以写多个,如果和多个字段发生联系的话,这里要和columns对应。这个设置其实是可选的,如果为空,关联字段自动被设置成为关联表的主键。上面例子中并没有使用主键作为关联字段,所以手动设置。
* onDelete=>可选字段,设置当删除是的动作。
* onUpdate=>可选字段,设置当更新表时的动作。

以上定义关系。

从关联表中取数据:

如果我们已经得到了一个查询结果,我们可以通过一下语句去取得这个结果相关联的表的查询结果:

$row->findDependentRowset($table, [$rule]);

这个方法一般使用与一多对应的两个实体表中,在多多对应的两个实体表和一个关系表如何从一个实体表取出另一个实体表的数据,我们会在下面叙述。
第一个字段$table是指和这个表想相联系的表对应的类名。第二个字段是可选的,是我们刚刚说到的rule key,就是这个关系的名字,如果省略,则默认为这个表中的第一个关系。下面是例子:

<?php
$accountsTable      = new Accounts();
$accountsRowset     = $accountsTable->find(1234);
$user1234           = $accountsRowset->current();

$bugsReportedByUser = $user1234->findDependentRowset(‘Bugs’);

例子中,我们先读取了一个编号为1234的用户,然后去查找这个家伙报了什么bug,由于zend默认是第一个关联,所以这里和Account发生关联的第一个就是’Reporter,所以就取出了Reporter的记录。

如果我们想取出其他的记录,比如Engineer,可以按照下面的办法:

<?php
$accountsTable      = new Accounts();
$accountsRowset     = $accountsTable->find(1234);
$user1234           = $accountsRowset->current();

$bugsAssignedToUser = $user1234->findDependentRowset(‘Bugs’, ‘Engineer’);

除了使用findDependentRowset之外,我们还可以使用叫做“魔术方法”(Magic Method)的机制。之所以这么叫,就是因为好像是在变魔术一样。所以方法findDependentRowset(‘<TableClass>’, ‘<Rule>’)就可以等价于如下:
- $row->find<TableClass>()
- $row->find<TableClass>By<Rule>()

注:这个机制是我们最一开始是在Ruby on Rails里面看到的。这里的<TableClass>和<Rule>一定要使用和相关联的类名以及关联名(Rule Key)完全一样的名字,才可以生效。下面是例子:

<?php
$accountsTable    = new Accounts();
$accountsRowset   = $accountsTable->find(1234);
$user1234         = $accountsRowset->current();

// Use the default reference rule
$bugsReportedBy   = $user1234->findBugs();

// Specify the reference rule
$bugsAssignedTo   = $user1234->findBugsByEngineer();

从父表取得字段:

刚刚我们介绍了一多关系中的从一去多的方法,现在我们反过来,从多取一,其实是从多中的一个取他相对应的那个记录。
类似的我们有这样的语句:

$row->findParentRow($table, [$rule]);

类似的,$table为类名,而可选参数$rule填入对应的Rule Key。下面是例子:

<?php
$bugsTable         = new Bugs();
$bugsRowset        = $bugsTable->fetchAll(array(‘bug_status = ?’ => ‘NEW’));
$bug1              = $bugsRowset->current();

$reporter          = $bug1->findParentRow(‘Accounts’);

和上面不太一样的是,上面返回的是一个多个记录的集合,而这次返回的必然是一条记录。下面的例子是设置Rule:

<?php
$bugsTable         = new Bugs();
$bugsRowset        = $bugsTable->fetchAll(‘bug_status = ?’, ‘NEW’);
$bug1              = $bugsRowset->current();

$engineer          = $bug1->findParentRow(‘Accounts’, ‘Engineer’);

只需要吧Rule填入就好了。相似的,这个方法也有“魔术字段”。findParentRow(‘<TableClass>’, ‘<Rule>’)对应:
- $row->findParent<TableClass>()
- $row->findParent<TableClass>By<Rule>()
例子:

<?php
$bugsTable         = new Bugs();
$bugsRowset        = $bugsTable->fetchAll(‘bug_status = ?’, ‘NEW’);
$bug1              = $bugsRowset->current();

// Use the default reference rule
$reporter          = $bug1->findParentAccounts();

// Specify the reference rule
$engineer          = $bug1->findParentAccountsByEngineer();

取得多对多关系表的字段:

上面两个方法讲述了一对多的使用,下面就是多对多了。我们使用如下方法取得多对多关系表的数据:

$row->findManyToManyRowset($table, $intersectionTable, [$rule1, [$rule2]]);

这里参数变成了4个,因为需要增加一个关系表来存储多对多的关系。
$table是与之发生多对多关系的表的类名,$intersectionTable是中间存储关系的关系表的类名。$rule1和$rule2是上面两个数据表的Rule Key。省略Rule Key的例子如下:

<?php
$bugsTable        = new Bugs();
$bugsRowset       = $bugsTable->find(1234);
$bug1234          = $bugsRowset->current();

$productsRowset   = $bug1234->findManyToManyRowset(‘Products’, ‘BugsProducts’);

下面是该方法的全部参数调用例子:

<?php
$bugsTable        = new Bugs();
$bugsRowset       = $bugsTable->find(1234);
$bug1234          = $bugsRowset->current();

$productsRowset   = $bug1234->findManyToManyRowset(‘Products’, ‘BugsProducts’, ‘Bug’);

这次的“魔术方法”是,对应 findManyToManyRowset(‘<TableClass>’, ‘<IntersectionTableClass>’, ‘<Rule1>’, ‘<Rule2>’)
- $row->find<TableClass>Via<IntersectionTableClass>()
- $row->find<TableClass>Via<IntersectionTableClass>By<Rule1>()
- $row->find<TableClass>Via<IntersectionTableClass>By<Rule1>And<Rule2>()

例子:

<?php
$bugsTable        = new Bugs();
$bugsRowset       = $bugsTable->find(1234);
$bug1234          = $bugsRowset->current();

// Use the default reference rule
$products          = $bug1234->findProductsViaBugsProducts();

// Specify the reference rule
$products          = $bug1234->findProductsViaBugsProductsByBug();

如何使用Zend_Form组件来实现表单提交,并且让错误提示信息为中文显示.

// January 6th, 2010 // No Comments » // Zend Framework 累积

今天因为在这里看到一篇帖子, 我也就找了一下怎样实现..没想到还真被我给搞事实上了….同时公司又要开发一个群组功能..我也就想运用一下Zend_Form来实现创建群组的功能.主要还是看中Zend_Form可以在写Form时候.实现服务器端的验证功能..省得我们在把数据提交到数据库的时候再验证一次..所以呢.我就看了一下这方面的手册..通过Zend Framework手册找到了相关的使用说明…最简单的使用方式就是在控制器(Controller)里写一个现成的Action,这样..在这个控制器里就可以直接使用这个Action…代码可以如下:

<?php
public    function   formAction()
{
$form=new Zend_Form();
$form->setName(‘group’)
$title = new Zend_Form_Element_Select(‘title’);
$title ->setLabel(‘性别’)
->setMultiOptions(array(‘mr’=>’Mr’, ‘mrs’=>’Mrs’))
->setRequired(true)
->addValidator(‘NotEmpty’, true);
$yourName = new Zend_Form_Element_Text(‘firstName’);
$yourName->setLabel(‘姓名’)
->setRequired(true)
->addValidator(‘NotEmpty’, true) ;
$email = new Zend_Form_Element_Text(‘email’);
$email->setLabel(‘电子邮件地址’)
->addFilter(‘StringToLower’)
->setRequired(false)
->addValidator(‘NotEmpty’);

$submit = new Zend_Form_Element_Submit(‘submit’);
$submit->setLabel(‘group’);
$form->addElements(array($title, $yourName,$email,$submit));
}
?>

当然..我也可以把这个Form专门写成一个类…存放在一个forms共同的目录下.这样就方便我们管理我们所有的Form表单..我的实现方式就是把它放在和控制器(Controller)的同一级别的目录下…这样管理起来也方便..当然不同的朋友..有不同的想法…另一种方式..就是可以把它写成View Helper…这个方式实现起来,,也很方便..这里我就不想多写了…Zend Framework实现起来很方便…只要你想的到…无论你怎样完成你的任务,,都是可以的..在这里我就不多说其它的…我只想谈一下怎样让 Zend_Form实现中文的提示信息功能…我这里有二种方法..
第一:比较笨的方式就是:如果你的网站不要做成多国语言的网站..同时你的Zend Framework版本不是经常更换的话…你就可以找到相关提示信息的源码…更改成中文的提示.
这个笨方法..实在是没有办法的办法…呵呵…
第二:我也是在英文站…看到的一个比较好的方式,就是通过重写这个提示信息.把它换成我们想要的语言…这样…就算我们会去换语言..或是换Zend Framework的版本..
对我们的影响也不是很大…我们只要更改一下我们的Form的表单就可以搞定了..现在这种方式的代码如下(我这里只写了Email提示信息..其它的不要多写出):

<?php
$email = new Zend_Form_Element_Text(‘email’);
$email->setLabel(‘电子邮件地址’)
->addFilter(‘StringToLower’)
->setRequired(false)
->addValidator(‘NotEmpty’)
->addValidator(‘EmailAddress’,true,array(‘messages’ => array(
‘emailAddressInvalid’ => ‘这不是一个可用的电子邮件!’,
‘emailAddressInvalidHostname’ => ‘这不是一个有效的主机名!’,
‘emailAddressInvalidMxRecord’ => ‘这不是一个有效的电子邮件地址!’,
‘emailAddressDotAtom’ => ‘这不是一个有效的电子邮件地址!’,
‘emailAddressQuotedString’ => ‘这不是一个有效的电子邮件地址!’,
‘emailAddressInvalidLocalPart’ => ‘这不是一个有效的电子邮件地址!’,)));

?>

到这里..Zend_Form这个组件还有一个比较重要的功能..就是Zend_Form_Decorator..手册上称为装饰器,也就是说你可以自己写你想要的装饰器..比如说..你要把你的Form用Table包含起来..我们要怎样实现呢?这个时候..我们就要用到比如说 HtmlTag,Label这些装饰器来达到我们想要的功能…这里是一个比较重要的概念了..有兴趣的朋友可以去去看一下…因为如果你想要用 Zend_Form这个组件..不会装饰器因该用起来会很困难..所以必须要会这个东西..才可以创建你自己想要的表单功能..最后..就是一点装饰器的小运用
我只是实现一个小的功能…如下代码:

<?php
$email = new Zend_Form_Element_Text(‘email’);
$email->setLabel(‘电子邮件地址’)
->addFilter(‘StringToLower’)
->setRequired(false)
//利用装饰器来增加td标签
->addDecorator(‘HtmlTag’, array(‘tag’ => ‘td’))
->addDecorator(‘Label’, array(‘tag’ => ‘td’))
//重复利用HtmlTag装饰器来增加tr标签
->addDecorator(array(‘FooTr’ => ‘HtmlTag’), array(‘tag’ => ‘tr’))
->addValidator(‘NotEmpty’);
?>

哈哈….大致的运用就是这样了…最后..就是验证提交的数据了…看如何验检验用户提交的数据….这里就不多说了…OK…

是快一个星期没有更新我的这个博客了..今天因为在这里看到一篇帖子, 我也就找了一下怎样实现..没想到还真被我给搞事实上了….同时公司又要开发一个群组功能..我也就想运用一下Zend_Form来实现创建群组的功 能.主要还是看中Zend_Form可以在写Form时候.实现服务器端的验证功能..省得我们在把数据提交到数据库的时候再验证一次..所以呢.我就看 了一下这方面的手册..通过Zend Framework手册找到了相关的使用说明…最简单的使用方式就是在控制器(Controller)里写一个现成的Action,这样..在这个控 制器里就可以直接使用这个Action…代码可以如下:

<?php
public	function   formAction()
{
 $form=new Zend_Form();
 $form->setName('group')
 $title = new Zend_Form_Element_Select('title');
 $title ->setLabel('性别')
        ->setMultiOptions(array('mr'=>'Mr', 'mrs'=>'Mrs'))
	->setRequired(true)
        ->addValidator('NotEmpty', true);
 $yourName = new Zend_Form_Element_Text('firstName');
 $yourName->setLabel('姓名')
          ->setRequired(true)
          ->addValidator('NotEmpty', true) ;
 $email = new Zend_Form_Element_Text('email');
 $email->setLabel('电子邮件地址')
       ->addFilter('StringToLower')
       ->setRequired(false)
       ->addValidator('NotEmpty');
 
 $submit = new Zend_Form_Element_Submit('submit');
 $submit->setLabel('group');
 $form->addElements(array($title, $yourName,$email,$submit));
}
?>

当然..我也可以把这个Form专门写成一个类…存放在一个forms共同的目录下.这样就方便我们管理我们所有的Form表单..我的实现方式就是 把它放在和控制器(Controller)的同一级别的目录下…这样管理起来也方便..当然不同的朋友..有不同的想法…另一种方式..就是可以 把它写成View Helper…这个方式实现起来,,也很方便..这里我就不想多写了…Zend Framework实现起来很方便…只要你想的到…无论你怎样完成你的任务,,都是可以的..在这里我就不多说其它的…我只想谈一下怎样让 Zend_Form实现中文的提示信息功能…我这里有二种方法..
第一:比较笨的方式就是:如果你的网站不要做成多国语言的网站..同时你的Zend Framework版本不是经常更换的话…你就可以找到相关提示信息的源码…更改成中文的提示.
这个笨方法..实在是没有办法的办法…呵呵…
第二:我也是在英文站…看到的一个比较好的方式,就是通过重写这个提示信息.把它换成我们想要的语言…这样…就算我们会去换语言..或是换Zend Framework的版本..
对我们的影响也不是很大…我们只要更改一下我们的Form的表单就可以搞定了..现在这种方式的代码如下(我这里只写了Email提示信息..其它的不要多写出):

<?php
$email = new Zend_Form_Element_Text('email');
$email->setLabel('电子邮件地址')
    ->addFilter('StringToLower')
    ->setRequired(false)
    ->addValidator('NotEmpty')
    ->addValidator('EmailAddress',true,array('messages' => array(
       'emailAddressInvalid' => '这不是一个可用的电子邮件!',
       'emailAddressInvalidHostname' => '这不是一个有效的主机名!',
       'emailAddressInvalidMxRecord' => '这不是一个有效的电子邮件地址!',
       'emailAddressDotAtom' => '这不是一个有效的电子邮件地址!',
       'emailAddressQuotedString' => '这不是一个有效的电子邮件地址!',
       'emailAddressInvalidLocalPart' => '这不是一个有效的电子邮件地址!',)));
 
?>

到这里..Zend_Form这个组件还有一个比较重要的功能..就是Zend_Form_Decorator..手册上称为装饰器,也就是说你可以自己 写你想要的装饰器..比如说..你要把你的Form用Table包含起来..我们要怎样实现呢?这个时候..我们就要用到比如说 HtmlTag,Label这些装饰器来达到我们想要的功能…这里是一个比较重要的概念了..有兴趣的朋友可以去去看一下…因为如果你想要用 Zend_Form这个组件..不会装饰器因该用起来会很困难..所以必须要会这个东西..才可以创建你自己想要的表单功能..最后..就是一点装饰器的 小运用
我只是实现一个小的功能…如下代码:

<?php
$email = new Zend_Form_Element_Text('email');
$email->setLabel('电子邮件地址')
   ->addFilter('StringToLower')
   ->setRequired(false)
   //利用装饰器来增加td标签
   ->addDecorator('HtmlTag', array('tag' => 'td'))
   ->addDecorator('Label', array('tag' => 'td'))
   //重复利用HtmlTag装饰器来增加tr标签
   ->addDecorator(array('FooTr' => 'HtmlTag'), array('tag' => 'tr'))
   ->addValidator('NotEmpty');
?>

哈哈….大致的运用就是这样了…最后..就是验证提交的数据了…看如何验检验用户提交的数据….这里就不多说了…OK…

用Zend_Auth实现Session身份持久认证

// January 5th, 2010 // No Comments » // Zend Framework 累积

今天用Zend Framework搞了一下身份持久认证…

手册里头提供了四种认证方式,一个是session,一个是数据库表,一个摘要式,最后一愕是HTTP认证适配器。我在实现session认证的时候(按照手册上的代码),起初发现了一个问题,无法验证…后来仔细看了一下,手册上的代码缺少了点东西…

Zend_Auth结合Zend_Auth_Storage_Session实现session的持久身份认证,主要有一下几点:

1、先做个适配器,也就是自己用来验证用户的类,如:

class AuthAdapter implements Zend_Auth_Adapter_Interface{
var $username;
var $password;
/**
* Sets username andpassword for authentication
* @return void
*/
public function __construct($username=”, $password=”){//
$this->username = $username;
$this->password = $password;
}
/**
* Performs an authentication attempt
* @throws Zend_Auth_Adapter_Exception If authentication cannot be performed
* @return Zend_Auth_Result
* Zend_Auth_Result::SUCCESS
* Zend_Auth_Result::FAILURE
* Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND
* Zend_Auth_Result::FAILURE_IDENTITY_AMBIGUOUS
* Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID
* Zend_Auth_Result::FAILURE_UNCATEGORIZED
*/
public function authenticate(){
$aryInfo;
if (isset($this->username) && (isset($this->password))){
$aryInfo[0] = _e(’验证正确’);
return new Zend_Auth_Result(1,$aryInfo);
}else{
$aryInfo[0] = _e(’验证失败’);
return new Zend_Auth_Result(-1,$aryInfo);
}
}
}

2、在获取一个Zend_Auth的实例

$auth = Zend_Auth::getInstance();
3、创建认证session的命名空间,并放到Zend_Auth的实例的存储器中
$auth->setStorage(new Zend_Auth_Storage_Session('someNamespace'));
4、加入上面的认证适配器实例:

include (’../classes/authadapter.class.php’);
$authAdapter = new AuthAdapter(’usrname’, ‘pwd’);
$result = $this->_auth->authenticate($authAdapter);

5、判断验证结果:

if (!$result->isValid()) {
foreach ($result->getMessages() as $message){
echo “$message\n”;
}
}else{

foreach ($result->getIdentity() as $message){
echo “$message\n”;
}
}

如果在其他页面去验证的时候直接调用一个方法就可以验证了:

$auth = Zend_Auth::getInstance();
$auth->setStorage(new Zend_Auth_Storage_Session(’sessionAuth’));
if ($auth->hasIdentity()){
$identity = $auth->getIdentity();
echo ‘认证’;
}else{
echo ‘未认证’;
foreach ($auth->getIdentity() as $message){
echo “$message\n”;
}

其实按照我的理解,采用什么样的方式去验证,只要在适配器验证方法中去决定就可以了…

对于身份持久,最好与ACL配合好使用才好…否则对于权限和资源的访问控制还是会有很多问题…

还有一个重要的问题,从根本上来讲,无论那种验证方式,还都是和session有很大关系的,除非在每次验证的时候都去调用验证的原始方法,而不是只根据验证session中取出来的值,当然,session中的值会作为验证信息的依据。

另外几种验证方法,再看看,也许会有更好更合适的,到时候再分享。

PS:有个重要需要搞清楚的问题,用这样的认证方式是否能够判断用户在线时长…

PHP 计算时间差 (转)

// December 31st, 2009 // No Comments » // PHP基础知识累积, PHP知识累计

<?php
/**
* 时间差计算
*
* @param Timestamp $time
* @return String Time Elapsed
* @author Shelley Shyan
* @copyright http://phparch.cn (Professional PHP Architecture)
*/
function time2Units ($time)
{
$year   = floor($time / 60 / 60 / 24 / 365);
$time  -= $year * 60 * 60 * 24 * 365;
$month  = floor($time / 60 / 60 / 24 / 30);
$time  -= $month * 60 * 60 * 24 * 30;
$week   = floor($time / 60 / 60 / 24 / 7);
$time  -= $week * 60 * 60 * 24 * 7;
$day    = floor($time / 60 / 60 / 24);
$time  -= $day * 60 * 60 * 24;
$hour   = floor($time / 60 / 60);
$time  -= $hour * 60 * 60;
$minute = floor($time / 60);
$time  -= $minute * 60;
$second = $time;
$elapse = ”;

$unitArr = array(‘年’  =>’year’, ’个月’=>’month’,  ’周’=>’week’, ’天’=>’day’,
‘小时’=>’hour’, ’分钟’=>’minute’, ’秒’=>’second’
);

foreach ( $unitArr as $cn => $u )
{
if ( $$u > 0 )
{
$elapse = $$u . $cn;
break;
}
}

return $elapse;
}

$past = 2052345678; // Some timestamp in the past
$now  = time();     // Current timestamp
$diff = $now - $past;

echo ’发表于’ . time2Units($diff) . ’前’;
?>

Zend Framework之Zend_ACL

// December 24th, 2009 // No Comments » // Zend Framework 累积

在Zend_Acl當中有兩個重要的部份:Resource(資源) 與 Role(角色),其中「角色」存取「資源」,舉個白話的例子,在一個進出受管制的電梯大樓,小明可以進入101樓的辦公室,表示小明這個「角色」,他的 通行證可以刷卡進入101樓的電梯,進入101樓這個「資源」。

比較常見的是網站管理系統的權限實作,例如編輯部的同仁只能用網站的內容編輯系統,廣告部則可以使用廣告刊播系統以及報表系統,但沒有使用內容編輯的權限。那在ZF中,Zend_Acl如何實作這樣的概念呢?

一、建立資源:
Zend_Acl裡有個Zend_Acl_Resource_Interface這個介面可以方便的來建立資源。

實作自這個介面的類別,會有一個getResourceId()這個方法,代表它是一個資源

另外,Zend_Acl_Resource類別中也包含一些Zend_Acl的一些實作,讓開發者可以直接繼承來使用。

Zend_Acl使用樹狀結構的方式來管理資源,透過這個樹狀結構,可以新增新的資源進來,並儲存在結構中。

對樹狀結構上層的修改,亦可影響至下層,不過即使下層不想繼承上層的規則,也可以簡單的加上一些例外規則,在這結構中,Resource只能一次繼承自一個Parent,不過每個Parent都可以有各自的Parent,以此類推。

二、建立角色:
透 過Zend_Acl_Role_Interface的實作,建立角色也是相當容易的。如同資源的建立,角色可透過實作的getRoleId()方法來建 立。同Zend_Acl_Resource,Zend_Acl裡也有個Zend_Acl_Role的類別可以直接繼承來使用。

不過與資源不同的,一個角色可以一次繼承自一個或多個角色。

以上面那個網站管理系統的例子來說,假設eddie可能只是編輯部的同事(單一角色),而joanne是企劃部門的主管,她除了管企劃部門的案子之外,亦可能需要看一下網站的新聞是不是有錯字,則可以說她的角色同時屬於”企劃部”及”編輯部”(多重角色)。

其 實透過角色繼承的方式,可以在設計系統時不用指定特定某個人的使用權限。例如設定”編輯部”的權限是可以用內容管理系統,然後只要讓eddie”屬於”這 個部門,eddie這個帳號也可以有使用的權限了;若同像上面joanne的例子,只要讓她同時”屬於”企劃部跟編輯部即可,在設定上相當簡潔且方便。

我們以上面這個例子來實作一小段程式碼,設定三種角色:編輯部editor、企劃部planner以及網站系統管理員administrator。

<?php
require_once ‘Zend/Acl.php’;
require_once ‘Zend/Acl/Role.php’;
require_once ‘Zend/Acl/Resource.php’;

//建立一個Access Control List(ACL)物件
$acl = new Zend_Acl();

//在ACL中建立三個角色:editor、planner及administrator
$acl->addRole(new Zend_Acl_Role(‘editor’))
->addRole(new Zend_Acl_Role(‘planner’))
->addRole(new Zend_Acl_Role(‘administrator’));

//建立joanne角色,並設定此角色同時屬於editor及planner兩個角色
$group1 = array(‘editor’, ‘planner’);
$acl->addRole(new Zend_Acl_Role(‘joanne’), $group1);

//在ACL中建立三個資源:內容管理系統ContentManager、廣告系統ADManager及系統管理SystemAdmin
$acl->add(new Zend_Acl_Resource(‘ContentManager’))
->add(new Zend_Acl_Resource(‘ADManager’))
->add(new Zend_Acl_Resource(‘SystemAdmin’));

//設定角色與資源之關係
$acl->allow(‘editor’, ‘ContentManager’);     //editor角色允許使用ContentManager資源
$acl->allow(‘planner’, ‘ADManager’);          //planner角色允許使用ADManager資源
$acl->allow(‘administrator’, ‘SystemAdmin’); //administrator角色允許使用SystemAdmin資源

//測試權限
//joanne不屬administrator角色….
echo $acl->isAllowed(‘joanne’, ‘SystemAdmin’)?’allowed’:'denined’;     //得到denined

//joanne屬於editor角色…
echo $acl->isAllowed(‘joanne’, ‘ContentManager’)?’allowed’:'denined’;   //得到allowed

上面這段程式碼中,因為joanne這個角色沒有SystemAdmin資源的使用權限,所以以isAllowed方法測試時會得到false。

Zend_Acl 在判斷isAllowed的時候,除了會去看被查的那個角色本身(上例中則為joanne)對資源的存取權限,也會從該角色所屬的角色群來詢問該角色是否 有足夠權限。上例中,其實只是將joanne繼承了editor角色,並沒有直接的定義joanne與ContentManager之間的關係,但因為 editor是有權限可以使用ContentManager,所以joanne也跟著有相同的權限。

不過要特別注意的是,上面的例子中,$group1的前後順序是有差別的,在某些情況下,可能會造成非預期的答案,舉例如下:


$group2 = array(‘editor’, ‘planner’, ‘administrator’);
$acl->addRole(new Zend_Acl_Role(‘eddie’), $group2);
$acl->allow(‘editor’, ‘ContentManager’);  //設定editor可以使用ContentManager
$acl->deny(‘planner’, ‘ContentManager’);   //設定planner不可以使用ContentManager

//測試結果
echo $acl->isAllowed(‘eddie’, ‘ContentManager’)?’allowed’:'denined’;  //得到denined

但如果改一下$group2的順序則會得到不同的結果:

$group2 = array(‘planner’, ‘editor’, ‘administrator’);
echo $acl->isAllowed(‘eddie’, ‘ContentManager’)?’allowed’:'denined’;  //得到allowed

第 一個例子中,因為eddie並沒有”直接”的被定義規則,所以Zend_Acl往它所繼承的上層角色搜尋,它會先搜尋”administrator”角 色,結果沒符合的規則,它會再往下一個”planner”搜尋,發現了”deny”的規則,搜尋結束,回傳false;而第二個例子中,先搜 尋”administrator”角色,然後再搜尋”editor”,發現”allow”的定義,搜尋結束,回傳true。

Zend_Acl在搜尋角色規則時,會在搜尋到第一個有定義規則時回傳答案。

所以如果直接定義如下:

$acl->allow(“eddie”, “ContentManager”);
echo $acl->isAllowed(‘eddie’, ‘ContentManager’)?’allowed’:'denined’;  //得到allowed

如同上面我們規納出來的規則,它在遇到第一個定義時(就是它本身的角色)就回傳true了。

存取控制清單(Access Control List – ACL)的建立
我們在上一段已經可以設定某個帳號有權限可以使用網站的內容管理系統了,但如果還需要更細部的設定,例如企劃部的同仁帳號只能”檢視”文章,編輯部的帳號可以”檢視”及”編輯”文章,主管則有全部的功能。

我們假設把內容管理系統的權限再細分為三種:view、edit、delete

角色 權限
planner view
editor view, edit
manager view, edit, delete

<?php
require_once ‘Zend/Acl.php’;
require_once ‘Zend/Acl/Role.php’;

//建立ACL
$acl = new Zend_Acl();

//建立三個角色 planner、editor及manager
$acl->addRole(new Zend_Acl_Role(‘planner’));
$acl->addRole(new Zend_Acl_Role(‘editor’));
$acl->addRole(new Zend_Acl_Role(‘manager’));

$acl->allow(‘planner’, null, ‘view’);
$acl->allow(‘editor’, null, array(‘view’,'edit’));
$acl->allow(‘manager’, null , array(‘view’, ‘edit’, ‘delete’));

//測試:企劃部能檢視文章?
echo $acl->isAllowed(‘planner’, null, ‘view’) ? “allowed” : “denied”;  //allowed
//測試:企劃部能編輯文章?
echo $acl->isAllowed(‘planner’, null, ‘edit’) ? “allowed” : “denied”;  //denined
//測試:編輯部能編文章?
echo $acl->isAllowed(‘editor’, null, ‘edit’) ?  “allowed” : “denied”;  //allowed
//測試:編輯部能刪除文章?
echo $acl->isAllowed(‘editor’, null, ‘delete’) ?  “allowed” : “denied”; //denined
//測試:主管可刪除文章?
echo $acl->isAllowed(‘manager’, null, ‘delete’) ?  “allowed” : “denied”; //allowed

上面這段程式碼分別對三個角色(planner、editor及manager)定義了使用的權限。如果仔細觀察權限列表,用角色繼承的方式可以更有彈性的完成實作。


//建立三個角色,manager繼承editor,editor繼承planner
$rolePlanner = $acl->addRole(new Zend_Acl_Role(‘planner’);
$roleEditor = $acl->addRole(new Zend_Acl_Role(‘editor’), $rolePlanner);
$roleManager = $acl->addRole(new Zend_Acl_Role(‘manager’), $roleEditor);

//對各別角色定義權限
$acl->allow($rolePlanner, null, ‘view’);
$acl->allow($roleEditor, null, ‘edit’);
$acl->allow($roleManager, null, ‘delete’);

如此一來,雖然editor只有定義了”edit”的權限,但因為它繼承自planner角色,所以它也有”view”的功能,同理,manager則同時擁有繼承別的角色的”view”、”edit”及自己定義的”delete”權限。

更複雜的權限設定
前 面的章節說明了如何定義角色與資源之間的關係,除了指定某帳號可使用某資源外,也可以定義某帳號可以使用該資源的特定功能,如檢視、修改、刪除等。不過我 們可能會遇到更複雜的例子,例如同樣是編輯部的三個同事:eddie、lindsay及jessica,其中eddie負責的是電子報的工作,還偶爾幫忙 lindsay看一下她的電子報有沒有錯字,lindsay負責的是網站最新消息的發布,jessica則是負責網站一般文章的撰寫,所以接下來我們要來 針對內容管理系統這個資源做更細部的權限設定。

我們將上述的角色及工作列一個簡單的表格:

角色 功能 權限
eddie epaper view, edit, send
news view
lindsay news view, edit, publish
jessica content view, edit, publish

程式碼如下:

<?php
require_once ‘Zend/Acl.php’;
require_once ‘Zend/Acl/Role.php’;
require_once ‘Zend/Acl/Resource.php’;
$acl = new Zend_Acl();

//定義三個角色
$acl->addRole(new Zend_Acl_Role(‘eddie’));
$acl->addRole(new Zend_Acl_Role(‘lindsay’));
$acl->addRole(new Zend_Acl_Role(‘jessica’));

//定義三個資源
$acl->add(new Zend_Acl_Resource(‘epaper’));
$acl->add(new Zend_Acl_Resource(‘news’));
$acl->add(new Zend_Acl_Resource(‘content’));

//定義規則
$acl->allow(‘eddie’, ‘epaper’, array(‘view’, ‘edit’, ‘send’));
$acl->allow(‘eddie’, ‘news’, ‘view’);
$acl->allow(‘lindsay’, ‘news’, array(‘view’, ‘edit’, ‘publish’));
$acl->allow(‘jessica’, ‘content’, array(‘view’, ‘edit’, ‘publish’));

//測試:eddie是否可編輯電子報?
echo $acl->isAllowed(‘eddie’, ‘epaper’, ‘edit’) ? “allowed” : “denied”; // allowed
//測試:lindsay是否可編輯網站文章?
echo $acl->isAllowed(‘lindsay’, ‘content’, ‘edit’) ? “allowed” : “denied”; // denied
//測試:jessica是否可發布網站文章?
echo $acl->isAllowed(‘jessica’, ‘content’, ‘publish’) ? “allowed” : “denied”; // allowed

上面這段,我們是針對各別角色來做資源及權限的設定,不過只要人數一多,這樣的做法顯然不切實際,建議就改用之前提到的角色繼承的方式讓它更有彈性。

移除權限
要移除權限設定也是相當容易的,只要用removeAllow()或removeDeny()這兩個方法就行了。

<?php
//..延用上一章節的程式碼..
//移除eddie對電子報的編輯權限
$acl->removeDeny(‘eddie’, ‘epaper’, ‘edit’);
進階應用
一、以資料庫管理ACL
Zend_Acl在角色及資源的權限設定上相當容易,但如果能配合資料庫系統做管理,則可更方便管理每個角色及資源之間之關係。

二、進階權限設定
除了原本Zend_Acl裡定義的角色、資源、權限的關係外,也可以再結合其它程式功能(例如IP白名單),或是只能在限定時間內使用該權限等等設定,使整個權限管控更加安全。

本文主要是參照Zend Framework的官方文件自己試寫的,新手上路難免有誤,若有謬誤還請指教

官方網站上也有附了一段程式碼,有興趣的可以上去看看 :)
http://framework.zend.com/manual/en/zend.acl.advanced.html

http://blog.eddie.com.tw/2008/04/30/zend-framework-zend-acl

ZendFramework之Acl+Auth+Dispatcher数据库表认证和授权,分发器简单完整实例

// December 23rd, 2009 // No Comments » // PHP知识累计, Zend Framework 累积

ZF 的版本跟新实在太快了,一个多月前开始用1.84做自己的项目,就自己一个人专研的,实在是学的太慢,网上最新版本实例几乎没有,遇到问题,google 翻烂都找不到方法,都说英文资料比较丰富,但这一个月google下来感觉也不咋样啊,特别是1.8x新版的。没办法,就只有分析源码来理解喽。。。

如果只是针对 Acl + Auth 的话,网上找的资料也能拼凑出个大概来,虽然都是老版本的,但也知道是怎么回事的,关键是这个Dispatcher,1.8x在哪里,怎样注册这个
My_Plugin_Auth 完全不明白,实在是把我弄晕了,研究了三天,今天终于成功了,赶紧趁热打铁,把整个过程写在这儿,跟大家分享下,我也好巩固下思路,当然不足之处肯定是很多的,也希望大家能不吝赐教,谢谢!

Dispatcher 中的 preDispatch 是分发前被调用的方法,通过重载这个方法,实现对行为的过滤,个人理解也就是在这儿监视用户当前行为是否被授权并进行相应调控吧。
另外,preDispatch 授权的验证应该是只能是在 frontController 里面重载这个方法才行,我之前稀里糊涂的,在controller_action里面重载这个方法,一直死循环,郁闷的要死!
今天上午看了一个 dispatch 的模型图,才恍然大悟的。大家也可以看看这个pdf,我在国外网站上找到的,结构非常清晰了,大家看看就知道了
下载地址:zendframeworkdispatchworkflow-090508180623-phpapp02.pdf 223KB

Bootstrap.php

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initControllers()
{
$this->bootstrap(‘FrontController’);
//print_r(get_class_methods($this->frontController));
//exit;
require_once realpath(APPLICATION_CURR . ‘/acl/Acl.php’);
require_once realpath(APPLICATION_CURR . ‘/acl/Auth.php’);

$acl = new MyAcl;
$auth = Zend_Auth::getInstance();
$this->frontController->registerPlugin(new My_Plugin_Auth($auth, $acl));
}

Acl.php

class MyAcl extends Zend_Acl
{
public function __construct()
{
$this->add(new Zend_Acl_Resource(‘System_Category’));
$this->add(new Zend_Acl_Resource(‘System_Global’));
$this->add(new Zend_Acl_Resource(‘Article_Category’));
$this->add(new Zend_Acl_Resource(‘Article_Page’));
$this->add(new Zend_Acl_Resource(‘index’));
$this->add(new Zend_Acl_Resource(‘login’));
$this->add(new Zend_Acl_Resource(‘error’));

$this->addRole(new Zend_Acl_Role(‘guest’));
$this->addRole(new Zend_Acl_Role(‘administrator’));

$this->allow(‘guest’, ‘login’, null);
$this->allow(‘administrator’, null, null);
}
}

Auth.php

class My_Plugin_Auth extends Zend_Controller_Plugin_Abstract
{
private $_auth;
private $_acl;

// 认证没有通过(没有登陆)转向登录页面
private $_noauth = array(
‘module’ => ‘default’,
‘controller’ => ‘login’,
‘action’ => ‘index’);

// 没有权限转向错误页面
private $_noacl = array(
‘module’ => ‘default’,
‘controller’ => ‘error’,
‘action’ => ‘index’);

public function __construct($auth, $acl)
{
$this->_auth = $auth;
$this->_acl = $acl;
}

// 重载 preDispatch() 方法,认证用户
public function preDispatch($request)
{
if ($this->_auth->hasIdentity()) {
// 获取当前用户角色
$role = $this->_auth->getIdentity()->roleName;
}
else {
$role = ‘guest’;
}

$controller = $request->controller;
$action = $request->action;
$module = $request->module;
$resource = $controller;

if (!$this->_acl->has($resource)) {
$resource = null;
}

// Acl 配置该角色不能执行 $resource $action 的行为时
if (!$this->_acl->isAllowed($role, $resource, $action)) {
// 用户是由于没有通过认证 则 跳转到登录页面
if (!$this->_auth->hasIdentity()) {
$module = $this->_noauth['module'];
$controller = $this->_noauth['controller'];
$action = $this->_noauth['action'];
}
else {
// 用户没有权限,提示错误
$module = $this->_noacl['module'];
$controller = $this->_noacl['controller'];
$action = $this->_noacl['action'];
}
}

// 设置转向参数
$request->setModuleName($module);
$request->setControllerName($controller);
$request->setActionName($action);
}
}

LoginController.php

class LoginController extends Abstract_Controller
{
public function indexAction()
{
if ($this->_auth->hasIdentity()) {
// Already authenticated? Navigate away
$this->_redirect->myRedirect(‘/app/admin/index’);
}
$this->_view->display(‘login.html’);
}

public function checkAction()
{
if($this->_request->isPost()) {
// 过滤用户输入
$filter = new Zend_Filter_Striptags();
$username = trim($filter->filter($this->_request->getPost(‘account’)));
$password = trim($filter->filter($this->_request->getPost(‘password’)));

$bootstrap = $this->getInvokeArg(‘bootstrap’);
$db = $bootstrap->getResource(‘db’);
// 数据库表认证
$authAdapter = new Zend_Auth_Adapter_DbTable($db, ‘blog_system_user’, ‘account’, ‘password’);
$authAdapter->setIdentity($username)->setCredential(md5($password));
$result = $this->_auth->authenticate($authAdapter);

if($result->isValid()) {
// 将当前用户于 user 表中的记录除去 password 字段内容,其他的字段数据全部取出来
$data = $authAdapter->getResultRowObject(null, ‘password’);
// 这里添加当前用户的角色信息,由于 $data->roleID , 只记录了 id ,需要在 role 表里面查询 roleName
// 添加当前用户的角色名
$data->roleName = ‘administrator’;

// 将当前用户信息存入 Zend_Session
$this->_auth->getStorage()->write($data);
//$this->_redirect->myRedirect(‘/app/admin/index’);
echo $result->getCode();
}
else {
//$this->_view->assign(‘message’, $result->getMessages());
//print_r($result->getMessages());
//$this->_redirect->myRedirect(‘/app/admin/login’);
// 我用的 Ajax 判断登陆的所以直接 echo
echo $result->getCode();
}
}
}
}

上面就是4个文件的代码就是整个 认证+授权+分发 所涉及到的,都是最基础的,比较简单了,没想到用了三天才理解,这部分做完,我的整个项目也差不多完成了 嘿嘿!
大家看到有不足,或者有问题的地方 还麻烦你给指出来哦 谢谢啊!
欢迎转载,但请注明出生地哦!http://www.163fly.com/node/313

Zend Framework 剖析之MVC

// December 23rd, 2009 // No Comments » // PHP知识累计, Zend Framework 累积

【开篇】
在Web开发中,除了ASP.NET的Page Controller之外,MVC是其他开发语言中一个非常重要和常用的架构模式,本文就Zend Framework中的 MVC处理流程做一下浅显的分析。

【结构】
这里是一个Zend Framework 开发项目的目录结构,可以做为参考。具体的Front Controller 设计模式、MVC等可以参阅相关资料。目录结构如下:

/app/controllers       所有的controller
/app/models              所有的model
/app/views/scripts  view的模板文件夹
/config                        存放config文件
/db                               数据库相关脚步、sql存放
/lib                               库文件,如/lib/Zend存放Zend Framework,/lib/YourProj可以存放自己项目的库文件
/log                             日志文件
/root                            根目录,该文件夹下只有一个文件index.php即前端控制器,.htaccess是apache配置文件,将所有的请求转发到index.php。

一个典型的前端控制器代码:
$ctrl = Zend_Controller_Front::getInstance();
$ctrl ->throwExceptions(true);
$ctrl ->dispatch();
OK,一个dispatch就处理完了所有的请求,将处理结果传给了客户端,那么我们就从 Zend_Front_Controller来开始我们的Zend Framework之旅吧。

【流程分析】
首先,打开文件 /lib/Zend/Controller/Front.php文件。

getInstance 方法:
很简单的  Singleton 设计模式。下面进入构造函数。

__construct 方法:
$this->_plugins = new Zend_Controller_Plugin_Broker();
初始化当前 _plugins 字段。

在 实际开发中,本人觉得前端控制器的构造方法不应该是private,而应改成protected,理由很简单,如果要继承该Front Controller,那么就可以在构造函数中调用父类的构造函数,private的则没有办法实现,如果你要继承Front Controller的话,那必须在你的构造函数中加上一句: $this->_plugins = new Zend_Controller_Plugin_Broker();否则,当前的_plugins会因为没有初始化而出错。

?在实际开发中,本人觉得前端控制器的构造方法不应该是private,而应改成protected,理由很简单,如果要继承该Front Controller,那么就可以在构造函数中调用父类的构造函数,private的则没有办法实现,如果你要继承Front Controller的话,那必须在你的构造函数中加上一句:
$this->_plugins = new Zend_Controller_Plugin_Broker();否则,当前的_plugins会因为没有初始化而出错。

<!–[if !vml]–>
构造完之后,我们来重点分析一下dispatch方法。

dispatch方法:
dispatch方法是整个Zend MVC处理过程的核心,由于代码比较多,这里采用注释加评说的方法来讲解。
dispatch主要包括以下几个阶段:
(1)初始化阶段
/**
* 判断当前参数中是否设定了noErrorHandler参数,如果有或者已经有
* Plugin_ErrorHandler就不加载 这个plugin,否则就加载这个Plugin
*/
if (!$this->getParam(”noErrorHandler”) && !$this->_plugins->hasPlugin(”Zend_Controller_Plugin_ErrorHandler”)) {
// Register with stack index of 100
$this->_plugins->registerPlugin(new     Zend_Controller_Plugin_ErrorHandler(), 100
);
}
/**
*判断当前参数是否设定了noViewRenderer参数,如果有或者已经有了一个viewRenderer
*就不添加 Helper_ViewRenderer,否则就添加
*Zend_Controller_Action_Helper_ViewRenderer 实例作为默认ViewRenderer
*/
if (!$this->getParam(”noViewRenderer”)  && !Zend_Controller_Action_HelperBroker::hasHelper(”viewRenderer”)) {
Zend_Controller_Action_HelperBroker::addHelper(new Zend_Controller_Action_Helper_ViewRenderer()
);
}

Zend Framework MVC中plugin机制类似于ASP.NET中的HTTPModule,也就是说你添加的所有的plugin都会在处理具体的Action之前和之后触 发相应的事件。一次请求只能有一个具体的action来处理(可以通过setDispatched的方法来让一个请求处理多个action),但是可以有 多个plugin。这个和ASP.NET中的HTTPHandler和HTTPModule完全类似。 一个plugin必须继承Zend_Controller_Plugin_Abstract 类,重载某些方法来实现相应的功能。 主要的事件有:

public function preDispatch(Zend_Controller_Request_Abstract $request){} public function postDispatch(Zend_Controller_Request_Abstract $request){}
preDispatch是在调用具体action之前触发,所以在这个地方,我们可以添加权限认证,URL转发等动作。
postDispatch 是在调用action之后触发,在这个地方,可以做输出缓存等。 如何注册一个plugin呢?前端控制器中有一个方法registerPlugin,所以最简单的办法就是在index.php中初始化一个 plugin,然后调用 $ctrl->registerPlugin(myPlugin);

?Zend Framework MVC中plugin机制类似于ASP.NET中的HTTPModule,也就是说你添加的所有的plugin都会在处理具体的Action之前和之后触 发相应的事件。一次请求只能有一个具体的action来处理(可以通过setDispatched的方法来让一个请求处理多个action,我们将这个做 为一个小技巧 J ),但是可以有多个plugin。这个和ASP.NET中的HTTPHandler和HTTPModule完全类似。

一个plugin必须继承Zend_Controller_Plugin_Abstract 类,重载某些方法来实现相应的功能。主要的事件有:
??? public function preDispatch(Zend_Controller_Request_Abstract $request){}
public function postDispatch(Zend_Controller_Request_Abstract $request){}
preDispatch是在调用具体action之前触发,所以在这个地方,我们可以添加权限认证,URL转发等动作。
postDispatch是在调用action之后触发,在这个地方,可以做输出缓存等。

如何注册一个plugin呢?前端控制器中有一个方法registerPlugin,所以最简单的办法就是在index.php中初始化一个plugin,然后调用 $ctrl->registerPlugin(myPlugin);

<!–[if !vml]–>
/**
* 如果没有提供Request对象,则用默认的Request对象
*/
if (null !== $request) {
$this->setRequest($request);
} elseif ((null === $request)
&& (null === ($request = $this->getRequest()))) {
require_once ”Zend/Controller/Request/Http.php”;
$request = new Zend_Controller_Request_Http();
$this->setRequest($request);
}
Response 对象的处理类似,在此略过。
/**
* 给所有注册的plugin添加request和repsonse对象
*/
$this->_plugins
->setRequest($this->_request)
->setResponse($this->_response);
/**
*初始化一个Router,默认的路由是用 Zend_Controller_Router_Rewrite
*/
$router = $this->getRouter();
$router->setParams($this->getParams());
/**
*初始化一个dispatcher,默认的dispatcher是
*Zend_Controller_Dispatcher_Standard
*/
$dispatcher = $this->getDispatcher();
$dispatcher->setParams($this->getParams())

?如何去改变默认提供的这些路由、dispatcher以及其他参数对象呢?在Zend_Controller_Front中都有类似 setXYZ的方法,以供设置这些对象,当然也有 对应的getXYZ来获取这些对象。所以一个典型的前端控制器的初始化也通常有如下写法:
$front->setRouter($router)
???? ??->setDispatcher(new Zend_Controller_ModuleDispatcher())
???? ??->registerPlugin(new My_Plugin_Auth($auth, $acl))
???? ??->registerPlugin(new My_Plugin_View($view))
??->setControllerDirectory(array(
”default” => realpath(”../app/controllers/default”),
”admin” => realpath(”../app/controllers/admin”)
))
???? ??->setParam(”auth”, $auth)
???? ??->setParam(”view”, $view)
???? ??->setParam(”config”, $config)
???? ??->setParam(”sitemap”, $sitemap)
???? ??->dispatch();???????????????? ???????????????????

<!–[if !vml]–>          ->setResponse($this->_response);
如何去改变默认提供的这些路由、dispatcher以及其他参数对象呢?在Zend_Controller_Front中都有类似 setXYZ的方法,以供设置这些对象,当然也有 对应的getXYZ来获取这些对象。所以一个典型的前端控制器的初始化也通常有如下写法:

$front   ->setRouter($router)
->setDispatcher(new Zend_Controller_ModuleDispatcher())
->registerPlugin(new My_Plugin_Auth($auth, $acl))
->registerPlugin(new My_Plugin_View($view))
->setControllerDirectory(
array( ”default” => realpath(”../app/controllers/default”),
&nbs

p;         ”admin” => realpath(”../app/controllers/admin”)
))
->setParam(”auth”, $auth)
->setParam(”view”, $view)
->setParam(”config”, $config)
->setParam(”sitemap”, $sitemap)
->dispatch();

(2)路由阶段
/**
* 触发所有plugin的routeStartup事件
*/
$this->_plugins->routeStartup($this->_request);
/**
* 路由当前请求,给request对象添加一些重要参数:module、controller、action,
* 表示当前的请求由哪一个module的 那一个controller的那个action处理
*/
$router->route($this->_request);
/**
* 触发所有plugin的routeShutdown事件
*/
$this->_plugins->routeShutdown($this->_request);
/**
* 触发所有plugin的dispatchLoopStartup事件
*/
$this->_plugins->dispatchLoopStartup($this->_request);

(3)分发处理
/**
* 设置当前已经被dispatch
*/
$this->_request->setDispatched(true);
/**
* 触发所有plugin的preDispatch
*/
$this->_plugins->preDispatch($this->_request);
/**
* 最重要的方法,该方法处理了以下动作:从request中找到当前的module、controller、
* action,然后实例化相应的类,最后执行action方法
*/
$dispatcher->dispatch($this->_request, $this->_response);
/**
* 触发所有plugin的  postDispatch
*/
$this->_plugins->postDispatch($this->_request);
/**
* 触发所有plugin的  dispatchLoopShutdown
*/
$this->_plugins->dispatchLoopShutdown();

(4)收尾阶段
如果要返回response对象,那么就返回当前的Response,否则就将response对象中的内容直接输出。
可以在这里返回response对象,然后对response对象做一些自定义的处理。

if ($this->returnResponse()) {
return $this->_response;
}
$this->_response->sendResponse();

?Zend_Controller_Action_HelperBroker 类实现了Helper类到Action类的代理,是Zend 构架中一个很优秀的机制,这种模式有点类似注册工厂,也就是向HelperBroker注册对象,然后再从HelperBroker中取回对象,下面是注 册工厂参考图

但是又不完全和注册工厂一样,在获取Helper类的时候是实例化了一个HelperBroker,又和MonoState模式做法一样,如果要和模式挂上钩的话,那么可以暂且称之为 “RegistryMonoState” 。
不足之处是PluginBroker类,并且在前端控制器的构造函数中实例化PluginBroker是非常不好的做法,PluginBroker也完全可以用HelperBroker类的做法,这样可以大大降低耦合性。

<!–[if !vml]–><!–[endif]–>

【参考】
Zend Framework 开发Jira地址: http://framework.zend.com/issues/secure/Dashboard.jspa
SVN仓库:http://framework.zend.com/svn/framework/trunk

http://www.diybl.com/course/4_webprogram/php/phpjs/20090307/159362.html

php4操作xml文件

// December 23rd, 2009 // No Comments » // PHP基础知识累积, PHP知识累计

xml文件内容
<book> php-xml
<title>2007.04.13 </title>
<author> hamiguapi </author>
<publisher> php-xml test </publisher>
<date> 2007 </date>
</book>
php文件内容
<?php
/*****读取节点及其内容*******/
$doc = domxml_open_file(realpath(“books.xml”));
$root = $doc->document_element();
$node=$root->node_name();
echo “读取节点及其内容<br>”;
echo ‘Attributes of ‘.$node.”<br>”;
foreach($root->child_nodes() as $node)
{
if ($node->node_type() == XML_ELEMENT_NODE)
{
echo $node->node_name().”:”;
$value=$node->get_content();
echo $value;echo “<br>”;
}
}
echo htmlentities($doc->dump_mem(true));
echo “<br><br>”;
$doc->dump_file(“test1.xml”,true);
/******删除节点及内容********/
echo “删除节点及内容……..<br>”;
$doc = domxml_open_file(realpath(“books.xml”));
$denode=’title’;
$root = $doc->document_element();
$node=$root->node_name();
$node_array=$root->get_elements_by_tagname(‘title’);
$child = $root->remove_child($node_array[0]);
foreach ($node_array as $node) {
echo ‘YOU will delete ‘.$denode.’ the value is ‘ .$node->get_content().”<br>”;
}

$root=$doc->document_element();
$node=$root->node_name();
echo ‘Attributes of ‘.$node.”<br>”;
foreach($root->child_nodes() as $node)
{
if ($node->node_type() == XML_ELEMENT_NODE)
{
echo $node->node_name().”:”;
$value=$node->get_content();
echo $value;echo “<br>”;
}
}
echo htmlentities($doc->dump_mem(true));
echo “<br><br>”;
$doc->dump_file(“test1.xml”,true);

/**********修改节点及内容**********/
echo “修改节点及内容<br>”;
$doc = domxml_open_file(realpath(“books.xml”));
$addnode=’time’;
$root = $doc->document_element();
$node=$root->node_name();
echo ‘the old Attributes of ‘.$node.”<br>”;
foreach($root->child_nodes() as $node)
{
if ($node->node_type() == XML_ELEMENT_NODE)
{
echo $node->node_name().”:”;
$value=$node->get_content();
echo $value;echo “<br>”;
}
}

$node = $doc->create_element(“date”);
$node->set_content(“17:52″);
$elements = $doc->get_elements_by_tagname(“date”);
$element = $elements[0];
$newnode = $element->replace_node($node);

echo “<br>”;
$root = $doc->document_element();
$node=$root->node_name();
echo ‘the new Attributes of ‘.$node.”<br>”;
foreach($root->child_nodes() as $node)
{
if ($node->node_type() == XML_ELEMENT_NODE)
{
echo $node->node_name().”:”;
$value=$node->get_content();
echo $value;echo “<br>”;
}
}
echo htmlentities($doc->dump_mem(true));
echo “<br><br>”;
$doc->dump_file(“test1.xml”,true);
/**********添加节点及内容**********/
echo “添加节点及内容……<br>”;
$doc = domxml_open_file(realpath(“books.xml”));
$addnode=’time’;
$root = $doc->document_element();
$node=$root->node_name();
echo ‘the old Attributes of ‘.$node.”<br>”;
foreach($root->child_nodes() as $node)
{
if ($node->node_type() == XML_ELEMENT_NODE)
{
echo $node->node_name().”:”;
$value=$node->get_content();
echo $value;echo “<br>”;
}
}

$item = $doc->create_Element(“time”);
$item=$root->append_Child($item);

// create text node
$text = $doc->create_Text_Node(“11:07″);
$text=$item->append_Child($text);

echo “<br>”;
$root = $doc->document_element();
$node=$root->node_name();
echo ‘the new Attributes of ‘.$node.”<br>”;
foreach($root->child_nodes() as $node)
{
if ($node->node_type() == XML_ELEMENT_NODE)
{
echo $node->node_name().”:”;
$value=$node->get_content();
echo $value;echo “<br>”;
}
}
echo htmlentities($doc->dump_mem(true));
echo “<br><br>”;
$doc->dump_file(“test1.xml”,true);
/*******php写xml文件*******/
echo “php写xml文件”;
echo “<br>”;
// create doctype
$dom = domxml_new_doc(“1.0″);

// create root element
$root = $dom->create_Element(“top”);
$root=$dom->append_Child($root);

// create child element
$item = $dom->create_Element(“item”);
$item=$root->append_Child($item);

// create text node
$text = $dom->create_Text_Node(“hamigapi”);
$text=$item->append_Child($text);

// create child element
$item = $dom->create_Element(“item”);
$item=$root->append_Child($item);

// create another text node
$text = $dom->create_Text_Node(“qqbaobao”);
$text=$item->append_Child($text);

// save and display tree
echo htmlentities($dom->dump_mem(true));
$dom->dump_file(“test.xml”,true);
?>

xml文件内容
<book>
php-xml
<title>2007.04.13
</title>
<author>
hamiguapi
</author>
<publisher>
php-xml test
</publisher>
<date>
2007
</date>
</book>
php文件内容
<?php
/*****读取节点及其内容*******/
$doc = domxml_open_file(realpath(“books.xml”));
$root = $doc->document_element();
$node=$root->node_name();
echo “读取节点及其内容<br>”;
echo ‘Attributes of ‘.$node.”<br>”;
foreach($root->child_nodes() as $node)
{
if ($node->node_type() == XML_ELEMENT_NODE)
{
echo $node->node_name().”:”;
$value=$node->get_content();
echo $value;echo “<br>”;
}
}
echo htmlentities($doc->dump_mem(true));
echo “<br><br>”;
$doc->dump_file(“test1.xml”,true);
/******删除节点及内容********/
echo “删除节点及内容……..<br>”;
$doc = domxml_open_file(realpath(“books.xml”));
$denode=’title’;
$root = $doc->document_element();
$node=$root->node_name();
$node_array=$root->get_elements_by_tagname(‘title’);
$child = $root->remove_child($node_array[0]);
foreach ($node_array as $node) {
echo ‘YOU will delete ‘.$denode.’ the value is ‘ .$node->get_content().”<br>”;
}

$root=$doc->document_element();
$node=$root->node_name();
echo ‘Attributes of ‘.$node.”<br>”;
foreach($root->child_nodes() as $node)
{
if ($node->node_type() == XML_ELEMENT_NODE)
{
echo $node->node_name().”:”;
$value=$node->get_content();
echo $value;echo “<br>”;
}
}
echo htmlentities($doc->dump_mem(true));
echo “<br><br>”;
$doc->dump_file(“test1.xml”,true);

/**********修改节点及内容**********/
echo “修改节点及内容<br>”;
$doc = domxml_open_file(realpath(“books.xml”));
$addnode=’time’;
$root = $doc->document_element();
$node=$root->node_name();
echo ‘the old Attributes of ‘.$node.”<br>”;
foreach($root->child_nodes() as $node)
{
if ($node->node_type() == XML_ELEMENT_NODE)
{
echo $node->node_name().”:”;
$value=$node->get_content();
echo $value;echo “<br>”;
}
}

$node = $doc->create_element(“date”);
$node->set_content(“17:52″);
$elements = $doc->get_elements_by_tagname(“date”);
$element = $elements[0];
$newnode = $element->replace_node($node);

echo “<br>”;
$root = $doc->document_element();
$node=$root->node_name();
echo ‘the new Attributes of ‘.$node.”<br>”;
foreach($root->child_nodes() as $node)
{
if ($node->node_type() == XML_ELEMENT_NODE)
{
echo $node->node_name().”:”;
$value=$node->get_content();
echo $value;echo “<br>”;
}
}
echo htmlentities($doc->dump_mem(true));
echo “<br><br>”;
$doc->dump_file(“test1.xml”,true);
/**********添加节点及内容**********/
echo “添加节点及内容……<br>”;
$doc = domxml_open_file(realpath(“books.xml”));
$addnode=’time’;
$root = $doc->document_element();
$node=$root->node_name();
echo ‘the old Attributes of ‘.$node.”<br>”;
foreach($root->child_nodes() as $node)
{
if ($node->node_type() == XML_ELEMENT_NODE)
{
echo $node->node_name().”:”;
$value=$node->get_content();
echo $value;echo “<br>”;
}
}

$item = $doc->create_Element(“time”);
$item=$root->append_Child($item);

// create text node
$text = $doc->create_Text_Node(“11:07″);
$text=$item->append_Child($text);

echo “<br>”;
$root = $doc->document_element();
$node=$root->node_name();
echo ‘the new Attributes of ‘.$node.”<br>”;
foreach($root->child_nodes() as $node)
{
if ($node->node_type() == XML_ELEMENT_NODE)
{
echo $node->node_name().”:”;
$value=$node->get_content();
echo $value;echo “<br>”;
}
}
echo htmlentities($doc->dump_mem(true));
echo “<br><br>”;
$doc->dump_file(“test1.xml”,true);
/*******php写xml文件*******/
echo “php写xml文件”;
echo “<br>”;
// create doctype
$dom = domxml_new_doc(“1.0″);

// create root element
$root = $dom->create_Element(“top”);
$root=$dom->append_Child($root);

// create child element
$item = $dom->create_Element(“item”);
$item=$root->append_Child($item);

// create text node
$text = $dom->create_Text_Node(“hamigapi”);
$text=$item->append_Child($text);

// create child element
$item = $dom->create_Element(“item”);
$item=$root->append_Child($item);

// create another text node
$text = $dom->create_Text_Node(“qqbaobao”);
$text=$item->append_Child($text);

// save and display tree
echo htmlentities($dom->dump_mem(true));
$dom->dump_file(“test.xml”,true);
?>

初级篇:Zend Framework 环境配置以及第一个Hello World 输出!

// December 21st, 2009 // No Comments » // PHP知识累计, Zend Framework 累积

Zend Framework教程列表出来也有几天了,只有少许PHPer给本人提出意见以及建议..那没办法..我现在只有按照我自己的想法去写这一系列的教程..
我答应过PHPer会在这周出最少一篇的教程..今天,我有点时间我就来写第一篇吧.
OK!开工了…
第一步:确认你的PHP环境:
1.请PHPer确认你的PHP版本是否在5.2.0以上..如果不是的话..请更新到5.2.0,否则.Zend Framework 好像用不了..我自己有试过.
遇到过这样的问题..所以请你们自己测试一下..PHP源码最新版下载地址为:http://www.php.net/downloads.php.
2.你的PHP环境配置好了之后,请打开php.ini文件,确认PDO扩展是否打开.如果没有请把extension=php_pdo.dll之前的;号给去掉.
3.打开APACHE文件夹里面的httpd.conf文件.查找到apache的mod_rewrite模块,确认LoadModule rewrite_module modules/mod_rewrite.so是否打开.如果没有请去掉
它前面的#号.
4. 查找到httpd.conf文件,如果AllowOverride为None的话..请一定把None都改成all.这样你写.htaccess这样的文件才会起到作用..
5.重新启动你的APACHE服务器..这样我们的PHP环境就可以运用Zend Framewrok了.

第二步:获取Zend Framework源码:
1.下载最新版的Zend Framework源码.现在最新版好像是1.7.0.但是怕不稳定..所以请PHPer们自己决定用什么版本的.
大家可以在这里下载http://www.zendframework.com/download/latest最新版本的源码.

第三步:建立项目目录:
我也不想去多说什么..我把图片给展示出来..是我的这个教程的项目目录…我在上面都有说明..大家可以按照下面的方式来建立目录..当然下面我会提供源码下载.
不过建议朋友们一定要自己动手..才可以学到更多..我给也源码也只是给大家做为一个参考.

第四步:程序说明:这里我不多说什么.因为每个文件里面都有注解.我想不会有太难.要是有的朋友不懂请在博客上给我留言.我会关注这个博客..尽量回答你们的问题..谢谢..
index.php(网站入口)文件及说明:

<?php
error_reporting(E_ALL|E_STRICT);
date_default_timezone_set('Asia/Shanghai');
 
set_include_path('.' .PATH_SEPARATOR .'./library'
.PATH_SEPARATOR .'./application/models/'.PATH_SEPARATOR .get_include_path());
 require_once 'Zend/Loader.php';
Zend_Loader::registerAutoload();//设置Zend Framework 自动载入类文件
$registry = Zend_Registry::getInstance();
 //设置模板显示路径
$view = new Zend_View();
$view->setScriptPath('./application/views/scripts/');
$registry['view'] = $view;//注册View
//设置控制器
$frontController =Zend_Controller_Front::getInstance();
$frontController->setBaseUrl('/zendframework')//设置基本路径
		->setParam('noViewRenderer', true)
		->setControllerDirectory('./application/controllers')
		->throwExceptions(true)
		->dispatch();

IndexController.php文件及说明:

<?php
class IndexController extends Zend_Controller_Action
{
    function init()
    {
        $this->registry = Zend_Registry::getInstance();
        $this->view = $this->registry['view'];
        $this->view->baseUrl = $this->_request->getBaseUrl();
 
    }
 
   function indexAction()
    {
      	//这里给变量赋值,在index.phtml模板里显示
        $this->view->bodyTitle = '<h1>Hello World!</h1>';
		echo $this->view->render('index.phtml');//显示模版
    }
 
}

index.phtml模板文件说明:

<?=$this->bodyTitle; ?> <!-- 这里输出控制器里Action传过来的值:hello world -->

整个文件源码下载…地址.在这里..不过我的library里面没有加上Zend. 请各位PHPer自己加上..因为空间有限…哈哈…好了…这个教程就算是搞定了..要是有什么问题..请朋友给我留言…有空就常关注册一下我的博客…..现在这个博客还没什么人气….呵呵…

源码下载地址:  源码下载

Zend Framework实例教程

// December 19th, 2009 // No Comments » // PHP知识累计, Zend Framework 累积

Zend Framework发布了!虽然仍处于开发初期,这个教程仍突出讲解目前几个最好的功能,并指导你完成一个简单程序的构建。

Zend最早在社区里发布了ZF。基于同样的想法,这个教程写来用于展示ZF现有的功能。由于这个教程是在线发布,我将在ZF变化时对其进行更新,以便尽可能有效。

要求

Zend Framework要求PHP5。为了更好利用本教程的代码,你还需要Apache网页服务器。因为示范程序(一个新闻管理系统)用到了mod_rewrite

这个教程的代码可以自由下载,所以你可以自己试一下。你可以从Brain Buld的网站下载到代码:http://brainbulb.com/zend-framework-tutorial.tar.gz

下载ZF

当你开始这篇教程时,你需要下载ZF的最新版本。你可以用浏览器手工从http://framework.zend.com/download选择tar.gzzip文件进行下载,或者使用下列命令:

$ wget http://framework.zend.com/download/tgz
$ tar -xvzf ZendFramework-0.1.2.tar.gz
提示:Zend计划提供自有PEAR通道简化下载。

一旦你下载了预览版,把library目录放到方便的地方。在这个教程,我把library重命名为lib以便有个简洁的目录结构:

app/
views/
controllers/
www/
.htaccess
index.php
lib/

www目录是文档根目录,controllersviews目录是以后会用到的空目录,而lib目录来自你下载的预览版。

开始

我要介绍的第一个组件是Zend_Controller。从很多方面看,它为你开发的程序提供了基础,同时也部分决定了Zend Framework不只是个组件的集合。但是,你在用之前需要将所有的得到的请求都放到一个简单的PHP脚本。本教程用的是mod_rewrite

mod_rewrite自身是一种艺术,但幸运的是,这个特殊的任务特别简单。如果你对mod_rewrite或Apache的一般配置不熟悉,在文档根目录下创建一个.htaccess文件,并添加以下内容:

RewriteEngine on
RewriteRule !\.(js|ico|gif|jpg|png|css)$ index.php
提示: Zend_Controller的一个TODO项目就是取消对mod_rewrite的依赖。为了提供一个预览版的范例,本教程用了mod_rewrite

如果你直接把这些内容添加到httpd.conf,你必须重启网页服务器。但如果你用.htaccess文件,则什么都不必做。你可以放一些具体的文本到index.php并访问任意路径如/foo/bar做一下快速测试。如你的域名为example.org,则访问http://example.org/foo/bar

你还要设置ZF库的路径到include_path。你可以在php.ini设置,也可以直接在你的.htaccess文件放下列内容:

php_value include_path "/path/to/lib"

Zend

Zend类包含了一些经常使用的静态方法的集合。下面是唯一一个你要手工添加的类:

<?php

include 'Zend.php';

?>

一旦你包含了Zend.php,你就已经包含了Zend类的所有的类方法。用loadClass()就可以简单地加载其它类。例如,加载Zend_Controller_Front类:

<?php

include 'Zend.php';

Zend::loadClass('Zend_Controller_Front');

?>

include_path能理解loadclass()及ZF的组织和目录结构。我用它加载所有其它类。

Zend_Controller

使用这个controller非常直观。事实上,我写本教程时并没有用到它丰富的文档。

提示:文档目前已经可以在http://framework.zend.com/manual/zend.controller.html看到。

我一开始是用一个叫Zend_Controller_Front的front controller。为了理解它是怎么工作的,请把下列代码放在你的index.php文件:

<?php

include 'Zend.php';

Zend::loadClass('Zend_Controller_Front');

$controller = Zend_Controller_Front::getInstance();
$controller->setControllerDirectory('/path/to/controllers');
$controller->dispatch();

?>

如果你更喜欢对象链结,可以用以下代码代替:

<?php

include 'Zend.php';

Zend::loadClass('Zend_Controller_Front');

$controller = Zend_Controller_Front::getInstance()
->
setControllerDirectory('/path/to/controllers')
->
dispatch();

?>

现在如果你访问/foo/bar,会有错误发生。没错!它让你知道发生了什么事。主要的问题是找不到IndexController.php文件。

在你创建这个文件之前,应先理解一下ZF想让你怎样组织这些事情。ZF把访问请求给拆分开来。假如访问的是/foo/bar,则foo是controller,而bar是action。它们的默认值都是index.

如果foo是controller,ZF就会去查找controllers目录下的FooController.php文件。因为这个文件不存在,ZF就退回到IndexController.php。结果都没有找到,就报错了。

接下来,在controllers目录创建IndexController.php文件(可以用setControllerDirectory()设置):

<?php

Zend::loadClass('Zend_Controller_Action');

class IndexController extends Zend_Controller_Action
{
public function
indexAction()
{
echo
'IndexController::indexAction()';
}
}

?>

就如刚才说明的,IndexController类处理来自index controller或controller不存在的请求。indexAction()方法处理action为index的访问。要记住的是index是controller和action的默认值。如果你访问//index/index/indexindexAction()方法就会被执行。 (最后面的斜杠并不会改变这个行为。) 而访问其他任何资源只会导致出错。

在继续做之前,还要在IndexController加上另外一个有用的类方法。不管什么时候访问一个不存在的控制器,都要调用noRouteAction()类方法。例如,在FooController.php不存在的条件下,访问/foo/bar就会执行noRouteAction()。但是访问/index/foo仍会出错,因为foo是action,而不是controller.

noRouteAction()添加到IndexController.php:

<?php

Zend::loadClass('Zend_Controller_Action');

class IndexController extends Zend_Controller_Action
{
public function
indexAction()
{
echo
'IndexController::indexAction()';
}

public function noRouteAction()
{
$this->_redirect('/');
}
}

?>

例子中使用$this->_redirect('/')来描述执行noRouteAction()时,可能发生的行为。这会将对不存在controllers的访问重定向到根文档(首页)。

现在创建FooController.php

<?php

Zend::loadClass('Zend_Controller_Action');

class FooController extends Zend_Controller_Action
{
public function
indexAction()
{
echo
'FooController::indexAction()';
}

public function barAction()
{
echo
'FooController::barAction()';
}
}

?>

如果你再次访问/foo/bar,你会发现执行了barAction(),因为bar是action。现在你不只支持了友好的URL,还可以只用几行代码就做得这么有条理。酷吧!

你也可以创建一个__call()类方法来处理像/foo/baz这样未定义的action。

<?php

Zend::loadClass('Zend_Controller_Action');

class FooController extends Zend_Controller_Action
{
public function
indexAction()
{
echo
'FooController::indexAction()';
}

public function barAction()
{
echo
'FooController::barAction()';
}

public function __call($action, $arguments)
{
echo
'FooController:__call()';
}
}

?>

现在你只要几行代码就可以很好地处理用户的访问了,准备好继续。

Zend_View

Zend_View是一个用来帮助你组织好你的view逻辑的类。这对于模板-系统是不可知的,为了简单起见,本教程不使用模板。如果你喜欢的话,不妨用一下。

记住,现在所有的访问都是由front controller进行处理。因此应用框架已经存在了,另外也必须遵守它。为了展示Zend_View的一个基本应用,将IndexController.php修改如下:

<?php

Zend::loadClass('Zend_Controller_Action');
Zend::loadClass('Zend_View');

class IndexController extends Zend_Controller_Action
{
public function
indexAction()
{
$view = new Zend_View();
$view->setScriptPath('/path/to/views');
echo
$view->render('example.php');
}

public function noRouteAction()
{
$this->_redirect('/');
}
}

?>

views目录创建example.php文件:

<html>
<head>
<title>This Is an Example</title>
</head>
<body>
<p>This is an example.</p>
</body>
</html>

现在,如果你访问自己网站的根资源,你会看到example.php的内容。这仍没什么用,但你要清楚你要在以一种结构和组织非常清楚的方式在开发网络应用。

为了让Zend_View的应用更清楚一点,,修改你的模板(example.php)包含以下内容:

<html>
<head>
<title><?php echo $this->escape($this->title); ?></title>
</head>
<body>
<?php echo $this->escape($this->body); ?>
</body>
</html>

现在已经添加了两个功能。$this->escape()类方法用于所有的输出。即使你自己创建输出,就像这个例子一样。避开所有输出也是一个很好的习惯,它可以在默认情况下帮助你防止跨站脚本攻击(XSS)。

$this->title$this->body属性用来展示动态数据。这些也可以在controller中定义,所以我们修改IndexController.php以指定它们:

<?php

Zend::loadClass('Zend_Controller_Action');
Zend::loadClass('Zend_View');

class IndexController extends Zend_Controller_Action
{
public function
indexAction()
{
$view = new Zend_View();
$view->setScriptPath('/path/to/views');
$view->title = 'Dynamic Title';
$view->body = 'This is a dynamic body.';
echo
$view->render('example.php');
}

public function noRouteAction()
{
$this->_redirect('/');
}
}

?>

现在你再次访问根目录,应该就可以看到模板所使用的这些值了。因为你在模板中使用的$this就是在Zend_View范围内所执行的实例。

要记住example.php只是一个普通的PHP脚本,所以你完全可以做你想做的。只是应努力只在要求显示数据时才使用模板。你的controller (controller分发的模块)应处理你全部的业务逻辑。

在继续之前,我想做最后一个关于Zend_View的提示。在controller的每个类方法内初始化$view对象需要额外输入一些内容,而我们的主要目标是让快速开发网络应用更简单。如果所有模板都放在一个目录下,是否要在每个例子中都调用setScriptPath()也存在争议。

幸运的是,Zend类包含了一个寄存器来帮助减少工作量。你可以用register()方法把你的$view对象存储在寄存器:

<?php

Zend::register('view', $view);

?>

registry()方法进行检索:

<?php

$view = Zend::registry('view');

?>

基于这点,本教程使用寄存器。

Zend_InputFilter

本教程讨论的最后一个组件是Zend_InputFilter。这个类提供了一种简单而有效的输入过滤方法。你可以通过提供一组待过滤数据来进行初始化。

<?php

$filterPost = new Zend_InputFilter($_POST);

?>

这会将($_POST)设置为NULL,所以就不能直接进入了。Zend_InputFilter提供了一个简单、集中的根据特定规则过滤数据的类方法集。例如,你可以用getAlpha()来获取$_POST['name']中的字母:

<?php

/* $_POST['name'] = 'John123Doe'; */

$filterPost = new Zend_InputFilter($_POST);

/* $_POST = NULL; */

$alphaName = $filterPost->getAlpha('name');

/* $alphaName = 'JohnDoe'; */

?>

每一个类方法的参数都是对应要过滤的元素的关键词。对象(例子中的$filterPost)可以保护数据不被篡改,并能更好地控制对数据的操作及一致性。因此,当你操纵输入数据,应始终使用Zend_InputFilter

提示:Zend_Filter提供与Zend_InputFilter方法一样的静态方法。

构建新闻管理系统

虽然预览版提供了许多组件(甚至许多已经被开发),我们已经讨论了构建一个简单程序所需要的全部组件。在这里,你会对ZF的基本结构和设计有更清楚的理解。

每个人开发的程序都会有所不同,而Zend Framework试图包容这些差异。同样,这个教程是根据我的喜好写的,请根据自己的偏好自行调整。

当我开发程序时,我会先做界面。这并不意味着我把时间都花在标签、样式表和图片上,而是我从一个用户的角度去考虑问题。因此我把程序看成是页面的集合,每一页都是一个独立的网址。这个新闻系统就是由以下网址组成的:

/
/add/news
/add/comment
/admin
/admin/approve
/view/{id}

你可以直接把这些网址和controller联系起来。IndexController列出新闻,AddController添加新闻和评论,AdminController处理一些如批准新闻之类的管理,ViewController特定新闻和对应评论的显示。

如果你的FooController.php还在,把它删除。修改IndexController.php,为业务逻辑以添加相应的action和一些注释:

<?php

Zend::loadClass('Zend_Controller_Action');

class IndexController extends Zend_Controller_Action
{
public function
indexAction()
{
/* List the news. */
}

public function noRouteAction()
{
$this->_redirect('/');
}
}

?>

接下来,创建AddController.php文件:

<?php

Zend::loadClass('Zend_Controller_Action');

class AddController extends Zend_Controller_Action
{
function
indexAction()
{
$this->_redirect('/');
}

function commentAction()
{
/* Add a comment. */
}

function newsAction()
{
/* Add news. */
}

function __call($action, $arguments)
{
$this->_redirect('/');
}
}

?>

记住AddControllerindexAction()方法不能调用。当访问/add时会执行这个类方法。因为用户可以手工访问这个网址,这是有可能的,所以你要把用户重定向到主页、显示错误或你认为合适的行为。

接下来,创建AdminController.php文件:

<?php

Zend::loadClass('Zend_Controller_Action');

class AdminController extends Zend_Controller_Action
{
function
indexAction()
{
/* Display admin interface. */
}

function approveAction()
{
/* Approve news. */
}

function __call($action, $arguments)
{
$this->_redirect('/');
}
}

?>

最后,创建ViewController.php文件:

<?php

Zend::loadClass('Zend_Controller_Action');

class ViewController extends Zend_Controller_Action
{
function
indexAction()
{
$this->_redirect('/');
}

function __call($id, $arguments)
{
/* Display news and comments for $id. */
}
}

?>

AddController一样,index()方法不能调用,所以你可以使用你认为合适的action。ViewController和其它的有点不同,因为你不知道什么才是有效的action。为了支持像/view/23这样的网址,你要使用__call()来支持动态action。

数据库操作

因为Zend Framework的数据库组件还不稳定,而我希望这个演示可以做得简单一点。我使用了一个简单的类,用SQLite进行新闻条目和评论的存储和查询。

<?php

class Database
{
private
$_db;

public function __construct($filename)
{
$this->_db = new SQLiteDatabase($filename);
}

public function addComment($name, $comment, $newsId)
{
$name = sqlite_escape_string($name);
$comment = sqlite_escape_string($comment);
$newsId = sqlite_escape_string($newsId);

$sql = "INSERT
INTO   comments (name, comment, newsId)
VALUES ('$name', '$comment', '$newsId')"
;

return $this->_db->query($sql);
}

public function addNews($title, $content)
{
$title = sqlite_escape_string($title);
$content = sqlite_escape_string($content);

$sql = "INSERT
INTO   news (title, content)
VALUES ('$title', '$content')"
;

return $this->_db->query($sql);
}

public function approveNews($ids)
{
foreach (
$ids as $id) {
$id = sqlite_escape_string($id);

$sql = "UPDATE news
SET    approval = 'T'
WHERE  id = '$id'"
;

if (!$this->_db->query($sql)) {
return
FALSE;
}
}

return TRUE;
}

public function getComments($newsId)
{
$newsId = sqlite_escape_string($newsId);

$sql = "SELECT name, comment
FROM   comments
WHERE  newsId = '$newsId'"
;

if ($result = $this->_db->query($sql)) {
return
$result->fetchAll();
}

return FALSE;
}

public function getNews($id = 'ALL')
{
$id = sqlite_escape_string($id);

switch ($id) {
case
'ALL':
$sql = "SELECT id,
title
FROM   news
WHERE  approval = 'T'"
;
break;
case
'NEW':
$sql = "SELECT *
FROM   news
WHERE  approval != 'T'"
;
break;
default:
$sql = "SELECT *
FROM   news
WHERE  id = '$id'"
;
break;
}

if ($result = $this->_db->query($sql)) {
if (
$result->numRows() != 1) {
return
$result->fetchAll();
} else {
return
$result->fetch();
}
}

return FALSE;
}
}

?>

(你可以用自己的解决方案随意替换这个类。这里只是为你提供一个完整示例的介绍,并非建议要这么实现。)

这个类的构造器需要SQLite数据库的完整路径和文件名,你必须自己进行创建。

<?php

$db = new SQLiteDatabase('/path/to/db.sqlite');

$db->query("CREATE TABLE news (
id       INTEGER PRIMARY KEY,
title    VARCHAR(255),
content  TEXT,
approval CHAR(1) DEFAULT 'F'
)"
);

$db->query("CREATE TABLE comments (
id       INTEGER PRIMARY KEY,
name     VARCHAR(255),
comment  TEXT,
newsId   INTEGER
)"
);

?>

你只需要做一次,以后直接给出Database类构造器的完整路径和文件名即可:

<?php

$db = new Database('/path/to/db.sqlite');

?>

整合

为了进行整合,在lib目录下创建Database.phploadClass()就可以找到它。你的index.php文件现在就会初始化$view$db并存储到寄存器。你也可以创建__autoload()函数自动加载你所需要的类:

<?php

include 'Zend.php';

function __autoload($class)
{
Zend::loadClass($class);
}

$db = new Database('/path/to/db.sqlite');
Zend::register('db', $db);

$view = new Zend_View;
$view->setScriptPath('/path/to/views');
Zend::register('view', $view);

$controller = Zend_Controller_Front::getInstance()
->
setControllerDirectory('/path/to/controllers')
->
dispatch();

?>

接下来,在views目录创建一些简单的模板。index.php可以用来显示index视图:

<html>
<head>
<title>News</title>
</head>
<body>
<h1>News</h1>
<?php foreach ($this->news as $entry) { ?>
<p>
<a href="/view/<?php echo $this->escape($entry['id']); ?>">
<?php echo $this->escape($entry['title']); ?>
</a>
</p>
<?php } ?>
<h1>Add News</h1>
<form action="/add/news" method="POST">
<p>Title:<br /><input type="text" name="title" /></p>
<p>Content:<br /><textarea name="content"></textarea></p>
<p><input type="submit" value="Add News" /></p>
</form>
</body>
</html>

view.php模板可以用来显示选定的新闻条目:

<html>
<head>
<title>
<?php echo $this->escape($this->news['title']); ?>
</title>
</head>
<body>
<h1>
<?php echo $this->escape($this->news['title']); ?>
</h1>
<p>
<?php echo $this->escape($this->news['content']); ?>
</p>
<h1>Comments</h1>
<?php foreach ($this->comments as $comment) { ?>
<p>
<?php echo $this->escape($comment['name']); ?> writes:
</p>
<blockquote>
<?php echo $this->escape($comment['comment']); ?>
</blockquote>
<?php } ?>
<h1>Add a Comment</h1>
<form action="/add/comment" method="POST">
<input type="hidden" name="newsId"
value="<?php echo $this->escape($this->id); ?>" />
<p>Name:<br /><input type="text" name="name" /></p>
<p>Comment:<br /><textarea name="comment"></textarea></p>
<p><input type="submit" value="Add Comment" /></p>
</form>
</body>
</html>

最后,admin.php模板可以用来批准新闻条目:

<html>
<head>
<title>News Admin</title>
</head>
<body>
<form action="/admin/approve" method="POST">
<?php foreach ($this->news as $entry) { ?>
<p>
<input type="checkbox" name="ids[]"
value="<?php echo $this->escape($entry['id']); ?>" />
<?php echo $this->escape($entry['title']); ?>
<?php echo $this->escape($entry['content']); ?>
</p>
<?php } ?>
<p>
Password:<br /><input type="password" name="password" />
</p>
<p><input type="submit" value="Approve" /></p>
</form>
</body>
</html>
提示:为了保持简单,这个表单用密码作为验证机制。

使用到模板的地方,你只需要把注释替换成几行代码。如IndexController.php就变成下面这样:

<?php

class IndexController extends Zend_Controller_Action
{
public function
indexAction()
{
/* List the news. */
$db = Zend::registry('db');
$view = Zend::registry('view');
$view->news = $db->getNews();
echo
$view->render('index.php');
}

public function noRouteAction()
{
$this->_redirect('/');
}
}

?>

因为条理比较清楚,这个程序首页的整个业务逻辑只有四行代码。AddController.php更复杂一点,它需要更多的代码:

<?php

class AddController extends Zend_Controller_Action
{
function
indexAction()
{
$this->_redirect('/');
}

function commentAction()
{
/* Add a comment. */
$filterPost = new Zend_InputFilter($_POST);
$db = Zend::registry('db');
$name = $filterPost->getAlpha('name');
$comment = $filterPost->noTags('comment');
$newsId = $filterPost->getDigits('newsId');
$db->addComment($name, $comment, $newsId);
$this->_redirect("/view/$newsId");
}

function newsAction()
{
/* Add news. */
$filterPost = new Zend_InputFilter($_POST);
$db = Zend::registry('db');
$title = $filterPost->noTags('title');
$content = $filterPost->noTags('content');
$db->addNews($title, $content);
$this->_redirect('/');
}

function __call($action, $arguments)
{
$this->_redirect('/');
}
}

?>

因为用户在提交表单后被重定向,这个controller不需要视图。

AdminController.php,你要处理显示管理界面和批准新闻两个action:

<?php

class AdminController extends Zend_Controller_Action
{
function
indexAction()
{
/* Display admin interface. */
$db = Zend::registry('db');
$view = Zend::registry('view');
$view->news = $db->getNews('NEW');
echo
$view->render('admin.php');
}

function approveAction()
{
/* Approve news. */
$filterPost = new Zend_InputFilter($_POST);
$db = Zend::registry('db');
if (
$filterPost->getRaw('password') == 'mypass') {
$db->approveNews($filterPost->getRaw('ids'));
$this->_redirect('/');
} else {
echo
'The password is incorrect.';
}
}

function __call($action, $arguments)
{
$this->_redirect('/');
}
}

?>

最后是ViewController.php

<?php

class ViewController extends Zend_Controller_Action
{
function
indexAction()
{
$this->_redirect('/');
}

function __call($id, $arguments)
{
/* Display news and comments for $id. */
$id = Zend_Filter::getDigits($id);
$db = Zend::registry('db');
$view = Zend::registry('view');
$view->news = $db->getNews($id);
$view->comments = $db->getComments($id);
$view->id = $id;
echo
$view->render('view.php');
}
}

?>

虽然很简单,但我们还是提供了一个功能较全的新闻和评论程序。最好的地方是由于有较好的设计,增加功能变得很简单。而且随着Zend Framework越来越成熟,只会变得更好。

Zend_Db_Table Relationships 关联查询实战(一)

// December 19th, 2009 // No Comments » // PHP知识累计, Zend Framework 累积

我们通过四个数据表articles(文章表),categories(分类表),tags(标签表),articles_tags(文章_标签对应表)
来学习使用Zend_Db_Table Relationships关联查询
以下是创建这四个表的SQL语句:

复制内容到剪贴板

PHP代码:

-- --------------------------------------------------------

--

-- 表的结构 `articles`

--

CREATE TABLE IF NOT EXISTS `articles` (

`id` int(10) unsigned NOT NULL auto_increment,

`cat_id` tinyint(3) unsigned NOT NULL default '1',

`title` varchar(100) NOT NULL,

PRIMARY KEY (`id`),

KEY `title` (`title`)

) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

-- --------------------------------------------------------

--

-- 表的结构 `articles_tags`

--

CREATE TABLE IF NOT EXISTS `articles_tags` (

`article_id` int(10) unsigned NOT NULL,

`tag_id` int(10) unsigned NOT NULL,

PRIMARY KEY (`article_id`,`tag_id`)

) ENGINE=MyISAM DEFAULT CHARSET=utf8;

-- --------------------------------------------------------

--

-- 表的结构 `categories`

--

CREATE TABLE IF NOT EXISTS `categories` (

`cat_id` tinyint(3) unsigned NOT NULL auto_increment,

`parent_id` tinyint(3) unsigned NOT NULL default '0',

`name` varchar(50) NOT NULL,

PRIMARY KEY (`cat_id`),

KEY `name` (`name`)

) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

-- --------------------------------------------------------

--

-- 表的结构 `tags`

--

CREATE TABLE IF NOT EXISTS `tags` (

`tag_id` int(10) unsigned NOT NULL auto_increment,

`name` varchar(100) NOT NULL,

PRIMARY KEY (`tag_id`),

KEY `name` (`name`)

) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

同时我们在文章表和分类表中插入一些测试数据

复制内容到剪贴板

PHP代码:

INSERT INTO `categories` (`cat_id`, `parent_id`, `name`) VALUES

(1, 0, '分类一'),

(2, 0, '分类二');

INSERT INTO `articles` (`id`, `cat_id`, `title`) VALUES

(1, 1, '这是一篇属于分类一的文章'),

(2, 1, '这是一篇属于分类一的文章'),

(3, 2, '这是一篇属于分类二的文章'),

(4, 2, '这是一篇属于分类二的文章');

下面分别定义文章模型文件(Article.php)和分类模型文件(Category.php):
models/Article.php

复制内容到剪贴板

PHP代码:

class Article extends Zend_Db_Table {

protected $_name = 'articles';

protected $_primary = 'id';

}

models/Category.php

复制内容到剪贴板

PHP代码:

class Category extends Zend_Db_Table {

protected $_name = 'categories';

protected $_primary = 'cat_id';

}

我们通过Zend_Db_Table的$_referenceMap属性来定义数据表的关联关系
models/Article.php

复制内容到剪贴板

PHP代码:

class Article extends Zend_Db_Table {

protected $_name = 'articles';

protected $_primary = 'id';

protected $_referenceMap = array(

'category' => array( //关联名称

'columns' => 'cat_id',

'refTableClass' => 'Category',

'refColumns' => 'cat_id',

),

);

}

$_referenceMap 的相关属性说明:
columns:指定当前数据表通过那个字段和外表关联,一般指外键(PK)
refTableClass:关联表所对应的类名称
refColumns:关联表使用那个字段和其它表关联,一般指主键(FK)

现在我们已经将文章数据表和分类数据表关联起来了,下面我们看来如何在查询某一篇文章的同时查询出相关的分类信息,
修改modles/Article.php,添加下面方法:

复制内容到剪贴板

PHP代码:

public function getById($id)

{

$where = $this -> select() -> where('id = ?', $id);

$article = $this -> fetchRow($where);

$select = $this-> select() -> from('categories', array('name'));

$category = $article -> findParentRow('Category', null, $select) -> toArray();

$row = $article -> toArray();

$row['category'] = $category;

return $row;

}

然后在任意controller里,比如IndexController.php里

复制内容到剪贴板

PHP代码:

class IndexController extends Zend_Controller_Action {

public function init()

{

}

public function indexAction()

{

$modelArticle = new Article();

$article = $modelArticle -> getById(3);

Zend_Debug::dump($article);

}

}

运行程序结果复制内容到剪贴板

PHP代码:

array(4) {

["id"] => string(1) "3"

["cat_id"] => string(1) "2"

["title"] => string(24) "这是一篇属于分类二的文章"

["category"] => array(1) {

["name"] => string(6) "分类二"

}

}

PHP技巧:PHP 使用 CURL 同步抓取多个网页

// December 16th, 2009 // No Comments » // PHP基础知识累积, PHP知识累计

以下为引用的内容:

<?php
function async_get_url($url_array, $wait_usec = 0)
{
if (!is_array($url_array))
return false;

$wait_usec = intval($wait_usec);

$data    = array();
$handle  = array();
$running = 0;

$mh = curl_multi_init(); // multi curl handler

$i = 0;
foreach($url_array as $url) {
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // return don't print
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // 302 redirect
curl_setopt($ch, CURLOPT_MAXREDIRS, 7);

curl_multi_add_handle($mh, $ch); // 把 curl resource 放进 multi curl handler 里

$handle[$i++] = $ch;
}

/* 执行 */
do {
curl_multi_exec($mh, $running);

if ($wait_usec > 0) /* 每个 connect 要间隔多久 */
usleep($wait_usec); // 250000 = 0.25 sec
} while ($running > 0);

/* 读取资料 */
foreach($handle as $i => $ch) {
$content  = curl_multi_getcontent($ch);
$data[$i] = (curl_errno($ch) == 0) ? $content : false;
}

/* 移除 handle*/
foreach($handle as $ch) {
curl_multi_remove_handle($mh, $ch);
}

curl_multi_close($mh);

return $data;
}
?>

使用

以下为引用的内容:
<?php
$urls = array(‘http://example1.com’, ‘http://example2.com’);
print_r(async_get_url($urls)); // [0] => example1, [1] => example2
?>

测试

sleep.php # 看时间延长取得的效果

以下为引用的内容:
<?php
sleep(intval($_GET['time']));
echo intval($_GET['time']);
?>

以下为引用的内容:
<?php
$url_array = array(
‘http://example.com/sleep.php?time=5′,
‘http://example.com/sleep.php?time=10′,
‘http://example.com/sleep.php?time=7′,
‘http://example.com/sleep.php?time=5′,
);
print_r(async_get_url($url_array));
// 总花费时间会是 10 秒, 并印出 [0] => 5, [1] => 10, [2] => 7, [3] => 5
?>

PHP+FTP远程上传文件至服务器

// December 6th, 2009 // No Comments » // PHP基础知识累积, PHP知识累计

公司网站XXXXX.cn主要为用户提供手机游戏下载服务,对服务器要求比较高.

为了优化服务器的性能,现在主服务器上只放程序文件,用一台服务器专门放静态文件,像图片,游戏客户端文件等都放上面.通过URL来使用这上面的文件.

因为文件服务器和程序服务器是分开的,之前采用的办法是在主服务器上传完文件后,再同步到文件服务器,文件同步需要设置一个间隔时间,也就是多久同步一次,对文件的实时显示会有影响.

为了解决这个问题,决定采用PHP通过FTP上传到文件服务器.从网上找了个PHP的FTP文件上传类,作了少许修改.

上传时先传至本地对文件作必要的修改,如加水印等等操作,然后再通过FTP传至远程服务器.

使用示例:
//上传图片
if ($_FILES['pic']['name'])
{
$file_path=’/opt/www/img/’;
$pic = upload(‘pic’, $filename, ‘jpg|jpeg|gif|bmp|png’, $file_path);
if(!$pic)
{
echo “<script language=javascript>alert(‘图片上传失败!’);history.go(-1)</script>”;
exit;
}
require_once(ROOT_PATH . ‘Lib/Class/Ftp.class.php’);
$ftp = new ftp(“127.0.0.1″,”gamezeroftp”,”123456″,”/opt/www”);
$localfile=’/opt/www/img/’.$pic;
$remotefile=’/opt/www/gamepics/’.$pic;
$ftpput = $ftp->put($localfile, $remotefile); //FTP上传原图到远程服务器
if(!$ftpput){
echo “上传图片到远程服务器失败!”;
}
$ftp->bye(); //关闭FTP连接
}

附上FTP操作类:
<?php
// ————————————————————————–
// File name   :
Ftp.class.php
// Description : FTP上传类
// Requirement : PHP5 (
http://www.php.net)
// Copyright(C), gamezero.cn, 2008, All Rights Reserved.
// Author: Jipeng (
jipeng001@hotmail.com)
// ————————————————————————–
//R FTP 处理;
class ftp {
var $ftpUrl = ’127.0.0.1;
var $ftpUser = ‘gamezeroftp’;
var $ftpPass = ’123456′;
var $ftpDir = ‘/opt/ftp/upload/’;
var $ftpR = ”; //R ftp资源;
var $status = ”;
//R 1:成功;2:无法连接ftp;3:用户错误;
function ftp($ftpUrl=”", $ftpUser=”", $ftpPass=”", $ftpDir=”") {
if($ftpUrl){
$this->ftpUrl=$ftpUrl;
}
if($ftpUser){
$this->ftpUser=$ftpUser;
}
if($ftpPass){
$this->ftpPass=$ftpPass;
}
if($ftpUrl){
$this->ftpDir=$ftpDir;
}
if ($this->ftpR = ftp_connect($this->ftpUrl, 21)) {
if (ftp_login($this->ftpR, $this->ftpUser, $this->ftpPass)) {
if (!empty($this->ftpDir)) {
ftp_chdir($this->ftpR, $this->ftpDir);
}
ftp_pasv($this->ftpR, true);//R 启用被动模式;
$status = 1;
} else {
$status = 3;
}
} else {
$status = 2;
}
}
//R 切换目录;
function cd($dir) {
return ftp_chdir($this->ftpR, $dir);
}
//R 返回当前路劲;
function pwd() {
return ftp_pwd($this->ftpR);
}
//R 创建目录
function mkdir($directory) {
return ftp_mkdir($this->ftpR,$directory);
}
//R 删除目录
function rmdir($directory) {
return ftp_rmdir($this->ftpR,$directory);
}
//R 上传文件;
function put($localFile, $remoteFile = ”) {
if ($remoteFile == ”) {
$remoteFile = end(explode(‘/’, $localFile));
}
$res = ftp_nb_put($this->ftpR, $remoteFile, $localFile, FTP_BINARY);
while ($res == FTP_MOREDATA) {
$res = ftp_nb_continue($this->ftpR);
}
if ($res == FTP_FINISHED) {
return true;
} elseif ($res == FTP_FAILED) {
return false;
}
}
//R 下载文件;
function get($remoteFile, $localFile = ”) {
if ($localFile == ”) {
$localFile = end(explode(‘/’, $remoteFile));
}
if (ftp_get($this->ftpR, $localFile, $remoteFile, FTP_BINARY)) {
$flag = true;
} else {
$flag = false;
}
return $flag;
}
//R 文件大小;
function size($file) {
return ftp_size($this->ftpR, $file);
}
//R 文件是否存在;
function isFile($file) {
if ($this->size($file) >= 0) {
return true;
} else {
return false;
}
}
//R 文件时间
function fileTime($file) {
return ftp_mdtm($this->ftpR, $file);
}
//R 删除文件;
function unlink($file) {
return ftp_delete($this->ftpR, $file);
}
function nlist($dir = ‘/service/resource/’) {
return ftp_nlist($this->ftpR, $dir);
}
//R 关闭连接;
function bye() {
return ftp_close($this->ftpR);
}
}
?>

Flex连接PHP

// November 24th, 2008 // No Comments » // Flex积累, PHP知识累计

  1. import mx.rpc.http.HTTPService;
  2. import mx.rpc.events.ResultEvent;
  3. import mx.binding.utils.BindingUtils;
  4. import mx.controls.Alert;
  5. import mx.events.*;
  6. import mx.collections.ArrayCollection;
  7. import mx.rpc.events.FaultEvent;
  8. import mx.controls.dataGridClasses.DataGridColumn;
  9. import mx.controls.dataGridClasses.DataGridItemRenderer;
  10. import mx.events.DataGridEvent;
  11. import mx.events.CloseEvent;
  12. import mx.managers.CursorManager;
  13. import mx.utils.ObjectUtil;
  14. import mx.rpc.AsyncToken;
  15. import mx.utils.StringUtil;
  16.  
  17. [Bindable]
  18. private var xmlData:XML=new XML();
  19.  
  20. public const ENDPOINT_URL:String = "<a href="http://localhost/BigpapaPHP/BigpapaPHP.php">http://localhost/BigpapaPHP/BigpapaPHP.php</a>";
  21.  
  22. private var httpservice:HTTPService = new HTTPService();
  23.  
  24. //初始化
  25. private function Init():void{
  26. httpservice.url = ENDPOINT_URL;
  27. httpservice.method = "POST";
  28. httpservice.useProxy = false;
  29. httpservice.resultFormat = "e4x";
  30. initfill();
  31. }
  32. //查询出所有数据
  33. private function initfill():void{
  34. CursorManager.setBusyCursor();
  35. var sql:String = "select * from ItemsbyContainer";
  36. var parameters:* = {
  37. "sql": sql
  38. }
  39. doResultcontainer("FindAll",parameters,fillHandler);
  40. }
  41.  
  42. //传递sql给php页面,并添加监听
  43. private function doResultcontainer(Methodname:String,parameters:Object,callback:Function):void{
  44. parameters['method'] = Methodname;
  45. httpservice.request = parameters;
  46. httpservice.addEventListener(ResultEvent.RESULT, resultHandler);
  47. httpservice.addEventListener(FaultEvent.FAULT, faultHandler);
  48. var call:AsyncToken = httpservice.send();
  49. call.request_params = httpservice.request;
  50. call.handler = callback;
  51. }
  52.  
  53. //查询的监听函数
  54. private function fillHandler(e:Object):void
  55. {
  56. if (e.isError)
  57. {
  58. Alert.show("Error: " + e.data.toString());
  59. }
  60. else
  61. {
  62. this.xmlData=XML(e.data);
  63. CursorManager.removeBusyCursor();
  64. }
  65. }
  66.  
  67. //httpService请求成功的监听方法
  68. public function resultHandler(e:ResultEvent):void
  69. {
  70. var topass:* = deserialize(e.result, e);
  71. e.token.handler.call(null, topass);
  72. }
  73.  
  74. //httpService请求失败的监听方法
  75. public function faultHandler(e:FaultEvent):void
  76. {
  77. Alert.show("Connection error: " + e.fault.faultDetail + "!"+e.message.toString());
  78. }
  79.  
  80. //异步处理函数
  81. public function deserialize(obj:*, e:*):*
  82. {
  83. var toret:Object = {};
  84. toret.originalEvent = e;
  85.  
  86. if (obj.data.elements("error").length() &gt; 0)
  87. {
  88. toret.isError = true;
  89. toret.data = obj.data;
  90. }
  91. else
  92. {
  93. toret.isError = false;
  94. toret.metadata = obj.metadata;
  95. toret.data = obj.data;
  96. }
  97. return toret;
  98. }
  99.  
  100. private function btnAdd_click():void
  101. {
  102. this.currentState="Edit";
  103. this.btnEdit.label=this.btnAdd.label;
  104. }
  105.  
  106. private function btnModify_click():void
  107. {
  108. this.currentState="Edit";
  109. this.btnEdit.label=this.btnModify.label;
  110. txtName.text = xmlData.row[dgContainer.selectedIndex].Name;
  111. txaDescription.text = xmlData.row[dgContainer.selectedIndex].Description;
  112. txtPrice.text = xmlData.row[dgContainer.selectedIndex].Price;
  113. if(xmlData.row[dgContainer.selectedIndex].Container =="Pint"){
  114. rbgContainer.selectedValue = "Pint"
  115. }else{
  116. rbgContainer.selectedValue = "Quart"
  117. }
  118. txtServes.text = xmlData.row[dgContainer.selectedIndex].Serves;
  119. if(xmlData.row[dgContainer.selectedIndex].LowCarb == 1){
  120. cbxLowCarb.selected = true;
  121. }
  122. btnEdit.enabled = true;
  123. }
  124.  
  125. private function btnDelete_click():void
  126. {
  127. this.currentState="Edit";
  128. this.btnEdit.label=this.btnDelete.label;
  129. txtName.text = xmlData.row[dgContainer.selectedIndex].Name;
  130. txaDescription.text = xmlData.row[dgContainer.selectedIndex].Description;
  131. txtPrice.text = xmlData.row[dgContainer.selectedIndex].Price;
  132. if(xmlData.row[dgContainer.selectedIndex].Container =="Pint"){
  133. rbgContainer.selectedValue = "Pint"
  134. }else{
  135. rbgContainer.selectedValue = "Quart"
  136. }
  137. txtServes.text = xmlData.row[dgContainer.selectedIndex].Serves;
  138. if(xmlData.row[dgContainer.selectedIndex].LowCarb == 1){
  139. cbxLowCarb.selected = true;
  140. }
  141. btnEdit.enabled = true;
  142. btnEdit.enabled=true;
  143. }
  144.  
  145. private function btnEdit_click():void
  146. {
  147. var i:Number = 0;
  148. if(cbxLowCarb.selected)
  149. i = 1;
  150. if(this.btnEdit.label=="Add"){
  151. var sql:String = "insert into Itemsbycontainer(Name,Description,Price,Container,Serves,LowCarb) values('"+txtName.text+"','"+txaDescription.text+"',"+txtPrice.text+",'"+rbgContainer.selectedValue+"',"+txtServes.text+","+i+")";
  152. var parameters:* = {
  153. "sql":sql
  154. }
  155. doResultcontainer("Insert",parameters,dataUpdate);
  156. }else if(this.btnEdit.label=="Modify"){
  157. var updata:String = "update Itemsbycontainer set Name='"+txtName.text+"' where bycontainerID="+this.dgContainer.selectedItem.byContainerID;
  158. var parameterUpdate:* = {
  159. "sql":updata
  160. }
  161. doResultcontainer("Update",parameterUpdate,dataUpdate);
  162. }else if(this.btnEdit.label=="Delete"){
  163. var del:String = "delete from Itemsbycontainer where bycontainerID="+this.dgContainer.selectedItem.byContainerID;
  164. var parameterdel:* = {
  165. "sql":del
  166. }
  167. doResultcontainer("Delete",parameterdel,dataUpdate);
  168. }
  169. }
  170.  
  171. //添加数据的监听方法,以便在添加成功或失败后进行自定义操作
  172. private function dataUpdate(obj:Object):void{
  173. if (obj.isError)
  174. {
  175. Alert.show("Error: " + obj.data.error);
  176. }
  177. else
  178. {
  179. initfill();
  180. }
  181. }
  182.  
  183. private function goToDefaultState():void{
  184. this.currentState="";
  185. }
  186.  
  187. private function dgContainer_itemClick():void
  188. {
  189.  
  190. }
  191.  
  192. //验证方法
  193. private function change():void
  194. {
  195. for each(var obj:Object in this.getChildren())
  196. {
  197. if(obj is Canvas)
  198. {
  199. for each(var tObj:Object in obj.getChildren())
  200. {
  201. if(tObj is TextInput)
  202. {
  203. var txtInput:TextInput=TextInput(tObj);
  204. if(StringUtil.trim(txtInput.text).length&lt;1)
  205. {
  206. txtInput.errorString="Not allow empty";
  207. this.btnEdit.enabled=false;
  208. return;
  209. }else{
  210. txtInput.errorString="";
  211. }
  212. }else if(tObj is TextArea)
  213. {
  214. var txtArea:TextArea=TextArea(tObj)
  215. if(StringUtil.trim(txtArea.text).length&lt;1)
  216. {
  217. txtArea.errorString="Not allow empty";
  218. this.btnEdit.enabled=false;
  219. return;
  220. }else{
  221. txtArea.errorString="";
  222. }
  223. }
  224. }
  225. }
  226. }
  227. this.btnEdit.enabled=true;
  228. }

PHP

  1. function findAll() {
  2. global $conn, $filter_field, $filter_type;
  3. $recordset = mysql_query(@$_REQUEST['sql'], $conn);
  4. $toret = array();
  5. while ($row_recordset = mysql_fetch_assoc($recordset)) {
  6. array_push($toret, $row_recordset);
  7. }
  8. $toret = array(
  9. "data" =&gt; $toret,
  10. "metadata" =&gt; array (
  11. "totalRows" =&gt; $totalrows,
  12. "pageNum" =&gt; $pageNum
  13. )
  14. );
  15.  
  16. return $toret;
  17. }
  18. function insert() {
  19. global $conn;
  20.  
  21. $query_insert =  str_replace('\\','',$_REQUEST['sql']);
  22. $ok = mysql_query($query_insert);
  23. $toret = '&lt;?xml version="1.0" encoding="utf8"&gt;&lt;Return&gt;&lt;return&gt;'.$query_insert.'&lt;/return&gt;&lt;/Return&gt;';
  24. return $toret;
  25. }
  26.  
  27. function update() {
  28. global $conn;
  29.  
  30. $query_insert =  str_replace('\\','',$_REQUEST['sql']);
  31. $ok = mysql_query($query_insert);
  32. $toret = '&lt;?xml version="1.0" encoding="utf8"&gt;&lt;Return&gt;&lt;return&gt;'.$query_insert.'&lt;/return&gt;&lt;/Return&gt;';
  33. return $toret;
  34. }
  35. function delete() {
  36. global $conn;
  37.  
  38. $query_insert =  str_replace('\\','',$_REQUEST['sql']);
  39. $ok = mysql_query($query_insert);
  40. $toret = '&lt;?xml version="1.0" encoding="utf8"&gt;&lt;Return&gt;&lt;return&gt;'.$query_insert.'&lt;/return&gt;&lt;/Return&gt;';
  41. return $toret;
  42. }
  43. if ($conn === false) {
  44. $ret = array(
  45. "data" =&gt; array("error" =&gt; "database connection error, please check your settings !"),
  46. "metadata" =&gt; array()
  47. );
  48. } else {
  49. mysql_select_db($database_conn, $conn);
  50.  
  51. switch (@$_REQUEST["method"]) {
  52. case "FindAll":
  53. $ret = findAll();
  54. break;
  55. case "Insert":
  56. $ret = insert();
  57. break;
  58. case "Update":
  59. $ret = update();
  60. break;
  61. case "Delete":
  62. $ret = delete();
  63. break;
  64. case "Count":
  65. $ret = rowCount();
  66. break;
  67. }
  68. }

转:http://blog.csdn.net/faith_zerg/archive/2007/10/31/1859835.aspx

Joomla之组件开发全攻略

// November 8th, 2008 // No Comments » // Joomla!集锦

前面已经介绍了模块,触发器的开发,对于组件的开发教程迟迟没动手.原因是相比较模块和触发器的开发组件开发难度更大,涉及JOOMLA的知识也更 多,不过现在使用J的朋友越来越多,使用也越来越深入,希望这个系列的几篇文章能帮你澄清一些概念,提供组件开发的一个清晰的思路.

这里我想以一个实际的最简单的例子来说明组件开发的一般套路.jlleblanc 也曾写过我以为相当不错的文章,可惜是E文,而且其编辑代码的方式方法未必适合我们中国开发者.

1. 规划你的数据库 (万丈高楼平地起)
2. 建立CLASS数据表文件 (此文件将直接关联到我们对数据库的CRUD操作即C-create R-read U-update D-delete操作,熟悉开发框架的朋友应该知道了)
3. 规划你的文件结构 (似乎不是很重要,但你要想开发个fireboard/virtueMarket其重要性就可想而知了)
4.编码 (编写各个文件,如果是使用模板引擎的那还要编辑一些静态模板文件)
5 测试

首先热身一下编写一个小的后台组件完成这样一个小功能(就是往数据库写数据),就一个表,表结构如下
|————-|—————|
|    id           |        name |
|————-|—————| (more…)

关于Joomla mvc组件的开发教程

// November 8th, 2008 // No Comments » // Joomla!集锦

源程序下载

http://www.besthdd.com/download.php?id=1AEDFCD41

什么是MVC
模型视图控制器(Model-view-controller,MVC)是一种软件工程中使用的经典设计模式.在用来表现大量数据的复杂计算机应用中,人们希望可以分离数据(model)和相关的用户界面(view),以便于改变用户界面的时候不影响数据处理逻辑,或者改变数据的时候也不需要修改用户界面.MVC通过一个中间层,也就是所谓的控制器 (controller),来分离数据访问业务逻辑部分与表现数据的用户界面部分,从而解决了这个问题.通常使用的时候,MVC分解一个应用程序为三个层次:表现层(UI),域和数据访问.在表现层又进一步分离成为视图和控制器.对一个应用来说,MVC比一般的设计模式更加关注该应用的体系结构.

模型(model)
该应用所操作的相关信息.在Joomla中,是指MySQL数据表.Joomla模型类基本包含表设计,以前是mosTable,现在是josTable.
视图(view)
把模型转化成一种适合用户交互的形式,一般是某种用户界面元素.在 Joomla中,是指视图类的集合以及一个或多个web模板.
控制器(controller)
针对事件的回应和过程,一般是用户动作,可以引起模型的改变.在Joomla中,一般是触发器任务,你唯一需要做的是在控制器类中创建与任务同名的方法函数.
Joomla MVC工作如下
用户访问组件(不包括任何任务或者控制器变量)
构建默认控制类, 然后控制器调用默认视图,并且进行web显示.
用户点击来进行任务控制.一种控制需要在URL中包含相应任务名和控制器变量, 或者只有任务名.(例如:index.php?option=com_mvc&controller=books&task=view)
该控制器通过后,Joomla继续寻找新的控制器文件并且构建它,然后再调用响应的任务. (more…)