PHP核心进阶-面向对象

探讨面向对象
1.面向对象是什么:
面向对象开发简称:OOP。它是一种程序设计的规范,同时也是一种开发方法。它的核心思想为对象化(顶层的设计)、封装化(代码集合在一块儿)、可重用性(减小冗余)和可扩展性(方便后期维护)。
所谓对象化,就是将程序做为一个基本的单元,经过命令式的去执行。
所谓封装化和可重用性,就是将大量可能会冗余的代码集合在一块儿模块话,只留一些对外接口给开发者调用。
所谓可扩展性,就是造成一种规范,方便后续增长更多功能而不破坏原先的结构。

2.面向对象的核心:
面向对象有三个核心功能:封装、继承和多态。
封装:即将大量实现代码集合在一块儿,留出可调用的接口对外。能够想象一下电脑主机里,用机箱将主板、CPU、硬盘、内存等封装在里面,防止噪音也防止灰尘,只留下一排对外的接口供你调用。
继承:即将一些程序代码有规则的复制到另外一个地方使用的方式。能够理解为,儿子继承了父亲房产,那么房产属于他们公有的部分。固然也会出现一些规则限制,好比父亲的微信号或QQ号等,儿子没法继承。
多态:即一种事物的多种形态,在程序中体如今不一样的对象执行同一个方法返回出不一样的结果。一个很经典的例子:“剪”这个行为。园丁去用剪这个动做,就是修理花草;理发师用剪这个动做,就是打理头发;总裁去用剪这个动做,就是裁人。这也是面向对象的高级抽象以及精华所在。

3.面向过程和面向对象的比较:
面向过程,是一种能够理解为按照必定的步骤一步一步的完成所须要的功能。这种方式的优势很明显,思惟单一,好理解,适合入门开发的人上手起步。缺点也很明显,各类功能中相同的代码冗余太多,扩展性差,维护性差。能够理解为刚成立的小公司,只有本身一我的,凡事都是本身亲力亲为。
使用面向对象,是一种自上而下的开发方式,顶层作好架构,封装留出接口,下面具体实现。缺点呢,就是起步稍显困难一些,在很小的程序感受有点臃肿;优势是:稍微有些规模的项目,极大的释放编码的潜力,可重复性和可扩展性获得的很大的提高。用现实的例子比如一家有些规模的公司,上层管理者制定计划方案递交给底层具体实施的人员去执行操做。

4.面向对象的目的:
1.解决代码冗余,没法可重复性的问题,封装模块化后冗余被大量减小;
2.解决扩展性和维护性,因为制定了一些的规则后,并不像面向过程那么随意的进行编码,这样无论是一段时间后的维护扩展仍是换人更新,均可以迅速上手,不须要研读他人思路的代码;
3.可团队化,每一个功能接口实现的规则都一致,多人开发造成了可能。php

 

类与对象
1.建立一个类:
面向对象有具体的语法,首先第一部须要建立一个类。
建立一个类有三个部分组成:第一部分:关键字class,第二部分:类名规范第一个字母大写,第三部分:花括号{}
//建立一个类
class Person
{

}
一个类建立好了,在花括号里暂时什么都没有,这是一个最基本的空类。

2.声明对象:
(1)使用 new 类名;语法来建立一个对象。数据库

//建立一个对象
new Person;

建立Person对象,咱们也能够称为:声明Person对象或实例化Person类。
当实例化后会在内存区域分配一个存储空间,这个内存叫堆内存,在堆内存分配了一个存储空间new Person,这个空间存放这个对象的具体内容

(2)查看对象
能够经过print_r或var_dump函数来获得这个对象的具体信息。编程

//查看对象
print_r(new Person);
var_dump(new Person);

返回结果:Person Object ( )
返回结果:object(Person)[1]
推荐var_dump,信息更全,更加直观

(3)建立多个对象
实例化一个类就分配一个内存区域,咱们也能够声明多个对象来实现各自独立的功能。此时,咱们须要给每一个对象关联上相应的引用地址(或叫对象变量标识)来区分每一个对象。注:有时咱们直接就称这个引用变量为对象。数组

//建立多个对象,把对象的引用地址赋值给变量,这个变量也能够俗称对象。
$p1 = new Person;
$p2 = new Person;

var_dump($p1);
var_dump($p2);

打印结果:
object(Person)[1]
object(Person)[2]

3.类与对象的探讨
(1)类是什么?是一种抽象组织,比如一台还没有启动的机器,它的结构是固定的统一的;
(2)对象是什么?是具体实现者,比如操控这台机器的人,它多是不同的;
(3)当人不去操控这台机器时,这台机器毫无做用;
(4)而不一样的人去操控这台机器时,获得的效果也大相径庭。微信

 

属性和方法架构

