php 面向对象知识体系

来源:互联网 发布:俄罗斯战斗民族知乎 编辑:程序博客网 时间:2024/05/20 16:09
(一)声明一个类 new一个对象

<?php
header('Content-type:text/html;charset=utf-8');

//声明一个类
class Meizi{
//属性
public $eye = "big";
public $threeWei = "100,100,100";

}

$mm = new Meizi();
var_dump($mm);
echo '<br>';
$gg = new Meizi();
var_dump($gg);

结果
object(Meizi)#1 (2) { ["eye"]=> string(3) "big" ["threeWei"]=> string(11) "100,100,100"}
object(Meizi)#2 (2) { ["eye"]=> string(3) "big" ["threeWei"]=> string(11) "100,100,100"}


$gg $mm都是Meizi类的对象 对象号不一样 但是属性都是一样的
(二)用$this 调用成员属性
<?php
header('Content-type:text/html;charset=utf-8');

//声明一个类
class Meizi{
//属性
public $eye = "big";
public $threeWei = "100,100,100";
//方法
public function getInfo(){
echo "hello,我是妹子,我的眼睛:".$this->eye.", 我的三围:".$this->threeWei."<br>";
//var_dump($this);
}
}
//$this 在类的内部调用成员属性和成员方法 此时对象还没有实例化 echo $this->$eye 输出成员属性

$mm = new Meizi();
$mm->getInfo();

$gg = new Meizi();
$gg->getInfo();
结果
//var_dump($this);
hello,我是妹子,我的眼睛:big, 我的三围:100,100,100
hello,我是妹子,我的眼睛:big, 我的三围:100,100,100

var_dump($this);
hello,我是妹子,我的眼睛:big, 我的三围:100,100,100
object(Meizi)#1 (2) { ["eye"]=> string(3) "big" ["threeWei"]=> string(11) "100,100,100"}
hello,我是妹子,我的眼睛:big, 我的三围:100,100,100
object(Meizi)#2 (2) { ["eye"]=> string(3) "big" ["threeWei"]=> string(11) "100,100,100"}

$this 就是new 的对象 new几个对象var_dump($this)时有就几个
(三)成员属性的取值规范 和重新赋值

<?php
header('Content-type:text/html;charset=utf-8');
//声明一个类
class Person{
//成员属性
public $sex = 'man';
var $height;
public $size = array(1,2,3);
//public $num = 1+1; 不能出现运算 只能出现数值
//public $time = time(); 不能出现 函数
//public $sex = "woman"; 不能出现相同的成员属性 要想改变属性值 可以重新给属性赋值

}

//实例化成 对象 new
$p = new Person();

//属性 只能 给对象用
echo $p->sex."<br>"; //输出成员属性
$p->sex = "woman"; //成员属性重新赋值
echo $p->sex."<br>";
结果
man
woman

1 属性声明可以用var
2 属性的赋值 不能出现运算 只能出现数值 不能出现 函数 不能出现相同的成员属性 要想改变属性值 可以重新给属性赋值
3$p->sex = "woman"; 将原来的属性 man 重新赋值为woman

(四)构造方法给成员属性赋值

<?php
header('Content-type:text/html;charset=utf-8');

class Sister{
public $name;

public function __construct($n){
echo "啊,我是构造方法<br>";
$this->name = $n;
}
}
//实例化
$s = new Sister('cuicui');
var_dump($s);

结果

啊,我是构造方法
object(Sister)#1 (1) { ["name"]=> string(6) "cuicui"}

构造函数有一个形参是$n
实例化时$s = new Sister(cuicui);
给个实参 再在构造方法里$this->name = $n; 赋值给成员属性
var_dump($s)此时就能得到 成员属性 name=cuicui;

(五)构造方法的 两种实现方式

<?php
header('Content-type:text/html;charset=utf-8');

class Sister{
public function __construct(){
echo "啊,我也被调用了,我是大的<br>";
}

public function sister(){
echo "啊,我对调用了<br>";//方法名和类名一样 相当于构造方法 但是不建议使用
}

}

$s = new Sister();


结果

啊,我也被调用了,我是大的
(六)析构方法
<?php
header('Content-type:text/html;charset=utf-8');

class Sister{

public function __construct(){
echo "啊,我也被调用了,我是大的<br>";
}
public function __destruct(){
echo "啊,我被析构了<br>";
}
}
$s = new Sister();

结果
啊,我也被调用了,我是大的
啊,我被析构了



(一)~(六)小节

1 函数是存在 内存中 类是存在 内存中的堆里 实例化后 对象名存在栈里 方法既函数存在堆里 对象名在栈里是存的
路径 调用方法时通过栈里的路径到堆里找函数

2 声明一个类 可以实例化很多个对象 var_dump时会显示 对象1 对象2 。。。
成员属性 若有赋值 var_dump是会显示
也可以实例化之后用-> echo输出

在在类的内部调用成员属性和成员方法
此时对象还没有实例化 用$this 输出成员属性 必须在类的外部实例化 才能在内部用$this
成员属性 在 var_dump对象时能看到 也可以在实例化之后再重新赋值

3 对成员属性的赋值有三种

(1) 声明成员属性时直接赋值
(2)在构造方法里给形参 实例化时给实参 在再构造方法里赋值
(3)实例化类后 再对成员属性赋值 (若声明成员属性的时候没有赋值)或者重新赋值(若声明成员属性的时候有赋值)

4 实例化之后 公有的属性方法 都用用-> 访问 属性要echo 方法直接调用

(七)构造方法析构方法 特性

<?php
header('Content-type:text/html;charset=utf-8');

class User{
public $name;

public function __construct($name){
$this->name = $name;
echo "啊,{$this->name}被构造了<br>";
}

public function __destruct(){
echo "啊,{$this->name}被析构了<br>";
}
}


// 第一次种 实例化对象 赋值给一个变量 变量存在栈里 满足先进后出
$u1 = new User('莉莉');
$u2 = new User('翠翠');
$u3 = new User('艳艳');

结果
啊莉莉被构造了
啊翠翠被构造了
啊艳艳被构造了

啊艳艳被析构了
啊翠翠被析构了
啊莉莉被析构了

//第二种 实例化没有赋值给变量
new User('莉莉');
new User('翠翠');
new User('艳艳');

结果
啊莉莉被构造了
啊莉莉被析构了

啊翠翠被构造了
啊翠翠被析构了
啊艳艳被构造了
啊艳艳被析构了

