龚哥哥 爱生活、做自己!
PHP使用localhost无法连接MySQL 127.0.0.1可以
发表于 2015-10-9 | 浏览(1707) | PHP

As we know,在UNIX/LINUX中,使用localhost进行连接默认会使用Unix socket,使用127.0.0.1会使用tcp socket.

sh-3.2# mysql -uroot -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 85
Server version: 5.6.21 MySQL Community Server (GPL)
Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> status
--------------
mysql  Ver 14.14 Distrib 5.6.21, for osx10.8 (x86_64) using  EditLine wrapper
Connection id:	85
Current database:
Current user:	root@localhost
SSL:	Not in use
Current pager:	stdout
Using outfile:	''
Using delimiter:	;
Server version:	5.6.21 MySQL Community Server (GPL)
Protocol version:	10
Connection:	Localhost via UNIX socket
Server characterset:	utf8
Db     characterset:	utf8
Client characterset:	utf8
Conn.  characterset:	utf8
UNIX socket:	/tmp/mysql.sock
Uptime:	4 min 22 sec
Threads: 4  Questions: 178  Slow queries: 0  Opens: 118  Flush tables: 1  Open tables: 111  Queries per second avg: 0.679
--------------
mysql> 

编辑php.ini文件(常见位置 /etc/php.ini)
    mysql.default_socket=/tmp/mysql.sock
    pdo_mysql.default_socket=/tmp/mysql.sock
保存重启PHP即可

阅读全文

PHP7革新与性能优化
发表于 2015-10-4 | 浏览(1014) | PHP
2015年的PHP技术峰会(PHPCON),鸟哥(惠新宸)的关于PHP7的新特性和性能优化的分享,一切都令人感到激动。鸟哥是国内最权威的PHP专家,他的分享有很多非常有价值的东西,我通过整理分享的PPT和收集相关资料,整理为这篇解读性质的技术文章,希望能给做PHP开发的同学一些帮助。
 
PHP已经走过了20年的历史,直到今天,PHP7都发布了RC版,据说,PHP7正式版应该会在2015年11月份左右发布。PHP7对于上一个系列的PHP5.*,可以说是一个大规模的革新,尤其是在性能方面实现跨越式的大幅提升。

PHP是一种在全球范围内被广泛使用的Web开发语言,PHP7的革新也当然会给这些Web服务带来更深刻的变化。这里引用鸟哥PPT中的一个图表(82%的Web站点有使用PHP作为开发语言):

1.png

(注:一个web站点可以会使用多种语言作为它的开发语言)
(注:本文含有不少从鸟哥PPT里的截图,图片版权归鸟哥所有)
 
我们先看看两张激动人心的性能测试结果图:
Benchmark对比(图片来自于PPT):


2.png

PHP7的性能测试结果,性能压测结果,耗时从2.991下降到1.186,大幅度下降60%。
WordPress的QPS压测(图片来自于PPT):
3.png


而在WordPress项目中,PHP7对比PHP5.6,QPS提升2.77倍。
看完令人激动的性能测试结果对比,我们就进入正题哈。PHP7的新增特性很多,不过,我们会更聚焦于那些主要的变化。
 
一、新增特性和改变
1. 标量类型和返回类型声明(Scalar Type Declarations & Scalar Type Declarations)
PHP语言一个非常重要的特点就是“弱类型”,它让PHP的程序变得非常容易编写,新手接触PHP能够快速上手,不过,它也伴随着一些争议。支持变量类型的定义,可以说是革新性质的变化,PHP开始以可选的方式支持类型定义。除此之外,还引入了一个开关指令declare(strict_type=1);,当这个指令一旦开启,将会强制当前文件下的程序遵循严格的函数传参类型和返回类型。
例如一个add函数加上类型定义,可以写成这样:
4.png


如果配合强制类型开关指令,则可以变为这样:

5.png

如果不开启strict_type,PHP将会尝试帮你转换成要求的类型,而开启之后,会改变PHP就不再做类型转换,类型不匹配就会抛出错误。对于喜欢“强类型”语言的同学来说,这是一大福音。
更为详细的介绍:
PHP7标量类型声明RFC[翻译]
 
2. 更多的Error变为可捕获的Exception
PHP7实现了一个全局的throwable接口,原来的Exception和部分Error都实现了这个接口(interface), 以接口的方式定义了异常的继承结构。于是,PHP7中更多的Error变为可捕获的Exception返回给开发者,如果不进行捕获则为Error,如果捕获就变为一个可在程序内处理的Exception。这些可被捕获的Error通常都是不会对程序造成致命伤害的Error,例如函数不存。PHP7进一步方便开发者处理,让开发者对程序的掌控能力更强。因为在默认情况下,Error会直接导致程序中断,而PHP7则提供捕获并且处理的能力,让程序继续执行下去,为程序员提供更灵活的选择。
例如,执行一个我们不确定是否存在的函数,PHP5兼容的做法是在函数被调用之前追加的判断function_exist,而PHP7则支持捕获Exception的处理方式。
如下图中的例子(截图来源于PPT内):

6.png

3. AST(Abstract Syntax Tree,抽象语法树)
AST在PHP编译过程作为一个中间件的角色,替换原来直接从解释器吐出opcode的方式,让解释器(parser)和编译器(compliler)解耦,可以减少一些Hack代码,同时,让实现更容易理解和可维护。
PHP5:
7.jpg

PHP7:

8.jpg

更多AST信息:
https://wiki.php.net/rfc/abstract_syntax_tree
 