属性设置
1.属性其实就是一种变量,只不过它是类里面的变量。咱们还能够称做:字段或成员变量。
2.属性和变量的设置差不了太多,只不过必须在类里面设置。最重要的一点是,在属性前面须要加上一个可见的修饰符,省略修饰符会报错。修饰符有如下三种形式:
(1).public 表示公开的,在类内、子类和类外都可以读写;
(2).protected 表示受保护的,在类内或子类能够能够读写,类外不行;
(3).privte 表示私有的,只能在类内读写,子类和类外不行。
3.对于 public 公共属性,能够直接在类外经过对象的赋值与取值操做。
4.使用对象调用类里面的属性,能够经过“->”符号来访问属性或方法。请注意,调用属性的时候,这个变量将不须要再带“$”符号。框架

<?php
//属性建立
class Person
{
    public $name = 'Lee';
}

//属性的赋值和取值操做
$p1 = new Person;
//输出name
echo $p1->name;

输出结果:
Leeide

5.咱们在设置属性的时候,能够不用当即初始化赋值,只是单纯的声明,将赋值的操做留给后面动态处理。
6.建立多个对象实例后,每一个对象实例分配一个内存区域,而它们之间是没有关联的,本身运行着本身的属性和方法。模块化

<?php
//属性建立
class Person
{
    public $name;
}

//对象1
$p1 = new Person;
//赋值
$p1->name = 'Wang';
//取值
echo $p1->name;

//对象2
$p2 = new Person;
$p2->name = 'xixi';
echo $p2->name;

输出结果:
Wangxixi函数

7.也有种状况就是将其中的一个对象变量赋值给另一个变量,从而让两个变量同时指向一个对象实例。

<?php
//属性建立
class Person
{
    public $name;
}

//对象1
$p1 = new Person;
//赋值
$p1->name = 'Wang';
//取值
echo $p1->name;

//对象2并无new,不new的话就不会开辟新的内存空间
$p2 = $p1;
echo $p2->name;
$p2->name = 'xixi';
echo $p1->name;

方法设置
1.方法就是类里面的函数;
2.调用方法和属性基本一致,而方法和函数的表现形式也是同样的;

<?php
//方法建立
class Person
{
    public function run()
    {
        return '运行中...';
    }
}

$p1 = new Person;
echo $p1->run();

输出结果:
运行中...

3.前面的修饰符默认状况下,能够省略,那么默认就是公开的,也有受保护和私有的形式。但规范要求咱们,前面必须加上修饰符,以表示肯定。
4.使用$this 关键字,能够在类的内部调用属性或方法。

<?php
//方法建立
class Person
{
    public $name = 'Lee';

    public function run()
    {
        //注意:$this表明当前实例化的对象,好比$p1
        return $this->name.'运行中...';
    }
}

$p1 = new Person;
echo $p1->run();

输出结果:
Lee运行中...

 5.$this 关键字在多个实例时,只表示当前实例的属性值。

 

<?php
//方法建立
class Person
{
    public $name;
    public function run()
    {
        //注意:$this表明当前实例化的对象,好比$p1
        return $this->name.'运行中...';
    }
}

//$p1调用了run,$this就表示$p1
$p1 = new Person;
$p1->name = 'Wang';
echo $p1->run();

//$p2调用了run,$this就表示$p2
$p2 = new Person;
$p2->name = 'XiXi';
echo $p2->run();

输出结果:
Wang运行中...XiXi运行中...

读写方式
1.若是咱们想将属性进行封装保护起来,不对外直接访问操做,也就是设置成private。

<?php
//方法建立
class Person
{
    private $name = 'Wang';

    public function getName()
    {
        return $this->name;
    }
}

$p1 = new Person;
echo $p1->getName();

输出结果:
Wang

详解:setName传一个参数'Wang'给setName()里的$name的变量,这个变量再传给属性$this->name = $name,经过public function getName()这个方法打印出

 

属性和方法前置修饰符

可见性控制
1.public 是公共对外访问的修饰符,若是默认不加修饰符,则就是 public。固然,规范要求严格必须加上修饰符,以保证程序的健壮性。
2.private 是将属性或方法私有化,将没必要要对外的属性或方法封装起来,能够极大减小调用者的使用成本和难度。
3.面向对象的其中一种原则就是:若是没有必要从让这个属性和方法对外访问,那么就不要将它公开,只有小量的对外接口便可。固然,若是过分的封装会致使扩展不顺畅,好比一个房子,四面均为墙,只留了一个狗洞,那么进出就很不方便。那么,此时可使用protected 受保护的,它对外也是不可访问的,但对于扩展类来讲是能够访问的。
4.使用两个方法进行对不直接公开的属性进行赋值和取值,咱们成为setter和getter。

 

<?php

//受保护的
class Person
{
    protected $name;

    public function setName($name)
    {
        $this->name = $name;
    }

