夜雪天狼
学习笔记
技术博文
转载备份
心灵鸡汤
php
发布者:caijw
阅读量:42332
发布时间:2014-05-01 08:52:23
# 学习资料 [官方手册](https://www.php.net/manual/zh/ "官方手册") # 学习笔记 ## 面向对象编程 ###类与对象的关系 1. 类是抽象的,代表一类事物 2. 对象是具体的,是类的一个具体实例 3. 类是对象的模板,对象是类的一个个体实例 ### 成员属性 1. 成员属性是从某个事物中提取的,它可以是基本数据类型,也可以是复合数据类型 2. 访问public成员属性的语法是:$对象名->属性名;(属性名前面没有$) ### 对象在内存中存在的形式  ### 函数传值与内存的关系 1. 函数开始执行时,会在内存开辟一个新栈,函数内的全部变量存在于新栈中,称为局部变量,所以即使函数外有同名的变量,这两个也不是同一个变量,因为存在于不同的栈中 2. 函数结束时,其所对应的栈以及栈内的局部变量也会随之消失,作用域结束 3. 函数中的参数称为形参,实际传入函数的参数称为实参,如果传入的是值(如变量、数组等),则会拷贝一份值给形参,对形参的操作不对实参的值产生影响,如果传入的是地址(如&$变量、对象)则形参实参指向同一个值,对形参的操作会影响实参的值 ### 成员方法 函数和成员方法的关系:把函数写到了类中,则该函数可以称为成员方法 成员方法的基本语法: ```php 访问修饰符号 function 成员方法名(参数列表){函数体} ``` 调用成员方法:和普通函数的调用机制没有区别,只是成员方法有的所属,是调用某个对象的成员方法 ### 构造方法 构造方法是类中一个特殊的方法,它的主要作用是完成一个新对象的初始化 #### 特点: - 构造方法没有返回值 - 在创建一个类的新对象的时候,系统会自动调用该类的构造方法完成对象的初始化 - php4中,构造函数和类名一致 - php5中,新增\_\_construct方式,优先级高于旧构造函数,建议使用\_\_construct #### this关键字 - 直观理解:在构造函数中,如果要完成对象的初始化,即为成员变量赋值,需要this关键字,this在类中表示当前对象,哪个对象调用含有this的函数(this可以使用在任意成员方法中而不仅仅是构造方法)该this代表的就是哪个对象,通俗理解this就是”我”的意思 - 内存说明:php是先创建对象(将地址存于堆内存),而后自动调用构造函数(成员函数和构造函数存在于代码区),调用构造函数的时候,会将对象的地址传到构造函数中,而this的值就是这个地址,所以this就是一个引用,其值是对应对象在堆区的地址 #### 默认构造方法 如未创建构造方法,则系统自动生成个空构造方法,构造方法仅能有一个,不支持重载 #### 小结 1. 构造方法的作用是完成对象的初始化,而不是创建对象 2. 构造方法是在创建对象之后被系统自动调用,无需手动调用 3. 构造方法的默认修饰符是public ### 析构方法 #### 特点 析构函数会被自动调用,主要用于销毁资源,析构函数调用的顺序是先创建的对象最后被销毁,原因是栈的特性,先入后出 #### 调用时机 1. 当程序退出(进程结束)时 2. 当一个对象成为垃圾对象的时候,该对象的析构方法会立刻执行,所谓垃圾对象就是指没有任何变量在引用它 #### 小结 析构方法是php5中新增的:destruct(); 析构方法没有返回值 作用是释放资源,而不是销毁对象 在销毁对象前,系统自动调用该类的析构方法,无需手动调用 一个类最多只能有一个析构方法 ### 静态变量 #### 问题提出 一群小孩在玩游戏,不时有新的小孩加入,如何知道现有多少个小孩在玩游戏 #### 思路 1. 使用全局变量 全局变量:在程序中,都可以使用的变量就称为全局变量,代码如下: ```php // 注意:全局变量定义和赋值要分开,否则会报错 global $a;//定义了一个全局变量 function test(){//开辟了一个新栈 global $a;//没定义新变量,而是将全局区的$a引入,则在该函数范围内$a就是外面的$a; $a = 90; } test(); echo $a; ``` 用全局变量虽然能解决这个问题,但从一定程度上破坏了对象的封装性,所以建议使用静态变量 2. 使用静态变量 静态变量和全局变量本质是一样的,但静态变量被类包了起来,所以只能使用类来访问 #### 静态变量基本用法 - 在类中定义静态变量:`修饰符 static $变量名` - 在类中访问静态变量: 1. self::$静态变量名(与用this调用普通成员变量不一样,这里的$不能少) 2. 类名::$静态变量名(与用this调用普通成员变量不一样,这里的$不能少) - 在类外访问静态变量:类名::$静态变量名(这里的$不能少) #### 静态变量在内存中的情况 在创建对象时,在栈就会有一个变量,其值是堆区的一个地址,堆区的该地址对应一个对象实体,与普通成员变量不同的是,**静态变量的值不是变量值,而是一个地址,该地址指向静态区**,这样该类的所有对象的静态变量都是指向同一个地址,所以该值就是共享的了 #### 静态变量说明 静态变量与对象无关,不依赖于对象,即使不创建对象,也可以访问到类的静态变量 ### 静态方法(类方法)的基本用法 - 在类中定义静态方法:`访问修饰符 static function 方法名(){}` - 在类中访问静态方法: 1. self::静态方法名 2. 类名::静态方法名 - 在类外访问静态方法: 1. 对象名->静态方法名 2. 类名::静态方法名 > 注意:静态方法主要用于操作静态变量,静态方法中不能使用非静态变量,而普通的成员方法既可以操作静态变量,也可以操作非静态变量 ### 静态变量和静态方法小结 - 什么时候使用静态变量 当某个变量属于所有对象共享的时候 - 静态变量和普通变量的区别 1. 用static修饰的是静态变量,否则是实例变量 2. 静态变量是和类相关的,是公共的属性,实例变量是属于每个对象的属性 - 什么时候使用静态方法 一个专门用于操作静态变量的方法,则定义为静态方法 - 静态方法和普通方法的区别 1. 用static修饰的是静态方法,否则是实例方法 2. 静态方法是和类相关的,是公共的方法,实例方法是属于每个对象的方法 ### 面向对象之封装 #### 定义 封装就是把抽象出的数据和对数据的操作封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(成员方法)才能对数据进行操作 #### 访问修饰符 访问修饰符的作用就是控制外界是否可以直接访问到该数据,以达到封装的效果,访问修饰符写在定义前面,既可以修饰方法,也可以修饰属性,如果一个成员方法没写访问修饰符,默认是public,如果一个成员属性没写访问修饰符,则会报错 - public:全局公开,类内部,外部和子类都可以访问 - protected:受保护的,只有本类和子类可以访问 - private:私有的,只有本类内部可以访问 > 要想访问权限外访问protected变量(方法)和private变量(方法),通常的做法是通过一个public的成员方法来访问变量(方法),此做法并非多此一举,因为在成员方法中,可以进行权限的判断,继而达到封装和权限的作用 ### 面向对象之继承 #### 定义 所谓继承就是一个子类通过extends 父类 的方式,把父类的(public/protected)属性和(public/protected)方法继承下来,即不能继承父类的私有属性和私有方法 #### 基本语法 ```php class 类名 extends 父类名{ //这里写子类特有的东西,包括父类没有的和父类已经有的但却不一样的 } ``` #### 细节说明 1. 子类最多只能继承一个父类(指直接继承) 2. 当创建一个子类对象的时候,默认情况下不会自动调用父类的构造方法(与Java不同) 3. 如果子类想要调用父类的方法(成员方法、构造方法,且访问修饰符不为private),可以使用`父类名::方法名(参数列表)`(或者`parent::方法名(参数列表)`)来完成 4. 如果子类和父类的方法重名,则称为方法覆盖或者方法重写 ### 面向对象之多态 #### 重载(overload)(与多态无关,只是和重写一起讲,真正与多态相关的是重写) - 定义 函数重载是指多个函数名使用同一个标识符,且能够通过函数的参数个数或者类型将这些同名函数区分,使调用不发生混淆 - PHP5中的重载 1. php5虽可以支持重载,但重载在具体实现上,和其他语言还有较大的差别,如试图在类中直接定义多个同名函数,则程序运行时会报错 2. php5对函数重载是通过魔术方法__call实现的(虽然可以实现,但官方不推荐使用) - \_\_call的运行原理 当对象调用某个方法,而该方法不存在,则系统会自动调用\_\_call方法,\_\_call有两个参数(可以在\_\_call方法中对第一个参数的名字判断以及第二个参数的个数和类型判断,已达到函数重载的效果) 1. 不存在的方法名传递给第一个参数 2. 将(不存在的方法里的)参数列表以数组的形式传递给第二个参数 代码说明: ```php class A{ public function test1($p){}//接受一个参数 public function test2($p){}//接受两个参数 public function __call($method,$p){ if($method=="test"){ if (count($p)==1){$this->test1($p);} elseif (count($p)==2){$this->test2($p);} } } } $n=new A(); $n->test(15);//会调用test1 $n->test(15,20);//会调用test2 ``` #### 方法重写/覆盖(override) - 技术由来 当父类知道子类一定会有某个方法,却无法确定方法的具体内容,可以让子类去覆盖这个方法 - 覆盖定义 覆盖就是子类有一个方法和父类的某方法,名称、参数列表都一样(并不要求参数名称一样),那么就说子类的这个方法覆盖了父类的那个方法 - 细节说明 1. 所谓覆盖是在子类中覆盖了从父类中继承的方法,对父类的方法没有影响,而且依然可以通过父类名或parent调用父类的方法 2. 子类覆盖父类的方法时,子类方法不能比父类方法的访问权限小 #### 多态 php中的多态不像java那样复杂,因为php是弱类型的 php的多态指的是,建立一个子类对象,在执行一个从父类中继承的方法的时候,如果子类覆盖了父类的方法,那么就执行子类的方法,如果没有覆盖,就执行从父类继承的方法 ### 面向对象之抽象类 #### 概念 在面向对象中有种类是由其他类抽取出来(即其他类的父类)但是这个类本身无需实例化,其用途就是让子类继承该类,该类存在的意义是规范了子类的属性和行为,但子类的行为主体需要子类自己实现(覆盖),这样可以达到代码复用,同时利于项目设计者设计类 #### 定义方法 1. 抽象类:用abstract 修饰类,在class之前 2. 抽象方法:用abstract 修饰方法,在方法修饰符之前,不需要也不能写方法体 #### 注意事项 - 抽象类不能被实例化 - 抽象类可以有非抽象方法,非抽象类不能有抽象方法 - 抽象方法不能有方法体 - 如果一个类继承了抽象类,那么必须实现所有的抽象方法,除非该子类也是抽象类 ### 面向对象之接口 #### 接口的定义和使用基本语法 接口就是给出一些没有实现的方法,封装到一起,当某个类要使用的时候,再根据具体情况把这些方法实现,语法如下: ```php // 定义接口 interface 接口名{ //属性 //方法 } // 实现接口 class 类名 implements 接口名,接口名,接口名{ //可以实现多个接口,用,分割 } ``` #### 细节说明 - 接口体现了程序设计的多态和高内聚低耦合的设计思想 - 接口名的命名规范,通常以i开头,后跟上接口名字,i后面的首字母大写 - 接口中的方法都是抽象的,无需写abstract,可理解为更抽象的抽象类,不能实例化 - 抽象类是可以有非抽象方法,而接口有且只能有抽象方法,即全部没有方法体 - 一个类可以实现多个接口,用逗号隔开 - 接口中可以有属性,但必须是常量,默认是public,即可以不写访问修饰符(常量用const定义,且名字前面不要加$,常理通常都是大写,常理可以通过 接口名::常理名 取出) - 接口的方法只能是public的,默认是public,即可以不写访问修饰符 - 接口和接口之间可以有继承关系(类和类、接口和接口是继承,类和接口之间是实现) - 接口可以实现多继承 - 一个类实现了接口,就要实现这些接口的所有方法,包括这些接口本身的方法和它们从别的接口继承来的方法 #### 什么情况下可以考虑使用接口 - 定下规范,让其他程序员来实现 - 当多个类,没有继承关系,都要实现某个功能但实现方式不一样的时候,会抽取出一个接口,让这些类来实现,这时候这个接口就是一个功能 ### 面向对象之实现接口VS继承类 1. 实现接口可以看作是对单一继承的一种补充,还有,继承是层级式的,不太灵活,这种结构修改某一个类就会打破这种继承的平衡,而接口就没有这样的麻烦,因为它只针对实现接口的类才起作用 2. 实现接口可在不打破继承关系的前提下,对某个类进行功能扩展,非常灵活 3. 用java的理解方法,继承是a类是b类的...,接口是a类象b接口的... ### 面向对象之final关键字 - final的作用 1. 修饰类,则该类不能被继承 2. 修饰方法,则该方法不能被子类覆盖 - final-什么时候用 1. 不希望某个类被其他类继承 2. 因为安全的考虑,类的某个方法不允许修改 - 特别说明:final关键字不能修饰属性(变量) ### 面向对象之const关键字 - 需求 当不希望一个成员变量被修改,希望该变量的值是固定不变的,这是可以用const去修饰该成员变量,这样这个变量就自动变成常量 - 基本用法 ```php 定义:const 常量名=常量值; 使用:类名(在类中使用也可以用self)::常量名; 或者 接口名::常量名 ``` - 注意事项 1. 常量名应该全部大写 2. 定义常量的同时必须赋初值 3. 常量前面不要有$ 4. 常量const关键字前面不能加访问修饰符 ## 错误和异常处理 ### 错误处理 #### 案例说明: ```php $fp = fopen("aaa.txt","r");//打开文件 echo "ok"; //上面的程序没有进行错误或异常处理,如果文件不存在会报系统默认错误,健壮性很差 if(!file_exists("aaa.txt")){//判断文件是否存在 echo "文件不存在"; exit(); }else{ $fp = fopen("aaa.txt","r");//打开文件 echo “ok”; } ``` #### 错误处理的方式 - 简单的 "die()" 语句 - 自定义错误和错误触发器 - 错误日志 #### "die()" 语句 die()语句相当于exit(),但是可以直接输出错误信息 使用方法: ```php // 用if判断 if(!file_exists("aaa.txt")){//判断文件是否存在 die("文件不存在"); }else{ $fp = fopen("aaa.txt","r");//打开文件 echo "ok"; } // 用or关键字,如果函数返回真即文件存在就继续执行,否则就执行or后面的代码 file_exists("aaa.txt") or die("文件不存在"); //打开文件操作… ``` #### 自定义错误和错误触发器 - 为什么需要自定义处理机制 PHP有一个默认的错误处理机制,当程序出现错误且没有进行错误处理的时候,会调用该默认处理机制,将错误信息输出。但默认的处理机制可读性差,所以可以自定义处理机制,增强可读性和健壮性 - 自定义处理机制原理 创建一个自定义的错误处理机制非常简单。先创建了一个自定义错误处理函数,再将该函数改造为脚本运行期间的默认错误处理程序 - 自定义错误处理函数 该错误处理函数必须有能力处理至少两个参数 (error level 和 error message),但是可以接受最多五个参数(可选的:file, line-number 以及 error context) 语法是:`错误处理函数名(error_level,error_message,error_file,error_line,error_context)` 参数含义详见:[set_error_handler](https://www.php.net/manual/zh/function.set-error-handler.php "set_error_handler") - 错误报告级别 详见[php错误级别](https://www.php.net/manual/zh/errorfunc.constants.php "php错误级别") - 使用set_error_handler()函数将自定义函数设置为默认错误处理函数,set_error_handler()函数可以接受两个参数 1. 自定义的函数名称 2. 该函数相对应的错误级别,如不写,即针对所有错误使用自定义错误处理函数 - 错误类型 1. 系统错误 此类错误是系统定义的,例如:忘写分号,要打开的文件不存在等等 此类错误只要设置好自定义错误处理函数,当对应类错误出现时,会自动调用相应的函数输出相应的错误信息 2. 逻辑错误 是程序员定义的逻辑规则,不符合即为错误,是逻辑错误,此类错误是由程序员定义的,例如:对输入的年龄做范围限制,小于0或者大于120即为错误 逻辑错误我们使用错误触发器来定义错误信息和错误级别 语法:trigger_error();该方法接收两个参数 1. 错误信息 2. 错误级别 例如: ```php if($age > 120 || $age < 0) { trigger_error("年龄范围不正确",E_USER_NOTICE);exit(); } ``` 当错误触发时,会调用设置好的相对应级别的自定义错误处理函数,如没有设置,调用系统默认错误处理机制 #### 错误日志 错误日志保存是通过error_log() 函数实现的,该函数需要三个参数 1. 错误的信息,为了排版整齐,一般在后面加上\r\n(表示向文件输出一个换行) 2. 是保存类型,一般是3(在后面追加) 3. 是保存路径 ### 异常处理 #### 定义 异常处理用于在指定的错误(异常)情况发生的时候,改变脚本的正常流程 #### 基本语法 ```php try{ //可能出现错误(异常的)的代码 }catch(Exception $e){ //捕获异常并处理 //1.自己处理 //处理代码 //2.自己不处理,将异常再次抛出,通常是抛出下一个异常 throw e; } ``` #### 引出异常的原由 当我们需要做一个操作(一般都会将该操作封装成函数)的时候,需要知道该操作是否成功,常规的做法是在操作内部判断,如果成功返回真,否则返回假,然后在调用该函数的地方根据返回值判断是否成功,较为麻烦,但还能忍 但是在实际开发中,通常需要多个操作同时成功,才算成功,比如转账,需要对转出方的钱减少成功,且对转入方的钱增加成功才可以,否则就是失败,在这种情况下,上面的方式判断虽然可以,但是会很麻烦,冗余代码增多,显得臃肿,而且如果成功还好,要是失败,并不知道是哪个操作失败了,维护性极差 所以需要一个新的技术来解决这种问题,就是异常处理 案例: ```php function addUser($name){ if($name=="caijw"){ //可以添加 }else{ throw new Exception("添加失败"); } } function updateUser($name){ if($name=="caijw"){ //可以更新 }else{ throw new Exception("更新失败"); } } try { addUser("caijw"); updateUser("xxx"); } catch (Exception $e) { echo "失败信息是".$e->getMessage(); } ``` #### 细节说明 - 异常处理的方式,可以更有效的控制错误,所以在开发中大量的使用 - 当异常被抛出时,其后面的代码不会继续执行,PHP会尝试查找匹配的catch代码块 - 抛出一个异常,会抛给调用者,可以简单的理解为return - 如果抛出异常没有捕获,又没有进行错误处理,系统就会报出未捕获的异常错误 - 捕获异常后,如处理不了,可继续抛出,如抛到最上面一层,还没处理的话会启动系统默认处理器来处理,也可自定义异常处理器,像上面错误处理一样,自定义一个异常处理函数(该函数接受一个参数,就是要捕获的异常),在通过set_exception_handler()函数(该函数只接受一个参数,即自定义的异常处理函数名)将自定义的异常处理函数,指定为系统默认处理函数,该处理器仅能处理最顶级的未处理的异常 - 使用多个catch,可以捕获不同种类的异常 ```php try{ //代码 }catch(PDOException $e){ //处理PDOException异常 }catch(Exception $e){ //处理Exception异常 } ``` - 如果抛出异常就必须捕获,或者使用顶级异常处理器处理 - 与其他编程语言不一样的是,catch能抓到一个异常,取决于try中有没有抛异常,如果没有抛出异常,即使try中有错误,catch也获取不到,这种情况PHP认为是错误,交给了默认的处理机制,所以没有抛出异常是捕获不到的,所以异常处理机制只能处理自定义错误,因为只有自定义的错误,才会手动的抛异常,所以系统错误是不能用异常处理的,只能用前面说的错误处理机制,所以这也就是为什么php中会有异常处理机制还有错误处理机制的原因 ## PHP进阶 ### HTTP协议 #### 什么是HTTP协议 - 超文本传输协议(HTTP Hyper Text Transfer Protocol) - 是工作在TCP/IP基础上的 - 所有的WWW文件都必须遵守这个标准 - 设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法 - 通过httpwatch插件来抓起http请求内容 - http1.0(短连接):连接上,发送完数据立刻断开 - http1.1(长连接):连接上,发送完数据,在指定时间内并不断掉 #### HTTP请求基本结构 - 请求行:表示发送的请求方式(get/post)和请求资源,以及http协议版本 - 消息头 - Accept:用于指定客户端接受哪些类型的信息 - */*:表示可以接受任意的数据类型 - Referer:表示我是从哪来的(应用广泛,比如防盗链) - Accept-Language:指定可以接受的语言类型 - User-Agent:告诉服务器,浏览器的内核,以及操作系统 - Accept-Encoding:指定可以接受的压缩格式 - Host:要寻找的主机,带端口号,如果不写默认是80 - Connection:Keep-Alive表示不用立刻断掉请求 - <=空行 - 消息实体 > 特别说明:一个http请求的内容并不是永远不变的,有些消息头并不是何时何地发都会有 #### HTTP响应基本结构 - 状态行:HTTP/1.1 200 ok:200 ok表示客户端请求信息成功 - 响应头 - Server:告诉浏览器服务器的情况 - Date:告诉浏览器请求页面的时间 - Content-Length:返回的数据的字节数 - Content-Type:返回的数据的类型 - Cache-control:private:表示使用缓存 - Last-Modified:表示浏览器请求的资源最新更新时间 - Refresh:1;url:http://www.baidu.com :表示一秒后重定向到百度 - Expires:-1:禁用缓存 - Cache-Control:no-cache:禁用缓存 - Pragma:no-cache:禁用缓存 - <=空行 - 响应实体 #### 状态码的使用 header的介绍 header接受参数,可以想http响应头写入信息,可以改变服务器要回应的响应头,发给浏览器一个自定义的响应头 - 302的使用 如果一个页面写着如下代码 ```php // 说明:302状态码也可以让其跳转到外网去 header("Location:b.php");//该代码会向浏览器发一个302状态吗,并告知浏览器重新访问b.php // 控制浏览器间隔一定时间去跳转 header("Refresh:1;url=http://www.baidu.com");//一秒后重定向到百度 ``` - 404的使用 404不用代码控制,如果要请求的内容不存在,就会返回404错误 - 304的使用 如果一个资源已被请求了,则在本地有缓存,当需要再次请求该资源时,会向服务器发一个请求告知本地已有缓存,并将本地缓存的更新时间告知,服务器拿到该时间后与本地文件进行比较,如一样,则返回304码,意思是不需要再下载,可以直接使用缓存信息 #### 通过http响应控制页面的缓存 在默认情况下,浏览器会缓存页面,可以通过header来禁用缓存 ```php header("Expires:-1"); header("Cache-Control:no-cache"); header("Pragma:no-cache"); ``` -separator-