smarty中调用php内置函数

最后更新于:2022-04-01 11:54:28

CleverCode发现smarty中调用php内置函数可以通过|实现。|前后没有空格。如果是数组求count需要加上@。 1个参数时候: ~~~ {{'param1'|functionName}}  例如 {{$tmpStr|strlen}} ~~~ 2个参数时候: ~~~ {{'param1'|functionName:'param2'}} {$tmpStr|substr:'1'}  ~~~ 多个参数时候: ~~~ {{'param1'|functionName:'param2':'param3'..}}  {{$tmpStr|substr:'1':'2'}} {{'a'|str_replace:'A':$tmpStr}} ~~~ ## 1 marty判断是否为空  如下代码php分配给tmpStr为空或没分配;那么smarty中输出0。这个是调用了php的内置函数strlen php代码: ~~~ $smarty->assign('tmpStr',''); smarty代码: {{if $tmpStr|strlen > 0 }}     1 {{else}}     0 {{/if}} ~~~ ## 2 marty求数组长度 通过php内置的函数is_array和count求得数组长度,如下的代码输出5 php代码: ~~~ $smarty->assign('tmpArr',array(1,2,3,4,5)); ~~~ smarty代码: ~~~ {{if $tmpArr|is_array && $tmpArr|@count > 0}}     {{$tmpArr|@count}} {{else}}     0 {{/if}} ~~~ ## 3 marty字符串替换 如下的代码是将tmpStr中的b替换为c ~~~ $smarty->assign('tmpStr','abb'); {{'b'|str_replace:'c':$tmpStr}} 输出:acc ~~~ ## 4 marty截取字段 ~~~ $smarty->assign('tmpStr','abb'); {{$tmpStr|substr:'1'}}  输出:bb {{$tmpStr|substr:'1':'1'}} 输出:b ~~~
';

PHP编码规范文档

最后更新于:2022-04-01 11:54:25

      为了提高工作效率,保证开发的有效性和合理性,并最大程度提高程序代码的可读性和可重复利用性,提高沟通效率,需要一份代码编写规范。让大家养成良好的代码编写习惯,同时减少代码中的bug。       CleverCode整理了一些规范。本规范包含PHP开发时程序编码中命名规范、代码缩进规则、控制结构、函数调用、函数定义、注释、包含代码、PHP标记、常最命名等方面的规则。 ## 1 文件格式 ### 1.1 文件标记 所有PHP文件,其代码标记均使用完整PHP标签,不建议使用短标签,例如: ~~~ <?php //推荐 echo 'hello world'; ?> <? //短标签格式不推荐 echo ' hello world '; ?> ~~~ 1) 使用短标签格式容易和XML混淆,并且不是所有PHP版本和服务器都默认支持或打开短标签选项(从PHP5.4开始,php.ini中的短标签选项不影响短标签的使用)。对于只含有PHP代码的文件,将在文件结尾处忽略?>。这是为了防止多余空格或者其他字符影响到代码。 2)实际上这个问题只有在不开启压缩或缓存输出时才会出现,例如: php.ini-禁止压缩输出及缓存输出 zlib.output_conpression = off output_buffering = off foo.php,注意这个时候有一些空格或换行符掉在了之后,当然这在页面上是看不 到的。 ~~~ <?php $foo= 'foo'; ?> ~~~ index.php,在包含foo.php的同时,实际上已经输出一些空格或换行了。 ~~~ <?php include 'foo.php'; session_start(); ?> ~~~ 这时将看到一个警告(warning):“...Cannotsendsessioncachelimiter-headersalreadysent...” ### 1.2 文件和目录命名 程序文件名和目录名均采用有意义的英文命名,不使用拼音或无意义的字母,只允许出现字母、数字、下画线和中画线字符,同时必须以“.php”结尾(模板文件除外)。 //类统一采用 DemoTest.php ## 2 命名规范 ### 2.1 变量命名 PHP中的变量用一个美元符号后面跟变量名表示。变量名区分大小写。一个有效变景名由字母或者下画线开头,后面跟任意数量的字母、数字、下画线。正常的正则表达式将表述为:[a-zA-Z_\x7f-\xff][a-zA-ZO-9_'x7f-\xff],不应该在变量中使用中文等非ASCII字符。 ### 2.1.1 程序整体 程序整体以驼峰法命名,以小写字母开始,同时命名要有意义,如: ~~~ function displayName($name){ echo $name; } ~~~ ### 2.1.2 PHP全局变量键值 PHP全局变量键值两边都有中间使用驼峰法命名。 ### 2.1.3 普通变量 普通变量整体采用驼峰法, 按照约定命名,并避免使用常用关键字或存在模糊意义的单词。变量应该以名词为主。 字符串:$myName 数组:$myArray 不推荐: $yes:不应该使用其作为bool型变童,因为变量很可能被改变,其可能使得Syeszflase,而让其代码逻辑变得混乱。 $sex:具有模糊意义且不地道的英文单词,性别的命名应该是$gender。 ### 2.1.4 函数名 函数名既要有意义,一看就知道要干什么,也要尽量缩写。建议采用动词或动词加形容词 的命名方式,如showMsg。不建议下面这样的函数名:getPublishedAdvertisementBy CategoryAndCategoryldAndPosition() 上面的函数名可以提炼为:getAd($category,$categoryid,$position,$published) 例如1)类公共函数: public function doGetUserName($job) 例如2)类私有函数,以“_”开头: private function _doGetUserName($job) 例如3)类保护函数,以“_”开头: protected function _doGetUserName($job) ### 2.1.5 类中的属性 类中的变量遵守普通变量的命名规则。 例如1)公共属性,static属性: public $userName = ’CleverCode’; static $userType = array(1,2,3); 例如2)私有属性,以“_”开头: private $_userName = ’CleverCode’; 例如3)保护属性,以“_”开头: protected $_userName = ’CleverCode’; 例如4)常量,全部大写,以“_”分隔: const TYPE_GZ = 4; ### 2.2 数据库命名 ### 2.2.1 库命名 1)使用小写字母。(windows不区分大小写,linux区分大小写,为了库移植兼容,所以全部小写) 2)多个单词组成,单词之间用"_"分隔。 例如:db_user,db_system。 ### 2.2.2 表命名 1)表名均使用小写字母。 2)表名字使用统一的前缀,且前缀不能为空(模块化,且可有效规避MYSQL保留字)。 3)对于多个单词组成的表名,使用"_"间隔。 例如: pre_users,pre_user_shop ### 2.2.3 表字段命名 1)全部使用小写字母命名。 2)多个单词不用下画线进行分割(重要)。 3)如果有必要,给常用字段加上表名首字母作为前缀。 4)避免使用关键字和保留字,但约定俗成的除外。 例如: username,newsid,userid,logid ## 3 注释规范 ### 3.1 文件注释 文件注释通常放在整个PHP文件头部,其内容包括文件版权、作者、编写日期、版本号等 重要信息。PHP中,可以参照phpdocument规范,便于利用程序自动生成文档。 文件注释遵循以下规则: 1)必须包含本程序的描述; 2)必须包含作者; 3)必须包含版权; 4)必须包含文件的名称; 5)可以包含书写日期; 6)可以包含版本信息; 7)可以包含重要的使用说明,如类的调用方法、注意事项等。 例如: ~~~ <?php /** * SystemUser.php * * 系统用户操作操作 * * Copyright (c) 2015 http://blog.csdn.net/CleverCode * * modification history: * -------------------- * 2015/5/11, by Clever Code, Create * ~~~ ### 3.2 类与接口注释 类和接口的注释应该尽量简洁。按照一般的习惯,一个文件只包含一个类,在类注释中通常不需要再加上作者和版本等信息,加上可见性和简中的描述即可。如果文件注释已经足够详细,可以不用给类写注释。如果同时存在接口和接口的实现类,通常做法是仅在接口中进行注释。 ### 3.3 方法和函数注释 方法和函数的注释写在前面,通常需要标明的信息主要是可见性、参数类型和返回值的类 例如1: ~~~ /** * 对比新旧数据 * * @param bigint $userid 人编号 * @param array $oldMap 旧数据 * @param array $newMap 新数据(输出参数) * @return string 成功返回'OK',失败返回错误信息 */ public static function diffRecommendInfo($userid, $oldMap, &$newMap){ } ~~~ 例如2: ~~~ /** * 插入日志数据 * * @param bigint $data[‘userid’] 用户编号 * @param array $data[‘logintime’] 登录时间 * @return string 成功返回'OK',失败返回错误信息 */ public static function insertLogData($data){ } ~~~ ### 3.4 Action注释 由于我们都是使用的zend开发模式,在Action是http请求处理逻辑的入口,那么必然会传递get,post等参数。可以注释如下. 例如1)没有get,post传递参数时候: ~~~ /** * 自动设置名称 * * @return void */ public function autosetAction(){ } ~~~ 例如2 )有get传递参数时候: ~~~ /** * 获取用户名称 * * @get int $userid 用户编号 * @get int $currpage 当前页 * @get int $pagesize * * @return void */ public function getusernameAction(){ } ~~~ 例如3) 有post传递参数时候: ~~~ /** * 删除用户 * * @post int $userid 用户编号 * @return void */ public function deleteuserAction(){ } ~~~ ### 3.5 单行注释 1)写在被注释代码前面,而不是后面。但对于单行语句,按照习惯可以把注释放在语句末尾,也可以写在行上面。 2)对于大段注释,使用/**/格式,通常在文件和函数注释中使用,而代码内部统一使用//注释,因为其写起来简单。 例如: //姓名 $name = ’CleverCode’; ## 4 代码风格 ### 4.1 缩进与空格 在书写代码的时候,必须注意代码的缩进规则: 1)使用4个空格作为缩进,而不使用tab缩进(如在UltraEdit中可以进行预先设置)。 2)变量赋值时,等号左右留出空格。 例如: $name = 'CleverCode';//推荐 $name='CleverCode';//不推荐 为了最大程度减轻工作量,保持代码美观,建议使用大型IDE管理代码。比如,在zend studio中,使用Ctrl+Shift+F组合键对代码进行格式化。 ### 4.2 语句断行 代码书写中应遵循以下原则: 1)尽量保证程序语句一行就是一句; 2)尽量不要使一行的代码太长,一般控制在80个字符以内; 如果一行代码太长,请使用类似.=的方式断行书写; 执行数据库的SQL语句操作时,尽量不要在函数内写SQL语句,而先用变量定义SQL 语句,然后在执行操作的函数中调用定义的变量。 例如: //代码分割 $sql= "SELECTusername,password,address,age,postcode from test_t"; $sql.= "WHEREusername=${user}"; $ret = mysql_query($sql); 3)一个函数控制在200行以内; 4)if最多嵌套3层; //不推荐 ~~~ If(){ If(){ If(){ If(){ …… } } } } ~~~ 5)循环最多3层。 ~~~ //不推荐 For(){ For(){ For(){ For(){ …… } } } } ~~~ 6)if或者for语句块中只有一行时候,加上{}。当有语句变动的时候会带来不必要的bug。 ~~~ //推荐 If($a == 1){ echo 1; } //不推荐 If($a == 1) echo 1; ~~~ ### 4.3 空行 1)函数与函数之间空行。 2)同一个函数不同逻辑块之间空行,查阅不同的逻辑块条理更清晰。 ### 4.4 函数结构 通常一个函数分为三部分。第一部分:检查参数;第二部分:处理逻辑;第三部分:返回结果。 例如: ~~~ /** * 删除日志通过uid * * @param string $uid 用户uid * @return string 成功返回'OK',失败返回错误信息 */ public static function deleteLogByUid($uid){ //第一步:检查参数。防止处理部分异常;比如$uid是传入array(); if (!is_numeric($uid)) { return '!is_numeric($uid)'; } //第二步:处理逻辑。 $affected = $userLogTable->delete('where userid = ' . $uid); //第三步:返回结果。让调用者知道是否处理正常。 if($affected){ return 'OK'; } return 'delete error!'; } ~~~ ### 4.5 函数返回函数 需要客户端的函数: 返回值 $ret = array(‘code’=> 1 ,msg=>’’,data => array()); ### 4.6 更好的习惯 在代码中,使用下面列举的写法,可以使代码更优雅。 1)多使用PHP中已经存在的常量,而不要自己定义,例如: echo$meg."\r\n"; echo$msg,PHPJEOL; PHP中,PHP_EOL是一个预定义常量,表示一行结束,随着所使用系统的不同,使用PHP_EOL会让代码更具有可移植性。 2)更详尽的注释。 注释是一门艺术,好的注释可以比代码更精彩。不用担心效率问题。一则注释对代码的效 率影响不大,其次在正式产品中可以对代码中的注释进行批量删除。注释做到极致和完美的典型代表是Apache组织各种产品的源代码。 3)不要滥用语法糖。 语法糖也就是语言中的潜规则,即不具有普遍代表性的语法。少量使用语法糖会尝到甜 头,大量使用则是一种灾难。 例如以下代码,可读性比较差; $a?$a-$b:3&&$c&&$d=1; ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-29_56fa2a0c7acf5.jpg)