    public function getName()
    {
        return $this->name;
    }
}

$p1 = new Person;
//echo $p1->name;

$p1->setName('Wang');
echo $p1->getName();

打印结果
Wang

静态修饰符
1.静态属性存放在静态内存区域,属于公共区域,能够覆盖或累计。不须要实例化对象,直接经过“类名::静态属性名”方式调用。
2.静态属性仅属于类的属性,而不属于对象的属性。言下之意就是,它直接由类操控,而不并不是对象操控的。因此,不存在会分配多个内存区域,只有一个静态区。

<?php
//静态属性
class Person
{
    public static $name = 'Lee';
}

Person::$name = 'Wang';
echo Person::$name;

打印结果
Wang

3.在普通对象的方法里怎么调用静态方法呢?可使用 self 关键“self::静态属性名”。

<?php

//静态属性
class Person
{
    //私有化,在类外没法调用
    private static $name = 'Lee';

    public function getStaticName()
    {
        return self::$name;
    }
}

$p1 = new Person();
echo $p1->getStaticName();

打印结果
Wang

 4.静态属性经常使用于一些数据的累计,好比统计次数。

<?php
//静态属性,数据统计
class Person
{
    private static $count = 0;

    public function addCount()
    {
        self::$count++;
    }

    public function getCount()
    {
        return self::$count;
    }
}

$p1 = new Person;
$p1->addCount();
$p1->addCount();
$p1->addCount();

$p2 = new Person;
$p2->addCount();
$p2->addCount();
$p2->addCount();

echo $p1->getCount();

打印结果:
6

5.静态方法的功能就简单明了,直接经过“类名::静态方法名”调用,和面向过程函数同样调用。静态方法经常使用于工具类的那种,不须要实例化直接调用便可的。

<?php

class Tool
{
    public static function back()
    {
        echo "<script>alert('非法操做!')</script>";
    }
}

Tool::back();

执行结果:
弹出一句话:非法操做!

 

魔术方法

PHP中的魔术方法,这种方法由两个下划线“__”开头,具备一些特殊的做用。

1.__set 与__get

1.对于封装的属性,咱们经过setter和getter方式来对属性进行赋值和取值。而实际上,一个类中可能有十几个或更多的属性,那么就必须有十几组赋值和取值的对外方法。这样的工做量也是很是庞大的,PHP 提供了这组魔术方法来解决这个问题。

?php
//__set和__get
class Person
{
    private $gender;
    private $age;
    private $work;

    //动态通用赋值,$name表示属性名称,$value表示属性的值
    public function __set($name, $value)
    {
        //注意,这里的name 带$符号的,表示的是变量而不是属性
        //而$name可能就是某个属性,根据传递过来的参数决定
        //若是传递的是"gender"和"男",$this->gender = '男';
        $this->$name = $value;
    }

    //动态通用取值
    public function __get($name)
    {
        return $this->$name;
    }
}

$p1 = new Person;
$p1->gender = '男';  //这里至关于$p1->__set('gender', '男');
$p1->age = 30;
$p1->work = '医生';
echo $p1->gender.$p1->age.$p1->work;

返回值:
男30医生

2.__set和__get主要是对没法直接访问的属性起做用,若是是公共的属性private,它会直接赋值取值。
3.以前获取属性方式是$this->name,这种形式。若是用__set则是$this->$name,$name 变量经过参数传递,多是$name表示的是gender、age或work,这是动态的。

 

2.__construct构造
1.构造方法或构造函数是一种特殊的类的方法,早期使用只须要和类同名的方法便可实现构造方法。
2.而目前为了防止混淆,使用了魔术方法__construct 来取代类同名的方法,可读性更加的高。
3.构造方法的参数,由实例化时进行传递。

<?php

//构造方法
class Person
{
    public function __construct($name)
    {
        echo $name.'魔术方法构造!';
    }
}

$p1 = new Person('XiXi');
$p2 = new Person('Wang');

返回值:
XiXi魔术方法构造!Wang魔术方法构造!

4.因而可知,构造方法的目的是当对象实例化后,是对整个对象数据的初始化工做。就好像机器启动后,各个环节进行充能就位同样。
5.对于没有参数传递时,之后默认的写法就留着空括号便可,方便之后扩展,不须要刻意的去掉括号。
//不传参
$p1 = new Person();

 

3.__destructor()析构
1.析构方法和构造方法正好相反,当对象的方法所有执行完毕后自动调用析构方法。这种方法通常用于清理和销毁操做,好比清理内存,销毁数据库连接等。固然,这个方法不太经常使用,由于如今都自动清理(俗称垃圾回收)。

//析构方法
public function __destruct()
{
echo '析构方法,运行结束后销毁还原!';
}

 

4.__call 方法
1.因为某种缘由,有时可能调用了不存在的方法。这样,会报出一个找不到相关方法的错误

