每个类的定义都以关键字 class 开头,后面跟着类名,后面跟着一对花括号,里面包含有类的属性与方法的定义。

类名可以是任何非 PHP 保留字 的合法标签。一个合法类名以字母或下划线开头,后面跟着若干字母,数字或下划线。

一个类可以包含有属于自己的 常量变量(称为“属性”)以及函数(称为“方法”)。

1. 封装类

  • 格式

    1. class 类名{
    2. 访问控制符 属性;
    3. ...
    4. 方法;
    5. ...
    6. }
  • 实例

    <?php
    // 封装类
    class Person{
       // 类中包含的属性
       public $name = "tom";
       public $age = 12;
       public $gender = "male";
    
       // 无参方法
       function show(){
          echo "this is obj method";
       }
    
       // 带参数的方法
       function display($str){
          echo "params is :" . $str;
       }
    
       // 带参带返回值的方法
       function getUpperString($str){
          return strtoupper($str);
       }
    }
    ?>
    

2. 实例对象

类是抽象的,不是客观存在的,如果要使用类中的内容,必须要进行实例化,

要创建一个类的实例,必须使用 new 关键字。类应在被实例化之前定义。

  • 格式

    对象变量 = new 类名();
    
  • 实例

    $obj1 = new Person();
    $obj2 = new Person();
    var_dump($obj1);
    echo '<br>';
    var_dump($obj2);
    echo '<br>';
    

3. 对象属性

类的变量成员叫做 属性,或者叫 字段,在本文档统一称为 属性

从 PHP 7.4 开始,属性声明是由关键字 publicprotected 或者 private 开头,然后跟变量声明来组成。

属性中的变量可以初始化,但是初始化的值必须是常数 。

通过->符号,可以在程序中,使用实例对象,访问该对象的属性

  • 格式

    对象变量->属性名
    
  • 实例

    $obj1 = new Person();
    $obj2 = new Person();
    
    echo $obj1->name;
    echo '<br>';
    echo $obj1->age;
    echo '<br>';
    echo $obj1->gender;
    echo '<hr>';
    echo $obj2->name;
    echo '<br>';
    echo $obj2->age;
    echo '<br>';
    echo $obj2->gender;
    

4. 对象方法