';

千万级PV网站架构

最后更新于:2022-04-01 11:54:23

## 1 架构背景         CleverCode了解了一下架构。现在的情况是:一共约有50台服务器,安装的服务nginx,mysql,memcached,squid,solor等。 现在日均纯PHP访问的PV是2500万,最高峰值可以抗住5000万访问。        以下只列出来一些常用域名,部分访问域名未列出来,其中的机器也只列出来部分。 ## 2 架构原理图 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f502da94b.jpg) ## 3 架构原理说明 ### 3.1 LVS部分 1)首先让所有的域名都通过DNS指向211.181.168.168。211.181.168.168是一个外网IP。对于LVS结构来说他是一个虚拟IP。    这个ip可以配置到eth0:1(一个网卡多个ip),禁止登陆用。eth0(211.181.168.151)的ip用于登陆。     2)当请求到了VIP之后,LVS会进行负载均衡,通过IP隧道将请求发送到某一个实际的服务器上,即(192.168.1.100,192.168.1.101,192.168.1.102)其中一个上。    LVS的负载均衡时IP层的负载均衡,速度特别快,给服务器的压力也特别小。 说明:    211.181.168.168通常只用与负载均衡用,尽量不要安装其他的服务,比如mysql。可以用配置较低的服务器当VIP。  ### 3.2 Nginx反向代理负载均衡 3)192.168.1.100,192.168.1.101,192.168.1.102是三台Nginx反向代理服务器。三台机器的nginx配置完全一样。 4)当请求到达比如192.168.1.102后,nginx会根据请求的域名做负载均衡。这时候可以使一组域名,用同一个upstream。upstream中的服务器可以根据配置设置权重。 5)如果访问的是css,js,图片,可以负载均衡到squid服务器。 说明:    三台RealServer,也尽量不要安装其他的服务,比如mysql。也可以用配置较低的服务器当RealServer。  ### 3.3 squid透明代理 6)squid服务器收到js,css,images请求后,首先看自己的缓存是否命中,如有命中再去后面的nginx源站中去取数据,然后在缓存一份。 7)squid取数据的时候可以根据域名做透明的反向代理。 说明:    squid服务器尽量配置2台,每台服务器都缓存,同一个图片会被缓存2次。防止一台挂掉后,静态资源访问不了。squid软件需要占用80端口,所以不能安装nginx服务。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-29_56fa2a0c7acf5.jpg)
';

Zend Studio 12.0.2源码自动格式化

最后更新于:2022-04-01 11:54:21

1)下载PHP Conventions [built-in] 4.xml。这个配置。地址:http://download.csdn.net/detail/clevercode/9278835。 2)导入配置。窗口 > 首选项 > PHP > 代码样式 > 格式化程序 。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f502965c8.jpg) 3) 2)源码 > 格式 。自动格式化程序。或者Ctrl + Shift + F。
';

为何UE复制粘贴不能用了?

最后更新于:2022-04-01 11:54:19

有几次使用 UltraEdit 的过程中,发现从其他文章处拷贝的内容,粘贴到UE中,就是不一样,不管是Ctrl+C,还是,右键-复制 都不管用。 但是在UE内的 复制、粘贴确是想要的。以前也遇到过,但是以为字数少,就没在意。 这次找到了问题所在 : UltraEdit有10个剪切板(clipboard),分别用Ctrl+0 - Ctrl+9 切换.  Ctrl+0 是 Windows 的,其他则为用户自定义的.可能是不知道什么时候,按下了 CTRL+n, 结果内容就有问题了. 解决问题很简单,按下 Ctrl+0  就可以了。 打开‘编辑’-‘剪贴板’ 可以看到UE 的剪贴板设置,选中 windows剪贴板 。
';

微信开发中文字交互项目详解