//调用不存在的方法语法:
class Person
{
}
$p1 = new Person();
$p1->run();
2.可使用__call 魔术方法来屏蔽错误调用,当调用不存在方式时,则执行这个魔术方法。须要填写两个参数:$name 表示方法名;$arguments 表示参数列表。
<?php
//调用不存在的方法
class Person
{
    //没有执行到指定方法则会自动执行__call魔术方法
    public function __call($name, $args)
    {
        echo $name.'方法不存在!';
        echo '<br>';

        foreach ($args as $value) {
            echo $value;
        }
    }
}

$p1 = new Person();
$p1->run('Mr.', 'Lee', 25);

返回值:
run方法不存在!
Mr.Lee25

3.相对于了还有一种是静态方法调用不存在时,使用__callStatic魔术方法。

<?php
//调用不存在的方法
class Person
{
    //没有执行到指定方法则会自动执行__call魔术方法
    public static function __callStatic($name, $args)
    {
        echo $name.'方法不存在!';
        echo '<br>';

        foreach ($args as $value) {
            echo $value;
        }
    }
}
Person::tool('Mr.', 'Lee', 25);

返回值:
tool方法不存在!
Mr.Lee25

5.__isset 方法
1.当不可对外访问的属性在类外调用了 isset/empty 方法判断是否设置或有值时,会自动调用__isset 魔术方法,来避免错误和返回判断结果。

<?php
//__isset判断非公开属性是否存在或是否有值
class Person
{
    private $name = 'Wang';

    public function __isset($name)
    {
        return isset($this->$name);
    }
}

$p1 = new Person();

echo isset($p1->name);

返回值:
1

详解:isset执行($this->$name)的时候发现private $name = 'Wang';是私有的执行不到,而后会自动跳到__isset($name)去执行

2.反之,还有一个__unset 魔术方法,即:调用unset不对外的属性时触发。

<?php
//__isset判断非公开属性是否存在或是否有值
class Person
{
    private $name = 'Wang';

    public function __isset($name)
    {
        return isset($this->$name);
    }

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

$p1 = new Person();

echo empty($p1->name);

unset($p1->name);

返回值:
1

6.__toString 方法
1.原则上,是没法直接输出(echo)对象信息的。而经过 var_dump 只能输出系统自己格式化后的信息。有时,咱们完成一个类,相对调用的开发这显示出这个类全部属性和方法具体的信息介绍,可使用__toString 魔术方法来自定义对象的信息输出格式。

<?php
//输出对象自定义信息
class Person
{
    private $name = 'XIXI';

    public function run()
    {
        return 'running...';
    }

    public function __toString()
    {
        $str = '';
        $str .= '私有字段:name,用于姓名的赋值和取值<br>';
        $str .= '公共方法:run(),用于对外输出名称';
        return $str;
    }
}
$p1 = new Person();

echo $p1;

私有字段:name,用于姓名的赋值和取值
公共方法:run(),用于对外输出名称
2.魔术方法的原理很简单,当没法执行或报错时就会调用这个方法。

 

对象中使用继承(解决代码中大量冗余和扩展的便利性)

继承的概念
1.继承是类与类之间相互结合的关系。通俗一点就是说:儿子(子类)从父亲(父类)或母亲那边继承了外貌(属性)和性格(方法)。
2.子类能够从父类那里继承或扩展,而父类并不知道子类继承了它。因此,一个父类能够有被多少个子类继承没有限制。
3.子类继承了父类的全部特性,并在这个基础上进行加强,最终获得独具一格的扩展。
4.继承的关键字是:extends。

<?php
//父类(基类)
class Father
{
    public $name = 'Mr.Wang';

    public function run()
    {
        return $this->name.'running...';
    }
}

//子类(派生类)
class Children extends Father
{
    //1.能够把非私有的属性继承下来
    //2.能够把非私有的方法继承下来
}

$c1 = new Children();
echo $c1->name;
echo $c1->run();

返回值:
Mr.WangMr.Wangrunning...

<?php
//父类
class Father
{
    protected $name = 'Mr.Wang';

    protected function run()
    {
        return $this->name . 'running...';
    }
}

//子类
class Children extends Father
{
    //受保护的,是能够继承下来的,可是不能类外访问
    public function test()
    {
        echo $this->name;
        echo $this->run();
    }
}

$c1 = new Children();
$c1->test();

返回值:
Mr.WangMr.Wangrunning...

5.子类继承了父类的属性和方法,那么实例化子类对象后,可直接调用属性和方法。
6.若是当父类的属性或方法设置为私有:private 时,那么就没法被子类继承。 

<?php
//父类
class Father
{
    private $name = 'Mr.Lee';