(八)成员属性和成员方法的封装(封装性就是 使用非公有的2P 去修饰 属性和方法


<?php
header('Content-type:text/html;charset=utf-8');

class User{
public $name = "翠翠";
private $sex = "girl";
protected $age = 18;

public function getInfo(){
echo "内部调用:".$this->sex." : ".$this->age."<br>";
$this->demo();

//封装后的成员属性成员方法不能在类的外部直接方法 要在类的内部 用公有的成员方法中通过$this调用 在外部通过调用 公有的方法间接 使用封装的成员属性和成员方法
}

private function demo(){
echo "demo<br>";
}
}


//实例化
$u = new User();

echo "name : ".$u->name."<br>";
//echo "sex : ".$u->sex."<br>"; 私有的属性 不能直接在实例化之后方法
//echo "age : ".$u->age."<br>"; 收保护的属性 不能直接在实例化之后方法

$u->getInfo();

//$u->demo(); 私有的方法 不能直接在实例化之后方法

结果

name : 翠翠echo "name : ".$u->name."<br>"; 调用公有属性得到的

$u->getInfo(); 调用公有方法得到的 因为这个公有方法调用了两个封装的属性和一个封装的方法
内部调用:girl : 18
demo
(九)成员方法的封装 实例(对方法的封装就是 把一个大的方法 拆分成几个小的方法 这几个小的方法是非公有的 在 类的外部不能访问 只能在类的内部 让大方法调用

<?php
header('Content-type:text/html;charset=utf-8');

class Person{

//走路的功能
public function walk(){
$this->upLeg();
$this->downLeg();
}

//抬腿
private function upLeg(){
echo "肌肉收缩,抬腿<br>";
}

//放腿
private function downLeg(){
echo "肌肉放松,放腿<br>";
}
}


$p = new Person();
$p->walk(); // 可以访问
//$p->upLeg(); 不能访问
//$p->downLeg(); 不能访问



结果
肌肉收缩,抬腿
肌肉放松,放腿

(十)公有方法给私有属性赋值和访问私有属性

<?php
header('Content-type:text/html;charset=utf-8');

class Person{
private $name;
private $age;

//声明共有方法 给name属性赋值
public function setName($name){ //和构造方法赋值一样 不同的是构造方法一实例化就会传入 这个需要在调用
$this->name = $name;
}

//设置一个方法
public function getName(){ //上面赋值之后 就是私有属性name有值了但是不能echo 调用 再设置一个方法 返回值
return $this->name;
}

//声明共有方法 给age属性赋值
public function setAge($age){ // 也就是私有属性 赋值的判断
if ($age < 0 || $age > 165) {
return;
}
$this->age = $age;
}

//设置一个方法
public function getAge(){
return $this->age;
}
}


//实例化
$p = new Person();


//给属性赋值
//$p->name = "翠翠"; 必须用公有方法给私有属性赋值
$p->setName('翠翠');

$p->setAge(250); // 在设置值的时候进行了访问控制
$p->setAge(18);


echo "name : ".$p->getName()."<br>";
echo "age : ".$p->getAge()."<br>";

结果
name : 翠翠
age : 18



(十一)魔术方法 _set() _get()给私有属性赋值和访问私有属性

<?php
header('Content-type:text/html;charset=utf-8');

class Person{
private $name ;
private $age ;



public function __set($param, $value){

if ($param == 'age' && ($value < 0 || $value > 160)){
return false;
}
$this->$param = $value;
}



public function __get($param){


return $this->$param;
}



}

$p = new Person();

//设置 非公有属性的值
$p->name = "艳艳";
$p->age = 34;

echo 'name : '.$p->name."<br>";
echo 'age : '.$p->age."<br>";

结果
name : 艳艳
age : 34


(十)~(十一)小节


封装性主要是多属性的封装 实现访问控制 有两种方法
(1)用公有方法 设置私有属性 然后 访问私有属性

设置是不能实例化 然后->这样赋值 而是需要一个方法(有形参) 实例化然后调用方法(传入实参)跟构造方法不一样的是 要实例化之后再 调用 构造方法是实例化时传入实参,访问控制也是在这里实现,在方法里 对 传入形参的值进行判断, 然后echo 属性值 时 不能 echo $对象->属性名 要有一个方法 返回 属性值

(2)使用__get __set 魔术方法访问私有属性

这个在类的外部 实例化之后 赋值 和echo 输出值 没有变化 在两个魔术方法类会自动调用
实现访问控制也是在__set里面 对赋的值进行判断
(十二) 判断 非公有属性是否存在(_isset())销毁非公有属性(_unset())

1
<?php
header('Content-type:text/html;charset=utf-8');

class Person{
public $name = '翠翠';
protected $age = 17;
private $sex = 'girl';
}

$p = new Person();

//isset判断 某个对象中是否含有某个属性
var_dump(isset($p->name));
var_dump(isset($p->age));
var_dump(isset($p->sex));
var_dump(isset($p->grade));

结果

bool(true)
bool(false)
bool(false)
bool(false)
grade 确实不存在 返回false 正常 由于 age sex 为非公有属性 用 isset()函数判断不了非公有属性 ,只对公有属性起作用


2
<?php
header('Content-type:text/html;charset=utf-8');

class Person{
public $name = '翠翠';
protected $age = 17;
private $sex = 'girl';

public function __isset($param){
echo "啊,我被调用了<br>";
var_dump($param);

}
}

$p = new Person();

//isset判断 某个对象中是否含有某个属性
var_dump(isset($p->name));
var_dump(isset($p->age));
var_dump(isset($p->sex));
var_dump(isset($p->grade));
echo "<hr>";

结果
bool(true)啊,我被调用了
string(3) "age"bool(false)啊,我被调用了
string(3) "sex"bool(false)啊,我被调用了
string(5) "grade"bool(false)

isset()魔术方法调用了 三次 即 在 isset() 函数 判断非公有的属性和不存在的属性 是否 存在的时候调用 并且 $param的值 是 age sex grade

3
<?php
header('Content-type:text/html;charset=utf-8');

class Person{
public $name = '翠翠';
protected $age = 17;
private $sex = 'girl';

public function __isset($param){
echo "啊,我被调用了<br>";
// var_dump($param);
return isset($this->$param);
}

}

$p = new Person();

//isset判断 某个对象中是否含有某个属性
var_dump(isset($p->name));
var_dump(isset($p->age));
var_dump(isset($p->sex));
var_dump(isset($p->grade));
echo "<hr>";
结果

bool(true)啊,我被调用了
bool(true)啊,我被调用了
bool(true)啊,我被调用了
bool(false)


两个非公有的属性 此时也是true 不存在的属性 是 false 因为在 _isset() 的魔术方法是公有的 并且里面 isset()函数 在 公有的方法里面 判断 非公有属性 是有效果的

4
<?php
header('Content-type:text/html;charset=utf-8');

class Person{
public $name = '翠翠';
protected $age = 17;
private $sex = 'girl';

public function __isset($param){
echo "啊,我被调用了<br>";
// var_dump($param);
return isset($this->$param);
}

}

$p = new Person();

var_dump(empty($p->age));
echo "<hr>";

结果
啊,我被调用了
bool(true)


isset() 和empty() 判断非公有属性的时候 都能触发 _isset()魔术方法 只是结果是想法的

所以 这个结果 应该是false 所以是错的 看5

5
<?php
header('Content-type:text/html;charset=utf-8');

class Person{
public $name = '翠翠';
protected $age = 17;
private $sex = 'girl';

public function __isset($param){
echo "啊,我被调用了<br>";
// var_dump($param);
return isset($this->$param);

}

public function __get($param){
return $this->$param;
}

}

$p = new Person();

var_dump(empty($p->age));
echo "<hr>";
结果
啊,我被调用了
bool(false)


加了一个_get()的魔术方法 当$p->age时就会调用 获得属性 就与之前的结果相反

6
<?php
header('Content-type:text/html;charset=utf-8');

class Person{
public $name = '翠翠';
protected $age = 17;
private $sex = 'girl';

}

$p = new Person();


var_dump($p);
结果
object(Person)#1 (3) { ["name"]=> string(6) "翠翠" ["age":protected]=> int(17) ["sex":"Person":private]=> string(4) "girl"}

7
<?php
header('Content-type:text/html;charset=utf-8');

class Person{
public $name = '翠翠';
protected $age = 17;
private $sex = 'girl';

}

$p = new Person();

unset($p->name);
var_dump($p);

结果
object(Person)#1 (2) { ["age":protected]=> int(17) ["sex":"Person":private]=> string(4) "girl"}

删除一个公有属性 还有两个属性

8
<?php
header('Content-type:text/html;charset=utf-8');

class Person{
public $name = '翠翠';
protected $age = 17;
private $sex = 'girl';

}

$p = new Person();

unset($p->name);
unset($p->age)
var_dump($p);

结果
报错
unset()不能删除 非公有的属性

9
<?php
header('Content-type:text/html;charset=utf-8');

class Person{
public $name = '翠翠';
protected $age = 17;
private $sex = 'girl';



public function __unset($param){
unset($this->$param);
}

}

$p = new Person();

unset($p->name);
unset($p->age);
var_dump($p);

结果
object(Person)#1 (1) { ["sex":"Person":private]=> string(4) "girl"}

把 公有的属性 name 和一个非公有的属性 age 因为 unset()函数操作非公有的属性时 会调用_unset()这个魔术方法 而在这个方法里 删除了 age属性

(十二)小节

判断属性是否存在 还是基于isset()函数 删除属性还是基于unset()函数
只是他们无法操作非公有的属性 所以
当isset()操作非公有属性时 触发_isset()魔术方法 在这个方法里即类的内部去判断
当unset()操作非公有属性时触发_unset()魔术方法 在这个方法里即类的内部去删除变量

empty()和isset()返回值相反 所以用empty()操作非公有属性时要添加一个_get()的魔术方法 在类的内部得到这个属性 这样就和isset()的结果一样了



(十三)PHP中::、-&gt;、self、$this操作符的区别


在访问PHP类中的成员变量或方法时,如果被引用的变量或者方法被声明成const(定义常量)或者static(声明静态),那么就必须使用操作符::,反之如果被引用的变量或者方法没有被声明成const或者static,那么就必须使用操作符->。

另外,如果从类的内部访问const或者static变量或者方法,那么就必须使用自引用的self,反之如果从类的内部访问不为const或者static变量或者方法,那么就必须使用自引用的$this。

(十四)继承性

1封装 构造方法

<?php
class Person{
private function __construct(){
echo "啊,我被构造了<br>";
}
}

$p = new Person()

会报错 换成public 就会输出文字(构造方法在实例化的时候就会调用),换成非公有的就会报错 ,即封装的构造方法 不能被实例化 ,假如你有一个类 不让实例化 就封装一个构造方法
因为构成方法 实例化时会自动触发 而封装的构造方法不能被实例化

2继承性特点和三种访问级别

一个类只能有一个父类,但可以有多个子类
如果子类中方法名属性名与父类中相同,会发生 方法的重写,属性的重写
子类 重写 父类 的属性或方法, 可以修改访问控制级别,只能改得更开放(public proctected private)

访问控制 三种访问级别
public protected private
类的外部 yes no no
类的内部 yes yes yes
子类中 yes yes no


区分protected 和private 要在子类中 才能区分

在子类中也是要在子类的内部区分

3子类中重写父类的方法

定义 跟 父类方法重名 的 就可以重写
子类中调用父类方法; parent::funName()

4 代码分析

(1)

<?php

header("Content-type:text/html;charset=utf-8");


class Person{

private $grade = "s27";

}

class WhiteMan extends Person{

public $grade = "s110";
}

$p = new Person();
$wp = new WhiteMan();

var_dump($p);
echo '<br>';
var_dump($wp);


结果

object(Person)#1 (1) { ["grade":"Person":private]=> string(3) "s27"}
object(WhiteMan)#2 (2) { ["grade"]=> string(4) "s110" ["grade":"Person":private]=> string(3) "s27"}




当父类的属性书private的时候是 子类访问级别可以任意写 因为 private 已经是访问级别最低的了 但是 子类重写时其实并改变父类的属性 只是重新 新加一个属性

注意 子类对父类属性方法的访问 和子类对父类属性方法的重写

(2)子类中调用父类方法 (子类调用自己的方法 自己的方法里面再调用父类的方法)


<?php

header("Content-type:text/html;charset=utf-8");

class User{

private $name;
private $age;

public function __construct($name, $age){
$this->name = $name;
$this->age = $age;
}


public function getInfo(){
echo "我的名字:".$this->name." , 我的年纪:".$this->age.",";
}

protected function fun1(){
echo "fun1<br>";
}
}


class VipUser extends User{
private $grade;

public function __construct($grade, $name, $age){
$this->grade = $grade;
// 把父类的构造方法 调用一次
parent::__construct($name, $age);
}

//重写getInfo
public function getInfo(){
parent::getInfo();
echo "我的grade : ".$this->grade;
}

public function demo(){
//调用父类方法
parent::getInfo();
parent::fun1();
}
}

$u = new User('莉莉', 16);
$u->getInfo();
echo "<hr>";


$vu = new VipUser('s27', '艳艳', 14);
$vu->getInfo();

echo "<hr>";
$vu->demo();


结果


我的名字:莉莉 , 我的年纪:16,
我的名字:艳艳 , 我的年纪:14,我的grade : s27
我的名字:艳艳 , 我的年纪:14,fun1

(3)连贯 操作 对象链


<?php

header("Content-type:text/html;charset=utf-8");


class User{
private $number = 0;

public function add($num){
$this->number += $num;
return $this; //之所以可以连贯操作 是因为每次调用后都返回一个对象
}

public function getNum(){
return $this->number;
}

}

$u = new User();

echo $u->add(100)->add(10)->add(20)->getNum();

结果
130

关于继承性
<?php
class Person{
public $age;
protected $sex;
private $grade;

public function text(){
echo 'text';
}

protected function demo(){
echo 'demo';
}

private function ceshi (){
echo 'ceshi';
}

}


class blackman extends Person{


}


$blackman=new blackman();
var_dump($blackman);

object(blackman)#1 (3) { ["age"]=> NULL ["sex:protected"]=> NULL ["grade:private"]=> NULL}

<?php
class Person{
public $age;
protected $sex;
private $grade;

public function text(){
echo 'text';
}

protected function demo(){
echo 'demo';
}

private function ceshi(){
echo 'ceshi';
}

}

class blackman extends Person{}


$blackman=new blackman();
$blackman->age=11;
//$blackman->sex='nan';
//$blackman->grade='1';


var_dump($blackman);

object(blackman)#1 (3) { ["age"]=> int(11) ["sex:protected"]=> NULL ["grade:private"]=> NULL}

只有public的可以赋值 但是可以用__set魔术方法 请见③
<?php

class Person{
public $age;
protected $sex;
private $grade;

public function text(){
echo 'text';
}

protected function demo(){
echo 'demo';
}

private function ceshi(){
echo 'ceshi';
}

}


class blackman extends Person{



public function __set($param, $value){

$this->$param = $value;

}

}


$blackman=new blackman();
$blackman->age=11;
$blackman->sex='nan';
$blackman->grade='1';


var_dump($blackman);



object(blackman)#1 (4) { ["age"]=> int(11) ["sex:protected"]=> string(3) "nan" ["grade:private"]=> NULL ["grade"]=> string(1) "1"}

私有的grade 并没有在原来的基础上改变 只是增加了一个

<?php

class Person{
public $age;
protected $sex;
private $grade;

public function text(){
echo 'text';
}

protected function demo(){
echo 'demo';
}

private function ceshi(){
echo 'ceshi';
}

}


class blackman extends Person{
public $age=12;
protected $sex='nan';
private $grade='111';

}
$blackman=new blackman();

var_dump($blackman);



object(blackman)#1 (4) { ["age"]=> int(12) ["sex:protected"]=> string(3) "nan" ["grade:private"]=> string(3) "111" ["grade:private"]=> NULL}

跟③的结果一样

<?php

class Person{


public function text(){
echo 'text';
}

protected function demo(){
echo 'demo';
}

private function ceshi(){
echo 'ceshi';
}

}


class blackman extends Person{

}


$blackman=new blackman();

$blackman->text();
//$blackman->demo();
//$blackman->ceshi();


text 后面两个报错

<?php

class Person{

public function text(){
echo 'text';
}

protected function demo(){
echo 'demo';
}

private function ceshi(){
echo 'ceshi';
}

}


class blackman extends Person{


public function getinfo(){
$this->text();
$this->demo();
// $this->ceshi();
}

}


$blackman=new blackman();

$blackman->getinfo();

text demo 私有的方法 内部也不能被公共方法调用

<?php

class Person{
public function text(){
echo 'text';
}

protected function demo(){
echo 'demo';
}

private function ceshi(){
echo 'ceshi';
}

}


class blackman extends Person{



public function getmassage(){

parent::text();
parent::demo();
// parent::ceshi();

}


}


$blackman=new blackman();

$blackman->getmassage();



textdemo 给⑥一样 就是写法不一样
<?php
class Person{
public function text(){
echo 'text';
}
protected function demo(){
echo 'demo';
}
private function ceshi(){
echo 'ceshi';
}
}

class blackman extends Person{
}

Blackman::text();

Strict Standards: Non-static method Person::text() should not be called statically inD:\WWW\test.php on line 25
严格的标准:非静态方法::text()不应该被称为静态

text

虽然也可以 输出 但是报错 框架里面经常这样用 不知道父类的方法是不是是静态的

(十五)常见关键字和魔术方法


(1)final 关键字
  • adj. 最终的;决定性的;不可更改的
可以用于修饰 类 和 方法
final 修饰一个类,导致该类不能被继承
final 修饰一个方法,该方法不能被重写
提高代码安全性
代码可读性



提高代码安全性 实例

<?php
//操作类
class User{
final public function login($username, $password){
//验证用户 密码
if ($username == 'admin' && $password == '123456') {
echo "login success!";
} else {
echo "error!";
}
}
}

class User2 extends User{
public function login($username, $password){
echo "login success";
}
}


$u = new User2();

$u->login('admin', '12356');



上传一个文件 重写你的方法 让任何登陆名和密码都能登陆 所以要要防止对方法的重写


(2) static 关键字 3p依然适用 不用实例化类就能调用属性和方法
  • adj. 静态的;静电的;静力的

self
  • n. 自己,自我;本质;私心

用于修饰 属性 和 方法
静态属性
静态方法
访问方式: 类的内部 self::$属性/方法名 类的外部:类名::$属性名/方法名
静态属性只能使用静态的方式, 静态方法可以使用静态方式也可以使用动态方法(不推荐)
一个方法如果 内部没有动态的内容,默认把该方法当做静态方法(即使没有static)
静态方法中 不允许 出现动态内容(非静态内容) ($this)
作用
类中几个方法没有太大的联系,声明静态方法
实现设计模式



<?php
header('Content-type:text/html;charset=utf-8');

//定义类
class Person{

public static $name = "小莉莉";
private static $age = 19;

public static function demo(){
echo "啊,我是静态的<br>";
}

public static function getAge(){
return self::$age; // 和 非静态的一样 只是不能用$this 要用self
}

//定义静态方法
public function fun1(){
echo "啊。我司fun1<br>";
}

public static function fun2(){
echo "我司fun2<br>";
var_dump($this); // $this 是动态的类容 静态方法里没有动态内容 即$this这个变量时空值 所以要报错
}

}


$p = new Person();

echo "name : ".Person::$name."<br>";//若是公有的静态属性 在类的外部不用实例化 直接用 类名::属性名 调用公有静态属性
Person::demo(); //若是公有的静态方法 在类的外部不用实例化 直接用 类名::方法名 调用公有静态方法
$p->demo(); //不推荐 也可以实例化 然后调用 这个就叫动态方法

//echo "age : ".Person::$age."<br>";// 私有的静态属性和 私有属性一样 都不能在类的外面调用 在类的外部调用公有方法
echo "age : ".Person::getAge()."<br>"; // 调用公有方法 也是类名::方法名

echo "<hr>";
//$p->fun1();//正常调用
//Person::fun1(); // 若是方法没有static 修饰 也可以不用 实例化 直接类名:: 方法名 只是不建议 新版本会报错
Person::fun2(); //若是公有的静态方法 在类的外部不用实例化 直接用 类名::方法名 调用公有静态方法

(3) static 关键字 实现单例设计模式

一个类只允许存在一个对象
实现步骤
① 封装 构造方法 使类不能被实例化
② 定义 静态方法, 可以访问该类
③ 在静态方法取 实例化 该类
④ 定义静态属性, 实现 第一次调用静态方法会new,并把结果保存到静态属性,以后调用静态方法直接返回静态属性



<?php
header('Content-type:text/html;charset=utf-8');

//单例设计模式 一个类只允许存在一个实例 在static的基础上


//第一步 使类不能被实例化 不能new 来获取对象
class Test{
//设置 封装 的构造方法
private function __construct(){

}

// 第二步 给该类留个后门

//设置静态方法, 留后门 因为可以不实例化调用 静态方法 类名::方法名
public static function getObject(){
echo "啊,我是后门,进吧<br>";
}


//第三步在这个静态的方法里 实现new 一个对象

//设置静态方法, 留后门 在这个静态的方法里 实现new 一个对象
public static function getObject(){
return new Test();
}
}


//第四步
class Test{
private static $object = null; //静态属性 存储该类的实例

//设置 封装 的构造方法
private function __construct(){

}

//设置静态方法, 留后门
public static function getObject(){
if (self::$object === null) {
self::$object = new Test(); // 第一次为空 则new一个对象 往后就不为空了
}
return self::$object;
}


}

$t1 = Test::getObject();
$t2 = Test::getObject();
var_dump($t1);
var_dump($t2);
if ($t1 === $t2) {
echo "t1和t2是同一个实例<br>";
} else {
echo "t1和t2不是同一个实例<br>";
}

结果
object(Test)#1 (0) {}
object(Test)#1 (0) {}
t1和t2是同一个实例


有一个封装的构造方法 所以这个类不能实例化 但是有一个静态的方法 这个这个方法返回一个对象 可以不用实例化就可调用这个方法 所以$t1 = Test::getObject(); 这样就得到一个对象 有一个静态的属性 初始值是空 而这时 这个静态的属性值就是一个对象 而静态方法里 只有这个静态属性值为空时才能new 一个对象 所以只能实例化一个对象
(4)const
  • n. 常量,常数

在类的内部 定义 常量
访问 对比静态属性和静态方法的访问
类内部 self::常量名
类的外部 类名::常量名
作用
给该类中的方法设置参数选项


<?php
header('Content-type:text/html;charset=utf-8');

const HDS = '高级屌丝';
echo HDS."<br>";// php 5.3 以后加的 和difene 一样 都是定义常量 输出高级屌丝

class Test{
const LDS = "老屌丝";
const SDS = "小屌丝";

public function demo(){
echo "LDS : ".self::LDS."<br>"; //跟静态属性一样 类的内部 self::常量名
echo "SDS : ".self::SDS."<br>";
}
}
echo "LDS : ".Test::LDS."<br>";//跟静态属性一样 类的外部 类名::常量名
echo "SDS : ".Test::SDS."<br>";
$t = new Test();
$t->demo();// 也能输出

echo pathinfo('http://www.baidu.con/index.php', PATHINFO_EXTENSION).'<br>';
echo PATHINFO_EXTENSION."<br>"; // 输出4 在定义pathinfo这个函数时 后面的参数就是常量 1234
echo pathinfo('http://www.baidu.con/index.php', 4).'<br>';
echo pathinfo('http://www.baidu.con/index.php', 3).'<br>';
echo pathinfo('http://www.baidu.con/index.php', 2).'<br>';
echo pathinfo('http://www.baidu.con/index.php', 1).'<br>';




//定义游戏的类
class Game{
//定义常量
const LEFT = 37;
const UP = 38;
const RIGHT = 39;
const DOWN = 40;
//控制移动
public static function move($m){
switch ($m) {
case '37': echo "向左移动....<br>";break;
case '38': echo "向上移动....<br>";break;
case '39': echo "向右移动....<br>";break;
case '40': echo "向下移动....<br>";break;
}
}
}

Game::move(37);
Game::move(39);
Game::move(GAME::UP);//代码可读性高
Game::move(GAME::DOWN); //类名:: 方法名(类名::常量名)

(5)instanceof
  • n. 实例;运算符
运算符 判断 某个对象是否是某个类或该类子类的实例 返回 true/false


<?php
header('Content-type:text/html;charset=utf-8');

//定义类
class A{

}

class B{


}

class C extends A{

}


$a = new A();
$b = new B();
$c = new C();


if ($a instanceof A) {
echo '$a是A的实例<br>';//$a是A的实例
} else {
echo '$a不是A的实例<br>';
}

if ($b instanceof A) {
echo '$b是A的实例<br>';//$b不是A的实例
} else {
echo '$b不是A的实例<br>';
}

if ($c instanceof A) {
echo '$c是A的实例<br>';
} else {
echo '$c不是A的实例<br>';//$c是A的实例 子类也行
} else {
}
(6)克隆对象

为什么克隆
对象 引用赋值 的机制
语法
$a = clone $b
魔术方法 __clone();
在该对象被克隆的时候 会自动调用
如果封装该魔术方法 导致该对象不能被克隆

1
<?php
header('Content-type:text/html;charset=utf-8');

$a = 100;
$b = $a;
$b = 200;
echo $a."<br>"; //输出的是100

class User{
public $name = '小翠翠';
}

$c = new User();
$d = $c;
$d->name = "小艳艳";
echo $c->name."<br>";//输出的是小艳艳 对象的引用赋值机制 $d=$c 就是把d的路径给了c


class User{
public $name = '小翠翠';
}

$c = new User();
$d = clone $c;
$d->name = "小艳艳";
echo $c->name."<br>"; // 小翠翠 d又是一个新的对象 对$c没有影响

echo "<hr>";


2__clone()魔术方法
<?php
header('Content-type:text/html;charset=utf-8');

class Person{
//克隆魔术方法
public function __clone(){
echo "啊,我被克隆了<br>";
}
}


$p1 = new Person();
$p2 = clone $p1; //输出 啊,我被克隆了 就是克隆一次就调用这个方法 若是私有方法 就不能克隆

3__clone()魔术方法 用途
<?php
header('Content-type:text/html;charset=utf-8');


class User{
public $name = '小翠翠';
}

//克隆魔术方法的作用
class Man{
public $obj = null; //类型是对象

public function __construct($obj){
$this->obj = $obj;
}
}

$m = new Man(new User());
$m=$n;

var_dump($m);
var_dump($n);



此时 $m和 $n是一样的 对象号都一样 属性也是一个对象 user类的对象 对象号也是一样的



如果
<?php
header('Content-type:text/html;charset=utf-8');


class User{
public $name = '小翠翠';
}

//克隆魔术方法的作用
class Man{
public $obj = null; //类型是对象

public function __construct($obj){
$this->obj = $obj;
}
}

$m = new Man(new User());
$m= clone $n;

var_dump($m);
var_dump($n);


此时 $m和 $n是不一样的 对象号不一样 但是属性也是一个对象 是一样的 user类的对象 对象号也是一样的


必须
<?php
header('Content-type:text/html;charset=utf-8');


class User{
public $name = '小翠翠';
}

//克隆魔术方法的作用
class Man{
public $obj = null; //类型是对象

public function __construct($obj){
$this->obj = $obj;
}
public function __clone(){
$this->obj = clone $this->obj;
}

}

$m = new Man(new User());
$m= clone $n;

var_dump($m);
var_dump($n);




此时 $m和 $n是不一样的 对象号不一样 属性也是一个对象 也是不一样的 在$m克隆$n的时候调用_clone 魔术方法 把里面的user对象也克隆了一份

(7)类中通用的方法 __toString()
当 对象 被当做 字符串 一样 取输出的时候,自动调用
要求方法必须return 一个字符串

<?php
header('Content-type:text/html;charset=utf-8');
class Person{
public function __toString(){
return "啊,我被输出了<br>";
}
}

$p = new Person();
echo $p;// 输出 啊,我被输出了
print $p;//输出啊,我被输出了
var_dump($p);// 不能调用
print_r($p);//不能调用

只有echo print 能将对象以字符串形式输出 此时才能调用_toString()这个魔术方法
(8)类中通用的方法__invoke()
  • vt. 调用;祈求;引起;恳求

<?php
header('Content-type:text/html;charset=utf-8');

class Person{
public function __invoke(){
echo "啊,我被调用了<br>";
}
}

$p = new Person();
$p();
$p();
$p();
$p();

结果
啊,我被调用了
啊,我被调用了
啊,我被调用了
啊,我被调用了

当对象被当做函数时被调用

(9)__call() 和 __callStatic() 方法的应用

当调用不存在的方法的时候
接收两个参数 第一个 方法名,第二个 给的参数(数组)
作用: 面向对象开发程序,一个方法对应一个操作, 用户调用了不存在的操作,用来做友好的提示

<?php
header('Content-type:text/html;charset=utf-8');

class Person{

public function say(){
echo "say....<br>";
}

public function eat(){
echo "eat....<br>";
}

public function __call($funName, $params){
var_dump($funName);
var_dump($params);// 输出的是一个数组 即调用时写的实参
echo "啊,我被call了<br>";
}

public static function __callStatic($funName, $params){
echo "啊,我也被call了<br>";
}
}


$p = new Person();

$p->say();
$p->eat();
$p->run();
$p->walk('艳艳', '莉莉');

//调用不存在 的静态方法 触发__callStatic 也是两个参数 一个是方法名 一个是参数(var_dump时以数组形式显示)
Person::make();
(10)__autoload 自动加载类


格式
function __autoload($classname){
require "";
}
作用
自动导入 需要的类

<?php
header('Content-type:text/html;charset=utf-8');

//自动加载类
function __autoload($classname){
if (file_exists('./controllers/'.$classname.'.class.php')) {//文件名和类名一样 因为此时 controllers 里面有三个类 如果这三个类和当前的自动加载类在同一级就不用了
require './controllers/'.$classname.'.class.php';
} else {
echo "404";
exit;
}
}

上面根据__autoload这个函数 把get传参的值接收并且导入相应的类
new 一个本页面没有的类时触发$classname会直接使用类名所以要文件名和类名一样

//获取参数
$c = empty($_GET['c'])?'User':$_GET['c'];

//实例化
$controller = new $c();

controllers目录里面的类



(11)serialize()串行化 和unserialize()反串行化

串行化 是对对象的长久储存 json是对数组的长久储存
也可以对数组进行串行化设置
数组

array(2) { [0]=> string(2) " 3" [1]=> string(2) " 4" }
串行化后
a:2:{i:0;s:2:" 3";i:1;s:2:" 4";}

<?php
header('Content-type:text/html;charset=utf-8');

//对象 串行化
class Person{

private $name;
private $age;

public function __construct($name, $age){
$this->name = $name;
$this->age = $age;
}

}


$p = new Person('静静', 16);

//对象 对象p 进行 串行化 操作 serialize 把对象转化成字符串
$info = serialize($p);

echo $info.'<br>';

file_put_contents('data.txt', $info);//将字符串信息存入 data.tex文件中


//10个月之后

$mess = file_get_contents('data.txt');

//把串行化的字符串恢复成 对象 反串行化和之前的对象属性方法一样 对象号不一样
$obj = unserialize($mess);


把对象串行化就就是保存当前的状态即属性和方法 重新new一个 你可能不知道当时 传递什么属性了

(12)串行化 和反串行化 相关的魔术方法__sleep()__wakeup()


<?php
header('Content-type:text/html;charset=utf-8');

//对象 串行化
class Person{

private $name;
private $age;

public function __construct($name, $age){
$this->name = $name;
$this->age = $age;
}

public function getMessage(){
echo $this->name." : ".$this->age."<br>";
}

public function __sleep(){
echo "啊,我被串行化了<br>";
return array('name', 'age'); //串行化时调用 需要返回一个数组 数组里是要保存的当前的状态 即对象的熟悉 没什么鸟用其实
}

public function __wakeup(){
echo "啊,我被反串行化le1<br>";// 反串行化时调用 不需要返回一个数组
}

}


$p = new Person('静静', 16);

$info = serialize($p);
file_put_contents('data.txt', $info);

$mess = file_get_contents('data.txt');
$obj = unserialize($mess);


(13)json 格式 (把数组转换成字符串用于数组信息永久存,串行化是将对象转换成字符串用于对象信息永久存储



<?php
header('Content-type:text/html;charset=utf-8');

//json 格式 把数组转换成 字符串 用于数组信息永久存储
$list = array(1,2,3,4,5,6,7);

//函数
//json_encode()
$str = json_encode($list);

//存储到文件中
file_put_contents('data.json', $str);

/*
$arr = [10,30,40,50]; //php 5.4 以后加的 直接用json格式就能声明数组

var_dump($arr);
*/


//10个月以后
$mess = file_get_contents('data.json');
//json_decode()
$arr = json_decode($mess);


$list = array('name'=>'静静', 'age'=>12, 'grade'=>'s27');
$str = json_encode($list);
$arr = json_decode($str);



// 1 数组转字符串 implode()函数
// 2 对象转字符串 串行化 但此时的字符串 不是普通的字符串也不是json字符串
// 3 数组json字符串
//(1) 索引数组 转化后的json字符串用[] 再用json_decode 得到的还是数组
//(2) 关联数组 转化后的json字符串用{} 再用json_decode 得到的就是对象了

(14)类型约束
约束 方法 或 函数 的参数类型
目前 只能约束 数组 或 类的实例


<?php
header('Content-type:text/html;charset=utf-8');


//类型约束 约束参数类型 (数组 对象) 用于对函数的 实参 或者类中方法的实参

function test(array $param){
var_dump($param);
}

//test(12); 没类型约束前 输出的是 int 12 类型约束后就会报错 实参只能是 数组
test(array(1,2,3));



//约束对象
class A{
}
class B{
}
class C extends A{

}

function demo(A $param){
var_dump($param);
}

//demo(1);
demo(new A());// 可以 必须是A 或A的子集 的对象 (实例化)
demo(new C());//可以
demo(new B());//不可以

(15)stdClass
快速获取对象

<?php
header('Content-type:text/html;charset=utf-8');

$obj = new StdClass();//就是php里面 给你定义的一个方法 想得到对象时不用 写了class 类 但是声明的属性都是公有的
$obj->name = "艳艳";
$obj->age = 89;

var_dump($obj);//是一个对象

(十六)多态性

不能被实例化的类①封装构造方法②抽象类
(1)抽象方法 和 抽象 类
抽象方法: 使用关键字abstract修饰的方法称之为抽象方法, 抽象方法不能有方法体
抽象类: 使用关键字abstract修饰的类称之为抽象类, 如果一个类中含有一个抽象方法,必须把该类设为抽象类
抽象类里不一样有抽象方法 有抽象方法的类一定是抽象类
特点:
抽象类不能被实例化
抽象类不是用的,是标准 类型文档说明
使用抽象类定义的标准,定义类继承抽象类,并且重写抽象类的所有的抽象方法
语法
abstract class classname{
abstract public function funName();
abstract public function funName();
abstract public function funName();
}


<?php
header('Content-type:text/html;charset=utf-8');


//抽象类 的定义: 一个类中 只要含有一个抽象方法,必须把该类定义成抽象类
abstract class Person{
public $name;

public function say(){
echo "say...<br>";
}

//抽象方法 : 没有方法体的方法 称之为抽象方法 就是没有函数没有花括号{}
abstract public function run();
}


//抽象类 不是让你用的,给你定义标准的 不能实例抽象类 其他的类 继承抽象类中的抽象方法 然后对抽象方法进行重写来使用该方法
class BlackMan extends Person{
//把 实现抽象 方法 重写 继承抽象类 要是不重写抽象方法 在类前加 abstract 也行但是没有意义 其实抽象类定了一个标准 子类根据自己的情况重写抽象方法 并且所有抽象方法都要重写
public function run(){
echo "run...<br>";
}
}


//实例化 抽象类
//$p = new Person(); //会报错不能实例抽象类

$bm = new BlackMan();
$bm->run();
$bm->say();

(2)接口 interface 在抽象类基础上 抽象类可以有抽象方法也可以有普通方法 但是接口里只有抽象方法
使用interface定义
特点
① 接口中 只允许有 抽象方法 和 常量
② 抽象方法 省略 abstract
③ 接口不能被实例化
④ 定义标准, 定义类 继承 接口, 重写接口中所有的方法
⑤ 实现(继承)接口 不能使用extends 使用 implements
⑥ 接口 可以实现 多继承
语法
interface 接口类{
public function 方法名();
public function 方法名();
public function 方法名();
}

接口和抽象类的区别
① 接口中所有方法都是抽象方法 抽象类 可以有普通方法
② 接口不能有属性 抽象类可以有属性
③ 接口可以多继承 抽象类不可以



接口实例
<?php
header('Content-type:text/html;charset=utf-8');

//接口
interface User{ // 不用class 用interface
const DDS = "大屌丝"; //不能有属性 只能有常量和方法

public function demo();// 抽象类中可以有 抽象方法也可以有普通方法 单接口中只能有抽象方法 所以就不能写abstract了
public function demo1();
public function demo2();
}

//$u = new User(); 接口不能实例化

//接口 不能被继承 只能被实现
class VipUser implements User{ // 实际上也是继承 但不能用 extends 要用 implements
public function demo(){}
public function demo1(){} // 必须要接口中所有的抽象方法都 重写了 也就是说 这个方法数量比接口中多
public function demo2(){}
public function demo4(){}

}
接口 可以实现 多继承, 一个类可以继承多个接口

<?php
header('Content-type:text/html;charset=utf-8');

interface A{
public function demo();
public function test();
}

interface B{
public function demo();
public function fun();
}

class C implements A,B{
public function demo(){}
public function test(){}
public function fun(){}
}

(3)多态
子类继承父类,重写父类的方法和属性, 一个类可以有多个子类,不同子类之间具有 不同状态的属性和功能的方法, 就是多态


多态实例 使用Interface 实现多态

<?php
header('Content-type:text/html;charset=utf-8');

//定义 标准
interface PCI{
public function start();
public function stop();
}

//声明 声卡
class SoundCard implements PCI{
public function start(){
echo "声卡开启....<br>";
}

public function stop(){
echo "声卡关闭.....<br>";
}
}


//声明网卡
class NetCard implements PCI{
public function start(){
echo "网卡开启...<br>";
}

public function stop(){
echo "网卡关闭...<br>";
}
}


//声明 显卡
class VideoCard implements PCI{
public function start(){
echo "显卡开启...<br>";
}

public function stop(){
echo "显卡关闭...<br>";
}
}


//分别实例化 三个卡
$sc = new SoundCard();
$nc = new NetCard();
$vc = new VideoCard();



//多态的应用
class MainBoard{
//插卡的操作
public function usePCI(PCI $obj){//多态就是一个父类 有多个子类 这几个子类继承父类的方法 但彼此都不一样
$obj->start(); //声明一个PCI 有两个方法 三个子类都有这两个方法 方法名一样 但是输出的内容不一样
$obj->stop(); // 三个子类都实例化了 再声明一个类 用来分别使用这三个子类 定义一个方法 类型约束为 PCI 的子类对象
} // 方法也是两个 并且一样和PCI类 形参和调用的主体是三个子类
}

//实例化
$mb = new MainBoard();
$mb->usePCI($nc); // 实参是 三个子类声明的对象
$mb->usePCI($sc);
$mb->usePCI($vc);


多态实例 使用abstract 实现多态

<?php
header('Content-type:text/html;charset=utf-8');

//多态实例 使用abstract 实现多态

//定义 标准
abstract class PCI{
abstract public function start();
abstract public function stop();
}

//声明 声卡
class SoundCard extends PCI{
public function start(){
echo "声卡开启....<br>";
}

public function stop(){
echo "声卡关闭.....<br>";
}
}


//声明网卡
class NetCard extends PCI{
public function start(){
echo "网卡开启...<br>";
}

public function stop(){
echo "网卡关闭...<br>";
}
}


//声明 显卡
class VideoCard extends PCI{
public function start(){
echo "显卡开启...<br>";
}

public function stop(){
echo "显卡关闭...<br>";
}
}


//分别实例化 三个卡
$sc = new SoundCard();
$nc = new NetCard();
$vc = new VideoCard();



//多态的应用
class MainBoard{
//插卡的操作
public function usePCI(PCI $obj){
$obj->start();
$obj->stop();
}
}

//实例化
$mb = new MainBoard();
$mb->usePCI($nc);
$mb->usePCI($sc);
$mb->usePCI($vc);
多态实例 使用普通类 实现多态(不建议

<?php
header('Content-type:text/html;charset=utf-8');

//多态实例 使用普通类 实现多态(不建议)

//定义 标准
class PCI{
public function start(){
}
public function stop(){

}
}

//声明 声卡
class SoundCard extends PCI{
public function start(){
echo "声卡开启....<br>";
}

public function stop(){
echo "声卡关闭.....<br>";
}
}


//声明网卡
class NetCard extends PCI{
public function start(){
echo "网卡开启...<br>";
}

public function stop(){
echo "网卡关闭...<br>";
}
}


//声明 显卡
class VideoCard extends PCI{
public function start(){
echo "显卡开启...<br>";
}

public function stop(){
echo "显卡关闭...<br>";
}
}


//分别实例化 三个卡
$sc = new SoundCard();
$nc = new NetCard();
$vc = new VideoCard();



//多态的应用
class MainBoard{
//插卡的操作
public function usePCI(PCI $obj){
$obj->start();
$obj->stop();
}
}

//实例化
$mb = new MainBoard();
$mb->usePCI($nc);
$mb->usePCI($sc);
$mb->usePCI($vc);


(十七)异常 反射API 类对象的函数

①刚开始是在try{} 里面抛出异常
② 在方法或者函数里面 抛出异常
try{
调用函数或者方法
}catch(){
echo 异常信息

}
③自动接收异常
不用写 try catch 但是要方法 或者函数 抛出异常
再写一个函数 输出异常信息set_exception_handler 调用这个函数
④系统错误异常 抛出
要写 try catch 抛出异常写一个函数set_error_handler 调用这个函数
系统错误异常 抛出自动接收异常
不用写 try catch 一个函数 输出异常信息 set_exception_handler 调用这个函数

一个函数抛出异常写一个函数set_error_handler调用这个函数



分为 抛出和 接收两个过程 自动接收就不用写try catch
抛出分为
① try {} 中抛出
② 方法或者函数中抛出 try中调用
③ 系统错误异常抛出

1异常处理
定义
处理逻辑错误(可预期的错误) 正常逻辑过程中出现的异常
语法
try {
if (异常) {
throw new Excpetion('message');
}
} catch (Exception $e) {
echo $e->getMessage();
}
特点
如果 抛出异常 throw后面代码 会终止
try 后 的不影响
通常 会把 抛出 异常的过程(throw) 封装到函数或类中, 这样调用函数或类必须写在try中 thinkphp 中 抛出异常放在M层 控制器在try 中调用M层的方法

与其他语言(C++ Java....)区别
PHP的异常需要手工抛出
其他语言 系统 自动抛出
使用异常的情况
程序员悲观
代码健壮性要求
业务需要

系统自带的异常处理类
Exception {

/* 属性 */

protected string $message ;

protected int $code ;

protected string $file ;

protected int $line ;

/* 方法 */

public __construct ([ string $message = "" [, int $code = 0 [, Exception $previous = NULL ]]] )

final public string getMessage ( void )

final public Exception getPrevious ( void )

final public int getCode ( void )

final public string getFile ( void )

final public int getLine ( void )

final public array getTrace ( void )

final public string getTraceAsString ( void )

public string __toString ( void )

final private void __clone ( void )
}

异常实例

<?php
header('Content-type:text/html;charset=utf-8');

try{
echo "睁眼<br>";
//如果 睁不开眼
if (true) {
//echo "啊,睁不开眼了<br>";
//exit; 原来的写法
//抛出异常
throw new Exception('啊,我睁不开眼');
}
echo "下床<br>";
echo "洗脸<br>";
echo "刷牙<br>";
echo "早饭<br>";
} catch(Exception $e) { // 类型约束 是exception类的实例
echo $e->getMessage();//输出啊,我睁不开眼 即throw new Exception里面的信息
}



echo "<hr>哈哈,我是try外的<br>";// 可以正常输出 不受影响
异常实例 正常使用案例throw new Exception()放在函数和方法中





<?php
header('Content-type:text/html;charset=utf-8');

//封装到函数中 实现两个数相除
function demo($a, $b) {
//除数不能等于0
if ($b == 0) {
//抛出异常
throw new Exception('除数不能为0');
}
return $a / $b;
}


//封装到 类中
class Person{
private $age;
public function __construct($age){
//判断年龄的合法性
if ($age < 0 || $age > 150) {
throw new Exception('您的年龄不合法');
}
$this->age = $age;
}
}


//调用 try 和 catch中 调用
try {
echo demo(10, 3).'<br>';
//echo demo(10, 0).'<br>';//输出除数不能为0
new Person(18);
new Person(188); //输出您的年龄不合法

} catch (Exception $e) {
echo $e->getMessage();
}

系统异常类 的方法使用


<?php
header('Content-type:text/html;charset=utf-8');


function demo($a, $b) {
if ($b == 0) {
throw new Exception('除数不能为0', 250);
}
return $a / $b;
}




//调用 try 和 catch中 调用
try {
echo demo(10, 3).'<br>';
echo demo(10, 0).'<br>';
} catch (Exception $e) {
echo '错误信息: '.$e->getMessage().'<br>';//输出错误信息
echo "异常号Code : ".$e->getCode()."<br>";//输出错误号throw new Exception()第二个参数
echo "异常所在的文件 : ".$e->getFile()."<br>";//输出异常文件路径
echo "异常所在的行号 : ".$e->getLine()."<br>";//输出异常地方行号
echo $e.'<br>';//异常类有_toString()方法 把对象当字符串时输出时调用 输出上面所有信息 以字符串形式
var_dump($e->getTrace());//输出上面所有信息 以数组形式
echo $e->getTraceAsString()."<br>";//输出上面所有信息 以字符串形式
var_dump($e->getPrevious());//得到上一条异常
}

自定义异常处理类
定义一个类,继承系统Exception
Exception中大部分都不能重写



<?php
header('Content-type:text/html;charset=utf-8');
//自定义异常处理类继承Exception
class MyException extends Exception{
//自定义报错信息
public function getString(){
return "自定义异常:".$this->getCode()." : ".$this->getMessage();
}
}


function demo($a, $b) {
if ($b == 0) {
//抛出异常MyException
throw new MyException('除数不能为0', 250);
}
return $a / $b;
}




//调用 try 和 catch中 调用
try {

echo demo(10, 0).'<br>';
} catch (MyException $e) {
//输出自己的报错信息
echo $e->getString();
exit;
}

处理多个异常

try {

} catch() {

}catch(){

}




<?php
header('Content-type:text/html;charset=utf-8');
//自定义异常处理类
class MyException extends Exception{

public function getString(){
return "自定义异常:".$this->getCode()." : ".$this->getMessage();
}
}


//封装到 函数
function demo($a, $b) {
//除数不能等于0
if ($b == 0) {
//抛出异常
throw new MyException('除数不能为0', 250);
}
return $a / $b;
}

//封装到 类中
class Person{
private $age;
public function __construct($age){
//判断年龄的合法性
if ($age < 0 || $age > 150) {
throw new Exception('您的年龄不合法');
}
$this->age = $age;
}
}


//调用 try 和 catch中 调用
try {

echo demo(10, 0).'<br>';
new Person(434);

} catch (MyException $e) {
echo $e->getString(); //调用自定义异常
exit;
}catch (Exception $e) {
echo $e->getMessage();//调用系统异常
exit;
}


抛出异常 有在函数中定义 又在类方法中定义 有系统的异常类 也有自定义的异常类 所有写两个Catch 接收相对应的异常

自动接收异常
set_exception_handler() 在出异常时调用 参数是自己定义的输出信息 不用写try catch

首先还是 函数或者方法 要抛出异常
然后 set_exception_handler 会自动接收异常信息
有一个方法 要输出异常信息

<?php
header('Content-type:text/html;charset=utf-8');
//声明回调函数
function customException($e){
echo $e->getMessage();
}

set_exception_handler('customException');

class Person{
private $age;
public function __construct($age){
//判断年龄的合法性
if ($age < 0 || $age > 150) {
throw new Exception('您的年龄不合法');
}
$this->age = $age;
}
}

new Person(456);




实现系统错误 用异常抛出
set_error_handler();在出现waring 和 notice 错误时 不出现系统错误提示
只能接收 waring 和 notice 错误


实现系统错误 用异常抛出
<?php
header('Content-type:text/html;charset=utf-8');

//实现系统错误 用异常抛出

function customError($errno, $errstr, $errfile, $errline){
throw new Exception($errno." : ".$errstr." , ".$errfile." : ".$errline);
}

set_error_handler('customError'); //负责抛出异常


try {
echo $a;
}catch(Exception $e){
echo $e->getMessage();
}



实现系统错误 用异常抛出并且自动以 错误 处理机制

<?php
header('Content-type:text/html;charset=utf-8');


function customError($errno, $errstr, $errfile, $errline){
throw new Exception($errno." : ".$errstr." , ".$errfile." : ".$errline);
}

set_error_handler('customError');


function customException($e){
echo $e->getMessage();
}

set_exception_handler('customException');

2 反射API


面向对象编程中被赋予了自省的能力,而这个能力就是反射
根据到达地找到出发地和来源。 通过对象知道它所属的类、拥有哪些方法
反射指在PHP运行状态中,扩展分析PHP程序, 导出或提取出关于类、方法、属性、参数等的详细信息,包括注释。 这种动态获取信息以及动态调用对象方法的功能称之为反射API

反射API 的使用
ReflectionObject
ReflectionClass
获取对象或类中的属性和方法

作用:
反射可以用于文档生成。


<?php
header('Content-type:text/html;charset=utf-8');

//得到系统的对象
$e = new Exception('hello', 100);

$mysqli = new Mysqli('localhost', 'root', '123456', 's27');


$reflect = new ReflectionObject($mysqli);

$methods = $reflect->getMethods(); //得到这个对象的属性和方法

var_dump($methods);


$con = $methods[5];

var_dump($con->isPublic());//判断第5个方法是否是 公有方法


3PHP类与对象的相关函数
class_exists(clasName) 判断类是否已经定义, 会触发__autoload(第二个参数设置为false 不触发 autoload)
get_class_methods(clasName/ob) 获取对象或类 中所有的共有方法
get_class_vars(clasName) 获取类中所有的共有属性
get_object_vars(ob) 获取对象中所有的共有属性
get_class(ob) 获取对象的类名
get_parent_class(ob/className) 获取对象或类的父类
method_exists(ob/className,funName) 判断类或对象中是否存在该方法
property_exists(ob/className,varName) 判断类或对象中是否存在该属性
is_a(ob,className) 等同于instanceof
get_declared_classes() 返回所有已定义的类(包括系统定义的类)


<?php
header('Content-type:text/html;charset=utf-8');

function __autoload($classname){
echo "啊,我被触发了<br>";
var_dump($classname);
}
//对象和类 相关的函数
class A{
public $name;
public $age;
private $grade;
protected $sex;

public function fun1(){}
public function fun2(){}
private function fun3(){}
protected function fun4(){}
}

class B extends A{

}

class C{

}


$a = new A();
$b = new B();
$c = new C();


echo "class_exists : <hr>";
var_dump(class_exists('A'));
var_dump(class_exists('B'));
var_dump(class_exists('F', false));//class_exists(clasName) 判断类是否已经定义, 会触发__autoload(第二个参数设置为false 不触发 autoload)


echo "<hr>get_class_methods:<hr>";//获取类或对象中所有共有方法
var_dump(get_class_methods('A'));// 必须用双引号
var_dump(get_class_methods($a));
var_dump(get_class_methods($b));

echo "<hr>get_class_vars():</hr>";//获取类中所有的共有属性
var_dump(get_class_vars('A'));
//var_dump(get_class_vars($a));


echo "<hr>get_object_vars()</hr>";//获取对象中所有的共有属性
var_dump(get_object_vars($a));


echo "<hr>get_class:<hr>";//返回对象的类名
echo '$a的类名: '.get_class($a)."<br>";
echo '$b的类名: '.get_class($b)."<br>";


echo "<hr>get_parent_class:<hr>"; //返回对象或类的父类名
echo "b父对象 :".get_parent_class($b)."<br>";
echo "B父对象 :".get_parent_class('B')."<br>";
echo "A父对象 :".get_parent_class('A')."<br>";


echo "<hr>method_exists:<hr>"; //判断方法是否存现
var_dump(method_exists('A', 'fun1'));
var_dump(method_exists($a, 'fun1'));
var_dump(method_exists($a, 'fun4'));
var_dump(method_exists($a, 'fun6'));

echo "<hr>property_exists :<hr>"; //判断 属性 是否 存在
var_dump(property_exists($a, 'name'));
var_dump(property_exists($a, 'grade'));
var_dump(property_exists("A", 'grade'));

echo "<hr>is_a():<hr>"; //判断某个对象 是否 是某个类的实例
var_dump(is_a($a, 'A'));
var_dump(is_a($b, 'A'));
var_dump(is_a($c, 'A'));

echo "<pre>";
print_r(get_declared_classes());


(十八) PDO


(1) PHP连接MySQL的方式

MySQL方式 php_mysql.dll
Mysqli 方式 php_mysqli.dll PHP过程到对象 的过渡 支持 过程的语法 也 支持对象的语法
数据库抽象层 PDO

pdo(php data object) 扩展类库,为PHP访问数据库提供了轻量级的,统一的接口。无论使用
什么数据库,都可以通过一致的函数执行查询和获取数据
PDO的优势
① 为不同数据库提供统一的接口,极大提高了程序的灵活性
② POD 对象 SQL的解, 提高执行效率
③ PDO 提供了 预处理机制
PDO的应用场景
① 海量数据
② 高并发

pdo的安装
①php.ini 开启 extension=php_pdo_mysql.dll
②重启apache



安装MySQL 分服务端和客户端 安装wamp 时会安装服务端 客户端默认是cmd
像sqllog等数据库管理工具都是客户端
PHP操作MySQL其实也需要客户端 也就是扩展 php_mysql.dll

(2)使用mysqli 来操作 mysql 过程的风格

<?php
header('Content-type:text/html;charset=utf-8');

//连接数据库 判断是否成功 选择数据库
$mysqli = mysqli_connect('localhost', 'root', '123456', 's27') or die('连接失败!');

//设置字符集 编码
mysqli_query($mysqli, 'set names utf8');

//定义sql
$sql = "select * from student";

//发送sql
$result = mysqli_query($mysqli, $sql);

//处理结果集
while ($rows = mysqli_fetch_assoc($result)) {
var_dump($rows);
}

//关闭数据库
mysqli_close($mysqli);

(3)使用mysqli 来操作 mysql 对象的风格

<?php
header('Content-type:text/html;charset=utf-8');


//连接数据库 判断是否成功 选择数据库
$mysqli = new Mysqli('localhost', 'root', '123456', 's27');

if ($mysqli->errno > 0) {
echo $mysqli->error;
exit;
}

//设置字符集 编码
$mysqli->query('set names utf8');

//定义sql
$sql = "select * from student";

//发送sql
$result = $mysqli->query($sql);

//处理结果集
while ($rows = $result->fetch_assoc()) {
var_dump($rows);
}

//关闭数据库
$mysqli->close();

(4)创建PDO对象(即new 一个pdo类 这个类有构造方法 )
由于继承了异常了 所以要写try catch 但是是自动抛出的异常即没有throw new Exception();
以多种方式调用构造方法
DSN: data source name 连接不同的数据库 dsn不一样的
try {
new PDO(dsn, username, password)
}catch(){
}


1 dsn写文件中
<?php
header('Content-type:text/html;charset=utf-8');


try {
//实例化PDO对象 DSN User Pass
$pdo = new PDO('mysql:host=localhost;dbname=s27', 'root', '123456');

} catch(PDOException $e) {
echo $e->getMessage();
exit;
}
2 dsn写配置文件中



<?php
header('Content-type:text/html;charset=utf-8');


try {
//实例化PDO对象 DSN User Pass
$pdo = new PDO('xiaocuicui', 'root', '123456');
//xiaocuicui 是在php.ini定义的
} catch(PDOException $e) {
echo $e->getMessage();
exit;
}

var_dump($pdo);

(5)设置连接属性(上面创建好对象 这里设置一下属性)



① 实例化PDO 给第四个参数
② 方法 setAttribute() (推荐)
获取连接属性
方法: getAttribute()


<?php
header('Content-type:text/html;charset=utf-8');// PDO的使用
//导入配置
require "pdoconfig.php";
try {
//实例化PDO对象 DSN User Pass
$pdo = new PDO(DSN, USER, PASS);

//设置连接属性
$pdo->setAttribute(PDO::ATTR_AUTOCOMMIT, 1);

} catch(PDOException $e) {
echo $e->getMessage();
exit;
}

echo "PDO是否自动提交 : ".$pdo->getAttribute(PDO::ATTR_AUTOCOMMIT)."<br>";
echo "PDO是否自动提交 : ".$pdo->getAttribute(0)."<br>";//0=PDO::ATTR_AUTOCOMMIT


echo PDO::ATTR_AUTOCOMMIT;//输出的是0


echo "<br>PDO是否关闭自动提交功能:". $pdo->getAttribute(PDO::ATTR_AUTOCOMMIT);
echo "<br>当前PDO的错误处理的模式:". $pdo->getAttribute(PDO::ATTR_ERRMODE);
echo "<br>表字段字符的大小写转换: ". $pdo->getAttribute(PDO::ATTR_CASE);
echo "<br>与连接状态相关特有信息: ". $pdo->getAttribute(PDO::ATTR_CONNECTION_STATUS);
echo "<br>空字符串转换为SQL的null:". $pdo->getAttribute(PDO::ATTR_ORACLE_NULLS);
echo "<br>应用程序提前获取数据大小:".$pdo->getAttribute(PDO::ATTR_PERSISTENT);
echo "<br>与数据库特有的服务器信息:".$pdo->getAttribute(PDO::ATTR_SERVER_INFO);
echo "<br>数据库服务器版本号信息:". $pdo->getAttribute(PDO::ATTR_SERVER_VERSION);
echo "<br>数据库客户端版本号信息:". $pdo->getAttribute(PDO::ATTR_CLIENT_VERSION);




(6)PDO的错误处理模式
设置sql的错误处理方式
属性: PDO::ATTR_ERRMODE
值: PDO::ERRMODE_SILENT (默认)
POD::ERRMODE_WARNING
PDO::ERRMODE_EXCEPTION

POD::ERRMODE_WARNING方式

<?php
header('Content-type:text/html;charset=utf-8');

//导入配置
require "pdoconfig.php";
try {
$pdo = new PDO(DSN, USER, PASS);

//设置PDO的错误处理方式
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);

} catch(PDOException $e) {
echo $e->getMessage();
exit;
}


//定义sql
$sql = "select * from alibbc";
$pdo->query($sql);


会报系统的错误 如果不设置默认是不报错的


PDO::ERRMODE_EXCEPTION方式 推荐


<?php
header('Content-type:text/html;charset=utf-8');


require "pdoconfig.php";
try {
//实例化PDO对象 DSN User Pass
$pdo = new PDO(DSN, USER, PASS);

//设置PDO的错误处理方式

$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);//异常方式

} catch(PDOException $e) {
echo $e->getMessage();
exit;
}

try {
//定义sql
$sql = "select * from alibbc";
$pdo->query($sql);
} catch (PDOException $e) {
echo $e->getMessage();
exit;
}
会报异常的错误
(7)PDO实现增删改


query(sql) 返回对象 查
exec(sql) 返回 影响行数 增删改

<?php
header('Content-type:text/html;charset=utf-8');

//导入配置
require "pdoconfig.php";
try {
//实例化PDO对象 DSN User Pass
$pdo = new PDO(DSN, USER, PASS);

//设置PDO的错误处理方式
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);//异常方式

//设置字符集
$pdo->query('set names utf8');

} catch(PDOException $e) {
echo $e->getMessage();
exit;
}

try {
//insert 操作
$sql = "insert into student values(null, '小翠翠', 'w', 's27')";
$rows = $pdo->exec($sql);
$id = $pdo->lastInsertId();//返回自增id
echo "共插入{$rows}条<br>";
echo "最后一个自增的id是 {$id}<hr>";

//update操作
$sql = "update student set name='大翠翠' where id={$id}";
$rows = $pdo->exec($sql);
echo "共修改了{$rows}条<hr>";

//delete 操作
$sql = "delete from student where id={$id}";
$rows = $pdo->exec($sql);
echo "共删除了{$rows}条<hr>";

} catch (PDOException $e) {
echo $e->getMessage();
exit;
}
(8)PDO实现数据库查询 (三种方式)


pdo处理结果集方式
① 使用fetch 配合 while 循环
② 使用fetchAll 返回二维数组
③ 使用foreach 遍历 PDOStatment对象

使用query 返回 PDOStatment 的实例 得到一个对象再 使用fetch 配合 while 循环 此时要设置得到的数据类型

有两种方式 ①② 参见下面代码


<?php
header('Content-type:text/html;charset=utf-8');
// PDO 执行查询
//导入配置
require "pdoconfig.php";
try {
//实例化PDO对象 DSN User Pass
$pdo = new PDO(DSN, USER, PASS);

//设置PDO的错误处理方式
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);//异常方式

//设置字符集
$pdo->query('set names utf8');

//设置结果集数组方式①
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);//关联
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_NUM);//索引
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);//对象
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_BOTH);//混合 默认

} catch(PDOException $e) {
echo $e->getMessage();
exit;
}

