IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> PHP知识库 -> PHP学习笔记10:类和对象 II -> 正文阅读

[PHP知识库]PHP学习笔记10:类和对象 II

PHP学习笔记10:类和对象 II

image-20211129162010327

图源:php.net

自动加载

在现实项目中,我们往往会将类定义单独放在一个php文件中,比如有一个类MyClass,对应的php文件名可能是my_class.cls.php,使用的时候要先使用requireinclude加载对应的文件。

除了手动加载文件以外,php还提供一种自动加载类文件的机制:

<?php
require_once "../../util/class.php";
spl_autoload_register(function ($clsName){
    $fileName = "./".convert_clsname_to_fname($clsName).".cls.php";
    require_once $fileName;
});
$mc = new MyClass;
var_dump($mc);

像上面展示的那样,可以使用spl_autoload_register函数注册一个自定义的类加载器,加载器的具体职责就是根据类名加载对应的类源码文件。

这里的convert_clsname_to_fname工具函数是我编写的一个可以将驼峰样式类名转换为下划线连接的样式的函数。代码行数较多,就不在这里展示了,感兴趣的可以前往本笔记的Github仓库查看。

php解释器会在遇到new MyClass后调用注册的类加载器加载对应的类文件,这样就可以正确使用类了。

构造函数

构造函数的主要用途是在创建类实例时对其进行初始化。

默认情况下子类的构造函数不会主动调用父类的构造函数:

<?php
class Base
{
    public function __construct()
    {
        echo "Base::__construct is called." . PHP_EOL;
    }
}

class Child extends Base
{
    public function __construct()
    {
        echo "Child::__construct is called." . PHP_EOL;
    }
}

$child =  new Child();
// Child::__construct is called.

为了能正确调用父类的构造函数,一般要这样:

...
class Child extends Base
{
    public function __construct()
    {
        parent::__construct();
        echo "Child::__construct is called." . PHP_EOL;
    }
}

$child =  new Child();
// Base::__construct is called.
// Child::__construct is called.

如果子类没有定义构造方法,会继承父类的构造方法:

<?php
class Base
{
    public function __construct()
    {
        echo "Base::__construct is called." . PHP_EOL;
    }
}

class Child extends Base
{
}

$child =  new Child();
// Base::__construct is called.

与其它函数不同,构造函数不会受到“函数签名兼容性规则”的限制,这很好理解,因为构造函数并不涉及多态的问题:

<?php
class Base
{
    public function __construct($param)
    {
        echo "Base::__construct is called." . PHP_EOL;
        echo "\$param:{$param}" . PHP_EOL;
    }
}

class Child extends Base
{
    public function __construct()
    {
        parent::__construct('test');
        echo "Child::__construct is called." . PHP_EOL;
    }
}

$child =  new Child();
// Base::__construct is called.
// $param:test
// Child::__construct is called.

上面的例子中,子类的构造函数不需要参数,父类的构造函数需要1个参数,子类并不能兼容父类,但这并不影响程序的执行。

php8.0.0开始,构造函数也可以使用指名传参:

class Pointer
{
    private int $x;
    private int $y;
    public function __construct($x, $y)
    {
        $this->x = $x;
        $this->y = $y;
    }
}
$p = new Pointer(y: 2, x: 1);

属性提升

像上面实例中那样,使用传入的参数初始化同名属性的构造函数非常常见,所以php8.0.0增加了一个新语法,可以直接将构造函数中的参数转化为同名属性:

<?php
class Pointer
{
    public function __construct(private $x, private $y)
    {
    }
    public function __toString()
    {
        return "Pointer(x:{$this->x},y:{$this->y})";
    }
}
$p = new Pointer(y: 2, x: 1);
echo $p . PHP_EOL;
// Pointer(x:1,y:2)

对构造函数的参数使用访问修饰符后,该参数就会转换为类的同名属性,当然参数本身依然有效。转换完成后,构造函数内部的代码依然会被正常执行。

多构造

php不支持函数重载,所以我们只能定义一个__construct方法作为构造函数,如果我们需要像C++中那样的多构造,可以通过定义多个静态方法作为“工厂方法”来创建实例:

<?php
class Pointer
{
    private function __construct(private $x, private $y)
    {
    }
    public function __toString()
    {
        return "Pointer(x:{$this->x},y:{$this->y})";
    }
    public static function create_from_normal(int $x, int $y): self
    {
        return new self($x, $y);
    }
    public static function create_from_json(string $jsonStr): self
    {
        $arr = json_decode($jsonStr, true);
        return new self($arr['x'], $arr['y']);
    }
    public static function create_from_array(array $arr): self
    {
        return new self($arr['x'], $arr['y']);
    }
}
$p1 = Pointer::create_from_array(['x' => 1, 'y' => 9]);
$p2 = Pointer::create_from_json('{"x":2,"y":6}');
$p3 = Pointer::create_from_normal(5, 10);
echo $p1 . PHP_EOL;
echo $p2 . PHP_EOL;
echo $p3 . PHP_EOL;
// Pointer(x:1,y:9)
// Pointer(x:2,y:6)
// Pointer(x:5,y:10)