4. Native TLS(Native Thread local storage,原生线程本地存储)
PHP在多线程模式下(例如,Web服务器Apache的woker和event模式,就是多线程),需要解决“线程安全”(TS,Thread Safe)的问题,因为线程是共享进程的内存空间的,所以每个线程本身需要通过某种方式,构建私有的空间来保存自己的私有数据,避免和其他线程相互污染。而PHP5采用的方式,就是维护一个全局大数组,为每一个线程分配一份独立的存储空间,线程通过各自拥有的key值来访问这个全局数据组。
而这个独有的key值在PHP5中需要传递给每一个需要用到全局变量的函数,PHP7认为这种传递的方式并不友好,并且存在一些问题。因而,尝试采用一个全局的线程特定变量来保存这个key值。
相关的Native TLS问题:
https://wiki.php.net/rfc/native-tls
 
5. 其他新特性
PHP7新特性和变化不少,我们这里并不全部展开来细说哈。
(1) Int64支持,统一不同平台下的整型长度,字符串和文件上传都支持大于2GB。
(2) 统一变量语法(Uniform variable syntax)。
(3) foreach表现行为一致(Consistently foreach behaviors)
(4) 新的操作符 <=>, ??
(5) Unicode字符格式支持(\u{xxxxx})
(6) 匿名类支持(Anonymous Class)
… …
 
二、跨越式的性能突破:全速前进
1. JIT与性能
Just In Time(即时编译)是一种软件优化技术,指在运行时才会去编译字节码为机器码。从直觉出发,我们都很容易认为,机器码是计算机能够直接识别和执行的,比起Zend读取opcode逐条执行效率会更高。其中,HHVM(HipHop Virtual Machine,HHVM是一个Facebook开源的PHP虚拟机)就采用JIT,让他们的PHP性能测试提升了一个数量级,放出一个令人震惊的测试结果,也让我们直观地认为JIT是一项点石成金的强大技术。
而实际上,在2013年的时候,鸟哥和Dmitry(PHP语言内核开发者之一)就曾经在PHP5.5的版本上做过一个JIT的尝试(并没有发布)。PHP5.5的原来的执行流程,是将PHP代码通过词法和语法分析,编译成opcode字节码(格式和汇编有点像),然后,Zend引擎读取这些opcode指令,逐条解析执行。
9.png

而他们在opcode环节后引入了类型推断(TypeInf),然后通过JIT生成ByteCodes,然后再执行。

10.png

于是,在benchmark(测试程序)中得到令人兴奋的结果,实现JIT后性能比PHP5.5提升了8倍。然而,当他们把这个优化放入到实际的项目WordPress(一个开源博客项目)中,却几乎看不见性能的提升,得到了一个令人费解的测试结果。
于是,他们使用Linux下的profile类型工具,对程序执行进行CPU耗时占用分析。
执行100次WordPress的CPU消耗的分布(截图来自PPT):
11.png

注解:
21%CPU时间花费在内存管理。
12%CPU时间花费在hash table操作,主要是PHP数组的增删改查。
30%CPU时间花费在内置函数,例如strlen。
25%CPU时间花费在VM(Zend引擎)。
 
经过分析之后,得到了两个结论:
(1)JIT生成的ByteCodes如果太大,会引起CPU缓存命中率下降(CPU Cache Miss)
在PHP5.5的代码里,因为并没有明显类型定义,只能靠类型推断。尽可能将可以推断出来的变量类型,定义出来,然后,结合类型推断,将非该类型的分支代码去掉,生成直接可执行的机器码。然而,类型推断不能推断出全部类型,在WordPress中,能够推断出来的类型信息只有不到30%,能够减少的分支代码有限。导致JIT以后,直接生成机器码,生成的ByteCodes太大,最终引起CPU缓存命中大幅度下降(CPU Cache Miss)。
CPU缓存命中是指,CPU在读取并执行指令的过程中,如果需要的数据在CPU一级缓存(L1)中读取不到,就不得不往下继续寻找,一直到二级缓存(L2)和三级缓存(L3),最终会尝试到内存区域里寻找所需要的指令数据,而内存和CPU缓存之间的读取耗时差距可以达到100倍级别。所以,ByteCodes如果过大,执行指令数量过多,导致多级缓存无法容纳如此之多的数据,部分指令将不得不被存放到内存区域。
12.png

CPU的各级缓存的大小也是有限的,下图是Intel i7 920的配置信息:

13.png

因此,CPU缓存命中率下降会带来严重的耗时增加,另一方面,JIT带来的性能提升,也被它所抵消掉了。
 
通过JIT,可以降低VM的开销,同时,通过指令优化,可以间接降低内存管理的开发,因为可以减少内存分配的次数。然而,对于真实的WordPress项目来说,CPU耗时只有25%在VM上,主要的问题和瓶颈实际上并不在VM上。因此,JIT的优化计划,最后没有被列入该版本的PHP7特性中。不过,它很可能会在更后面的版本中实现,这点也非常值得我们期待哈。
 
(2)JIT性能的提升效果取决于项目的实际瓶颈
JIT在benchmark中有大幅度的提升,是因为代码量比较少,最终生成的ByteCodes也比较小,同时主要的开销是在VM中。而应用在WordPress实际项目中并没有明显的性能提升,原因WordPress的代码量要比benchmark大得多,虽然JIT降低了VM的开销,但是因为ByteCodes太大而又引起CPU缓存命中下降和额外的内存开销,最终变成没有提升。
不同类型的项目会有不同的CPU开销比例,也会得到不同的结果,脱离实际项目的性能测试,并不具有很好的代表性。
 