    private function run()
    {
        return $this->name . 'running...';
    }
}

//子类
class Children extends Father
{
    //私有的属性和方法没法继承
    public function test()
    {
        //echo $this->name;
        //echo $this->run();
    }
}

$c1 = new Children();
$c1->test();

7.若是父类建立了构造方法,而子类没有建立构造方法,则声明子类会自动执行父类的构造方法。

<?php
//父类
class Father
{
    public function __construct()
    {
        echo '父类构造';
    }
}

//子类
class Children extends Father
{
    
}

$c1 = new Children();

打印:
父类构造

覆盖重写
1.若是子类建立了构造方法,则父类的构造方法会被覆盖(重写),按规范要求须要在子类构造方法里调用父类构造。
2.使用 parent::__construct()方式能够调用父类的构造方法,parent 表示父类,也可使用父类名::__construct()调用,但推荐使用 parent。
3.普通的父类方法,也能够被子类重写,也可使用parent关键字调用父类方法。

<?php
//父类
class Father
{
    public function __construct()
    {
        echo '父类构造';
    }
}

//子类
class Children extends Father
{
    public function __construct()
    {
        //调用父类构造方法:父类名::__construct()
        //Father::__construct();
        //推荐parent表明父类
        //构造方法主要做用是初始化数据,父类的初始化也必须运行,因此子类构造须要调用父类构造
        parent::__construct();
        echo '子类构造';
    }
}

$c1 = new Children();

打印:
父类构造子类构造

3.普通的父类方法,也能够被子类重写,也可使用parent关键字调用父类方法。
4.通常来讲子类调用自身的方法使用$this 便可,也可使用 self::方法()来调用自身重写的方法,来强调不是调用父类方法。

<?php
//父类
class Father
{
    public function run()
    {
        echo '父类run';
    }
}

//子类
class Children extends Father
{
    //1.父类方法被继承,this就调用父类的方法
    //2.子类方法重写了,this就调用子类的方法
    //3.this可使用self来强调,我是调用的子类。
    public function run()
    {
        echo '子类run';
    }

    public function test()
    {
        //$this->run();
        self::run();
    }
}

$c1 = new Children();
$c1->test();

子类run
6.继承只支持单继承,也就是说,不能够同时继承多个父类

 

面向对象中的抽象类和其它的抽象方法

抽象类
1.抽象类提供了一种机制,父类中的抽象方法并无实现(仅提供方法声明),而是必需要求子类重写这个方法去实现它。
2.若是一个类中有抽象方法,那么此类必须声明成抽象类。
3.抽象类和抽象方法的声明修饰符是:abstract。
4.抽象方法声明的修饰符不能够是private的,必需要子类继承,private没法继承实现,而protected(受保护的)或public(公有的)都可。
5.抽象类没法实例化,也就是说,抽象方法的修饰符是protected和public并无什么区别。因此不少状况下,咱们发现都是省略不写的,按照规范来讲,建议写上去可读性高一些。

<?php
//抽象类
abstract class Person
{
//抽象方法
    abstract protected function run();
}
//子类
class Man extends Person
{
    //实现抽象方法
    public function run()
    {
    echo '运行...';
    }
}
$m1 = new Man();
echo $m1 ->run();

打印:
运行...

6.抽象方法里面也可使用普通的属性和普通方法,在子类继承下来,正常使用。

<?php
//抽象类
abstract class Person
{
    protected $name = 'Wang';
    public function getName()
    {
        return $this->name;
    }

//抽象方法
    abstract protected function run();
}
//子类
class Man extends Person
{
    //实现抽象方法
    public function run()
    {
         return '运行...';
    }
}
$m1 = new Man();
echo $m1 ->run();
echo $m1->getName();

打印:
运行...Wang

7.若是父类的抽象方法有参数,那么子类实现时,也必须跟着实现这两个参数。

<?php
//抽象类
abstract class Person
{
    protected $name = 'Wang';
    public function getName()
    {
        return $this->name;
    }

//抽象方法
    abstract protected function run($key, $value);
}
//子类
class Man extends Person
{
    //实现抽象方法
    public function run($key,$value)
    {
          return '运行...'.$key.$value;
    }
}
$m1 = new Man();
echo $m1 ->run(1,'Wang');
echo $m1->getName();

返回值:
运行...1WangWang
8.为什么要设计抽象方法这种只提供方法名和参数的机制呢?这就是面向对象的一种思惟,顶层设计出相应的规范,而后底层子类按照这个规范进行实现。能够提高代码的可读性、维护扩展性和协做性。
9.固然,抽象类不仅仅能够提供规范让子类实现,也能够本身实现让子类继承直接使用,很是的灵活。

 

面向对象中的接口

interface接口的使用
1.接口有两种概念:第一种是表示提供对外服务的出口,好比不少网站提供的API开发接口,支付宝、QQ登陆等等。而第二种是PHP的一个关键字语法:interface接口。
2.接口和抽象类很像,接口定义一条规范,总裁把各类标准计划设计好,让底层员工去具体实现。
3.接口比抽象类作的更加完全,它规定了全部方法都是抽象方法,只声明不实现。而且不须要在前面加上abstract。
4.接口的抽象方法,修饰符必须是 public,其它修饰符直接报错。
5.子类实现接口(相似继承),必须重写接口的抽象方法。

<?php
//接口
interface Person
{
    //抽象方法(作一个规范)
    public function run();
}

//子类实现接口
class Man implements Person
{
    //强制性实现抽象方法
    public function run()
    {
        echo '运行...';
    }

}

$m1 = new Man();
$m1->run();

返回:
运行...

6.接口不能设置属性,只能设置常量
常量使用调用:

<?php

class Person
{
    const PI = 3.14;
}

echo Person::PI;

打印:
3.14

子类实现接口调用:

<?php
//接口
interface Person
{
    const PI = 3.14;

