PHP实现产品列表分类筛选与排序的项目实例

 更新时间:2016年11月25日 17:17  点击:1854
在电子商务网站中,产品列表页的分类筛选以及按照用户的意向选择排序几乎都有,比如淘宝。本文我们来分享用php实现这一功能的实例。

一、简单的单条件查询

工作都是从简单的开始,先从最简单的单表查询开始,这个一般用在首页以及一些比较独立的页面,只需要查找几个符合条件的产品展示出来即可,可以使用分页或者不使用分页。下面这个是产品控制器 ProductController 中的一个函数,用于简单的查询,比如199元专区就可以使用 getTypeSimPro('price=199');


/**简单的筛选条件分类产品,单表查询
 * @param string $sql 单表查询的SQL
 * @param int $countPerPage=16 每页商品数
 * @param string $orderBy='salseF DESC' 排序 默认销量阈值
 * @return array $res 产品二维数组
 */
function getTypeSimPro($sql, $countPerPage = 16, $orderBy='salesF DESC'){
    //$sql = "SELECT ProductId,name,mainPic,priceN,priceVIP,isNew,isHot,sales FROM product WHERE ".$sql;
    $productM = M('product');    // 实例化Data数据对象       
    $where = $sql ? 'onSale=1 AND '.$sql : 'onSale=1';

    $tempSQL = $productM->field('ProductId,name,mainPic,priceN,priceVIP,isNew,isHot,sales,salesF')
                         ->where( $where )                        
                        ->order( $orderBy );
                        
//    $res = $this->executeTempSQL($tempSQL, $countPerPage);
    $res = $tempSQL->select();    //演示不使用分页,直接返回结果集
    return $res;
}


二、使用分页

由于Thinkphp的自带Page分页类有些不太好用,所以我进行了一点小改造,可以进行传递配置参数修改页码显示的方式。这里的主要实现逻辑是:

1、利用同一个临时数据库对象 $tempSQL ,使计数和查询结果的条件保持一致,注意这里使用了对象克隆,因为TP中,一个Model执行完操作后会被初始化成原始的Model对象,参见 TP手册连贯操作说明>> 。

2、$_GET['p']是Page类默认的辨别当前页码的参数。Page类尤其里面的 show() 函数是经过我改造的,可以传递定制化页码导航栏参数。不定制也可以,就是页码导航有点太长。

3、这里的 count() 在后面多表查询的时候是有BUG的,后面再说。


/**
 * 执行分类和搜索中的SQL对象
 * @param TP.Model $tempSQL Thinkphp的Model对象
 * @param int $countPerPage=16 每页的产品数
 * @return array $res['nowP']当前页数  $res['totalP']总产品数  $res['links']分页栏HTML     $res['productList']产品二维数组
 * */
protected function executeTempSQL( $tempSQL, $countPerPage=16 ){
    $tempSQL2 = clone $tempSQL;        //对象复制,否则调用一次后第二次会被初始化成原始的M对象
//    print_r($tempSQL);
    $count = $tempSQL->count(); // 查询满足要求的总记录数,这里在多表查询时一定要以产品编号为限制条件
//  var_dump($count);
//    var_dump($tempSQL);

    $nowPage = isset($_GET['p'])?$_GET['p']:1;  //当前页        
    import('ORG.Util.Page');// 导入分页类
    $Page = new \Think\Page($count,$countPerPage);        // 实例化分页类 传入总记录数,每页数
    $list = $tempSQL2->page($nowPage.','.$Page->listRows)->select();        //查询结果集        
//    var_dump($list);
        
    //分页导航的定制
    $showConfig = array(
        'first'  => '首页',
        'prev'   => '上一页',
        'next'   => '下一页',    
//        'last'   => '尾页',    //这个不行
        'rollPage' => 5,        //最多显示5页导航
    );
    $links = $Page->show( $showConfig );            // 分页显示输出
    //var_dump($links);
    //var_dump($list);
    $res['nowP'] = $nowPage;
    $res['totalP'] = $count;
    $res['links'] = $links;            //分页输出
    $res['productList'] = $list;    //数据集
    return $res;
}


三、多表查询功能概览

先来一张截图,要达到的筛选功能大概是这个样子的。


其中的数据库设计为:

product表:ProductId-产品ID、name-产品名、sort1-一级分类、sort2-品牌分类、price-价格、onSale-上下架……等等
reserve表:ProductId-产品ID、color-颜色、size-尺码、reserve-库存
tagpro表:Id-自增没实际用途、tagId-标签ID、ProductId-产品ID
tag表:Id-标签ID、tag_name-标签名

标签与产品是多对多的关系。在上面展示的分类和搜索中,黑色导航栏、性别以及以后可能扩展的筛选项为标签联表查询,尺码为库存表联表查询。

四、SearchController控制器

定义了一个Search控制器,里面有下面几个方法:

function index() 方法是根据上面页面中的筛选选项拼装相应的SQL语句的,提交到ProductController去筛选出相关的产品;