最后更新于:2022-04-01 11:54:16

       CleverCode前一段时间想去接触一下微信开发,申请了一个人订阅号,发现暂不能申请个人认证,而且没有微信接口的很多权限,也没有自定义菜单的权限(开发模式下)。在开发模式下,只能到手公众号里面的回复信息,然后响应。 ## 1 项目背景           CleverCode想了很久,运营一个什么样的公众号,比较好呢?现在的公众号太多了。比较好的点子,基本都被人想到了。那几天CleverCode的遇到点烦心事,心情不大爽,就想着能不能设计一个讲笑话的公众号,大家能够在里面讲笑话,然后查看笑话,开心一下自己。讲笑话后能够获取金币。积累到一定的金币后,可以兑换奖品。奖品的方式可以是充话费的形式。           说干就干。脑袋一热,CleverCode就开始疯狂的忙碌起来的了............................    ## 2 需求分析         CleverCode发现个人订阅号,只能简单收到客户的信息,然后响应信息。然后CleverCode就想,不如就让用户输入0-9的数字,组合成命令,这样服务器每次收到不同的数字后,去响应不同的请求就可以了。(这种方式基本停留在没有图片,颜色;只有文字交互的时代...........谁叫咋申请的订阅号,没有认证,就没有一些接口的权限)。 ## 3 源码下载        [http://download.csdn.net/detail/clevercode/8916699](http://download.csdn.net/detail/clevercode/8916699)。 ## 4 项目演示        如果你想查看本微信的详细的演示效果,可以在微信中搜索微信号:taihaoxiaole888。或者扫描下方二维码关注。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f500466c8.jpg)      ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f5005d20c.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f50077f5e.jpg) ## 5  设计过程 ### 5.1 微信配置接口        申请完微信订阅号后,要想微信开发,必须在微信后台([mp.weixin.qq.com](https://mp.weixin.qq.com/))切换到开发模式,然后配置接口。如下图。http://xxxx.com是我服务器的域名地址。如果没有服务器也可以用新浪的sae。/apithxl/interface表示的我是使用的zend framework框架。这个url可以是任意的,只要能够访问得到即可。例如http://xxxx.com/api.php,http://xxxx.com/api.jsp等等。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f50096c5f.jpg) ### 5.2 微信接口交互过程         当用户回复内容后,微信需要将用户回复的先传到自己的服务,如果配置成为了开发模式,微信会将调用接口(http://xxxxx.com/apithxl/interface),通过post方式将回复的内容发送到接口服务器上,我接口服务器处理完请求后,只需要输出一个xml信息,微信服务器获取这个xml信息后,在将获取的信息返回给微信用户。具体过程如下图。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f500c24b9.jpg) ### 5.3 检查签名         当用户配置完接口后,需要提交配置,微信向接口发送get请求的字符串。你需要echo它的字符串。这个在微信开发者文档中都会有,我代码中WeiXinCheck::checkSignature($signature, $timestamp, $nonce)也有。 ## 6 接口设计架构 ### 6.1 接口工作原理            当接口收到微信服务器发送过来的请求后,首先需要到命令解析中心,分析出得到是什么请求。然后将得到命令发送到命令调用中心;调度中心会根据不同的命令调用不用执行逻辑,然后返回结果。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f500e2c53.jpg) ApithxlController.php,主函数执行过程如下。入口函数为interfaceAction(),这个是zend framework框架的写法。首先通过_getRequest($request)方法保存参数,然后验证签名 ,通过CmdCenter::findCmd($request)解析命令,通过switch ($cmd)调用命令,通过$this->displayUTF8($retArray, 'thxl/validate.html')返回xml格式的内容。 ~~~ <?php /** * ApithxlController.php * * 太好笑了接口 * * Copyright (c) 2015 http://blog.csdn.net/CleverCode * * modification history: * -------------------- * 2015/7/10, by CleverCode, Create * */ define("TOKEN", "CleverCode"); define("HTTP_RAW_POST_DATA_TEST", WEB_ROOT_DIR . '/log/http_raw_post_data_test.txt'); define("HTTP_REQUEST_RESPONSE_LOG", WEB_ROOT_DIR . '/log/http_request_response_log-' . date('Y-m-d') . '.txt'); class ApithxlController extends My_Controller{ public $check_auth = false; public $check_auth_menu = false; /** * 命令接口 * * @return void */ function interfaceAction(){ $ret = $this->_interface(); } /** * 私有命令接口 * * @return string 成功返回'OK',失败返回错误信息 */ private function _interface(){ $request = array(); // 获取参数 $ret = $this->_getRequest($request); if ($ret != 'OK') { return $ret; } // 检查签名 if (!WeiXinCheck::checkSignature($request["signature"], $request["timestamp"], $request["nonce"])) { return 'checkSignature retrun false!'; } // 请求日志 if (!empty($GLOBALS["HTTP_RAW_POST_DATA"])) { logMsg(HTTP_REQUEST_RESPONSE_LOG, 'REQUEST', $GLOBALS["HTTP_RAW_POST_DATA"]); } // 解析文本命令 $cmd = CmdCenter::findCmd($request); $retArray = array(); $retArray['request'] = $request; // 执行命令 switch ($cmd) { // 校验 case 'validate' : $retMessage = InfoCenter::validate($request, &$retArray); if ($retMessage != 'OK') { return; } $this->displayUTF8($retArray, 'thxl/validate.html'); break; // 再来一个 case 'getOneAgain' : $retMessage = InfoCenter::getOneAgain($request, &$retArray); if ($retMessage != 'OK') { return; } $this->displayUTF8($retArray, 'thxl/textMsg.html'); break; // 查看帮助 case 'readHelp' : $retMessage = InfoCenter::readHelp($request, &$retArray); if ($retMessage != 'OK') { return; } $this->displayUTF8($retArray, 'thxl/textMsg.html'); break; // 账户详情 case 'accountDetail' : $retMessage = InfoCenter::accountDetail($request, &$retArray); if ($retMessage != 'OK') { return; } $this->displayUTF8($retArray, 'thxl/textMsg.html'); break; // 兑换奖品 case 'exchangePrizesDescribe' : $retMessage = InfoCenter::exchangePrizesDescribe($request, &$retArray); if ($retMessage != 'OK') { return; } $this->displayUTF8($retArray, 'thxl/textMsg.html'); break; // 挣取金币 case 'earnCoin' : $retMessage = InfoCenter::earnCoin($request, &$retArray); if ($retMessage != 'OK') { return; } $this->displayUTF8($retArray, 'thxl/textMsg.html'); break; // 点赞 case 'dianZan' : $retMessage = InfoCenter::dianZan($request, &$retArray); if ($retMessage != 'OK') { return; } $this->displayUTF8($retArray, 'thxl/textMsg.html'); break; // 我讲一个 case 'addJoke' : $retMessage = InfoCenter::addJoke($request, &$retArray); if ($retMessage != 'OK') { return; } $this->displayUTF8($retArray, 'thxl/textMsg.html'); break; // 我的笑话 case 'myJoke' : $retMessage = InfoCenter::myJoke($request, &$retArray); if ($retMessage != 'OK') { return; } $this->displayUTF8($retArray, 'thxl/textMsg.html'); break; // 金币记录 case 'tradeLog' : $retMessage = InfoCenter::tradeLog($request, &$retArray); if ($retMessage != 'OK') { return; } $this->displayUTF8($retArray, 'thxl/textMsg.html'); break; // 兑换奖品 case 'exchangePrizes' : $retMessage = InfoCenter::exchangePrizes($request, &$retArray); if ($retMessage != 'OK') { return; } $this->displayUTF8($retArray, 'thxl/textMsg.html'); break; // 给客服留言 case 'giveMessage' : $retMessage = InfoCenter::giveMessage($request, &$retArray); if ($retMessage != 'OK') { return; } $this->displayUTF8($retArray, 'thxl/textMsg.html'); break; // 关注 case 'subscribe' : $retMessage = InfoCenter::subscribe($request, &$retArray); if ($retMessage != 'OK') { return; } $this->displayUTF8($retArray, 'thxl/textMsg.html'); break; // 默认 default : $retMessage = InfoCenter::cmdNotFound($request, &$retArray); if ($retMessage != 'OK') { return; } $this->displayUTF8($retArray, 'thxl/textMsg.html'); break; } } /** * 获取请求参数 * * @param array $request 请求数组 * @return string 成功返回'OK',失败返回错误信息 */ private function _getRequest(&$request){ // 本机平台 if (SYS_RELEASE == 0) { $_GET["signature"] = '5b7b4a7c06b3bc4116a2fcbbbb2c887557cd07a6'; $_GET["timestamp"] = '1436056391'; $_GET["nonce"] = '1929760760'; // $_GET["echostr"] = 'this is from echostr'; $fp = fopen(HTTP_RAW_POST_DATA_TEST, "r"); $GLOBALS["HTTP_RAW_POST_DATA"] = fread($fp, filesize(HTTP_RAW_POST_DATA_TEST)); fclose($fp); } $request['signature'] = $_GET["signature"]; $request['timestamp'] = $_GET["timestamp"]; $request['nonce'] = $_GET["nonce"]; $request['echostr'] = $_GET["echostr"]; if (isset($GLOBALS["HTTP_RAW_POST_DATA"]) && !empty($GLOBALS["HTTP_RAW_POST_DATA"])) { $request['post'] = array(); $request['post']['items'] = XmlCenter::xmlToArray($GLOBALS["HTTP_RAW_POST_DATA"]); if (isset($request['post']['items']['Content'])) { $request['post']['items']['Content'] = iconv('UTF-8', 'GBK', $request['post']['items']['Content']); } $request['post']['string'] = $GLOBALS["HTTP_RAW_POST_DATA"]; // 插入请求日志 UserLog::insertRequestLog($request); } return 'OK'; } } ~~~ ### 6.2 保存get与post传输xml数据          每次微信服务器请求都会传输get参数,与post的xml数据。需要将这些数据保存到$request数组中。xml数据的格式如下。 ~~~ <xml><ToUserName><![CDATA[gh_5bcc295a14c4]]></ToUserName> <FromUserName><![CDATA[oihwct-iYa_xYXHAR2ZmnAPasEzQ]]></FromUserName> <CreateTime>1436275541</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[9#123456789]]></Content> <MsgId>6168756476849070426</MsgId> </xml> ~~~ 在ApithxlController.php,通过_getRequest(&$request)函数,将get与post参数保存到$request数组中。 ### 6.2 命令解析中心         当保存完参数后,需要对用户输入的内容进行解析,分析的数字命令是什么。在CmdCenter.php,查找的命令的入口函数式findCmd($request);首先查找是否是接口验证的命令,然后在从文本中查找命令,最后查找是否为事件命令。 ~~~ <?php /** * CmdCenter.php * * 查找cmd * * Copyright (c) 2015 http://blog.csdn.net/CleverCode * * modification history: * -------------------- * 2015/7/10, by CleverCode, Create * */ class CmdCenter{ // 命名字典 public static $cmdMap = array( 'validate' => '接口验证', 'subscribe' => '关注', 'readHelp' => '查看帮助', 'getOneAgain' => '发布一个', 'accountDetail' => '账户详情', 'earnCoin' => '挣取金币', 'dianZan' => '点赞', 'exchangePrizesDescribe' => '兑换奖品描述', 'addJoke' => '我讲一个', 'myJoke' => '我的笑话', 'tradeLog' => '交易记录', 'exchangePrizes' => '兑换奖品', 'giveMessage' => '给客服留言' ); // 数字到字符串命令字典 public static $numToCmdStr = array( '0' => 'readHelp', '1' => 'getOneAgain', '2' => 'exchangePrizesDescribe', '3' => 'earnCoin', '4' => 'accountDetail', '5' => 'myJoke', '6' => 'tradeLog', '7' => 'addJoke', '8' => 'dianZan', '9' => 'giveMessage', '100' => 'exchangePrizes' ); /** * 查找命令 * * @param array $request 请求数组 * @return string cmd */ public static function findCmd($request){ // 验证cmd $cmd = self::findValidateCmd($request); if (strlen($cmd) > 0) { return self::checkCmdValid($cmd); } // 查找文本命令 $cmd = self::findTextCmd($request); if (strlen($cmd) > 0) { return self::checkCmdValid($cmd); } // 查找事件命令 $cmd = self::findEventCmd($request); if (strlen($cmd) > 0) { return self::checkCmdValid($cmd); } } /** * 检查命令的有效性 * * @param string $cmd 命令 * @return string 有效返回$cmd,否则为空 */ public static function checkCmdValid($cmd){ if (isset(self::$cmdMap[$cmd])) { return $cmd; } } /** * 验证命令 * * @param array $request 请求数组 * @return string cmd */ public static function findValidateCmd($request){ if (isset($request['echostr']) && strlen($request['echostr']) > 0) { return 'validate'; } } /** * 查找文本命令 * * @param array $request 请求数组 * @return string cmd */ public static function findTextCmd($request){ if (empty($request['post'])) { return; } $msgType = $request['post']['items']['MsgType']; if ($msgType != 'text') { return; } $content = $request['post']['items']['Content']; $cmd = trim($content); $cmd = ltrim($cmd, '【'); $cmd = rtrim($cmd, '】'); if (strpos($cmd, "#") !== false) { $cmd = substr($cmd, 0, strpos($cmd, "#")); } // 大写 $cmd = strtoupper($cmd); if (!is_numeric($cmd)) { return; } if (isset(self::$numToCmdStr[$cmd])) { return self::$numToCmdStr[$cmd]; } } /** * 查找事件命令 * * @param array $request 请求数组 * @return string cmd */ public static function findEventCmd($request){ if (empty($request['post'])) { return; } $msgType = $request['post']['items']['MsgType']; if ($msgType != 'event') { return; } return $request['post']['items']['Event']; } } ~~~ ### 6.3 命令调度中心           当分析出命令后,通过switch ($cmd){}进行调度,通过不同的cmd调用不同InfoCenter执行逻辑去执行。例如用户回复的数字是1,那么命令解析中心就会把命令翻译为getOneAgain,调度中心会给根据getOneAgain,调用InfoCenter::getOneAgain($request, &$retArray),最后通过displayUTF8($retArray, 'thxl/validate.html')输出结果。 ### 6.4 命令执行中心         当调度中心执行命令中心的函数后,函数根据自己的业务逻辑去执行,这里重点介绍的是回复的内容,我们可以使用smarty模板。然后将模板的内容读入到字符串中。如下只举例获取帮助的代码。 readHelpContent.html模板如下。 ~~~ 命令帮助: ******************** 0.回复0,查看帮助! 1.回复1,再来一个! 2.回复2,兑换奖品! 3.回复3,挣取金币! 4.回复4,账户详情! 5.回复5,我的笑话! 6.回复6,金币记录! 7.回复7#笑话标题#笑 话正文,我讲一个! 8.回复8#笑话编号,点赞 ! 9.回复9#留言内容,给客 服留言! ******************** ~~~ 执行函数如下: ~~~ <?php /** * InfoCenter.php * * 信息中心 * * Copyright (c) 2015 http://blog.csdn.net/CleverCode * * modification history: * -------------------- * 2015/7/10, by CleverCode, Create * */ class InfoCenter{ //...... /** * 查看帮助 * * @param array $request 请求数组 * @param array $retArray 返回数组(输出参数) * @return string 成功返回'OK',失败返回错误信息 */ public static function readHelp($request, &$retArray){ $postStr = $request['post']['string']; if (empty($postStr)) { return 'empty($postStr)'; } $retArray['data']['fromUsername'] = $request['post']['items']['ToUserName']; $retArray['data']['toUsername'] = $request['post']['items']['FromUserName']; $retArray['data']['createTime'] = time(); $retArray['data']['msgType'] = 'text'; $content = SmartyWork::fetch(array(), 'thxl/readHelpContent.html'); $content = str_replace("\r", '', $content); $retArray['data']['content'] = $content; return 'OK'; } //...... } ~~~ ## 7 命令回复预览 ### 7.1 关注事件     关注公众号后,会自动获取1000金币,然后默认将一个笑话。    ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f5005d20c.jpg) ### 7.2 回复1     回复1,可以随机一个笑话。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f501143b2.jpg)     ### 7.2 回复2     回复2,可以查看兑换奖品帮助。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f50163a93.jpg) ### 7.3 回复3     回复3,挣取金币。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f50185675.jpg) ### 7.4 回复4     回复4,账户详情! ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f501a4491.jpg) ### 7.5 回复5 回复5,我的笑话! ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f501c1647.jpg) ### 7.6 回复6 回复6,金币记录! ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f501e6c54.jpg) ### 7.7 回复7 回复7#笑话标题#笑    话正文,我讲一个! ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f50210466.jpg) ### 7.8 回复8 回复8#笑话编号,点赞    ! ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f5022fc2f.jpg) ### 7.9 回复9 回复9#留言内容,给客    服留言! ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f50248d87.jpg) ### 7.10 回复0 回复0,查看帮助。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f5026c5c4.jpg) ## 8 总结           项目经过几天的设计后,终于做完了,于是让自己的好朋友测测,玩一玩。他们给出的点评是,还需要回复啊,不给个按钮更好。我说没权限获取自定义按钮。那个说笑话需要手动输入那么多笑话的汉字,手机打字太费劲了。          种种的原因后,这个微信项目搁浅了,我现在想不出来更好的运营方法。后来一想还不如把我的经历写出来分享给大家。一个是相互学习与交流。也希望大家不要走学我,头脑一发热说干就干。一定需要很周密的计划才行!谋定而后动..........................