try {
//定义查询 的sql 语句
$sql = "select * from student";
//发送sql
$stmt = $pdo->query($sql);//使用query 返回 PDOStatment 的实例 得到一个对象
//处理结果集
while ($rows = $stmt->fetch(PDO::FETCH_ASSOC)) {//设置结果集数组方式
var_dump($rows);
}
} catch (PDOException $e) {
echo $e->getMessage();
exit;
}


使用fetchAll 返回二维数组

<?php
header('Content-type:text/html;charset=utf-8');
// PDO 执行查询
//导入配置
require "pdoconfig.php";
try {
//实例化PDO对象 DSN User Pass
$pdo = new PDO(DSN, USER, PASS);

//设置PDO的错误处理方式
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);//异常方式

//设置字符集
$pdo->query('set names utf8');


} catch(PDOException $e) {
echo $e->getMessage();
exit;
}

try {
//定义查询 的sql 语句
$sql = "select * from student";
//发送sql
$stmt = $pdo->query($sql);
//处理结果集
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);//得到二维数组 然后遍历使用

echo "<ul>";
foreach ($data as $v) {
echo "<li>";
echo $v['id']." , ";
echo $v['name']." , ";
echo $v['sex']." , ";
echo $v['grade'];
echo "</li>";
}
echo "</ul>";
} catch (PDOException $e) {
echo $e->getMessage();
exit;
}
使用foreach 遍历 PDOStatment对象
<?php
header('Content-type:text/html;charset=utf-8');
// PDO 执行查询
//导入配置
require "pdoconfig.php";
try {
//实例化PDO对象 DSN User Pass
$pdo = new PDO(DSN, USER, PASS);

//设置PDO的错误处理方式
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);//异常方式