function getCutURL($getKey, $CtrlName=CONTROLLER_NAME) 是为了给页面生成一系列切除了指定get值的URL地址的;

function pageCheck() 如果改变了筛选条件,则去除页码参数,回到从第一页开始;

在我的项目规划中IndexController负责页面的显示,所以IndexController中的 search() 方法则负责搜索页面的展示,代码如下


    function search(){        
        $searchC = A('search');
        $res = $searchC->index();
        $URLArr['type2URL'] = $searchC->getCutURL('type2');            //取消选择分类的URL
        $URLArr['brandURL'] = $searchC->getCutURL('brand');            //取消选择品牌的URL
        $URLArr['peopleURL'] = $searchC->getCutURL('people');        //取消选择性别人群的URL
        $URLArr['sizeURL'] = $searchC->getCutURL('size');            //取消选择尺码的URL
        $URLArr['priceURL'] = $searchC->getCutURL('price');            //取消选择价格区间的URL        
        $URLArr['keyword'] = $searchC->getCutURL('keyword');        //取消搜索关键字的URL    
        $URLArr['orderbyURL'] = $searchC->getCutURL('orderby');        //orderby按钮的URL前部分
        
        $this->assign('URLArr', $URLArr)        
            ->assign ( 'productArr', $res['productList'] )
            ->assign( 'totalNum', $res['totalP'] )
            ->assign( 'pageinfo', $res['links'])
            ->display();
    }


五、两表多次查询

因为产品与标签是多对多的关系,所以有一种需求是:查询同时拥有两个标签一个产品,姑且设读取列为*即全部列。

一开始想到的SQL语句是这个样子的

SELECT * FROM product p INNER JOIN tagpro ON tagpro.ProductId = p.ProductId INNER JOIN tagpro ON tagpro.ProductId = p.ProductId WHERE onSale=1 AND tagpro.tagId=46 AND tagpro.tagId=40;

然而这条语句并没有执行成功,而是报错 Not unique table/alias: 'tagpro' ,意思是说两次INNER JOIN的表是同一个表/表别名,所以不行。所以我就试着把一个INNER JOIN删掉,然后再看是可以执行了,但是却是没有查到任何结果。到这里,我差点就要骂SQL不够智能了,明明是该产品在tagpro表中有tagId等于46也有tagId等于40,为什么你要理解成了 tagId同时等于46和40呢?

SELECT * FROM product p INNER JOIN tagpro ON tagpro.ProductId = p.ProductId WHERE onSale=1 AND tagpro.tagId=46 AND tagpro.tagId=40;

骂也没用,只能再想办法,相信这种事情很多人都遇到,SQL肯定有办法解决这种问题,后来尝试了一下这种写法,把同一个表分别用了两个不同的别名,然后终于能查出来了。 ???

SELECT * FROM product p INNER JOIN tagpro a ON a.ProductId = p.ProductId INNER JOIN tagpro b ON b.ProductId = p.ProductId WHERE onSale=1 AND a.tagId=46 AND b.tagId=40;

六、产品控制器中的SQL查询函数

前面说了,Search控制器中的index()方法负责拼接SQL语句,提交到 Product控制器中进行产品的查询,现在在Product控制器中新建一个 getSearchPro() 方法,参考原来简单查询中的做法,另外加入JOIN的处理。这里其实就是把 where拼接起来, 把 join 拼接起来。原始的where和join的生成在Search控制器的index()中。


    /**根据筛选条件查找分类产品,多表查询     //默认每页16    //排序为销售阈值
     * @param string $sql 单表查询的SQL
     * @param int $countPerPage=16 每页商品数
     * @param string $orderBy='salseF DESC' 销量阈值
     * @param array $joinConfig=NULL 多表查询时
     *         $joinConfig['joinTable']为联合表名二维数组,每一列遍历为 $joinTableL
     *                 $joinTableL[name]为真实表名  $joinTableL[asname]为as表名
     *         $joinConfig['where']为附加查询条件as表名的字段=条件  $joinTableL[asname].size=$size;
     * @return array $res['nowP']当前页数  $res['totalP']总产品数  $res['links']分页栏HTML $res['productList']产品二维数组
     */    
    function getSearchPro($sql, $countPerPage = 16, $orderBy='salesF DESC', $joinConfig=NULL){        
        $productM = M('product')->alias('p');    // 实例化Data数据对象
        $where = $sql ? 'p.onSale=1 AND '.$sql : 'p.onSale=1';

        $joinTableArr = $joinConfig['joinTable'];    //要JOIN的表名
        $joinWhereArr = $joinConfig['where'];        //要筛选的格外条件
        foreach( $joinWhereArr as $joinLine ){
            $where .= ' AND '.$joinLine;
        }
        
        $tempSQL = $productM->distinct(true)
                        ->field('p.ProductId,p.name,p.mainPic,p.priceN,p.priceVIP,p.isNew,p.isHot,p.sales,p.salesF')
                        ->where( $where )
                        ->order( $orderBy );
        //处理JOIN
        foreach( $joinTableArr as $joinTableL){
            $tempSQL = $tempSQL->join("$joinTableL[name] AS $joinTableL[asname] ON $joinTableL[asname].ProductId = p.ProductId");
        }
    
        $res = $this->executeTempSQL($tempSQL, $countPerPage, 'p.ProductId');
        return $res;
    }    