';

写入Memcache时是否应该压缩值?

最后更新于:2022-04-01 11:54:14

       CleverCode最近在查看资料时,发现一种写入Memcache时,先采用gzcompress()压缩后在写入。这种做法到底好不好,我在这里分析一些,与大家一起探讨一下。 ## 1 php压缩效率分析 ### 1.1 压缩函数         当我们说到压缩,我们可能会想到文件压缩,其实,字符串也是可以压缩的。PHP提供了。gzcompress()和gzuncompress()函数。同时,你还可以使用gzencode()和gzdecode()函数来压缩,只不用其用了不同的压缩算法。 这里使用gzcompress()和gzuncompress()函数。 ~~~ <?php /** * testInfo.php * * php压缩效率分析 * * Copyright (c) 2015 http://blog.csdn.net/CleverCode * * modification history: * -------------------- * 2015/6/30, by CleverCode, Create * */ $string = "CleverCode是一名PHP工程师,他只想把自己一点点的智慧分享给大家! 他的博客地址:http://blog.csdn.net/CleverCode。 他的微博地址:http://weibo.com/CleverCode。 他推荐的博客专栏: 《设计模式之PHP项目应用》:http://blog.csdn.net/column/details/phpusedesignpattern.html。 《Linux常用软件安装与配置》:http://blog.csdn.net/column/details/linuxsoftwareinstall.html。 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc ut elit id mi ultricies adipiscing. Nulla facilisi. Praesent pulvinar, sapien vel feugiat vestibulum, nulla dui pretium orci, non ultricies elit lacus quis ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam pretium ullamcorper urna quis iaculis. Etiam ac massa sed turpis tempor luctus. Curabitur sed nibh eu elit mollis congue. Praesent ipsum diam, consectetur vitae ornare a, aliquam a nunc. In id magna pellentesque tellus posuere adipiscing. Sed non mi metus, at lacinia augue. Sed magna nisi, ornare in mollis in, mollis sed nunc. Etiam at justo in leo congue mollis. Nullam in neque eget metus hendrerit scelerisque eu non enim. Ut malesuada lacus eu nulla bibendum id euismod urna sodales."; //压缩 $compressed = gzcompress($string); echo "Original size: ". strlen($string)."\n"; /* 输出原始大小 Original size: 1157 */ echo "Compressed size: ". strlen($compressed)."\n"; /* 输出压缩后的大小 Compressed size: 680 */ // 解压缩 $original = gzuncompress($compressed); ?> ~~~ ### 1.2 压缩结果 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f50029619.jpg) ## 2 Memcache写入压缩       根据上面的压缩方式,可以看到节省了内存约50%。那么在我们内存不充足的情况下。我们在写入Memcache时可以先压缩,然后再放入到内存中。 ### 2.1 自己压缩 ~~~ <?php /** * testMemcache.php * * 写入Memcache * * Copyright (c) 2015 http://blog.csdn.net/CleverCode * * modification history: * -------------------- * 2015/6/30, by CleverCode, Create * */ /** * 写缓存 * * @param string $key key * @param mixed $data 数据 * @param int $expire 有效时间 * @return null */ function mc_set($key = '', $data = '', $expire = 3600){ // 序列化 $string = serialize($data); // 压缩 $compressed = gzcompress($string); // 写入缓存 $memcache_obj = new Memcache(); $memcache_obj->connect('memcache_host', 11211); $memcache_obj->set($key, $compressed, 0, $expire); } /** * 读缓存 * * @param string $key * @return mixed */ function mc_get($key = ''){ // 写入缓存 $memcache_obj = new Memcache(); /* connect to memcached server */ $memcache_obj->connect('memcache_host', 11211); // 读取 $compressed = $memcache_obj->get($key); // 解压 $string = gzuncompress($compressed); // 反序例化 $data = unserialize($string); return $data; } ?> ~~~ ### 2.2 自带压缩 Memcache函数set时候有一个参数,可以指定是否压缩。如下。       bool Memcache::set ( string $key , mixed $var [, int $flag [, int $expire ]] )       Memcache::set()向key存储一个元素值为 var。参数expire是以秒为单位的失效时间, 如果设置为0表明该元素永不过期(但是它可能会因为为了给其他项分配空间而被删除)。如果你希望存储的元素 经过压缩(使用zlib),你可以设置flag的值为MEMCACHE_COMPRESSED。 样例: ~~~ <?php /** * testMemcache.php * * 写入Memcache * * Copyright (c) 2015 http://blog.csdn.net/CleverCode * * modification history: * -------------------- * 2015/6/30, by CleverCode, Create * */ /* OO API */ $memcache_obj = new Memcache(); /* connect to memcached server */ $memcache_obj->connect('memcache_host', 11211); /* * 设置'var_key'对应值,使用即时压缩 * 失效时间为50秒 */ $memcache_obj->set('var_key', 'some really big variable', MEMCACHE_COMPRESSED, 50); echo $memcache_obj->get('var_key'); ?> ~~~ ## 3 总结         将数据压缩后然后在写入memcache。 优点:       1)压缩后的数据存储,减少了内存的开销。        2) 压缩的数据,长度变小,减少了客户端与memcache服务器端的网络发送时间。 缺点:        1)由于需要压缩与解压,这样会多占用cpu的开销。        2)压缩比不压缩时间开销稍微更大点。 ## 4 扩展     压缩的算法,有很多种,最常见的一种就是减少重复。比如2,3,4,5,5,5,5,5,5,5,7;上面的数据重复了6个5;可以在压缩的时候写出2,3,4,0,6,5,7。具体详解,有兴趣可以查看我转载的一篇优秀博客《几种压缩算法原理介绍》:[http://blog.csdn.net/clevercode/article/details/46691645](http://blog.csdn.net/clevercode/article/details/46691645)。 ## 版权声明: 1)原创作品,出自"CleverCode的博客",严禁转载,否则追究版权法律责任。 2)原创地址:[http://blog.csdn.net/clevercode/article/details/46691851](http://blog.csdn.net/clevercode/article/details/46691851)。 3)分类地址(PHP程序员技术精粹):[http://blog.csdn.net/clevercode/article/category/3169943](http://blog.csdn.net/clevercode/article/category/3169943)(博客持续增加,关注请收藏)。 4)欢迎大家关注CleverCode博客更多的精彩内容:[http://blog.csdn.net/CleverCode](http://blog.csdn.net/CleverCode)。 5)欢迎大家关注CleverCode的微博:[http://weibo.com/CleverCode](http://weibo.com/CleverCode)。
