极湖

无不用其“极”

Subscribe to RSS feed

Posts tagged with "PHP"

用 PHP 判断客户端 Cookie 是否有效

,

刚开始以为很简单,只要判断 $_COOKIE 是否为空就行了,结果不行。
如果客户端起初设置 Cookie 有效,中途删除 Cookie 后再提交请求,以上方法就会导致误判。

正确的做法是分两步,$_COOKIE 为空的情况下,在URL上加一个参数,再次提交提交请求(用redirect实现),
如果 $_COOKIE 还是为空,那就可以判定客户端 Cookie 无效。

示例代码如下:
<?php
setcookie('test', 1, time()+3600);
if(empty($_COOKIE)){
    if(isset($_GET['chk_cookie'])) {
        echo "客户端 Cookie 无效";
    } else {
        $testUri = $_SERVER['REQUEST_URI'];
        $testUri .= ((strpos($testUri, '?') === false) ? '?' : '&') .'chk_cookie=1';
        header('Location:'.$testUri);
    }
} else {
    echo "客户端 Cookie 有效";
}

将数组转换为 XML 的 PHP 函数

,

函数:
/**
 * 将数组转换成 XML
 *
 * @param array $data             数组
 * @param array $encoding         编码(默认是 UTF-8)
 * @param string $rootNodeName    根节点名称(默认是 data)
 * @param SimpleXMLElement $xml   XML对象(递归用)
 * @return string XML
 */
function toXml($data, $encoding = 'UTF-8', $rootNodeName = 'data', $xml = null)
{
    if ($xml == null) {
        $xml = simplexml_load_string('<?xml version="1.0" encoding="'.$encoding.'"?><'.$rootNodeName.' />');
    }
    
    foreach($data as $key => $value) {
        if (is_numeric($key)) {
            $key = 'item_' . (string) $key;
        }
        
        $key = preg_replace('/\W+/i', '', $key);
        
        if (is_array($value)) {
            $node = $xml->addChild($key);
            toXml($value, $encoding, $rootNodeName, $node);
        } else {
            //$value = htmlentities($value);
            $xml->addChild($key, $value);
        }
    }
    return $xml->asXML();
}


测试代码:
$arr = array(
    'name' => '张三',
    'age' => 18,
    'sex' => '男'
);
header("Content-type: text/xml; charset=UTF-8");
echo toXml($arr);

输出:
<?xml version="1.0" encoding="UTF-8"?>
<data>
    <name>张三</name>
    <age>18</age>
    <sex>男</sex>
</data>

通过 PHP 下载文件的方法

,

基本原理(缓冲区内的字符串内容输出为 txt 文件):
header('Content-Disposition: attachment; filename="hoge.txt"');
header('Content-Type: application/octet-stream');
header('Content-Transfer-Encoding: binary');
header('Content-Length: '.strlen($buf));
print $buf;

文件下载方法如下:
header('Content-Disposition: attachment; filename="'.basename($filepath).'"');
header('Content-Type: application/octet-stream');
header('Content-Transfer-Encoding: binary');
header('Content-Length: '.filesize($filepath));
readfile($filepath);

可以使用 PEAR::File_Archive 的环境,推荐使用以下方法。

优点:
header 之类的东西不用自己写。
可通过改变 toArchive() 的第一个参数可实现压缩之后再下载。
(支持 tar, gz, tgz, bz2, tbz, zip, gzip, ar, deb 等格式)

将缓冲区的内容以文件 a.txt 进行下载。
require_once 'File/Archive.php';
$fa = new File_Archive();
$fa->extract($fa->readMemory($buf, 'a.txt'), $fa->toOutput());

以 zip 格式下载文件。
require_once 'File/Archive.php';
File_Archive::setOption("zipCompressionLevel", 9);
$fa = new File_Archive();
$fa->extract($filepath, $fa->toArchive("hoge.zip", $fa->toOutput()));