注意最后的 $res = $this->executeTempSQL($tempSQL, $countPerPage, 'p.ProductId'); 最后比之前的函数多了一个参数,因为前文提到的 executeTempSQL()方法中的 $count = $tempSQL->count();  改为了  $count = $tempSQL->count('DISTINCT '.$countCond); 否则在多表查询时计数会出现count的数量比实际查到的结果条数多的情况。 这里的executeTempSQL()后面新增的参数为 $countCond,默认值为'ProductId',以便单表查询时不必填写这个无相紧要的参数。

七、Search控制器,筛选项转换成SQL拼接

index()函数:生成查询的SQL语句段。逻辑是:
1、根据 get 的参数,分别依次进行筛选/排序处理;
2、只在product表中产生where条件的,以一次查询加 简单where SQL拼接的方式处理;
3、多表联合并在其它表有 where条件的,以 join 数组的形式提交给产品控制器统一拼接处理;
4、这个是目前现行的方案,以后还要再优化的;


    //搜索入口
    function index( $defaultTag=NULL ){
        //如果改变了筛选条件,则去除页码参数
        $this->pageCheck();

        //********处理筛选**********************************
        $type2 = I('get.type2');        // type2: 篮球鞋、跑步鞋……
        $brand = I('get.brand');        // brand: 阿迪、匹克、李宁……
        $people = I('get.people');        // people: 男、女、中性……
        $size = I('get.size');            // size: 35~46、S~L……
        $price = I('get.price');        // price: 小于99、100~199……
        $tag = I('get.tag');            // tagsId: 限时、热销、性价比……
        $keyword = trim( I('get.keyword') );// keyword: 搜索关键字……
        $orderby = I('get.orderby');
        
        $joinTableNameIndex = 0;    //join所用的表替代名称后缀,为了两个表多次联合查询而设计所有的join表as tb0、tb1...
        //例如 SELECT * FROM product p INNER JOIN tagpro tb0 ON tb0.ProductId = p.ProductId INNER JOIN tagpro tb1 ON tb1.ProductId = p.ProductId WHERE onSale=1 AND tb0.tagId=46 AND tb1.tagId=40;
        
        $sql = '';
        //二级分类: /type2/篮球鞋
        if( $type2 ){
            $type2Id = M()->query("SELECT Id FROM sort WHERE sortName='$type2'");
            $type2Id = $type2Id[0]['Id'];
            $thesql = "p.sort_secType='$type2Id'";
            $sql = $sql ? $sql.' AND '.$thesql : $thesql;
        }
        
        //品牌筛选: /brand/李宁
        if( $brand ){
            $brandId = M()->query("SELECT Id FROM sort WHERE sortName='$brand'");
            $brandId = $brandId[0]['Id'];
            $thesql = "(p.sort_brand='$brandId' OR p.name LIKE '%$brand%')";    //这里除了品牌分类匹配外还根据商品名称模糊匹配
            $sql = $sql ? $sql.' AND '.$thesql : $thesql;
        }

        //性别: /people/男性 /people/女性 /people/中性
        if( $people ){
            //人群标签ID
            $tagsPeopleId = M('tags')->where("tag_name='$people'")->field('Id')->select();
            $tagsPeopleId = $tagsPeopleId[0]['Id'];
            
            $join['joinTable'][] = array('name'=>'tagpro', 'asname'=>'tb'.$joinTableNameIndex );
            $join['where'][] = 'tb'.$joinTableNameIndex.'.tagId='.$tagsPeopleId ;
            $joinTableNameIndex++;
//            $sql = $sql ? $sql.' AND '.$thesql : $thesql;
        }
        
        //营销标签(与人群标签一样处理逻辑)
        if( $tag ){
            $tagsId = M('tags')->where("tag_name='$tag'")->field('Id')->select();
            $tagsId = $tagsId[0]['Id'];
            $join['joinTable'][] = array('name'=>'tagpro', 'asname'=>'tb'.$joinTableNameIndex );
            $join['where'][] = 'tb'.$joinTableNameIndex.'.tagId='.$tagsId ;
            $joinTableNameIndex++;
//            $sql = $sql ? $sql.' AND '.$thesql : $thesql;            
        }
        
        //尺码筛选: /size/35
        if( $size ){
            $join['joinTable'][] = array('name'=>'reserve', 'asname'=>'tb'.$joinTableNameIndex );
            $join['where'][] = 'tb'.$joinTableNameIndex.'.size='.$size ;
            $joinTableNameIndex++;
//            $join['joinTable'][] = 'reserve';
//            $join['where'][] = 'reserve.size='.$size ;
//            $sql = $sql ? $sql.' AND '.$thesql : $thesql;
        }
        
        
        //价格筛选: /price/100~199 /price/大于1999  /price/小于99
        if( $price ){
//            var_dump( $price );
            if( preg_match('/~/', $price) ){                //    echo '介于';
                $priceArr = explode('~', $price);
                $thesql = "p.priceVIP BETWEEN '$priceArr[0]' AND '$priceArr[1]'";                
            }elseif( preg_match('/大于/', $price) ){            //    echo '大于';
                $priceArr = explode('大于', $price);
                $thesql = "p.priceVIP>'$priceArr[1]'";    
            }elseif( preg_match('/小于/', $price) ){            //    echo '小于';
                $priceArr = explode('小于', $price);
                $thesql = "p.priceVIP<'$priceArr[1]'";
            }            
            $sql = $sql ? $sql.' AND '.$thesql : $thesql;
        }
        
        //搜索关键字
        if( $keyword ){
            $thesql = "(p.ProductId LIKE '%$keyword%' OR p.name LIKE '%$keyword%')";
            $sql = $sql ? $sql.' AND '.$thesql : $thesql;
        }
        
        //排序
        if( $orderby ){
            if( $orderby == 'default'){
                $orderbySQL = NULL;
            }elseif( $orderby == 'priceVIPa' ){
                $orderbySQL = 'priceVIP ASC';
            }elseif( $orderby == 'priceVIPd' ){
                $orderbySQL = 'priceVIP DESC';
            }else{
                $orderbySQL = $orderby.' DESC';
            }
        }
        
    //*******************************************    
        $productC = A('DataProduct');
        $res = $productC->getSearchPro($sql,20,$orderbySQL,$join);
//    var_dump($res);
        return $res;
    }