//设置字符集
$pdo->query('set names utf8');


} catch(PDOException $e) {
echo $e->getMessage();
exit;
}

try {
//定义查询 的sql 语句
$sql = "select * from student";
//发送sql
$stmt = $pdo->query($sql);
echo "<ul>";
foreach ( $stmt as $v) {//直接遍历 PDOStatment对象
echo "<li>";
echo $v['id']." , ";
echo $v['name']." , ";
echo $v['sex']." , ";
echo $v['grade'];
echo "</li>";
}
echo "</ul>";
} catch (PDOException $e) {
echo $e->getMessage();
exit;
}
(9)PDO预处理 防止sql 注入

可以注入的代码
<?php
header('Content-type:text/html;charset=utf-8');

require "pdoconfig.php";
try {
$pdo = new PDO(DSN, USER, PASS);

$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);//异常方式

$pdo->query('set names utf8');


} catch(PDOException $e) {
echo $e->getMessage();
exit;
}

try {
//接收表单的数据
$username = $_POST['username'];
$password = $_POST['password'];

//定义sql
$sql = "select * from user where username='{$username}' && password='{$password}'";

//发送sql
$stmt = $pdo->query($sql);

//判断是否登陆成功
if ($stmt->rowCount() > 0) { //rowCount() quert()方法时得到影响行数
echo "登陆成功....";
} else {
echo "登陆失败....";
}


} catch (PDOException $e) {
echo $e->getMessage();
exit;
}
假如用户 输入用户名 admin 密码 是'or 1=1' 则sql语句是
select * from user where username='admin' && password=''or 1=1"; 就会进去网站