    //抽象方法
    public function run();
}

//echo Person::PI;
//子类实现接口
class Man implements Person
{
    //实现父类接口的抽象方法
    public function run()
    {

    }
}

echo Man::PI;

返回:
3.14

7.子类能够实现多个接口。

<?php
//接口1
interface Person
{
    //抽象方法
    public function run();
}

//接口2
interface Computer
{
    //抽象方法
    public function sleep();
}

//子类实现两个接口
class Man implements Person,Computer
{
    public function run()
    {

    }

    public function sleep()
    {

    }
}

//接口能够多实现,继承只能单继承

 

面向对象中的多姿态

1.用术语来讲明多态性,就是经过多种状态或阶段描述相同对象的编程方法。它的意义在于,实际开发中,咱们只要关心接口或父类编程,并不须要关心一个对象所属于的具体类。

<?php
//人抽象类(父类)
abstract class Person
{
    //作两个规范声明
    abstract protected function type();
    abstract protected function content();
}
//理发师类(子类1)
class Barber extends Person
{
    public function type()
    {
        return '理发师';
    }
    //content要操做的内容
    public function content()
    {
        return '修理头发';
    }
}

//园丁类(子类2)
class Gardener extends Person
{
    public function type()
    {
        return '园丁';
    }

    public function content()
    {
        return '整理花卉';
    }
}

//总裁类(子类3)
class President extends Person
{
    public function type()
    {
        return '总裁';
    }

    public function content()
    {
        return '裁人';
    }
}

//剪刀类(行为类)
class Cut
{
    //这里的run须要传递一个参数
    //传递一我的的对象,好比园丁对象或理发师对象
    //能够写$object表示一个对象,可是用$person更具有语义
    //由于我不知道是园丁仍是理发师,仍是总裁,可是他们都是人,那么用$person更加合理
    public function run($person)
    {
        return $person->type().'正在'.$person->content().'中...';
    }
}

//实例化三我的(理发师,园丁,总裁)的对象
$b = new Barber();
$g = new Gardener();
$p = new President();

//实例化行为对象
$cut = new Cut();
//这里传对象,就是根据不一样的对象,去执行相同的方法,最终致使多态性
echo $cut->run($p);

传的总裁$p这个对象返回:
总裁正在裁人中...

7.从上面的例子看出,多态就是同一类对象在运行时的具体化。不一样的人物对象做为参数传入剪类,就会调用传入类的方法。
8.而抽象父类,规定了这些人物类必须实现一样的方法名,为多态实现了可能。
9.其实,多态的本质仍是条件判断语句。

 

类和对象的检测机制

类和对象的检测机制,这种机制可让程序检测对象的特性,包括对象名称、
类的检测
1.使用 class_exists()函数来肯定一个类是否存在。
//判断 Person 类是否存在
echo class_exists('Person');
2.使用 get_declared_classes()函数返回目前可用类的数组列表,使用var_dump没法全面打印。
//输出可用类列表
var_dump(get_declared_classes());
3.使用 get_class_methods()函数返回类中全部的方法,包括继承下来的。
//输出类中的方法
var_dump(get_class_methods('Person'));
4.使用 get_class_vars()函数返回类中全部的属性,包括继承下来的。
//输出类中的属性
var_dump(get_class_vars('Person'));

<?php
//
class Person
{
    public $name = 'Mr.Wang';