示例展示了如何通过三种方式来创建Pointer类。如果通过这种方式创建类,可以将构造函数定义为privateprotected,这样做可以避免无意中对构造函数的使用。

析构函数

同C++一样,php的类也具有析构函数,会在对象销毁时调用:

<?php
class MyClass
{
    public function __destruct()
    {
        echo __CLASS__ . "'s __destruct is called." . PHP_EOL;
    }
}
$mc =  new MyClass;
// MyClass's __destruct is called.

访问控制

可以使用访问修饰符public/protected/private对类的属性、方法、常量进行访问控制。

属性

public的属性可以在任何地方访问,protected属性仅能在类和子类中访问,private属性只能在所属的类中访问:

<?php
class MyClass
{
    public $publicAttr = 'public attr';
    protected $protectedAttr = 'protected attr';
    private $privateAttr = 'private attr';
    public function __construct()
    {
        echo $this->publicAttr . PHP_EOL;
        echo $this->protectedAttr . PHP_EOL;
        echo $this->privateAttr . PHP_EOL;
    }
}
class Child extends MyClass
{
    public function __construct()
    {
        echo $this->publicAttr . PHP_EOL;
        echo $this->protectedAttr . PHP_EOL;
    }
}
$mc = new MyClass;
echo $mc->publicAttr;
// public attr
// protected attr
// private attr
// public attr

静态属性的访问规则与普通属性相同。

方法

访问限定符对方法的控制规则与属性类似:

<?php
class MyClass{
    public function __construct()
    {
        $this->public_method();
        $this->protected_method();
        $this->private_method();
    }
    public function public_method(){
        echo "public method is called.".PHP_EOL;
    }
    protected function protected_method(){
        echo "protected method is called.".PHP_EOL;
    }
    private function private_method(){
        echo "private method is called.".PHP_EOL;
    }
}
class Child extends MyClass{
    public function __construct()
    {
        $this->public_method();
        $this->protected_method();
    }
}
$mc = new MyClass;
$mc->public_method();
// public method is called.
// protected method is called.
// private method is called.
// public method is called.

常量

php7.1.0开始,类常量也可以使用访问修饰符:

<?php
class MyClass
{
    public const PUBLIC_CONST = 'public const';
    protected const PROTECTED_CONST = 'protected const';
    private const PRIVATE_CONST = 'private const';
    public function __construct()
    {
        echo self::PUBLIC_CONST . PHP_EOL;
        echo self::PROTECTED_CONST . PHP_EOL;
        echo self::PRIVATE_CONST . PHP_EOL;
    }
}
class Child extends MyClass
{
    public function __construct()
    {
        echo parent::PUBLIC_CONST . PHP_EOL;
        echo parent::PROTECTED_CONST . PHP_EOL;
    }
}
echo MyClass::PUBLIC_CONST . PHP_EOL;
$mc = new MyClass;
// public const
// public const
// protected const
// private const

其它对象

需要注意的是,访问修饰符的限制并不仅仅局限与当前类的实例,同样可以在类方法中访问当前类的其它实例的privateprotected属性和方法:

<?php
class Pointer
{
    public function __construct(private int $x, private  int $y)
    {
    }
    public function add(Pointer $other): Pointer
    {
        $x = $this->x + $other->x;
        $y = $this->y + $other->y;
        return new self($x, $y);
    }
    public function __toString()
    {
        return "({$this->x},{$this->y})";
    }
}
$p1 = new Pointer(1, 3);
$p2 = new Pointer(2, 6);
$p3 = $p1->add($p2);
echo "{$p1}+{$p2}={$p3}" . PHP_EOL;
// (1,3)+(2,6)=(3,9)

对使用访问控制符的建议是:应当遵循最小访问原则。即能使用private就不使用protectedpublic,能使用protected就不使用public。因为声明为private的属性和方法可以很容易地修改为protectedpublic,声明为protected的属性和方法也能很容易地修改为public,反之则不行,可能需要大量重构已有代码。

往期内容

  PHP知识库 最新文章
Laravel 下实现 Google 2fa 验证
UUCTF WP
DASCTF10月 web
XAMPP任意命令执行提升权限漏洞(CVE-2020-
[GYCTF2020]Easyphp
iwebsec靶场 代码执行关卡通关笔记
多个线程同步执行,多个线程依次执行,多个
php 没事记录下常用方法 (TP5.1)
php之jwt
2021-09-18
上一篇文章      下一篇文章      查看所有文章
加:2021-12-09 11:27:40  更:2021-12-09 11:28:07 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/27 21:10:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计