不可以注入的代码


<?php
header('Content-type:text/html;charset=utf-8');
// PDO 预处理 执行登陆
//导入配置
require "pdoconfig.php";
try {
//实例化PDO对象 DSN User Pass
$pdo = new PDO(DSN, USER, PASS);

//设置PDO的错误处理方式
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);//异常方式

//设置字符集
$pdo->query('set names utf8');


} catch(PDOException $e) {
echo $e->getMessage();
exit;
}

try {
//接收表单的数据
$username = $_POST['username'];
$password = $_POST['password'];

//定义sql
$sql = "select * from user where username=? AND password=?";

//预处理
$stmt = $pdo->prepare($sql); //预处理

//给sql 绑定数据 其实就是解析转义字符 把引号滤过
$stmt->bindParam(1, $username);
$stmt->bindParam(2, $password);

//正式执行
$stmt->execute();


//判断是否登陆成功
if ($stmt->rowCount() > 0) {
echo "登陆成功....";
} else {
echo "登陆失败....";
}


} catch (PDOException $e) {
echo $e->getMessage();
exit;
}
(10)PDO预处理 执行添加数据 占位符用?


<?php
header('Content-type:text/html;charset=utf-8');
require "pdoconfig.php";

try {
$pdo = new PDO(DSN, USER, PASS);
$pdo->exec('set names utf8');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}catch (PDOException $e) {
echo $e->getMessage();
exit;
}