    public function run()
    {
        return '运行...';
    }
}

$p1 = new Person();

echo class_exists('Person');
var_dump(get_declared_classes());
var_dump(get_class_methods('Person'));
var_dump(get_class_vars('Person'));

注意:不对外的方法和属性获取不到。

 

对象检测
1.使用 get_class()函数来获取对象所属的类。
//获取对象所属的类
echo get_class($p1);
2.使用 method_exists()函数来判断对象的某个方法是否存在。
//判断对象的某个方法是否存在
echo method_exists($p1, 'run');
3.使用 get_object_vars()函数来获取对象中的属性列表。
//经过对象参数返回属性了列表
var_dump(get_object_vars($p1));
4.使用 get_parent_class()函数来获取父类的名称。
//返回父类的名称,传参能够是类名或对象名
echo get_parent_class('Person');

<?php
//父类
class A
{

}
//
class Person extends A
{
    public $name = 'Mr.Wang';

    public function run()
    {
        return '运行...';
    }
}

$p1 = new Person();

echo get_class($p1);
echo method_exists($p1, 'run');
var_dump(get_object_vars($p1));
echo get_parent_class('Person');

打印:
Person1
array (size=1)
  'name' => string 'Mr.Wang' (length=7)
A

 

检查自省的反射API,是一种对类和对象的自查机制(只不过是面向对象方式的)
使用反射
1.建立一个基本的类,包含了属性和方法
2.建立一个获取对象的反射类。
3.获取属性列表和方法列表。

<?php
//建立一个基本的类
class Person
{
    private $name;
    private $age;

    public function __set($name, $value)
    {
        $this->$name = $value;
    }

    public function __get($name)
    {
        return $this->$name;
    }
}
//实例化
$p = new Person();

//声明一个经过传递对象参数的反射类,这个类是系统内部类
$r = new ReflectionObject($p);

//属性列表
foreach ($r->getProperties() as $value) {
    var_dump($value);
}

//方法列表
foreach ($r->getMethods() as $value) {
    var_dump($value);
}

打印结果:
//打印属性列表
object(ReflectionProperty)[3]
  public 'name' => string 'name' (length=4)
  public 'class' => string 'Person' (length=6)

object(ReflectionProperty)[4]
  public 'name' => string 'age' (length=3)
  public 'class' => string 'Person' (length=6)
//打印方法列表
object(ReflectionMethod)[3]
  public 'name' => string '__set' (length=5)
  public 'class' => string 'Person' (length=6)

object(ReflectionMethod)[5]
  public 'name' => string '__get' (length=5)
  public 'class' => string 'Person' (length=6)

 

4.使用传递类名方式的反射类。

<?php
//建立一个基本的类
class Person
{
    private $name;
    private $age;