多个文件合并压缩之后再下载。
require_once 'File/Archive.php';
$fa = new File_Archive();
$src = $fa->readMulti();
$src->addSource(File_Archive::read("huge1.txt", "hoge/huge1.txt"));
$src->addSource(File_Archive::read("huge2.txt", "hoge/huge2.txt"));
$src->addSource(File_Archive::read("huge3.txt", "hoge/huge3.txt"));
$fa->extract($src, $fa->toArchive("hoge.zip", $fa->toOutput()));

extract() 的第一个参数是输入方法,第二个参数是输出方法。

稍微修改以上例子即可实现各种组合形式的下载。

除了下载用的 toOutput(),还有 toFile(),toMail(),toVariable() 等方法,因此具有很广的应用范围。

以上内容翻译整理自 PHPでファイルダウンロードさせる方法

PHP 取文件扩展名的几种方法

方法一:
$ext = substr($filename, strrpos($filename, '.') + 1);

方法二:
$ext = substr(strrchr($filename, '.'), 1);

方法三:
$ext = pathinfo($filename, PATHINFO_EXTENSION);


扩展名一般用小写,因此,以上语句最好再加上 strtolower,即:
$ext = strtolower(...);

EmEditor 的配色方案 —— Monokai

, ,

用了好多年 EmEditor,也没怎么搞明白它的配色方案是怎么实现,直到看到这篇文章《emeditor的monokai配色》。
Monokai 也是我喜欢的配色方案,于是导入了这个配色方案。由于该方案只针对 Python 配色,别的语言表现不是太好,为此我修改了这个配色方案,并重新定义了 php 的关键字。
效果如图:

附上配色文件。
Monokai.eetheme
php.esy

※追加: java, Python, xml 关键字定义
java.esy
Python.esy
xml.esy

用文件和数据库实现 Zend Framework 的 SESSION 管理

, ,

一. 用文件管理 SESSION
只需在 application.ini 中作以下设置,并保证目标路径可写即可。
; SESSION 设置
resources.session.save_path = APPLICATION_PATH "/../data/sessions"
resources.session.use_trans_sid = 1
resources.session.use_only_cookies = 0
resources.session.remember_me_seconds = 864000  ; 10 天
resources.session.gc_maxlifetime = 864000


二. 用数据库管理 SESSION
首先,建表:
-- SESSION 数据表
CREATE TABLE session (
    id char(32),
    modified int,
    lifetime int,
    data text,
    PRIMARY KEY (id)
);


然后,在 application.ini 中作以下设置:
; SESSION 设置
resources.session.saveHandler.class = "Zend_Session_SaveHandler_DbTable"
resources.session.saveHandler.options.name = "session"
resources.session.saveHandler.options.primary = "id"
resources.session.saveHandler.options.modifiedColumn = "modified"
resources.session.saveHandler.options.dataColumn = "data"
resources.session.saveHandler.options.lifetimeColumn = "lifetime"

resources.session.use_trans_sid = 1
resources.session.use_only_cookies = 0
resources.session.remember_me_seconds = 864000  ; 10 天
resources.session.gc_maxlifetime = 864000


两种方式,各有优缺点,数据库压力较大的情况下推荐用文件方式,有负载均衡的情况下推荐用数据库。
多台Web服务器用文件方式也可以用文件存储 SESSION,不过要设置 NFS。
还有人用 Memcache 管理 SESSION,如果网站要求反应速度快,可以考虑。
网上有人说管理 SESSION 的最好方式是 数据库 + Memcache 的组合。

Zend Framework 的 Plugin 内实现 URL 跳转的方法

,

URL 跳转在 Controller 内部很容易实现,如下即可:
$this->_redirect('/anothor/page');


在 Plugin 里面没这么简单,不过也有办法,总结如下:

方法一)
跳转到某个 module 的某个 controller 的某个 action,Login 的处理常用这个方法。
$request->setModuleName('xxx');
$request->setControllerName('xxx');
$request->setActionName('xxx');

※ $request 是 Plugin 函数的参数。例如 dispatchLoopStartup(Zend_Controller_Request_Abstract $request) ...

方法二)
不执行所请求的 Action,跳转到任意 URL
$redirector = Zend_Controller_Action_HelperBroker::getStaticHelper('redirector');
$redirector->gotoUrl('http://anothor/page')->redirectAndExit();