Search 控制器下的另外两个函数如下:


/**
 * 如果改变了筛选条件,则去除页码参数,回到从第一页开始
  * 实现原理:如果存在p参数且不是最后一个参数时,则认为是修改了筛选条件
 * 这里有一点BUG,多项选择再翻页时、取消一个选项并不会回到第一页(因为p参数还是在最后)
 */
function pageCheck(){
//        var_dump( $_SERVER['HTTP_REFERER'] );
//        var_dump( $_GET );
    $getKeyArr = array_keys( $_GET ); //var_dump($getKeyArr);
    $pKeyIndex = array_search('p', $getKeyArr);
//        var_dump($pKeyIndex);
    $arrL = sizeof($getKeyArr); //var_dump($getKeyArr);
    
    if( $pKeyIndex!==FALSE && $pKeyIndex+1 < $arrL ){    //p参数如果不是在最后则为更改了筛选条件
        $cutPurl = $this->getCutURL('p');
        redirect( $cutPurl );
    }
    
}

/**
 * 获得切除了指定get值的URL
 * @param string $getKey 要去除的get键
 * @param string $CtrlName 控制器名,默认为页面URL中的控制器名
 * @return string 不含http://域名 的URL,可直接用于前端输出
 * */
function getCutURL($getKey, $CtrlName=CONTROLLER_NAME){
    $getStr ='';
    $getArr = I('get.');
    unset($getArr[$getKey]);
    foreach( $getArr as $getKey=>$getVal){
        $getStr .= '/'.$getKey.'/'.$getVal;
    }
//  var_dump($getStr);
    $thisURL = rtrim( U("$CtrlName/search"), 'html');
    $thisURL = rtrim($thisURL, '.');    //这两句合并起来会出问题,把seach中的h也去掉了
    return $thisURL.$getStr;        
}

智能回复是根据用户输入的条件来反馈结果用用户了,这个小编以前有做过信整理了一些例子供各位参考,比较完整主要是介绍在开发端了。

微信自推出后,着实火了一把,而支付功能的推出,又把微信推到了一个无可比拟的高度,然后申请微信订阅号或者服务号的人也开始比肩接踵。下面我将给大家简单讲解下微信公共平台开发接口。

先去 微信公共平台 申请账号,然后按照提示一步步。在选择订阅号和服务号上,个人只能申请订阅号,而且局限于基础功能;而企业两者都可以申请。订阅号和服务号的区别在于:订阅号可以每天群发一条消息,而服务号一个月才能群发一条;订阅号需要微信认证才能自定义菜单(企业才能认证,认证300元一次),而服务号则一开始就有自定义菜单,但是也可以认证,认证后服务号直接升级高级功能。更多差异请百度...

我申请的是订阅号,因为是个人。只要传一张手捧身份证的人头照就可以了,虽然有点傻。然后等待信息登记审核(一天左右时间)。通过后直接进入 微信公共平台 ,点击功能进入高级功能,关闭编辑模式,开启开发模式,然后下载微信提供的demo,解压,就一个文件:wx_sample.php,代码如下:

 代码如下 复制代码

<?php
/**
  * wechat php test
  */
 
//define your token
define("TOKEN", "weixin");
$wechatObj = new wechatCallbackapiTest();
$wechatObj->valid();
 
class wechatCallbackapiTest
{
 public function valid()
    {
        $echoStr = $_GET["echostr"];
 
        //valid signature , option
        if($this->checkSignature()){
         echo $echoStr;
         exit;
        }
    }
 