//预处理
try {
//定义sql 使用占位符
$sql = "insert into student(name, sex, grade) values(?,?,?)";
//预处理sql
$stmt = $pdo->prepare($sql);
//绑定参数
$sex = 'm';
$stmt->bindValue(1, '小艳艳');
$stmt->bindValue(2, $sex);
$stmt->bindValue(3, 's27');
//正式执行
$stmt->execute();

//返回结果
$rows = $stmt->rowCount();//rowCount() 是PDOStatment的方法
$id = $pdo->lastInsertId();//lastInsertId() 是PDO的方法
echo "共插入 {$rows} 条, 自增id: ".$id."<br>";

}catch (PDOException $e) {
echo $e->getMessage();
exit;
}
(11)PDO预处理 执行添加数据 占位符用:


<?php
header('Content-type:text/html;charset=utf-8');
require "pdoconfig.php";

try {
$pdo = new PDO(DSN, USER, PASS);
$pdo->exec('set names utf8');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}catch (PDOException $e) {
echo $e->getMessage();
exit;
}

//预处理
try {
//定义sql 使用占位符
$sql = "insert into student(name, sex, grade) values(:n,:s,:g)";
//预处理sql
$stmt = $pdo->prepare($sql);
//绑定参数
$sex = 'm';
$stmt->bindValue(':n', '大艳艳');
$stmt->bindValue('s', $sex);
$stmt->bindValue('g', 's27');
//正式执行
$stmt->execute();

//返回结果
$rows = $stmt->rowCount();
$id = $pdo->lastInsertId();
echo "共插入 {$rows} 条, 自增id: ".$id."<br>";

}catch (PDOException $e) {
echo $e->getMessage();
exit;
}