    public function __set($name, $value)
    {
        $this->$name = $value;
    }

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

$r = new ReflectionClass('Person');
//获取属性列表
foreach ($r->getProperties() as $value) {
    var_dump($value);
}
//获取方法列表
foreach ($r->getMethods() as $value) {
    var_dump($value);
}

打印:
//获取属性列表
object(ReflectionProperty)[2]
  public 'name' => string 'name' (length=4)
  public 'class' => string 'Person' (length=6)

object(ReflectionProperty)[3]
  public 'name' => string 'age' (length=3)
  public 'class' => string 'Person' (length=6)
//获取方法列表
object(ReflectionMethod)[2]
  public 'name' => string '__set' (length=5)
  public 'class' => string 'Person' (length=6)

object(ReflectionMethod)[4]
  public 'name' => string '__get' (length=5)
  public 'class' => string 'Person' (length=6)


反射的做用
1.反射能够用于生成类或对象的文档,提供开发者阅读;
2.可使用反射功能开发出各类插件系统,由于这种类型的系统老是要检测;
3.不少框架使用反射的特性动态的加载须要的类,判断方法等。

 

命名空间对类进行分装

命名空间概念
1.命名空间是一种封装事物的方法,就好像系统中的文件夹系统,若是文件名相同,能够存放在不一样的文件夹以免冲突。
2.命名空间就是为了防止类与类之间可能产生的冲突,而制定的这套机制。
3.还能够防止命名空间和PHP系统的类等发生冲突。
4.防止那些为了缓解冲突,声明很长的类名,提升可读性。
5.命名空间不必定非要和目录结构的名称同样,但同样的话,可读性会高。

定义命名空间
1.首先在第一个文件设置一个带有命名空间的类文件。

1.php

<?php
//设置命名空间(虚拟目录App)
namespace App;

class Person
{
    public function run()
    {
        return '1运行...';
    }
}

2.建立第二个文件2.php,引入1.php时,实例化Person。
2.php

<?php

require '1.php';
require '3.php';

$p1 = new \App\Person();
echo $p1->run();

$p2 = new \Home\Person();
echo $p2->run();

3.若是在3.php 中再建立一个Person类,只要命名空间不一样,就不会冲突了。
3.php

<?php
//设置命名空间(虚拟目录Home)
namespace Home;


class Person
{
    public function run()
    {
        return '3运行...';
    }
}

4.不一样命名空间的同名类,不会产生冲突。
//不冲突
$p1 = new \App\Person();
$p2 = new \Home\Person();

运行http://192.168.3.62/xiangmu3/2.php
1运行...3运行...

 

5.若是说,自己创建了命名空间机制,但并无重名类的问题,可使用use语法。
4.php

<?php

require '1.php';
require '3.php';

use App\Person;

$p1 = new Person();
echo $p1->run();

运行http://192.168.3.62/xiangmu3/4.php
1运行...

6.若是有重名的,就不适合使用 use 语法了。
7.也能够像文件系统那样定义多个子命名空间,让层次结构更加细化。
1.php

<?php
//设置命名空间(虚拟目录)
namespace App\Bev\Org;

class Person
{
    public function run()
    {
        return '1运行...';
    }
}

5.php

<?php

require '1.php';

use App\Bev\Org\Person;

//$p1 = new \App\Bev\Org\Person();
$p1 = new Person();
echo $p1->run();

运行http://192.168.3.62/xiangmu3/5.php
1运行...

8.使用__METHOD__常量能够获取方法的所有路径。
9.使用__FUNCTION__常量能够获得单纯的方法名
10.能够在有命名空间的文件下使用__NAMESPACE__常量获取命名空间路径。

1.php

<?php
//设置命名空间(虚拟目录)
namespace App\Bev\Org;

class Person
{
    public function run()
    {
        return '1运行...'.__METHOD__.__FUNCTION__.__NAMESPACE__;
    }
}

5.php

<?php

require '1.php';

use App\Bev\Org\Person;

//$p1 = new \App\Bev\Org\Person();
$p1 = new Person();
echo $p1->run();

运行http://192.168.3.62/xiangmu3/5.php
分别获得的返回值是:
1运行...
App\Bev\Org\Person::run
run
App\Bev\Org

11.使用use别名来简化重名类的冲突问题。
1.php

<?php
//设置命名空间(虚拟目录)
namespace App\Bev\Org;

class Person
{
    public function run()
    {
        return '1运行...'.__METHOD__.__FUNCTION__;
    }
}

3.php

<?php
//设置命名空间
namespace Home\Go\Back;


class Person
{
    public function run()
    {
        return '3运行...';
    }
}

7.php

<?php
require '1.php';
require '3.php';

use App\Bev\Org\Person as One;
use Home\Go\Back\Person as Two;

$p1 = new One();
echo $p1->run();

$p2 = new Two();
echo $p2->run();

http://192.168.3.62/xiangmu3/7.php
1运行...App\Bev\Org\Person::runrun3运行...

 

异常类

异常的概念
1.异常就是在可能发生错误的代码区域使用异常语法包裹住,当程序代码发生可能出现的错误,及时抛出异常的一种策略。
2.异常通常来讲须要如下四个步骤实现:
(1)程序尝试执行一些代码;
(2)若是执行失败,则抛出一个异常;
(3)捕获该异常;
(4)清理执行出现异常代码而遗留的资源。

使用异常类
1.PHP 中内置一个基本的异常类(Exception),基本的异常类用于在脚本发生异常时创建异常对象,该对象能够存储、抛出和捕获异常信息。
2.能够先执行一个会产生错误的代码。
若是被除数为0的话返回被除数不能为零!

<?php

$a = 5;
$b = 0;

if ($b == 0) {
    exit('被除数不能为零!');
} else {
    echo $a / $b;
}

返回值:
被除数不能为零!

3.若是使用异常方式,须要使用try catch语法。throw 是手动抛出异常,若是PHP有相关处理此异常的异常类,就会自动抛出,而不须要判断。

<?php

$a = 5;
$b = 0;

try {
    //throw new Exception手动抛出异常
    if ($b == 0) throw new Exception();
    //这句话可能会有错误
    echo $a / $b;

} catch (Exception $e) {
    echo '被除数不得为零!';
}

返回值:
被除数不得为零!

4.错误提示能够经过构造传参传递。
//经过构造传参抛出错误信息

<?php

$a = 5;
$b = 0;

try {
    //手动抛出异常
    if ($b == 0) throw new Exception('被除数不得为零');
    //这句话可能会有错误
    echo $a / $b;

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

返回值:
被除数不得为零

 

5.异常类有不少方法能够调用。
(1).getMessage():返回传递给构造方法的信息;
(2).getCode():返回传递给构造方法的代码;
(3).getFile():返回产生异常代码文件的完整路径;
(4).getLine():返回代码文件中产生代码的行号;
(5).getTrace():返回一个包含产生异常代码回退路径的数组;

<?php

$a = 5;
$b = 0;

try {
    //手动抛出异常
    if ($b == 0) throw new Exception('被除数不得为零');
    //这句话可能会有错误
    echo $a / $b;

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

返回值:.getMessage():被除数不得为零.getCode():0.getLine():8.getFile():E:\Program\www\xiangmu3\1.php

相关文章
相关标签/搜索