    public function responseMsg()
    {
  //get post data, May be due to the different environments
  $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
 
       //extract post data
  if (!empty($postStr)){
               
               $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
                $fromUsername = $postObj->FromUserName;
                $toUsername = $postObj->ToUserName;
                $keyword = trim($postObj->Content);
                $time = time();
                $textTpl = "<xml>
       <ToUserName><![CDATA[%s]]></ToUserName>
       <FromUserName><![CDATA[%s]]></FromUserName>
       <CreateTime>%s</CreateTime>
       <MsgType><![CDATA[%s]]></MsgType>
       <Content><![CDATA[%s]]></Content>
       <FuncFlag>0</FuncFlag>
       </xml>";            
    if(!empty( $keyword ))
                {
                $msgType = "text";
                 $contentStr = "Welcome to wechat world!";
                 $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
                 echo $resultStr;
                }else{
                 echo "Input something...";
                }
 
        }else {
         echo "";
         exit;
        }
    }
  
 private function checkSignature()
 {
        $signature = $_GET["signature"];
        $timestamp = $_GET["timestamp"];
        $nonce = $_GET["nonce"]; 
          
  $token = TOKEN;
  $tmpArr = array($token, $timestamp, $nonce);
  sort($tmpArr, SORT_STRING);
  $tmpStr = implode( $tmpArr );
  $tmpStr = sha1( $tmpStr );
  
  if( $tmpStr == $signature ){
   return true;
  }else{
   return false;
  }
 }
}
 
?>

其实就是认证,然后发送消息。将文件传到你的服务器上,我放在根目录下,然后修改开发模式下的url和token值。我的url就是http://www.111cn.net /wx_sample.php,token就是上面define的token,这个可以改的,只要两边保持一致,默认是weixin。然后点提交,就会提示你成功了。然后扫下你申请的号码,发个消息,你会发现没反应,这个时候我们需要小调整一下,关闭接口文档中调用认证的方法,开启调用处理回复信息的方法:

 代码如下 复制代码

//$wechatObj->valid();
$wechatObj->responseMsg();

这个时候你再发个消息,你就会收到:Welcome to wechat world!


是不是在关注了有些订阅号或者服务号之后,马上会收到一条消息。什么回复1,怎样怎样;回复2,怎样怎样之类的。

拿我自己的博客举例,我的关注语是:
    感谢您关注AndyYang个人博客微信小助手。
    回复【1】返回两篇最新文章
    回复【2】返回两篇人气文章
    回复【3】返回两篇热评文章
    回复【4】返回两篇最新技术文章
    回复【5】返回两篇最新写作文章
    回复其他返回搜索关键字的两篇文章
    更多精彩内容,尽在:www.111cn.net。亲们,请多多支持哦,谢谢~

那这个怎么实现呢?
直接上代码:

 代码如下 复制代码
<?php
/**
 * wechat php test
 */
 
//define your token
define("TOKEN", "weixin");
$wechatObj = new wechatCallbackapiTest();
//$wechatObj->valid();
$wechatObj->responseMsg();
 
class wechatCallbackapiTest
{
 
    public function valid()
    {
        $echoStr = $_GET["echostr"];
 
        //valid signature , option
        if($this->checkSignature()){
            echo $echoStr;
            exit;
        }
    }
 
    public function responseMsg()
    {
        //get post data, May be due to the different environments
        $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
 
        //extract post data
        if (!empty($postStr)){
            $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
            $fromUsername = $postObj->FromUserName;
            $toUsername = $postObj->ToUserName;
            $keyword = trim($postObj->Content);
            $time = time();
            $MsgType = $postObj->MsgType; //add
            $textTpl = "<xml>
                <ToUserName><![CDATA[%s]]></ToUserName>
                <FromUserName><![CDATA[%s]]></FromUserName>
                <CreateTime>%s</CreateTime>
                <MsgType><![CDATA[%s]]></MsgType>
                <Content><![CDATA[%s]]></Content>
                <FuncFlag>0</FuncFlag>
                </xml>";
 
            if($MsgType != 'event') {
                if(!empty( $keyword ))
                {
                    $msgType = "text";
                    $contentStr = "Welcome to wechat world!";
                }else{
                    echo "Input something...";
                }
            } else {
                $msgType = "text";
                $contentStr = "感谢您关注AndyYang个人博客微信小助手。\r\n".
                    "回复【1】返回两篇最新文章\r\n".
                    "回复【2】返回两篇人气文章\r\n".
                    "回复【3】返回两篇热评文章\r\n".
                    "回复【4】返回两篇最新技术文章\r\n".
                    "回复【5】返回两篇最新写作文章\r\n".
                    "回复其他返回搜索关键字的两篇文章\r\n".
                    "更多精彩内容,尽在:<a href='http://www.111cn.net'>www.111cn.net</a>。亲们,请多多支持哦,谢谢~";
                ;
            }
            $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
            echo $resultStr;
 
        }else {
            echo "";
            exit;
        }
    }
 