(12)PDO预处理 执行添加数据 占位符用?绑定参数 用bindParam
<?php
header('Content-type:text/html;charset=utf-8');

require "pdoconfig.php";


try {
$pdo = new PDO(DSN, USER, PASS);
$pdo->exec('set names utf8');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}catch (PDOException $e) {
echo $e->getMessage();
exit;
}

//预处理
try {
//定义sql 使用占位符
$sql = "insert into student(name, sex, grade) values(?,?,?)";
//预处理sql
$stmt = $pdo->prepare($sql);
//绑定参数bindParam只能是变量 bindValue都可以
$sex = 'm';
$name = "老艳艳";
$grade = "s27";
$stmt->bindParam(1, $name);
$stmt->bindParam(2, $sex);
$stmt->bindParam(3, $grade);
//正式执行
$stmt->execute();

//返回结果
$rows = $stmt->rowCount();
$id = $pdo->lastInsertId();
echo "共插入 {$rows} 条, 自增id: ".$id."<br>";

}catch (PDOException $e) {
echo $e->getMessage();
exit;
}
(13)PDO预处理 执行添加数据 占位符用:绑定参数 用bindParam


<?php
header('Content-type:text/html;charset=utf-8');
require "pdoconfig.php";
try {
$pdo = new PDO(DSN, USER, PASS);
$pdo->exec('set names utf8');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}catch (PDOException $e) {
echo $e->getMessage();
exit;
}

//预处理
try {
//定义sql 使用占位符
$sql = "insert into student(name, sex, grade) values(:n,:s,:g)";
//预处理sql
$stmt = $pdo->prepare($sql);
//绑定参数
$sex = 'm';
$name = "中艳艳";
$grade = "s27";
$stmt->bindParam("n", $name);
$stmt->bindParam("s", $sex);
$stmt->bindParam("g", $grade);
//正式执行
$stmt->execute();

//返回结果
$rows = $stmt->rowCount();
$id = $pdo->lastInsertId();
echo "共插入 {$rows} 条, 自增id: ".$id."<br>";

}catch (PDOException $e) {
echo $e->getMessage();
exit;
}
(14)PDO预处理 执行添加数据 占位符用?绑定参数 用 execute()

<?php
header('Content-type:text/html;charset=utf-8');

require "pdoconfig.php";

try {
$pdo = new PDO(DSN, USER, PASS);
$pdo->exec('set names utf8');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}catch (PDOException $e) {
echo $e->getMessage();
exit;
}

//预处理
try {
//定义sql 使用占位符
$sql = "insert into student(name, sex, grade) values(?,?,?)";
//预处理sql
$stmt = $pdo->prepare($sql);
//正式执行
$stmt->execute(array('翠翠', 'w', 's27')); //索引数组

//返回结果
$rows = $stmt->rowCount();
$id = $pdo->lastInsertId();
echo "共插入 {$rows} 条, 自增id: ".$id."<br>";

}catch (PDOException $e) {
echo $e->getMessage();
exit;
}

(15)PDO预处理 执行添加数据 占位符用:绑定参数 用 execute()

<?php
header('Content-type:text/html;charset=utf-8');

require "pdoconfig.php";

try {
$pdo = new PDO(DSN, USER, PASS);
$pdo->exec('set names utf8');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}catch (PDOException $e) {
echo $e->getMessage();
exit;
}

//预处理
try {
//定义sql 使用占位符
$sql = "insert into student(name, sex, grade) values(:n,:s,:g)";
//预处理sql
$stmt = $pdo->prepare($sql);
//正式执行
$stmt->execute(array(':n'=>'xiao翠翠', "s"=>'w', 'g'=>'s27'));//关联数组

//返回结果
$rows = $stmt->rowCount();
$id = $pdo->lastInsertId();
echo "共插入 {$rows} 条, 自增id: ".$id."<br>";

}catch (PDOException $e) {
echo $e->getMessage();
exit;
}
(16)PDO预处理对查询结果进行绑定


<?php
header('Content-type:text/html;charset=utf-8');

require "pdoconfig.php";

try {
$pdo = new PDO(DSN, USER, PASS);
$pdo->exec('set names utf8');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}catch (PDOException $e) {
echo $e->getMessage();
exit;
}

//预处理
try {
//定义sql
$sql = "select * from student";
//预处理
$stmt = $pdo->prepare($sql);
//执行
$stmt->execute();

//获取查询的条目
echo "共有".$stmt->rowCount()."条<br>";

//对查询结果进行绑定 把字段绑定为变量
$stmt->bindColumn('id', $id);
$stmt->bindColumn('name', $name);
$stmt->bindColumn('sex', $sex);
$stmt->bindColumn('grade', $grade);

while ($stmt->fetch()){
echo $id." : ".$name." : ".$sex." : ".$grade."<br>";
}

}catch (PDOException $e) {
echo $e->getMessage();
exit;
}

(17)MySQL的事务

① 自动提交 autocommit
② 关闭自动提交
set autocommit=0;
③ 开启事务
start transaction;
④ 提交 回滚
rollback;
commit;


MySQL 要想有事务 引擎必须是innodb (默认就是) 在 涉及到钱的时候要用事务
比如你付款成功 需要对订单表 余额表等表进行修改 但是其实有的表没有修改 就必须进行回滚 操作

数据库操作 分两步 第一步执行sql 语句 这个结果存在缓存中 可以回滚 第二步 提交 提交后就不能进行回滚 但是默认自动提交是开启的

所有要想执行事务首先 1关闭自动提交 2 开启事务
执行完sql 语句时 要是出错可以回滚 没有错就提交 提交完就不能回滚了
(18) pdo的事务


添加数据配合异常 预处理实现事务

<?php
header('Content-type:text/html;charset=utf-8');

require "pdoconfig.php";

//实例化pdo
try {
$pdo = new PDO(DSN, USER, PASS);
$pdo->exec('set names utf8');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//关闭自动提交
$pdo->setAttribute(PDO::ATTR_AUTOCOMMIT, 0);
//开启事务
//$pdo->query("start transaction");
$pdo->beginTransaction();
}catch (PDOException $e) {
echo $e->getMessage();
exit;
}

//预处理
try {
//批量操作
$data = array(
array('小王', 'w', 's27'),
array('老王', 'm',),
array('老李', 'm', 's27'),
);

//定义sql
$sql = "insert into student(name, sex, grade) values(?,?,?)";
//预处理sql
$stmt = $pdo->prepare($sql);
$count = 0;
$ids = array();
//遍历
foreach ($data as $v) {
$stmt->execute($v);
$count += $stmt->rowCount();
$ids[] = $pdo->lastInsertId();
}
$pdo->commit(); //没有异常就提交

echo "共插入多条数据:".$count."<br>";
var_dump($ids);

}catch (PDOException $e) {
//回滚
$pdo->rollBack();//有异常就回滚
echo $e->getMessage();
exit;
}


(19)PDO大块数据存储

之前存储图片时将图片放在目录中 数据库存路径 现在直接将图片资源存入数据库 优点是读取快

存图片

<?php
header('Content-type:text/html;charset=utf-8');
require "pdoconfig.php";


try {
$pdo = new PDO(DSN, USER, PASS);
$pdo->exec('set names utf8');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}catch (PDOException $e) {
echo $e->getMessage();
exit;
}

//打开图片
$image = fopen('1.jpg', 'r'); //得到是一个资源

//预处理
try {

//定义sql
$sql = "insert into image(type, content) values(?,?)";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(1, 'image/jpeg');
$stmt->bindValue(2, $image, PDO::PARAM_LOB);// 第三个参数就是把图片资源转化成大块数据
$stmt->execute();

echo $stmt->rowCount();


}catch (PDOException $e) {

echo $e->getMessage();
exit;
}

读取图片

<?php
header('Content-type:text/html;charset=utf-8');
//PDO大数据处理

//导入 配置文件
require "pdoconfig.php";

//实例化pdo
try {
$pdo = new PDO(DSN, USER, PASS);
$pdo->exec('set names utf8');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}catch (PDOException $e) {
echo $e->getMessage();
exit;
}
);

//预处理
try {

//定义sql
$sql = "select * from image where id=1";
$stmt = $pdo->prepare($sql);
$stmt->execute();

//处理结果
$stmt->bindColumn('type', $type);
$stmt->bindColumn('content', $content);

$stmt->fetch();

header("Content-type:".$type);
echo $content; //就能在页面显示图片


}catch (PDOException $e) {
echo $e->getMessage();
exit;
}

(20)PDO 操作 sqlite 数据库
只要改dsn 就行
<?php
header('Content-type:text/html;charset=utf-8');
require "pdoconfig.php";
try {
$pdo = new PDO('sqlite:D:\wamp\www\s27\OOP08\pdo\dds.db', null, null);
$pdo->exec('set names utf8');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}catch (PDOException $e) {
echo $e->getMessage();
exit;
}