2. Zval的改变
PHP的各种类型的变量,其实,真正存储的载体就是Zval,它特点是海纳百川,有容乃大。从本质上看,它是C语言实现的一个结构体(struct)。对于写PHP的同学,可以将它粗略理解为是一个类似array数组的东西。
PHP5的Zval,内存占据24个字节(截图来自PPT):
14.png

PHP7的Zval,内存占据16个字节(截图来自PPT):

15.png

Zval从24个字节下降到16个字节,为什么会下降呢,这里需要补一点点的C语言基础,辅助不熟悉C的同学理解。struct和union(联合体)有点不同,Struct的每一个成员变量要各自占据一块独立的内存空间,而union里的成员变量是共用一块内存空间(也就是说修改其中一个成员变量,公有空间就被修改了,其他成员变量的记录也就没有了)。因此,虽然成员变量看起来多了不少,但是实际占据的内存空间却下降了。
 
除此之外,还有被明显改变的特性,部分简单类型不再使用引用。
Zval结构图(来源于PPT中):
16.png

图中Zval的由2个64bits(1字节=8bit,bit是“位”)组成,如果变量类型是long、bealoon这些长度不超过64bit的,则直接存储到value中,就没有下面的引用了。当变量类型是array、objec、string等超过64bit的,value存储的就是一个指针,指向真实的存储结构地址。
对于简单的变量类型来说,Zval的存储变得非常简单和高效。
不需要引用的类型:NULL、Boolean、Long、Double
需要引用的类型:String、Array、Object、Resource、Reference
 
3. 内部类型zend_string
Zend_string是实际存储字符串的结构体,实际的内容会存储在val(char,字符型)中,而val是一个char数组,长度为1(方便成员变量占位)。

17.png

结构体最后一个成员变量采用char数组,而不是使用char*,这里有一个小优化技巧,可以降低CPU的cache miss。
如果使用char数组,当malloc申请上述结构体内存,是申请在同一片区域的,通常是长度是sizeof(_zend_string) + 实际char存储空间。但是,如果使用char*,那个这个位置存储的只是一个指针,真实的存储又在另外一片独立的内存区域内。
使用char[1]和char*的内存分配对比:
18.png

从逻辑实现的角度来看,两者其实也没有多大区别,效果很类似。而实际上,当这些内存块被载入到CPU的中,就显得非常不一样。前者因为是连续分配在一起的同一块内存,在CPU读取时,通常都可以一同获得(因为会在同一级缓存中)。而后者,因为是两块内存的数据,CPU读取第一块内存的时候,很可能第二块内存数据不在同一级缓存中,使CPU不得不往L2(二级缓存)以下寻找,甚至到内存区域查到想要的第二块内存数据。这里就会引起CPU Cache Miss,而两者的耗时最高可以相差100倍。
另外,在字符串复制的时候,采用引用赋值,zend_string可以避免的内存拷贝。
 
6. PHP数组的变化(HashTable和Zend Array)
在编写PHP程序过程中,使用最频繁的类型莫过于数组,PHP5的数组采用HashTable实现。如果用比较粗略的概括方式来说,它算是一个支持双向链表的HashTable,不仅支持通过数组的key来做hash映射访问元素,也能通过foreach以访问双向链表的方式遍历数组元素。
PHP5的HashTable(截图来自于PPT):
19.png

这个图看起来很复杂,各种指针跳来跳去,当我们通过key值访问一个元素内容的时候,有时需要3次的指针跳跃才能找对需要的内容。而最重要的一点,就在于这些数组元素存储,都是分散在各个不同的内存区域的。同理可得,在CPU读取的时候,因为它们就很可能不在同一级缓存中,会导致CPU不得不到下级缓存甚至内存区域查找,也就是引起CPU缓存命中下降,进而增加更多的耗时。
PHP7的Zend Array(截图来源于PPT):
20.png

新版本的数组结构,非常简洁,让人眼前一亮。最大的特点是,整块的数组元素和hash映射表全部连接在一起,被分配在同一块内存内。如果是遍历一个整型的简单类型数组,效率会非常快,因为,数组元素(Bucket)本身是连续分配在同一块内存里,并且,数组元素的zval会把整型元素存储在内部,也不再有指针外链,全部数据都存储在当前内存区域内。当然,最重要的是,它能够避免CPU Cache Miss(CPU缓存命中率下降)。
Zend Array的变化:
(1) 数组的value默认为zval。
(2) HashTable的大小从72下降到56字节,减少22%。
(3) Buckets的大小从72下降到32字节,减少50%。
(4) 数组元素的Buckets的内存空间是一同分配的。
(5) 数组元素的key(Bucket.key)指向zend_string。
(6) 数组元素的value被嵌入到Bucket中。
(7) 降低CPU Cache Miss。
 
7. 函数调用机制(Function Calling Convention)
PHP7改进了函数的调用机制,通过优化参数传递的环节,减少了一些指令,提高执行效率。
PHP5的函数调用机制(截图来自于PPT):
21.jpg

图中,在vm栈中的指令send_val和recv参数的指令是相同,PHP7通过减少这两条重复,来达到对函数调用机制的底层优化。
PHP7的函数调用机制(截图来自于PPT):
22.png