    private function checkSignature()
    {
        $signature = $_GET["signature"];
        $timestamp = $_GET["timestamp"];
        $nonce = $_GET["nonce"]; 
        $token = TOKEN;
        $tmpArr = array($token, $timestamp, $nonce);
        sort($tmpArr, SORT_STRING); //这个在新的sdk中添加了第二个参数(compare items as strings)
        $tmpStr = implode( $tmpArr );
        $tmpStr = sha1( $tmpStr );
        if( $tmpStr == $signature ){
            return true;
        }else{
            return false;
        }
    }
 
}

当然这里只是简单的实现下,在微信公共平台提供的sdk上做简单的修改,实际上msgtype类型很多,就算消息类型为event的,它里面也有subscribe、LOCATION等,而如果细化的话,就用Event为subscribe来处理初次关注的事件。

 代码如下 复制代码

<?php
define("TOKEN", "weixin");
$wechatObj = new wechatCallbackapiTest();
$wechatObj->weixin_run();
 
class wechatCallbackapiTest {
 
    private $fromUsername;
    private $toUsername;
    private $times;
    private $keyword;
    private $MsgType;
 
    public function responseMsg() {
  $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
  if (!empty($postStr)) {
            $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
            $this->fromUsername = $postObj->FromUserName;
            $this->toUsername   = $postObj->ToUserName;
            $this->keyword      = trim($postObj->Content);
            $this->time         = time();
            $this->MsgType      = $postObj->MsgType;
        } else {
         echo "Pay attention to <a href='http://{$_SERVER['HTTP_HOST']}'>http://{$_SERVER['HTTP_HOST']}</a>,thanks!";
         exit;
        }
    }
 
    public function weixin_run() {
        $this->responseMsg();
        if($this->MsgType != 'event') { //attention
            $data = $this->getData();
         $this->fun_xml("news", $data, count($data));
        } else {
            $data = $this->getWelData();
         $this->fun_xml("text", $data, 1);
        }
    }
 
 //type: text 文本类型, news 图文类型
 //text,array(内容),array(ID)
 //news,array(array(标题,介绍,图片,超链接),...小于10条),条数
 private function fun_xml($type, $value_arr, $count) {
     $con="<xml>
     <ToUserName><![CDATA[{$this->fromUsername}]]></ToUserName>
     <FromUserName><![CDATA[{$this->toUsername}]]></FromUserName>
     <CreateTime>{$this->times}</CreateTime>
     <MsgType><![CDATA[{$type}]]></MsgType>";
    
        switch($type) {
         case "text" :
      $con.="<Content><![CDATA[$value_arr]]></Content>";
          break;
      case "news" :
      $con.="<ArticleCount>{$count}</ArticleCount>
       <Articles>";
      foreach($value_arr as $key => $v) {
            $con.="<item>
             <Title><![CDATA[{$v[0]}]]></Title>
             <Description><![CDATA[{$v[1]}]]></Description>
             <PicUrl><![CDATA[{$v[2]}]]></PicUrl>
             <Url><![CDATA[{$v[3]}]]></Url>
             </item>";
      }
      $con.="</Articles>";
          break;
     }
     echo $con."</xml>";
 }
 
    private function getData() {
        //数据库通过关键字查询文章
 
        //。。。。。。。。。。。。
        //。。。。。。。。。。。。
 
        //返回文章结果的数组
        return $data;
    }
 
    private function getWelData() {
        $data = "感谢您关注AndyYang个人博客微信小助手。\r\n".
                    "回复【1】返回两篇最新文章\r\n".
                    "回复【2】返回两篇人气文章\r\n".
                    "回复【3】返回两篇热评文章\r\n".
                    "回复【4】返回两篇最新技术文章\r\n".
                    "回复【5】返回两篇最新写作文章\r\n".
                    "回复其他返回搜索关键字的两篇文章\r\n".
                    "更多精彩内容,尽在:<a href='http://m.111cn.net/'>www.111cn.net</a>。亲们,请多多支持哦,谢谢~";
                ;
        return $data;
    }
  
}

老实说很想弄个服务号玩玩,自定义菜单是没什么技术含量的,但是后面的微信支付之类,服务号仅有的服务,还是挺值得去尝试下的。。。

php在线压缩打包rar并自动下载文件是需要基于ZipArchive了, linux需开启zlib了,下面我们就一起来看看了,希望例子能够帮助到各位朋友。

linux需开启zlib。下面是具体的开启方法

 1. 下载安装zlib

http://www.gzip.org/zlib/

解压tar包,进入目录执行命令:

./configure && make && make install
 

2. 添加php扩展,命令如下:

[root@Ansen zlib]# pwd
/root/lnmp/php-5.3.0/ext/zlib #php源码包目录
[root@Ansen zlib]#mv config0.m4 config.m4
[root@Ansen zlib]#/usr/local/php5/bin/phpize
[root@Ansen zlib]#./configure --with-php-config=/usr/local/php/bin/php-config
[root@Ansen zlib]#make
[root@Ansen zlib]# cp modules/zlib.so /usr/local/php/lib/php/extension/   #php扩展目录
[root@Ansen zlib]#vi /usr/local/php5/lib/php.ini
添加extension=zlib.so

