<?php

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

/*********************************

《关于PHP面向对象的总结》

**********************************

前言:

1.其实魔术方法也是方法是函数,跟前面讲的函数一样,只不过在类里面有

$this->name这样的东西,他就是类的成员属性,他可以在这个类的任意位置出现。

有可能这个会扰乱你的思路,你只需分分辨清楚就行了,既然都是

函数都道理都是一样的,有的是有计算功能的,有的有返回值的你用完函数后打印出来,

或者你不打印,在函数里面不用return 用输出都行。

2.$this可以理解为好比是对象的“引用”,我们知道对象访问他的成员属性和成员方法时只能用引用来访问

$this->name;

$this->say();

3.在类中某个成员属性如果在构造函数中不传入的话,在外部检测不到这个成员属性。

4.$class->nage 这个东西如果单独写,除了$this,他就是一个变量,你可以先赋值在输出试试,

5.魔术方法是在我们操作对象的时候自动运行,我们不确定他什么时候运行,但是他会自动运行。

6.类直接访问(不用创建对象)方法Person::tell();如果单纯的输入有值,如果里面有$this->什么的,就会报错了。


一.魔术方法

__construct()

__destruct()

__set()

__get()

__isset()

__unset()

__call()

__toString()

__autoload()

__sleep()

__wakeup()

__clone()

自动加载类方法

__autoload()


对象串行化方法

serialize()

unserialize()


二.常用的关键字和修饰字

var

public

protected

private

new

extends

final

static

const

instanceof

clone

abstract

interface

implements


三.单例模式

四.关于多态性

/




1.构造函数__construct()

构造函数可以接受参数,能够在创建对象时赋值给对象属性

构造函数可以调用类方法或其他函数

构造函数可以调用其他类的构造函数

<?php
class Person{
    private $name;
    private $age;
    private $sex;

    public function __construct($name,$age,$sex){
        $this->name=$name;
        $this->age=$age;
        $this->sex=$sex;
        $this->run($name);
    }
    private function run(){
        echo $this->name."在跑步";
    }
    public function say(){
        echo $this->name."在说话";
    }
    //下面这个是一个带参数
    public function say1($name){
        echo $name."在说话";
    }

}
$person = new Person("东方不败",18,'不详');
$person->say();
$person->say1("nihao");
?>

调用父类构造函数,调用其他类的构造函数:

public function __constuct(){
parent::__constuct();
classname::__construct();
}

如果想要依次调用几个父类的构造函数,可以使用类名直接调用构造函数


2.析构函数__destuct()

这个主要是对象在销毁时自动调用的函数,一个对象在数去引用时,会自动销毁。

$a=$b=$c=new Person();

$a = null;

unset($b);

一个对象三个引用,失去了两个还有一个,程序加载完成后会自动销毁最后一个。


3.赋值函数__set()

__set( $property, $value ) 给一个未定义的成员属性赋值时调用,(私有成员)


4.取值函数__get()

__get( $property ) 获取成员属性的值(私有成员)

先可以判定下是否有这个成员

return isset($this->$property) ? $this->$property : null;

class Person{
    private $name;
    private $age;
    private $sex;
    public function __construct($name,$age,$sex){
        $this->name=$name;
        $this->age=$age;
        $this->sex=$sex;
        $this->run();
    }
    private function run(){
        echo $this->name."在跑步".$this->age;
    }
    public function say(){
        echo $this->name."在说话";
    }
    public function say1($name){
        echo $name."在说话";
    }
    public function __set($field,$value){
        if($field=="age"){
            if($value>200){
                // return "非法数据";
                $value="非法数据";
            }
        }
        $this->$field=$value;
    }
    public function __get($field){
        return $this->$field;
    }
}
$person = new Person("东方不败",210,'不详');
$person->say();
// $person->run();
$person->say1("nihao");
echo "<br/>---------------------我是华丽丽的分界线---------------------<br/>";
$person->age=220;
echo $person->age;





5.__isset()方法用于检测私有属性值是否被设定。


如果对象里面成员是公有的,可以直接使用 isset() 函数。如果是私有的成员属性,

那就需要在类里面加上一个 __isset() 方法,然后再使用isset()函数如下

public function __isset($property_name)

{

    return isset($this->$property_name);

}

这样当在类外部使用 isset() 函数来测定对象里面的私有成员是否被设定时,就会自动调用 __isset() 方法来检测。



6.__unset()方法用于删除私有属性。

同 isset() 函数一样,unset() 函数只能删除对象的公有成员属性,当要删除对象内部的私有成员属性时,

需要使用__unset() 方法:

public function __unset($property_name)

{

    unset($this->$property_name);

}


7.__call() 方法用于监视错误的方法调用。

为了避免当调用的方法不存在时产生错误,可以使用 __call() 方法来避免。该方法在调用的方法

不存在时会自动调用,程序仍会继续执行下去。语法:

public function __call($function_name, $arguments)

{

    ......

}

该方法有两个参数,

第一个参数 $function_name 会自动接收不存在的方法名。

第二个 $args 则以数组的方式接收不存在的方法的多个参数。


8.__toString()方法用来输出对象的引用

这个方法也是一样自动被调用的,是在 直接输出对象引用时自动调用的, 前面我们讲过对象引用是一个指针,

比如说:“$p=new Person()”中,$p 就是一个引用,我们不能使用echo 直接输出$p,这样会输出"Catchable fatal 

error: Object of class Person could not be converted to string"这样的错误,如果你在类里面定义 

了"__toString()"方法,在直接输出对象引用的时候,就不会产生错误,而是自动调用了 "__toString()"方法,

输出"__toString()"方法中返回的字符,所以"__toString()"方法一定 要有个返回值(return 语句)。


9.__autoload()方法用于自动加载其他文件中的类。

这个方法是唯一一个不在类中写的方法。

function __autoload($classname){

    include_once ucfirst($classname)."Controller.php";

}


10.__sleep()方法是对象在序列化时执行的方法

注意了,对象在序列化后里面保存的只是他的属性(初始化的时候赋值了),函数和方法都是类的,不是对象的。这点要明确

11.__wakeup()方法是对象在反序列化时执行的方法.

在wake.php中

class db {
    private $host;
    private $user;
    private $pwd;
    private $dbname;
    private $mysqli;
    function __construct($host, $user, $pwd, $dbname) {
        $this->host = $host;
        $this->user = $user;
        $this->pwd = $pwd;
        $this->dbname = $dbname;
        $this->db();
    }
    function db() {
        $this->mysqli = new mysqli ( $this->host, $this->user, $this->pwd, $this->dbname );
    }
    function select() {
        $this->mysqli->query("SET CHARSET GBK");
        $sql = "SELECT * FROM ams_archives_rs LIMIT 0,4";
        $result = $this->mysqli
            ->query ( $sql );
        $rows = array ();
        while ( $row = $result->fetch_assoc () ) {
            $rows [] = $row;
        }
        ECHO "<PRE>";
        print_r ( $rows );
    }
    function __sleep(){
        return array_keys(get_object_vars($this));
    }
    function __wakeup(){
        $this->db();
    }
}
session_start();
$chanel = new db("localhost",'root','admin888','ams');
//$chanel->select();
$_SESSION['channel_obj'] = serialize($chanel);


在wakeupa.php中

session_start();
include 'wakeup.php';
$channel_obj=unserialize($_SESSION['channel_obj']);
$channel_obj->select();
//如果在59.php中不写上__wakeup()方法会连接不上
11.__clone()这个使用关键字clone时自动运行的方法
public function __clone(){
    $this->class=2;
}
$person1=clone $person;
echo $person1->class;


二、这几个关键字我们挑选几个不常用的看看


1.const

他在类的内部使用的方法如下

self::常量名大写;

他在类的外部使用方法

类名::常量名大写;


2.extends 英文extend的第三人称单数是扩展继承的意思


3.final

可以修饰类和方法

修饰类,这个类不能被继承

修饰方法,这个方法不能被重写




4.static

翻译中文是静态的意思

用来修饰成员属性和方法


如果用来修饰成员属性,初始化后的所有对象都是共用这个成员属性。

他在类的内部使用方法如下

public function getName(){
    //return 类名::静态变量;
    //return self::静态变量;
    //return Person::$name;
    return self::$name;
}
public function setName($val){
    Person::$name=$val;
    //self::$name=$val;
}

$p->setName('东方不败');

//这里设置后所有的对象name都是东方不败了


他在类的外部使用的方法如下

Person::$name;//只能用类名::方法名


如果是用来修饰成员方法,他就不能访问非静态成员,因为非静态的成员必须用对象的引用来访问。

因此,用static修饰的方法里面就一定不使用非静态成员


5.instanceof

这个是用来判断一个对象是不是某个类实例化出来的

$p=new Person();

echo intval($p instanceof Person);

//还有一个函数也可以

echo intval(is_a($p,'Person');//PHP官方不建议使用

还有一个函数是用来判断一个对象是不是某个类的子类实例化出来的(父亲级别)

is_subclass_of($p,'Pseson');


6.abstract

中文意思是抽象的意思

这个关键字既可以修饰类也可以修饰方法


关于抽象方法:没有方法体的方法称作抽象方法,注意在类中声明抽象方法必须有abstract修饰

function fun();


关于抽象类:如果一个类中有abstract修饰的方法那么这个类就是抽象类

abstract class Person{

    abstract function fun();

}

抽象类是为了规定一些共性的成员,一般是给了N个抽象类,抽象类自己不

被实例化,必须由子类继承过来(所以子类就不能再是抽象类了,而且非抽象方法和成员属性的访问

权限也就必须得protected和public),而且子类必须逐一实现这N个抽象方法;而且实现的方法必须

是public 或者不写;


7.interface

中文意思为接口

PHP是单继承,每个类只能继承一个父类


他是一种特殊的类,

接口里面的成员必须全是public权限,

接口里面的方法全都是抽象方法,并且不能用abstract修饰,

接口里的成员属性只能是常量(const NAME='hby');


可以写另外一个接口来继承(extends)一个接口,用来扩展里面的方法。

可以写一个抽象类来实现(implements)一个接口,用来实现里面的部分方法

可以写一个普通类来实现(implements)一个接口,用来实现里面的全部方法

一个类可以同时继承父类还实现N个接口.

class 类名 extends 父类名 implements 接口1,接口2,...接口n{

    //实现所有接口中的抽象方法;

}


三、单例模式

网上说的意思归结到一点就是:让一个类只有一个实例.

如何做到呢?

1.前面学过,每次用 new 类名 的方式,就可以创建一个对象。我们必须禁止

外部程序用 new 类名的方式来创建多个实例。

解决办法是:我们将构造函数设置成 private ,让构造函数只能在内部被调用,

而外部不能调用。这样,这个类就不能被外部用 new 的方式建立多个实例了。

class A{

    private function __construct(){}    

}

$a = new A();


2.我们已经禁止外部用new实例化这个类,我们改如何让用户访问这个类呢?前门堵了,

我们需要给用户留个后门。

解决办法是:static 修饰的方法,可以不经实例化一个类就可以直接访问这个方法。

class A{
    private function __construct(){}
    static function getClassA(){
        return "这里是后门,可以通过这里进入类内部..";
    }
}
echo A::getClassA();


3.虽然我们已经进入类内部,但我们要的是这个类的唯一实例?先不管别的,

我们先需要一个实例。通过这个static的方法返回这个实例,如何做呢?

下面的例子我们确实返回了A类的实例,但注意两次执行返回的不是同一个实例。

class A{
    private function __construct(){}
    static function getClassA(){
        $a = new A();
        return $a;
    }        
}

// 看到这里确实返回的是 A 的实例.但不是同一个对象.

$a1 = A::getClassA();
$a2 = A::getClassA();
echo "\$a1 的类是 ".get_class($a1)." , \$a2 是 ".get_class($a1);
if($a1 === $a2){ 
   echo "<br> \$a1 \$a2 指向同一对象.";
}else{ 
   echo "<br> \$a1 \$a2 不是一个对象.";
}


4.我们已经通过static方法返回了A的实例。但还有问题。我们如何保证我们

多次操作获得的是同一个实例的呢?解决办法:static的属性在内部也只有一个。

static 属性能有效的被静态方法调用。将这个属性也设置成private,以防止外部调用。

先将这个属性设置成 null。每次返回对象前,先判断这个属性是否为 null 。

如果为 null 就创建这个类的新实例,并赋值给这个 static 属性。如果不为空,

就返回这个指向实例的 static 属性。

class A{
    private static $a = null;
    private function __construct(){}
    static function getClassA(){
        if( null == self::$a){
            self::$a = new A();
        }       
        return self::$a;
    }        
}
// 看到这里确实返回的是 A 的实例.但不是同一个对象.
$a1 = A::getClassA();
$a2 = A::getClassA();
echo "\$a1 的类是 ".get_class($a1)." , \$a2 是 ".get_class($a1);
if($a1 === $a2){ 
   echo "<br> \$a1 \$a2 指向同一对象.";
}else{ 
   echo "<br> \$a1 \$a2 不是一个对象.";
}

最终这里无论是多少次都是同一个对象。


下面给一个例子

<?php
class Database {
    private $_db;
    static $_instance;
    final private function __construct($dbname,$user='root',$password='',$port='3306') {
        $conStr = "dbname=".$dbname." user=".$user." password=".$password." port=".$port;
        $this->_db = pg_connect($conStr);
    }
    private __clone() {};//不允许被克隆
    public static function getInstance($dbname,$user='root',$password='',$port='3306') {
        if( ! (self::$_instance instanceof self) ) {
            self::$_instance = new self($dbname,$user='root',$password='',$port='3306');
        }
        return self::$_instance;
    }
    public function query($sql) {
        return pg_query($this->_db, $sql);
    }
}
?>



代码从这开始

**/