8. 通过宏定义和内联函数(inline),让编译器提前完成部分工作
C语言的宏定义会被在预处理阶段(编译阶段)执行,提前将部分工作完成,无需在程序运行时分配内存,能够实现类似函数的功能,却没有函数调用的压栈、弹栈开销,效率会比较高。内联函数也类似,在预处理阶段,将程序中的函数替换为函数体,真实运行的程序执行到这里,就不会产生函数调用的开销。
PHP7在这方面做了不少的优化,将不少需要在运行阶段要执行的工作,放到了编译阶段。例如参数类型的判断(Parameters Parsing),因为这里涉及的都是固定的字符常量,因此,可以放到到编译阶段来完成,进而提升后续的执行效率。
例如下图中处理传递参数类型的方式,从左边的写法,优化为右边宏的写法。
23.jpg

三、小结
鸟哥的PPT里放出过一组对比数据,就是WordPress在PHP5.6执行100次会产生70亿次的CPU指令执行数目,而在PHP7中只需要25亿次,减少64.2%,这是一个令人震撼的数据。
在鸟哥的整个分享中,给我最深刻的一个观点是:要注意细节,很多个细小的优化,一点点持续地积累,积少成多,最终汇聚为惊艳的成果。为山九仞,岂一日之功,我想大概也是这个道理。
毫无疑问,PHP7在性能方面实现跨越式的提升,如果能够将这些成果应用在PHP的Web系统中,也许我们只需要更少的机器,就可以支撑起更高请求量的服务。PHP7正式版的发布,令人充满无限憧憬。
 
参考&引用资料:
鸟哥(惠新宸)的分享PPT,http://www.laruence.com/
PHP官方社区,http://php.net/

PDF:PHP7-2015.pdf

阅读全文

PHP标准工厂模式
发表于 2015-9-24 | 浏览(970) | PHP
<?php
 
//定义接口
interface database
{
    public function connect($param);  //定义接口方法
    public function query($sql);  //定义接口方法
    public function fetch($query);  //定义接口方法
}
 
//接口继承
class mysql implements database
{
    //集成上面 connect 的接口
    public function connect($param)
    {
        echo 'mysql::connect('.$param.')';
    }
     
    //集成上面 query 的接口
    public function query($sql)
    {
        echo 'mysql::query('.$sql.')';
    }
     
    //集成上面 fetch 的接口
    public function fetch($query)
    {
        echo 'mysql::fetch('.$query.')';
    }
}
 
//接口继承
class sqlserver implements database
{
    public function connect($param)
    {
        echo 'sqlserver::connect('.$param.')';
    }
     
    public function query($sql)
    {
        echo 'sqlserver::query('.$sql.')';
    }
     
    public function fetch($query)
    {
        echo 'sqlserver::fetch('.$query.')';
    }
}
 
//接口继承
class oracle implements database
{
    public function connect($param)
    {
        echo 'oracle::connect('.$param.')';
    }
     
    public function query($sql)
    {
        echo 'oracle::query('.$sql.')';
    }
     
    public function fetch($query)
    {
        echo 'oracle::fetch('.$query.')';
    }
}
 
class dbfactory
{
    private $objects = array();
    private $classes = array();
 
    public function __construct()
    {
        $this->classes = array(
                'mysql',
                'sqlserver',
                'oracle'
            );
        
        for($i=0; $i<count($this->classes); $i++)
       {
            if(in_array('database', class_implements($this->classes[$i], false)))
           {
                $this->objects[$this->classes[$i]] = new $this->classes[$i];
            } else {
                unset($this->classes[$i]);
            }
        }
    }
 
    public function query($sql, $type = 'mysql')
    {
        // empty 检测变量对象 $this->objects 是否为空
        if(empty($this->objects))
        {
            exit('No class');
        }
        //检测 $this->classes 成员属性数组对象中是否存在 $type 传递过来的值,这里 ! 号 取反,有则 执行 if 外面
        if(!in_array($type, $this->classes))
        {
            exit('NO '.$type.' 没有继承接口');  //没有这个值的时候结束当前函数操作并退出
        }
 
        //下面这句话同等: $object = new mysql();   $object->query($sql);
        $this->objects[$type]->query($sql);  //实例化 类 及调用类里面的方法
    }
}
 
$factory = new dbfactory();
$factory->query('SELECT * FROM member', 'mysql');
echo '<hr/>';
$factory->query('SELECT * FROM member', 'sqlserver');
echo '<hr/>';
$factory->query('SELECT * FROM member', 'oracle');

?>

阅读全文

CURL错误77 使用了SSL证书
发表于 2015-9-10 | 浏览(1245) | PHP

解决PHP curl https时error 77(Problem with reading the SSL CA cert (path? access rights?))

服务器环境为CentOS,php-fpm,使用curl一个https站时失败,打开curl_error,捕获错误:Problem with reading the SSL CA cert (path? access rights?)

解决方案:

1. sudo yum install ca-certificates (无论有没有,安装确认一下)

2. 重启php-fpm,这步比较重要,更改底层的东西时一定要重启一下

3. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);  //关闭curl使用证书

阅读全文

ThinkPHP分页驱动 仿百度
发表于 2015-9-1 | 浏览(695) | PHP

52c6db6d3b1b0.jpg