';

PHP长整型在32位系统中强制转化溢出

最后更新于:2022-04-01 11:54:12

        CleverCode最近遇到一个PHP项目整形转化问题,mysql有一个字段id是bigint的,里面有长整型,如id = 5147486396。但是php代码由于历史原因却部署在多台机器中,其中A机器32位系统中,B机器64系统中。现在的问题是64系统中页面访问正常。32位系统中访问出错了。原因是php整形溢出。 ## 1 A机器演示 ### 1.1 获取A机器系统位数 ~~~ # getconf LONG_BIT ~~~ ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f4ffa5c71.jpg) ### 1.2 整形转化代码 ~~~ <?php $id = 5147486396; echo '$id:'.$id."\r\n"; $value = (int)$id; echo '(int)$id:'.$value."\r\n"; $value = intval($id); echo 'intval($id):'.$value."\r\n"; $value = filter_var($id, FILTER_VALIDATE_INT); echo 'filter_var($id, FILTER_VALIDATE_INT):'."\r\n"; var_dump($value); ?> ~~~ ### 1.3 运行结果 (int)5147486396的结果是852519100,intval(5147486396)的结果是852519100,filter_var(5147486396, FILTER_VALIDATE_INT)结果是false。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f4ffbd2f5.jpg) ## 2 B机器演示 ### 2.1 获取B机器系统位数 `# getconf LONG_BIT` ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f4ffddbf1.jpg) ### 2.2 整形转化代码 ~~~ <?php $id = 5147486396; echo '$id:'.$id."\r\n"; $value = (int)$id; echo '(int)$id:'.$value."\r\n"; $value = intval($id); echo 'intval($id):'.$value."\r\n"; $value = filter_var($id, FILTER_VALIDATE_INT); echo 'filter_var($id, FILTER_VALIDATE_INT):'."\r\n"; var_dump($value); ?> ~~~ ### 2.3 运行结果 (int)5147486396的结果是5147486396,intval(5147486396)的结果是5147486396,filter_var(5147486396, FILTER_VALIDATE_INT)结果是5147486396。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f50008db5.jpg) ## 3 结论         整型数的字长和平台有关,尽管通常最大值是大约二十亿(32 位有符号)。64 位平台下的最大值通常是大约 9E18。PHP 不支持无符号整数。Integer 值的字长可以用常量 PHP_INT_SIZE来表示,自 PHP 4.4.0 和 PHP 5.0.5后,最大值可以用常量 PHP_INT_MAX 来表示。 ## 版权声明: 1)原创作品,出自"CleverCode的博客",严禁转载,否则追究版权法律责任。 2)原创地址:[http://blog.csdn.net/clevercode/article/details/46423103](http://blog.csdn.net/clevercode/article/details/46423103)。 3)分类地址(PHP程序员技术精粹):[http://blog.csdn.net/clevercode/article/category/3169943](http://blog.csdn.net/clevercode/article/category/3169943)(博客持续增加,关注请收藏)。 4)欢迎大家关注CleverCode博客更多的精彩内容:[http://blog.csdn.net/CleverCode](http://blog.csdn.net/CleverCode)。 5)欢迎大家关注CleverCode的微博:[http://weibo.com/CleverCode](http://weibo.com/CleverCode)。
';

PHP导入与导出xml格式的Excel

最后更新于:2022-04-01 11:54:10