3.重启apache

php 在线压缩打包rar代码

<?php
$filename = "./zip/" . date ( 'YmdH' ) . ".zip"; // 生成的压缩包名称(含路径)
// 生成文件
$zip = new ZipArchive (); // linux需开启zlib,
if ($zip->open ( $filename, ZIPARCHIVE::CREATE ) !== TRUE) {
    exit ( '无法打开文件,或者文件创建失败' );
}
$fileNameArr = array('/upload/20140606/20140606011813-0.jpg','/upload/20140606/20140606013717-0.jpg');
foreach ( $fileNameArr as $val ) {
    $zip->addFile ( $val, basename ( $val ) ); // 第二个参数是放在压缩包中的文件名称,要检查一下文件是否已经有了
}
$zip->close (); // 关闭
 
//发送到浏览器提示下载;
header ( "Cache-Control: max-age=0" );
header ( "Content-Description: File Transfer" );
header ( 'Content-disposition: attachment; filename=' . basename ( $filename ) ); // 文件名
header ( "Content-Type: application/zip" ); // zip格式的
header ( "Content-Transfer-Encoding: binary" ); // 告诉浏览器,二进制文件
header ( 'Content-Length: ' . filesize ( $filename ) ); // 告诉浏览器,文件大小
readfile ( $filename );//输出文件;
?>

php循环执行多次效率执行速度问题估计很多的朋友们没正真的去理解了,下面我们来拿两个例子,希望文章能够帮助到各位有。


在设计的 php 生成静态页面中,静态页面越来越多,在不断的执行生成 html 的过程中,效率越来越慢,所以优化 php 语句就变得刻不容缓了,先检查了自己之前写的 php 生成 html 的程序,原来就是直接的循环,没有附加任何的优化,先看一下夏日博客之前的 PHP 循环语句:

$stat = time();  

  

for($i=1;$i<=10000;$i++){  

            $strTemp = $strTemp."<option value='".$i."'";  

  

            $strTemp=$strTemp.">第".$i."页</option>";  

        }  

$end = time();  

  

        echo $end-$stat;

这个循环只是把 php 生成静态语句给简化来了,主要了解一下循环生成的效率时间,具体的生成 html 这里就不具体讲解了,可以参看以前夏日博客的文章,这里是使用 php 执行循环 10000 次,如果变成 10000 00 之后呢,循环的速度会越来越慢,echo 出来的秒数也会越来越大,最后的解决方法就是将这段 PHP 循环语句进行简单的改造,如下:

$stat = time();  

$strTemp = '';  

for($i=1;$i<=10000;$i++){  

            $strTemp .= "<option value='".$i."'";  

            $strTemp .= ">第".$i."页</option>";  

        }  

$end = time();  

echo $end-$stat;

再次运行,不需要一秒的时间,时间效率非常的快,通过两段代码的对比可以看出,第一段是在每执行一次循环再重新赋值,要赋值 10000 次,速度肯定会慢,而第二段代码则是通过 点 的符号将值直接连接成一个字符串,只进行一次赋值就可以了,所以效率很高,我们可以把后面的代码放在任何执行多次的循环语句中,比如 php 生成多条 html 的时候。

 

 实现代码如下:

<?php
$Stime=0;
$Etime=0;
$Ttime=0;
$Stime=microtime(true);//获取程序开始执行的时间
//echo $Stime."<br/>";
for ($i=1;$i<=10000000;$i++){} //为了实现有一定的时间差,所以用了一个FOR来消耗一些资源.
$Etime=microtime(true);//获取程序执行结束的时间
//echo $Etime."<br/>";
$Ttime=$Etime-$Stime;//计算差值
//echo $Ttime."<br/>";
$str_total=var_export($Ttime,TRUE);
if(substr_count($str_total,"E")){ //为了避免1.28746032715E-005这种结果的出现,做了一下处理.
$float_total=floatval(substr($str_total,5));
$Ttime=$float_total/100000;
}
echo $Ttime.'秒';
?>


   百恒开发工程师在对相关函数进行介绍:
       microtime() 函数返回当前 Unix 时间戳和微秒数。
       var_export() 函数返回关于传递给该函数的变量的结构信息,它和 var_dump() 类似,不同的是其返回的表示是合法的 PHP 代码。
       substr_count() 函数计算子串在字符串中出现的次数。
   备注:南昌网站制作公司工程师提醒广大开发人员:$Stime=microtime(true);要放在页面的首要位置,$Etime=microtime(true);要放页面的未位置,否则计算不出时间.