方法三)
执行所请求的 Action 之后,跳转到任意 URL
$this->getResponse()->setRedirect('http://anothor/page', 301);


用 PHP 实现文件查找的两种方法

,

废话少说,直接贴代码:
<?php
/**
 * 方法一
 * 用 PHP 的 glob 函数和递归
 *
 * @return array 所有匹配文件的数组
 *
 * @param string $dir     - 目标路径
 * @param string $pattern - 匹配模式
 */
function find($dir, $pattern){
    $dir = escapeshellcmd($dir);
    $files = glob("$dir/$pattern");
    foreach (glob("$dir/{.[^.]*,*}", GLOB_BRACE|GLOB_ONLYDIR) as $sub_dir){
        $arr   = find($sub_dir, $pattern);
        $files = array_merge($files, $arr);
    }
    return $files;
}

/**
 * 方法二
 * 利用 Unix 的 find 命令
 *
 * @return array 所有匹配文件的数组
 *
 * @param string $dir     - 目标路径
 * @param string $pattern - 匹配模式
 */
function find2($dir, $pattern){
    $dir = escapeshellcmd($dir);
    $files = shell_exec("find $dir -name '$pattern' -print");
    $files = explode("\n", trim($files));
    return $files;
}

// 测试代码
$time1 = microtime();
$files = find('/home/ZF/', '*');
$time2 = microtime();
//print_r($files);
echo 'find1 count: '. count($files);
echo "\n";
echo 'find1 time: '. ($time2 - $time1);
echo "\n";

$time1 = microtime();
$files = find('/home/ZF/', '*');
$time2 = microtime();
//print_r($files);
echo 'find2 count: '. count($files);
echo "\n";
echo 'find2 time: '. ($time2 - $time1);
echo "\n";


// 运行结果
$ php find.php
find1 count: 2809
find1 time: 0.064942
find2 count: 2809
find2 time: 0.064129

$ php find.php
find1 count: 2809
find1 time: 0.064989
find2 count: 2809
find2 time: 0.064322

可以看到,两种方法,效率相差不大,用 find 命令实现的方式稍稍快那么一点点。

参考: http://www.redips.net/php/find-files-with-php/

PHP 的日期比较代码

写了一段测试代码如下:
<?php
$date1 = "2010-09-09 10:00:00";
$date2 = date("Y-m-d H:i:s");
echo "date1 = $date1\n";
echo "date2 = $date2\n";

$sdate1 = strtotime($date1);
$sdate2 = strtotime($date2);
echo "sdate1 = $sdate1\n";
echo "sdate2 = $sdate2\n";

if ($sdate1 > $sdate2) {
     echo "date1 > date2\n";
} else {
     echo "date1 < date2\n";
}


原理: 把日期字符串转化为 UNIX 时间戳之后再比较。

将 HTML 变换为普通文本的两个函数

, ,

目的很简单,把 HTML 代码的 TAG 全部去掉,仅保留换行样式(比如 <br> 需转化为 \n), 原先的 <td> 或 <th> 之间加空格。

以下分别用 PHP 和 JavaScript 实现。
PHP:
function htmlToText( $html ){
 	$html = preg_replace( '/\r?\n/' , '' , $html );
 	$html = preg_replace('/<(\/?)(br|hr|div|p|object|marquee|table|tr|ul|ol|li|dd|dl|h[1-9])(\b)/i', $this->LFC.'<${1}${2}${3}', $html);
 	$html = preg_replace('/<\/(td|th)(\b)/i', ' </${1}${2}', $html);
 	return trim(strip_tags($html));
}
JavaScript:
function htmlToText(sHtml) {
 	sHtml = sHtml.replace(/\r?\n/g, '').replace(/<\/?(br|div|p|object|marquee|table|tr|ul|ol|li|dd|dl|h[1-9])\b/gi, function($0, tag){
 		return '\n' + $0;
 	}).replace(/<\/(td|th)\b/gi, function($0, tag){
 		return  ' ' + $0;
 	});
 	var element = document.createElement('div');  
 	element.innerHTML = sHtml;  
 	return element.textContent || element.innerText;
}
February 2012
S M T W T F S
January 2012March 2012
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29