## 1 简介 ### 1.1 导出          在实际的工作项目中,经常需要将一些重要的数据库中存的数据导出成Excel,比如导出考勤报表,导出财务报表,导出业绩报表,导出销售报表等。CleverCode以前使用了两年的PHPExcel来制作Excel导出数据,但发现用PHPExcel生成Excel实在是太麻烦了,特别是控制单元格的颜色,合并单元格,给单元格设置长度等。这些设计一个Excel通常会需要花费一天的时间。后来CleverCode发现了一个简便的方法PHP导出xml格式的Excel,以前需要一天的工作量,现在半小时就搞定了,实在是事半功倍!   ### 1.2 导入        同时有的项目也需要,将一些Excel数据导入到数据库中。比如银行给的银行流水,销售报表导入的数据库中。这个通常的做法都是使用PHPExcel。         虽然读取xml格式的Excel可以使用Xml parser, SimpleXML, XMLReader, DOMDocument等方式,但是CleverCode尝试过使用这些方式,发现太复杂,太费劲,没有PHPExcel好使。        所以当需要读取Excel的时候(包括xml格式的),CleverCode推荐使用PHPExcel库。 ## 2 需求     某个集团需要各个地区的负责人将其负责的城市站的订单和销售额导入到数据库。     1)网站提供一个导入的销售报表模板。     2)每个负责人只能上传与下载各自负责城市的数据(权限检查)。     3)只用上传生成当年,当日的拥有的所有季度。比如今天是2015-05-26。那么只有生成2015一季度,与二季度。         如果是2015-12-01。则需要生成2015一,二,三,四季度。     4)显示本季度以前季度的数据。     5)本季度的数据默认全是0。     6)只能修改本季度的数据。 ## 3 程序设计源码下载        [http://download.csdn.net/detail/clevercode/8750869](http://download.csdn.net/detail/clevercode/8750869) ## 4 设计网站页面 ### 4.1 显示 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f4fe8343f.jpg) ### 4.2 display.php代码 ~~~ <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=gbk" /> <title>PHP导入与导出xml格式的Excel</title> <style type="text/css"> body{ font-size:14px;} input{ vertical-align:middle; margin:0; padding:0} .file-box{ position:relative;width:340px} .txt{ height:22px; border:1px solid #cdcdcd; width:180px;} .btn{ background-color:#FFF; border:1px solid #CDCDCD;height:24px; width:70px;} .file{ position:absolute; top:0; right:80px; height:24px; filter:alpha(opacity:0); opacity: 0;width:260px } </style> </head> <body> <div class="file-box"> <form action="" method="post" enctype="multipart/form-data"> <input type='text' name='textfield' id='textfield' class='txt' /> <input type='button' class='btn' value='浏览...' /> <input type="file" name="fileField" class="file" id="fileField" size="28" onchange="document.getElementById('textfield').value=this.value" /> <input type="submit" name="submit" class="btn" value="上传" /> </form> <a href="export.php">下载销售报表模板</a> </div> </body> </html> ~~~    ## 5 PHP导出xml格式的Excel(导出销售报表模板) 1)新建一个【销售报表.xlsx】。设计如下。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f4fe99c4c.jpg) 2)将【销售报表.xlsx】文件另存为【销售报表.xml】 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f4febfed1.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f4ff0086c.jpg) 3)打开【销售报表.xml】可以看到xml格式的数据。 4)找到table信息。删除掉ss:ExpandedColumnCount="5" ss:ExpandedRowCount="6"。这个限制死了表格的长度和宽度,所以必须去掉。    <Table ss:ExpandedColumnCount="5" ss:ExpandedRowCount="6" x:FullColumns="1"    x:FullRows="1" ss:StyleID="s23" ss:DefaultColumnWidth="54"    ss:DefaultRowHeight="18.75">   改成   <Table  x:FullColumns="1"    x:FullRows="1" ss:StyleID="s23" ss:DefaultColumnWidth="54"    ss:DefaultRowHeight="18.75"> ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f4ff31258.jpg) 5 PHP导出Excel业务逻辑代码(Excel.php) ~~~ <?php /** * Excel.php * * Excel操作 * * Copyright (c) 2015 http://blog.csdn.net/CleverCode * * modification history: * -------------------- * 2015/5/14, by CleverCode, Create * */ class Excel{ /** * 导出excel * * @param int $userid 用户编号 * @return string $xmlStr */ public static function export($userid){ // 根据不同用户的权限,获取不同的数据 $data = self::getExportData($personid); // 获取字符串,如果excel的列是固定的可以通过Smarty方式获取 // 但是如果excel的列需要通过动态生成,则可以通过php组合字符串。 // $xmlStr = self::getXmlStrBySmarty($data); // 这个需要根据当前日期动态的生成有几个季度 $xmlStr = self::getXmlStrByPHP($data); return $xmlStr; } /** * 生成excel数据 * * @param int $userid 用户编号 * @return array 结果数据 */ public static function getExportData($userid){ if (!is_int($userid)) { return array(); } $infoBJ = array( 'city' => '北京', 'order_1' => 100, 'money_1' => 10000, 'order_2' => 200, 'money_2' => 40000 ); $infoTJ = array( 'city' => '天津', 'order_1' => 50, 'money_1' => 1000, 'order_2' => 100, 'money_2' => 2000 ); $infoGZ = array( 'city' => '广州', 'order_1' => 50, 'money_1' => 1000, 'order_2' => 100, 'money_2' => 2000 ); // 根据不同用户的权限,获取不同的数据 if (is_admin($userid)) { $data[] = $infoBJ; $data[] = $infoTJ; $data[] = $infoGZ; } else { $data[] = $infoBJ; } return $data; } /** * 通过Smarty方式获取xml字符串 * * @param array $data 结果集 * @return string $xmlStr */ public static function getXmlStrBySmarty($data){ require_once 'Smarty.class.php'; $smarty = new Smarty(); $tpl = 'file/export.tpl'; $smarty->assign('list', $data); // capture the output // 捕获输出 $xml = $smarty->fetch($tpl); return $xml; } /** * 通过PHP组合字符串方式获取xml字符串(可以动态扩展列) * * @param array $data 结果集 * @return string $xmlStr */ public static function getXmlStrByPHP($data){ $xml = ' <?xml version="1.0"?> <?mso-application progid="Excel.Sheet"?> <Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office" ............ </Style> </Styles> <Worksheet ss:Name="Sheet1"> <Table x:FullColumns="1" x:FullRows="1" ss:StyleID="s16" ss:DefaultColumnWidth="54" ss:DefaultRowHeight="18.75"> '; //可以根据季度的多少动态扩展列,这里不做说明,请自行尝试。 $xml. = ' <Row ss:AutoFitHeight="0"> <Cell ss:MergeDown="1" ss:StyleID="m42513364"><Data ss:Type="String">城市</Data></Cell> <Cell ss:MergeAcross="1" ss:StyleID="s25"><Data ss:Type="String">2015一季度</Data></Cell> <Cell ss:MergeAcross="1" ss:StyleID="m42513344"><Data ss:Type="String">2015二季度</Data></Cell> </Row> <Row ss:AutoFitHeight="0"> <Cell ss:Index="2" ss:StyleID="s17"><Data ss:Type="String">订单</Data></Cell> <Cell ss:StyleID="s17"><Data ss:Type="String">销售额</Data></Cell> <Cell ss:StyleID="s17"><Data ss:Type="String">订单</Data></Cell> <Cell ss:StyleID="s17"><Data ss:Type="String">销售额</Data></Cell> </Row> '; // 输出数据 foreach ( $data as $row ) { $xml .= ' <Row ss:AutoFitHeight="0"> <Cell ss:StyleID="s18"><Data ss:Type="String">' . $row['city'] . '</Data></Cell> <Cell ss:StyleID="s19"><Data ss:Type="Number">' . $row['order_1'] . '</Data></Cell> <Cell ss:StyleID="s19"><Data ss:Type="Number">' . $row['money_1'] . '</Data></Cell> <Cell ss:StyleID="s19"><Data ss:Type="Number">' . $row['order_2'] . '</Data></Cell> <Cell ss:StyleID="s19"><Data ss:Type="Number">' . $row['money_2'] . '</Data></Cell> </Row> '; } $xml .= ' <WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel"> ........... </Workbook> '; return $xml; } } ~~~ 6 PHP导出Excel客户端代码(export.php) ~~~ <?php /** * export.php * * 导出excel * * Copyright (c) 2015 http://blog.csdn.net/CleverCode * * modification history: * -------------------- * 2015/5/14, by CleverCode, Create * */ // Excel类 include_once ('Excel.php'); /* * 客户端类 * 让客户端和业务逻辑尽可能的分离,降低客户端和业务逻辑算法的耦合, * 使业务逻辑的算法更具有可移植性 */ class Client{ public function main(){ // 获取xml格式字符串 $xmlStr = Excel::export(1); // 头部 $filename = '销售报表模板'; header("Content-Type: application/vnd.ms-excel; charset=UTF-8"); header("Content-Disposition: inline; filename=\"$filename.xls\""); header("Content-Transfer-Encoding: binary"); header("Pragma: public"); header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); // 输出字符串 echo $xmlStr; exit(); } } /** * 程序入口 */ function start(){ $client = new Client(); $client->main(); } start(); ?> ~~~ ## 6 PHP导入xml格式的Excel 1)下载销售模板后,填写其中的数据,然后点击页面中的上传按钮,上传Excel数据。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f4ff8c56e.jpg) 2) PHP导入的Excel业务逻辑代码(Excel.php)。这里使用了PHPExcel库。 ~~~ <?php /** * Excel.php * * Excel操作 * * Copyright (c) 2015 http://blog.csdn.net/CleverCode * * modification history: * -------------------- * 2015/5/14, by CleverCode, Create * */ class Excel{ /* * 读取Excel格式的数据(可以读取xml格式数据) * * @param string $filename excel文件 * @param string $startRow 开始行 * @param string $endRow 结束行 * @param string $startColumn 开始列 * @param string $endColumn 结束列 * @return array excel结果集数据 */ public static function read($filename, $startRow = 1, $endRow = null, $startColumn = 0, $endColumn = null){ $excelData = array(); if (!file_exists($filename)) { return $excelData; } require_once 'PHPExcel/PHPExcel.php'; require_once 'PHPExcel/PHPExcel/IOFactory.php'; // 加载excel文件 $objPHPExcel = PHPExcel_IOFactory::load($filename); // 获取焦点Sheet $objWorksheet = $objPHPExcel->getActiveSheet(); // 获取总行 $totalRows = $objWorksheet->getHighestRow(); // 获取总行数 // 获取总列 $highestColumn = $objWorksheet->getHighestColumn(); $totalColumns = PHPExcel_Cell::columnIndexFromString($highestColumn); // 开始行 if (!is_int($startRow) || $startRow < 1) { $startRow = 1; } // 结束行 if ($endRow == null || !is_int($endRow) || $endRow > $totalRows) { $endRow = $totalRows; } // 开始列 if (!is_int($startColumn) || $startColumn < 0) { $startColumn = 0; } // 结束列 if ($endColumn == null || !is_int($endColumn) || $endColumn > $totalColumns) { $endColumn = $totalColumns; } // 读取数据 for($rowNum = $startRow; $rowNum <= $endRow; $rowNum++) { for($colNum = $startColumn; $colNum < $endColumn; $colNum++) { $item = $objWorksheet->getCellByColumnAndRow($colNum, $rowNum); $exValue = trim($item->getValue()); $excelData[$rowNum][$colNum] = $exValue; } } return $excelData; } } ~~~ 3) PHP导入的Excel客户端代码(import.php) ~~~ <?php /** * import.php * * 导入excel * * Copyright (c) 2015 http://blog.csdn.net/CleverCode * * modification history: * -------------------- * 2015/5/14, by CleverCode, Create * */ // Excel类 include_once ('Excel.php'); /* * 客户端类 * 让客户端和业务逻辑尽可能的分离,降低客户端和业务逻辑算法的耦合, * 使业务逻辑的算法更具有可移植性 */ class Client{ public function main(){ if (!$_FILES['file']) { exit(); } // 从第3行开始读取Excel数据 $datas = Excel::read($_FILES['file']['tmp_name'], 3); // 将$datas保存到数据库 // .... } } /** * 程序入口 */ function start(){ $client = new Client(); $client->main(); } start(); ?> ~~~ **版权声明:** 1)原创作品,出自"CleverCode的博客",转载时请务必注明以下原创地址,否则追究版权法律责任。 2)原创地址:[http://blog.csdn.net/clevercode/article/details/46226461](http://blog.csdn.net/clevercode/article/details/46226461)(转载务必注明该地址)。 3)分类地址(PHP程序员技术精粹):[http://blog.csdn.net/clevercode/article/category/3169943](http://blog.csdn.net/clevercode/article/category/3169943)(博客持续增加,关注请收藏) 4)欢迎大家关注我博客更多的精彩内容:[http://blog.csdn.net/CleverCode](http://blog.csdn.net/CleverCode)。
';

Zend Studio 12.0.2设置文件定界符和文件编码(解决中文乱码问题)

最后更新于:2022-04-01 11:54:07

## 1 简介         CleverCode开发的PHP项目使用的编辑器是Zend Studio 12.0.2,PHP一般都是文件定界符是Windows的,文件编码也是gbk的。但是最近接到一个项目里面的所有php文件定界符是Unix的,文件编码是utf-8的。打开之后就是如下图的乱码了。打开test.php。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f4fd4be67.jpg) ## 2 设定某一个PHP的文件编码 1 选中test.php,然后点击右键,选择【属性】。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f4fd79384.jpg) 2) 设置文件的编码为UTF-8。然后test.php编码就正常了。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f4fd9d100.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f4fdc6269.jpg) ## 3 给项目设置文件编码和文件定界符 1)如果一个项目php非常多,一个一个php设置成utf-8,肯定是不合适的。zend studio提供了一个继承的方式。只需要将导入的项目文件夹的属性设置为utf-8。这个项目的中所有的php打开后默认继承编码都是utf-8。继承可同时设定文件定界符为Unix,某些svn中限制只能Unix编码提交,不是Unix编码的文件提交不了。 2)选中CleverCode项目根目录,点击右键,选择【属性】。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f4fdedb06.jpg) 2)查看CleverCode项目默认的配置。默认的文件定界符为Windows,文件编码为gbk。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f4fe3534d.jpg) 3)设置CleverCode项目。文件定界符为Unix,文件编码为UTF-8。那么CleverCode项目下所的文件打开时候,都继承这个配置,都是Unix定界符,和UTF-8编码。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f4fe59aa3.jpg) 4)按照以上的方法,可以给每一个导入的项目配置不同的定界符和文件编码。 **版权声明:** 1)原创作品,出自"CleverCode的博客",转载时请务必注明以下原创地址,否则追究版权法律责任。 2)原创地址:[http://blog.csdn.net/clevercode/article/details/46118597](http://blog.csdn.net/clevercode/article/details/46118597)(转载务必注明该地址)。 3)分类地址(PHP程序员技术精粹):[http://blog.csdn.net/clevercode/article/category/3169943](http://blog.csdn.net/clevercode/article/category/3169943)(博客持续增加,关注请收藏) 4)欢迎大家关注我博客更多的精彩内容:[http://blog.csdn.net/CleverCode](http://blog.csdn.net/CleverCode)。
';

