标签分类 热门分类
当前位置:首页 > > PHP编程电子书网盘下载
PHP核心技术与最佳实践 PHP核心技术与最佳实践
码小辫

码小辫 提供上传

资源
48
粉丝
5
喜欢
404
评论
10

    PHP核心技术与最佳实践 PDF 高质量版

    PHP编程电子书
    • 发布时间:

    给大家带来的一篇关于PHP编程相关的电子书资源,介绍了关于PHP、核心技术、实践方面的内容,本书是由机械工业出版社出版,格式为PDF,资源大小197.3 MB,列旭松,陈文编写,目前豆瓣、亚马逊、当当、京东等电子书综合评分为:9.5

  • PHP核心技术与最佳实践 PDF 下载
  • 下载地址:https://pan.baidu.com/s/1cO8O0mzTFSPebYMrD6zo0
  • 分享码:2fe5
  • 读者评价

    成书时间虽然有些久远,但内容在当下也绝对有指导意义,适合有PHP基础的人深读,书中的内容会帮助你打开PHP深层应用的视野,虽然对部分内容的讲解还不够深入,但起码已为你指引了方向,可以借此进一步研究,好书,值得一看。

    对于PHP学习很有帮助, 不适合初学者, 里面有很大篇幅讲到扩展的开发, 实际开发较少使用, 但是对PHP理解可以更深. 总体来说是本好书, 值得购买

    此书比较实际,看的多看版,代码排版很糟糕,下划线都没了,有些特殊符号还是全角的,代码拷出来换行符也没了,代码主要还是作参考吧

    前三章写的还是挺好的,但是! 很遗憾我被前三章的高质量给骗了。。。居然认认真真的翻完了后面章节的内容- - 讲的没什么意思,看起来更像是网络教程合集(虽然属于原创内容,但也是原创的网络教程,随用随查就好了,没有必要买书专门看),编写php扩展时推荐的编辑器居然是vc6...然后开始放Zend扩展的API介绍…… 所以,最好还是不要买国人写的技术书…… 珍爱生命,只读经典

    正如书序中所说的,这是一本面向突破瓶颈的php程序员的书。
    由于语言的特性,php语言简单好用导致了编程人员水平以及代码的良莠不齐,工作一到两年的php人员都是模板工人的代名词。
    此书在一定范围内开阔了开发人员的视野,比如通过面向对象的方式来解决php代码扩展性差的问题,网络编程应用来扩展php的实用范围,并且花费了相当的篇幅进行php源码的剖析,完成一个php的扩展项目,这是深度方面。
    虽然有的地方不尽完善,表达不是特别到位,总的来说,是一本读后肯定有所脾益的书。

    正如这本书所说的,市场上都是讲如何安装php如何写hello word,如何写html,这本书主要是中高级程序员看的一本书。
    目前看到了面向对象这一块,各种设计模式的优缺点等,收获不少。
    书中有大量的跟java的对比,还好我有一些java的基础,可能没有java基础的人会觉得这些对比没有意义或者说是php黑。
    书里的内容不够连贯,难道是因为两位作者合作编写存在风格和沟通问题?
    作为一个最佳实践的书,很多实现却是在windows下实现,实再让人跌破眼镜。再如会讲到很多redis,varnish的功能,我觉得好像跟php核心技术无关,顶多会算在php实践里,但这些实践的讲解也只讲了一些安装和配置,真正和php结合的项目,设计的代码并没有。
    总的来说,应该是一本好书。如果有再版,可以将内容做得更加连贯一些,应对开发的需求做系统分析,而不是活生生的php硬生生拆成几个模块;同时建议代码可以再丰富一些,比如一些高级的方法,核心类。
    如果有再版,还会再买的。

    读完这本书后感觉还是有点用吧,书中开始就从面向对象说起,直至错误处理、异常,我深不以为然,这种知识入门级书上都会有,而讲到内核、扩展,我也只能撇撇嘴,如果读者要了解这方面的,你这小小一两章肯定是不够的。本书中大多地方也就平淡无奇,有点凑字数的样子,更不知本书书题中核心技术在书中所指!不过,让我印象比较深刻的是最后的文件数据库,也算不白买此书了吧!

    正如书序中所说的,这是一本面向突破瓶颈的php程序员的书。由于语言的特性,php语言简单好用导致了编程人员水平以及代码的良莠不齐,工作一到两年的php人员都是模板工人的代名词。此书在一定范围内开阔了开发人员的视野,比如通过面向对象的方式来解决php代码扩展性差的问题,网络编程应用来扩展php的实用范围,并且花费了相当的篇幅进行php源码的剖析,完成一个php的扩展项目,这是深度方面。

    php的东西都讲到了。从与php相关不大的面向对象开始,接着谈及了php网络编程中设计到的一些重要概念,然后沿着php编程中常用的php-mysql-模板这样的技术路线大致说明,接着探讨了写php内核中的一些概念,最后重点讲解了网络编程中的各种缓存技术和网站优化。有一个疑问,这书可以作为初级php程序员和中级php程序员的评判吗?

    编辑推荐

    系统归纳和深刻解读PHP开发中的编程思想、底层原理、核心技术、开发技巧、编码规范和**实践,为PHP程序员进阶修炼提供全面而高效的指导!

    内容简介

    这是一本致力于为希望成为中高级PHP程序员的读者提供高效而有针对性指导的经典著作。本书系统归纳和深刻解读了PHP开发中的编程思想、底层原理、核心技术、开发技巧、编码规范和*实践。

     全书分为5个部分:第一部分(1~2章)从不同的角度阐述了面向对象软件设计思想的核心概念、技术和原则,分析了面向对象的特性、设计模式的理念,指出了如何设计低耦合、高可扩展性的软件,等等;第二部分(3~6章)详细讲解了PHP中正则表达式的规范和使用技巧,PHP网络编程的原理、方法、技巧和一些重要的操作,PDO、数据库应用优化,数据库设计和MySQL的高级应用,PHP扩展引擎的原理与实践;第三部分(第7章)拨云见日,围绕PHP扩展开发进行了细致而深入的探讨,解析了PHP的底层实现和Zend虚拟机API,并用PHP扩展开发的实例带领读者走进PHP的底层世界,旨在让读者对PHP性能优化、底层原理进行深入的理解。第四部分(8~11章)重点讨论了缓存的设计、Memcached的原理与实践、NoSQL数据库Redis源码分析与应用实践、高性能PHP网站的架构和设计等内容;第五部分(12~14章)详细讲解了PHP代码的调试和测试、Hash算法和数据库的实现,以及PHP的编码规范,旨在帮助读者提高开发效率,养成良好编程习惯。

    章节总结

    1、总共14章;

    2、第1、2章讲PHP的OOP;

    其中第一章侧重于PHP的OOP与Java等的对比,面对对象开发的概念性问题,PHP的OOP的特殊之处,并对此给出代码例子;

    3、第3、4、5章对某一个基础技术应用进行详解;

    第3章讲正则表达式(md,为啥我总是看了就忘,这个东西。。。);

    第4章讲网络技术,包括HTTP协议,socket,抓包,cURL,SMTP等应用层应用;

    第5章讲PHP和数据库,包括基本的数据库概念,PDO和MySQL;

    4、第6、7章讲PHP模板和拓展;

    基本都是先讲概念,然后侧重于实践(代码);

    5、第8,9,10章讲缓存

    先缓存,然后Memcached,然后Redis

    6、其他

    高性能网络架构(承上启下,为以后学更高级内容做准备);

    代码调试和测试;

    Hash算法;

    PHP编码规范;

    作者介绍

    列旭松,资深PHP技术工程师,精通PHP及其相关技术,对PHP内核原理有较深入的理解,开发经验丰富。曾自主开发了关键字匹配服务器和消息队列SquirrelMQ。平时喜欢开发一些实用的PHP扩展,如PHP字典扩展(红黑树算法)和PHP索引扩展(B+树算法)。精通C语言,同时对Web服务器的架构和优化、高并发服务端编程、Redis和Memcached等技术有深入的研究和认识。活跃于PHPChina和ChinaUnix等专业社区,担任PHPChina论坛内核版块版主。

    陈文,资深PHP技术工程师,精通PHP及其相关技术,尤其擅长于PHP框架开发和应用架构。他还是一位资深的Java开发工程师,具有Fortran、Scala和C++语言的开发和使用背景,在传统软件和互联网开发领域都有丰富的实战经验。此外,他还擅长TCP/IP编程、多线程与并发程序设计、网络协议分析、数据库性能优化以及各种缓存技术,熟悉MySQL和Oracle等关系数据库产品。现从事网络安全软件开发,以及移动SI业务开发。对语言特性和软件设计思想有独到的见解,追求代码之美和高效率程序开发,爱好钻研底层技术,崇尚和提倡“以理论指导实践”。尤其爱好数学,认为数学是培养和锻炼思维和逻辑能力的重要工具,对算法有一定研究。长期活跃在PHPChina、ITeye和看雪论坛等社区,在PHPChina社区担任版主。

    内容节选

    php一致性hash算法:

    class FlexHash
    {
        private $serverList = array();
        private $isSorted = false;
     
        /**
         * 增加一个服务器
         * @param $server
         * @return bool
         */
        public function addServer($server)
        {
            $hash = $this->hash($server);
            if (! array_key_exists($hash, $this->serverList)) {
                $this->serverList[$hash] = $server;
            }
            $this->isSorted = false;
            return true;
        }
     
        public function lookup($key)
        {
            $hash = $this->hash($key);
            // 如果没有排序,先排序,形成一个倒序的环
            if (! $this->isSorted) {
                krsort($this->serverList, SORT_NUMERIC);
                $this->isSorted = true;
            }
     
            // 如果hash值大于等于一个server的hash,直接返回该server
            foreach ($this->serverList as $pos => $server) {
                if ($hash >= $pos) {
                    return $server;
                }
            }
     
            // 如果不符合,取出最后一个server
            return end($this->serverList);
        }
     
     
        /**
         * @param $key
         * @return int
         */
        public function hash($key)
        {
            $md5 = substr(md5($key), 0, 8);
            $seed = 33;
            $hash = 0;
            for ($i = 0; $i<8; $i++) {
                $hash = $hash * $seed + ord($md5{$i});
            }
            return $hash & 0x7FFFFFFF;
        }
    }
     
    $flexHash = new FlexHash();
    $flexHash->addServer('192.168.2.1');
    $flexHash->addServer('127.0.0.1');
    $flexHash->addServer('192.168.2.3');
    $flexHash->addServer('192.168.2.4');
     
    echo $flexHash->lookup('key1') . PHP_EOL;
    echo $flexHash->lookup('key2') . PHP_EOL;
    echo $flexHash->lookup('key3') . PHP_EOL;
    echo $flexHash->lookup('key4') . PHP_EOL;
    echo $flexHash->lookup('key5') . PHP_EOL;
    echo $flexHash->lookup('key6') . PHP_EOL;

    1.PHP的写时复制:

    如果通过赋值的方式赋值给变量时不会申请新内存来存放新变量所保存的值,而是简单的通过一个计数器来共用内存,只有在其中的一个引用指向变量的值发生变化时才申请新空间来保存值内容以减少对内存的占用。

    看一段代码:
    $a = 100;
    $b = $a;
     
    debug_zval_dump($a);
     
    输出long(100) refcount(3),引用统计为3(debug_zval_dump也引用了a,所以为3);
     
    $a = 100;
    $b = $a;
    $a = 50; // 写时复制,会复制a的内容到新的内存,将b指向新的内存,断开a的引用
     
    debug_zval_dump($a);
    输出long(10) refcount(2)

    2.PHP日志记录

    php.ini
    log_errors = On
    error_reporting = E_ALL
    error_log = PATH //错误日志存放位置

    记录log不会对php运行效率产生实质影响.

    3.apache日志:

    ErrorLog logs/error_log //存放位置

    LogLevel warn //错误日志级别

    CustomLog logs/access_log combined // 访问日志

    目录

    • 前言
    • 第1章 面向对象思想的核心概念
    • 1.1 面向对象的“形”与“本”
    • 1.1.1 对象的“形”
    • 1.1.2 对象的“本”
    • 1.1.3 对象与数组
    • 1.1.4 对象与类
    • 1.2 魔术方法的应用
    • 1.2.1 set和get方法
    • 1.2.2 call和callStatic方法
    • 1.2.3 toString方法
    • 1.3 继承与多态
    • 1.3.1 类的组合与继承
    • 1.3.2 各种语言中的多态
    • 1.4 面向接口编程
    • 1.4.1 接口的作用
    • 1.4.2 对PHP接口的思考
    • 1.5 反射
    • 1.5.1 如何使用反射API
    • 1.5.2 反射有什么作用
    • 1.6 异常和错误处理
    • 1.6.1 如何使用异常处理机制
    • 1.6.2 怎样看PHP的异常
    • 1.6.3 PHP中的错误级别
    • 1.6.4 PHP中的错误处理机制
    • 1.7 本章小结
    • 第2章 面向对象的设计原则
    • 2.1 面向对象设计的五大原则
    • 2.1.1 单一职责原则
    • 2.1.2 接口隔离原则
    • 2.1.3 开放-封闭原则
    • 2.1.4 替换原则
    • 2.1.5 依赖倒置原则
    • 2.2 一个面向对象留言本的实例
    • 2.3 面向对象的思考
    • 2.4 本章小结
    • 第3章 正则表达式基础与应用
    • 3.1 认识正则表达式
    • 3.1.1 PHP中的正则函数
    • 3.1.2 正则表达式的组成
    • 3.1.3 测试工具的使用
    • 3.2 正则表达式中的元字符
    • 3.2.1 什么是元字符
    • 3.2.2 起始和结束元字符
    • 3.2.3 点号
    • 3.2.4 量词
    • 3.3 正则表达式匹配规则
    • 3.3.1 字符组
    • 3.3.2 转义
    • 3.3.3 反义
    • 3.3.4 分支
    • 3.3.5 分组
    • 3.3.6 反向引用
    • 3.3.7 环视
    • 3.3.8 贪婪懒惰匹配模式
    • 3.4 构造正则表达式
    • 3.4.1 正则表达式的逻辑关系
    • 3.4.2 运算符优先级
    • 3.4.3 正则表达式的常用模式
    • 3.5 正则在实际开发中的应用
    • 3.5.1 移动手机校验
    • 3.5.2 匹配E-mail地址
    • 3.5.3 转义在数据安全中的应用
    • 3.5.4 URL重写与搜索引擎优化
    • 3.5.5 删除文件中的空行和注释
    • 3.6 正则表达式的效率与优化
    • 3.7 本章小结
    • 第4章 PHP网络技术及应用
    • 4.1 HTTP协议详解
    • 4.1.1 HTTP协议与SPDY协议
    • 4.1.2 HTTP协议如何工作
    • 4.1.3 HTTP应用:模拟灌水机器人
    • 4.1.4 垃圾信息防御措施
    • 4.2 抓包工具
    • 4.2.1 抓包工具分类
    • 4.2.2 Fiddler功能与原理
    • 4.2.3 安装Fiddler
    • 4.2.4 Fiddler基本界面
    • 4.2.5 使用Fiddler进行HTTP断点调试
    • 4.3 Socket进程通信机制及应用
    • 4.3.1 进程通信相关概念
    • 4.3.2 Socket演示:实现服务器端与客户端的交互
    • 4.3.3 Socket函数原型
    • 4.3.4 PHP中的Socket函数
    • 4.3.5 Socket交互应用:使用Socket抓取数据
    • 4.4 cURL工具及应用
    • 4.4.1 建立cURL请求的基本步骤
    • 4.4.2 检查cURL错误和获取返回信息
    • 4.4.3 在cURL中伪造头信息
    • 4.4.4 在cURL中用POST方法发送数据
    • 4.4.5 使用cURL上传文件
    • 4.4.6 cURL批处理
    • 4.4.7 cURL设置项
    • 4.4.8 网络应用:使用cURL抓取腾讯微博
    • 4.5 简单邮件传输协议SMTP
    • 4.5.1 SMTP协议如何工作
    • 4.5.2 SMTP协议常用命令
    • 4.5.3 SMTP协议应用:使用Socket发送邮件
    • 4.6 WebService的前世今生
    • 4.6.1 WebService简介
    • 4.6.2 认识PHPRPC协议
    • 4.6.3 Web服务的实现模式
    • 4.6.4 简单对象访问协议SOAP
    • 4.6.5 调试工具soapUI
    • 4.7 Cookie详解
    • 4.7.1 Cookie的基本概念及设置
    • 4.7.2 PHP和JavaScript对Cookie的操作
    • 4.7.3 Cookie存储机制及应用
    • 4.7.4 Cookie跨域与P3P协议
    • 4.7.5 本地存储localStorage
    • 4.8 Session详解
    • 4.8.1 Session的基本概念及设置
    • 4.8.2 Session的工作原理
    • 4.8.3 Session入库
    • 4.8.4 Cookie与Session问答
    • 4.9 本章小结
    • 第5章 PHP与数据库基础
    • 5.1 什么是PDO
    • 5.1.1 PDO预定义类
    • 5.1.2 如何使用PDO
    • 5.1.3 PDO参数绑定与预编译
    • 5.1.4 PDO事务处理
    • 5.1.5 PDO的效率问题
    • 5.2 数据库应用优化
    • 5.2.1 基本语句优化10个原则
    • 5.2.2 索引与性能分析
    • 5.2.3 服务器和配置的优化
    • 5.2.4 MySQL瓶颈及应对措施
    • 5.3 数据库设计
    • 5.3.1 范式与反范式
    • 5.3.2 数据库分区
    • 5.3.3 分表的应用
    • 5.4 MySQL的高级应用
    • 5.4.1 MySQL自增长序列
    • 5.4.2 MySQL视图
    • 5.4.3 MySQL存储过程和事件调度
    • 5.4.4 用MySQL模拟消息队列
    • 5.4.5 SQL注入漏洞与防范
    • 5.5 本章小结
    • 第6章 PHP模板引擎的原理与实践
    • 6.1 代码分层的思想
    • 6.2 实现一个简单的模板引擎骨架
    • 6.2.1 搭建模板引擎基础类骨架
    • 6.2.2 编译类骨架
    • 6.2.3 测试模板引擎
    • 6.3 模板引擎的编译
    • 6.3.1 实现变量标签
    • 6.3.2 实现foreach标签
    • 6.3.3 实现if…else标签
    • 6.3.4 对PHP原生语法的支持
    • 6.4 完善模板引擎
    • 6.4.1 模板缓存机制的实现
    • 6.4.2 调试和缓存清理
    • 6.4.3 如何使用模板
    • 6.5 常用模板引擎
    • 6.5.1 Discuz模板引擎
    • 6.5.2 Smarty模板引擎
    • 6.5.3 DedeCms模板引擎
    • 6.5.4 Blitz模板引擎
    • 6.5.5 模板引擎的一些思考
    • 6.6 本章小结
    • 第7章 PHP扩展开发
    • 7.1 为什么要开发PHP扩展
    • 7.2 搭建PHP扩展框架
    • 7.2.1 PHP源代码目录
    • 7.2.2 ext_skel工具
    • 7.2.3 Windows平台环境配置
    • 7.2.4 Linux平台环境配置
    • 7.2.5 PHP的生命周期
    • 7.3 PHP内核中的变量
    • 7.3.1 PHP变量在内核中的存储方式
    • 7.3.2 PHP内核变量访问宏
    • 7.3.3 引用计数器与写时复制
    • 7.4 PHP内核中的HashTable分析
    • 7.4.1 PHP内核HashTable的数据结构
    • 7.4.2 HashTable的代码实现
    • 7.5 Zend API详解与扩展编写
    • 7.5.1 什么是Zend引擎
    • 7.5.2 Zend引擎内存管理
    • 7.5.3 PHP扩展的架构
    • 7.5.4 接收用户传递的参数
    • 7.5.5 在PHP扩展中创建变量
    • 7.5.6 在PHP扩展中为变量赋值
    • 7.5.7 错误和输出API
    • 7.5.8 运行时信息函数
    • 7.5.9 调用用户自定义函数
    • 7.5.10 PHP配置项
    • 7.5.11 创建常量的宏
    • 7.6 编写一个完整的扩展
    • 7.6.1 链表结构的实现
    • 7.6.2 创建PHP扩展框架
    • 7.6.3 编写代码
    • 7.6.4 编译安装扩展
    • 7.6.5 测试扩展
    • 7.7 本章小结
    • 第8章 缓存详解
    • 8.1 认识缓存
    • 8.1.1 为什么使用缓存
    • 8.1.2 命中率
    • 8.1.3 缓存更新策略
    • 8.1.4 缓存最大数据量
    • 8.2 文件缓存
    • 8.2.1 文件缓存机制
    • 8.2.2 文件缓存开源产品Secache
    • 8.3 Opcode缓存
    • 8.3.1 eAccelerator下载及使用
    • 8.3.2 如何查看Opcode
    • 8.4 客户端缓存
    • 8.4.1 客户端缓存规则
    • 8.4.2 HTTP协议中的缓存使用
    • 8.4.3 HTTP缓存实例
    • 8.4.4 HTML 5中的Application Cache
    • 8.5 Web服务器缓存
    • 8.5.1 Apache缓存
    • 8.5.2 Nginx缓存
    • 8.6 本章小结
    • 第9章 Memcached使用与实践
    • 9.1 为什么要用Memcached
    • 9.2 Memcached的安装及使用
    • 9.2.1 安装Memcached服务器
    • 9.2.2 安装Memcached客户端
    • 9.2.3 使用memcache扩展访问Memcached服务器
    • 9.2.4 使用Memcached加速Web应用
    • 9.3 深入了解Memcached
    • 9.3.1 Memcached如何支持高并发
    • 9.3.2 使用Slab分配算法保存数据
    • 9.3.3 删除过期item
    • 9.3.4 使用LRU算法淘汰数据
    • 9.3.5 Memcached多线程模型
    • 9.4 Memcached分布式布置方案
    • 9.4.1 普通Hash分布
    • 9.4.2 一致性Hash分布
    • 9.4.3 一致性Hash分布算法实例
    • 9.5 本章小结
    • 第10章 Redis使用与实践
    • 10.1 Redis的安装及使用
    • 10.1.1 Redis安装步骤
    • 10.1.2 修改Redis配置文件
    • 10.1.3 运行Redis服务器
    • 10.1.4 key相关命令
    • 10.1.5 Redis支持的数据类型
    • 10.1.6 Redis排序命令详解
    • 10.2 事务处理
    • 10.2.1 事务处理原理
    • 10.2.2 事务处理实现
    • 10.3 持久化
    • 10.3.1 内存快照
    • 10.3.2 日志追加
    • 10.4 主从同步
    • 10.4.1 Redis主从同步原理
    • 10.4.2 Slave端的工作流程
    • 10.4.3 Master端的工作流程
    • 10.5 虚拟内存
    • 10.5.1 配置文件信息
    • 10.5.2 开启VM的后台操作
    • 10.5.3 Redis Object和VM Pointer
    • 10.5.4 交换过程
    • 10.5.5 阻塞式VM
    • 10.5.6 非阻塞式VM
    • 10.6 扩展库phpredis安装及使用
    • 10.7 Redis应用实践
    • 10.7.1 使用消息队列发布微博
    • 10.7.2 Redis替代文件存储Session
    • 10.8 深入了解Redis内核
    • 10.8.1 内存淘汰
    • 10.8.2 对象引用计数器
    • 10.8.3 自动关闭超时连接
    • 10.8.4 清除过期数据
    • 10.9 本章小结
    • 第11章 高性能网站架构方案
    • 11.1 如何优化网站响应时间
    • 11.1.1 吞吐率
    • 11.1.2 压力测试
    • 11.1.3 持久连接
    • 11.2 MySQL响应速度提高方案:HandlerSocket
    • 11.2.1 HandlerSocket工作原理
    • 11.2.2 HandlerSocket安装和配置
    • 11.2.3 PHP-HandlerSocket性能测试
    • 11.3 MySQL稳定性提高方案:主从复制
    • 11.3.1 主从复制工作原理
    • 11.3.2 主从复制配置
    • 11.3.3 连接主从服务器
    • 11.4 Web应用加速方案:Varnish
    • 11.4.1 传统代理与反向代理
    • 11.4.2 Varnish安装和配置
    • 11.4.3 Varnish性能测试
    • 11.4.4 修改缓存规则
    • 11.4.5 监控Varnish运行状态
    • 11.5 异步计算方案:Gearman
    • 11.5.1 Gearman工作原理
    • 11.5.2 安装Gearman和PHP扩展
    • 11.5.3 使用Gearman异步发送邮件
    • 11.6 本章小结
    • 第12章 代码调试和测试
    • 12.1 调试PHP代码
    • 12.1.1 PHP调试函数
    • 12.1.2 断点调试与变量跟踪工具Xdebug
    • 12.2 前端调试
    • 12.2.1 Firebug调试API
    • 12.2.2 使用Firebug调试DOM结构
    • 12.2.3 使用Firebug调试JavaScript
    • 12.2.4 使用Fiddler调试远程服务器上的文件
    • 12.3 日志管理
    • 12.3.1 PHP日志
    • 12.3.2 Apache服务器日志
    • 12.3.3 MySQL日志
    • 12.4 代码性能测试技术
    • 12.4.1 时间点测试
    • 12.4.2 文件查看工具WinCacheGrind
    • 12.4.3 性能测试注意事项
    • 12.5 单元测试
    • 12.5.1 单元测试框架PHPUnit的安装
    • 12.5.2 结合NetBeans使用PHPUnit进行单元测试
    • 12.5.3 PHPUnit中的断言函数
    • 12.5.4 PHPUnit常用方法
    • 12.5.5 PHPUnit常用注解
    • 12.6 压力测试
    • 12.6.1 使用JMeter压力测试HTTP
    • 12.6.2 压力测试MySQL
    • 12.6.3 JMeter+Badboy组合测试
    • 12.7 本章小结
    • 第13章 Hash算法与数据库实现
    • 13.1 Hash函数
    • 13.2 Hash算法
    • 13.2.1 直接取余法
    • 13.2.2 乘积取整法
    • 13.2.3 经典Hash算法Times33
    • 13.3 Hash表
    • 13.3.1 Hash表结构
    • 13.3.2 使用PHP实现Hash表
    • 13.3.3 Hash表冲突
    • 13.3.4 拉链法解决冲突
    • 13.4 一个小型数据库的实现
    • 13.4.1 pack函数的用法
    • 13.4.2 unpack函数的用法
    • 13.4.3 索引文件和数据文件
    • 13.4.4 数据库接口方法
    • 13.4.5 源代码解析
    • 13.4.6 测试代码
    • 13.5 本章小结
    • 第14章 PHP编码规范
    • 14.1 文件格式
    • 14.1.1 文件标记
    • 14.1.2 文件和目录命名
    • 14.1.3 文件目录结构
    • 14.2 命名规范
    • 14.2.1 变量命名
    • 14.2.2 类及接口命名
    • 14.2.3 数据库命名
    • 14.2.4 习惯与约定
    • 14.3 注释规范
    • 14.3.1 程序注释
    • 14.3.2 文件注释
    • 14.3.3 类接口注释
    • 14.3.4 方法和函数注释
    • 14.3.5 标注的使用
    • 14.4 代码风格
    • 14.4.1 缩进和空格
    • 14.4.2 语句断行
    • 14.4.3 更好的习惯
    • 14.5 本章小结

     

    读书笔记

    PHP调整数组顺序使奇数位于偶数前(代码/闭包扩展)

    php如何实现原址排序数组使奇数位于偶数前面(代码)

    输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

    1、遍历数组,判断元素奇数偶数,push进新数组,空间换时间

    2、插入排序的思想 空间上是原址排序

    2.1从前往后遍历,判断当前的是奇数

    2.2从当前的开始,从后往前遍历,如果是偶数就往后一位移动

    2.3当前奇数插入位置

    for i=1;i<arr.length;i++
        target=arr[i]
        if arr[i]%2==1
            j=i-1
            while j>=0&&arr[j]%2==0
                arr[j+1]=arr[j]
                j--
            arr[j+1]=target
    <?php
    $arr=array(1,2,3,4,5,6,7,8,9,10);
    function reOrderArray($arr){
            $length=count($arr);
            //从前往后遍历
            for($i=1;$i<$length;$i++){
                    //判断当前元素是奇数
                    $target=$arr[$i];
                    if($target%2==1){
                            //从后往前遍历,如果有偶数就往后移动一位
                            $j=$i-1;
                            while($j>=0 && $arr[$j]%2==0){
                                    $arr[$j+1]=$arr[$j];
                                    $j--;
                            }   
                            //把奇数插入位置
                            $arr[$j+1]=$target;
                    }   
            }   
            return $arr;
    }
     
    $arr2=reOrderArray($arr);
    var_dump($arr2);
    array(10) {
      [0]=>
      int(1)
      [1]=>
      int(3)
      [2]=>
      int(5)
      [3]=>
      int(7)
      [4]=>
      int(9)
      [5]=>
      int(2)
      [6]=>
      int(4)
      [7]=>
      int(6)
      [8]=>
      int(8)
      [9]=>
      int(10)
    }

    调整数组顺序使奇数位于偶数前(闭包扩展)

    这道题意思就是,给所有奇数放到偶数前面,我们可以设置两个指针,一个从前往后,直到他扫到偶数,一个从后往前,直到他扫到奇数,然后交换两个数的位置,然后往下扫,当begin>end的时候停止。代码实现很简单,如下:

    <?php
    //调整数组,使奇数位于偶数前面
    
    function reorder($arr){
            $length = count($arr);
            $begin = 0;
            $end = $length - 1;
            while($begin < $end){
                    //向后移动begin,直到它指到偶数
                    while(($begin < $end) && (($arr[$end] & 0x1) != 0)){
                            $begin++;
                    }
    
                    //向前移动end,指到它指到奇数
                    while(($begin < $end) && (($arr[$end] & 0x1) == 0)){
                            $end--;
                    }
    
                    if($begin < $end){
                            $temp = $arr[$begin];
                            $arr[$begin] = $arr[$end];
                            $arr[$end] = $temp;
                    }
            }
            return $arr;
    }
    
    $arr = [1,2,3,4,5,6,7,8];
    var_dump(reorder($arr));

    上面需要注意的时,判断一个数是奇数还是偶数的时候,我用的$num & 0x1 如果等于1,则为奇数,等于0,则为偶数,效率比%要高一些。

    到这里,如果单纯对于这道题,已经算是完事了。

    但是如果题目一改,将所有偶数放于奇数前,你可能又要重写一个函数了。

    这时,需求又改了,将所有能被3整除的放到前面………

    所有负数放到前面…….

    写完以上那么多函数,发现这些函数都类似啊,只有判断条件不一样。

    我们可不可以将整个函数,解耦成两部分,一个是判断数字应该在函数的前半部分还是后半部分,一个是交换数组位置。

    这时候我们就用到了闭包,代码如下

    <?php
    //调整数组,使奇数位于偶数前面
    
    function reorder($arr, $func){
            $length = count($arr);
            $begin = 0;
            $end = $length - 1;
            while($begin < $end){
                    //向后移动begin,直到它指到偶数
                    while(($begin < $end) && (!$func($arr[$begin]))){
                            $begin++;
                    }
    
                    //向前移动end,指到它指到奇数
                    while(($begin < $end) && ($func($arr[$end]))){
                            $end--;
                    }
    
                    if($begin < $end){
                            $temp = $arr[$begin];
                            $arr[$begin] = $arr[$end];
                            $arr[$end] = $temp;
                    }
            }
            return $arr;
    }
    
    $func = function($condition){
            $flag = ($condition & 0x1) == 0;
            return $flag;
    };
    
    $arr = [1,2,3,4,5,6,7,8];
    var_dump(reorder($arr, $func));

     

    《PHP、MySQL与JavaScript学习手册》学习笔记与总结

    php常用系统函数大全

    字符串函数

    strlen:获取字符串长度,字节长度

    substr_count 某字符串出现的次数

    substr:字符串截取,获取字符串(按照字节进行截取)

    mb_strlen
    mb_substr

    strchr:与substr相似,从指定位置截取一直到最后

    strrchr(获取文件后缀名):与strchr一样,只是从右边开始查找字符

    strtolower:所有的字符都小写(针对英文字母)

    strtoupper:所有的字符都大写

    strrev:字符串反转(只能反转英文:英文存储只有一个字节),按照字节进行反转

    strpos:从字符串中找对应字符出现的位置(数字下标),从最左边开始找

    strrpos:与strpos一样,只是从字符串的右边开始找

    trim:去掉函数两边的字符,默认是空格

    str_split 函数把字符串分割到数组中。

    chunk_split() 函数把字符串分割为一连串更小的部分

    str_repeat("Shanghai",5);把字符串 "Shanghai " 重复 5 次

    str_replace('\\', '/', dirname(DIR))); 替换

    ucfirst 首字母大写

    时间日期函数

    time:得到当前时间的时间戳(整型:从格林威治时间1970年1月1日0时0分0秒开始)秒数

    date:时间序列化函数,将指定的时间戳转换成规定时间日期的显示格式(随意的字符串:有专业的格式符规定),如果没有指定时间戳,系统默认使用当前时间的时间戳

    strtotime:时间日期格式的字符串转换成对应的时间戳(只要是正确的英语时间表达方式,都可以进行转换)

    microtime:微秒时间戳,根据不同的要求返回不同的结果 混合 microtime (布尔类型 ),可以返回一个浮点数的时间,也可以返回一个数组(时间戳和微秒数)

    数学相关函数

    abs:绝对值

    floor:向下取整 floor(3.2) 结果等于3

    ceil:向上取整

    round:四舍五入

    rand:取得一个指定范围内的随机整数

    mt_rand:取得一个指定范围内的随机整数(效率更高)

    min:PHP 会将非数值的 string 当成 0,但如果这个正是最小的数值则仍然会返回一个字符串。如果多个参数都求值为 0 且是最小值,min() 会返回按字母表顺序最小的字符串,如果其中没有字符串的话,则返回数值的 0;

    max:PHP 会将非数值的字符串当成 0,但如果这个正是最大的数值则仍然会返回一个字符串。如果多个参数都求值为 0 且是最大值,max() 会返回其中数值的 0,如果参数中没有数值的 0,则返回按字母表顺序最大的字符串。对于多个数组,max从左到右比较;如果同时出现数组和非数组参数总把数组作为最大值返回;

    数组相关函数

    count() // 非数组返回1

    key:获取当前数组当前指针所指向的元素的下标

    current:获取的当前指针指向元素的数值

    next:获取下一个元素的值,并且将指针下移

    prev:获取上一个元素的值,并且将指针上移

    end :将指针移到数组的最后一个元素,并返回最终指针位置的值

    reset:将指针移到数组的第一个元素,返回最终指针位置的值

    array_keys:获取一个数组的所有键名,返回一个索引数组

    array_values:获取一个数组的所有值,返回一个索引数组

    explode:爆炸,将一个字符串按照某个指定的规则(通常是特殊字符),将数组分成多个段,每一段都当做一个数组的元素,返回一个索引数组

    split 类似 explode explode('.', 'abc.txt')等于split('\.','abc.txt')

    implode:粘合,将一个数组内部的所有元素按照某个指定的规则(特殊字符),将所有的元素拼接成一个字符串

    join() 把数组元素组合为一个字符串

    array_merge:合并,指的是将两个数组中的元素进行累计。如果后面的数组与前面的数组有下标(键名:关联)相同的,那么后面的元素的值会覆盖前面的;如果是索引的相同下标,会自动的修改下标叠加到前面的数组里。

    array_reverse — 返回反转后的数组

    array_flip — 交换数组中的键和值

    数据结构模拟函数

    array_shift:从数组的前面弹出元素,得到元素的值

    array_pop:从数组的后面弹出元素,获得元素的值

    array_unshift:从数组的前面压入元素,得到当前数组元素的个数

    array_push:从数组的后面压入元素,得到当前数组元素的个数

    判断变量

    is_bool:判断是否是布尔类型

    is_float:判断浮点型

    is_integer:判断整型

    is_object:判断对象

    is_array:判断数组

    is_string:判断字符串

    is_resource:判断资源

    is_scalar:scalar是标量的,判断是基本数据类型:整型,浮点型,布尔型和字符串型

    is_null 检测变量是否为NULL 是返回TRUE 否则返回false。1.被赋值为NULL;2.变量没被赋值;3.被unset()

    is_numeric:判断数字或者纯数字组成的字符串

    gettype:获得数据类型

    settype:改变数据类型

    isset

    unset() 如果在函数中 unset() 一个全局变量,则只是局部变量被销毁,而在调用环境中的变量将保持调用 unset() 之前一样的值,如果在函数中 unset() 一个通过引用传递的变量,则只是局部变量被销毁,而在调用环境中的变量将保持调用 unset() 之前一样的值。

    empty//array(),"",0,"0",NULL,FALSE 都返回true

    文件操作函数

    opendir(路径):打开一个路径资源(将路径内部的所有数据读入到内存)

    readdir(路径资源):从文件夹资源中读取当前资源指针所指向的文件的名字,指针会向下移动一位

    closedir(资源):释放对应的文件资源

    scandir(路径):读取一个路径内部的所有文件名,返回一个数组,数组的每一个元素都是文件名。

    file_exists:判断一个文件是否存在(文件是广义:路径和文件)

    is_dir:判断一个指定路径是否存在(文件夹)

    is_file:判断一个指定路径是否是文件(文件)

    mkdir:创建一个路径,如果路径存在就会报错

    rmdir:移除文件夹

    file_get_contents:从一个指定的文件内读取数据内容。

    file_put_contents:将指定的字符串写入到对应的文件

    fopen:打开一个文件资源

    fgetc:c代表character,一次读取一个字符

    fgets:s代表string,代表可以读取多个字符,取决于指定的读取长度或者是否碰到换行(最多只能读取一行数据)

    两个函数都是对当前资源指针进行操作,读取之后都会将指针下移

    fread:获取指定长度的数据直到文件结束

    fwrite:向文件资源指针所在的位置写入数据,写东西不会将当前位置已有的东西往后移,而是会覆盖

    fseek:将指针指定到对应的位置

    fclose:使用对应的文件资源

    copy:复制

    unlink:删除文件

    rename:重命名文件

    filemtime:m代表modify,文件最后被修改的时间

    filesize:文件大小(字节)

    fileperms:文件权限(Linux下的八进制)

    排序

    rsort() 函数用于对数组单元从高到低进行排序。
    asort() 函数用于对数组单元从低到高进行排序并保持索引关系。
    arsort() 函数用于对数组单元从高到低进行排序并保持索引关系。
    ksort() 函数用于对数组单元按照键名从低到高进行排序。
    krsort() 函数用于对数组单元按照键名从高到低进行排序。

    报错

    error_reporting(E_ALL)

    ini_set('display_errors', 1)

    常量

    define() 定义常量
    defined() 检测常量是否定义

    序列化

    serialize

    unserialize

    json_encode 对变量进行 JSON 编码

    json_decode 对JSON 格式的字符串进行编码

    编码

    base64_encode 本函数将字符串以 MIME BASE64 编码。在 BASE64 编码后的字符串只包含英文字母大小写、阿拉伯数字、加号与反斜线,共 64 个基本字符,不包含其它特殊的字符,因而才取名 BASE64。

    base64_decode 解码

    MySQL常用命令

    # @Date    : 2017-11-15 13:18:34
    # @Author  : 师兵范 (shibingfan@163.com)
     
    #常用MySQL数据库命令
    +------------+----------------+
    | COMD       |   DESCRIPTION  | 
    +------------+----------------+
    |ALTER       |修改            |
    |BACKUP      |备份            | 
    |\C          |取消输入        | 
    |CREATE      |创建            | 
    |DELETE      |删除行          | 
    |DESCRIBE    |表结构          | 
    |DROP        |删除            | 
    |EXIT(CTRL-C)|退出            | 
    |GRANT       |修改权限        | 
    |HELP(\h,\?) |帮助            | 
    |INSERT      |插入            | 
    |LOCK        |锁住            | 
    |QUIT(\q)    |退出            | 
    |RENAME      |重命名          | 
    |SHOW        |项目说明        | 
    |SOURCE      |从文件名执行命令| 
    |STATUS(\s)  |显示当前状态    | 
    |TRUNCATE    |清空表          | 
    |UNLOCK      |解锁表          | 
    |UPDATE      |更新数据        | 
    |USE         |打开数据库      | 
    +------------+----------------+
    #显示已有数据库
    mysql> SHOW databaseS; 
    +--------------------+
    | Database           |
    +--------------------+
    | information_schema |
    | drupal7            |
    | mysql              |
    | northwind          |
    | performance_schema |
    | shibingfan         |
    | sys                |
    | testdb             |
    +--------------------+
    8 rows in set (0.06 sec)
    #创建数据库
    mysql> CREATE DATABASE publications;
    Query OK, 1 row affected (0.03 sec)
    #使用数据库
    mysql> USE publications;
    Database changed
    #数据库授权
    mysql> GRANT ALL ON publications.* TO 'shibingfan'@'localhost' IDENTIFIED BY 'shibingfan';
    Query OK, 0 rows affected, 1 warning (0.00 sec)
    #创建数据库表
    mysql> CREATE TABLE classics(
        -> author VARCHAR(128),
        -> title VARCHAR(128),
        -> type VARCHAR(16),
        -> YEAR char(4))ENGINE  MyISAM;
    Query OK, 0 rows affected (0.08 sec)
    #查询表结构
    mysql> DESCRIBE classics;
    +--------+--------------+------+-----+---------+-------+
    | Field  | Type         | Null | Key | Default | Extra |
    +--------+--------------+------+-----+---------+-------+
    | author | varchar(128) | YES  |     | NULL    |       |
    | title  | varchar(128) | YES  |     | NULL    |       |
    | type   | varchar(16)  | YES  |     | NULL    |       |
    | YEAR   | char(4)      | YES  |     | NULL    |       |
    +--------+--------------+------+-----+---------+-------+
    4 rows in set (0.02 sec)
    #数据类型
    CHAR(n)   VARCHAR(n)
    BINARY(n) VARBINARY(n)
    TEXT(n)   TINYTEXT(n)   MEDIUMTEXT(n)  LONGTEXT(n)
    TINYBLOB(n)  BLOB(n)
    TINYINT   SMALLINT      MEDIUMINT      INT/INTEGER    BIGINT
    FLOAT     DOUBLE/REAL
    DATETIME  DATE          TIMESTAMP      TIME           YEAR
    INT UNSIGNED,  NOT NULL,  AUTO_INCREMENT,  KEY
    #添加表列id
    mysql> ALTER TABLE classics ADD id INT UNSIGNED NOT NULL AUTO_INCREMENT KEY;
    Query OK, 0 rows affected (0.06 sec)
    Records: 0  Duplicates: 0  Warnings: 0
    #删除表列
    mysql> ALTER TABLE classics DROP id;
    Query OK, 0 rows affected (0.08 sec)
    Records: 0  Duplicates: 0  Warnings: 0
    #向表中添加数据
    mysql> INSERT INTO classics(author,title,type,year)VALUES('Shibingfan', 'LEARN MYSQL', 'EDUCATION', '1887');
    Query OK, 1 row affected (0.00 sec)
    #重命名表
    mysql> ALTER TABLE classics RENAME mytable ;
    Query OK, 0 rows affected (0.02 sec)
    #改变列数据类型
    mysql> ALTER TABLE mytable MODIFY year SMALLINT;
    Query OK, 5 rows affected (0.08 sec)
    #添加新列
    mysql> ALTER TABLE mytable ADD pages SMALLINT UNSIGNED;
    Query OK, 5 rows affected (0.08 sec)
    #重命名列
    mysql> ALTER TABLE mytable CHANGE type category VARCHAR(16);
    Query OK, 0 rows affected (0.31 sec)
    #删除列
    mysql> ALTER TABLE mytable DROP pages;//DROP命令不可逆
    Query OK, 5 rows affected (0.33 sec)
    #删除表
    mysql> DROP TABLE mytable;
    Query OK, 0 rows affected (0.02 sec)
    #创建索引
    mysql> ALTER TABLE mytable ADD INDEX(author(20));
    mysql> CREATE INDEX author ON mytable(author(20));//也可在创建表时添加索引
    Query OK, 5 rows affected (0.08 sec)
    #创建全文索引
    mysql> ALTER TABLE mytable ENGINE = MyISAM;		//将表转换为MyISAM
    mysql> ALTER TABLE mytable ADD FULLTEXT(author,title);
    #数据库查询
    SELECT * FROM mytable;
    SELECT author,title FROM mytable;
    SELECT COUNT(*) FROM mytable;			//计数
    SELECT DISTINCT author FROM mytable; 	//去重
    DELETE FROM mytable WHERE title = 'LEARN MYSQL';
    SELECT * FROM mytable WHERE author='Shibingfan';
    SELECT * FROM mytable WHERE author LIKE "Shi%";		//like关键字,模糊匹配
    SELECT author FROM mytable LIMIT 3;					//LIMIT关键字,限定返回的行数
    SELECT author FROM mytable LIMIT 3,1;				//LIMIT关键字,限定从表的什么位置开始返回多少行
    SELECT * FROM mytable WHERE MATCH(author,title) AGAINST('Shi'); //自然语言在FULLTEXT索引列上搜索
    SELECT * FROM mytable WHERE MATCH(author,title) AGAINST('+Shi -bing' IN BOLLEAN MODE);//布尔模式下搜索
    UPDATE mytable SET author='Shibf' WHERE author='shibingfan'; 	//更新数据
    SELECT author,title FROM mytable ORDER BY author;				//排序
    SELECT category,COUNT(author) FROM mytable GROUP BY category;	//分组
    #连接表,新建customers表
    SELECT * FROM mytable,customers WHERE mytable.isbn=customers.isbn;
    SELECT * FROM customers NATURAL JOIN mytable;		//自然连接,将相同列名的表自动合并
    SELECT * FROM customers JOIN mytable ON mytable.isbn=customers.isbn;
    SELECT author,title FROM mytable AS au,ti;			//AS重命名
    SELECT * FROM mytable,customers WHERE mytable.isbn=customers.isbn AND author='shibingfan';
     
     
    #事物
    #事物存储引擎InnoDB
    mysql> CREATE TABLE accounts(number INT, balance FLOAT, PRIMARY KEY(number))ENGINE InnoDB;
    mysql> INSERT INTO accounts(number,balance) VALUES(12345,1025.50);
    mysql> INSERT INTO accounts(number,balance) VALUES(67890,140.00);
    mysql> SELECT * FROM accounts ;
    +--------+---------+
    | number | balance |
    +--------+---------+
    |  12345 |  1025.5 |
    |  67890 |     140 |
    +--------+---------+
    #事物处理BEGIN, COMMIT, ROOLBACK
    mysql> BEGIN;
    mysql> UPDATE accounts SET balance=balance+25.11 WHERE number=12345;
    mysql> COMMIT; 		//确认提交,数据库发生更改
    mysql> ROLLBACK;	//回滚操作,数据库恢复到BEGIN之前的状态
    #使用EXPLAIN,得到查询快照,进而对查询进行优化
    mysql> EXPLAIN SELECT * FROM accounts WHERE number='12345';
    +----+-------------+----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
    | id | select_type | table    | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
    +----+-------------+----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
    |  1 | SIMPLE      | accounts | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
    +----+-------------+----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
    #备份和恢复
    mysql> LOCK TABLES mytable READ,accounts READ;		//锁定表UNLOCK TABLES;
    mysql> mysqldump -u user -ppassword publications;
    mysql> mysqldump -u user -ppassword publications mytable > mytable.sql;
    mysql> mysqldump -u user -ppassword publications --all-database > all_db.sql;
    mysql> mysqldump -u user -ppassword < all_db.sql;
    mysql> mysqldump -u user -ppassword -D publications < mytable.sql;

     

    PHP7协程概念以及实现方法

    php7协程知识点

    多任务 (并行和并发)

    在讲协程之前,先谈谈多进程、多线程、并行和并发。

    对于单核处理器,多进程实现多任务的原理是让操作系统给一个任务每次分配一定的 CPU 时间片,然后中断、让下一个任务执行一定的时间片接着再中断并继续执行下一个,如此反复。

    由于切换执行任务的速度非常快,给外部用户的感受就是多个任务的执行是同时进行的。

    多进程的调度是由操作系统来实现的,进程自身不能控制自己何时被调度,也就是说: 进程的调度是由外层调度器抢占式实现的

    而协程要求当前正在运行的任务自动把控制权回传给调度器,这样就可以继续运行其他任务。这与抢占式的多任务正好相反, 抢占多任务的调度器可以强制中断正在运行的任务, 不管它自己有没有意愿。如果仅依靠程序自动交出控制的话,那么一些恶意程序将会很容易占用全部 CPU 时间而不与其他任务共享。

    协程的调度是由协程自身主动让出控制权到外层调度器实现的

    回到刚才生成器实现 xrange 函数的例子,整个执行过程的交替可以用下图来表示:

    协程可以理解为纯用户态的线程,通过协作而不是抢占来进行任务切换。

    相对于进程或者线程,协程所有的操作都可以在用户态而非操作系统内核态完成,创建和切换的消耗非常低。

    简单的说协程 就是提供一种方法来中断当前任务的执行,保存当前的局部变量,下次再过来又可以恢复当前局部变量继续执行。

    我们可以把大任务拆分成多个小任务轮流执行,如果有某个小任务在等待系统 IO,就跳过它,执行下一个小任务,这样往复调度,实现了 IO 操作和 CPU 计算的并行执行,总体上就提升了任务的执行效率,这也便是协程的意义

    多线程

    在单核下,多线程必定是并发的;

    不过现在的统一进程的多线程是可以运行在多核CPU下,所以可以是并行的

    并发(Concurrency)

    是指能处理多个同时性活动的能力,并发事件之间不一定要同一时刻发生。

    并行(Parallesim)

    是指同时发生的两个并发事件,具有并发的含义,而并发则不一定并行。
    多个操作可以在重叠的时间段内进行。

    并行和并发区别

    并发指的是程序的结构,并行指的是程序运行时的状态

    并行一定是并发的,并行是并发设计的一种

    单线程永远无法达到并行状态

    协程

    协程的支持是在生成器的基础上, 增加了可以回送数据给生成器的功能(调用者发送数据给被调用的生成器函数).

    这就把生成器到调用者的单向通信转变为两者之间的双向通信.

    我们在上篇文章已经讲过了send方法, 下面让我们理解下协程

    同步代码

    在没有涉及到异步执行代码之前,我们的代码都是这样的

    function printNum($max, $caller)
    {
      for ($i=0; $i<$max; $i++ ) {
        echo "调度者:" . $caller . " 打印:" . $i . PHP_EOL;
      }
    }
     
    printNum(3, "caller1");
    printNum(3, "caller2");
     
    # output
    调度者:caller1 打印:0
    调度者:caller1 打印:1
    调度者:caller1 打印:2
    调度者:caller2 打印:0
    调度者:caller2 打印:1
    调度者:caller2 打印:2

    使用协程后改进的代码

    初稿,手动调整生成器执行

    # 本代码手动调整了进程执行代码的顺序,当然本代码实现不用协程也可以,只是利用本流程说明协程作用
    # 生成器给了我们函数中断,协程[生成器send]给了我们重新唤起生成器函数的能力
    function printNumWithGen($max)
    {
      for ($i=0; $i<$max; $i++ ) {
        $res = yield $i;
        echo $res;
      }
    }
     
    $gen1 = printNumWithGen(3);
    $gen2 = printNumWithGen(3);
     
    // 手动执行caller1 再 caller2
    $gen1->send("调度者: caller1 打印:" . $gen1->current() . PHP_EOL);
    $gen2->send("调度者: caller2 打印:" . $gen2->current() . PHP_EOL);
     
    // 手动执行caller1 再 caller2
    $gen1->send("调度者: caller1 打印:" . $gen1->current() . PHP_EOL);
    $gen2->send("调度者: caller2 打印:" . $gen2->current() . PHP_EOL);
     
    // 手动执行caller2 再 caller1
    $gen2->send("调度者: caller2 打印:" . $gen2->current() . PHP_EOL);
    $gen1->send("调度者: caller1 打印:" . $gen1->current() . PHP_EOL);
     
    # output
    调度者: caller1 打印:0
    调度者: caller2 打印:0
    调度者: caller1 打印:1
    调度者: caller2 打印:1
    调度者: caller2 打印:2
    调度者: caller1 打印:2

    总结

    上面案例应该让大家理解了协程设计的意义和如何使用协程

    那么接下去我们为我们的协程自动一个自动调度器(Co自动执行器),无需再手动来中断和恢复了

    PHP7下协程的实现方法

    前言

    相信大家都听说过『协程』这个概念吧。

    但是有些同学对这个概念似懂非懂,不知道怎么实现,怎么用,用在哪,甚至有些人认为yield就是协程!

    我始终相信,如果你无法准确地表达出一个知识点的话,我可以认为你就是不懂。

    我写这篇文章的目的,是想对鸟哥文章做更加充足的补充,毕竟有部分同学的基础还是不够好,看得也是云头雾里的。

    什么是协程

    先搞清楚,什么是协程。

    你可能已经听过『进程』和『线程』这两个概念。

    进程就是二进制可执行文件在计算机内存里的一个运行实例,就好比你的.exe文件是个类,进程就是new出来的那个实例。

    进程是计算机系统进行资源分配和调度的基本单位(调度单位这里别纠结线程进程的),每个CPU下同一时刻只能处理一个进程。

    所谓的并行,只不过是看起来并行,CPU事实上在用很快的速度切换不同的进程。

    进程的切换需要进行系统调用,CPU要保存当前进程的各个信息,同时还会使CPUCache被废掉。

    所以进程切换不到费不得已就不做。

    那么怎么实现『进程切换不到费不得已就不做』呢?

    首先进程被切换的条件是:进程执行完毕、分配给进程的CPU时间片结束,系统发生中断需要处理,或者进程等待必要的资源(进程阻塞)等。你想下,前面几种情况自然没有什么话可说,但是如果是在阻塞等待,是不是就浪费了。

    其实阻塞的话我们的程序还有其他可执行的地方可以执行,不一定要傻傻的等!

    所以就有了线程。

    线程简单理解就是一个『微进程』,专门跑一个函数(逻辑流)。

    所以我们就可以在编写程序的过程中将可以同时运行的函数用线程来体现了。

    线程有两种类型,一种是由内核来管理和调度。

    我们说,只要涉及需要内核参与管理调度的,代价都是很大的。这种线程其实也就解决了当一个进程中,某个正在执行的线程遇到阻塞,我们可以调度另外一个可运行的线程来跑,但是还是在同一个进程里,所以没有了进程切换。

    还有另外一种线程,他的调度是由程序员自己写程序来管理的,对内核来说不可见。这种线程叫做『用户空间线程』。

    协程可以理解就是一种用户空间线程。

    协程,有几个特点:

    • 协同,因为是由程序员自己写的调度策略,其通过协作而不是抢占来进行切换
    • 在用户态完成创建,切换和销毁
    • ⚠️ 从编程角度上看,协程的思想本质上就是控制流的主动让出(yield)和恢复(resume)机制
    • 迭代器经常用来实现协程

    说到这里,你应该明白协程的基本概念了吧?

    PHP实现协程

    一步一步来,从解释概念说起!

    可迭代对象

    PHP5提供了一种定义对象的方法使其可以通过单元列表来遍历,例如用foreach语句。

    你如果要实现一个可迭代对象,你就要实现Iterator接口:

    <?php
    class MyIterator implements Iterator
    {
     private $var = array();
     public function __construct($array)
     {
      if (is_array($array)) {
       $this->var = $array;
      }
     }
     public function rewind() {
      echo "rewinding\n";
      reset($this->var);
     }
     public function current() {
      $var = current($this->var);
      echo "current: $var\n";
      return $var;
     }
     public function key() {
      $var = key($this->var);
      echo "key: $var\n";
      return $var;
     }
     public function next() {
      $var = next($this->var);
      echo "next: $var\n";
      return $var;
     }
     public function valid() {
      $var = $this->current() !== false;
      echo "valid: {$var}\n";
      return $var;
     }
    }
    $values = array(1,2,3);
    $it = new MyIterator($values);
    foreach ($it as $a => $b) {
     print "$a: $b\n";
    }

    生成器

    可以说之前为了拥有一个能够被foreach遍历的对象,你不得不去实现一堆的方法,yield关键字就是为了简化这个过程。

    生成器提供了一种更容易的方法来实现简单的对象迭代,相比较定义类实现Iterator接口的方式,性能开销和复杂性大大降低。

    <?php
    function xrange($start, $end, $step = 1) {
     for ($i = $start; $i <= $end; $i += $step) {
      yield $i;
     }
    }
    foreach (xrange(1, 1000000) as $num) {
     echo $num, "\n";
    }

    记住,一个函数中如果用了yield,他就是一个生成器,直接调用他是没有用的,不能等同于一个函数那样去执行!

    所以,yield就是yield,下次谁再说yield是协程,我肯定把你xxxx。

    PHP协程

    前面介绍协程的时候说了,协程需要程序员自己去编写调度机制,下面我们来看这个机制怎么写。

    0)生成器正确使用

    既然生成器不能像函数一样直接调用,那么怎么才能调用呢?

    方法如下:

    • foreach他
    • send($value)
    • current / next...

    1)Task实现

    Task就是一个任务的抽象,刚刚我们说了协程就是用户空间协程,线程可以理解就是跑一个函数。

    所以Task的构造函数中就是接收一个闭包函数,我们命名为coroutine。

    /**
     * Task任务类
     */
    class Task
    {
     protected $taskId;
     protected $coroutine;
     protected $beforeFirstYield = true;
     protected $sendValue;
    
     /**
      * Task constructor.
      * @param $taskId
      * @param Generator $coroutine
      */
     public function __construct($taskId, Generator $coroutine)
     {
      $this->taskId = $taskId;
      $this->coroutine = $coroutine;
     }
     /**
      * 获取当前的Task的ID
      * 
      * @return mixed
      */
     public function getTaskId()
     {
      return $this->taskId;
     }
     /**
      * 判断Task执行完毕了没有
      * 
      * @return bool
      */
     public function isFinished()
     {
      return !$this->coroutine->valid();
     }
     /**
      * 设置下次要传给协程的值,比如 $id = (yield $xxxx),这个值就给了$id了
      * 
      * @param $value
      */
     public function setSendValue($value)
     {
      $this->sendValue = $value;
     }
     /**
      * 运行任务
      * 
      * @return mixed
      */
     public function run()
     {
      // 这里要注意,生成器的开始会reset,所以第一个值要用current获取
      if ($this->beforeFirstYield) {
       $this->beforeFirstYield = false;
       return $this->coroutine->current();
      } else {
       // 我们说过了,用send去调用一个生成器
       $retval = $this->coroutine->send($this->sendValue);
       $this->sendValue = null;
       return $retval;
      }
     }
    }

    2)Scheduler实现

    接下来就是Scheduler这个重点核心部分,他扮演着调度员的角色。

    /**
     * Class Scheduler
     */
    Class Scheduler
    {
     /**
      * @var SplQueue
      */
     protected $taskQueue;
     /**
      * @var int
      */
     protected $tid = 0;
    
     /**
      * Scheduler constructor.
      */
     public function __construct()
     {
      /* 原理就是维护了一个队列,
       * 前面说过,从编程角度上看,协程的思想本质上就是控制流的主动让出(yield)和恢复(resume)机制
       * */
      $this->taskQueue = new SplQueue();
     }
     /**
      * 增加一个任务
      *
      * @param Generator $task
      * @return int
      */
     public function addTask(Generator $task)
     {
      $tid = $this->tid;
      $task = new Task($tid, $task);
      $this->taskQueue->enqueue($task);
      $this->tid++;
      return $tid;
     }
     /**
      * 把任务进入队列
      *
      * @param Task $task
      */
     public function schedule(Task $task)
     {
      $this->taskQueue->enqueue($task);
     }
     /**
      * 运行调度器
      */
     public function run()
     {
      while (!$this->taskQueue->isEmpty()) {
       // 任务出队
       $task = $this->taskQueue->dequeue();
       $res = $task->run(); // 运行任务直到 yield
    
       if (!$task->isFinished()) {
        $this->schedule($task); // 任务如果还没完全执行完毕,入队等下次执行
       }
      }
     }
    }

    这样我们基本就实现了一个协程调度器。

    你可以使用下面的代码来测试:

    <?php
    function task1() {
     for ($i = 1; $i <= 10; ++$i) {
      echo "This is task 1 iteration $i.\n";
      yield; // 主动让出CPU的执行权
     }
    }
    function task2() {
     for ($i = 1; $i <= 5; ++$i) {
      echo "This is task 2 iteration $i.\n";
      yield; // 主动让出CPU的执行权
     }
    }
    $scheduler = new Scheduler; // 实例化一个调度器
    $scheduler->newTask(task1()); // 添加不同的闭包函数作为任务
    $scheduler->newTask(task2());
    $scheduler->run();

    关键说下在哪里能用得到PHP协程。

    function task1() {
      /* 这里有一个远程任务,需要耗时10s,可能是一个远程机器抓取分析远程网址的任务,我们只要提交最后去远程机器拿结果就行了 */
      remote_task_commit();
      // 这时候请求发出后,我们不要在这里等,主动让出CPU的执行权给task2运行,他不依赖这个结果
      yield;
      yield (remote_task_receive());
      ...
    } 
    function task2() {
     for ($i = 1; $i <= 5; ++$i) {
      echo "This is task 2 iteration $i.\n";
      yield; // 主动让出CPU的执行权
     }
    }

    这样就提高了程序的执行效率。

    关于『系统调用』的实现,鸟哥已经讲得很明白,我这里不再说明。

    3)协程堆栈

    鸟哥文中还有一个协程堆栈的例子。

    我们上面说过了,如果在函数中使用了yield,就不能当做函数使用。

    所以你在一个协程函数中嵌套另外一个协程函数:

    <?php
    function echoTimes($msg, $max) {
     for ($i = 1; $i <= $max; ++$i) {
      echo "$msg iteration $i\n";
      yield;
     }
    }
    function task() {
     echoTimes('foo', 10); // print foo ten times
     echo "---\n";
     echoTimes('bar', 5); // print bar five times
     yield; // force it to be a coroutine
    }
    $scheduler = new Scheduler;
    $scheduler->newTask(task());
    $scheduler->run();

    这里的echoTimes是执行不了的!所以就需要协程堆栈。

    不过没关系,我们改一改我们刚刚的代码。

    把Task中的初始化方法改下,因为我们在运行一个Task的时候,我们要分析出他包含了哪些子协程,然后将子协程用一个堆栈保存。(C语言学的好的同学自然能理解这里,不理解的同学我建议去了解下进程的内存模型是怎么处理函数调用)

     /**
      * Task constructor.
      * @param $taskId
      * @param Generator $coroutine
      */
     public function __construct($taskId, Generator $coroutine)
     {
      $this->taskId = $taskId;
      // $this->coroutine = $coroutine;
      // 换成这个,实际Task->run的就是stackedCoroutine这个函数,不是$coroutine保存的闭包函数了
      $this->coroutine = stackedCoroutine($coroutine); 
     }

    当Task->run()的时候,一个循环来分析:

    /**
     * @param Generator $gen
     */
    function stackedCoroutine(Generator $gen)
    {
     $stack = new SplStack;
     // 不断遍历这个传进来的生成器
     for (; ;) {
      // $gen可以理解为指向当前运行的协程闭包函数(生成器)
      $value = $gen->current(); // 获取中断点,也就是yield出来的值
      if ($value instanceof Generator) {
       // 如果是也是一个生成器,这就是子协程了,把当前运行的协程入栈保存
       $stack->push($gen);
       $gen = $value; // 把子协程函数给gen,继续执行,注意接下来就是执行子协程的流程了
       continue;
      }
      // 我们对子协程返回的结果做了封装,下面讲
      $isReturnValue = $value instanceof CoroutineReturnValue; // 子协程返回`$value`需要主协程帮忙处理 
      if (!$gen->valid() || $isReturnValue) {
       if ($stack->isEmpty()) {
        return;
       }
       // 如果是gen已经执行完毕,或者遇到子协程需要返回值给主协程去处理
       $gen = $stack->pop(); //出栈,得到之前入栈保存的主协程
       $gen->send($isReturnValue ? $value->getValue() : NULL); // 调用主协程处理子协程的输出值
       continue;
      }
      $gen->send(yield $gen->key() => $value); // 继续执行子协程
     }
    }

    然后我们增加echoTime的结束标示:

    class CoroutineReturnValue {
     protected $value;
     
     public function __construct($value) {
      $this->value = $value;
     }
     // 获取能把子协程的输出值给主协程,作为主协程的send参数
     public function getValue() {
      return $this->value;
     }
    }
    function retval($value) {
     return new CoroutineReturnValue($value);
    }

    然后修改echoTimes:

    function echoTimes($msg, $max) {
     for ($i = 1; $i <= $max; ++$i) {
      echo "$msg iteration $i\n";
      yield;
     }
     yield retval(""); // 增加这个作为结束标示
    }

    Task变为:

    function task1()
    {
     yield echoTimes('bar', 5);
    }

    这样就实现了一个协程堆栈,现在你可以举一反三了。

    4)PHP7中yield from关键字

    PHP7中增加了yield from,所以我们不需要自己实现携程堆栈,真实太好了。

    把Task的构造函数改回去:

     public function __construct($taskId, Generator $coroutine)
     {
      $this->taskId = $taskId;
      $this->coroutine = $coroutine;
      // $this->coroutine = stackedCoroutine($coroutine); //不需要自己实现了,改回之前的
     }

    echoTimes函数:

    function echoTimes($msg, $max) {
     for ($i = 1; $i <= $max; ++$i) {
      echo "$msg iteration $i\n";
      yield;
     }
    }

    task1生成器:

    function task1()
    {
     yield from echoTimes('bar', 5);
    }

    这样,轻松调用子协程。

    总结

    这下应该明白怎么实现PHP协程了吧?

    实例分析PHP7的异常

    PHP 7 异常用于向下兼容及增强旧的assert()函数。它能在生产环境中实现零成本的断言,并且提供抛出自定义异常及错误的能力。

    老版本的API出于兼容目的将继续被维护,assert()现在是一个语言结构,它允许第一个参数是一个表达式,而不仅仅是一个待计算的 string或一个待测试的boolean。

    assert() 配置

    配置项默认值可选值
    zend.assertions1 1 - 生成和执行代码 (开发模式) 0 - 生成代码,但在执行时跳过它 -1 - 不生成代码 (生产环境)
    assert.exception0 1 - 断言失败时抛出,可以抛出异常对象,如果没有提供异常,则抛出 AssertionError 对象实例。 0 - 使用或生成 Throwable, 仅仅是基于对象生成的警告而不是抛出对象(与 PHP 5 兼容)

    参数

    assertion

    断言。在 PHP 5 中,是一个用于执行的字符串或者用于测试的布尔值。在 PHP 7 中,可以是一个返回任何值的表达式, 它将被执行结果用于指明断言是否成功。

    description

    如果 assertion 失败了,选项 description 将会包括在失败信息里。

    exception

    在 PHP 7 中,第二个参数可以是一个 Throwable 对象,而不是一个字符串,如果断言失败且启用了 assert.exception 该对象将被抛出。

    实例

    将 zend.assertions 设置为 0:

    <?php 
    ini_set('zend.assertions', 0); 
    
    assert(true == false); 
    echo 'Hi!'; 
    ?>

    以上程序执行输出结果为:

    Hi!

    将 zend.assertions 设置为 1,assert.exception 设置为 1:

    <?php 
    ini_set('zend.assertions', 1); 
    ini_set('assert.exception', 1); 
    
    assert(true == false); 
    echo 'Hi!'; 
    ?>

    以上程序执行输出结果为:

    Fatal error: Uncaught AssertionError: assert(true == false) in -:2
    Stack trace:
    #0 -(2): assert(false, 'assert(true == ...')
    #1 {main}
      thrown in - on line 2

     

    上一篇:PHP7内核剖析  下一篇:PHP开发实例大全:提高卷

    展开 +

    收起 -

     
    PHP编程 相关电子书
    关于PHP编程的学习笔记

    Copyright 2018-2020 xz577.com 码农之家

    电子书资源由网友、会员提供上传,本站记录提供者的基本信息及资源来路

    鸣谢: “ 码小辫 ” 公众号提供回调API服务、“ 脚本CDN ”提供网站加速(本站寻求更多赞助支持)

    版权投诉 / 书籍推广 / 赞助:520161757@qq.com

    上传资源(网友、会员均可提供)

    查看最新会员资料及资源信息