一、面向对象基础
1.类与对象
定义类基本语法:class 类名{}
<?php
class Nothing {
}
类无法直接访问,需要得到类的具体对象才能访问,可以通过实例化new来实现对象的创建:new类名[( )]
<?php
class My { }
new My ;
new My ( ) ;
对象创建后可以直接使用或者打印,但是为了方便后续使用,通常使用变量保存实例化的对象
<?php
class My { }
var_dump ( new My ) ;
$m = new My ;
var_dump ( $m ) ;
成员变量:就是类结构{}下直接定义的变量,但是定义的方式与普通变量稍微有点不同,需要在变量名字前使用一个关键字public,定义语法:public变量名字[=值];,成员变量可以赋值也可以不赋值
<?php
class Buyer {
public $name ;
public $money = 0 ;
}
成员变量访问:成员变量必须通过对象才能进行访问,也就是需要先通过实例化得到对象,然后通过对象实现对成员变量的增删改查:访问语法:$对象名->属性名
<?php
class Buyer {
public $name ;
public $money = 0 ;
}
$b = new Buyer ( ) ;
echo $b -> money ;
$b -> money = 1000 ;
unset ( $b -> name ) ;
$b -> age = 20 ;
成员方法:就是在类结构{}下定义的函数,函数内部的内容与原来函数结构一样,可以有分支、循环结构等
<?php
class Buyer {
function display ( ) {
echo __CLASS__ ;
}
}
成员方法访问:成员方法也是需要通过对象进行访问的,访问语法为:$对象名->方法名字
<?php
$b = new Buyer ( ) ;
$b -> display ( ) ;
类常量:类常量是在类结构{}下定义的常量,类常量的定义只能使用一种方式:const 常量名 = 值;
<?php
class Buyer {
const PI = 3.14 ;
}
类成员中:属性、类常量和方法都可以无限定义,但是定义的原则是相关性。除了以上三个类成员,不能在类结构{}中直接写其他任何代码 访问修饰限定符:public、protected、private
2.构造方法
构造方法实现:在类中增加一个方法__construct()即可
<?php
class Saler {
public $count ;
private $money ;
public function __construct ( $count , $money ) {
$this -> count = $count ;
$this -> money = $money ;
}
}
$s1 = new Saler ( 100 , 100 ) ;
$s2 = new Saler ( 1000 , 1000 ) ;
3.析构方法
析构方法实现:类中增加一个__desturct()方法
<?php
class Saler ( ) {
public function __destruct ( ) {
echo __FUNCTION__ ;
}
}
析构方法是对象用来调用释放对象中的资源,不是用来删除对象的 对象销毁情形:
保存对象的变量被用来保存其他数据,导致对象内存没有任何变量引用 删除对象变量 脚本执行结束:释放所有变量
4.对象传值
对象传值就是保存对象的变量赋值给另外一个变量 对象传值是引用传递,不管对象赋值给多少个变量,内存中只有一个对象
<?php
class Saler { }
$s1 = new Saler ( ) ;
$s2 = $s1 ;
var_dump ( $s1 , $s2 ) ;
$s1 -> name = 'Saler' ;
echo $s2 -> name ;
5.范围解析操作符
范围解析操作符,由两个冒号组成“::”,是专门用于类实现类成员操作的,可以实现类直接访问类成员 类常量的访问方式:类名:: 常量名
<?php
class Saler {
const PI = 3.14 ;
}
echo Saler :: PI ;
6.静态常量
静态属性:在类中定义属性的时候使用static关键字修饰,访问的时候只能使用类+范围解析操作符+静态属性访问 静态方法:在类中定义方法的时候使用static关键字修饰,访问的时候使用类+范围解析操作符+静态方法名字()访问
<?php
class Saler {
public $money = 0 ;
public static $count = 0 ;
public static function showCount ( ) {
echo Saler :: $count , __FUNCTION__ , '<br/>' ;
}
echo Saler :: $count ;
Saler :: showCount ( ) ;
$s = new Saler ( ) ;
var_dump ( $s ) ;
echo $s -> money ;
在类的内部也可以访问静态成员,同样是使用类名+范围解析操作符+静态属性/静态方法() 静态方法的本质是给类访问,所以不允许在静态方法内部使用$this对象 静态成员的访问效率比非静态成员高,因此有种说法是能用静态的时候就不用非静态
7.self关键字
self关键字是一种在类的内部(方法里面)使用,代替类名的写法。能够保障用户方便修改类名字 self是用来代替类名的,与范围解析符::一起使用的 self也可以在类的内部方便实例化对象:比如构造方法被私有化之后,就没有办法在类外部实例化对象,此时可以在类内部进行对象实例化
<?php
class Sale {
private static $count = 0 ;
public static function showClass ( ) {
echo Saler :: $count ;
echo self :: $count ;
}
private function __construct ( ) { }
public static function getInstance ( ) {
return new self ( ) ;
}
}
sale :: showClass ( ) ;
8.类的加载
定义:所谓类的加载,本质是因为类的访问必须保证类在内存中已经存在,所以需要在用类之前将类所在的PHP文件加载到内存 手动加载:即要访问某个类之前,使用文件包含将类所在的文件加载进来
类文件:Saler. php
< ? php
class Saler { }
? >
应用文件:useSaler. php
< ? php
include_once 'Saler.php' ;
$s = new Saler ( ) ;
? >
加载类文件是一种比较消耗资源的方式,所以有的时候不确定是否在内存中存在,可以事先使用class_exists()函数来判断是否存在,存在就不用加载,不存在才加载
<?php
if ( ! class_exists ( 'Saler' ) ) {
include_once 'Saler.php' ;
}
$s = new Saler ( ) ;
自动加载:PHP没有那么智能的系统自动加载,所谓自动加载只是PHP提供了一种加载机制:即事先定义一个函数__autoload(),然后当系统需要使用类,而内存中又不存在的时候,系统就会自动调用__autoload()函数来加载类文件
<?php
function __autoload ( $classname ) {
include_once $classname . '.php' ;
exit ;
}
$s = new Saler ( ) ;
? >
一个系统里,可能类文件会放到不同的路径下,因此一个完整的自动加载函数,应该进行文件判定以及加载功能 随着PHP版本的提升,在7以后,采用一种注册机制,将用户自定义的函数,放到系统内部,使用spl_autoload_register(定义好的函数)
<?php
function my__autoload ( $classname ) {
$c_file = 'c/' . $classname . '.php' ;
if ( file_exists ( $c_file ) ) {
include_once $c_file ;
exit ;
} else {
$m_file = 'm/' . $classname . '.php' ;
if ( file_exists ( $m_file ) ) {
include_once $m_file ;
}
}
}
spl_autoload_register ( 'my_autoload' ) ;
? >
同时,spl_autoload_register()函数可以注册多个自定义的加载函数,更方便管理
<?php
function c_autoload ( $classname ) {
$c_file = 'c/' . $classname . '.php' ;
if ( file_exists ( $c_file ) ) {
include_once $c_file ;
}
}
function m_autoload ( $classname ) {
$c_file = 'm/' . $classname . '.php' ;
if ( file_exists ( $m_file ) ) {
include_once $m_file ;
}
}
spl_autoload_register ( 'c_autoload' ) ;
spl_autoload_register ( 'm_autoload' ) ;
类实现自动加载
class Autoload {
public static function loadC ( $classname ) {
$c_file = 'c/' . $classname . '.class.php' ;
if ( file_exists ( $c_file ) ) require_once $c_file ;
}
public static function loadM ( $classname ) {
$m_file = 'm/' . $classname . '.class.php' ;
if ( file_exists ( $m_file ) ) require_once $m_file ;
}
}
spl_autoload_register ( array ( 'Autoload' , 'loadC' ) ) ;
spl_autoload_register ( array ( 'Autoload' , 'loadM' ) ) ;
9.对象克隆
克隆对象clone,即通过已有的对象复制一个新的同样地对象,但是两者之间并非同一个对象 对象克隆是通过clone关键字实现,即:clone对象 克隆出来的对象与原来对象是两个内存地址,因此是两个不同的对象
<?php
class Saler {
public $count ;
private $money ;
}
$s1 = new Saler ( ) ;
$s1 -> count = 1 ;
$s2 = clone $s1 ;
? >
对象在实例化的时候会自动调用存在的构造方法__construct(),同样地,在类的内部,PHP允许定义一个__clone()的方法,在对象被克隆后,新克隆出来的对象会自动调用 如果不允许对象被克隆,可以将__clone()方法私有化(本质是不允许对象在外部被克隆)
二、设计模式
1.单例模式
定义:单例模式singleton,是一种类的设计最多只会产生一个对象的设计思想 单例模式的目的是为了保护资源的唯一性 –私有化构造方法:禁止在类外无限实例化对象 –私有化静态属性:禁止对象无限克隆对象 –公有化静态方法:允许外部通过调用类内部方法获取对象
<?php
class Singleton {
private static $object = NULL ;
private function __construct ( ) { }
public static function getInstance ( ) {
if ( ! ( self :: $object instanceof self ) ) {
self :: $object = new self ( ) ;
}
return self :: $object ;
}
private function __clone ( ) { }
}
$s = Singleton :: getInstance ( ) ;
2.工厂模式
工厂模式factory,是指像工厂一样流水线生产对象,由一个地方生产对象,其他位置就不需要额外实例化对象,从而可以方便后期代码统一的维护。而且工厂模式下可以方便隐藏真实的类结构,因此更加安全 工厂模式针对的是“相同模型”的统一产出,即使用工厂模式产出对象的类都有相同的结构或者功能。所以,首先要有一批具有类似功能的类(其实本质是同样的大类下的小类)
class factory {
public static function getInstance ( $classname ) {
return new $classname ( ) ;
}
}
$m = factory :: getInstance ( ' ' ) ;
三、PHP继承
1.基本实现
继承基本语法:class 子类 extends 父类{}
<?php
class Human {
public function showName ( ) {
echo __CLASS__ ;
}
}
class Man extends Human {
}
$m = new Man ( ) ;
$m -> showName ( ) ;
2.有限继承
继承内容:PHP中继承是子类继承父类所有的公有成员、受保护成员和私有属性,不能继承父类的私有方法 静态成员(类常量)也遵循继承规则(PHP继承本质是对象),只是访问方式是由类进行访问 构造方法和析构方法也可以被子类继承,此时需要注意子类对象实例化时对应的父类构造方法的参数
3.重写override
定义:重写,即子类中定义了与父类重名的成员,子类可以重写父类任意类成员,通常重写是用来重写父类的方法,用于扩展或者更改某些业务逻辑 重写父类成员之后,子类只会直接访问子类的成员(覆盖) 不管是公有还是受保护属性,一旦重写,父类的就会不存在,而私有属性不会被覆盖而丢失 重写的要求: ① 子类重写父类的方法,控制权不能高于父类,即子类可以比父类更开放; ② PHP中重写要求子类重写父类方法的时候,必须保证与父类同名方法参数一致(在方法参数一致不单单要求数量一致,而且数据类型要求也必须相同,但形参名字可以不同); ③重写针对的是被继承的成员,父类私有方法不会被继承,因此不受要求。 重写是指子类拥有特殊的情况,一般是需要在父类的基础上进行扩展,此时如果想要继续保证父类被重写的方法继续执行(默认永远只访问子类重写的新方法),需要在子类重写方法的时候使用parent关键字
<?php
class Human {
protected function show ( ) {
echo __CLASS__ , '<br/>' ;
}
}
class Man extends Human {
public function show ( ) {
parent :: show ( ) ;
echo __CLASS__ , '<br/>' ;
}
}
parent不能访问父类的属性,可以访问静态属性、静态方法、类常量和普通方法
4.PHP继承特点
PHP中继承只能单继承:即子类只有一个父类(有些语言支持多继承) PHP若想继承多个类,可以使用链式继承 PHP中继承只有私有方法不能被继承 PHP允许子类继承父类的构造方法和析构方法
5.静态延迟绑定
静态延迟绑定,即在类的内部用来代表类本身的关键字部分不是在类编译时固定好,而是当方法被访问时动态地选择来访者所属的类。静态延迟绑定就是利用static关键字静态绑定self,静态延迟绑定需要使用到静态成员的重写 静态延迟绑定一定是通过继承后的子类来进行访问才有效果
class Human {
public static $name = 'Human' ;
public static function showName ( ) {
echo self :: $name , '<br/>' ;
echo static :: $name , '<br/>' ;
}
}
class Man extends Human {
public static $name = 'Man' ;
}
Man :: showName ( ) ;
6.Final关键字
定义:最终类,使用final关键字修饰类名,表示此类不可以被继承 基本语法:final class 类名 final关键字不止修饰类表示类不可被继承,还能修饰方法,表示方法不能被重写
class Human {
public function show ( ) {
public final function walk ( ) {
}
class Man extends Human {
public function show ( ) { }
public function walk ( ) { }
}
7.Abstract关键字
定义:抽象类,使用abstract关键字修饰的类,表示该类只能被继承,不能被实例化 抽象类只能被继承
abstract class Human { }
abstract关键字还可以用来修饰方法(抽象方法),abstract修饰的方法不能有方法体,而且有抽象方法的类必须声明为抽象类 抽象方法因为要被子类继承实现,所以不能使用private修饰(私有方法不会被继承) 子类继承抽象类后,如果抽象类中有抽象方法,那么子类必须选择自己成为抽象类或者实现抽象方法(所有抽象方法)
8.接口Interface
定义:使用interface关键字定义,与类类似,专门用来规范一些共性类必须实现的方法 接口定义:使用interface关键字,后跟接口名字(与类结构一样)
interface Human { }
接口不是类,不可以被实例化 接口实现:接口只能被类实现:implements
class Man implements Human { }
接口成员:接口中只能定义公有抽象方法 和接口常量
interface Human {
const NAME = '人' ;
public function eat ( ) ;
public function go ( ) { }
public $age ;
public static $count = 0 ;
protected function walk ( ) ;
}
实现接口的类成员,不允许重写接口中的常量,不允许增加接口方法的控制权限 接口可以继承接口:extends,而且接口可以多继承接口
interface Human {
public function walk ( ) ;
}
interface Animal {
public function eat ( ) ;
}
interface Man extends Human { }
interface Ape extends Human , Animal{ }
9.trait代码复用
Trait是为类似PHP的单继承语音而准备的一种代码复用机制,是一种类似class的关键字
trait Eat {
}
trait内部可以拥有一个类能拥有的成员属性(包含静态),成员方法(包含静态),但不能有类常量 trait是用来实现代码的复用的,不可以被实例化也不可以被继承(不是类) trait是用来将公共代码提供给其他类使用的,而类要使用trait的前提是加载对应的trait
trait Eat {
public function show ( ) {
echo 'eat' ;
}
}
class Human {
use Eat ;
}
$h = new Human ( ) ;
$h -> show ( ) ;
如果同时引入的多个trait中有同名方法,那么会产生冲突:解决冲突的方法是使用insteadof代替处理以及对被替代方法使用别名
trait t1 {
public function eat ( ) {
echo 't1,eat' ;
}
}
trait t2 {
public function eat ( ) {
echo 't2,eat' ;
}
}
class Human {
use t1 , t2;
}
class Person {
use t1 , t2{
t2 :: eat insteadof t1;
}
}
$p = new Person ( ) ;
$p -> eat ( ) ;
class Animal {
use t1 , t2{
t1 :: eat insteadof t2;
t2 :: eat as eat2;
}
}
$a = new Animal ( ) ;
$a -> eat ( ) ;
$a -> eat2 ( ) ;
同名覆盖问题:如果类中有与引入的trait同名成员,会有不同处理
属性:不允许重名,即类中不允许定义与trait中同名的成员属性(静态属性也一样) 方法:类覆盖trait
继承覆盖问题:如果类中在使用trait的同时,也是继承自父类,而trait中与父类中有同名方法,那么trait中将覆盖父类同名方法;如果要访问父类方法,可以在trait同名方法中使用parent关键字访问父类同名方法 另外,trait自己不能访问,只是用来给其他类提供代码复用的,因此允许类在使用trait时更高里面方法的访问控制权:在as之后,使用目标访问修饰限定符
trait Eat {
private function show ( ) {
echo 'eat' ;
}
}
class Human {
use Eat {
show as public eshow;
}
}
$h = new Human ( ) ;
$h -> eshow ( ) ;
10.PHP重载
定义:重载overload,本意指在一个类中可以出现多个同名方法,彼此之间参数个数和类型不一样。但是PHP中不支持同名方法,而且也不区分数据类型(弱类型语言),所以PHP不支持传统重载。PHP中的重载指的是当某些不允许操作发生时,会自动调用的一种内部机制,即自动调用相关的魔术方法。 魔术方法:指系统为类中预先设计好的,只需要开发者实现的方法,魔术方法以双下划线__
开始。对象在某些特定情况下会自动调用的方法。构造方法、析构方法和克隆方法就是魔术方法
class Man {
public function __construct ( ) { }
}
new Man ( ) ;
PHP重载是指某些容错处理(也可以理解为为了某些特殊情况而自动调用),在访问没有权限或者不存在的属性或者方法的时候,会自动触发的魔术方法。
属性重载:当PHP对象访问不存在的或者没有权限访问的属性的时候会自动调用的方法
__get($key):读属性的时候触发 __set($ key,$value):写属性的时候触发 __isset($key):外部调用isset()函数或者empty()函数时自动触发 __unset($key):外部调用unset()结构删除对象属性时自动触发 __toString():对象被当做普通变量输出或者连接时自动调用
class Man {
private $age = 10 ;
public function __get ( $key ) {
echo $key , __METHOD__ , '<br/>' ;
}
public function __set ( $key , $value ) {
echo $key . ' : ' . $value . '<br/>' ;
}
public function __isset ( $key ) {
echo $key , __METHOD__ , '<br/>'
}
public function __unset ( $key ) {
echo $key , __METHOD__ , '<br/>' ;
}
public function __toString ( ) {
echo __METHOD__ , '<br/>' ;
}
}
$m = new Man ( ) ;
$m -> age ;
$m -> age = 100 ;
isset ( $m -> age ) ;
unset ( $m -> age ) ;
属性重载的目的:一方面为了不让程序运行出错,另一方面可以在类内部由我们自己控制内容的访问
class Man {
private $age = 10 ;
public function __get ( $key ) {
$allow = array ( 'age' ) ;
if ( in_array ( $key , $allow ) ) {
return $this -> $key ;
}
return false ;
}
public function __set ( $key , $value ) {
}
public function __isset ( $key ) {
return isset ( $this -> $key ) ;
}
public function __toString ( ) {
return __METHOD__ ;
}
}
方法重载:当PHP对象访问不存在的方法或者不允许访问的方法时自动调用的方法(抑或是谋者特殊情况,如构造方法)
__call($ function_name, [$args]):对象调用不可调用方法时触发 __callStatic($ function_name, [$args]):类访问不可调用静态方法时触发
class Man {
private function show ( ) {
echo __METHOD__ , '<br/>' ;
}
private static function staticShow ( ) {
echo __METHOD__ , '<br/>' ;
}
public function __call ( $name ) {
echo $name , __METHOD__ , '<br/>' ;
}
public static function __callStatic ( $name ) {
echo $name , __METHOD__ , '<br/>' ;
}
}
Man :: staticShow ( ) ;
$m = new Man ( ) ;
$m -> show ( ) ;
方法重载的主要目的:不让外部访问出错。当然,如果必要时也可以进行内部访问
class Man {
private function show ( ) {
echo __METHOD__ , '<br/>' ;
}
private static function staticShow ( ) {
echo __METHOD__ , '<br/>' ;
}
public function __call ( $name , $arg ) {
$allow = array ( 'show' ) ;
if ( in_array ( $name , $allow ) ) return $this -> $name ( $arg ) ;
return false ;
}
public static function __callStatic ( $name ) {
return false ;
}
}
并非所有类都需要实现这些重载,只是如果有类需要对外提供访问使用的时候才有必要采取
11.对象遍历foreach
定义:遍历对象,其实就是指将对象中的所有属性(公有属性)以键值对的形式取出并进行访问
class Man {
public $name = 'LiLei' ;
public $height = 178 ;
public $weight = 140 ;
protected $age = 30 ;
private $money = 1000 ;
}
$m = new Man ( ) ;
foreach ( $m as $k => $v ) {
echo $k . ' : ' . $v . '<br/>' ;
}
四、命名空间
1.命名空间基础
定义:命名空间namespace,是指人为的将内存进行分隔,让不同内存区域的同名结构共存。从而解决在大型项目中可能出现的重名结构问题 基本语法:namespace 空间名字;
<?php
namespace my_space ;
命名空间的命名规则
由字母、下划线和数字构成 可以以字母和下划线开头 较少出现多单词空间名,一般使用下划线法
命名空间的作用:能够创建同名结构,包含函数、常量和类 命名空间注意事项:命名空间的声明(第一次)必须在所有代码之前
2.命名空间子空间
定义:子空间,即在已有空间之上,再在内部进行空间划分,让每个小空间独立起来 命名空间子空间是直接通过namespace+路径符号 \ 实现
namespace space ;
function display ( ) { }
namespace space\ space1 ;
function display ( ) { }
子空间的创建不一定非要在前面创建了上级空间,即可以直接在某个脚本中创建子空间
3.命名空间访问
在PHP命名空间中,提供了三种空间元素的访问方式:非限定名称、限定名称和完全限定名称 非限定名称:即直接访问空间元素的名字,此类访问方式访问的是当前代码所属空间内的元素
namespace space1 ;
function display ( ) {
echo 'space1' ;
}
namespace space2 ;
function display ( ) {
echo 'space2' ;
}
display ( ) ;
限定名称访问,即在访问元素的前面使用相应的空间名字,非限定名称的访问是基于子空间来实现的
namespace space\ space1 ;
function display ( ) {
echo 'space\space1<br/>' ;
}
namespace space\ space2 ;
function display ( ) {
echo 'space\space2<br/>' ;
}
namespace space ;
function display ( ) {
echo 'space<br/>' ;
}
display ( ) ;
space1\ display ( ) ;
完全限定名称访问,即从根目录(全局空间)开始访问,使用 \ 作为全局空间开始符号
\ space\ display( ) ;
\ space\ space1\ display( ) ;
4.全局空间
没有指定空间的元素所属的空间属于全局空间
function display ( ) {
echo __NAMESPACE__ ,'<br/>' ;
}
所有的空间本质都是在全局空间下的划分 全局空间元素的访问:使用完全限定名称访问
function display ( ) {
echo __NAMESPACE__ , '<br/>' ;
}
display ( ) ;
\ display( ) ;
5.命名空间引入
空间引入方式:use关键字
namespace space ;
class Man { }
namespace space1 ;
use space\ Man ;
new Man ( ) ;
use进行空间包含的时候,默认是从全局空间开始构建空间路径的(不是自己空间的相对路径),所以上述代码等价于以下代码
namespace space ;
class Man { }
namespace space1 ;
use \ space\ Man;
空间引入的元素默认是类,如果要引入其他元素,就必须使用相应关键字:function和const
namespace space ;
function display ( ) { }
class Man { }
const PI = 3.14 ;
namespace space1 ;
use function space\ display ;
use space\ Man ;
use const space\PI ;
display ( ) ;
new Man ( ) ;
echo PI ;
如果被引入的元素在当前空间已经存在,则会出现重名,解决方案是使用别名as alias
namespace space ;
function display ( ) { }
class Man { }
const PI = 3.14 ;
namespace space1 ;
class Man { }
use space\ Man as M ;
use function space\ display as dis;
use const space\PI as D ;
一旦引入的时候使用了别名,那么在使用的时候就直接通过别名使用(且只能使用别名) 如果一个空间中有多个元素要引入,那么可以进行一次引入多个,使用逗号,分隔即可,以上方式都是在引入同一种元素,如果要引入多个不同元素,可以如下使用
use space\space1\{
Man as M ,
Woman,
Ladyboy,
function display,
function show,
const PI as P
} ;
如果说确定一个空间里的所有元素都需要引入进来,也可以直接引入空间
namespace space ;
class Man { }
namespace space1 ;
use space ;
如果是直接进行空间引入,那么被引入的空间属于当前空间的一个元素,要访问引入空间的其他元素,得从引入的空间开始:即引入的空间最后一级空间名字+元素(引入空间当做当前空间的子空间)
namespace space\ space1\ space2 ;
class Man { }
namespace space3 ;
class Man { }
use space\ space1\ space2 ;
new Man ( ) ;
new space2\ Man ( ) ;
五、PDO
PDO类:统一的数据库的初始化操作,包括连接认证和执行SQL指令 PDOStatement类:数据解析操作,主要针对数据结果操作(有数据结果返回) PDOException类:异常处理操作,针对所有PDO操作可能出现的错误采用的异常处理模式