二进制是机器一通常是以01代表的一个东西了,下文我们来看一个PHP二进制与字符串之间的相互转换函数,有兴趣的朋友对各位有帮助。
<?php
header("Content-type: text/html; charset=utf-8");    
/**
* 将字符串转换成二进制
* @param type $str
* @return type
*/
function StrToBin($str){
    //1.列出每个字符
    $arr = preg_split('/(?<!^)(?!$)/u', $str);
    //2.unpack字符
    foreach($arr as &$v){
        $temp = unpack('H*', $v);
        $v = base_convert($temp[1], 16, 2);
        unset($temp);
    }
    return join(' ',$arr);
}
/**
* 讲二进制转换成字符串
* @param type $str
* @return type
*/
function BinToStr($str){
    $arr = explode(' ', $str);
    foreach($arr as &$v){
        $v = pack("H".strlen(base_convert($v, 2, 16)), base_convert($v, 2, 16));
    }
    return join('', $arr);
}
echo StrToBin("php二次开发:www.111cn.net");;
echo '<br/>';
echo BinToStr("1110000 1101000 1110000 111001001011101010001100 111001101010110010100001 111001011011110010000000 111001011000111110010001 111011111011110010011010 1110111 1110111 1110111 101110 1110000 1101000 1110000 110010 101110 1100011 1100011");

[!--infotagslink--]

相关文章

  • js实现列表按字母排序

    这篇文章主要为大家详细介绍了js实现列表按字母排序,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-08-11
  • javaScript年份下拉列表框内容为当前年份及前后50年

    javascript下拉列表框,内容为当前年份及前后50年,默认选择为当前年份 复制代码 代码如下: <script language="javascript" type="text/javascript"> window.onload=function(){ //设置年份的选择 var myDate= new Date(...2014-05-31
  • Python 列表(List)的底层实现原理分析

    这篇文章主要介绍了Python 列表(List)的底层实现原理分析,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-03-09
  • easyUI下拉列表点击事件使用方法

    这篇文章主要为大家详细介绍了easyUI下拉列表点击事件的使用方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2017-05-22
  • 利用Node.js获取项目根目录的小技巧

    这篇文章介绍的是一个小技巧来获取node.js项目根目录,这个技巧非常实用。有需要的朋友们可以参考借鉴,下面来一起看看吧。...2016-10-02
  • JavaScript实现网页下拉列表的省市联动

    这篇文章主要为大家详细介绍了JavaScript实现网页下拉列表的省市联动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-07
  • 基于Pycharm加载多个项目过程图解

    这篇文章主要介绍了基于Pycharm加载多个项目过程图解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-04-22
  • intellij idea如何将web项目打成war包的实现

    这篇文章主要介绍了intellij idea如何将web项目打成war包的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-07-04
  • C# 列表List的常用属性和方法介绍

    这篇文章主要介绍了C# 列表List的常用属性和方法介绍,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-04-12
  • Idea打包springboot项目没有.original文件解决方案

    这篇文章主要介绍了Idea打包springboot项目没有.original文件解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-07-26
  • Tomcat首次部署web项目流程图解

    这篇文章主要介绍了Tomcat首次部署web项目流程图解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-12-11
  • 微信小程序虚拟列表的实现示例

    大部分小程序都会有这样的需求,页面有长列表,需要下拉到底时请求后台数据,一直渲染数据,当数据列表长时,会发现明显的卡顿,页面白屏闪顿现象,那么如何实现小程序虚拟列表,感兴趣的可以了解一下...2021-07-16
  • AngularJS使用ngOption实现下拉列表的实例代码

    这篇文章主要介绍了AngularJS使用ngOption实现下拉列表的实例代码的相关资料,需要的朋友可以参考下...2016-01-25
  • react+ts实现简单jira项目的最佳实践记录

    这篇文章主要介绍了react+ts实现简单jira项目,本文通过图文实例相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-07-30
  • Vue项目中如何运用vuex的实战记录

    如果说是JQuery是手工作坊,那么Vue.js就像是一座工厂,虽然Vue.js做的任何事情JQuery都可以做,但无论是代码量还是流程规范性都是前者较优,下面这篇文章主要给大家介绍了关于Vue项目中如何运用vuex的相关资料,需要的朋友可以参考下...2021-09-29
  • php有序列表或数组中删除指定的值的实现代码

    这篇文章主要介绍了php有序列表或数组中删除指定的值的实现代码,删除给定的值之后,得到一个新的有序列表,长度-1,下面是具体的实现方法...2021-08-22
  • Angularjs---项目搭建图文教程

    下面小编就为大家带来一篇Angularjs---项目搭建图文教程。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2016-07-25
  • Perl中的列表和数组学习笔记

    这篇文章主要介绍了Perl中的列表和数组学习笔记,本文讲解了列表、数组--列表的存贮、数组的存取、字符串中的方括号和变量替换、列表范围、数组的输出等内容,需要的朋友可以参考下...2020-06-29
  • js动态添加带圆圈序号列表的实例代码

    这篇文章主要介绍了js动态添加带圆圈序号列表的实例代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-02-18
  • idea导入项目框架的详细操作方法

    大家使用idea开发工具时经常会需要导入项目框架,纠结该怎么操作呢,今天小编给大家分享一篇图文教程,帮助大家解决idea导入项目框架的问题,感兴趣的朋友一起看看吧...2021-05-11