class Person{
    const HIGH='170';
    private $name;
    private $age;
    private $sex;
    public $class;
    public function __construct($name,$age,$sex,$class){
        $this->name=$name;
        $this->age=$age;
        $this->sex=$sex;
        $this->class=$class;
        $this->run();
    }
    private function run(){
        echo $this->name."在跑步".$this->age;
    }
    public function say(){
        echo $this->name."在说话";
    }
    public function say1($name){
        echo $name."在说话";
    }
public function __set($field,$value){
if($field=="age"){
if($value>200){
// return "非法数据";
                $value="非法数据"; 
}
}
$this->$field=$value;
}
    public function __get($field){
        return $this->$field;
    }
    public function __isset($property_name){
        return isset($this->$property_name);
    }
    public function __unset($property_name){
        unset($this->$property_name);
    }
    public function __call($function_name,$arguments){
        echo "您所调用的方法".$function_name."参数<br/>";
        echo var_dump($arguments)."不存在";
    }
    public function __toString(){
        return $this->name."qwertyuiop";
    }
    public function __clone(){
        $this->class='2';
    }
    public function tellhigh(){
        echo "身高是".self::HIGH;
    }
}
$person = new Person("东方不败",210,'不详','大学');
$person->say();
// $person->run();
$person->say1("nihao");
echo "<br/>---------------------我是华丽丽的分界线---------------------<br/>";
$person->age=220;
echo $person->age;
echo "<br/>---------------------我是华丽丽的分界线---------------------<br/>";
echo isset($person->class) ? "存在":"不存在";
echo "<br/>---------------------我是华丽丽的分界线---------------------<br/>";
unset($person->age);
echo isset($person->class) ? "存在":"不存在";
echo isset($person->age) ? "存在":"不存在";
echo "<br/>---------------------我是华丽丽的分界线---------------------<br/>";
$person->age=80;
echo isset($person->age) ? "存在":"不存在";
echo "<br/>---------------------我是华丽丽的分界线---------------------<br/>";
$person->drink(123,"we");
echo "<br/>---------------------我是华丽丽的分界线---------------------<br/>";
echo $person;//输出对象的引用
echo "<br/>---------------------我是华丽丽的分界线---------------------<br/>";
//下面写一个自动加载函数
function __autoload($classname){
    include_once ucfirst($classname)."Controller.php";
}
$mx=new Phone(1999,'4.5英寸','2200mah','魅族MX2','MX2');
$mx->tell();
echo Phone::FUNC;
echo "<br/>---------------------我是华丽丽的分界线---------------------<br/>";
$person1=clone $person;
echo intval($person1===$person);
echo $person1->class;
echo "<br/>---------------------我是华丽丽的分界线---------------------<br/>";
$person->tellhigh();
echo "<br/>---------------------我是华丽丽的分界线---------------------<br/>";
echo Person::HIGH;
echo "<br/>---------------------我是华丽丽的分界线---------------------<br/>";
abstract class Student{
    abstract function say();
    abstract function study();
    public function tell(){
        echo "这是个抽象类里面的普通方法";
    }
}
class LStudent extends Student{
    public function a(){
        echo "我是LStudent类中的a方法";
    }
    public function say(){
        echo "重写抽象类中的say方法";
    }
    function study(){
        echo "重写抽象类中的study方法";
    }
}
$LS=new LStudent();
$LS->a();
echo "<br/>---------------------我是华丽丽的分界线---------------------<br/>";
$LS->say();
echo "<br/>---------------------我是华丽丽的分界线---------------------<br/>";
interface PS{
    const NAME='串口';
    function start();
    function work();
}
interface PS1 extends PS{
    function stop();
}
abstract class PS2 implements PS{
    function start(){
        echo "抽象类实现部分方法这里是start方法";
    }
}
class PS3 implements PS1{
    function start(){
        echo "抽象类实现全部方法这里是start方法";
    }
    function work(){
        echo "抽象类实现全部方法这里是work方法";
    }
    function stop(){
        echo "抽象类实现全部方法这里是stop方法";
    }
}
$ps=new PS3();
$ps->start();
echo "<br/>---------------------我是华丽丽的分界线---------------------<br/>";
echo PS::NAME;
?>