标签分类 热门分类
当前位置:首页 > > PHP编程电子书网盘下载
深入PHP:面向对象、模式与实践 深入PHP:面向对象、模式与实践
码小辫

码小辫 提供上传

资源
34
粉丝
2
喜欢
292
评论
17

    深入PHP:面向对象、模式与实践 PDF 中文完整第3版

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

    给大家带来的一篇关于PHP编程相关的电子书资源,介绍了关于PHP、面向对象、实践方面的内容,本书是由人民邮电出版社出版,格式为PDF,资源大小41.3 MB,Matt Zandstra编写,目前豆瓣、亚马逊、当当、京东等电子书综合评分为:9.1

  • 深入PHP:面向对象、模式与实践 PDF 下载
  • 下载地址:https://pan.baidu.com/s/1ITFsjs1fX2vwbc3N72Ke0
  • 分享码:e757
  • 读者评价

    php 面向对象开发了一年左右了。但是没有十分具体细化的学习思想。希望这本书可以更好的理解和应用。这本PHP书籍还是不错的,里面的内容对于初学者来说比较有用。

    这书跟踪一年多了,貌似每次满减活动都没份参与,要么就是瞬间没货,还好这次抢到优惠券,也算减了不少。之前入门对象时在PDF上看过前面几章,讲解比较透彻,买纸书回来方便划重点和复习。很多基础知识,系统性的讲了,用的都是高大尚的名词有些基础的买来看看系统的整理自己的知识体系还是不错的。

    内容比较深入的,不建议初学者入手,并且这本书比较老了,composer已经取代了pear,git也基本取代了svn。但是前面讲的思想部分是不会过时的。

    每个段落先提出问题, 给出实现, 并讨论成效,
    对于OO入门有一定帮助,能够帮助开拓思路,对OO老鸟有参考价值,可以换换空气,让脑子清空一下,听听别人说什么,对开发新程序有一定作用
    内容并不能说新颖,毕竟内容已经是2007的了,不过设计模式并不会随着技术的改进而有多少变化,毕竟理论层面上不会有什么改变的东西
    该书的第三版早已上市,中文版什么时候出现不得而知,该书的中文翻译算是我看过的php书籍中质量较高的一本,虽然是第二版,我并不认为这会妨碍你阅读第二版的中文版,好书就是好书,过几年仍然会是,推荐阅读

    另外一本是PHP in Action。
    PHP架构中常用的设计模式不多,书中基本都谈到了。我觉得学习设计模式最好是和框架一起进行,一个是理论,一个是实践,而且流行的框架基本代表了设计的最新思想,设计模式没有好坏之分,所以有空都应该学学。

    看到有人说这本书没有达到书名的目标,可能“深入”这个词让他产生的误解了吧,这本书更像一本实实在在的PHP进阶指南。
    本书全文分为三个方面:PHP面向对象思想,PHP设计模式,PHP实践。这三个方面对于初级PHP工程师进阶来说都是很重要的内容。
    PHP OOP,一般非直接通过PHP入门编程的童鞋对OOP都应该有一定的理解,这些内容对于PHP草根工程师更有帮助。此外,这一部分对PHP的一些OOP特性进行了阐述。
    PHP 设计模式,这一块介绍了比较常用的设计模式,并且用PHP的语法进行了实现,对于对设计模式理解不到位的童鞋有帮助。此外,这一部分还涉及一些架构方面的内容,但是浅尝辄止,有兴趣的童鞋可以看一看《企业应用架构模式》一书
    PHP 实践,对于不了解的童鞋帮助很大,对于有所了解的童鞋帮助很小。这一块比较成体系的介绍了PHP开发的相关工具,仔细看一遍还是有一些收获的。不过,关于包管理一节用到的PEAR包未免太落后了,可以不用学,把精力用来学Composer是更优的选择。至于版本管理,SVN已经落伍了,不过学习一下倒也无妨,本书下一版本已经把SVN换成Git了。

    过程:花了一个月的时间把书中的主要部分,‘面向对象’和‘模式’部分。第三部分我只是选读了我认为比较有用的‘单元测试’章节。

    翻译: 不错。没有咬文嚼字,通俗易懂,都是按照中国程序员对专属名词的普遍认识所翻译的。
    内容部分-‘面向對象’:在读面向对象部分时,我对本书还是抱有很大希望了,东西讲的比较深入,虽然有错误的地方,但是结构还是比较清晰。作者没有把读者当作是刚刚进入php世界的小孩子,而是认为读者有了一定的工作经验。所以在作者花了更多的精力来,讲解面向对象的高级特性,还为了给最重要的‘模式’部分铺路,讲了一下’对象与设计‘。
    内容部分-‘模式’: 我对第7-11章节还是比较满意的,作者没有太纠缠于任何一个复杂的设计模式,从一些原则开始讲起,譬如,‘组合与继承’,’解耦‘, ’聚合‘,‘正交’等等。作者将这些零散而又重要的知识点一一呈现,并且细细讲清楚说明白,我还是觉得蛮受用的。一条’针对接口编程,而不是针对实现编程‘贯彻始终。 后面几个章节的各个小模式不管有用没用讲的也算是清晰(都画了UML)。但是,但是,个人认为最最恶心的第12,13章节让我彻底对这本书失望至极,如果说企业模式章节,是为了说明MVC结构的好,那我觉得还勉强说的过去,毕竟有对前面章节的一些知识点的升华,但是‘数据库模式’章节,我就真的不能忍受了。一上来就搞了一个‘数据库映射器’,我不知道该不该称其为模式。其作用是将数据库表中的行和列转换成项目中的数据结构,这么看感觉还是蛮有意义的,但是使用的方法比较复杂,最可气的是当你废了很大功夫搞懂之后,这一章的后半部分开始说这个方法有哪里哪里不好,我们应该把它细化,而使用的方法针对性很强,灵活性不高,感觉上只有‘解耦和’这个作者的思想可以运用到实战中,书中提到的代码我看就算了吧。
    都有更好的替代品:想获得面向对象的知识,可以看《php手册》。想获得模式的知识,可以看《Head First设计模式》。而第三部分“时间”的各个工具,我想也应该和phpunit一样有官方手册可以看吧。没有必要花时间在这本书上面。除非你已经很了解我上面提到的经典书籍里面的知识,想看看如何用php来实现模式的各个概念的,那。。。就看看它吧。

    读第一遍读到数据库模式, 感觉吃不消了, 所以跳过去直接读后面的实践部分. 目前在读第二遍, 希望这次能吃透作者讲的数据库模式.
    这绝对是一本每读一遍都会受益一便的好书, 虽然书中讲的各种模式目前看来没有应用到工作中的机会, 但是通过作者的讲解, 你会看到这些模式一旦应用到项目中, 会给整个代码的架构和质量带来多大的提升.
    很久之前就想买一本介绍模式的书, 看亚马逊的评论, 有三个选择, 四人帮的设计模式, head first desgin pattern 和这一本. 如果你是 php 程序员, 有一定的代码经验, 想了解一下设计模式, 这本书应该是你第一本入手的书. 四人帮的设计模式应该是你的进阶书. 而我个人来说不是那种中意类似 看读学XX 的读者, 如果你是的话可能 head first design pattern 比较适合你.
    该书的结构很清晰, 作者行文也很流畅, 虽然有个别语句可能读第一遍不很明白, 但那很可能是你没有透彻理解当前的上下文, 返回多读几遍便能了解作者的用意. 翻译质量算很高的了. 但是其中的错误(不知是印刷错误还是作者的错误还是译者的错误)有点多, 我目前都有一个长长的列表,打算去官方勘误页面提交(不过貌似现在打不开...).
    对象, 模式 和实践是本书的三个核心部分, 有人说第三部分不是很必要, 但是对于项目经验不是很丰富的程序员来说, 第三部分绝对会对你项目开发流程的认识拓展很多, 个人觉得是不能忽略的部分.
    对象部分对 php 对面向对象的支持和实现讲解的很透彻, 不过重点是在实现, 而不是设计, 如果你需要面向对象设计方面的知识, 这部分是远远不够的, 但是对于 PHP 面向对象的实现, 这部分应该是你需要读得唯一的东西了.
    模式部分是本书的精华部分, 我个人也在网上或视频教程中学过设计模式, 但是还是感觉这本书对于模式的讲解最为透彻. 包括项目中遇到的真实问题, 可能的解决方案, 每种解决方案的不足之处, 引入设计模式, 讲解模式的实现以及实现结果的优势和不足之处, 实现的可能变种...等等方方面面. 全面而透彻.
    5星推荐给任何想要对 OOP 有所了解并应用到项目中的程序员.

    设计模式一直以来很难懂,之前遇到很大的瓶颈,买回来这本书,读起来基本一目十行,不是因为内容太简单,而是该做的我都已经做过了,只是在模式上认识还不够清晰,概念体系不完整,所以想看书补补,觉得这本书设计模式部分写的非常好。甚至这本书应该只保留对象和设计模式部分,其它都是多余的。推荐已经了解并且实践过的同行们都来一读,模式这种东西基本上不会发生太大改变,这本书里描述的也算是非常精准了。
    至于其它部分,变化太快,比如工具中,phar没有讲到,发展历程中,错误的预计了php的发展方向:php6搁浅了,phpng(也就是php7)将在2015年推出第一个版本。不过也无怪于作者,毕竟写这本书的时候鸟哥都没有在github上提交过php的源码。去掉这些多余的部分这本书简直可以堪称完美,或者保留发展历史,把展望去掉,太不好展望了,需要充分了解开源社区中风起云涌的变化。

    编辑推荐

    赞德斯编著的《深入PHP:面向对象、模式与实践(第3版)》全面深入地剖析了面向对象的PHP编程与设计。书中首先介绍了PHP的对象特性(包括抽象类、反射、接口和错误处理等)及可帮助开发人员了解类、对象和方法的对象工具,然后介绍了设计模式。阐述了模式的概念,展示了如何在PHP中实现一些关键的模式。并用专门的章节介绍了企业模式和数据库模式。最后,本书围绕PHP应用程序开发。详细介绍了一批极为实用的辅助开发工具。讨论了具有普遍意义的开发实践。另外,这一版中还新增了闭包、命名空间、持续集成等内容。本书适合每位PHP开发人员进阶参考。可帮助他们掌握PHP面向对象设计和开发的精髓,并最终跻身高端PHP开发人员之列。

    作者简介

    Mart Zandstra,Yahoo公司的高级程序员,曾从事过教师等工作,著有Sams Teach Yourself PHP in 24 Hours等书,还为Linux Magazine、IBMD DeveloperWorks、Zend.com和bdz-corlsuit.com写过PHP方面的文章。

    内容简介

    《深入PHP:面向对象、模式与实践(第3版)》是PHP专家经典力作的新版本。书中主要介绍了如何使用面向对象技术和设计模式编写稳定的、可维护的代码,如何使用Subversion管理多个开发人员,如何使用Phing和PEAR进行构建和安装,以及将构建和测试过程自动化的策略,包括持续集成。

    《深入PHP:面向对象、模式与实践(第3版)》适合中高级PHP程序员阅读。

    内容节选

    单例模式

    生成对象的问题和解决方案

    abstract class Employee
    {
        protected $name;
    
        public function __construct($name)
        {
            $this->name = $name;
        }
    
        abstract function fire();
    }
    
    class Minion extends Employee
    {
        function fire()
        {
            print "{$this->name}:I'll clear my desk\n";
        }
    }
    
    class NastyBoss
    {
        private $employees = array();
    
        function addEmployee($employeeName)
        {
            $this->employees[] = new Minion($employeeName);
        }
    
        function projectFail()
        {
            if (count($this->employees) > 0) {
                $emp = array_pop($this->employees);
                $emp->fire();
            }
        }
    }
    
    $boss = new NastyBoss();
    $boss->addEmployee("harry");
    $boss->addEmployee("bob");
    $boss->addEmployee("mary");
    $boss->projectFail();

    由于在NastyBoss类中直接实例化Minion对象,代码的灵活性受到限制。如果NastyBoss对象可以使用Employee类的任何实例,那么代码在运行时就能应对更多特殊的Employee。如下类图:

    如果NastyBoss类不实例化Minion对象,那么Minion对象从何而来?在方法声明中限制参数类型来巧妙避开这个问题,然后除了在测试时实例化对象,在其他时候尽量避免提及。

    如果是这里存在一个原则的话,那便是“把对象实例化的工作委托出来”。我们可以委托一个独立的类或方法来生成Employee对象。

    abstract class Employee
    {
        protected $name;
        private static $types = array("minion", 'clueup', 'wellconnected');
    
        static function recruit($name)
        {
            $num = rand(1, count(self::$types)) - 1;
            $class = self::$types[$num];
            return new $class($name);
        }
    
        public function __construct($name)
        {
            $this->name = $name;
        }
    
        abstract function fire();
    }
    
    class wellConnected extends Employee
    {
        function fire()
        {
            print "{$this->name}: i'll call my dad\n";
        }
    }
    
    $boss = new NastyBoss();
    $boss->addEmployee(Employee::recruit("harry"));
    $boss->addEmployee(Employee::recruit("bob"));
    $boss->addEmployee(Employee::recruit("mary"));

    目录

    • 第一部分 介绍
    • 第1章 PHP:设计与管理
    • 1.1 问题
    • 1.2 PHP 和其他语言
    • 1.3 关于本书
    • 1.3.1 对象
    • 1.3.2 模式
    • 1.3.3 实践
    • 1.3.4 第3版新增内容
    • 1.4 小结
    • 第二部分 对象
    • 第2章 PHP与对象
    • 2.1 PHP对象的偶然成功
    • 2.1.1 最初:PHP/FI
    • 2.1.2 语法糖:PHP 3
    • 2.1.3 一场静悄悄的革命:PHP 4
    • 2.1.4 拥抱改变:PHP 5
    • 2.2 走向未来:PHP 6
    • 2.3 拥护和疑虑:关于对象的争辩
    • 2.4 小结
    • 第3章 对象基础
    • 3.1 类和对象
    • 3.1.1 编写第一个类
    • 3.1.2 第一个对象(或两个)
    • 3.2 设置类中的属性
    • 3.3 使用方法
    • 3.4 参数和类型
    • 3.4.1 基本类型
    • 3.4.2 获得提示:对象类型
    • 3.5 继承
    • 3.5.1 继承问题
    • 3.5.2 使用继承
    • 3.5.3 public、private、protected:管理类的访问
    • 3.6 小结
    • 第4章 高级特性
    • 4.1 静态方法和属性
    • 4.2 常量属性
    • 4.3 抽象类
    • 4.4 接口
    • 4.5 延迟静态绑定:static关键字
    • 4.6 错误处理
    • 4.7 Final 类和方法
    • 4.8 使用拦截器
    • 4.9 析构方法
    • 4.10 使用__clone()复制对象
    • 4.11 定义对象的字符串值
    • 4.12 回调、匿名函数和闭包
    • 4.13 小结
    • 第5章 对象工具
    • 5.1 PHP和包
    • 5.1.1 PHP包和命名空间
    • 5.1.2 自动加载
    • 5.2 类函数和对象函数
    • 5.2.1 查找类
    • 5.2.2 了解对象或类
    • 5.2.3 了解类中的方法
    • 5.2.4 了解类属性
    • 5.2.5 了解继承
    • 5.2.6 方法调用
    • 5.3 反射API
    • 5.3.1 入门
    • 5.3.2 开始行动
    • 5.3.3 检查类
    • 5.3.4 检查方法
    • 5.3.5 检查方法参数
    • 5.3.6 使用反射API
    • 5.4 小结
    • 第6章 对象与设计
    • 6.1 代码设计的定义
    • 6.2 面向对象设计和过程式编程
    • 6.2.1 职责
    • 6.2.2 内聚
    • 6.2.3 耦合
    • 6.2.4 正交
    • 6.3 选择类
    • 6.4 多态
    • 6.5 封装
    • 6.6 忘记细节
    • 6.7 4 个方向标
    • 6.7.1 代码重复
    • 6.7.2 类知道的太多
    • 6.7.3 万能的类
    • 6.7.4 条件语句
    • 6.8 UML
    • 6.8.1 类图
    • 6.8.2 时序图
    • 6.9 小结
    • 第三部分 模式
    • 第7章 什么是设计模式?为何使用它们
    • 7.1 什么是设计模式
    • 7.2 设计模式概览
    • 7.2.1 命名
    • 7.2.2 问题
    • 7.2.3 解决方案
    • 7.2.4 效果
    • 7.3 《设计模式》格式
    • 7.4 为什么使用设计模式
    • 7.4.1 一个设计模式定义了一个问题
    • 7.4.2 一个设计模式定义了一个解决方案
    • 7.4.3 设计模式是语言无关的
    • 7.4.4 模式定义了一组词汇
    • 7.4.5 模式是经过测试的
    • 7.4.6 模式是为协作而设计的
    • 7.4.7 设计模式促进良好设计
    • 7.5 PHP与设计模式
    • 7.6 小结
    • 第8章 模式原则
    • 8.1 模式的启示
    • 8.2 组合与继承
    • 8.2.1 问题
    • 8.2.2 使用组合
    • 8.3 解耦
    • 8.3.1 问题
    • 8.3.2 降低耦合
    • 8.4 针对接口编程,而不是针对实现编程
    • 8.5 变化的概念
    • 8.6 父子关系
    • 8.7 模式
    • 8.7.1 用于生成对象的模式
    • 8.7.2 用于组织对象和类的模式
    • 8.7.3 面向任务的模式
    • 8.7.4 企业模式
    • 8.7.5 数据库模式
    • 8.8 小结
    • 第9章 生成对象
    • 9.1 生成对象的问题和解决方法
    • 9.2 单例模式
    • 9.2.1 问题
    • 9.2.2 实现
    • 9.2.3 结果
    • 9.3 工厂方法模式
    • 9.3.1 问题
    • 9.3.2 实现
    • 9.3.3 结果
    • 9.4 抽象工厂模式
    • 9.4.1 问题
    • 9.4.2 实现
    • 9.4.3 结果
    • 9.4.4 原型模式
    • 9.4.5 问题
    • 9.4.6 实现
    • 9.5 某些模式的骗术
    • 9.6 小结
    • 第10章 让面向对象编程更加灵活的模式
    • 10.1 构造可灵活创建对象的类
    • 10.2 组合模式
    • 10.2.1 问题
    • 10.2.2 实现
    • 10.2.3 效果
    • 10.2.4 组合模式小结
    • 10.3 装饰模式
    • 10.3.1 问题
    • 10.3.2 实现
    • 10.3.3 效果
    • 10.4 外观模式
    • 10.4.1 问题
    • 10.4.2 实现
    • 10.4.3 效果
    • 10.5 小结
    • 第11章 执行及描述任务
    • 11.1 解释器模式
    • 11.1.1 问题
    • 11.1.2 实现
    • 11.1.3 解释器的问题
    • 11.2 策略模式
    • 11.2.1 问题
    • 11.2.2 实现
    • 11.3 观察者模式
    • 11.4 访问者模式
    • 11.4.1 问题
    • 11.4.2 实现
    • 11.4.3 访问者模式的问题
    • 11.5 命令模式
    • 11.5.1 问题
    • 11.5.2 实现
    • 11.6 小结
    • 第12章 企业模式
    • 12.1 架构概述
    • 12.1.1 模式
    • 12.1.2 应用程序和层
    • 12.2 企业架构之外的基础模式
    • 12.2.1 注册表
    • 12.2.2 实现
    • 12.3 表现层
    • 12.3.1 前端控制器
    • 12.3.2 应用控制器
    • 12.3.3 页面控制器
    • 12.3.4 模板视图和视图助手
    • 12.4 业务逻辑层
    • 12.4.1 事务脚本
    • 12.4.2 领域模型
    • 12.5 小结
    • 第13章 数据库模式
    • 13.1 数据层
    • 13.2 数据映射器
    • 13.2.1 问题
    • 13.2.2 实现
    • 13.2.3 效果
    • 13.3 标识映射
    • 13.3.1 问题
    • 13.3.2 实现
    • 13.3.3 效果
    • 13.4 工作单元
    • 13.4.1 问题
    • 13.4.2 实现
    • 13.4.3 效果
    • 13.4.4 延迟加载
    • 13.4.5 问题
    • 13.4.6 实现
    • 13.4.7 效果
    • 13.5 领域对象工厂
    • 13.5.1 问题
    • 13.5.2 实现
    • 13.5.3 效果
    • 13.6 标识对象
    • 13.6.1 问题
    • 13.6.2 实现
    • 13.6.3 效果
    • 13.7 选择工厂和更新工厂模式
    • 13.7.1 问题
    • 13.7.2 实现
    • 13.7.3 效果
    • 13.8 数据映射器中剩下些什么
    • 13.9 小结
    • 第四部分 实践
    • 第14章 良好和糟糕的实践
    • 14.1 超越代码
    • 14.2 借一个轮子
    • 14.3 合作愉快
    • 14.4 为你的代码插上双翼
    • 14.5 文档
    • 14.6 测试
    • 14.7 持续集成
    • 14.8 小结
    • 第15章 PEAR和Pyrus
    • 15.1 什么是PEAR
    • 15.2 了解Pyrus
    • 15.3 安装PEAR包
    • 15.4 使用PEAR包
    • 15.5 创建自己的PEAR包
    • 15.5.1 package.xml
    • 15.5.2 package.xml的组成
    • 15.5.3 contents元素
    • 15.5.4 依赖
    • 15.5.5 使用phprelease进行灵活的自定义安装
    • 15.5.6 准备发布包
    • 15.5.7 创建自己的PEAR频道
    • 15.6 小结
    • 第16章 用phpDocumentor生成文档
    • 16.1 为什么要使用文档
    • 16.2 安装
    • 16.3 生成文档
    • 16.4 DocBlock注释
    • 16.5 类的文档
    • 16.6 文件的文档
    • 16.7 属性的文档
    • 16.8 方法的文档
    • 16.9 在文档中创建链接
    • 16.10 小结
    • 第17章 使用Subversion进行版本控制
    • 17.1 为什么要使用版本控制
    • 17.2 获得Subversion
    • 17.3 配置Subversion代码库
    • 17.4 开始项目
    • 17.5 更新和提交
    • 17.6 增加和删除文件及目录
    • 17.6.1 添加文件
    • 17.6.2 删除文件
    • 17.6.3 添加目录
    • 17.6.4 删除目录
    • 17.7 标记和导出项目
    • 17.7.1 标记项目
    • 17.7.2 导出项目
    • 17.8 创建项目分支
    • 17.9 小结
    • 第18章 使用PHPUnit进行测试
    • 18.1 功能测试与单元测试
    • 18.2 手工测试
    • 18.3 引入PHPUnit
    • 18.3.1 创建测试用例
    • 18.3.2 断言方法
    • 18.3.3 测试异常
    • 18.3.4 运行测试套件
    • 18.3.5 约束
    • 18.3.6 模拟与桩
    • 18.3.7 失败是成功之母
    • 18.4 编写Web测试
    • 18.4.1 为测试重构Web应用
    • 18.4.2 简单的Web测试
    • 18.4.3 Selenium
    • 18.5 警告
    • 18.6 小结
    • 第19章 用Phing实现项目的自动构建
    • 19.1 什么是Phing
    • 19.2 获取和安装Phing
    • 19.3 编写build文档
    • 19.3.1 目标
    • 19.3.2 属性
    • 19.3.3 类型
    • 19.3.4 任务
    • 19.4 小结
    • 第五部分 结论
    • 第20章 持续集成
    • 20.1 什么是持续集成
    • 20.2 CruiseControl和phpUnderControl
    • 20.2.1 安装CruiseControl
    • 20.2.2 安装phpUnderControl
    • 20.2.3 安装项目
    • 20.3 小结
    • 第21章 对象、模式与实践
    • 21.1 对象
    • 21.1.1 选择
    • 21.1.2 封装和委托
    • 21.1.3 解耦
    • 21.1.4 复用性
    • 21.1.5 美学
    • 21.2 模式
    • 21.2.1 模式给我们带来了什么
    • 21.2.2 模式和设计原则
    • 21.3 实践
    • 21.3.1 测试
    • 21.3.2 文档
    • 21.3.3 版本控制
    • 21.3.4 自动构建
    • 21.3.5 持续集成
    • 21.3.6 我们还遗漏了什么
    • 21.4 小结
    • 第六部分 附录
    • 附录A 参考文献
    • 附录B 简单的解析器

     

    读书笔记

    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

     

    上一篇:PHP开发实例大全:提高卷  下一篇:ThinkPHP实战

    展开 +

    收起 -

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

    Copyright 2018-2020 xz577.com 码农之家

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

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

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

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

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