ThinkPHP的一些类与方法
获取客户端IP地址
获取客户端IP地址
$type表示返回类型 0 返回IP地址 1 返回IPV4地址数字
function get_client_ip($type = 0) {
$type = $type ? 1 : 0;
static $ip = NULL;
if ($ip !== NULL) return $ip[$type];
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$pos = array_search('unknown',$arr);
if(false !== $pos) unset($arr[$pos]);
$ip = trim($arr[0]);
}elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
}elseif (isset($_SERVER['REMOTE_ADDR'])) {
$ip = $_SERVER['REMOTE_ADDR'];
}
// IP地址合法验证
$long = ip2long($ip);
$ip = $long ? array($ip, $long) : array('0.0.0.0', 0);
return $ip[$type];
}
文件字节大小格式化
字节格式化 把字节数格式为 B K M G T 描述的大小
function byte_format($size, $dec=2){
$a = array("B", "KB", "MB", "GB", "TB", "PB");
$pos = 0;
while ($size >= 1024) {
$size /= 1024;
$pos++;
}
return round($size,$dec)." ".$a[$pos];
}
或者
function get_size($s,$u='B',$p=1){
$us = array('B'=>'K','K'=>'M','M'=>'G','G'=>'T');
return (($u!=='B')&&(!isset($us[$u]))||($s<1024))?(number_format($s,$p)." $u"):(get_size($s/1024,$us[$u],$p));
}
显示彩虹字符串
用于显示彩虹字符串,支持UTF8和中文,效果如下:
function color_txt($str){
$len = mb_strlen($str);
$colorTxt = '';
for($i=0; $i<$len; $i++) {
$colorTxt .= '<span style="color:'.rand_color().'">'.mb_substr($str,$i,1,'utf-8').'</span>';
}
return $colorTxt;
}
function rand_color(){
return '#'.sprintf("%02X",mt_rand(0,255)).sprintf("%02X",mt_rand(0,255)).sprintf("%02X",mt_rand(0,255));
}
让PHP更快的提供文件下载
一般来说, 我们可以通过直接让URL指向一个位于Document Root下面的文件, 来引导用户下载文件.
但是, 这样做, 就没办法做一些统计, 权限检查, 等等的工作. 于是, 很多时候, 我们采用让PHP来做转发, 为用户提供文件下载.
<?php
$file = "/tmp/dummy.tar.gz";
header("Content-type: application/octet-stream");
header('Content-Disposition: attachment; filename="' . basename($file) . '"');
header("Content-Length: ". filesize($file));
readfile($file);
但是这个有一个问题, 就是如果文件是中文名的话, 有的用户可能下载后的文件名是乱码.
于是, 我们做一下修改(参考: :
<?php
$file = "/tmp/中文名.tar.gz";
$filename = basename($file);
header("Content-type: application/octet-stream");
//处理中文文件名
$ua = $_SERVER["HTTP_USER_AGENT"];
$encoded_filename = urlencode($filename);
$encoded_filename = str_replace("+", "%20", $encoded_filename);
if (preg_match("/MSIE/", $ua)) {
header('Content-Disposition: attachment; filename="' . $encoded_filename . '"');
} else if (preg_match("/Firefox/", $ua)) {
header("Content-Disposition: attachment; filename*=\"utf8''" . $filename . '"');
} else {
header('Content-Disposition: attachment; filename="' . $filename . '"');
}
header('Content-Disposition: attachment; filename="' . $filename . '"');
header("Content-Length: ". filesize($file));
readfile($file);
恩, 现在看起来好多了, 不过还有一个问题, 那就是readfile, 虽然PHP的readfile尝试实现的尽量高效, 不占用PHP本身的内存, 但是实际上它还是需要采用MMAP(如果支持), 或者是一个固定的buffer去循环读取文件, 直接输出.
输出的时候, 如果是Apache + PHP mod, 那么还需要发送到Apache的输出缓冲区. 最后才发送给用户. 而对于Nginx + fpm如果他们分开部署的话, 那还会带来额外的网络IO.
那么, 能不能不经过PHP这层, 直接让Webserver直接把文件发送给用户呢?
今天, 我看到了一个有意思的文章: How I PHP: X-SendFile.
我们可以使用Apache的module mod_xsendfile, 让Apache直接发送这个文件给用户:
<?php
$file = "/tmp/中文名.tar.gz";
$filename = basename($file);
header("Content-type: application/octet-stream");
//处理中文文件名
$ua = $_SERVER["HTTP_USER_AGENT"];
$encoded_filename = urlencode($filename);
$encoded_filename = str_replace("+", "%20", $encoded_filename);
if (preg_match("/MSIE/", $ua)) {
header('Content-Disposition: attachment; filename="' . $encoded_filename . '"');
} else if (preg_match("/Firefox/", $ua)) {
header("Content-Disposition: attachment; filename*=\"utf8''" . $filename . '"');
} else {
header('Content-Disposition: attachment; filename="' . $filename . '"');
}
header('Content-Disposition: attachment; filename="' . basename($file) . '"');
//让Xsendfile发送文件
header("X-Sendfile: $file");
X-Sendfile头将被Apache处理, 并且把响应的文件直接发送给Client.
Lighttpd和Nginx也有类似的模块, 大家有兴趣的可以去找找看
配置htaccess文件隐藏index.php
用于在apache环境下面隐藏URL地址中的index.php
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
</IfModule>
去除PHP代码中的空白和注释
PHP内置了一个php_strip_whitespace方法用于读取php文件并去除代码中的空白和注释,但不支持直接读取内容去除空白和注释,下面的方法则可以支持读取字符串内容,并且ThinkPHP框架内置了该方法。
/**
* 去除代码中的空白和注释
* @param string $content 代码内容
* @return string
*/
function strip_whitespace($content) {
$stripStr = '';
//分析php源码
$tokens = token_get_all($content);
$last_space = false;
for ($i = 0, $j = count($tokens); $i < $j; $i++) {
if (is_string($tokens[$i])) {
$last_space = false;
$stripStr .= $tokens[$i];
} else {
switch ($tokens[$i][0]) {
//过滤各种PHP注释
case T_COMMENT:
case T_DOC_COMMENT:
break;
//过滤空格
case T_WHITESPACE:
if (!$last_space) {
$stripStr .= ' ';
$last_space = true;
}
break;
case T_START_HEREDOC:
$stripStr .= "<<<THINK\n";
break;
case T_END_HEREDOC:
$stripStr .= "THINK;\n";
for($k = $i+1; $k < $j; $k++) {
if(is_string($tokens[$k]) && $tokens[$k] == ';') {
$i = $k;
break;
} else if($tokens[$k][0] == T_CLOSE_TAG) {
break;
}
}
break;
default:
$last_space = false;
$stripStr .= $tokens[$i][1];
}
}
}
return $stripStr;
}
检查字符串是否是UTF8编码
用于判断某个字符串是否采用UTF8编码
function is_utf8($string){
return preg_match('%^(?:
[\x09\x0A\x0D\x20-\x7E] # ASCII
| [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
| \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
| \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
| \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
)*$%xs', $string);
}
XSS安全过滤
来源于网络,用于对字符串进行XSS安全过滤。
function remove_xss($val) {
// remove all non-printable characters. CR(0a) and LF(0b) and TAB(9) are allowed
// this prevents some character re-spacing such as <java\0script>
// note that you have to handle splits with \n, \r, and \t later since they *are* allowed in some inputs
$val = preg_replace('/([\x00-\x08,\x0b-\x0c,\x0e-\x19])/', '', $val);
// straight replacements, the user should never need these since they're normal characters
// this prevents like <IMG SRC=@avascript:alert('XSS')>
$search = 'abcdefghijklmnopqrstuvwxyz';
$search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$search .= '1234567890!@#$%^&*()';
$search .= '~`";:?+/={}[]-_|\'\\';
for ($i = 0; $i < strlen($search); $i++) {
// ;? matches the ;, which is optional
// 0{0,7} matches any padded zeros, which are optional and go up to 8 chars
// @ @ search for the hex values
$val = preg_replace('/(&#[xX]0{0,8}'.dechex(ord($search[$i])).';?)/i', $search[$i], $val); // with a ;
// @ @ 0{0,7} matches '0' zero to seven times
$val = preg_replace('/(�{0,8}'.ord($search[$i]).';?)/', $search[$i], $val); // with a ;
}
// now the only remaining whitespace attacks are \t, \n, and \r
$ra1 = array('javascript', 'vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'style', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base');
$ra2 = array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload');
$ra = array_merge($ra1, $ra2);
$found = true; // keep replacing as long as the previous round replaced something
while ($found == true) {
$val_before = $val;
for ($i = 0; $i < sizeof($ra); $i++) {
$pattern = '/';
for ($j = 0; $j < strlen($ra[$i]); $j++) {
if ($j > 0) {
$pattern .= '(';
$pattern .= '(&#[xX]0{0,8}([9ab]);)';
$pattern .= '|';
$pattern .= '|(�{0,8}([9|10|13]);)';
$pattern .= ')*';
}
$pattern .= $ra[$i][$j];
}
$pattern .= '/i';
$replacement = substr($ra[$i], 0, 2).'<x>'.substr($ra[$i], 2); // add in <> to nerf the tag
$val = preg_replace($pattern, $replacement, $val); // filter out the hex tags
if ($val_before == $val) {
// no replacements were made, so exit the loop
$found = false;
}
}
}
return $val;
}
COOKIE用法示例
cookie方法是ThinkPHP内置的函数,用于完成cookie的设置、获取和删除操作。
设置
cookie('name','value'); //设置cookie
cookie('name','value',3600); // 指定cookie保存时间为1小时
高级设置
cookie('name','value',array('expire'=>3600,'prefix'=>'think_')); // 指定有效期和前缀
// 下面的代码和上面等效
cookie('name','value','expire=3600&prefix=think_')
获取
$value = cookie('name');
无论是否设置了前缀参数,cookie方法会自动判断。
删除
删除某个cookie值,可以用:
cookie('name',null);
如果需要清空cookie,可以用:
cookie(null); // 清空当前设定前缀的所有cookie值
cookie(null,'think_'); // 清空指定前缀的所有cookie值
验证码不能显示?通常都是BOM信息惹的祸此代码能解决验证码不能显示问题.(批量去除BOM信息的代码)
有时,我们在本地测试环境中需要显示验证码的地方没有问题,一旦布置到服务器上去的时候.需要显示验证码的地方无法显示?如果你也碰到同样问题,请往下看.
问题的原因大部分是BOM头信息造成的,通常thinkphp的配置文件都要去除BOM头信息.什么是BOM头信息?百度一下就知道啦.
我通常的解决办法是,布置到服务器上去之后,新建一个去除所有文件的BOM头信息的代码文件.然后运行之即可.
比如:我在服务器根目录新建一个delBom.php文件.运行http://www.xxx.com/delBom.php即可.代码如下:
<?php
if (isset($_GET['dir'])){ //设置文件目录
$basedir=$_GET['dir'];
}else{
$basedir = '.';
}
$auto = 1;
checkdir($basedir);
function checkdir($basedir){
if ($dh = opendir($basedir)) {
while (($file = readdir($dh)) !== false) {
if ($file != '.' && $file != '..'){
if (!is_dir($basedir."/".$file)) {
echo "filename: $basedir/$file ".checkBOM("$basedir/$file")." <br>";
}else{
$dirname = $basedir."/".$file;
checkdir($dirname);
}
}
}
closedir($dh);
}
}
function checkBOM ($filename) {
global $auto;
$contents = file_get_contents($filename);
$charset[1] = substr($contents, 0, 1);
$charset[2] = substr($contents, 1, 1);
$charset[3] = substr($contents, 2, 1);
if (ord($charset[1]) == 239 && ord($charset[2]) == 187 && ord($charset[3]) == 191) {
if ($auto == 1) {
$rest = substr($contents, 3);
rewrite ($filename, $rest);
return ("<font color=red>BOM found, automatically removed._<a href=http://www.k686.com>http://www.k686.com</a></font>");
} else {
return ("<font color=red>BOM found.</font>");
}
}
else return ("BOM Not Found.");
}
function rewrite ($filename, $data) {
$filenum = fopen($filename, "w");
flock($filenum, LOCK_EX);
fwrite($filenum, $data);
fclose($filenum);
}
?>
U方法使用示例,地址方法,在模型或模板中都可以使用
U方法是ThinkPHP中用于自动生成URL地址的方法,能够帮助你因为不同的环境和配置而自动生成对应的URL地址。
特点如下:
1、自动识别当前的URL模式
2、自动识别当前的PATH_INFO分隔符
3、域名和二级域名支持
4、伪静态和锚点支持
5、路由地址支持
因此,在使用U方法的时候,你基本上不需要关注当前使用的是什么URL模式和什么配置参数,按照U方法统一的规则调用即可,在实际生成URL地址的时候U方法会自动识别。
下面是一些基本的用法:
// 当前模块的read操作地址,传入参数id为5
U('read','id=5');
如果要传入变量,则用:
U('read','id='.$vo['id']);
如果你的U方法是在模板调用,一般需要写成:
<a href="{:U('read','id=5')}">阅读文章</a>
生成Blog模块的index操作地址,并传入更多的参数:
U('blog/index','cate_id=5&type=1');
当然,也可以使用数组传参:
U('blog/index',array('cate_id'=>5,'type'=>1));
如果参数比较少,也可以直接在第一个参数中传入:
U('Blog/read?id=5');
U('Blog/cate?cate_id=1&status=1')
支持分组生成:
U('Home/Blog/read?id=5'); // Home分组下面的blog模块的read操作地址
U('Admin/Blog/cate?cate_id=1&status=1');// Admin分组
表示
U方法会自动加上当前配置的伪静态后缀,如果你配置了多个伪静态后缀,则默认会加上第一个,如果需要指定伪静态后缀,也可以使用:
U('Blog/read','id=1','xml');
表示输出伪静态后缀为.xml的URL地址
如果要使用U方法输出路由地址,则需要在第一个参数前加上"/",例如:
U('/news/1');
则表示要生成的URL地址是 news/1 这样的路由地址。
如果需要生成带域名的URL地址,可以使用:
U('Blog/read@blog.thinkphp.cn','id=1');
或者
U('Blog/read@blog','id=1');
表示采用当前域名的blog二级域名地址。
支持锚点生成(注意需要更新最新的Git版本才能支持)
U('Blog/read#review','id=5');
生成的URL地址最后会带上 #review 锚点,便于跳转到评论部分。
设置图片的HTTP缓存,也可以设置JS和CSS的
如果是Apache环境下面,可以在.htaccess文件中添加下面的代码,用于设置图片的HTTP缓存和有效期(需要开启apache的headers模块支持),减少网站的图片资源请求压力,提高访问速度和你的pagespeed值^_^。
<IfModule mod_headers.c>
<FilesMatch ".(gif|jpg|jpeg|png|ico)$">
Header set Cache-Control "max-age=604800"
</FilesMatch>
上面的代码设置了网站的图片使用为期一周的HTTP缓存,当然,你一样可以给js或者css文件加上http缓存哦。
检查字符串中是否有外链
/**
* all_external_link 检测字符串是否包含外链
* @param string $text 文字
* @param string $host 域名
* @return boolean false 有外链 true 无外链
*/
function all_external_link($text = '', $host = '') {
if (empty($host)) $host = $_SERVER['HTTP_HOST'];
$reg = '/http(?:s?):\/\/((?:[A-za-z0-9-]+\.)+[A-za-z]{2,4})/';
preg_match_all($reg, $text, $data);
$math = $data[1];
foreach ($math as $value) {
if($value != $host) return false;
}
return true;
}
在htaccess中设置域名重定向
仅用于Apache环境下面,可以在htaccess文件中添加下面的代码,当访问abc.com的时候会重定向到www.abc.com,当然你也可以设置重定向到其它的域名。
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{HTTP_HOST} ^abc.com$ [NC]
RewriteRule ^(.*)$ http://www.abc.com/$1 [R=301,L]
</IfModule>
PHP获取客户端的IP、地理信息、浏览器信息、本地真实IP
<?php
// 作用取得客户端的ip、地理信息、浏览器、本地真实IP
class get_gust_info {
////获得访客浏览器类型
function GetBrowser(){
if(!empty($_SERVER['HTTP_USER_AGENT'])){
$br = $_SERVER['HTTP_USER_AGENT'];
if (preg_match('/MSIE/i',$br)) {
$br = 'MSIE';
}elseif (preg_match('/Firefox/i',$br)) {
$br = 'Firefox';
}elseif (preg_match('/Chrome/i',$br)) {
$br = 'Chrome';
}elseif (preg_match('/Safari/i',$br)) {
$br = 'Safari';
}elseif (preg_match('/Opera/i',$br)) {
$br = 'Opera';
}else {
$br = 'Other';
}
return $br;
}else{return "获取浏览器信息失败!";}
}
////获得访客浏览器语言
function GetLang(){
if(!empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])){
$lang = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
$lang = substr($lang,0,5);
if(preg_match("/zh-cn/i",$lang)){
$lang = "简体中文";
}elseif(preg_match("/zh/i",$lang)){
$lang = "繁体中文";
}else{
$lang = "English";
}
return $lang;
}else{return "获取浏览器语言失败!";}
}
////获取访客操作系统
function GetOs(){
if(!empty($_SERVER['HTTP_USER_AGENT'])){
$OS = $_SERVER['HTTP_USER_AGENT'];
if (preg_match('/win/i',$OS)) {
$OS = 'Windows';
}elseif (preg_match('/mac/i',$OS)) {
$OS = 'MAC';
}elseif (preg_match('/linux/i',$OS)) {
$OS = 'Linux';
}elseif (preg_match('/unix/i',$OS)) {
$OS = 'Unix';
}elseif (preg_match('/bsd/i',$OS)) {
$OS = 'BSD';
}else {
$OS = 'Other';
}
return $OS;
}else{return "获取访客操作系统信息失败!";}
}
////获得访客真实ip
function Getip(){
if(!empty($_SERVER["HTTP_CLIENT_IP"])){
$ip = $_SERVER["HTTP_CLIENT_IP"];
}
if(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])){ //获取代理ip
$ips = explode(',',$_SERVER['HTTP_X_FORWARDED_FOR']);
}
if($ip){
$ips = array_unshift($ips,$ip);
}
$count = count($ips);
for($i=0;$i<$count;$i++){
if(!preg_match("/^(10|172\.16|192\.168)\./i",$ips[$i])){//排除局域网ip
$ip = $ips[$i];
break;
}
}
$tip = empty($_SERVER['REMOTE_ADDR']) ? $ip : $_SERVER['REMOTE_ADDR'];
if($tip=="127.0.0.1"){ //获得本地真实IP
return $this->get_onlineip();
}else{
return $tip;
}
}
////获得本地真实IP
function get_onlineip() {
$mip = file_get_contents("http://city.ip138.com/city0.asp");
if($mip){
preg_match("/\[.*\]/",$mip,$sip);
$p = array("/\[/","/\]/");
return preg_replace($p,"",$sip[0]);
}else{return "获取本地IP失败!";}
}
////根据ip获得访客所在地地名
function Getaddress($ip=''){
if(empty($ip)){
$ip = $this->Getip();
}
$ipadd = file_get_contents("http://int.dpool.sina.com.cn/iplookup/iplookup.php?ip=".$ip);//根据新浪api接口获取
if($ipadd){
$charset = iconv("gbk","utf-8",$ipadd);
preg_match_all("/[\x{4e00}-\x{9fa5}]+/u",$charset,$ipadds);
return $ipadds; //返回一个二维数组
}else{return "addree is none";}
}
}
$gifo = new get_gust_info();
echo "你的ip:".$gifo->Getip();
echo "<br/>所在地:";
$ipadds = $gifo->Getaddress();
foreach($ipadds[0] as $value){
echo "\r\n ".iconv("utf-8","gbk",$value);
}
echo "<br/>浏览器类型:".$gifo->GetBrowser();
echo "<br/>浏览器语言:".$gifo->GetLang();
echo "<br/>操作系统:".$gifo->GetOs();
?>
URL安全的字符串base64编码和解码
如果直接使用base64_encode和base64_decode方法的话,生成的字符串可能不适用URL地址。下面的方法可以解决该问题: URL安全的字符串编码:
function urlsafe_b64encode($string) {
$data = base64_encode($string);
$data = str_replace(array('+','/','='),array('-','_',''),$data);
return $data;
}
URL安全的字符串解码:
function urlsafe_b64decode($string) {
$data = str_replace(array('-','_'),array('+','/'),$string);
$mod4 = strlen($data) % 4;
if ($mod4) {
$data .= substr('====', $mod4);
}
return base64_decode($data);
}
获取客户端浏览器信息
/**
* 获取客户端浏览器类型
* @param string $glue 浏览器类型和版本号之间的连接符
* @return string|array 传递连接符则连接浏览器类型和版本号返回字符串否则直接返回数组 false为未知浏览器类型
*/
function get_client_browser($glue = null) {
$browser = array();
$agent = $_SERVER['HTTP_USER_AGENT']; //获取客户端信息
/* 定义浏览器特性正则表达式 */
$regex = array(
'ie' => '/(MSIE) (\d+\.\d)/',
'chrome' => '/(Chrome)\/(\d+\.\d+)/',
'firefox' => '/(Firefox)\/(\d+\.\d+)/',
'opera' => '/(Opera)\/(\d+\.\d+)/',
'safari' => '/Version\/(\d+\.\d+\.\d) (Safari)/',
);
foreach($regex as $type => $reg) {
preg_match($reg, $agent, $data);
if(!empty($data) && is_array($data)){
$browser = $type === 'safari' ? array($data[2], $data[1]) : array($data[1], $data[2]);
break;
}
}
return empty($browser) ? false : (is_null($glue) ? $browser : implode($glue, $browser));
}
时间戳友好化格式化函数 显示刚刚,几秒前
在一些微博系统中经常要将时间于现在时间相比显示为多久以前发布的,如显示为:刚刚、5秒前、5小时前、5天前..这种
/**
*
+--------------------------------------------------------------------
* Description 友好显示时间
+--------------------------------------------------------------------
* @param int $time 要格式化的时间戳 默认为当前时间
+--------------------------------------------------------------------
* @return string $text 格式化后的时间戳
+--------------------------------------------------------------------
* @author yijianqing
+--------------------------------------------------------------------
*/
function mdate($time = NULL) {
$text = '';
$time = $time === NULL || $time > time() ? time() : intval($time);
$t = time() - $time; //时间差 (秒)
if ($t == 0)
$text = '刚刚';
elseif ($t < 60)
$text = $t . '秒前'; // 一分钟内
elseif ($t < 60 * 60)
$text = floor($t / 60) . '分钟前'; //一小时内
elseif ($t < 60 * 60 * 24)
$text = floor($t / (60 * 60)) . '小时前'; // 一天内
elseif ($t < 60 * 60 * 24 * 3)
$text = floor($time/(60*60*24)) ==1 ?'昨天 ' . date('H:i', $time) : '前天 ' . date('H:i', $time) ; //昨天和前天
elseif ($t < 60 * 60 * 24 * 30)
$text = date('m月d日 H:i', $time); //一个月内
elseif ($t < 60 * 60 * 24 * 365)
$text = date('m月d日', $time); //一年内
else
$text = date('Y年m月d日', $time); //一年以前
return $text;
}
使用此函数,我们只需在前台用
{$vo.time|mdate}
实现时间友好化显示了
将返回的数据集转换成树结构
/**
* 将返回的数据集转换成树
* @param array $list 数据集
* @param string $pk 主键
* @param string $pid 父节点名称
* @param string $child 子节点名称
* @param integer $root 根节点ID
* @return array 转换后的树
*/
function list_to_tree($list, $pk = 'id', $pid = 'pid', $child = '_child', $root=0) {
$tree = array();// 创建Tree
if(is_array($list)) {
// 创建基于主键的数组引用
$refer = array();
foreach ($list as $key => $data) {
$refer[$data[$pk]] =& $list[$key];
}
foreach ($list as $key => $data) {
// 判断是否存在parent
$parentId = $data[$pid];
if ($root == $parentId) {
$tree[$data[$pk]] =& $list[$key];
}else{
if (isset($refer[$parentId])) {
$parent =& $refer[$parentId];
$parent[$child][] =& $list[$key];
}
}
}
}
return $tree;
}
头像编辑-当不改变头像时
目前是这么做的 将上传判断代码段
/* if(!$upload->upload()) {// 上传错误提示错误信息
$this->error($upload->getErrorMsg());
}else{// 上传成功 获取上传文件信息
$info = $upload->getUploadFileInfo();
} */
改为:
$upload->upload();
$info = $upload->getUploadFileInfo();
这样即使不上传图片也不提示错误, 然后在上传页面 添加
<input type='file' name='face'>
<input type='hidden' name='face' value='{$data.face}'>
在update()方法中做如下判断:
if(is_null($info[0]["savename"])){
$data['face']=$_POST['face'];
}else{
$data['face']=$info[0]["savename"];
}
合并数组函数
调用PHP原生的array_merge时,如果第一个参数为空,则会导致返回结果为空。这个函数做了相应处理。
function MergeArray($list1,$list2)
{
if(!isEmpty($list1) && !isEmpty($list2))
{
return array_merge($list1,$list2);
}
else return (isEmpty($list1)?(isEmpty($list2)?null:$list2):$list1);
}
function isEmpty($data)
{
return null == $data || false == $data || "" == $data;
}
Google翻译插件调用,采用CURL调取
调用Google翻译的接口,需要开启curl支持。
<?php
/*
Google翻译函数 by QQ366958903
$text 要翻译的文本
$tl 目标语言
$sl 原语言
$ie 字符编码
*/
function translate($text='',$tl='zh-CN',$sl='auto',$ie='UTF-8'){
$ch = curl_init('http://translate.google.cn/');
curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS,"&hl=zh-CN&sl={$sl}&ie={$ie}&tl={$tl}&text=".urlencode($text));
$html = curl_exec($ch);
preg_match('#<span id=result_box class="short_text">(.*?)</span></div>#',$html,$doc);
return strip_tags($doc['1'],'<br>');
}
//示例:把文字翻译成英文
$text='你好';
echo translate($text,'en');
?>
语言简写对应表
'auto'=>'检测语言',
'sq'=>'阿尔巴尼亚语',
'ar'=>'阿拉伯语',
'az'=>'阿塞拜疆语 ALPHA',
'ga'=>'爱尔兰语',
'et'=>'爱沙尼亚语',
'be'=>'白俄罗斯语',
'bg'=>'保加利亚语',
'is'=>'冰岛语',
'pl'=>'波兰语',
'fa'=>'波斯语',
'af'=>'布尔文(南非荷兰语)',
'da'=>'丹麦语',
'de'=>'德语',
'ru'=>'俄语',
'fr'=>'法语',
'tl'=>'菲律宾语',
'fi'=>'芬兰语',
'ka'=>'格鲁吉亚语 ALPHA',
'ht'=>'海地克里奥尔语 ALPHA',
'ko'=>'韩语',
'nl'=>'荷兰语',
'gl'=>'加利西亚语',
'ca'=>'加泰罗尼亚语',
'cs'=>'捷克语',
'hr'=>'克罗地亚语',
'lv'=>'拉脱维亚语',
'lt'=>'立陶宛语',
'ro'=>'罗马尼亚语',
'mt'=>'马耳他语',
'ms'=>'马来语',
'mk'=>'马其顿语',
'no'=>'挪威语',
'pt'=>'葡萄牙语',
'ja'=>'日语',
'sv'=>'瑞典语',
'sr'=>'塞尔维亚语',
'sk'=>'斯洛伐克语',
'sl'=>'斯洛文尼亚语',
'sw'=>'斯瓦希里语',
'th'=>'泰语',
'tr'=>'土耳其语',
'cy'=>'威尔士语',
'uk'=>'乌克兰语',
'eu'=>'西班牙的巴斯克语 ALPHA',
'es'=>'西班牙语',
'iw'=>'希伯来语',
'el'=>'希腊语',
'hu'=>'匈牙利语',
'hy'=>'亚美尼亚语 ALPHA',
'it'=>'意大利语',
'yi'=>'意第绪语',
'hi'=>'印地语',
'ur'=>'印度乌尔都语 ALPHA',
'id'=>'印尼语',
'en'=>'英语',
'vi'=>'越南语',
'zh-TW'=>'中文(繁体)',
'zh-CN'=>'中文(简体)',
备份数据库,可备份整个库或者备份部分表
全写在模块内了,可备份整个库,也可以选择部分表备份
纠正一处错误,361行empty 用错了
<?php
class BaksqlAction extends CommonAction {
public $config = ''; //相关配置
public $model = ''; //实例化一个model
public $content; //内容
public $dbName = ''; //数据库名
public $dir_sep = '/'; //路径符号
//初始化数据
function _initialize() {
parent::_initialize();
header("Content-type: text/html;charset=utf-8");
set_time_limit(0); //不超时
ini_set('memory_limit','500M');
$this->config = array(
'path' => C('DB_BACKUP'), //备份文件存在哪里
'isCompress' => 0, //是否开启gzip压缩 【未测试】
'isDownload' => 0 //备份完成后是否下载文件 【未测试】
);
$this->dbName = C('DB_NAME'); //当前数据库名称
$this->model = new Model();
//$sql = 'set interactive_timeout=24*3600'; //空闲多少秒后 断开链接
//$this->model>execute($sql);
}
/* -
* +------------------------------------------------------------------------
* * @ 已备份数据列表
* +------------------------------------------------------------------------
*/
function index() {
$path = $this->config['path'];
$fileArr = $this->MyScandir($path);
foreach ($fileArr as $key => $value) {
if ($key > 1) {
//获取文件创建时间
$fileTime = date('Y-m-d H:i:s', filemtime($path . '/' . $value));
$fileSize = filesize($path . '/' . $value) / 1024;
//获取文件大小
$fileSize = $fileSize < 1024 ? number_format($fileSize, 2) . ' KB' :
number_format($fileSize / 1024, 2) . ' MB';
//构建列表数组
$list[] = array(
'name' => $value,
'time' => $fileTime,
'size' => $fileSize
);
}
}
$this->assign('list', $list);
$this->display();
}
/* -
* +------------------------------------------------------------------------
* * @ 获取数据表
* +------------------------------------------------------------------------
*/
function tablist() {
$list = $this->model->query("SHOW TABLE STATUS FROM {$this->dbName}"); //得到表的信息
//echo $Backup->getLastSql();
$this->assign('list', $list);
$this->display();
}
/* -
* +------------------------------------------------------------------------
* * @ 备份整个数据库
* +------------------------------------------------------------------------
*/
function backall() {
$tables = $this->getTables();
if ($this->backup($tables)) {
$this->success('数据库备份成功!', '/public/ok');
} else {
$this->error('数据库备份失败!');
}
}
/* -
* +------------------------------------------------------------------------
* * @ 按表备份,可批量
* +------------------------------------------------------------------------
*/
function backtables() {
$tab = $_REQUEST['tab'];
if (is_array($tab))
$tables = $tab;
else
$tables[] = $tab;
if ($this->backup($tables)) {
if (is_array($tab))
$this->success('数据库备份成功!');
else
$this->success('数据库备份成功!', '/public/ok');
} else {
$this->error('数据库备份失败!');
}
}
//还原数据库
function recover() {
if ($this->recover_file($_GET['file'])) {
$this->success('数据还原成功!', '/public/ok');
} else {
$this->error('数据还原失败!');
}
}
//删除数据备份
function deletebak() {
if (unlink($this->config['path'] . $this->dir_sep . $_GET['file'])) {
$this->success('删除备份成功!', '/public/ok');
} else {
$this->error('删除备份失败!');
}
}
/* -
* +------------------------------------------------------------------------
* * @ 下载备份文件
* +------------------------------------------------------------------------
*/
function downloadBak() {
$file_name = $_GET['file'];
$file_dir = $this->config['path'];
if (!file_exists($file_dir . "/" . $file_name)) { //检查文件是否存在
return false;
exit;
} else {
$file = fopen($file_dir . "/" . $file_name, "r"); // 打开文件
// 输入文件标签
header('Content-Encoding: none');
header("Content-type: application/octet-stream");
header("Accept-Ranges: bytes");
header("Accept-Length: " . filesize($file_dir . "/" . $file_name));
header('Content-Transfer-Encoding: binary');
header("Content-Disposition: attachment; filename=" . $file_name); //以真实文件名提供给浏览器下载
header('Pragma: no-cache');
header('Expires: 0');
//输出文件内容
echo fread($file, filesize($file_dir . "/" . $file_name));
fclose($file);
exit;
}
}
/* -
* +------------------------------------------------------------------------
* * @ 获取 目录下文件数组
* +------------------------------------------------------------------------
* * @ $FilePath 目录路径
* * @ $Order 排序
* +------------------------------------------------------------------------
* * @ 获取指定目录下的文件列表,返回数组
* +------------------------------------------------------------------------
*/
private function MyScandir($FilePath = './', $Order = 0) {
$FilePath = opendir($FilePath);
while ($filename = readdir($FilePath)) {
$fileArr[] = $filename;
}
$Order == 0 ? sort($fileArr) : rsort($fileArr);
return $fileArr;
}
/* * ******************************************************************************************** */
/* -
* +------------------------------------------------------------------------
* * @ 读取备份文件
* +------------------------------------------------------------------------
* * @ $fileName 文件名
* +------------------------------------------------------------------------
*/
private function getFile($fileName) {
$this->content = '';
$fileName = $this->trimPath($this->config['path'] . $this->dir_sep . $fileName);
if (is_file($fileName)) {
$ext = strrchr($fileName, '.');
if ($ext == '.sql') {
$this->content = file_get_contents($fileName);
} elseif ($ext == '.gz') {
$this->content = implode('', gzfile($fileName));
} else {
$this->error('无法识别的文件格式!');
}
} else {
$this->error('文件不存在!');
}
}
/* -
* +------------------------------------------------------------------------
* * @ 把数据写入磁盘
* +------------------------------------------------------------------------
*/
private function setFile() {
$recognize = '';
$recognize = $this->dbName;
$fileName = $this->trimPath($this->config['path'] . $this->dir_sep . $recognize . '_' . date('YmdHis') . '_' . mt_rand(100000000, 999999999) . '.sql');
$path = $this->setPath($fileName);
if ($path !== true) {
$this->error("无法创建备份目录目录 '$path'");
}
if ($this->config['isCompress'] == 0) {
if (!file_put_contents($fileName, $this->content, LOCK_EX)) {
$this->error('写入文件失败,请检查磁盘空间或者权限!');
}
} else {
if (function_exists('gzwrite')) {
$fileName .= '.gz';
if ($gz = gzopen($fileName, 'wb')) {
gzwrite($gz, $this->content);
gzclose($gz);
} else {
$this->error('写入文件失败,请检查磁盘空间或者权限!');
}
} else {
$this->error('没有开启gzip扩展!');
}
}
if ($this->config['isDownload']) {
$this->downloadFile($fileName);
}
}
private function trimPath($path) {
return str_replace(array('/', '\\', '//', '\\\\'), $this->dir_sep, $path);
}
private function setPath($fileName) {
$dirs = explode($this->dir_sep, dirname($fileName));
$tmp = '';
foreach ($dirs as $dir) {
$tmp .= $dir . $this->dir_sep;
if (!file_exists($tmp) && !@mkdir($tmp, 0777))
return $tmp;
}
return true;
}
//未测试
private function downloadFile($fileName) {
ob_end_clean();
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Length: ' . filesize($fileName));
header('Content-Disposition: attachment; filename=' . basename($fileName));
readfile($fileName);
}
/* -
* +------------------------------------------------------------------------
* * @ 给字符串添加 ` `
* +------------------------------------------------------------------------
* * @ $str 字符串
* +------------------------------------------------------------------------
* * @ 返回 `$str`
* +------------------------------------------------------------------------
*/
private function backquote($str) {
return "`{$str}`";
}
/* -
* +------------------------------------------------------------------------
* * @ 获取数据库的所有表
* +------------------------------------------------------------------------
* * @ $dbName 数据库名称
* +------------------------------------------------------------------------
*/
private function getTables($dbName = '') {
if (!empty($dbName)) {
$sql = 'SHOW TABLES FROM ' . $dbName;
} else {
$sql = 'SHOW TABLES ';
}
$result = $this->model->query($sql);
$info = array();
foreach ($result as $key => $val) {
$info[$key] = current($val);
}
return $info;
}
/* -
* +------------------------------------------------------------------------
* * @ 把传过来的数据 按指定长度分割成数组
* +------------------------------------------------------------------------
* * @ $array 要分割的数据
* * @ $byte 要分割的长度
* +------------------------------------------------------------------------
* * @ 把数组按指定长度分割,并返回分割后的数组
* +------------------------------------------------------------------------
*/
private function chunkArrayByByte($array, $byte = 5120) {
$i = 0;
$sum = 0;
$return = array();
foreach ($array as $v) {
$sum += strlen($v);
if ($sum < $byte) {
$return[$i][] = $v;
} elseif ($sum == $byte) {
$return[++$i][] = $v;
$sum = 0;
} else {
$return[++$i][] = $v;
$i++;
$sum = 0;
}
}
return $return;
}
/* -
* +------------------------------------------------------------------------
* * @ 备份数据 { 备份每张表、视图及数据 }
* +------------------------------------------------------------------------
* * @ $tables 需要备份的表数组
* +------------------------------------------------------------------------
*/
private function backup($tables) {
if (empty($tables))
$this->error('没有需要备份的数据表!');
$this->content = '/* This file is created by MySQLReback ' . date('Y-m-d H:i:s') . ' */';
foreach ($tables as $i => $table) {
$table = $this->backquote($table); //为表名增加 ``
$tableRs = $this->model->query("SHOW CREATE TABLE {$table}"); //获取当前表的创建语句
if (!empty($tableRs[0]["Create View"])) {
$this->content .= "\r\n /* 创建视图结构 {$table} */";
$this->content .= "\r\n DROP VIEW IF EXISTS {$table};/* MySQLReback Separation */ " . $tableRs[0]["Create View"] . ";/* MySQLReback Separation */";
}
if (!empty($tableRs[0]["Create Table"])) {
$this->content .= "\r\n /* 创建表结构 {$table} */";
$this->content .= "\r\n DROP TABLE IF EXISTS {$table};/* MySQLReback Separation */ " . $tableRs[0]["Create Table"] . ";/* MySQLReback Separation */";
$tableDateRow = $this->model->query("SELECT * FROM {$table}");
$valuesArr = array();
$values = '';
if (false != $tableDateRow) {
foreach ($tableDateRow as &$y) {
foreach ($y as &$v) {
if ($v=='') //纠正empty 为0的时候 返回tree
$v = 'null'; //为空设为null
else
$v = "'" . mysql_escape_string($v) . "'"; //非空 加转意符
}
$valuesArr[] = '(' . implode(',', $y) . ')';
}
}
$temp = $this->chunkArrayByByte($valuesArr);
if (is_array($temp)) {
foreach ($temp as $v) {
$values = implode(',', $v) . ';/* MySQLReback Separation */';
if ($values != ';/* MySQLReback Separation */') {
$this->content .= "\r\n /* 插入数据 {$table} */";
$this->content .= "\r\n INSERT INTO {$table} VALUES {$values}";
}
}
}
// dump($this->content);
// exit;
}
}
if (!empty($this->content)) {
$this->setFile();
}
return true;
}
/* -
* +------------------------------------------------------------------------
* * @ 还原数据
* +------------------------------------------------------------------------
* * @ $fileName 文件名
* +------------------------------------------------------------------------
*/
private function recover_file($fileName) {
$this->getFile($fileName);
if (!empty($this->content)) {
$content = explode(';/* MySQLReback Separation */', $this->content);
foreach ($content as $i => $sql) {
$sql = trim($sql);
if (!empty($sql)) {
$mes = $this->model->execute($sql);
if (false === $mes) { //如果 null 写入失败,换成 ''
$table_change = array('null' => '\'\'');
$sql = strtr($sql, $table_change);
$mes = $this->model->execute($sql);
}
if (false === $mes) { //如果遇到错误、记录错误
$log_text = '以下代码还原遇到问题:';
$log_text.="\r\n $sql";
set_log($log_text);
}
}
}
} else {
$this->error('无法读取备份文件!');
}
return true;
}
}
?>
$this->_get(); 用法示例自动过滤
$this->_get('a','b','c');
三个参数:
a、$_GET提交上来的变量名;
b、过滤函数(多个用逗号隔开,如:'trim' / 'trim,String');
c、默认值;
一个简单的返回指定错误代码,错误页面的函数
httpstatus('404');
将向客户端发送一个404错误,并且错误页面是可以自定义的。
将函数放在common.php内,自动加载即可随意调用了
/**
* 返回错误代码,将错误页面放在入口文件目录./Public/httpstatus下,命名为 404.html,403.html,503.html等等
* @param $string 错误代码
* @param $msg 错误信息例如NOT FOUND,可以省略
*/
function httpstatus($string="404",$msg=""){
header("http/1.1 {$string} {$msg}");
include './Public/httpstatus/'.$string.'.html';
exit;
}