//预处理
try {

//定义sql
$sql = "insert into test(name, addtime) values(?,?)";
$stmt = $pdo->prepare($sql);
$stmt->execute(array('翠翠', time()));

echo $stmt->rowCount()."<br>";

}catch (PDOException $e) {

echo $e->getMessage();
exit;
}

pdo知识点

1. pdo的
pdo(php data object) 扩展类库,为PHP访问数据库提供了轻量级的,统一的接口。无论使用
什么数据库,都可以通过一致的函数执行查询和获取数据
PDO的优势
① 为不同数据库提供统一的接口,极大提高了程序的灵活性
② POD 对象 SQL的解, 提高执行效率
③ PDO 提供了 预处理机制
PDO的应用场景
① 海量数据
② 高并发

2. pdo的安装
①php.ini 开启 extension=php_pdo_mysql.dll
②重启apache


3. 创建PDO对象
3.1 以多种方式调用构造方法
DSN: data source name 连接不同的数据库 dsn不一样的
try {
new PDO(dsn, username, password)
}catch(){
}

3.2 PDO与连接相关的选项
设置连接属性
① 实例化PDO 给第四个参数
② 方法 setAttribute() (推荐)
获取连接属性
方法: getAttribute()

3.3 PDO的字符集设置

3.3 PDO的错误处理模式
设置sql的错误处理方式
属性: PDO::ATTR_ERRMODE
值: PDO::ERRMODE_SILENT (默认)
POD::ERRMODE_WARNING
PDO::ERRMODE_EXCEPTION

3.4 PDO对象中的成员方法
setAttribute(attrname, attrvalue)
getAttribute(attrname)
query(sql) 返回对象 查
exec(sql) 返回 影响行数 增删改
prepare() 预处理sql 返回PDOStatment对象
beginTransaction()
commit()
rollBack()

3.5 PDO的增删改查
1. 增删改
使用 exec() 发送sql 返回影响行数
2. 查
使用query 返回 PDOStatment 的实例

4. PDOStatment 对象
4.1 PDOStatmen中的成员方法
fetch() 返回 当前 记录 组成数组(一维数组)
fetchAll() 返回 所有记录 组成 的 二维数组
bindParam()
bindValue()
bindColumn()
execute()
rowCount()

4.2 设置返回结果的数组类型
① 通过setAttribute()
② 给fetch、fetchAll 参数
属性 PDO::ATTR_DEFAULT_FETCH_MODE
值: PDO::FETCH_BOTH(默认)
PDO::FETCH_ASSOC(关联数组)(推荐)
PDO::FETCH_NUM(索引数组)
PDO::FETCH_OBJ(对象)
4.3 pdo处理结果集方式
① 使用fetch 配合 while 循环
② 使用fetchAll 返回二维数组
③ 使用foreach 遍历 PDOStatment对象


5. pdo预处理的使用
5.1 预处理的优点
① 对象用户的数据 进行过滤 提高安全性
② 提高批量操作的性能
5.2 预处理的步骤
① 定义sql语句, 关键部分 使用(占位符)代替
② 对象sql进行预处理(prepare) 返回PDOStatment对象
③ 给占位部分 绑定参数
④ 正式执行
5.3 预处理sql方式(占位符)
① ?
② :name
5.4 绑定参数的方式
① bindValue()
② bindParam()
③ execute()
5.5 对查询结果进行绑定
bindColumn()


6. pdo事务机制
6.1 MySQL 事务
① 自动提交 autocommit
② 关闭自动提交
set autocommit=0;
③ 开启事务
start transaction;
④ 提交 回滚
rollback;
commit;

6.2 PDO 中使用事务

7. PDO大块数据存储

8. PDO 操作 sqlite 数据库
8.1 sqlite 数据库
基于文档的小型数据库
PHP5 自带
(21)命名空间

1 命名空间概述
① 用户编写的代码与PHP内部的类/函数/常量或第三方类/函数/常量之间的名字冲突。
② 为很长的标识符名称(通常是为了缓解第一类问题而定义的)创建一个别名(或简短)的名称,提高源代码的可读性。

<?php
//定义命名空间
namespace dds;

header('Content-type:text/html;charset=utf8');


function var_dump(){
echo "啊啊啊,我是自定义";
}

var_dump(1213); //输出啊啊啊,我是自定义
\var_dump(1213);// int (1213)
\dds\var_dump(1213);//输出啊啊啊,我是自定义

2 命名空间特点
① namespace前面不能有任何代码(除了namespace) namespace作用范围到下一个namespace开始或脚本结束
② namespace 需要 php5.3 以上
③ namespace 只对 本脚本 起作用 require的也不起作用
④ 一个脚本 如果 没有定义命名空间, 默认在 全局命名空间中 中




<?php
//定义命名空间
namespace dds;

header('Content-type:text/html;charset=utf8');

//导入 函数库
require "function.php";


function demo(){
echo "带帽...<br>";
}

demo(); //输出带帽...
\demo();//demo。。。。


function.php

<?php
namespace dsd;

function demo(){
echo "demo。。。。。<br>";
}


3 子命名空间

<?php
namespace dds\dsd\dsb;

header('Content-type:text/html;charset=utf8');


function demo(){
echo "带帽...<br>";
}

demo();//输出带帽...
\dds\dsd\dsb\demo();//输出带帽...

4子命名空间相对性

4.php
<?php
namespace dds\dsd\dsb;

header('Content-type:text/html;charset=utf8');


function demo(){
echo "带帽...<br>";
}

demo();//输出带帽...
\dds\dsd\dsb\demo();//输出带帽...



<?php
namespace dds;
require "4.php";
header('Content-type:text/html;charset=utf8');


function demo(){
echo "带帽...啊啊啊,hello<br>";
}

demo(); //非限定名称//输出带帽...啊啊啊
\dds\dsd\dsb\demo(); //完全限定名称 //输出带帽...
dsd\dsb\demo(); //限定名称 //输出带帽...

都在dds下面就不能写根目录了

1. 非限定名称,或不包含前缀的类名称,例如 $a=new foo(); 或 foo::staticmethod();。如果当前命名空间是 currentnamespace,foo 将被解析为 currentnamespace\foo。如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,则 foo 会被解析为foo。 警告:如果命名空间中的函数或常量未定义,则该非限定的函数名称或常量名称会被解析为全局函数名称或常量名称。详情参见 使用命名空间:后备全局函数名称/常量名称。
2. 限定名称,或包含前缀的名称,例如 $a = new subnamespace\foo(); 或 subnamespace\foo::staticmethod();。如果当前的命名空间是 currentnamespace,则 foo 会被解析为 currentnamespace\subnamespace\foo。如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,foo 会被解析为subnamespace\foo。
3. 完全限定名称,或包含了全局前缀操作符的名称,例如, $a = new \currentnamespace\foo(); 或 \currentnamespace\foo::staticmethod();。在这种情况下,foo 总是被解析为代码中的文字名(literal name)currentnamespace\foo。


5在同一个文件中定义多个命名空间

① 不推荐 一般 一个脚本定义一个命名空间
② 万不得已 建议使用 {} 可读性高


<?php
//同一文件 中 定义多个命名空间
namespace nb;

header('Content-type:text/html;charset=utf8');

class PDO{
static public function test(){
echo "PDO::test";
}
}

const DDS = "大屌丝";
echo DDS.'<br>';
PDO::test();
echo "<hr>";

namespace dnb;
class PDO{
static public function test(){
echo "我是大nb";
}
}
const DDS = "大大丝";
echo DDS.'<br>';
PDO::test();

echo \nb\DDS.'<br>';
\nb\PDO::test();






<?php
//同一文件 中 定义多个命名空间
namespace nb{
header('Content-type:text/html;charset=utf8');

class PDO{
static public function test(){
echo "PDO::test";
}
}

const DDS = "大屌丝";

echo DDS.'<br>';
PDO::test();


echo "<hr>";
}




//====================================================
namespace dnb{
class PDO{
static public function test(){
echo "我是大nb";
}
}

const DDS = "大大丝";

echo DDS.'<br>';
PDO::test();

echo \nb\DDS.'<br>';
\nb\PDO::test();

}

6. 命名空间和动态语言特征
如果 把 函数名 类名 或 常量名 赋值给变量
在命名空间 必须 写完全限定 名称

<?php
namespace php;

header('Content-type:text/html;charset=utf8');

$a = "nba";

$b = "a";

echo $$b."<br>"; //输出 nba


function demo(){
echo "demo...<br>";
}

$fun = "\php\demo";

$fun(); //输出demo...


class User{
public static function test(){
echo "test...<br>";
}
}

$class = "\php\User";

$class::test();//输出test...


const DDS = "大屌丝";

$c = "\php\DDS";

echo constant($c);//输出大屌丝constant()把变量当成常量名
7 namespace关键字和__NAMESPACE__常量
namespace 关键字
① 定义命名空间
② 用来简化调用
__NAMESPACE__ 关键字
自定获取本命名空间 名字


<?php

namespace nb{
header('Content-type:text/html;charset=utf8');

class PDO{
static public function test(){
echo "PDO::test<br>";
}
}

//调用本命名空间下的内容
PDO::test(); //输出PDO::test
namespace\PDO::test();//输出PDO::test
echo "本命名空间: ".__NAMESPACE__.'<br>';//输出nb
}

}


8 使用命名空间:别名/导入
别名
use 命名空间 [as 别名]
导入
use 命名空间\类名
只能用于导入类

8s.php
<?php
namespace www\s27\OOP08;
class User{
public static function test(){
echo "test<br>";
}
}
function demo(){
echo "demo";
}

起别名

<?php
namespace php;
//给命名空间取别名
use \www\s27\OOP08 as dds;
use \www\s27\OOP08; //简写 use \www\s27\OOP08 as OOP08;

//导入文件
require "8s.php";

\www\s27\OOP08\User::test(); //输出test
dds\User::test();//输出test
OOP08\User::test();//输出test

导入类


一般的步骤是
1 写本文件的命名空间
2 导入需要继承的文件
3 use 需要的类
4 自己的类继承 导入的类

但是一般的框架 只要 1 3 4 步并没有导入文件

use 其实也导入类 这叫做类额装载

http://www.pfinal.cn/blog/1002 良哥给的 并没有看懂

教学案例分享,PHP5.3以上带命名空间,如何做类的自动装载,采用PSR-4规范的简单版本。

如果需要多条 autoload 函数,spl_autoload_register() 满足了此类需求。 它实际上创建了 autoload 函数的队列,按定义时的顺序逐个执行。相比之下, __autoload() 只可以定义一次。

test.php

<?php// src目录中,使用PSR-4命名规范的类,使用时将被自动装载// PSR-4: 命名空间与目录对应,文件名采用"类名.php"格式,目录和文件名严格区分大小写require_once'./src/autoload.php';var_dump(new Bar);var_dump(new Demo\Foo);var_dump(new Demo\Test\Baz);useDemo\Test\Baz;var_dump(new Baz);

src/autoload.php

<?php/** * 注册类自动装载函数 PSR-4规范 简单版本 * @author 邹义良 */spl_autoload_register(function($class){    // 兼容 PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731if ('\\' == $class[0]) {        $class = substr($class, 1);    }    // 将类名转换为路径    $path = strtr($class, '\\', DIRECTORY_SEPARATOR);    // 拼接完整文件名    $file = __DIR__ . DIRECTORY_SEPARATOR . $path . '.php';    // 检测文件是否存在if (file_exists($file)) {        include $file;    }});








<?php

namespace php;
require "8s.php";

//把User 这个类 导入本命名空间下
use \www\s27\OOP08\User;
//use \www\s27\OOP08\demo;

User::test(); //输出test
//demo();//报错只能用于导入类

9. 全局空间
namespace{

}

相当于根目录

10. 使用命名空间:后备全局函数/常量

<?php
namespace linux\php;

var_dump(100); // int (100)
\var_dump(100);// int (100)

虽然有命名空间 使用时先找本命名空间里有没有 没有的话在到根目录中去找
原创粉丝点击