同样,也可以使用 ->调用对象的方法.

  • 格式

    对象名->方法名();
    
  • 实例
    ```php $obj1 = new Person(); $obj2 = new Person();

$obj1->show(); echo ‘
‘; $obj1->display(“Hello”); echo ‘
‘; echo $obj1->getUpperString(‘world’); echo ‘


‘; $obj2->show(); echo ‘
‘; $obj2->display(“Hello”); echo ‘
‘; echo $obj2->getUpperString(‘world’);


<a name="211cdaac"></a>
#### 5. 构造方法

前面定义的类只是让大家了解如何定义一个类,并通过类实例出对象进行使用,但并无实际意义,因为所有的实例对象都是相同的属性和方法.

PHP 允许开发者在一个类中定义一个方法作为构造函数。

具有构造函数的类会在每次创建新对象时自动调用此方法,所以非常适合在使用对象之前做一些初始化工作。

注意: 构造方法不是必须的,所以一个类中也可以没有构造方法.

-  格式  
```php
public function __construct(mixed ...$values = ""): void
  • 实例
    ```php <?php // 封装类 class Person{ // 类中包含的属性 public $name = NULL; public $age = NULL; public $gender = NULL;

       // 构造方法在实例对象时,自动调用 
    

    public function __construct($name, $age, $gender){

      $this->name = $name;
      $this->age = $age;
      $this->gender = $gender;
    

    }

    } // 在实例对象时,参数会被传入到构造方法中对属性进行初始化 $obj1 = new Person(“tom”,12,’male’); $obj2 = new Person(“jack”,23,’female’);

    echo $obj1->name; echo ‘
    ‘; echo $obj1->age; echo ‘
    ‘; echo $obj1->gender; echo ‘


    ‘; echo $obj2->name; echo ‘
    ‘; echo $obj2->age; echo ‘
    ‘; echo $obj2->gender;

?>


<a name="f41b9dae"></a>
#### 6. 析构方法

PHP 有析构函数的概念,这类似于其它面向对象的语言,如 C++。

析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。

因为PHP中存在垃圾回收机制 ,所以一般情况下,不会使用析构方法.

-  格式  
```php
function __destruct(): void
  • 实例
    ```php <?php // 封装类 class Person{ // 类中包含的属性 public $name = NULL; public $age = NULL; public $gender = NULL;

    public function __construct($name, $age, $gender){

      $this->name = $name;
      $this->age = $age;
      $this->gender = $gender;
    

    }

    function __destruct() {

      print "Destroying " . __CLASS__ . "\n";
    

    }

    }

    $obj3 = new Person(“tom”,12,’male’); $obj1 = $obj3; // 因为有两个引用 $obj1和 $obj3 同时引用一个对象,所以当 unset($obj3)时,该对象并没有马上被消毁 unset($obj3); $obj2 = new Person(“jack”,23,’female’);

echo $obj1->name; echo ‘
‘; echo $obj1->age; echo ‘
‘; echo $obj1->gender; echo ‘


‘; echo $obj2->name; echo ‘
‘; echo $obj2->age; echo ‘
‘; echo $obj2->gender;

?>


<a name="a2583a2a"></a>
#### 7. $this

当一个类中的方法或属性, 在类定义内部的另一个方法中被调用时, 可以使用伪变量 $this .

![](https://g.yuque.com/gr/latex?this%20%E7%9A%84%E5%8A%9F%E8%83%BD%E5%B0%B1%E6%98%AF%E4%BB%A3%E8%A1%A8%E5%BD%93%E5%89%8D%E5%AF%B9%E8%B1%A1%2C%E5%93%AA%E4%B8%AA%E5%AF%B9%E8%B1%A1%E4%BD%BF%E7%94%A8%E5%B1%9E%E6%80%A7%E6%88%96%E6%96%B9%E6%B3%95%2C#card=math&code=this%20%E7%9A%84%E5%8A%9F%E8%83%BD%E5%B0%B1%E6%98%AF%E4%BB%A3%E8%A1%A8%E5%BD%93%E5%89%8D%E5%AF%B9%E8%B1%A1%2C%E5%93%AA%E4%B8%AA%E5%AF%B9%E8%B1%A1%E4%BD%BF%E7%94%A8%E5%B1%9E%E6%80%A7%E6%88%96%E6%96%B9%E6%B3%95%2C&id=bhTv7)this 就表示哪个对象,作用相当于现实语言中代词`我`.

注意:在使用 ![](https://g.yuque.com/gr/latex?this%20%E5%BC%95%E7%94%A8%E5%B1%9E%E6%80%A7%E6%97%B6%2C%E7%9B%B8%E5%BD%93%E4%BA%8E%60#card=math&code=this%20%E5%BC%95%E7%94%A8%E5%B1%9E%E6%80%A7%E6%97%B6%2C%E7%9B%B8%E5%BD%93%E4%BA%8E%60&id=gc6X7)`符号被 前置了,所以变量前不能再加`$`符号

-  格式  
```php
$this->属性名;
$this->方法();
  • 实例

    <?php
    // 封装类
    class Person{
       // 类中包含的属性
       public $name = NULL;
       public $age = NULL;
       public $gender = NULL;
    
       public function __construct($name, $age, $gender){
          $this->name = $name;
          $this->age = $age;
          $this->gender = $gender;
       }
    
       public function toupper(){
          return strtoupper($this->name);
       }
    
       public function info(){
          echo $this->toupper() . $this->age . $this->gender;
       }
    
    }
    
    $obj1 = new Person("tom",12,'male');
    $obj1->info();
    ?>
    

8. 魔术方法 __set/___get/__toString()

  • 没有魔术方法时,操作对象属性
    ```php <?php class Site { / 成员变量 / public $title; private $url;

    / 成员函数 / public function setUrl($par){ $this->url = $par; }

    public function getUrl(){ echo $this->url ; } }

$obj1 = new Site(); // 直接访问公有属性不安全 $obj1->title = “百度”; // 如果属性为私有,那么每个属性都要设置 set/get方法,非常麻烦 $obj1->setUrl(“www.baidu.com”);

echo $obj1->title; echo $obj1->getUrl(); ?>


-  使用魔术方法 
   -  可以隐藏属性 
   -  可以进行数据检查 
   -  可以按需求格式化对象输出形式 