<?php
 class PagingModel
 {
    private $m_PagingDataArray;  //接收页面提交的post或者get的一维数组条件
    private $m_Configuration;  //配置项数据
    private $m_Fraction;  //每个页面显示的条数
    private $m_Total;  //数据的总条数
    private $m_Page;  //页面传递过来的页码值
    private $m_Starting;  //查询数据的起始值
    private $m_TotalFraction;  //计算出来的总页数
    private $m_Url;  //分页使用的url地址
    private $m_PageCoent;  //是否开启页面数字分页按钮
    
    /*
        构造方法
    */
    public function __construct($PagingDataArray = array(), $Configuration = array())
    {
        /* 初始化属性数据 */
        $this->m_PagingDataArray = array();
        $this->m_Configuration = array();
        
        /* 基础数据设置 */
        $this->SetPagingDataArray($PagingDataArray);
        $this->SetConfiguration($Configuration );
        $this->SetBasisData();
    }
    
    /*
        设置数据
    */
    private function SetPagingDataArray($PagingDataArray)
    {
        /* 判断配置项的数据是否为空 */
        if(false == empty($PagingDataArray)) {
            $this->m_PagingDataArray = $PagingDataArray;
        } else {
            $this->m_PagingDataArray = array();
        }
    }
    
    /*
        设置配置项数据
    */
    private function SetConfiguration($Configuration)
    {
        /* 判断配置项的数据是否为空 */
        if(false == empty($Configuration)) {
            $this->m_Configuration = $Configuration;
        } else {
            $this->m_Configuration = array();
        }
    }
    
    
    /*
        处理判断数组中是否存在某个键名
    */
    private function Setuppase($Property, $Key, $Content)
    {
        /* 判断 $Key 是否在数组中存在的键名 */
        if(true == array_key_exists($Key, $this->m_Configuration)) {
            $this->$Property = $this->m_Configuration["$Key"];
        } else {
            $this->$Property = $Content;
        }
    }
    
    /*
        基础数据设置
    */
    private function SetBasisData()
    {
        $this->SetFraction();
        $this->SetTotal();
        $this->SetPage();
        $this->SetStarting();
        $this->SetTotalFraction();
        $this->SetUrl();
        $this->SetPageCoent();
    }
    /*
        设置每页显示数据的条数
    */
    private function SetFraction()
    {
        $this->Setuppase('m_Fraction', 'traction', 15);
    }
    
    /*
        设置数据的总条数
    */
    private function SetTotal()
    {
        $this->Setuppase('m_Total', 'total', 0);
    }
    
    /*
        设置页面传递过来的页码值
    */
    private function SetPage()
    {
        /* 判断 $Key 是否在数组中存在的键名 */
        if(true == array_key_exists('page', $this->m_PagingDataArray)) {
            $this->m_Page = max(1, (false == empty($this->m_PagingDataArray['page']) ? intval($this->m_PagingDataArray['page']) : 0));
        } else {
            $this->m_Page = 1;
        }
    }
    
    /*
        设置查询数据的起始值
    */
    private function SetStarting()
    {
        $this->m_Starting = ($this->m_Page - 1) * $this->m_Fraction;
    }
    
    /*
        设置计算出来的总页数, 总页数 = 总条数除以每页显示的条数。
    */
    private function SetTotalFraction()
    {
        $this->m_TotalFraction = ceil($this->m_Total/$this->m_Fraction);
        
        /* 当前页数大于最大页数时,将总页数的值赋值给当前页面,防止超越操作。*/
        if($this->m_TotalFraction <= $this->m_Page) {
            $this->m_Page = $this->m_TotalFraction;
        }
    }
    
    /*
        设置分页的url地址
    */
    private function SetUrl()
    {
        $this->Setuppase('m_Url', 'url', null);
    }
    
    /*
        设置是否开启显示数字分页按钮
    */
    private function SetPageCoent()
    {
        $this->Setuppase('m_PageCoent', 'pagecoent', 0);
    }
    
    /*
    获取查询数据的起始值
    */
    public function GetStarting()
    {
        return $this->m_Starting;
    }
    
    /*
        获取每页显示的条数值
    */
    public function GetFraction()
    {
        return $this->m_Fraction;
    }
    
    /*
        获取拼接的每页显示的数字页码
    */
    private function GetPageCoent($PageUrl)
    {
        /* 如果page值不等于1的时候 */
        if($this->m_Page != 1) {
            /* 如果分页数值加显示的分页个数值大于当前总页码数的时候 */
            if(($this->m_Page+$this->m_PageCoent) > $this->m_TotalFraction) {
                /* 计算起始值 */
                $Pageis = $this->m_Page-$this->m_PageCoent;
                /* 计算最大数值 */
                $PageMax = $this->m_TotalFraction;
                
            /* 如果分页数值加显示的分页个数值不大于当前总页码数的时候 */
            } else {
                /* 计算起始值,如果当前page小于等于显示的页数时,就将起始设置为1,防止负数 */
                if($this->m_Page <= $this->m_PageCoent) {
                    $Pageis = 1;
                } else {
                    $Pageis = $this->m_Page-$this->m_PageCoent;
                }
                
                
                /* 计算最大数值,当前page数值加需要显示的页码个数值 */
                $PageMax = (($this->m_Page+$this->m_PageCoent));
            }
            
            /* 如果显示页码值大于等于总页码值时,将起始值设置为1 */
            if($this->m_PageCoent >= $this->m_TotalFraction) {
                $Pageis = 1;
            }
            
        /* 如果page等于1的时候 */
        } else {
            /* 如果显示页码值大于等于总页码值时,就将总页码值赋值给循环的最大值 */
            if($this->m_PageCoent >= $this->m_TotalFraction) {
                $PageMax = $this->m_TotalFraction;
            } else {
                $PageMax = $this->m_PageCoent+1;
            }
            $Pageis = 1;
        }
        
        /* 循环拼接需要显示的分页数值个数代码 */
        $PageCoent = '<li>';
        for($Pagei=$Pageis; $Pagei<=$PageMax; $Pagei++) {
            if($this->m_Page == $Pagei) {
                $PageCoent .= '<span class="pageingpagecoent pageingpagecoents">'.$Pagei.'</span>';
            } else {
                $PageCoent .= '<a href="'.$PageUrl.$Pagei.'"><span class="pageingpagecoent">'.$Pagei.'</span></a>';
            }
            
        }
        /* 返回拼接好的代码 */
        return $PageCoent;
    }
    
    
    /*
        获取url拼接,处理URL拼接方法
    */
    private function GetUrlSplice()
    {
        $UrlSplice = '?';
        if(false == empty($this->m_PagingDataArray)) {
            //删除当前数组中的page数据
            unset($this->m_PagingDataArray['page']);
            foreach($this->m_PagingDataArray as $PKey=>$pValue) {
                /* 拼接普通url */
                if((false == empty($pValue)) && (false == is_array($pValue))) {
                    $UrlSplice .= $PKey.'='.$pValue.'&';
                }
                
                /* 拼接是数组的url */
                /*if((false == empty($pValue)) && (true == is_array($pValue))) {
                    
                }*/
            }
            //print_r($this->m_PagingDataArray);
        }
        return $UrlSplice;
    }
    
    
    /*
        返回拼接好的html代码(包括js代码)
    */
    public function GetPagingHtmlInfo()
    {
        $UrlSplice = $this->GetUrlSplice();
        
        $PageUrl = $this->m_Url.$UrlSplice.'page=';
        $PageUrls = $PageUrl.($this->m_Page-1);
        $PageUrly = $PageUrl.($this->m_Page+1);
        
        if($this->m_PageCoent > 0) {
            $PageCoent = $this->GetPageCoent($PageUrl);
        } else {
            $PageCoent = null;
        }
        
        /* 定义分页数据 */
        $Html = '<div id="pageingmodel"><ul id="pageing">';
        
        $Home = '<li><a href="'.$PageUrl.'1">首页</a></li>';
        $Previous = '<li><a href="'.$PageUrls.'">上一页</a></li>';
        $Next = '<li><a href="'.$PageUrly.'">下一页</a></li>';
        $End = '<li><a href="'.$PageUrl.$this->m_TotalFraction.'">尾页</a></li>';
        
        $HomeS = '<li class="pageban">首页</li>';
        $PreviousS = '<li class="pageban">上一页</li>';
        $NextS = '<li class="pageban">下一页</li>';
        $EndS = '<li class="pageban">尾页</li>';
        
        
        /* 当只有一页数据的时候,就没有拼接url地址 */
        if($this->m_TotalFraction == 1) {
            $Html .= $HomeS.$PreviousS.$PageCoent.$NextS.$EndS;
        /* 当没有数据的时候,就没有拼接url地址 */
        } elseif($this->m_Page == $this->m_TotalFraction && $this->m_Total == 0) {
            $Html .= $HomeS.$PreviousS.$PageCoent.$NextS.$EndS;
        /* 当为第一页的时候 */
        } elseif($this->m_Page == 1) {
            $Html .= $HomeS.$PreviousS.$PageCoent.$Next.$End;
        /* 到尾部的时候 */
        } elseif($this->m_Page == $this->m_TotalFraction  && $this->m_TotalFraction > 1) {
            $Html .= $Home.$Previous.$PageCoent.$NextS.$EndS;
        /* 正常的时候 */
        } else {
                $Html .= $Home.$Previous.$PageCoent.$Next.$End;
        }
        $Html .= '<li>当前第<span class="pagepublic">'.$this->m_Page.'</span>页</li><li>共<span class="pagepublic">'.$this->m_TotalFraction.'</span>页</li><li>总有<span class="pagepublic">'.$this->m_Total.'</span>条数据</li></ul></div>';
        /* css代码 */
        $Css = '<style type="text/css">
            #pageingmodel { width:100%; }
            #pageing li { float:left; margin-right:15px; }
            #pageing li a { color:#0D93BF; }
            #pageing li a:hover { color:#F00; }
            .pageban { color:#999; }
            .pageingpagecoent, .pagepublic { font-weight:bold; }
            .pageingpagecoent { padding:4px 8px; background:#C4C5C5; color:#FFF; margin:0px 5px; -moz-border-radius:4px; -webkit-border-radius:4px; border-radius:4px; font-size:13px; }
            .pageingpagecoents, .pageingpagecoent:hover { background:#099ACF; }
            
        </style>';
            
        return $Html.$Css;
    }
    
 }

 //使用方法
 /* 调用分页模块 */
$Configuration = array(
    'total' => $StudentCount,  //数据总条数
    'traction' => $StudentPage,  //每页显示条数
    'pagecoent' => 3,  //分页显示的个数
    'url' => './StudentManagement',  //翻页的url地址
);
       //$_REQUEST : 如果当前查询有其它条件将会自动选择拼接起来
      //$Configuration : 配置项
$PageingObj = new PagingModel($_REQUEST, $Configuration);
$this->assign('pageing', $PageingObj->GetPagingHtmlInfo());

阅读全文

ThinkPHP无限分类模块
发表于 2015-9-1 | 浏览(795) | PHP
交流QQ群:423804514 (如果加的时候请注明 ThinkPHP)
分享快乐!
无限分类,单分类,都支持,增删改查基于ThinkPHP操作的,如果大家是自己的数据库操作可以改改就可以了,本身模块是不应该把数据层写在模块里面的,但是我的项目 分类基本都是这些操作,增删改查,所以我直接写在里面了,不想重复的再去写增删改查的代码。

我这里顺便把两张表也上传了,表前缀改成您们自己的就可以了。test.php 这段代码,可以在您的项目 Lib 里面的某一个类的嘛的其中一个方法里面就可以直接使用。

Tree无限分类.zip

阅读全文

PHP文件生成模块
发表于 2015-9-1 | 浏览(561) | PHP
<?php
 /*
 *    描    述    :    把数据写入文件
 */
 namespace Model\Backend;
 class FilePutModel
 {
    /* 关闭自动连接数据库 */
    protected $autoCheckFields = false;
    private $m_DirName;  //目录地址
    private $m_FileName;  //文件名称
    private $m_Suffis;  //文件后缀名
    private $m_DataArray;  //需要写入文件的数据
    /*
     *    构造方法
     */
    public function __construct($DirName, $FileName, $DataArray, $Suffis = null)
    {
        $this->m_DirName = null;
        $this->m_FileName = null;
        $this->m_FileSuffix = null;
        $this->m_DataArray = array();
        /* 基础数据设置 */
        $this->m_DirName = $DirName;
        $this->m_FileName = $FileName;
        $this->m_FileSuffix = $Suffis;
        $this->m_DataArray = $DataArray;
        
        /* 基础数据处理 */
        $this->IsDirThere();
        $this->IsFileSuffix();
    }
    /*
     *    数据处理
     */
    public function SetFileData()
    {
        if(false == empty($this->m_FileName) && false == empty($this->m_DataArray)) {
            $Statu = file_put_contents(PATH_PHP_FILE.$this->m_FileName.'.'.$this->m_FileSuffix, "<?php\n\rreturn ".var_export($this->m_DataArray, true).";\n\r?>");
            if(false != $Statu) {
                return true;
            } else {
                return false;
            }
        } else {
            return '数据不能为空!';
        }
    }
    
    /*
     *    判断目录是否存在
     */
    private function IsDirThere()
    {
        if(false == is_dir($this->m_DirName)) {
            mkdir($this->m_DirName, 0777, true);
        }
    }
    
    /*
     *    判断后缀名是否存在
     */
    private function IsFileSuffix()
    {
        if(true == empty($this->m_FileSuffix)) {
            $this->m_FileSuffix = 'php';
        }
    }
}
 ?>
//使用说明
$FilePutObj = new \Model\Backend\FilePutModel('路径',文件名称,'数据','文件后缀名');
        if(true == $FilePutObj->SetFileData()) {
            //成功
        }

阅读全文

PHP中常见的字符编码设置,截取字符部分
发表于 2015-9-1 | 浏览(572) | PHP
echo mb_substr('你aa好啊反反复复', 0, 6, 'utf-8').'<br />';   //mb_substr— 获取字符串的部分
  你aa好啊反

echo mb_strlen('ww刚好刚').'<br />';
  11

echo $str = '我好啊';  // 一个字符 3 字节
  我好啊

echo '<br />';
echo $ff = mb_detect_encoding($str).'<br />';   //mb_detect_encoding 检测字符的编码
  UTF-8

$ret = mb_convert_encoding($str, 'GBK', 'UTF-8');   //mb_convert_encoding— 转换字符的编码
  echo mb_strlen($str).'<br />';   //mb_strlen— 获取字符串的长度
    9
  echo mb_strlen($ret).'<br />';
    6

$a = "翻盖手机,MOTO,you like it?";
  echo mb_substr($a,0,6,"UTF-8");
    翻盖手机,M

阅读全文

2015最新微信支付APP 服务端处理
发表于 2015-9-1 | 浏览(1049) | PHP
<?php

/**
 * 微信支付驱动
 * @author  Devil
 * @version v_1.0.0
 */
class WeiXin
{
    private $config;

    /**
     * [__construct 从数据库读取微信申请的密钥]
     */
    public function __construct() 
    {
        $this->config = array(
                'partner_id'        =>  '',
                'partner_key'       =>  '',
                'appid'             =>  '',
                'secret'            =>  '',
                'pay_sign_key'      =>  '',
                'notify_url'        =>  '',
            );
    }

    /**
     * [Get_App_Code 生成支付信息]
     * @param  [array] $order   [订单数据]
     * @return [string]         [支付信息]
     */
    public function Get_App_Code($order)
    {
        /* 标题空格处理 */
        if(!empty($order['subject'])) $order['subject'] = str_replace(array(' ', "\n", "\r"), '', $order['subject']);

        $access_token = $this->Get_Access_Token();
        $param = array(
            'appid'         =>  $this->config['appid'],
            'traceid'       =>  $order['out_trade_no'],
            'noncestr'      =>  md5(time().rand()),
            'package'       =>  $this->GetParamData($order),
            'timestamp'     =>  time(),
            'sign_method'   =>  'sha1',
        );

        /* 获取支付签名 */
        $param['app_signature'] = $this->GetAppSign($param);

        /* 生成预支付 */
        $data = $this->GenprePayInsert($param, $access_token);

        /* 生成支付信息 */
        if(!empty($data['prepayid']) && !empty($data['errmsg']) && $data['errmsg'] == 'Success')
        {
            $pay = array(
                    'appid'         =>  $this->config['appid'],
                    'noncestr'      =>  $param['noncestr'],
                    'package'       =>  'Sign=WXPay',
                    'partnerid'     =>  $this->config['partner_id'],
                    'prepayid'      =>  $data['prepayid'],
                    'timestamp'     =>  $param['timestamp']
                );
            $pay['sign'] = $this->GetAppSign($pay);
            return $pay;
        }
        return '';
    }

    /**
     * [GenprePayInsert 生成预支付]
     * @param [array] $param        [请求参数]
     * @param [token] $access_token [token]
     */
    private function GenprePayInsert($param, $access_token)
    {
        return json_decode($this->Curl_Post('https://api.weixin.qq.com/pay/genprepay?access_token='.$access_token, json_encode($param)), true);
    }

    /**
     * [GetAppSign signature签名生成]
     * @param [array] $param    [参数数据]
     */
    private function GetAppSign($param)
    {
        unset($param['sign_method']);
        $param['appkey'] = $this->config['pay_sign_key'];
        ksort($param);
        return sha1($this->SetParam($param));
    }

    /**
     * [GetParamData 获取参数数据]
     * @param  [array] $data    [订单数据]
     * @return [array]          [参数和sign]
     */
    private function GetParamData($data)
    {
        $order = array(
            'bank_type'         =>  'WX',
            'body'              =>  $data['subject'],
            'total_fee'         =>  $data['total_fee']*100, /* 微信要求需要乘以100 */
            'spbill_create_ip'  =>  $GLOBALS['pz_log']->Getip(),
            'out_trade_no'      =>  $data['out_trade_no'],
            'notify_url'        =>  $this->config['notify_url'],
            'partner'           =>  $this->config['partner_id'],
            'fee_type'          =>  1,
            'input_charset'     =>  'UTF-8',
            'attach'            =>  'weixin',
        );
        ksort($order);

        $sgin = strtoupper(md5($this->SetParam($order).'&key='.$this->config['partner_key']));
        return $this->SetParam($order, true).'&sign='.$sgin;
    }

    /**
     * [Curl_Post curl模拟post]
     * @param  [string] $url  [请求地址]
     * @param  [array] $post  [发送的post数据]
     */
    private function Curl_Post($url, $post) {
        $options = array(
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HEADER         => false,
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => $post,
        );

        $ch = curl_init($url);
        curl_setopt_array($ch, $options);
        $result = curl_exec($ch);
        curl_close($ch);
        return $result;
    }

    /**
     * [SetParam url模式字符串拼接]
     * @param [array]  $param         [需要拼接的参数]
     * @param [boolean] $is_urlencode [是否urlencode转义value]
     */
    private function SetParam($param, $is_urlencode = false)
    {
        $str = '';
        foreach($param as $k=>$v) 
        {
            if($is_urlencode)
            {
                $str .= $k.'='.urlencode($v).'&';
            } else {
                $str .= $k.'='.$v.'&';
            }
        }
        return substr($str, 0, -1);
    }

    /**
     * [Get_Access_Token 获取微信支付token]
     * @return [string]         [token]
     */
    private function Get_Access_Token()
    {   
        if(file_exists('/tmp/weixin_pay_token.json'))
        {
            $temp = json_decode(file_get_contents('/tmp/weixin_pay_token.json'), true);
            if($temp['time'] > time()) $token = $temp['token'];
        }
        if(empty($token)) $token = $this->Set_Access_Token();
        return $token;
    }

    /**
     * [Set_Access_Token token设置]
     * @return [string]     [token]
     */
    private function Set_Access_Token()
    {
        $temp = json_decode(file_get_contents('https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid='.$this->config["appid"].'&secret='.$this->config['secret']), true);
        if(!empty($temp['access_token']))
        {
            $data = array(
                    'token' =>  $temp['access_token'],
                    'time'  =>  time()+7000
                );
            if(!is_dir('/tmp')) mkdir('/tmp');
            file_put_contents('/tmp/weixin_pay_token.json', json_encode($data));
            return $temp['access_token'];
        }
        return '';
    }

    /**
     * [Respond 响应操作]
     * @return [string] [响应处理结果]
     */
    public function Respond()
    {
        $param = $_GET;
        if(empty($param)) return;
        $param_sign = $param['sign']; unset($param['sign']);

        ksort($param);
        $sign = strtoupper(md5($this->SetParam($param).'&key='.$this->config['partner_key']));
        if($param_sign != $sign) return;

        /* check_money方法 价格校验是否一致 */
        if(isset($param['trade_state']) && $param['trade_state'] == 0 && check_money($param['out_trade_no'], $param['total_fee']/100))
        {
            //如果成功这里就可以处理自己的订单了,标识符是 $param['out_trade_no']
        }
    }
}

?>

阅读全文

PHP单例模式 设计模式 面向对象
发表于 2015-9-1 | 浏览(638) | PHP
<?php

/**
 * 单例模式
 * @author  Devil
 * @version v_0.0.1
 */
class Singleton
{
	private $parem;

	/**
	 * [__construct 构造方法]
	 * @param [mixed] $param [参数]
	 */
	private function __construct($param)
	{
		$this->param = $param;
	}

	/**
	 * [Instantiate 静态方法, 用于实例化类]
	 * @param  [mixed] $param [参数]
	 * @return [object]   [类对象]
	 */
	public static function Instantiate($param)
	{
		static $object = null;
		if(!is_object($object)) $object = new self($param);
		return $object;
	}

	/**
	 * [Show 测试方法]
	 */
	public function Show()
	{
		print_r($this->param);
	}
}



/**
 * 使用列子
 */
$param = array('test', 'demo', 'devil');
$obj = Singleton::Instantiate($param);
$obj->Show();

/**
 * $obj = Singleton::Instantiate($param);
 * 不管在项目中多少次这么对类进行实例化,都不会重复创建类对象。
 * $object 被定义成静态变量,不能被第二次赋值。
 * 只要$object是一个对象就直接返回当前对象,则进行实例化并返回。
 *
 * 单例模式可以防止重复创建对象,减轻内存开销。
 */

?>

阅读全文

TOP