Zend Studio 12.0.2打开文件夹项目

最后更新于:2022-04-01 11:54:05

1)【文件】=》【新建】=》【PHP Project from Existing Directory】 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f4fcb6e4c.jpg) 2)输入项目名称,选择文件位置。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f4fcdb8bd.jpg) 3)完成。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f4fd097a5.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f4fd2c43a.jpg) **版权声明:** 1)原创作品,出自"CleverCode的博客",转载时请务必注明以下原创地址,否则追究版权法律责任。 2)原创地址:[http://blog.csdn.net/clevercode/article/details/46047247](http://blog.csdn.net/clevercode/article/details/46047247)(转载务必注明该地址)。 3)分类地址(PHP程序员技术精粹):[http://blog.csdn.net/clevercode/article/category/3169943](http://blog.csdn.net/clevercode/article/category/3169943)(博客持续增加,关注请收藏) 4)欢迎大家关注我博客更多的精彩内容:[http://blog.csdn.net/CleverCode](http://blog.csdn.net/CleverCode)。
';

PHP调用Python快速发送高并发邮件

最后更新于:2022-04-01 11:54:03

## 1 简介        在PHP中发送邮件,通常都是封装一个php的smtp邮件类来发送邮件。但是PHP底层的socket编程相对于python来说效率是非常低的。CleverCode同时写过用python写的爬虫抓取网页,和用php写的爬虫抓取网页。发现虽然用了php的curl抓取网页,但是涉及到超时,多线程同时抓取等等。不得不说python在网络编程的效率要比PHP好的多。      PHP在发送邮件时候,自己写的smtp类,发送的效率和速度都比较低。特别是并发发送大量带有附件报表的邮件的时候。php的效率很低。建议可以使用php调用python的方式来发送邮件。 ## 2 程序 ### 2.1 python程序       php的程序和python的文件必须是相同的编码。如都是gbk编号,或者同时utf-8编码,否则容易出现乱码。python发送邮件主要使用了email模块。这里python文件和php文件都是gbk编码,发送的邮件标题内容与正文内容也是gbk编码。 ~~~ #!/usr/bin/python # -*- coding:gbk -*- """ 邮件发送类 """ # mail.py # # Copyright (c) 2014 by http://blog.csdn.net/CleverCode # # modification history: # -------------------- # 2014/8/15, by CleverCode, Create import threading import time import random from email.MIMEText import MIMEText from email.MIMEMultipart import MIMEMultipart from email.MIMEBase import MIMEBase from email import Utils, Encoders import mimetypes import sys import smtplib import socket import getopt import os class SendMail: def __init__(self,smtpServer,username,password): """ smtpServer:smtp服务器, username:登录名, password:登录密码 """ self.smtpServer = smtpServer self.username = username self.password = password def genMsgInfo(self,fromAddress,toAddress,subject,content,fileList,\ subtype = 'plain',charset = 'gb2312'): """ 组合消息发送包 fromAddress:发件人, toAddress:收件人, subject:标题, content:正文, fileList:附件, subtype:plain或者html charset:编码 """ msg = MIMEMultipart() msg['From'] = fromAddress msg['To'] = toAddress msg['Date'] = Utils.formatdate(localtime=1) msg['Message-ID'] = Utils.make_msgid() #标题 if subject: msg['Subject'] = subject #内容 if content: body = MIMEText(content,subtype,charset) msg.attach(body) #附件 if fileList: listArr = fileList.split(',') for item in listArr: #文件是否存在 if os.path.isfile(item) == False: continue att = MIMEText(open(item).read(), 'base64', 'gb2312') att["Content-Type"] = 'application/octet-stream' #这里的filename邮件中显示什么名字 filename = os.path.basename(item) att["Content-Disposition"] = 'attachment; filename=' + filename msg.attach(att) return msg.as_string() def send(self,fromAddress,toAddress,subject = None,content = None,fileList = None,\ subtype = 'plain',charset = 'gb2312'): """ 邮件发送函数 fromAddress:发件人, toAddress:收件人, subject:标题 content:正文 fileList:附件列表 subtype:plain或者html charset:编码 """ try: server = smtplib.SMTP(self.smtpServer) #登录 try: server.login(self.username,self.password) except smtplib.SMTPException,e: return "ERROR:Authentication failed:",e #发送邮件 server.sendmail(fromAddress,toAddress.split(',') \ ,self.genMsgInfo(fromAddress,toAddress,subject,content,fileList,subtype,charset)) #退出 server.quit() except (socket.gaierror,socket.error,socket.herror,smtplib.SMTPException),e: return "ERROR:Your mail send failed!",e return 'OK' def usage(): """ 使用帮助 """ print """Useage:%s [-h] -s <smtpServer> -u <username> -p <password> -f <fromAddress> -t <toAddress> [-S <subject> -c <content> -F <fileList>] Mandatory arguments to long options are mandatory for short options too. -s, --smtpServer= smpt.xxx.com. -u, --username= Login SMTP server username. -p, --password= Login SMTP server password. -f, --fromAddress= Sets the name of the "from" person (i.e., the envelope sender of the mail). -t, --toAddress= Addressee's address. -t "test@test.com,test1@test.com". -S, --subject= Mail subject. -c, --content= Mail message.-c "content, ......." -F, --fileList= Attachment file name. -h, --help Help documen. """ %sys.argv[0] def start(): """ """ try: options,args = getopt.getopt(sys.argv[1:],"hs:u:p:f:t:S:c:F:","--help --smtpServer= --username= --password= --fromAddress= --toAddress= --subject= --content= --fileList=",) except getopt.GetoptError: usage() sys.exit(2) return smtpServer = None username = None password = None fromAddress = None toAddress = None subject = None content = None fileList = None #获取参数 for name,value in options: if name in ("-h","--help"): usage() return if name in ("-s","--smtpServer"): smtpServer = value if name in ("-u","--username"): username = value if name in ("-p","--password"): password = value if name in ("-f","--fromAddress"): fromAddress = value if name in ("-t","--toAddress"): toAddress = value if name in ("-S","--subject"): subject = value if name in ("-c","--content"): content = value if name in ("-F","--fileList"): fileList = value if smtpServer == None or username == None or password == None: print 'smtpServer or username or password can not be empty!' sys.exit(3) mail = SendMail(smtpServer,username,password) ret = mail.send(fromAddress,toAddress,subject,content,fileList) if ret != 'OK': print ret sys.exit(4) print 'OK' return 'OK' if __name__ == '__main__': start() ~~~ ### 2.2 python程序使用帮助 输入以下命令,可以输出这个程序的使用帮助 `# python mail.py --help` ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f4fc626f7.jpg) ### 2.3 php程序 这个程序主要是php拼接命令字符串,调用python程序。注意:用程序发送邮件,需要到邮件服务商,开通stmp服务功能。如qq就需要开通smtp功能后,才能用程序发送邮件。开通如下图。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f4fc92672.jpg) php调用程序如下: ~~~ <?php /** * SendMail.php * * 发送邮件类 * * Copyright (c) 2015 by http://blog.csdn.net/CleverCode * * modification history: * -------------------- * 2015/5/18, by CleverCode, Create * */ class SendMail{ /** * 发送邮件方法 * * @param string $fromAddress 发件人,'clevercode@qq.com' 或者修改发件人名 'CleverCode<clevercode@qq.com>' * @param string $toAddress 收件人,多个收件人逗号分隔,'test1@qq.com,test2@qq.com,test3@qq.com....', 或者 'test1<test1@qq.com>,test2<test2@qq.com>,....' * @param string $subject 标题 * @param string $content 正文 * @param string $fileList 附件,附件必须是绝对路径,多个附件逗号分隔。'/data/test1.txt,/data/test2.tar.gz,...' * @return string 成功返回'OK',失败返回错误信息 */ public static function send($fromAddress, $toAddress, $subject = NULL, $content = NULL, $fileList = NULL){ if (strlen($fromAddress) < 1 || strlen($toAddress) < 1) { return '$fromAddress or $toAddress can not be empty!'; } // smtp服务器 $smtpServer = 'smtp.qq.com'; // 登录用户 $username = 'clevercode@qq.com'; // 登录密码 $password = '123456'; // 拼接命令字符串,实际是调用了/home/CleverCode/mail.py $cmd = "LANG=C && /usr/bin/python /home/CleverCode/mail.py"; $cmd .= " -s '$smtpServer'"; $cmd .= " -u '$username'"; $cmd .= " -p '$password'"; $cmd .= " -f '$fromAddress'"; $cmd .= " -t '$toAddress'"; if (isset($subject) && $subject != NULL) { $cmd .= " -S '$subject'"; } if (isset($content) && $content != NULL) { $cmd .= " -c '$content'"; } if (isset($fileList) && $fileList != NULL) { $cmd .= " -F '$fileList'"; } // 执行命令 exec($cmd, $out, $status); if ($status == 0) { return 'OK'; } else { return "Error,Send Mail,$fromAddress,$toAddress,$subject,$content,$fileList "; } return 'OK'; } } ~~~ ### 2.3 使用样例 压缩excel成附件,发送邮件。 ~~~ <?php /** * test.php * * 压缩excel成附件,发送邮件 * * Copyright (c) 2015 http://blog.csdn.net/CleverCode * * modification history: * -------------------- * 2015/5/14, by CleverCode, Create * */ include_once ('SendMail.php'); /* * 客户端类 * 让客户端和业务逻辑尽可能的分离,降低页面逻辑和业务逻辑算法的耦合, * 使业务逻辑的算法更具有可移植性 */ class Client{ public function main(){ // 发送者 $fromAddress = 'CleverCode<clevercode@qq.com>'; // 接收者 $toAddress = 'all@qq.com'; // 标题 $subject = '这里是标题!'; // 正文 $content = "您好:\r\n"; $content .= " 这里是正文\r\n "; // excel路径 $filePath = dirname(__FILE__) . '/excel'; $sdate = date('Y-m-d'); $PreName = 'CleverCode_' . $sdate; // 文件名 $fileName = $filePath . '/' . $PreName . '.xls'; // 压缩excel文件 $cmd = "cd $filePath && zip $PreName.zip $PreName.xls"; exec($cmd, $out, $status); $fileList = $filePath . '/' . $PreName . '.zip'; // 发送邮件(附件为压缩后的文件) $ret = SendMail::send($fromAddress, $toAddress, $subject, $content, $fileList); if ($ret != 'OK') { return $ret; } return 'OK'; } } /** * 程序入口 */ function start(){ $client = new Client(); $client->main(); } start(); ?> ~~~ ### 2.4 程序源码下载 [http://download.csdn.net/detail/clevercode/8711809](http://download.csdn.net/detail/clevercode/8711809) **版权声明:** 1)原创作品,出自"CleverCode的博客",转载时请务必注明以下原创地址,否则追究版权法律责任。 2)原创地址:[http://blog.csdn.net/clevercode/article/details/45815453](http://blog.csdn.net/clevercode/article/details/45815453)(转载务必注明该地址)。 3)欢迎大家关注我博客更多的精彩内容:[http://blog.csdn.net/CleverCode](http://blog.csdn.net/CleverCode)。
';

一个网站优秀的登录验证设计方案(登录页面的超时以及密码加上时间戳)

最后更新于:2022-04-01 11:54:00

## 1 有隐患登录的验证方式  ### 1.1有隐患登录的验证方式介绍      ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-07_5705f4fc4aab8.jpg)     如图所示,从firebug上抓取到我们输入完用户名和密码之后的信息,url:http://xxx.com/doLogin.php?username=CleverCode&pwd=123456,可以看出,我们的请求方式是Get请求,还有我们现在的密码传输用的是明文传输。可以很清楚的看到我们登录时候传递的URL中附带上了用户名:CleverCode,密码是:123456。 ### 1.2存在的问题     1) GET请求:         可以看到,在传输用户名和密码的时候是用的GET请求方式。这种请求方式参数会附带在URL中。这样会带来潜在的安全隐患。建议使用POST请求。     2)传输的过程中密码是明文:         在传输的过程中,发现传输的密码是明文,这个很容易就把用户的密码给泄露了。建议将密码加密后在进行传输。 ### 1.3潜在的风险     在传输的过程中使用了GET请求,并且密码没有进行加密。每次浏览器进行请求的时候,有的浏览器设计公司会记录下每次请求的URL,然后在进行一些行为分析,如果是这样,那么我们的用户的密码将存在非常的大的隐患。很有可能别人窃取。从而进行一些对本公司不利的活动。 ## 2建议改进的登录验证方式 ### 2.1 设计方案介绍     在设计的过程中,可以使用POST请求,POST请求,在传输过程中参数不会被附加在URL上,这个可以防止它人进行用户行为分析的时候,分析出用户名和密码。在传输的过程中,可以将密码进行加密,然后进行密文传输。密文加密的时候可以使用哈希函数中的md5()算法。Md5()加密的后的密文不会有哈希冲突,是常见的加密方式之一。 ### 2.1.1 前台页面源代码 ~~~ <?php //页面加载时间 $loadtime = time(); ?> <script>     //页面js本机加载时间     var loadtime = parseInt(new Date().getTime()/1000);          // 登录信息检查     function onlogin(){         var personId = document.login_form.personId.value;         var passwd = document.login_form.passwd.value;         if (personId.length == 0 || passwd.length == 0){             alert("无效的用户名或口令");             return false;         }         var currtime = parseInt(new Date().getTime()/1000);                  //当前时间戳 = php的loadtime时间 + (js当前时间 - js的load时间)         var timestamp = <?php echo $loadtime;?> + currtime - loadtime;                  //将123456进行md5加密         var passwdMd5 = hex_md5(passwd);                  //将CleverCode与passwdMd5以及与当前时间加密         passwdMd5 = hex_md5(personId + passwdMd5 + timestamp);         document.login_form.timestamp.value = timestamp;         document.login_form.passwdMd5.value = passwdMd5;         // hide the password         document.login_form.passwd.value = "******";         return true;     }   </script> ~~~ ### 2.1.2 前台页面源代码说明     1)传给服务器的数据结构样例:         personId:CleverCode         passwdMd5: 202cb962ac59075b964b07152d234b70         timestamp:1393998153         passwd:          2)计算当前时间戳:         首先使用php把当前服务器的时间戳赋值给了$loadtime,在js环境加载的时候,首先记录的js的加载时间loadtime = parseInt(new Date().getTime()/1000),这个时间是客户端的时间,在点击完登录按钮之后,在记录var currtime = parseInt(new Date().getTime()/1000)时间,当前的currtime是用js求得的,得到的客户端浏览器所在机器的时间,每个人客户端的时间设置可能都不一样,使用var timestamp = <?php echo $loadtime;?> + currtime – loadtime,就可以得到现在真正的时间。我们只取了在客户端登录的时间差,然后加上服务器的起始时间,那么就可以到的当前时间。          3)密码加密:         首先对用户的密码进行md5加密var passwdMd5 = hex_md5(passwd);,js中md5的加密函数为hex_md5();然后在用户的Id与passwdMd5与时间戳一起md5加密passwdMd5 = hex_md5(personId + passwdMd5 + timestamp),得到真正要传输给服务器的md5密码。          4)迷惑密码:         document.login_form.passwd.value = "******",这部分为使用的迷惑密码,并没有多大的实际意义。 ### 2.2 后台验证设计 ### 2.2.1后台页面源代码 ~~~ function checkLogin($personId, $timestamp, $passwdMd5){ //检验时间戳 $lifeTime = 60; $nowTime = time(); //登录页面如果超过60s,则超时需要重新输入 if(($nowTime - $timestamp) >= $lifeTime){ return 'Error:invalid timestamp!'; } //检验用户是否存在 if(isUers($personId) === false){ return "Error:$personId not exitsts!"; } //获取用户的密码,该密码为用户密码明文的MD5值,保存在数据库中 $passwd = getPasswd($personId); //检验密码是否正确 $realPasswd = md5($personId.$passwd.$timestamp); if($realPasswd == $passwdMd5){ return 'OK' } return "Error:passwd not right!" } ~~~ ### 2.2.2后台页面源代码说明     首先获取服务器当前时间戳$nowTime,用当前的时间戳减去传入的时间戳$timestamp,如果大于等于60s说明,响应时间超时,必须放弃这条检测;然后判断当前的用户是否存在;接着从保存用户密码的地方获取用户的密码$passwd,用户在数据库中保存的密码必须是明文的md5值;然后$personId.$passwd.$timestamp进行md5加密得到真正的密码$realPasswd;最后对传入的密码$passwdMd5与$realPasswd进行比较 ### 2.3 设计方案的优点     1) 使用POST请求传输,避免了url在传输过程中被它人窃取用户名和密码。     2) 密码传输的方式不再是明文传输,而是使用密文传输,不容易被破解。     3) 在传输的时候,首先对用户密码的明文进行md5计算,然后在对passwdMd5 =hex_md5(personId + passwdMd5 + timestamp)。为什么不直接将明文md5后就直接传输的原因是,如果有人有足够强大的md5映射库。然后他只需要拿到你的密文之后再进行匹配,即采用枚举破解法,既可以知道了明文。使用的timestamp之后就很难暴力破解了。     4)加密过程中使用时间戳,增加了加密的复杂度,提高了安全性。 ### 2.4 设计方案的不足     登录的时候没有设计验证码类似的功能,登录的接口直接裸露出来,很容易被它人模拟http请求,大量请求这个接口,造成这个接口的崩溃。建议自行设计时候,当用户如果输入密码错误N次的时候,需要输入验证码。 **版权声明:** **1)**原创作品,出自"CleverCode的博客",转载时请务必注明以下**原创****地址**,否则追究版权法律责任。 **2)**原创地址**:[http://blog.csdn.net/clevercode/article/details/45481409](http://blog.csdn.net/clevercode/article/details/45481409)(**转载**务必注明该地址**)**。** **3)**欢迎大家关注我博客更多的精彩内容:[http://blog.csdn.net/CleverCode](http://blog.csdn.net/CleverCode)。
';

前言

最后更新于:2022-04-01 11:53:58

> 原文出处:[PHP程序员技术精粹](http://blog.csdn.net/column/details/phpjingcui.html) 作者:[clevercode](http://blog.csdn.net/clevercode) **本系列文章经作者授权在看云整理发布,未经作者允许,请勿转载!** # PHP程序员技术精粹 > php程序员设计项目中,一些经典的代码技巧,PHP优秀项目分析,以及一些设计规范。
';