```php
<?php
class Site {
  /* 成员变量 */
  private $title;
  private $url;


/**
    * __set()方法用来设置私有属性
    *
    * @param unknown $name
    * @param unknown $value
    */
    public function __set($key,$value){
      switch ($key){
          case "title":
              $this->title = $value;
              break;
          case "url":
              $this->url = $value;
              break;
          }

  }
  /**
  * __get()方法用来获取私有属性
  *
  * @param unknown $name
  */
  public function __get($key){
      if(isset($key)){
          return $this->$key;
      }
      return false;
  }

  public function __toString()
  {
     return "网站名:" . $this->title . "<br>网站地址:" . $this->url;
  }
}

$obj1 = new Site();
$obj1->title = "百度";
$obj1->url = "www.baidu.com";

echo $obj1->title;
echo $obj1->url;
echo '<hr>';
echo $obj1;
?>

9. 访问控制符

对属性或方法的访问控制(PHP 7.1.0 以后支持常量),是通过在前面添加关键字 public(公有),protected(受保护)或 private(私有)来实现的。

  • public 被定义为公有的类成员可以在任何地方被访问。
  • protected 被定义为受保护的类成员则可以被其自身以及其子类和父类访问。
  • private 被定义为私有的类成员则只能被其定义所在的类访问。

属性的访问控制

属性必须定义为公有,受保护,私有之一。如果用 `var` 定义,则被视为公有。
<?php
class MyClass
{
    public $public = 'Public';
    protected $protected = 'Protected';
    private $private = 'Private';

    function printHello()
    {
        echo $this->public;
        echo $this->protected;
        echo $this->private;
    }
}

$obj = new MyClass();
echo $obj->public; // 这行能被正常执行
echo '<br>';
// echo $obj->protected; // 这行会产生一个致命错误
// echo $obj->private; // 这行也会产生一个致命错误
$obj->printHello(); // 输出 Public、Protected 和 Private
?>

方法的访问控制

类中的方法可以被定义为公有,私有或受保护。如果没有设置这些关键字,则该方法默认为公有。
<?php
class MyClass
{
    // 声明一个公有的构造函数
    public function __construct() {
      echo "Construct Method<br>";
     }

    // 声明一个公有的方法
    public function MyPublic() { 
       echo "Public Method<br>";
    }

    // 声明一个受保护的方法
    protected function MyProtected() { 
      echo "Protected Method<br>";
    }

    // 声明一个私有的方法
    private function MyPrivate() {
      echo "Private Method<br>";
     }

    // 此方法为公有
    function Foo()
    {
        $this->MyPublic();
        $this->MyProtected();
        $this->MyPrivate();
    }
}

$myclass = new MyClass;
$myclass->MyPublic(); // 这行能被正常执行
// $myclass->MyProtected(); // 这行会产生一个致命错误
// $myclass->MyPrivate(); // 这行会产生一个致命错误
$myclass->Foo(); // 公有,受保护,私有都可以执行
?>

10. 静态属性和静态方法

声明类属性或方法为静态,就可以不实例化类而直接访问。

可以在实例化的类对象中通过静态访问。

由于静态方法不需要通过对象即可调用,所以伪变量 $this 在静态方法中不可用。

一般类中存在静态方法时,都做为工具类的封装使用,在这种类使用时,不需要创建对象,相当于当相关的一些方法归类到一个统一的类中

<?

/**
 *  PDO数据库操作封装类
 *  */ 
class MySQLDB{

    // 查询多条记录操作
    public static function queryAll($sql){
        $url = "mysql:host=localhost;dbname=test";
        $db = new PDO($url, "root", '');
        $st = $db->query($sql);
        $rs = $st->fetchAll(PDO::FETCH_ASSOC);
        return $rs;
    }

    // 查询一条记录操作
    public static function queryOnce($sql){
      $url = "mysql:host=localhost;dbname=test";
      $db = new PDO($url, "root", '');
      $st = $db->query($sql);
      $rs = $st->fetch(PDO::FETCH_ASSOC);
      return $rs;
    }

    // 增删改
    public static function exec($sql){
      $url = "mysql:host=localhost;dbname=test";
      $db = new PDO($url, "root", '');
      return $db->exec($sql);
    }

}
   var_dump( MySQLDB::queryAll("select * from user"));
   echo '<hr>';
   var_dump( MySQLDB::queryOnce("select * from user where id = 1"));
?>

11. self与范围解析操作符::

由于静态属性和静态方法在使用时,可能没有实例对象存在,所以在静态方法中引用其它静态属性或方法时,不能使用 $this->

所以如果想要在静态方法中引用其它的静态属性和静态方法,需要使用 self::进行引用 .

<?

/**
 *  PDO数据库操作封装类
 *  */ 
class MySQLDB{
   // 静态属性
   private static $db = NULL;
   // 静态方法
   public static function createDbConnect($url,$user,$pwd){
      if(self::$db == NULL){
         self::$db = new PDO($url,$user,$pwd);
      }
   }

    // 查询多条记录操作
    public static function queryAll($sql){
        self::createDbConnect("mysql:host=localhost;dbname=test","root","");
        $st = self::$db->query($sql);
        $rs = $st->fetchAll(PDO::FETCH_ASSOC);
        return $rs;
    }

    // 查询一条记录操作
    public static function queryOnce($sql){
      self::createDbConnect("mysql:host=localhost;dbname=test","root","");
      $st = self::$db->query($sql);
      $rs = $st->fetch(PDO::FETCH_ASSOC);
      return $rs;
    }

    // 增删改
    public static function exec($sql){
      self::createDbConnect("mysql:host=localhost;dbname=test","root","");
      return self::$db->exec($sql);
    }

}
   var_dump( MySQLDB::queryAll("select * from user"));
   echo '<hr>';
   var_dump( MySQLDB::queryOnce("select * from user where id = 3"));
?>

12. 克隆对象

将一个对象赋值给另一个对象时,只是引用的赋值,两个对象实际指向同一个空间.

当对其中一个对象进行修改时,另一个对象的值也随之发生变化.

克隆会对当前对象进行一次深层拷贝,克隆后的对象都有一个独立的内存空间,互不影响 .

<?php
class Site {
  /* 成员变量 */
  var $url;
  var $title;

  /* 成员函数 */
  function setUrl($par){
     $this->url = $par;
  }

  function getUrl(){
     echo $this->url ;
  }

  function setTitle($par){
     $this->title = $par;
  }

  function getTitle(){
     echo $this->title ;
  }
}

$obj1 = new Site();
$obj2 = new Site();
$obj3 = clone($obj1);
$obj4 = $obj1;
$obj1->setUrl("www.baidu.com");
$obj3->setUrl("www.taobao.com");
$obj2->setTitle("网易");

var_dump($obj1);
echo '<br>';
var_dump($obj2);
echo '<br>';
var_dump($obj3);
echo '<br>';
var_dump($obj4);
?>

13. 单例类

在一些情况下,不需要创建多个对象存在,但有多个创建使用对象的场景,此时可以使用单例来创建对象

实现单例的必要条件

  • 一个私有的,静态的,用于保存当前对象的变量
  • 判断该变量是否初始化过,如果未初始化则创建对象,初始化过则不创建对象
  • 重写构造方法和克隆方法
<?

use MySQL as GlobalMySQL;

/**
 *  PDO数据库操作封装类
 *  */ 
class MySQLDB{
    // 数据库连接对象
    private $db = NULL;
    // 结果集对象
    private $rs = NULL;

    // 私有化构造函数
    private function __construct($host, $user, $passwd, $dbname){
        $url = "mysql:host={$host};dbname={$dbname}";
        $this->db = new PDO($url, $user, $passwd);
    }

    // 私有化克隆方法,防止克隆对象
    private function __clone(){}

    // 单例方法
    public static function getInstance($host, $user, $passwd, $dbname){

        static $obj = NULL;
        if($obj == NULL){
            $obj = new MySQLDB($host, $user, $passwd, $dbname);
        }
        return $obj;
    }

    // 查询多条记录操作
    public function queryAll($sql){
        $st = $this->db->query($sql);
        $this->rs = $st->fetchAll(PDO::FETCH_ASSOC);
        return $this->rs;
    }

    // 查询一条记录操作
    public function queryOnce($sql){
        $st = $this->db->query($sql);
        $this->rs = $st->fetch(PDO::FETCH_ASSOC);
        return $this->rs;
    }

    // 增删改
    public function exec($sql){
        return $this->db->exec($sql);
    }
}

 $obj1 = MySQLDB::getInstance("localhost",'root','','test');
 $obj2 = MySQLDB::getInstance("localhost",'root','','test');
 var_dump($obj1);
 echo '<hr>';
 var_dump($obj2);

?>

14. 案例

见OOP