php 版本处理类
最后更新于:2022-04-01 14:13:03
### php 版本处理类
例如记录app版本,或某些版本数据,如果使用1.0.0这种版本格式记录入库,在需要筛选查询时会比较麻烦。
而把版本字符串转为数字保存,可以方便版本间的比较和筛选。
例如:要查询3.0.1 与 10.0.1之间的版本,因为3.0.1比10.0.1大(字符串比较),因此需要处理才可以查询。
而把 **3.0.1** 和 **10.0.1** 先转为数字 **30001** 和 **100001**来比较查询,则很方便。
**Version.class.php**
~~~
<?php
/**
* 版本处理类,提供版本与数字互相转换,方便入库后进行比较筛选
* Date: 2015-06-30
* Author: fdipzone
* ver: 1.0
*
* Func:
* public version_to_integer 将版本转为数字
* public integer_to_version 将数字转为版本
* public check 检查版本格式是否正确
* public compare 比较两个版本的值
*/
class Version{ // class start
/**
* 将版本转为数字
* @param String $version 版本
* @return Int
*/
public function version_to_integer($version){
if($this->check($version)){
list($major, $minor, $sub) = explode('.', $version);
$integer_version = $major*10000 + $minor*100 + $sub;
return intval($integer_version);
}else{
throw new ErrorException('version Validate Error');
}
}
/**
* 将数字转为版本
* @param Int $version_code 版本的数字表示
* @return String
*/
public function integer_to_version($version_code){
if(is_numeric($version_code) && $version_code>=10000){
$version = array();
$version[0] = (int)($version_code/10000);
$version[1] = (int)($version_code%10000/100);
$version[2] = $version_code%100;
return implode('.', $version);
}else{
throw new ErrorException('version code Validate Error');
}
}
/**
* 检查版本格式是否正确
* @param String $version 版本
* @return Boolean
*/
public function check($version){
$ret = preg_match('/^[0-9]{1,3}\.[0-9]{1,2}\.[0-9]{1,2}$/', $version);
return $ret? true : false;
}
/**
* 比较两个版本的值
* @param String $version1 版本1
* @param String $version2 版本2
* @return Int -1:1<2, 0:相等, 1:1>2
*/
public function compare($version1, $version2){
if($this->check($version1) && $this->check($version2)){
$version1_code = $this->version_to_integer($version1);
$version2_code = $this->version_to_integer($version2);
if($version1_code>$version2_code){
return 1;
}elseif($version1_code<$version2_code){
return -1;
}else{
return 0;
}
}else{
throw new ErrorException('version1 or version2 Validate Error');
}
}
} // class end
?>
~~~
**demo.php**
~~~
<?php
require 'Version.class.php';
$version = '2.7.1';
$obj = new Version();
// 版本转数字
$version_code = $obj->version_to_integer($version);
echo $version_code.'<br>'; // 20701
// 数字转版本
$version = $obj->integer_to_version($version_code);
echo $version.'<br>'; // 2.7.1
// 检查版本
$version = '1.1.a';
var_dump($obj->check($version)); // false
// 比较两个版本
$version1 = '2.9.9';
$version2 = '10.0.1';
$result = $obj->compare($version1, $version2);
echo $result; // -1
?>
~~~
源码下载地址:点击查看
php 字符编码转换类,支持ANSI、Unicode、Unicode big endian、UTF-8、UTF-8+Bom 互相转换
最后更新于:2022-04-01 14:13:01
**php 字符编码转换类,支持ANSI、Unicode、Unicode big endian、UTF-8、UTF-8+Bom 互相转换。**
**四种常见文本文件编码方式**
**ANSI编码**:
无文件头(文件编码开头标志性字节)
ANSI编码字母数字占一个字节,汉字占两个字节
回车换行符,单字节, 十六进制表示为0d 0a
**UNICODE编码:**
文件头,十六进制表示为FF FE
每一个字符都用两个字节编码
回车换行符, 双字节,十六进制表示为 000d 000a
**Unicode big endian编码:**
文件头十六进制表示为FE FF
后面编码是把字符的高位放在前面,低位放在后面,正好和Unicode编码颠倒
回车换行符,双字节,十六进制表示为0d00 0a00
**UTF-8 编码:**
文件头,十六进制表示为EF BB BF
UTF-8是Unicode的一种变长字符编码,数字、字母、回车、换行都用一个字节表示,汉字占3个字节
回车换行符,单字节,十六进制表示为0d 0a
**转换原理:先把字符编码转为UTF-8,然后再从UTF-8转换为对应的字符编码。**
**CharsetConv.class.php**
~~~
<?php
/**字符编码转换类, ANSI、Unicode、Unicode big endian、UTF-8、UTF-8+Bom互相转换
*Date: 2015-01-28
*Author: fdipzone
*Ver: 1.0
*
*Func:
*public convert 转换
*private convToUtf8 把编码转为UTF-8编码
*private convFromUtf8 把UTF-8编码转换为输出编码
*/
class CharsetConv{ // class start
private $_in_charset = null; // 源编码
private $_out_charset = null; // 输出编码
private $_allow_charset = array('utf-8', 'utf-8bom', 'ansi', 'unicode', 'unicodebe');
/**初始化
* @param String $in_charset 源编码
* @param String $out_charset 输出编码
*/
public function __construct($in_charset, $out_charset){
$in_charset = strtolower($in_charset);
$out_charset = strtolower($out_charset);
// 检查源编码
if(in_array($in_charset, $this->_allow_charset)){
$this->_in_charset = $in_charset;
}
// 检查输出编码
if(in_array($out_charset, $this->_allow_charset)){
$this->_out_charset = $out_charset;
}
}
/**转换
* @param String $str 要转换的字符串
* @return String 转换后的字符串
*/
public function convert($str){
$str = $this->convToUtf8($str); // 先转为utf8
$str = $this->convFromUtf8($str); // 从utf8转为对应的编码
return $str;
}
/**把编码转为UTF-8编码
* @param String $str
* @return String
*/
private function convToUtf8($str){
if($this->_in_charset=='utf-8'){ // 编码已经是utf-8,不用转
return $str;
}
switch($this->_in_charset){
case 'utf-8bom':
$str = substr($str, 3);
break;
case 'ansi':
$str = iconv('GBK', 'UTF-8//IGNORE', $str);
break;
case 'unicode':
$str = iconv('UTF-16le', 'UTF-8//IGNORE', substr($str, 2));
break;
case 'unicodebe':
$str = iconv('UTF-16be', 'UTF-8//IGNORE', substr($str, 2));
break;
default:
break;
}
return $str;
}
/**把UTF-8编码转换为输出编码
* @param String $str
* @return String
*/
private function convFromUtf8($str){
if($this->_out_charset=='utf-8'){ // 输出编码已经是utf-8,不用转
return $str;
}
switch($this->_out_charset){
case 'utf-8bom':
$str = "\xef\xbb\xbf".$str;
break;
case 'ansi':
$str = iconv('UTF-8', 'GBK//IGNORE', $str);
break;
case 'unicode':
$str = "\xff\xfe".iconv('UTF-8', 'UTF-16le//IGNORE', $str);
break;
case 'unicodebe':
$str = "\xfe\xff".iconv('UTF-8', 'UTF-16be//IGNORE', $str);
break;
default:
break;
}
return $str;
}
} // class end
?>
~~~
**demo:**unicode big endian 转为 utf-8+bom
~~~
<?php
require "CharsetConv.class.php";
$str = file_get_contents('source/unicodebe.txt');
$obj = new CharsetConv('unicodebe', 'utf-8bom');
$response = $obj->convert($str);
file_put_contents('response/utf-8bom.txt', $response, true);
?>
~~~
**源码下载地址:[点击查看](http://download.csdn.net/detail/fdipzone/8411291)**
php 获取Youtube某个User所有Video信息
最后更新于:2022-04-01 14:12:58
**php 获取Youtube某个User所有Video信息**
因工作需要,获取Youtube上某个用户的所有视频信息,写了这个类,分享给大家。
**YTUserVideo.class.php**
~~~
<?php
/**获取Youtube某个User所有Video信息
*Date: 2015-01-08
*Author: fdipzone
*Ver: 1.0
*
*Func:
*public getVideosInfo 获取用户所有视频信息
*private getVideoNum 获取用户视频数量
*private getVideoInfo 获取视频信息
*private getContent 视频简介整理
*private unescape unicode转中文
*/
class YTUserVideo{ // class start
private $_user = ''; // 用户名称
/**初始化
* @param String $user 用户名称
*/
public function __construct($user=''){
if($user!=''){
$this->_user = $user;
}else{
throw new Exception("user is empty", 1);
}
}
/**获取user所有视频信息
* @return Array
*/
public function getVideosInfo(){
$info = array();
// 获取视频数量
$videonum = $this->getVideoNum();
// 获取视频信息
for($i=1; $i<=$videonum; $i++){
$videoInfo = $this->getVideoInfo($i);
array_push($info, $videoInfo);
}
return $info;
}
/**获取用户视频数量
* @return int
*/
private function getVideoNum(){
$videos = simplexml_load_file('http://gdata.youtube.com/feeds/base/users/'.$this->_user.'/uploads?max-results=1&start-index=1');
$videonum = $videos->children('openSearch', true)->totalResults;
return $videonum;
}
/**获取视频信息
* @param String $index 视频的序号
* @return Array
*/
private function getVideoInfo($index){
// 获取视频id及简介
$video = simplexml_load_file('http://gdata.youtube.com/feeds/base/users/'.$this->_user.'/uploads?max-results=1&start-index='.$index);
$videoId = str_replace('http://gdata.youtube.com/feeds/base/videos/', '', (string)($video->entry->id));
$videoContent = $this->getContent($video->entry->content);
$videoPublish = strtotime($video->entry->published);
// 根据视频id获取视频信息
$content = file_get_contents('http://youtube.com/get_video_info?video_id='.$videoId);
parse_str($content, $ytarr);
$info = array();
$info['id'] = $videoId;
$info['thumb_photo'] = $ytarr['thumbnail_url']; // 缩略图
$info['middle_photo'] = $ytarr['iurlmq']; // 中图
$info['big_photo'] = $ytarr['iurl']; // 大图
$info['title'] = $ytarr['title']; // 标题
$info['content'] = $videoContent; // 简介
$info['publish_date'] = $videoPublish; // 发布时间
$info['length_seconds'] = $ytarr['length_seconds']; // 视频长度(s)
$info['view_count'] = $ytarr['view_count']; // 观看次数
$info['avg_rating'] = $ytarr['avg_rating']; // 平均评分
$info['embed'] = '//www.youtube.com/embed/'.$videoId; // Embed
return $info;
}
/**获取视频简介
* @param String $content 内容
* @return String
*/
private function getContent($content){
preg_match('/<span>(.*?)<\/span>/is', $content, $matches);
return $this->unescape($matches[1]);
}
/* unicode 转 中文
* @param String $str unicode 字符串
* @return String
*/
private function unescape($str) {
$str = rawurldecode($str);
preg_match_all("/(?:%u.{4})|&#x.{4};|&#\d+;|.+/U",$str,$r);
$ar = $r[0];
foreach($ar as $k=>$v) {
if(substr($v,0,2) == "%u"){
$ar[$k] = iconv("UCS-2BE","UTF-8",pack("H4",substr($v,-4)));
}elseif(substr($v,0,3) == "&#x"){
$ar[$k] = iconv("UCS-2BE","UTF-8",pack("H4",substr($v,3,-1)));
}elseif(substr($v,0,2) == "&#") {
$ar[$k] = iconv("UCS-2BE","UTF-8",pack("n",substr($v,2,-1)));
}
}
return join("",$ar);
}
} // class end
?>
~~~
**demo.php**
~~~
<?php
require 'YTUserVideo.class.php';
$obj = new YTUserVideo('GOtriphk'); // 用户名称GOtriphk https://www.youtube.com/user/GOtriphk/videos
$videosInfo = $obj->getVideosInfo();
echo '<pre>';
print_r($videosInfo);
echo '</pre>';
?>
~~~
**输出:**
~~~
Array
(
[0] => Array
(
[id] => jYDwFozp6PY
[thumb_photo] => http://i.ytimg.com/vi/jYDwFozp6PY/default.jpg
[middle_photo] => http://i.ytimg.com/vi/jYDwFozp6PY/mqdefault.jpg
[big_photo] => http://i.ytimg.com/vi/jYDwFozp6PY/hqdefault.jpg
[title] => 【比卡超ssss突襲尖咀!!!】香港比卡超展
[content] => 香港有比卡超展,同場會展出全球最大、高13米嘅「比卡超立體飛船」,仲會有700隻唔同角色嘅精靈現身,當然亦唔小得又勁多期間限定紀念品可以優先搶以及由橫濱專程到港嘅聖誕版比卡超同粉絲全接觸,總之飛唔飛都一樣有得玩!The ONE x 寵物小精靈 聖誕夢想飛行日期:2014年11月9日至2015年1月4日時間:10am-10pm地點:The ONE UG2 中庭
[publish_date] => 1415257662
[length_seconds] => 124
[view_count] => 603
[avg_rating] => 0.0
[embed] => //www.youtube.com/embed/jYDwFozp6PY
)
.....
~~~
**源码下载地址:[点击查看](https://github.com/xfdipzone/25.YTUserVideo)**
php 获取/设置用户访问页面语言类
最后更新于:2022-04-01 14:12:56
User Language Class 获取/设置用户访问的页面语言,如果用户没有设置访问语言,则读取Accept-Language。根据用户选择的语言显示对应的页面(英文,简体中文,繁体中文)
**UserLang.class.php**
~~~
<?php
/**User Language Class 获取/设置用户访问的页面语言,如果用户没有设置访问语言,则读取Accept-Language
*Date: 2014-05-26
*Author: fdipzone
*Ver: 1.0
*
*Func:
*public get 获取用户访问语言
*public set 设置用户访问语言
*private getAcceptLanguage 获取HTTP_ACCEPT_LANGUAGE
*/
class UserLang{ // class start
private $name = 'userlang'; // cookie name
private $expire = 2592000; // cookie expire 30 days
/**初始化
* @param String $name cookie name
* @param int $expire cookie expire
*/
public function __construct($name='', $expire=null){
// 设置cookie name
if($name!=''){
$this->name = $name;
}
// 设置cookie expire
if(is_numeric($expire) && $expire>0){
$this->expire = intval($expire);
}
}
/**获取用户访问语言 */
public function get(){
// 判断用户是否有设置过语言
if(isset($_COOKIE[$this->name])){
$lang = $_COOKIE[$this->name];
}else{
$lang = $this->getAcceptLanguage();
}
return $lang;
}
/**设置用户访问语言
* @param String $lang 用户访问语言
*/
public function set($lang=''){
$lang = strtolower($lang);
// 只能是英文,简体中文,繁体中文
if(in_array($lang, array('en','sc','tc'))){
setcookie($this->name, $lang, time()+$this->expire);
}
}
/**获取HTTP_ACCEPT_LANGUAGE */
private function getAcceptLanguage(){
$lang = strtolower($_SERVER['HTTP_ACCEPT_LANGUAGE']);
if(in_array(substr($lang,0,5), array('zh-tw','zh_hk'))){
$lang = 'tc';
}elseif(in_array(substr($lang,0,5), array('zh-cn','zh-sg'))){
$lang = 'sc';
}else{
$lang = 'en';
}
return $lang;
}
} // class end
?>
~~~
**demo**
~~~
<?php
require "UserLang.class.php";
$obj = new UserLang('sitelang', 3600);
echo $obj->get().'<br>';
?>
~~~
php zip文件内容比较类
最后更新于:2022-04-01 14:12:54
php zip 文件比较类,比较两个zip文件的内容,返回新增,删除,及相同的文件列表。暂时只支持单层。
**需求:**上传一个zip文件,zip内有很多图片文件。需要对图片文件进行一系列很耗时的处理。当用户再更新zip文件时。判断zip内文件是否一致,只处理不同的文件。这样可以节省资源与时间,因此需要编写一个能够比较zip内文件的类。
**ZipCompare.class.php**
~~~
<?php
/**Zip Compare class 比较两个zip文件的内容,返回新增,删除,及相同的文件列表,暂时只支持单层
*Date: 2014-05-18
*Author: fdipzone
*Ver: 1.0
*
*Func:
*public compare 比较zip文件内容
*private getInfo 获取zip内文件列表
*private parse 分析两个zip的文件内容
*private check 检查zip文件是否正确
*private check_handler 检查服务器是否有安装unzip
*/
class ZipCompare{ // class start
/**比较zip文件内容,列出不相同的部分
* @param String $zipfile1 zip文件1
* @param String $zipfile2 zip文件2
* @return Array
*/
public function compare($zipfile1, $zipfile2){
// 检查是否有安装unzip
if(!$this->check_handler()){
throw new Exception('unzip not install');
}
// 检查zip文件
if(!$this->check($zipfile1) || !$this->check($zipfile2)){
throw new Exception('zipfile not exists or error');
}
// 获取zip内文件列表
$zipinfo1 = $this->getInfo($zipfile1);
$zipinfo2 = $this->getInfo($zipfile2);
// 分析两个zip的文件内容,返回相同及不同的文件列表
return $this->parse($zipinfo1, $zipinfo2);
}
/**获取zip内文件列表
* @param String $zipfile zip文件
* @return Array zip内文件列表
*/
private function getInfo($zipfile){
// unzip -v fields
$fields = array('Length','Method','Size','Cmpr','Date','Time','CRC-32','Name');
// zip verbose
$verbose = shell_exec(sprintf("unzip -v %s | sed '\$d' | sed '\$d' | sed -n '4,\$p'", $zipfile));
// zip info
$zipinfo = array();
$filelist = explode("\n", $verbose);
if($filelist){
foreach($filelist as $rowdata){
if($rowdata==''){
continue;
}
$rowdata = preg_replace('/[ ]{2,}/', ' ', $rowdata); // 将两个或以上空格替换为一个
$tmp = array_slice(explode(' ', $rowdata), 1); // 去掉第一个空格
$file = array_combine($fields, $tmp);
$zipinfo[$file['Name']] = $file['Length'].'_'.$file['CRC-32']; // 文件名,长度,CRC32,用于校验
}
}
return $zipinfo;
}
/**分析两个zip文件内容
* @param String $zipinfo1
* @param String $zipinfo2
* @return Array
*/
private function parse($zipinfo1, $zipinfo2){
$result = array(
'add' => array(), // 新增
'del' => array(), // 缺少
'match' => array() // 匹配
);
if($zipinfo1 && $zipinfo2){
// 在zip1但不在zip2的文件
$result['add'] = array_values(array_diff(array_keys($zipinfo1), array_keys($zipinfo2)));
// 在zip2但不在zip1的文件
$result['del'] = array_values(array_diff(array_keys($zipinfo2), array_keys($zipinfo1)));
// 同时在zip1与zip2的文件
$match_file = array_values(array_diff(array_keys($zipinfo1), $result['add']));
// 检查相同文件名的文件内容是否匹配
for($i=0,$len=count($match_file); $i<$len; $i++){
if($zipinfo1[$match_file[$i]]==$zipinfo2[$match_file[$i]]){ // match
array_push($result['match'], $match_file[$i]);
}else{ // not match, change to add
array_push($result['add'], $match_file[$i]);
}
}
}
return $result;
}
/**检查zip文件是否正确
* @param String $zipfile zip文件
* @return boolean
*/
private function check($zipfile){
// 文件存在且能解压
return file_exists($zipfile) && shell_exec(sprintf('unzip -v %s | wc -l', $zipfile))>1;
}
/**检查服务器是否有安装unzip
* @return boolean
*/
private function check_handler(){
return strstr(shell_exec('unzip -v'), 'version')!='';
}
} // class end
?>
~~~
**demo**
~~~
<?php
require "ZipCompare.class.php";
$obj = new ZipCompare();
$result = $obj->compare('test1.zip','test2.zip');
print_r($result);
?>
~~~
**执行后输出:**
~~~
Array
(
[add] => Array
(
[0] => 9.jpg
)
[del] => Array
(
[0] => 5.jpg
[1] => 6.jpg
[2] => 7.jpg
[3] => 8.jpg
)
[match] => Array
(
[0] => 1.jpg
[1] => 10.jpg
[2] => 11.jpg
[3] => 12.jpg
[4] => 13.jpg
[5] => 14.jpg
[6] => 15.jpg
[7] => 16.jpg
[8] => 17.jpg
[9] => 18.jpg
[10] => 2.jpg
[11] => 3.jpg
[12] => 4.jpg
)
)
~~~
**源码下载地址:[点击查看](http://download.csdn.net/detail/fdipzone/7382731)**
php 导出CSV抽象类
最后更新于:2022-04-01 14:12:52
**php 导出CSV抽象类,根据总记录数与每批次记录数,计算总批次,循环导出。避免内存不足的问题。**
**ExportCSV.class.php**
~~~
<?php
/**php Export CSV abstract class,根据总记录数与每批次记录数,计算总批次,循环导出。
*Date: 2014-05-16
*Author: fdipzone
*Ver: 1.0
*
*Func:
*public setPageSize 设置每批次导出的记录条数
*public setExportName 设置导出的文件名
*public setSeparator 设置分隔符
*public setDelimiter 设置定界符
*public export 执行导出
*private getPageCount 计算导出总批次
*private setHeader 设置导出文件header
*private formatCSV 将数据格式化为csv格式
*private escape 转义字符串
*abstract getExportTotal 获取总记录条数,抽象方法,需继承类实现
*abstract getExportFields 获取导出的列名,抽象方法,需继承类实现
*abstract getExportData 获取每页记录,抽象方法,需继承类实现
*/
abstract class ExportCSV{ // class start
// 定义子类必须要实现的方法
/**获取总记录条数
* @return int
*/
abstract protected function getExportTotal();
/**获取导出的列名
* @return Array
*/
abstract protected function getExportFields();
/**获取每批次数据
* @param int $offset 偏移量
* @param int $limit 获取的记录条数
* @return Array
*/
abstract protected function getExportData($offset, $limit);
// 定义类属性
protected $total = 0; // 总记录数
protected $pagesize = 500; // 每批次导出的记录数
protected $exportName = 'export.csv'; // 导出的文件名
protected $separator = ','; // 设置分隔符
protected $delimiter = '"'; // 设置定界符
/**设置每次导出的记录条数
* @param int $pagesize 每次导出的记录条数
*/
public function setPageSize($pagesize=0){
if(is_numeric($pagesize) && $pagesize>0){
$this->pagesize = $pagesize;
}
}
/**设置导出的文件名
* @param String $filename 导出的文件名
*/
public function setExportName($filename){
if($filename!=''){
$this->exportName = $filename;
}
}
/**设置分隔符
* @param String $separator 分隔符
*/
public function setSeparator($separator){
if($separator!=''){
$this->separator = $separator;
}
}
/**设置定界符
* @param String $delimiter 定界符
*/
public function setDelimiter($delimiter){
if($delimiter!=''){
$this->delimiter = $delimiter;
}
}
/**导出csv */
public function export(){
// 获取总记录数
$this->total = $this->getExportTotal();
// 没有记录
if(!$this->total){
return false;
}
// 计算导出总批次
$pagecount = $this->getPageCount();
// 获取导出的列名
$fields = $this->getExportFields();
// 设置导出文件header
$this->setHeader();
// 循环导出
for($i=0; $i<$pagecount; $i++){
$exportData = '';
if($i==0){ // 第一条记录前先导出列名
$exportData .= $this->formatCSV($fields);
}
// 设置偏移值
$offset = $i*$this->pagesize;
// 获取每页数据
$data = $this->getExportData($offset, $this->pagesize);
// 将每页数据转换为csv格式
if($data){
foreach($data as $row){
$exportData .= $this->formatCSV($row);
}
}
// 导出数据
echo $exportData;
}
}
/**计算总批次 */
private function getPageCount(){
$pagecount = (int)(($this->total-1)/$this->pagesize)+1;
return $pagecount;
}
/**设置导出文件header */
private function setHeader(){
header('content-type:application/x-msexcel');
$ua = $_SERVER['HTTP_USER_AGENT'];
if(preg_match("/MSIE/", $ua)){
header('content-disposition:attachment; filename="'.rawurlencode($this->exportName).'"');
}elseif(preg_match("/Firefox/", $ua)){
header("content-disposition:attachment; filename*=\"utf8''".$this->exportName.'"');
}else{
header('content-disposition:attachment; filename="'.$this->exportName.'"');
}
ob_end_flush();
ob_implicit_flush(true);
}
/**格式化为csv格式数据
* @param Array $data 要转换为csv格式的数组
*/
private function formatCSV($data=array()){
// 对数组每个元素进行转义
$data = array_map(array($this,'escape'), $data);
return $this->delimiter.implode($this->delimiter.$this->separator.$this->delimiter, $data).$this->delimiter."\r\n";
}
/**转义字符串
* @param String $str
* @return String
*/
private function escape($str){
return str_replace($this->delimiter, $this->delimiter.$this->delimiter, $str);
}
} // class end
?>
~~~
**demo**
~~~
<?php
// ExportCSV abstract class
require "ExportCSV.class.php";
// 定义继承类
class myexport extends ExportCSV{
// 要导出的数据,实际情况会从db读取
protected $data = array(
array('1','傲雪星枫"','男'),
array('2','傲雪星枫","','男'),
array('3','傲雪星枫","','男'),
array('4',"傲雪星枫\"\"\r\n换行",'男'),
array('5','傲雪星枫,,','男'),
array('6','傲雪星枫"','男'),
array('7','傲雪星枫','男'),
array('8','傲雪星枫','男'),
array('9','傲雪星枫','男'),
array('10','傲雪星枫','男')
);
/* 返回总导出记录数
* @return int
*/
protected function getExportTotal(){
return count($this->data);
}
/**返回导出的列名
* @return Array
*/
protected function getExportFields(){
$title = array('id','name','gender');
return $title;
}
/* 返回每批次的记录
* @param int $offset 偏移量
* @param int $limit 获取的记录条数
* @return Array
*/
protected function getExportData($offset, $limit){
return array_slice($this->data, $offset, $limit);
}
}
// 导出
$obj = new myexport();
$obj->setPageSize(1);
$obj->setExportName('myexport.csv');
$obj->setSeparator(',');
$obj->setDelimiter('"');
$obj->export();
?>
~~~
**源码下载地址:[点击查看](http://download.csdn.net/detail/fdipzone/7364495)**
php 双向队列类
最后更新于:2022-04-01 14:12:49
(deque,全名double-ended queue)是一种具有队列和栈的性质的数据结构。双向队列中的元素可以从两端弹出,其限定插入和删除操作在表的两端进行。
在实际使用中,还可以有输出受限的双向队列(即一个端点允许插入和删除,另一个端点只允许插入的双向队列)和输入受限的双向队列(即一个端点允许插入和删除,另一个端点只允许删除的双向队列)。而如果限定双向队列从某个端点插入的元素只能从该端点删除,则该双向队列就蜕变为两个栈底相邻的栈了。
**DEQue.class.php**
~~~
<?php
/**php 双向队列。支持限定队列长度,输入受限,输出受限,及输出必须与输入同端几种设置
*Date: 2014-04-30
*Author: fdipzone
*Ver: 1.0
*
*Func:
*public frontAdd 前端入列
*public frontRemove 前端出列
*public rearAdd 后端入列
*pulbic rearRemove 后端出列
*public clear 清空对列
*public isFull 判断对列是否已满
*private getLength 获取对列长度
*private setAddNum 记录入列,输出依赖输入时调用
*private setRemoveNum 记录出列,输出依赖输入时调用
*private checkRemove 检查是否输出依赖输入
*/
class DEQue{ // class start
private $_queue = array(); // 对列
private $_maxLength = 0; // 对列最大长度,0表示不限
private $_type = 0; // 对列类型
private $_frontNum = 0; // 前端插入的数量
private $_rearNum = 0; // 后端插入的数量
/**初始化
* @param $type 对列类型
* 1:两端均可输入输出
* 2:前端只能输入,后端可输入输出
* 3:前端只能输出,后端可输入输出
* 4:后端只能输入,前端可输入输出
* 5:后端只能输出,前端可输入输出
* 6:两端均可输入输出,在哪端输入只能从哪端输出
* @param $maxlength 对列最大长度
*/
public function __construct($type=1, $maxlength=0){
$this->_type = in_array($type, array(1,2,3,4,5,6))? $type : 1;
$this->_maxLength = intval($maxlength);
}
/**前端入列
* @param Mixed $data 数据
* @return boolean
*/
public function frontAdd($data=null){
if($this->_type==3){ // 前端输入限制
return false;
}
if(isset($data) && !$this->isFull()){
array_unshift($this->_queue, $data);
$this->setAddNum(1);
return true;
}
return false;
}
/**前端出列
* @return Array
*/
public function frontRemove(){
if($this->_type==2){ // 前端输出限制
return null;
}
if(!$this->checkRemove(1)){ // 检查是否依赖输入
return null;
}
$data = null;
if($this->getLength()>0){
$data = array_shift($this->_queue);
$this->setRemoveNum(1);
}
return $data;
}
/**后端入列
* @param Mixed $data 数据
* @return boolean
*/
public function rearAdd($data=null){
if($this->_type==5){ // 后端输入限制
return false;
}
if(isset($data) && !$this->isFull()){
array_push($this->_queue, $data);
$this->setAddNum(2);
return true;
}
return false;
}
/**后端出列
* @return Array
*/
public function rearRemove(){
if($this->_type==4){ // 后端输出限制
return null;
}
if(!$this->checkRemove(2)){ // 检查是否依赖输入
return null;
}
$data = null;
if($this->getLength()>0){
$data = array_pop($this->_queue);
$this->setRemoveNum(2);
}
return $data;
}
/**清空对列
* @return boolean
*/
public function clear(){
$this->_queue = array();
$this->_frontNum = 0;
$this->_rearNum = 0;
return true;
}
/**判断对列是否已满
* @return boolean
*/
public function isFull(){
$bIsFull = false;
if($this->_maxLength!=0 && $this->_maxLength==$this->getLength()){
$bIsFull = true;
}
return $bIsFull;
}
/**获取当前对列长度
* @return int
*/
private function getLength(){
return count($this->_queue);
}
/**记录入列,输出依赖输入时调用
* @param int $endpoint 端点 1:front 2:rear
*/
private function setAddNum($endpoint){
if($this->_type==6){
if($endpoint==1){
$this->_frontNum ++;
}else{
$this->_rearNum ++;
}
}
}
/**记录出列,输出依赖输入时调用
* @param int $endpoint 端点 1:front 2:rear
*/
private function setRemoveNum($endpoint){
if($this->_type==6){
if($endpoint==1){
$this->_frontNum --;
}else{
$this->_rearNum --;
}
}
}
/**检查是否输出依赖输入
* @param int $endpoint 端点 1:front 2:rear
*/
private function checkRemove($endpoint){
if($this->_type==6){
if($endpoint==1){
return $this->_frontNum>0;
}else{
return $this->_rearNum>0;
}
}
return true;
}
} // class end
?>
~~~
**demo.php**
~~~
<?php
require "DEQue.class.php";
// 例子1
$obj = new DEQue(); // 前后端都可以输入,无限长度
$obj->frontAdd('a'); // 前端入列
$obj->rearAdd('b'); // 后端入列
$obj->frontAdd('c'); // 前端入列
$obj->rearAdd('d'); // 后端入列
// 入列后数组应为 cabd
$result = array();
$result[] = $obj->rearRemove(); // 后端出列
$result[] = $obj->rearRemove(); // 后端出列
$result[] = $obj->frontRemove(); // 前端出列
$result[] = $obj->frontRemove(); // 前端出列
print_r($result); // 出列顺序应为 dbca
// 例子2
$obj = new DEQue(3, 5); // 前端只能输出,后端可输入输出,最大长度5
$insert = array();
$insert[] = $obj->rearAdd('a');
$insert[] = $obj->rearAdd('b');
$insert[] = $obj->frontAdd('c'); // 因前端只能输出,因此这里会返回false
$insert[] = $obj->rearAdd('d');
$insert[] = $obj->rearAdd('e');
$insert[] = $obj->rearAdd('f');
$insert[] = $obj->rearAdd('g'); // 超过长度,返回false
var_dump($insert);
// 例子3
$obj = new DEQue(6); // 输出依赖输入
$obj->frontAdd('a');
$obj->frontAdd('b');
$obj->frontAdd('c');
$obj->rearAdd('d');
$result = array();
$result[] = $obj->rearRemove();
$result[] = $obj->rearRemove(); // 因为输出依赖输入,这个会返回NULL
$result[] = $obj->frontRemove();
$result[] = $obj->frontRemove();
$result[] = $obj->frontRemove();
var_dump($result);
?>
~~~
**源码下载地址:[点击查看](http://download.csdn.net/detail/fdipzone/7282609)**
php Timer 页面运行时间监测类
最后更新于:2022-04-01 14:12:47
**php Timer 页面运行时间监测类,可按不同key监测不同的运行时间**
**Timer.class.php**
~~~
<?php
/**Timer class, 计算页面运行时间,可按不同key计算不同的运行时间
*Date: 2014-02-28
*Author: fdipzone
*Ver: 1.0
*
*Func:
*public start 记录开始时间
*public end 记录结束时间
*public getTime 计算运行时间
*pulbic printTime 输出运行时间
*private getKey 获取key
*private getMicrotime 获取microtime
*/
class Timer{ // class start
private $_start = array();
private $_end = array();
private $_default_key = 'Timer';
private $_prefix = 'Timer_';
/**记录开始时间
* @param String $key 标记
*/
public function start($key=''){
$flag = $this->getKey($key);
$this->_start[$flag] = $this->getMicrotime();
}
/**记录结束时间
* @param String $key 标记
*/
public function end($key=''){
$flag = $this->getKey($key);
$this->_end[$flag] = $this->getMicrotime();
}
/**计算运行时间
* @param String $key 标记
* @return float
*/
public function getTime($key=''){
$flag = $this->getKey($key);
if(isset($this->_end[$flag]) && isset($this->_start[$flag])){
return (float)($this->_end[$flag] - $this->_start[$flag]);
}else{
return 0;
}
}
/**输出页面运行时间
* @param String $key 标记
* @return String
*/
public function printTime($key=''){
printf("%srun time %f ms\r\n", $key==''? $key : $key.' ', $this->getTime($key)*1000);
}
/**获取key
* @param String $key 标记
* @return String
*/
private function getKey($key=''){
if($key==''){
return $this->_default_key;
}else{
return $this->_prefix.$key;
}
}
/**获取microtime
*/
private function getMicrotime(){
list($usec, $sec) = explode(' ', microtime());
return (float)$usec + (float)$sec;
}
} // class end
?>
~~~
**demo:**
~~~
<?php
require 'Timer.class.php';
$timer = new Timer();
$timer->start();
$timer->start('program1');
usleep(mt_rand(100000,500000));
$timer->end('program1');
$timer->printTime('program1');
$timer->start('program2');
usleep(mt_rand(100000,500000));
$timer->end('program2');
$timer->printTime('program2');
$timer->end();
$timer->printTime();
?>
~~~
**demo运行输出:**
~~~
program1 run time 163.285971 ms
program2 run time 100.347042 ms
run time 264.035940 ms
~~~
**源码下载地址:[点击查看](http://download.csdn.net/detail/fdipzone/6976601)**
php 根据url自动生成缩略图,并处理高并发问题
最后更新于:2022-04-01 14:12:45
**服务器生成缩略图的时机一般分为两种:**
**1.上传文件时生成**
优点:上传时就已经生成需要的缩略图,读取时不需要再判断,减少cpu运算。
缺点:当缩略图尺寸变化时或新增尺寸时,需要重新生成所有的缩略图。
**2.访问时生成**
优点:
1.当有用户访问时才需要生成,没有访问的不用生成,节省空间。
2.当修改缩略图尺寸时,只需要修改设置,无需重新生成所有缩略图。
缺点:当缩略图不存在需要生成时,高并发访问会非常耗服务器资源。
虽然访问时生成会有高并发问题,但其他优点都比第一种方法好,因此只要解决高并发问题就可以。
关于如何根据url自动生成缩略图的原理及实现,可以参考我之前写的[《php 根据url自动生成缩略图》](http://blog.csdn.net/fdipzone/article/details/9397725)。
**高并发处理原理:**
1.当判断需要生成图片时,在tmp/目录创建一个临时标记文件,文件名用md5(需要生成的文件名)来命名,处理结束后再将临时文件删除。
2.当判断要生成的文件在tmp/目录有临时标记文件,表示文件正在处理中,则不调用生成缩略图方法,而等待,直到临时标记文件被删除,生成成功输出。
修改的文件如下,其他与之前一样。
**createthumb.php**
~~~
<?php
define('WWW_PATH', dirname(dirname(__FILE__))); // 站点www目录
require(WWW_PATH.'/PicThumb.class.php'); // include PicThumb.class.php
require(WWW_PATH.'/ThumbConfig.php'); // include ThumbConfig.php
$logfile = WWW_PATH.'/createthumb.log'; // 日志文件
$source_path = WWW_PATH.'/upload/'; // 原路径
$dest_path = WWW_PATH.'/supload/'; // 目标路径
$path = isset($_GET['path'])? $_GET['path'] : ''; // 访问的图片URL
// 检查path
if(!$path){
exit();
}
// 获取图片URI
$relative_url = str_replace($dest_path, '', WWW_PATH.$path);
// 获取type
$type = substr($relative_url, 0, strpos($relative_url, '/'));
// 获取config
$config = isset($thumb_config[$type])? $thumb_config[$type] : '';
// 检查config
if(!$config || !isset($config['fromdir'])){
exit();
}
// 原图文件
$source = str_replace('/'.$type.'/', '/'.$config['fromdir'].'/', $source_path.$relative_url);
// 目标文件
$dest = $dest_path.$relative_url;
if(!file_exists($source)){ // 原图不存在
exit();
}
// 高并发处理
$processing_flag = '/tmp/thumb_'.md5($dest); // 用于判断文件是否处理中
$is_wait = 0; // 是否需要等待
$wait_timeout = 5; // 等待超时时间
if(!file_exists($processing_flag)){
file_put_contents($processing_flag, 1, true);
}else{
$is_wait = 1;
}
if($is_wait){ // 需要等待生成
while(file_exists($processing_flag)){
if(time()-$starttime>$wait_timeout){ // 超时
exit();
}
usleep(300000); // sleep 300 ms
}
if(file_exists($dest)){ // 图片生成成功
ob_clean();
header('content-type:'.mime_content_type($dest));
exit(file_get_contents($dest));
}else{
exit(); // 生成失败退出
}
}
// 创建缩略图
$obj = new PicThumb($logfile);
$obj->set_config($config);
$create_flag = $obj->create_thumb($source, $dest);
unlink($processing_flag); // 删除处理中标记文件
if($create_flag){ // 判断是否生成成功
ob_clean();
header('content-type:'.mime_content_type($dest));
exit(file_get_contents($dest));
}
?>
~~~
**源码下载地址:[点击查看](http://download.csdn.net/detail/fdipzone/6809757)**
php main 与 iframe 相互通讯类(同域/跨域)
最后更新于:2022-04-01 14:12:43
**main 与 iframe 相互通讯类**
之前写过一篇[《iframe与主框架跨域相互访问方法》](http://blog.csdn.net/fdipzone/article/details/17619673),介绍了main与iframe相互通讯的原理,不了解原理的可以先看看。
今天把main与iframe相互通讯的方法封装成类,主要有两个文件,
JS:FrameMessage.js 实现调用方法的接口,如跨域则创建临时iframe,调用同域执行者。
PHP:FrameMessage.class.php 实现接收到跨域请求时,根据参数返回执行方法的JS code。
**功能如下:**
1.支持同域与跨域通讯
2.传递的方法参数支持字符串,JSON,数组等。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-16_573946002a838.jpg)
~~~
FrameMessage.exec('http://127.0.0.1/execB.php', 'myframe', 'fIframe', ['fdipzone', '{"gender":"male","age":"29"}', '["http://blog.csdn.net/fdipzone", "http://weibo.com/fdipzone"]']);
~~~
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-16_57394600430bf.jpg)
~~~
FrameMessage.exec('http://localhost/execA.php', '', 'fMain', ['programmer', '{"first":"PHP","second":"javascript"}', '["EEG","NMG"]']);
~~~
因部分浏览器不支持JSON.stringify 与JSON.parse 方法(如IE6/7),为了兼容,需要包含json2.js,下载地址:[https://github.com/douglascrockford/JSON-js](https://github.com/douglascrockford/JSON-js)
**FrameMessage.js**
~~~
/**Main 与 Iframe 相互通讯类 支持同域与跨域通讯
* Date: 2013-12-29
*Author: fdipzone
*Ver: 1.0
*/
var FrameMessage = (function(){
this.oFrameMessageExec = null; // 临时iframe
/* 执行方法
executor 执行的页面,为空则为同域
frame 要调用的方法的框架名称,为空则为parent
func 要调用的方法名
args 要调用的方法的参数,必须为数组[arg1, arg2, arg3, argn...],方便apply调用
元素为字符串格式,请不要使用html,考虑注入安全的问题会过滤
*/
this.exec = function(executor, frame, func, args){
this.executor = typeof(executor)!='undefined'? executor : '';
this.frame = typeof(frame)!='undefined'? frame : '';
this.func = typeof(func)!='undefined'? func : '';
this.args = typeof(args)!='undefined'? (__fIsArray(args)? args : []) : []; // 必须是数组
if(executor==''){
__fSameDomainExec(); // same domain
}else{
__fCrossDomainExec(); // cross domain
}
}
/* 同域执行 */
function __fSameDomainExec(){
if(this.frame==''){ // parent
parent.window[this.func].apply(this, this.args);
}else{
window.frames[this.frame][this.func].apply(this, this.args);
}
}
/* 跨域执行 */
function __fCrossDomainExec(){
if(this.oFrameMessageExec == null){
this.oFrameMessageExec = document.createElement('iframe');
this.oFrameMessageExec.name = 'FrameMessage_tmp_frame';
this.oFrameMessageExec.src = __fGetSrc();
this.oFrameMessageExec.style.display = 'none';
document.body.appendChild(this.oFrameMessageExec);
}else{
this.oFrameMessageExec.src = __fGetSrc();
}
}
/* 获取执行的url */
function __fGetSrc(){
return this.executor + (this.executor.indexOf('?')==-1? '?' : '&') + 'frame=' + this.frame + '&func=' + this.func + '&args=' + JSON.stringify(this.args) + '&framemessage_rand=' + Math.random();
}
/* 判断是否数组 */
function __fIsArray(obj){
return Object.prototype.toString.call(obj) === '[object Array]';
}
return this;
}());
~~~
**FrameMessage.class.php**
~~~
<?php
/**Frame Message class main 与 iframe 相互通讯类
*Date: 2013-12-29
*Author: fdipzone
*Ver: 1.0
*
*Func:
*public execute 根据参数调用方法
*private returnJs 创建返回的javascript
*private jsFormat 转义参数
*/
class FrameMessage{ // class start
/* execute 根据参数调用方法
* @param String $frame 要调用的方法的框架名称,为空则为parent
* @param String $func 要调用的方法名
* @param JSONstr $args 要调用的方法的参数
* @return String
*/
public static function execute($frame, $func, $args=''){
if(!is_string($frame) || !is_string($func) || !is_string($args)){
return '';
}
// frame 与 func 限制只能是字母数字下划线
if(($frame!='' && !preg_match('/^[A-Za-z0-9_]+$/',$frame)) || !preg_match('/^[A-Za-z0-9_]+$/',$func)){
return '';
}
$params_str = '';
if($args){
$params = json_decode($args, true);
if(is_array($params)){
for($i=0,$len=count($params); $i<$len; $i++){ // 过滤参数,防止注入
$params[$i] = self::jsFormat($params[$i]);
}
$params_str = "'".implode("','", $params)."'";
}
}
if($frame==''){ // parent
return self::returnJs("parent.parent.".$func."(".$params_str.");");
}else{
return self::returnJs("parent.window.".$frame.".".$func."(".$params_str.");");
}
}
/**创建返回的javascript
* @param String $str
* @return String
*/
private static function returnJs($str){
$ret = '<script type="text/javascript">'."\r\n";
$ret .= $str."\r\n";
$ret .= '</script>';
return $ret;
}
/**转义参数
* @param String $str
* @return String
*/
private static function jsFormat($str){
$str = strip_tags(trim($str)); // 过滤html
$str = str_replace('\\s\\s', '\\s', $str);
$str = str_replace(chr(10), '', $str);
$str = str_replace(chr(13), '', $str);
$str = str_replace(' ', '', $str);
$str = str_replace('\\', '\\\\', $str);
$str = str_replace('"', '\\"', $str);
$str = str_replace('\\\'', '\\\\\'', $str);
$str = str_replace("'", "\'", $str);
return $str;
}
} // class end
?>
~~~
**A.html**
~~~
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title> main window </title>
<script type="text/javascript" src="json2.js"></script>
<script type="text/javascript" src="FrameMessage.js"></script>
<script type="text/javascript">
// main js function
function fMain(profession, skill, company){
var skill_p = JSON.parse(skill);
var company_p = JSON.parse(company);
var msg = "main function execute success\n\n";
msg += "profession:" + profession + "\n";
msg += "first skill:" + skill_p.first + "\n";
msg += "second skill:" + skill_p.second + "\n";
msg += "company1:" + company_p[0] + "\n";
msg += "company2:" + company_p[1] + "\n";
alert(msg);
}
// exec iframe function
function exec_iframe(){
// same domain
//FrameMessage.exec('', 'myframe', 'fIframe', ['fdipzone', '{"gender":"male","age":"29"}', '["http://blog.csdn.net/fdipzone", "http://weibo.com/fdipzone"]']);
// cross domain
FrameMessage.exec('http://127.0.0.1/execB.php', 'myframe', 'fIframe', ['fdipzone', '{"gender":"male","age":"29"}', '["http://blog.csdn.net/fdipzone", "http://weibo.com/fdipzone"]']);
}
</script>
</head>
<body>
<p>A.html main</p>
<p><input type="button" value="exec iframe function" onclick="exec_iframe()"></p>
<!-- same domain -->
<!--<iframe src="B.html" name="myframe" width="500" height="100"></iframe>-->
<!-- cross domain -->
<iframe src="http://127.0.0.1/B.html" name="myframe" width="500" height="100"></iframe>
</body>
</html>
~~~
**B.html**
~~~
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title> iframe window </title>
<script type="text/javascript" src="json2.js"></script>
<script type="text/javascript" src="FrameMessage.js"></script>
<script type="text/javascript">
// iframe js function
function fIframe(name, obj, arr){
var obj_p = JSON.parse(obj);
var arr_p = JSON.parse(arr);
var msg = "iframe function execute success\n\n";
msg += "name:" + name + "\n";
msg += "gender:" + obj_p.gender + "\n";
msg += "age:" + obj_p.age + "\n";
msg += "blog:" + arr_p[0] + "\n";
msg += "weibo:" + arr_p[1] + "\n";
alert(msg);
}
// exec main function
function exec_main(){
// same domain
//FrameMessage.exec('', '', 'fMain', ['programmer', '{"first":"PHP","second":"javascript"}', '["EEG","NMG"]']);
// cross domain
FrameMessage.exec('http://localhost/execA.php', '', 'fMain', ['programmer', '{"first":"PHP","second":"javascript"}', '["EEG","NMG"]']);
}
</script>
</head>
<body>
<p>B.html iframe</p>
<p><input type="button" value="exec main function" onclick="exec_main()"></p>
</body>
</html>
~~~
**execA.php 与 execB.php**
~~~
<?php
require 'FrameMessage.class.php';
$frame = isset($_GET['frame'])? $_GET['frame'] : '';
$func = isset($_GET['func'])? $_GET['func'] : '';
$args = isset($_GET['args'])? $_GET['args'] : '';
$result = FrameMessage::execute($frame, $func, $args);
echo $result;
?>
~~~
**源码下载地址:[点击查看](http://download.csdn.net/detail/fdipzone/6787109)**
php 密码生成类
最后更新于:2022-04-01 14:12:40
**php 密码生成类**
**功能:**
1.可设定密码长度。
2.可设定要生成的密码个数,批量生成。
3.可以指定密码的规则,字母,数字,特殊字符等。
**GeneratePassword.class.php**
~~~
<?php
/**Generate Password class,根据指定规则生成password
*Date: 2013-12-23
*Author: fdipzone
*Ver: 1.0
*
*Func:
*public batchGenerate 批量生成密码
*private generate 生成单个密码
*private getLetter 获取字母
*private getNumber 获取数字
*private getSpecial 获取特殊字符
*/
class GeneratePassword{ // class start
// 密码的规则 default
private $_rule = array(
'letter' => 1,
'number' => 1,
'special' => 1
);
private $_length = 8; // 密码长度
private $_num = 1; // 密码数量
private $_special = '!@#$%^&*()_+=-'; //允许的特殊字符
/**初始化
* @param int $length 密码长度
* @param int $num 密码数量
* @param Array $rule 密码规则
* @param String $special 允许的特殊字符
*/
public function __construct($length=8, $num=1, $rule=array(), $special=''){
if(isset($length) && is_numeric($length) && $length>=4 && $length<=50){ // 长度
$this->_length = $length;
}
if(isset($num) && is_numeric($num) && $num>0 && $num<=100){ // 数量
$this->_num = $num;
}
if(isset($special) && is_string($special) && $special!=''){ // 特殊字符
$this->_special = $special;
}
if($rule){ // 规则
$t_rule = array();
if(isset($rule['letter']) && in_array($rule['letter'], array(1,2,3,4,5))){ // 1:可选用 2:必须 3:必须小写 4:必须大写 5:大小写都必须
$t_rule['letter'] = $rule['letter'];
}
if(isset($rule['number']) && in_array($rule['number'], array(1,2))){ // 1:可选用 2:必须
$t_rule['number'] = $rule['number'];
}
if(isset($rule['special']) && in_array($rule['special'], array(1,2))){ // 1:可选用 2:必须
$t_rule['special'] = $rule['special'];
}
if($t_rule){
$this->_rule = $t_rule;
}
}
}
/**批量生成密码
* @return Array
*/
public function batchGenerate(){
$passwords = array();
for($i=0; $i<$this->_num; $i++){
array_push($passwords, $this->generate());
}
return $passwords;
}
/**生成单个密码
* @return String
*/
private function generate(){
$password = '';
$pool = '';
$force_pool = '';
if(isset($this->_rule['letter'])){
$letter = $this->getLetter();
switch($this->_rule['letter']){
case 2:
$force_pool .= substr($letter, mt_rand(0,strlen($letter)-1), 1);
break;
case 3:
$force_pool .= strtolower(substr($letter, mt_rand(0,strlen($letter)-1), 1));
$letter = strtolower($letter);
break;
case 4:
$force_pool .= strtoupper(substr($letter, mt_rand(0,strlen($letter)-1), 1));
$letter = strtoupper($letter);
break;
case 5:
$force_pool .= strtolower(substr($letter, mt_rand(0,strlen($letter)-1), 1));
$force_pool .= strtoupper(substr($letter, mt_rand(0,strlen($letter)-1), 1));
break;
}
$pool .= $letter;
}
if(isset($this->_rule['number'])){
$number = $this->getNumber();
switch($this->_rule['number']){
case 2:
$force_pool .= substr($number, mt_rand(0,strlen($number)-1), 1);
break;
}
$pool .= $number;
}
if(isset($this->_rule['special'])){
$special = $this->getSpecial();
switch($this->_rule['special']){
case 2:
$force_pool .= substr($special, mt_rand(0,strlen($special)-1), 1);
break;
}
$pool .= $special;
}
$pool = str_shuffle($pool); // 随机打乱
$password = str_shuffle($force_pool. substr($pool, 0, $this->_length-strlen($force_pool))); // 再次随机打乱
return $password;
}
/**字母 */
private function getLetter(){
$letter = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz';
return $letter;
}
/**数字 */
private function getNumber(){
$number = '1234567890';
return $number;
}
/**特殊字符 */
private function getSpecial(){
$special = $this->_special;
return $special;
}
} // class end
?>
~~~
**demo:**
~~~
<?php
require 'GeneratePassword.class.php';
$rule = array(
'letter' => 5, // 必须含有大小写字母
'number' => 2, // 必须含有数字
'special' => 2 // 必须含有特殊字符
);
$special = '!@#$%_-';
$obj = new GeneratePassword(8, 10, $rule, $special);
$passwords = $obj->batchGenerate();
echo implode('<br>', $passwords);
?>
~~~
**源码下载地址:[点击查看](http://download.csdn.net/detail/fdipzone/6760311)**
php Cookies 操作类
最后更新于:2022-04-01 14:12:38
**Cookies 操作类**
**功能:**
1.保存,读取,更新,清除cookies数据。
2.可设置前缀。
3.强制超时控制。
4.cookies数据可以是字符串,数组,对象等。
**Cookies.class.php**
~~~
<?php
/**Cookies class 保存,读取,更新,清除cookies数据。可设置前缀。强制超时。数据可以是字符串,数组,对象等。
*Date: 2013-12-22
*Author: fdipzone
*Ver: 1.0
*
*Func:
*public set 设置cookie
*public get 读取cookie
*public update 更新cookie
*public clear 清除cookie
*public setPrefix 设置前缀
*public setExpire 设置过期时间
*private authcode 加密/解密
*private pack 将数据打包
*private unpack 将数据解包
*private getName 获取cookie name,增加prefix处理
*/
class Cookies{ // class start
private $_prefix = ''; // cookie prefix
private $_securekey = 'ekOt4_Ut0f3XE-fJcpBvRFrg506jpcuJeixezgPNyALm'; // encrypt key
private $_expire = 3600; // default expire
/**初始化
* @param String $prefix cookie prefix
* @param int $expire 过期时间
* @param String $securekey cookie secure key
*/
public function __construct($prefix='', $expire=0, $securekey=''){
if(is_string($prefix) && $prefix!=''){
$this->_prefix = $prefix;
}
if(is_numeric($expire) && $expire>0){
$this->_expire = $expire;
}
if(is_string($securekey) && $securekey!=''){
$this->_securekey = $securekey;
}
}
/**设置cookie
* @param String $name cookie name
* @param mixed $value cookie value 可以是字符串,数组,对象等
* @param int $expire 过期时间
*/
public function set($name, $value, $expire=0){
$cookie_name = $this->getName($name);
$cookie_expire = time() + ($expire? $expire : $this->_expire);
$cookie_value = $this->pack($value, $cookie_expire);
$cookie_value = $this->authcode($cookie_value, 'ENCODE');
if($cookie_name && $cookie_value && $cookie_expire){
setcookie($cookie_name, $cookie_value, $cookie_expire);
}
}
/**读取cookie
* @param String $name cookie name
* @return mixed cookie value
*/
public function get($name){
$cookie_name = $this->getName($name);
if(isset($_COOKIE[$cookie_name])){
$cookie_value = $this->authcode($_COOKIE[$cookie_name], 'DECODE');
$cookie_value = $this->unpack($cookie_value);
return isset($cookie_value[0])? $cookie_value[0] : null;
}else{
return null;
}
}
/**更新cookie,只更新内容,如需要更新过期时间请使用set方法
* @param String $name cookie name
* @param mixed $value cookie value
* @return boolean
*/
public function update($name, $value){
$cookie_name = $this->getName($name);
if(isset($_COOKIE[$cookie_name])){
$old_cookie_value = $this->authcode($_COOKIE[$cookie_name], 'DECODE');
$old_cookie_value = $this->unpack($old_cookie_value);
if(isset($old_cookie_value[1]) && $old_cookie_value[1]>0){ // 获取之前的过期时间
$cookie_expire = $old_cookie_value[1];
// 更新数据
$cookie_value = $this->pack($value, $cookie_expire);
$cookie_value = $this->authcode($cookie_value, 'ENCODE');
if($cookie_name && $cookie_value && $cookie_expire){
setcookie($cookie_name, $cookie_value, $cookie_expire);
return true;
}
}
}
return false;
}
/**清除cookie
* @param String $name cookie name
*/
public function clear($name){
$cookie_name = $this->getName($name);
setcookie($cookie_name);
}
/**设置前缀
* @param String $prefix cookie prefix
*/
public function setPrefix($prefix){
if(is_string($prefix) && $prefix!=''){
$this->_prefix = $prefix;
}
}
/**设置过期时间
* @param int $expire cookie expire
*/
public function setExpire($expire){
if(is_numeric($expire) && $expire>0){
$this->_expire = $expire;
}
}
/**获取cookie name
* @param String $name
* @return String
*/
private function getName($name){
return $this->_prefix? $this->_prefix.'_'.$name : $name;
}
/**pack
* @param Mixed $data 数据
* @param int $expire 过期时间 用于判断
* @return
*/
private function pack($data, $expire){
if($data===''){
return '';
}
$cookie_data = array();
$cookie_data['value'] = $data;
$cookie_data['expire'] = $expire;
return json_encode($cookie_data);
}
/**unpack
* @param Mixed $data 数据
* @return array(数据,过期时间)
*/
private function unpack($data){
if($data===''){
return array('', 0);
}
$cookie_data = json_decode($data, true);
if(isset($cookie_data['value']) && isset($cookie_data['expire'])){
if(time()<$cookie_data['expire']){ // 未过期
return array($cookie_data['value'], $cookie_data['expire']);
}
}
return array('', 0);
}
/**加密/解密数据
* @param String $str 原文或密文
* @param String $operation ENCODE or DECODE
* @return String 根据设置返回明文活密文
*/
private function authcode($string, $operation = 'DECODE'){
$ckey_length = 4; // 随机密钥长度 取值 0-32;
$key = $this->_securekey;
$key = md5($key);
$keya = md5(substr($key, 0, 16));
$keyb = md5(substr($key, 16, 16));
$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';
$cryptkey = $keya.md5($keya.$keyc);
$key_length = strlen($cryptkey);
$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', 0).substr(md5($string.$keyb), 0, 16).$string;
$string_length = strlen($string);
$result = '';
$box = range(0, 255);
$rndkey = array();
for($i = 0; $i <= 255; $i++) {
$rndkey[$i] = ord($cryptkey[$i % $key_length]);
}
for($j = $i = 0; $i < 256; $i++) {
$j = ($j + $box[$i] + $rndkey[$i]) % 256;
$tmp = $box[$i];
$box[$i] = $box[$j];
$box[$j] = $tmp;
}
for($a = $j = $i = 0; $i < $string_length; $i++) {
$a = ($a + 1) % 256;
$j = ($j + $box[$a]) % 256;
$tmp = $box[$a];
$box[$a] = $box[$j];
$box[$j] = $tmp;
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
}
if($operation == 'DECODE') {
if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
return substr($result, 26);
} else {
return '';
}
} else {
return $keyc.str_replace('=', '', base64_encode($result));
}
}
} // class end
?>
~~~
**demo.php**
~~~
<?php
require 'Cookies.class.php';
$type = isset($_GET['type'])? strtolower($_GET['type']) : '';
if(!in_array($type, array('set','get','update','clear'))){
exit('type not exists');
}
$obj = new Cookies('member', 10); // obj
switch($type){
case 'set': // 设置
$data = array(
'name' => 'fdipzone',
'gender' => 'male'
);
$obj->set('me', $data, 5);
echo 'set cookies';
break;
case 'get': // 读取
$result = $obj->get('me');
echo '<pre>';
print_r($result);
echo '</pre>';
echo 'get cookies';
break;
case 'update': // 更新
$data = array(
'name' => 'angelababy',
'gender' => 'female'
);
$flag = $obj->update('me', $data);
if($flag){
echo 'update cookies success';
}else{
echo 'update cookies false';
}
break;
case 'clear': // 清除
$obj->clear('me');
echo 'clear cookies';
break;
}
?>
~~~
**源码下载地址:[点击查看](http://download.csdn.net/detail/fdipzone/6754787)**
php HTTP请求类,支持GET,POST,Multipart/form-data
最后更新于:2022-04-01 14:12:36
**PHP HTTP请求类,支持GET,POST,Multipart/form-data**
**HttpRequest.class.php**
~~~
<?php
/**HttpRequest class, HTTP请求类,支持GET,POST,Multipart/form-data
*Date: 2013-09-25
*Author: fdipzone
*Ver: 1.0
*
*Func:
*public setConfig 设置连接参数
*public setFormdata 设置表单数据
*public setFiledata 设置文件数据
*public send 发送数据
*private connect 创建连接
*private disconnect 断开连接
*private sendGet get 方式,处理发送的数据,不会处理文件数据
*private sendPost post 方式,处理发送的数据
*private sendMultipart multipart 方式,处理发送的数据,发送文件推荐使用此方式
*/
class HttpRequest{ // class start
private $_ip = '';
private $_host = '';
private $_url = '';
private $_port = '';
private $_errno = '';
private $_errstr = '';
private $_timeout = 15;
private $_fp = null;
private $_formdata = array();
private $_filedata = array();
// 设置连接参数
public function setConfig($config){
$this->_ip = isset($config['ip'])? $config['ip'] : '';
$this->_host = isset($config['host'])? $config['host'] : '';
$this->_url = isset($config['url'])? $config['url'] : '';
$this->_port = isset($config['port'])? $config['port'] : '';
$this->_errno = isset($config['errno'])? $config['errno'] : '';
$this->_errstr = isset($config['errstr'])? $config['errstr'] : '';
$this->_timeout = isset($confg['timeout'])? $confg['timeout'] : 15;
// 如没有设置ip,则用host代替
if($this->_ip==''){
$this->_ip = $this->_host;
}
}
// 设置表单数据
public function setFormData($formdata=array()){
$this->_formdata = $formdata;
}
// 设置文件数据
public function setFileData($filedata=array()){
$this->_filedata = $filedata;
}
// 发送数据
public function send($type='get'){
$type = strtolower($type);
// 检查发送类型
if(!in_array($type, array('get','post','multipart'))){
return false;
}
// 检查连接
if($this->connect()){
switch($type){
case 'get':
$out = $this->sendGet();
break;
case 'post':
$out = $this->sendPost();
break;
case 'multipart':
$out = $this->sendMultipart();
break;
}
// 空数据
if(!$out){
return false;
}
// 发送数据
fputs($this->_fp, $out);
// 读取返回数据
$response = '';
while($row = fread($this->_fp, 4096)){
$response .= $row;
}
// 断开连接
$this->disconnect();
$pos = strpos($response, "\r\n\r\n");
$response = substr($response, $pos+4);
return $response;
}else{
return false;
}
}
// 创建连接
private function connect(){
$this->_fp = fsockopen($this->_ip, $this->_port, $this->_errno, $this->_errstr, $this->_timeout);
if(!$this->_fp){
return false;
}
return true;
}
// 断开连接
private function disconnect(){
if($this->_fp!=null){
fclose($this->_fp);
$this->_fp = null;
}
}
// get 方式,处理发送的数据,不会处理文件数据
private function sendGet(){
// 检查是否空数据
if(!$this->_formdata){
return false;
}
// 处理url
$url = $this->_url.'?'.http_build_query($this->_formdata);
$out = "GET ".$url." http/1.1\r\n";
$out .= "host: ".$this->_host."\r\n";
$out .= "connection: close\r\n\r\n";
return $out;
}
// post 方式,处理发送的数据
private function sendPost(){
// 检查是否空数据
if(!$this->_formdata && !$this->_filedata){
return false;
}
// form data
$data = $this->_formdata? $this->_formdata : array();
// file data
if($this->_filedata){
foreach($this->_filedata as $filedata){
if(file_exists($filedata['path'])){
$data[$filedata['name']] = file_get_contents($filedata['path']);
}
}
}
if(!$data){
return false;
}
$data = http_build_query($data);
$out = "POST ".$this->_url." http/1.1\r\n";
$out .= "host: ".$this->_host."\r\n";
$out .= "content-type: application/x-www-form-urlencoded\r\n";
$out .= "content-length: ".strlen($data)."\r\n";
$out .= "connection: close\r\n\r\n";
$out .= $data;
return $out;
}
// multipart 方式,处理发送的数据,发送文件推荐使用此方式
private function sendMultipart(){
// 检查是否空数据
if(!$this->_formdata && !$this->_filedata){
return false;
}
// 设置分割标识
srand((double)microtime()*1000000);
$boundary = '---------------------------'.substr(md5(rand(0,32000)),0,10);
$data = '--'.$boundary."\r\n";
// form data
$formdata = '';
foreach($this->_formdata as $key=>$val){
$formdata .= "content-disposition: form-data; name=\"".$key."\"\r\n";
$formdata .= "content-type: text/plain\r\n\r\n";
if(is_array($val)){
$formdata .= json_encode($val)."\r\n"; // 数组使用json encode后方便处理
}else{
$formdata .= rawurlencode($val)."\r\n";
}
$formdata .= '--'.$boundary."\r\n";
}
// file data
$filedata = '';
foreach($this->_filedata as $val){
if(file_exists($val['path'])){
$filedata .= "content-disposition: form-data; name=\"".$val['name']."\"; filename=\"".$val['filename']."\"\r\n";
$filedata .= "content-type: ".mime_content_type($val['path'])."\r\n\r\n";
$filedata .= implode('', file($val['path']))."\r\n";
$filedata .= '--'.$boundary."\r\n";
}
}
if(!$formdata && !$filedata){
return false;
}
$data .= $formdata.$filedata."--\r\n\r\n";
$out = "POST ".$this->_url." http/1.1\r\n";
$out .= "host: ".$this->_host."\r\n";
$out .= "content-type: multipart/form-data; boundary=".$boundary."\r\n";
$out .= "content-length: ".strlen($data)."\r\n";
$out .= "connection: close\r\n\r\n";
$out .= $data;
return $out;
}
} // class end
?>
~~~
**demo**
~~~
<?php
require('HttpRequest.class.php');
$config = array(
'ip' => 'demo.fdipzone.com', // 如空则用host代替
'host' => 'demo.fdipzone.com',
'port' => 80,
'errno' => '',
'errstr' => '',
'timeout' => 30,
'url' => '/getapi.php',
//'url' => '/postapi.php',
//'url' => '/multipart.php'
);
$formdata = array(
'name' => 'fdipzone',
'gender' => 'man'
);
$filedata = array(
array(
'name' => 'photo',
'filename' => 'photo.jpg',
'path' => 'photo.jpg'
)
);
$obj = new HttpRequest();
$obj->setConfig($config);
$obj->setFormData($formdata);
$obj->setFileData($filedata);
$result = $obj->send('get');
//$result = $obj->send('post');
//$result = $obj->send('multipart');
echo '<pre>';
print_r($result);
echo '</pre>';
?>
~~~
**源文件下载:[点击查看](http://download.csdn.net/detail/fdipzone/6340863)**
php 过滤html标记属性类
最后更新于:2022-04-01 14:12:34
**php 过滤html标记属性类**
**HtmlAttributeFilter.class.php**
~~~
<?php
/**HTML Attribute Filter
*Date: 2013-09-22
*Author: fdipzone
*ver: 1.0
*
*Func:
*public strip 过滤属性
*public setAllow 设置允许的属性
*public setException 设置特例
*public setIgnore 设置忽略的标记
*private findElements 搜寻需要处理的元素
*private findAttributes 搜寻属性
*private removeAttributes 移除属性
*private isException 判断是否特例
*private createAttributes 创建属性
*private protect 特殊字符转义
*/
class HtmlAttributeFilter{ // class start
private $_str = ''; // 源字符串
private $_allow = array(); // 允许保留的属性 例如:array('id','class','title')
private $_exception = array(); // 特例 例如:array('a'=>array('href','class'),'span'=>array('class'))
private $_ignore = array(); // 忽略过滤的标记 例如:array('span','img')
/**处理HTML,过滤不保留的属性
* @param String $str 源字符串
* @return String
*/
public function strip($str){
$this->_str = $str;
if(is_string($this->_str) && strlen($this->_str)>0){ // 判断字符串
$this->_str = strtolower($this->_str); // 转成小写
$res = $this->findElements();
if(is_string($res)){
return $res;
}
$nodes = $this->findAttributes($res);
$this->removeAttributes($nodes);
}
return $this->_str;
}
/**设置允许的属性
* @param Array $param
*/
public function setAllow($param=array()){
$this->_allow = $param;
}
/**设置特例
* @param Array $param
*/
public function setException($param=array()){
$this->_exception = $param;
}
/**设置忽略的标记
* @param Array $param
*/
public function setIgnore($param=array()){
$this->_ignore = $param;
}
/**搜寻需要处理的元素 */
private function findElements(){
$nodes = array();
preg_match_all("/<([^ !\/\>\n]+)([^>]*)>/i", $this->_str, $elements);
foreach($elements[1] as $el_key => $element){
if($elements[2][$el_key]){
$literal = $elements[0][$el_key];
$element_name = $elements[1][$el_key];
$attributes = $elements[2][$el_key];
if(is_array($this->_ignore) && !in_array($element_name, $this->_ignore)){
$nodes[] = array('literal'=>$literal, 'name'=>$element_name, 'attributes'=>$attributes);
}
}
}
if(!$nodes[0]){
return $this->_str;
}else{
return $nodes;
}
}
/**搜寻属性
* @param Array $nodes 需要处理的元素
*/
private function findAttributes($nodes){
foreach($nodes as &$node){
preg_match_all("/([^ =]+)\s*=\s*[\"|']{0,1}([^\"']*)[\"|']{0,1}/i", $node['attributes'], $attributes);
if($attributes[1]){
foreach($attributes[1] as $att_key=>$att){
$literal = $attributes[0][$att_key];
$attribute_name = $attributes[1][$att_key];
$value = $attributes[2][$att_key];
$atts[] = array('literal'=>$literal, 'name'=>$attribute_name, 'value'=>$value);
}
}else{
$node['attributes'] = null;
}
$node['attributes'] = $atts;
unset($atts);
}
return $nodes;
}
/**移除属性
* @param Array $nodes 需要处理的元素
*/
private function removeAttributes($nodes){
foreach($nodes as $node){
$node_name = $node['name'];
$new_attributes = '';
if(is_array($node['attributes'])){
foreach($node['attributes'] as $attribute){
if((is_array($this->_allow) && in_array($attribute['name'], $this->_allow)) || $this->isException($node_name, $attribute['name'], $this->_exception)){
$new_attributes = $this->createAttributes($new_attributes, $attribute['name'], $attribute['value']);
}
}
}
$replacement = ($new_attributes) ? "<$node_name $new_attributes>" : "<$node_name>";
$this->_str = preg_replace('/'.$this->protect($node['literal']).'/', $replacement, $this->_str);
}
}
/**判断是否特例
* @param String $element_name 元素名
* @param String $attribute_name 属性名
* @param Array $exceptions 允许的特例
* @return boolean
*/
private function isException($element_name, $attribute_name, $exceptions){
if(array_key_exists($element_name, $this->_exception)){
if(in_array($attribute_name, $this->_exception[$element_name])){
return true;
}
}
return false;
}
/**创建属性
* @param String $new_attributes
* @param String $name
* @param String $value
* @return String
*/
private function createAttributes($new_attributes, $name, $value){
if($new_attributes){
$new_attributes .= " ";
}
$new_attributes .= "$name=\"$value\"";
return $new_attributes;
}
/**特殊字符转义
* @param String $str 源字符串
* @return String
*/
private function protect($str){
$conversions = array(
"^" => "\^",
"[" => "\[",
"." => "\.",
"$" => "\$",
"{" => "\{",
"*" => "\*",
"(" => "\(",
"\\" => "\\\\",
"/" => "\/",
"+" => "\+",
")" => "\)",
"|" => "\|",
"?" => "\?",
"<" => "\<",
">" => "\>"
);
return strtr($str, $conversions);
}
} // class end
?>
~~~
**demo**
~~~
<?php
require('HtmlAttributeFilter.class.php');
$str = '<div class="bd clearfix" id="index_hilite_ul"><ul class="list"><li><img src="http://su.bdimg.com/static/skin/img/logo_white.png" width="118" height="148"><div class="cover"><a class="text" href="http://www.csdn.net"><strong>yuna</strong><p>love</p></a><strong class="t g">want to know</strong><a href="/login.html" class="ppBtn"><strong class="text">YES</strong></a></div></li></ul></div>';
$obj = new HtmlAttributeFilter();
// 允许id属性
$obj->setAllow(array('id'));
$obj->setException(array(
'a' => array('href'), // a 标签允许有 href属性特例
'ul' => array('class') // ul 标签允许有 class属性特例
));
// img 标签忽略,不过滤任何属性
$obj->setIgnore(array('img'));
echo 'source str:<br>';
echo htmlspecialchars($str).'<br><br>';
echo 'filter str:<br>';
echo htmlspecialchars($obj->strip($str));
?>
~~~
**源文件下载:[点击下载](http://download.csdn.net/detail/fdipzone/6304791)**
php 根据url自动生成缩略图
最后更新于:2022-04-01 14:12:31
原理:设置apache rewrite ,当图片不存在时,调用php创建图片。
例如
原图路径为:http://localhost/upload/news/2013/07/21/1.jpg
缩略图路径为:http://localhost/supload/news/2013/07/21/1.jpg
当访问 http://localhost/supload/news/2013/07/21/1.jpg 时,如图片存在,则显示图片。否则,调用createthumb.php生成图片。
目录结构如下:
www/PicThumb.class.php
www/ThumbConfig.php
www/upload/news/2013/07/21/1.jpg
www/upload/article/2013/07/21/2.jpg
www/supload/.htaccess
www/supload/watermark.png
www/supload/createthumb.php
http://localhost/ 指向 www目录
[**PicThumb.class.php 用法请查看这里**](http://blog.csdn.net/fdipzone/article/details/9316385)
需要开启apache rewrite
~~~
sudo a2enmod rewrite
~~~
**.htaccess**
~~~
<IfModule mod_rewrite.c>
RewriteEngine On
# '-s' (is regular file, with size)
# '-l' (is symbolic link)
# '-d' (is directory)
# 'ornext|OR' (or next condition)
# 'nocase|NC' (no case)
# 'last|L' (last rule)
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ createthumb.php?path=%{REQUEST_URI} [NC,L]
</IfModule>
~~~
**createthumb.php**
~~~
<?php
define('WWW_PATH', dirname(dirname(__FILE__))); // 站点www目录
require(WWW_PATH.'/PicThumb.class.php'); // include PicThumb.class.php
require(WWW_PATH.'/ThumbConfig.php'); // include ThumbConfig.php
$logfile = WWW_PATH.'/createthumb.log'; // 日志文件
$source_path = WWW_PATH.'/upload/'; // 原路径
$dest_path = WWW_PATH.'/supload/'; // 目标路径
$path = isset($_GET['path'])? $_GET['path'] : ''; // 访问的图片URL
// 检查path
if(!$path){
exit();
}
// 获取图片URI
$relative_url = str_replace($dest_path, '', WWW_PATH.$path);
// 获取type
$type = substr($relative_url, 0, strpos($relative_url, '/'));
// 获取config
$config = isset($thumb_config[$type])? $thumb_config[$type] : '';
// 检查config
if(!$config || !isset($config['fromdir'])){
exit();
}
// 原图文件
$source = str_replace('/'.$type.'/', '/'.$config['fromdir'].'/', $source_path.$relative_url);
// 目标文件
$dest = $dest_path.$relative_url;
// 创建缩略图
$obj = new PicThumb($logfile);
$obj->set_config($config);
if($obj->create_thumb($source, $dest)){
ob_clean();
header('content-type:'.mime_content_type($dest));
exit(file_get_contents($dest));
}
?>
~~~
**ThumbConfig.php**
~~~
<?php
$thumb_config = array(
'news' => array(
'fromdir' => 'news', // 来源目录
'type' => 'fit',
'width' => 100,
'height' => 100,
'bgcolor' => '#FF0000'
),
'news_1' => array(
'fromdir' => 'news',
'type' => 'fit',
'width' => 200,
'height' => 200,
'bgcolor' => '#FFFF00'
),
'article' => array(
'fromdir' => 'article',
'type' => 'crop',
'width' => 250,
'height' => 250,
'watermark' => WWW_PATH.'/supload/watermark.png'
)
);
?>
~~~
~~~
访问这三个路径后会按config自动生成缩略图
http://localhost/supload/news/2013/07/21/1.jpg
http://localhost/supload/news_1/2013/07/21/1.jpg
http://localhost/supload/article/2013/07/21/2.jpg
~~~
**源码下载地址:[点击下载](http://download.csdn.net/detail/fdipzone/5791079)**
php 缩略图生成类,支持imagemagick及gd库两种处理
最后更新于:2022-04-01 14:12:29
**功能:**
1.按比例缩小/放大
2.填充背景色
3.按区域裁剪
4.添加水印,包括水印的位置,透明度等
使用**imagemagick/GD库**实现,**imagemagick**地址:[www.imagemagick.org](#)
需要安装imagemagick,安装方法如下:[点击查看](http://blog.csdn.net/fdipzone/article/details/9079143)
**PicThumb.class.php**
~~~
<?php
/**缩略图生成类,支持imagemagick及gd库两种处理
*Date: 2013-07-15
*Author: fdipzone
*Ver: 1.2
*
*Func:
*public set_config: 设置参数
*public create_thumb: 生成缩略图
*private fit: 缩略图片
*private crop: 裁剪图片
*private gd_fit: GD库缩略图片
*private gd_crop: GD库裁剪图片
*private get_size: 获取要转换的size
*private get_crop_offset: 获取裁图的偏移量
*private add_watermark: 添加水印
*private check_handler: 判断处理程序是否已安装
*private create_dirs: 创建目录
*private exists: 判断参数是否存在
*private to_log: 记录log
*private hex2rgb: hex颜色转rgb颜色
*private get_file_ext: 获取图片类型
*
*ver: 1.1 增加GD库处理
*ver: 1.2 增加width,height错误参数处理
* 增加当图片colorspace不为RGB时作转RGB处理
* 修正使用crop保存为gif时出现透明无效区域问题,使用+repage参数,删除透明无效区域即可
*
*tips:建议使用imagemagick
* GD库不支持透明度水印,如果必须使用透明水印,请将水印图片做成有透明度。
* GD库输出gif如加透明水印,会有问题。
*/
class PicThumb{ // class start
private $_log = null; // log file
private $_handler = null; // 进行图片处理的程序,imagemagick/gd库
private $_type = 'fit'; // fit or crop
private $_source = null; // 原图路径
private $_dest = null; // 缩略图路径
private $_watermark = null; // 水印图片
private $_opacity = 75; // 水印圖片透明度,gd库不支持
private $_gravity = 'SouthEast'; // 水印摆放位置 NorthWest, North, NorthEast, West, Center, East, SouthWest, South, SouthEast
private $_geometry = '+10+10'; // 水印定位,gd库不支持
private $_croppos = 'TL'; // 截图的位置 TL TM TR ML MM MR BL BM BR
private $_bgcolor = null; // 填充的背景色
private $_quality = 90; // 生成的图片质量
private $_width = null; // 指定区域宽度
private $_height = null; // 指定区域高度
// 初始化
public function __construct($logfile=''){
if($logfile!=''){
$this->_log = $logfile;
}
}
// 设置参数
public function set_config($param=array()){
$this->_handler = $this->exists($param, 'handler')? strtolower($param['handler']) : null;
$this->_type = $this->exists($param, 'type')? strtolower($param['type']) : 'fit';
$this->_watermark = $this->exists($param, 'watermark')? $param['watermark'] : null;
$this->_opacity = $this->exists($param, 'opacity')? $param['opacity'] : 75;
$this->_gravity = $this->exists($param, 'gravity')? $param['gravity'] : 'SouthEast';
$this->_geometry = $this->exists($param, 'geometry')? $param['geometry'] : '+10+10';
$this->_croppos = $this->exists($param, 'croppos')? $param['croppos'] : 'TL';
$this->_bgcolor = $this->exists($param, 'bgcolor')? $param['bgcolor'] : null;
$this->_quality = $this->exists($param, 'quality')? $param['quality'] : 90;
$this->_width = $this->exists($param, 'width')? $param['width'] : null;
$this->_height = $this->exists($param, 'height')? $param['height'] : null;
}
/**创建缩略图
* @param String $source 原图
* @param String $dest 目标图
* @return boolean
*/
public function create_thumb($source, $dest){
// 检查使用的handler是否已安装
if(!$this->check_handler()){
$this->to_log('handler not installed');
return false;
}
// 判断区域宽高是否正确
if(!is_numeric($this->_width) || !is_numeric($this->_height) || $this->_width<=0 || $this->_height<=0){
$this->to_log('width or height invalid');
return false;
}
// 判断源文件是否存在
if(!file_exists($source)){
$this->to_log($source.' not exists');
return false;
}
// 创建目标文件路径
if(!$this->create_dirs($dest)){
$this->to_log(dirname($dest).' create fail');
return false;
}
$this->_source = $source; // 源文件
$this->_dest = $dest; // 目标文件
// 处理图片
switch($this->_type){
case 'fit':
if($this->_handler=='imagemagick'){
return $this->fit();
}else{
return $this->gd_fit();
}
break;
case 'crop':
if($this->_handler=='imagemagick'){
return $this->crop();
}else{
return $this->gd_crop();
}
break;
default:
$this->to_log($this->_type.' not fit and crop');
return false;
}
}
/**按比例压缩或拉伸图片
* @return boolean
*/
private function fit(){
// 判断是否填充背景
$bgcolor = ($this->_bgcolor!=null)?
sprintf(" -background '%s' -gravity center -extent '%sx%s' ", $this->_bgcolor, $this->_width, $this->_height) : "";
// 判断是否要转为RGB
$source_info = getimagesize($this->_source);
$colorspace = (!isset($source_info['channels']) || $source_info['channels']!=3)? ' -colorspace RGB ' : '';
// 命令行
$cmd = sprintf("convert -resize '%sx%s' '%s' %s -quality %s %s '%s'", $this->_width, $this->_height, $this->_source, $bgcolor, $this->_quality, $colorspace, $this->_dest);
// 记录执行的命令
$this->to_log($cmd);
// 执行命令
exec($cmd);
// 添加水印
$this->add_watermark($this->_dest);
return is_file($this->_dest)? true : false;
}
/**裁剪图片
* @return boolean
*/
private function crop(){
// 获取生成的图片尺寸
list($pic_w, $pic_h) = $this->get_size();
// 获取截图的偏移量
list($offset_w, $offset_h) = $this->get_crop_offset($pic_w, $pic_h);
// 判断是否要转为RGB
$source_info = getimagesize($this->_source);
$colorspace = (!isset($source_info['channels']) || $source_info['channels']!=3)? ' -colorspace RGB ' : '';
// 命令行
$cmd = sprintf("convert -resize '%sx%s' '%s' -quality %s %s -crop %sx%s+%s+%s +repage '%s'", $pic_w, $pic_h, $this->_source, $this->_quality, $colorspace, $this->_width, $this->_height, $offset_w, $offset_h, $this->_dest);
// 记录执行的命令
$this->to_log($cmd);
// 执行命令
exec($cmd);
// 添加水印
$this->add_watermark($this->_dest);
return is_file($this->_dest)? true : false;
}
/**GD库按比例压缩或拉伸图片
* @return boolean
*/
private function gd_fit(){
// 获取生成的图片尺寸
list($pic_w, $pic_h) = $this->get_size();
list($owidth, $oheight, $otype) = getimagesize($this->_source);
switch($otype){
case 1: $source_img = imagecreatefromgif($this->_source); break;
case 2: $source_img = imagecreatefromjpeg($this->_source); break;
case 3: $source_img = imagecreatefrompng($this->_source); break;
default: return false;
}
// 按比例缩略/拉伸图片
$new_img = imagecreatetruecolor($pic_w, $pic_h);
imagecopyresampled($new_img, $source_img, 0, 0, 0, 0, $pic_w, $pic_h, $owidth, $oheight);
// 判断是否填充背景
if($this->_bgcolor!=null){
$bg_img = imagecreatetruecolor($this->_width, $this->_height);
$rgb = $this->hex2rgb($this->_bgcolor);
$bgcolor =imagecolorallocate($bg_img, $rgb['r'], $rgb['g'], $rgb['b']);
imagefill($bg_img, 0, 0, $bgcolor);
imagecopy($bg_img, $new_img, (int)(($this->_width-$pic_w)/2), (int)(($this->_height-$pic_h)/2), 0, 0, $pic_w, $pic_h);
$new_img = $bg_img;
}
// 获取目标图片的类型
$dest_ext = $this->get_file_ext($this->_dest);
// 生成图片
switch($dest_ext){
case 1: imagegif($new_img, $this->_dest, $this->_quality); break;
case 2: imagejpeg($new_img, $this->_dest, $this->_quality); break;
case 3: imagepng($new_img, $this->_dest, (int)(($this->_quality-1)/10)); break;
}
if(isset($source_img)){
imagedestroy($source_img);
}
if(isset($new_img)){
imagedestroy($new_img);
}
// 添加水印
$this->add_watermark($this->_dest);
return is_file($this->_dest)? true : false;
}
/**GD库裁剪图片
* @return boolean
*/
private function gd_crop(){
// 获取生成的图片尺寸
list($pic_w, $pic_h) = $this->get_size();
// 获取截图的偏移量
list($offset_w, $offset_h) = $this->get_crop_offset($pic_w, $pic_h);
list($owidth, $oheight, $otype) = getimagesize($this->_source);
switch($otype){
case 1: $source_img = imagecreatefromgif($this->_source); break;
case 2: $source_img = imagecreatefromjpeg($this->_source); break;
case 3: $source_img = imagecreatefrompng($this->_source); break;
default: return false;
}
// 按比例缩略/拉伸图片
$tmp_img = imagecreatetruecolor($pic_w, $pic_h);
imagecopyresampled($tmp_img, $source_img, 0, 0, 0, 0, $pic_w, $pic_h, $owidth, $oheight);
// 裁剪图片
$new_img = imagecreatetruecolor($this->_width, $this->_height);
imagecopyresampled($new_img, $tmp_img, 0, 0, $offset_w, $offset_h, $this->_width, $this->_height, $this->_width, $this->_height);
// 获取目标图片的类型
$dest_ext = $this->get_file_ext($this->_dest);
// 生成图片
switch($dest_ext){
case 1: imagegif($new_img, $this->_dest, $this->_quality); break;
case 2: imagejpeg($new_img, $this->_dest, $this->_quality); break;
case 3: imagepng($new_img, $this->_dest, (int)(($this->_quality-1)/10)); break;
}
if(isset($source_img)){
imagedestroy($source_img);
}
if(isset($tmp_img)){
imagedestroy($tmp_img);
}
if(isset($new_img)){
imagedestroy($new_img);
}
// 添加水印
$this->add_watermark($this->_dest);
return is_file($this->_dest)? true : false;
}
/**获取目标图生成的size
* @return Array $width, $height
*/
private function get_size(){
list($owidth, $oheight) = getimagesize($this->_source);
$width = (int)($this->_width);
$height = (int)($this->_height);
switch($this->_type){
case 'fit':
$pic_w = $width;
$pic_h = (int)($pic_w*$oheight/$owidth);
if($pic_h>$height){
$pic_h = $height;
$pic_w = (int)($pic_h*$owidth/$oheight);
}
break;
case 'crop':
$pic_w = $width;
$pic_h = (int)($pic_w*$oheight/$owidth);
if($pic_h<$height){
$pic_h = $height;
$pic_w = (int)($pic_h*$owidth/$oheight);
}
break;
}
return array($pic_w, $pic_h);
}
/**获取截图的偏移量
* @param int $pic_w 图宽度
* @param int $pic_h 图高度
* @return Array $offset_w, $offset_h
*/
private function get_crop_offset($pic_w, $pic_h){
$offset_w = 0;
$offset_h = 0;
switch(strtoupper($this->_croppos)){
case 'TL':
$offset_w = 0;
$offset_h = 0;
break;
case 'TM':
$offset_w = (int)(($pic_w-$this->_width)/2);
$offset_h = 0;
break;
case 'TR':
$offset_w = (int)($pic_w-$this->_width);
$offset_h = 0;
break;
case 'ML':
$offset_w = 0;
$offset_h = (int)(($pic_h-$this->_height)/2);
break;
case 'MM':
$offset_w = (int)(($pic_w-$this->_width)/2);
$offset_h = (int)(($pic_h-$this->_height)/2);
break;
case 'MR':
$offset_w = (int)($pic_w-$this->_width);
$offset_h = (int)(($pic_h-$this->_height)/2);
break;
case 'BL':
$offset_w = 0;
$offset_h = (int)($pic_h-$this->_height);
break;
case 'BM':
$offset_w = (int)(($pic_w-$this->_width)/2);
$offset_h = (int)($pic_h-$this->_height);
break;
case 'BR':
$offset_w = (int)($pic_w-$this->_width);
$offset_h = (int)($pic_h-$this->_height);
break;
}
return array($offset_w, $offset_h);
}
/**添加水印
* @param String $dest 图片路径
*/
private function add_watermark($dest){
if($this->_watermark!=null && file_exists($this->_watermark) && file_exists($dest)){
list($owidth, $oheight, $otype) = getimagesize($dest);
list($w, $h, $wtype) = getimagesize($this->_watermark);
// 水印图比原图要小才加水印
if($w<=$owidth && $h<=$oheight){
if($this->_handler=='imagemagick'){ // imagemagick 添加水印
$cmd = sprintf("composite -gravity %s -geometry %s -dissolve %s '%s' %s %s", $this->_gravity, $this->_geometry, $this->_opacity, $this->_watermark, $dest, $dest);
$this->to_log($cmd);
exec($cmd);
}else{ // gd 添加水印
switch($wtype){
case 1: $water_img = imagecreatefromgif($this->_watermark); break;
case 2: $water_img = imagecreatefromjpeg($this->_watermark); break;
case 3: $water_img = imagecreatefrompng($this->_watermark); break;
default: return false;
}
switch($otype){
case 1: $dest_img = imagecreatefromgif($dest); break;
case 2: $dest_img = imagecreatefromjpeg($dest); break;
case 3: $dest_img = imagecreatefrompng($dest); break;
default: return false;
}
// 水印位置
switch(strtolower($this->_gravity)){
case 'northwest':
$posX = 0;
$posY = 0;
break;
case 'north':
$posX = ($owidth - $w) / 2;
$posY = 0;
break;
case 'northeast':
$posX = $owidth - $w;
$posY = 0;
break;
case 'west':
$posX = 0;
$posY = ($oheight - $h) / 2;
break;
case 'center':
$posX = ($owidth - $w) / 2;
$posY = ($oheight - $h) / 2;
break;
case 'east':
$posX = $owidth - $w;
$posY = ($oheight - $h) / 2;
break;
case 'southwest':
$posX = 0;
$posY = $oheight - $h;
break;
case 'south':
$posX = ($owidth - $w) / 2;
$posY = $oheight - $h;
break;
case 'southeast':
$posX = $owidth - $w;
$posY = $oheight - $h;
break;
}
imagealphablending($dest_img, true);
imagecopy($dest_img, $water_img, $posX, $posY, 0, 0, $w, $h);
switch($otype){
case 1:imagegif($dest_img, $dest, $this->_quality); break;
case 2:imagejpeg($dest_img, $dest, $this->_quality); break;
case 3:imagepng($dest_img, $dest, (int)(($this->_quality-1)/10)); break;
}
if(isset($water_img)){
imagedestroy($water_img);
}
if(isset($dest_img)){
imagedestroy($dest_img);
}
}
}
}
}
/**判断处理程序是否已安装
* @return boolean
*/
private function check_handler(){
$handler = $this->_handler;
if(!in_array($handler, array('imagemagick', 'gd', null))){
return false;
}
// 检查是否有安装imagemagick
$imagemagick_installed = strstr(shell_exec('convert -version'),'Version: ImageMagick')!=''? true : false;
// 检查是否有安装gd库
$gd_installed = function_exists('gd_info')? true : false;
switch($handler){
case 'imagemagick':
return $imagemagick_installed;
break;
case 'gd':
return $gd_installed;
break;
case null:
if($imagemagick_installed){
$this->_handler = 'imagemagick';
return true;
}
if($gd_installed){
$this->_handler = 'gd';
return true;
}
break;
}
return false;
}
/**创建图片目录
* @param String $path
* @return boolean
*/
private function create_dirs($dest){
if(!is_dir(dirname($dest))){
return mkdir(dirname($dest), 0777, true);
}
return true;
}
/**判断参数是否存在
* @param Array $obj 数组对象
* @param String $key 要查找的key
* @return boolean
*/
private function exists($obj,$key=''){
if($key==''){
return isset($obj) && !empty($obj);
}else{
$keys = explode('.',$key);
for($i=0,$max=count($keys); $i<$max; $i++){
if(isset($obj[$keys[$i]])){
$obj = $obj[$keys[$i]];
}else{
return false;
}
}
return isset($obj) && !empty($obj);
}
}
/**记录log
* @param String $msg 要记录的log讯息
*/
private function to_log($msg){
if($this->_log){
$msg = '['.date('Y-m-d H:i:s').']'.$msg."\r\n";
file_put_contents($this->_log, $msg, FILE_APPEND);
}
}
/**hex颜色转rgb颜色
* @param String $color hex颜色
* @return Array
*/
private function hex2rgb($hexcolor){
$color = str_replace('#', '', $hexcolor);
if (strlen($color) > 3) {
$rgb = array(
'r' => hexdec(substr($color, 0, 2)),
'g' => hexdec(substr($color, 2, 2)),
'b' => hexdec(substr($color, 4, 2))
);
} else {
$r = substr($color, 0, 1) . substr($color, 0, 1);
$g = substr($color, 1, 1) . substr($color, 1, 1);
$b = substr($color, 2, 1) . substr($color, 2, 1);
$rgb = array(
'r' => hexdec($r),
'g' => hexdec($g),
'b' => hexdec($b)
);
}
return $rgb;
}
/**获取图片类型
* @param String $file 图片路径
* @return int
*/
private function get_file_ext($file){
$filename = basename($file);
list($name, $ext)= explode('.', $filename);
$ext_type = 0;
switch(strtolower($ext)){
case 'jpg':
case 'jpeg':
$ext_type = 2;
break;
case 'gif':
$ext_type = 1;
break;
case 'png':
$ext_type = 3;
break;
}
return $ext_type;
}
} // class end
?>
~~~
**demo:**
~~~
<?php
define('ROOT', dirname(__FILE__));
require(ROOT."/PicThumb.class.php");
$logfile = ROOT.'/PicThumb.log';
$source1 = ROOT.'/pic/source.jpg';
$dest1 = ROOT.'/pic/1.jpg';
$dest2 = ROOT.'/pic/2.gif';
$dest3 = ROOT.'/pic/3.png';
$source2 = ROOT.'/pic/source_cmyk.jpg';
$dest4 = ROOT.'/pic/4.jpg';
$dest5 = ROOT.'/pic/5.gif';
$dest6 = ROOT.'/pic/6.png';
$watermark = ROOT.'/pic/watermark.png';
// 按比例生成缩略图
$param = array(
'type' => 'fit',
'width' => 100,
'height' => 100,
);
$obj = new PicThumb($logfile);
$obj->set_config($param);
$flag = $obj->create_thumb($source1, $dest1);
if($flag){
echo '<img src="pic/'.basename($dest1).'">';
}else{
echo 'create thumb fail';
}
// 按比例生成缩略图,不足部分用#FF0000填充
$param = array(
'type' => 'fit',
'width' => 100,
'height' => 100,
'bgcolor' => '#FFFF00'
);
$obj = new PicThumb($logfile);
$obj->set_config($param);
$flag = $obj->create_thumb($source1, $dest2);
if($flag){
echo '<img src="pic/'.basename($dest2).'">';
}else{
echo 'create thumb fail';
}
// 裁剪250x250的缩略图,裁剪位置是底部中间,水印位置西南,透明度50
$param = array(
'type' => 'crop',
'croppos' => 'BM',
'width' => 250,
'height' => 250,
'watermark' => $watermark,
'opacity' => 50,
'gravity' => 'SouthWest'
);
$obj = new PicThumb($logfile);
$obj->set_config($param);
$flag = $obj->create_thumb($source1, $dest3);
if($flag){
echo '<img src="pic/'.basename($dest3).'">';
}else{
echo 'create thumb fail';
}
// 按比例生成缩略图 CMYK格式
$param = array(
'type' => 'fit',
'width' => 100,
'height' => 100,
);
$obj = new PicThumb($logfile);
$obj->set_config($param);
$flag = $obj->create_thumb($source2, $dest4);
if($flag){
echo '<img src="pic/'.basename($dest4).'">';
}else{
echo 'create thumb fail';
}
// 按比例生成缩略图,不足部分用#FF0000填充 CMYK格式
$param = array(
'type' => 'fit',
'width' => 100,
'height' => 100,
'bgcolor' => '#FFFF00'
);
$obj = new PicThumb($logfile);
$obj->set_config($param);
$flag = $obj->create_thumb($source2, $dest5);
if($flag){
echo '<img src="pic/'.basename($dest5).'">';
}else{
echo 'create thumb fail';
}
// 裁剪250x250的缩略图,裁剪位置是底部中间,水印位置西南,透明度50 CMYK格式
$param = array(
'type' => 'crop',
'croppos' => 'BM',
'width' => 250,
'height' => 250,
'watermark' => $watermark,
'opacity' => 50,
'gravity' => 'SouthWest'
);
$obj = new PicThumb($logfile);
$obj->set_config($param);
$flag = $obj->create_thumb($source2, $dest6);
if($flag){
echo '<img src="pic/'.basename($dest6).'">';
}else{
echo 'create thumb fail';
}
?>
~~~
**源文件下载:[点击下载](http://download.csdn.net/download/fdipzone/5763725)**
php 支持断点续传的文件下载类
最后更新于:2022-04-01 14:12:27
php 支持断点续传,主要依靠HTTP协议中 header HTTP_RANGE实现。
**HTTP断点续传原理**
Http头 Range、Content-Range()
HTTP头中一般断点下载时才用到Range和Content-Range实体头,
Range用户请求头中,指定第一个字节的位置和最后一个字节的位置,如(Range:200-300)
Content-Range用于响应头
请求下载整个文件:
GET /test.rar HTTP/1.1
Connection: close
Host: 116.1.219.219
Range: bytes=0-801 //一般请求下载整个文件是bytes=0- 或不用这个头
一般正常回应
HTTP/1.1 200 OK
Content-Length: 801
Content-Type: application/octet-stream
Content-Range: bytes 0-800/801 //801:文件总大小
**FileDownload.class.php**
~~~
<?php
/**php下载类,支持断点续传
*Date: 2013-06-30
*Author: fdipzone
*Ver: 1.0
*
*Func:
*download: 下载文件
*setSpeed: 设置下载速度
*getRange: 获取header中Range
*/
class FileDownload{ // class start
private $_speed = 512; // 下载速度
/**下载
* @param String $file 要下载的文件路径
* @param String $name 文件名称,为空则与下载的文件名称一样
* @param boolean $reload 是否开启断点续传
*/
public function download($file, $name='', $reload=false){
if(file_exists($file)){
if($name==''){
$name = basename($file);
}
$fp = fopen($file, 'rb');
$file_size = filesize($file);
$ranges = $this->getRange($file_size);
header('cache-control:public');
header('content-type:application/octet-stream');
header('content-disposition:attachment; filename='.$name);
if($reload && $ranges!=null){ // 使用续传
header('HTTP/1.1 206 Partial Content');
header('Accept-Ranges:bytes');
// 剩余长度
header(sprintf('content-length:%u',$ranges['end']-$ranges['start']));
// range信息
header(sprintf('content-range:bytes %s-%s/%s', $ranges['start'], $ranges['end'], $file_size));
// fp指针跳到断点位置
fseek($fp, sprintf('%u', $ranges['start']));
}else{
header('HTTP/1.1 200 OK');
header('content-length:'.$file_size);
}
while(!feof($fp)){
echo fread($fp, round($this->_speed*1024,0));
ob_flush();
//sleep(1); // 用于测试,减慢下载速度
}
($fp!=null) && fclose($fp);
}else{
return '';
}
}
/**设置下载速度
* @param int $speed
*/
public function setSpeed($speed){
if(is_numeric($speed) && $speed>16 && $speed<4096){
$this->_speed = $speed;
}
}
/**获取header range信息
* @param int $file_size 文件大小
* @return Array
*/
private function getRange($file_size){
if(isset($_SERVER['HTTP_RANGE']) && !empty($_SERVER['HTTP_RANGE'])){
$range = $_SERVER['HTTP_RANGE'];
$range = preg_replace('/[\s|,].*/', '', $range);
$range = explode('-', substr($range, 6));
if(count($range)<2){
$range[1] = $file_size;
}
$range = array_combine(array('start','end'), $range);
if(empty($range['start'])){
$range['start'] = 0;
}
if(empty($range['end'])){
$range['end'] = $file_size;
}
return $range;
}
return null;
}
} // class end
?>
~~~
**demo**
~~~
<?php
require('FileDownload.class.php');
$file = 'book.zip';
$name = time().'.zip';
$obj = new FileDownload();
$flag = $obj->download($file, $name);
//$flag = $obj->download($file, $name, true); // 断点续传
if(!$flag){
echo 'file not exists';
}
?>
~~~
**断点续传测试方法:**
使用linux wget命令去测试下载, wget -c -O file http://xxx
**1.先关闭断点续传**
$flag = $obj->download($file, $name);
~~~
fdipzone@ubuntu:~/Downloads$ wget -O test.rar http://demo.fdipzone.com/demo.php
--2013-06-30 16:52:44-- http://demo.fdipzone.com/demo.php
正在解析主机 demo.fdipzone.com... 127.0.0.1
正在连接 demo.fdipzone.com|127.0.0.1|:80... 已连接。
已发出 HTTP 请求,正在等待回应... 200 OK
长度: 10445120 (10.0M) [application/octet-stream]
正在保存至: “test.rar”
30% [============================> ] 3,146,580 513K/s 估时 14s
^C
fdipzone@ubuntu:~/Downloads$ wget -c -O test.rar http://demo.fdipzone.com/demo.php
--2013-06-30 16:52:57-- http://demo.fdipzone.com/demo.php
正在解析主机 demo.fdipzone.com... 127.0.0.1
正在连接 demo.fdipzone.com|127.0.0.1|:80... 已连接。
已发出 HTTP 请求,正在等待回应... 200 OK
长度: 10445120 (10.0M) [application/octet-stream]
正在保存至: “test.rar”
30% [============================> ] 3,146,580 515K/s 估时 14s
^C
可以看到,wget -c不能断点续传
~~~
**2.开启断点续传**
$flag = $obj->download($file, $name, true);
~~~
fdipzone@ubuntu:~/Downloads$ wget -O test.rar http://demo.fdipzone.com/demo.php
--2013-06-30 16:53:19-- http://demo.fdipzone.com/demo.php
正在解析主机 demo.fdipzone.com... 127.0.0.1
正在连接 demo.fdipzone.com|127.0.0.1|:80... 已连接。
已发出 HTTP 请求,正在等待回应... 200 OK
长度: 10445120 (10.0M) [application/octet-stream]
正在保存至: “test.rar”
20% [==================> ] 2,097,720 516K/s 估时 16s
^C
fdipzone@ubuntu:~/Downloads$ wget -c -O test.rar http://demo.fdipzone.com/demo.php
--2013-06-30 16:53:31-- http://demo.fdipzone.com/demo.php
正在解析主机 demo.fdipzone.com... 127.0.0.1
正在连接 demo.fdipzone.com|127.0.0.1|:80... 已连接。
已发出 HTTP 请求,正在等待回应... 206 Partial Content
长度: 10445121 (10.0M),7822971 (7.5M) 字节剩余 [application/octet-stream]
正在保存至: “test.rar”
100%[++++++++++++++++++++++++=========================================================================>] 10,445,121 543K/s 花时 14s
2013-06-30 16:53:45 (543 KB/s) - 已保存 “test.rar” [10445121/10445121])
可以看到会从断点的位置(%20)开始下载。
~~~
**源码下载地址:[点击下载](http://download.csdn.net/detail/fdipzone/5676439)**
php 获取页面中的指定内容类
最后更新于:2022-04-01 14:12:25
**功能:**
1.获取内容中的url,email,image。
2.替换内容中的url,email,image。
url:<a href="url">xxx</a>
email:admin@admin.com
image:<img src="image">
**Grep.class.php**
~~~
<?php
/**grep class
*Date: 2013-06-15
*Author: fdipzone
*Ver: 1.0
*
*Func:
*
*set: 设置内容
*get: 返回指定的内容
*replace: 返回替换后的内容
*get_pattern 根据type返回pattern
*/
class Grep{ // class start
private $_pattern = array(
'url' => '/<a.*?href="((http(s)?:\/\/).*?)".*?/si',
'email' => '/([\w\-\.]+@[\w\-\.]+(\.\w+))/',
'image' => '/<img.*?src=\"(http:\/\/.+\.(jpg|jpeg|gif|bmp|png))\">/i'
);
private $_content = ''; // 源内容
/* 設置搜尋的內容
* @param String $content
*/
public function set($content=''){
$this->_content = $content;
}
/* 获取指定内容
* @param String $type
* @param int $unique 0:all 1:unique
* @return Array
*/
public function get($type='', $unique=0){
$type = strtolower($type);
if($this->_content=='' || !in_array($type, array_keys($this->_pattern))){
return array();
}
$pattern = $this->get_pattern($type); // 获取pattern
preg_match_all($pattern, $this->_content, $matches);
return isset($matches[1])? ( $unique==0? $matches[1] : array_unique($matches[1]) ) : array();
}
/* 获取替换后的内容
* @param String $type
* @param String $callback
* @return String
*/
public function replace($type='', $callback=''){
$type = strtolower($type);
if($this->_content=='' || !in_array($type, array_keys($this->_pattern)) || $callback==''){
return $this->_content;
}
$pattern = $this->get_pattern($type);
return preg_replace_callback($pattern, $callback, $this->_content);
}
/* 根据type获取pattern
* @param String $type
* @return String
*/
private function get_pattern($type){
return $this->_pattern[$type];
}
} // class end
?>
~~~
**Demo**
~~~
<?php
header('content-type:text/htm;charset=utf8');
require('Grep.class.php');
$content = file_get_contents('http://www.test.com/');
$obj = new Grep();
$obj->set($content);
$url = $obj->get('url', 0);
$email = $obj->get('email', 1);
$image = $obj->get('image', 1);
print_r($url);
print_r($email);
print_r($image);
$url_new = $obj->replace('url', 'replace_url');
echo $url_new;
function replace_url($matches){
return isset($matches[1])? '[url]'.$matches[1].'[/url]' : '';
}
?>
~~~
php click captcha 验证码类
最后更新于:2022-04-01 14:12:22
**需求:**
现在常用的表单验证码大部分都是要用户输入为主,但这样对手机用户会不方便。
如果手机用户访问,可以不用输入,而是click某一位置便可确认验证码,这样就会方便很多。
原理:
1.使用PHP imagecreate创建PNG图象,在图中画N个圆弧,其中一个是完整的圆(验证用),将圆心坐标及半径记录入session。
2.在浏览器,当用户在验证码图片上点击时,记录点击的位置。
3.将用户点击的坐标与session记录的圆心坐标、半径比较,判断是否在圆中,如是则验证通过。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-16_573945f06e0c6.jpg)
**ClickCaptcha.class.php**
~~~
<?php
/**Click Captcha 验证码类
*Date: 2013-05-04
*Author: fdipzone
*Ver: 1.0
*/
class ClickCaptcha { // class start
public $sess_name = 'm_captcha';
public $width = 500;
public $height = 200;
public $icon = 5;
public $iconColor = array(255, 255, 0);
public $backgroundColor = array(0, 0, 0);
public $iconSize = 56;
private $_img_res = null;
public function __construct($sess_name=''){
if(session_id() == ''){
session_start();
}
if($sess_name!=''){
$this->sess_name = $sess_name; // 设置session name
}
}
/**创建验证码 */
public function create(){
// 创建图象
$this->_img_res = imagecreate($this->width, $this->height);
// 填充背景
ImageColorAllocate($this->_img_res, $this->backgroundColor[0], $this->backgroundColor[1], $this->backgroundColor[2]);
// 分配颜色
$col_ellipse = imagecolorallocate($this->_img_res, $this->iconColor[0], $this->iconColor[1], $this->iconColor[2]);
$minArea = $this->iconSize/2+3;
// 混淆用图象,不完整的圆
for($i=0; $i<$this->icon; $i++){
$x = mt_rand($minArea, $this->width-$minArea);
$y = mt_rand($minArea, $this->height-$minArea);
$s = mt_rand(0, 360);
$e = $s + 330;
imagearc($this->_img_res, $x, $y, $this->iconSize, $this->iconSize, $s, $e, $col_ellipse);
}
// 验证用图象,完整的圆
$x = mt_rand($minArea, $this->width-$minArea);
$y = mt_rand($minArea, $this->height-$minArea);
$r = $this->iconSize/2;
imagearc($this->_img_res, $x, $y, $this->iconSize, $this->iconSize, 0, 360, $col_ellipse);
// 记录圆心坐标及半径
$this->captcha_session($this->sess_name, array($x, $y, $r));
// 生成图象
Header("Content-type: image/PNG");
ImagePNG($this->_img_res);
ImageDestroy($this->_img_res);
exit();
}
/**检查验证码
* @param String $captcha 验证码
* @param int $flag 验证成功后 0:不清除session 1:清除session
* @return boolean
*/
public function check($captcha, $flag=1){
if(trim($captcha)==''){
return false;
}
if(!is_array($this->captcha_session($this->sess_name))){
return false;
}
list($px, $py) = explode(',', $captcha);
list($cx, $cy, $cr) = $this->captcha_session($this->sess_name);
if(isset($px) && is_numeric($px) && isset($py) && is_numeric($py) &&
isset($cx) && is_numeric($cx) && isset($cy) && is_numeric($cy) && isset($cr) && is_numeric($cr)){
if($this->pointInArea($px,$py,$cx,$cy,$cr)){
if($flag==1){
$this->captcha_session($this->sess_name,'');
}
return true;
}
}
return false;
}
/**判断点是否在圆中
* @param int $px 点x
* @param int $py 点y
* @param int $cx 圆心x
* @param int $cy 圆心y
* @param int $cr 圆半径
* sqrt(x^2+y^2)<r
*/
private function pointInArea($px, $py, $cx, $cy, $cr){
$x = $cx-$px;
$y = $cy-$py;
return round(sqrt($x*$x + $y*$y))<$cr;
}
/**验证码session处理方法
* @param String $name captcha session name
* @param String $value
* @return String
*/
private function captcha_session($name,$value=null){
if(isset($value)){
if($value!==''){
$_SESSION[$name] = $value;
}else{
unset($_SESSION[$name]);
}
}else{
return isset($_SESSION[$name])? $_SESSION[$name] : '';
}
}
} // class end
?>
~~~
**demo.php**
~~~
<?php
session_start();
require('ClickCaptcha.class.php');
if(isset($_GET['get_captcha'])){ // get captcha
$obj = new ClickCaptcha();
$obj->create();
exit();
}
if(isset($_POST['send']) && $_POST['send']=='true'){ // submit
$name = isset($_POST['name'])? trim($_POST['name']) : '';
$captcha = isset($_POST['captcha'])? trim($_POST['captcha']) : '';
$obj = new ClickCaptcha();
if($obj->check($captcha)){
echo 'your name is:'.$name;
}else{
echo 'captcha not match';
}
echo ' <a href="demo.php">back</a>';
}else{ // html
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title> Click Captcha Demo </title>
<script type="text/javascript" src="jquery-1.6.2.min.js"></script>
<script type="text/javascript">
$(function(){
$('#captcha_img').click(function(e){
var x = e.pageX - $(this).offset().left;
var y = e.pageY - $(this).offset().top;
$('#captcha').val(x+','+y);
})
$('#btn').click(function(e){
if($.trim($('#name').val())==''){
alert('Please input name!');
return false;
}
if($.trim($('#captcha').val())==''){
alert('Please click captcha!');
return false;
}
$('#form1')[0].submit();
})
})
</script>
</head>
<body>
<form name="form1" id="form1" method="post" action="demo.php" onsubmit="return false">
<p>name:<input type="text" name="name" id="name"></p>
<p>Captcha:Please click full circle<br><img id="captcha_img" src="demo.php?get_captcha=1&t=<?=time() ?>" style="cursor:pointer"></p>
<p><input type="submit" id="btn" value="submit"></p>
<input type="hidden" name="send" value="true">
<input type="hidden" name="captcha" id="captcha">
</form>
</body>
</html>
<?php } ?>
~~~
源码下载:[点击下载](http://download.csdn.net/detail/fdipzone/5331511)
自动登入google play下载app report
最后更新于:2022-04-01 14:12:20
**流程
1.登入google play**
登入google play需要三步
https://play.google.com/apps/publish/
https://accounts.google.com/ServiceLogin?hl=en&continue=https://play.google.com/apps/publish/
https://accounts.google.com/ServiceLoginAuth
**2.下载app report zip
3.unzip report**
**代码如下:**
~~~
<?php
define('ROOT_PATH', dirname(__FILE__));
define('GOOGLE_PLAY_COOKIE_FILE', 'google_play_cookie.txt');
/**
* Login google play, download report, unzip
* Date: 2013-04-17
* Author: fdipzone
* Version: 1.0
*/
class AndroidReportDownLoader{
private $username;
private $password;
private $dev_acc;
/* init
* @param String $username google play account
* @param String $password google play password
* @param String $dev_acc google play dev account
*/
public function __construct($username='', $password='', $dev_acc=''){
$this->username = $username;
$this->password = $password;
$this->dev_acc = $dev_acc;
}
/*
* @param String $appname
* @param String $sd 开始日期
* @param String $ed 结束日期
* @param String $downloadFile 保存的zip名称
*/
public function run($appname='', $sd='', $ed='', $downloadFile=''){
$package = $appname;
$dim = 'overall,country,language,os_version,device,app_version,carrier';
//$met = 'daily_device_installs,active_device_installs,daily_user_installs,total_user_installs,active_user_installs,daily_device_uninstalls,daily_user_uninstalls,daily_device_upgrades';
$met = "daily_device_installs,current_device_installs,daily_user_installs,total_user_installs,current_user_installs,daily_device_uninstalls,daily_user_uninstalls,daily_device_upgrades"; // google modify 2013-08-06
// login google play
$this->loginAuth($this->username, $this->password);
// download report zip
return $this->downloadReport($package, $sd, $ed, $dim, $met, $this->dev_acc, $downloadFile);
}
/* login google play,create cookies
* @param String $username
* @param String $password
* @return boolean
*/
private function loginAuth($username, $password){
// step1
$mainUrl = "https://play.google.com/apps/publish/";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $mainUrl);
curl_setopt($ch, CURLOPT_COOKIEJAR, GOOGLE_PLAY_COOKIE_FILE);
curl_setopt($ch, CURLOPT_COOKIEFILE, GOOGLE_PLAY_COOKIE_FILE);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_exec($ch);
curl_close($ch);
// step 2
$serviceLoginUrl = "https://accounts.google.com/ServiceLogin?hl=en&continue=".$mainUrl;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $serviceLoginUrl);
curl_setopt($ch, CURLOPT_COOKIEJAR, GOOGLE_PLAY_COOKIE_FILE);
curl_setopt($ch, CURLOPT_COOKIEFILE, GOOGLE_PLAY_COOKIE_FILE);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$serviceLoginRespHtml = curl_exec($ch);
curl_close($ch);
preg_match('/name="dsh"\s*id="dsh"\s*value="(.*?)"\s*/i', $serviceLoginRespHtml, $matches); // get dsh
$dsh = $matches[1];
preg_match('/name="GALX"\s*value="(.*?)"\s*/i', $serviceLoginRespHtml, $matches); // get GALX
$galx = $matches[1];
// step 3
$loginGoogleUrl = "https://accounts.google.com/ServiceLoginAuth";
$postFields = "Referer=".$serviceLoginUrl;
$postFields .= "&AllowAutoRedirect=false";
$postFields .= "&continue=".$mainUrl;
$postFields .= "&dsh=".$dsh;
$postFields .= "&h1=en";
$postFields .= "&GALX=".$galx;
$postFields .= "&Email=".$username;
$postFields .= "&Passwd=".$password;
$postFields .= "&signIn=Sign+in";
$postFields .= "&PersistentCookie=yes";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $loginGoogleUrl);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields);
curl_setopt($ch, CURLOPT_COOKIEJAR, GOOGLE_PLAY_COOKIE_FILE);
curl_setopt($ch, CURLOPT_COOKIEFILE, GOOGLE_PLAY_COOKIE_FILE);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_exec($ch);
curl_close($ch);
// login cookies create success
return true;
}
// download Report zip file
private function downloadReport($package, $sd, $ed, $dim, $met, $dev_acc, $downloadFile) {
$url = "https://play.google.com/apps/publish/statistics/download?package={$package}&sd={$sd}&ed={$ed}&dim={$dim}&met={$met}&dev_acc={$dev_acc}";
$fp = fopen($downloadFile,"w");
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_COOKIEFILE, GOOGLE_PLAY_COOKIE_FILE);
curl_exec($ch);
curl_close($ch);
fclose($fp);
if (file_exists($downloadFile)){
return true;
}
return false;
}
/* unzip report
* @param String $path 解压的路径
* @param String $downloadFile zip file
*/
public function unzipReport($path, $downloadFile){
$exec = "unzip ".$downloadFile. " -d ".$path;
shell_exec($exec);
unlink($downloadFile); // delete zip file
}
}
// demo
$username = 'testdev@gmail.com';
$password = 'abcd1234';
$dev_acc = '12345678901234567890';
$appname = 'com.testdev';
$sd = '20130417';
$ed = '20130417';
$downloadFile = 'testdev.zip';
$unzipPath = ROOT_PATH.'/testdev/';
$obj = new AndroidReportDownLoader($username, $password, $dev_acc);
if($obj->run($appname, $sd, $ed, $downloadFile)){
$obj->unzipReport($unzipPath, $downloadFile);
}
?>
~~~