2.4PHP类型之类(Class)

1.类声明

class 类名 {
    //类体
}

2.可变(Variable)类名

类的名称可以通过返回值为字符串类型的表达式动态指定。

//可变类名

//创建对象(注意:($变量名)()可省略外围的圆括号,简写为$变量名())
$对象名 = new (表达式)();
$对象名 = new (表达式);
(new (表达式)())->实例属性名;
(new (表达式)())->实例方法名();

//访问成员(注意:($变量名)可省略外围的圆括号,简写为$变量名)
(表达式)::$静态属性名;
(表达式)::静态方法();
(表达式)::常量名;

3.继承

class 子类 extends 父类 {
  //类体
}

一个子类可以继承一个父类的 静态属性、实例属性、常量、静态方法、实例方法、构造函数、析构函数。

一个子类不可以继承自多个父类,一个子类只可以继承自一个父类,但可以多个子类继承自同一个父类。

4.覆盖(Override)

从父类继承过来的静态属性、实例属性、常量、静态方法、实例方法、构造函数、析构函数可以通过在子类中使用相同的名称重新声明以覆盖它们。

当覆盖静态方法、实例方法时,子类方法的签名必须兼容父类方法。

兼容的签名必须符合里氏替换原则(Liskov Substitution Principle,LSP):遵守协变与逆变规则;强制参数可以改为可选参数;添加的新参数只能是可选;不可删除参数;相同或更加宽松的可见性。不过构造函数、析构函数和 private 方法不需要遵循此原则。

虽然,重命名子类方法的形数名是兼容的,但不建议这么做,因为当使用命名形参时,这种做法会导致运行时的 Error

5.构造函数和析构函数

function __construct() {
    //构造函数体
}

function __destruct() {
    //析构函数体
}

构造函数提升(promotion)是指:当一个构造函数的形参带可见性修饰符时,PHP 会同时把它当作构造函数的形参和类的实例属性,相当于省略了类的实例属性声明和构造函数体内的赋值操作。如果构造函数体有其它语句,相当于在赋值操作后执行。

class Point {
    protected int $x;
    protected int $y;

    public function __construct(int $x, int $y = 0) {
        $this->x = $x;
        $this->y = $y;
    }
}
//以上代码同等的构造函数提升方式
class Point {
    public function __construct(protected int $x, protected int $y = 0) {
    }
}

6.创建对象

$对象名 = new 类名();

//如果类没有构造函数或不传递实参给构造函数,则类名后的圆括号可以省略,一般不推荐省略。
$对象名 = new 类名;

7.访问成员

//静态
类::$静态属性名;
类::静态方法名();
$对象名::$静态属性名;
$对象名::$静态方法名();

//常量
类::常量名;
$对象名::常量名;

//实例
$对象名->实例属性名;
$对象名->实例方法名();
(new 类名())->实例属性名;
(new 类名())->实例方法名();

8.重载(Overload)

PHP语言 的重载与大多数面向对象语言不同。大多数面向对象语言的重载提供了具有相同名称但参数数量和类型不同的多个方法的能力。

PHP语言 的重载提供了动态创建属性和方法的魔术方法。

8.1属性重载

//__get()魔术方法是当从不可访问(protected或private)或不存在的属性中读取数据时会触发。
public __get(string $name): mixed

//__set()魔术方法是当将数据写入不可访问(protected或private)或不存在的属性时会触发。
public __set(string $name, mixed $value): void

//__isset()魔术方法是当在不可访问(protected或private)或不存在的属性上调用isset()或empty()时会触发。
public __isset(string $name): bool

//__unset()魔术方法是当在不可访问(protected或private)或不存在的属性上调用unset()时会触发。
public __unset(string $name): void

8.2方法重载

//__call()魔术方法是当在对象上下文中调用不可访问的方法时会触发。
public __call(string $name, array $arguments): mixed

//__callStatic()魔术方法是当在静态上下文中调用不可访问的方法时会触发。
public static __callStatic(string $name, array $arguments): mixed

9.匿名(Anonymous)类

//创建匿名类的对象
new class {
    //类体
}

由同一个匿名类声明创建的所有对象都是该匿名类的实例。

function anonymous_class()
{
    return new class {};
}

if (get_class(anonymous_class()) === get_class(anonymous_class())) {
    echo 'same class';
} else {
    echo 'different class';
}

//输出
same class

就跟普通类一样,匿名类也可以传递实参给构造函数、继承父类、实现接口、use trait

new class(实参) extends 父类 implements 接口 {
    use trait名;

    public function __construct(形参)
    {
        //构造函数体
    }
}

在一个外部类中嵌套匿名类时不会使匿名类访问该外部类的任何 protectedprivate 的属性或方法。为了使用外部类的 protected 属性或方法,匿名类可以继承该外部类。为了使用外部类的 private 属性或方法,必须通过匿名类的构造函数传递。

class Outer
{
    private $prop = 1;
    protected $prop2 = 2;

    protected function func1()
    {
        return 3;
    }

    public function func2()
    {
        return new class($this->prop) extends Outer {
            private $prop3;

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

            public function func3()
            {
                return $this->prop2 + $this->prop3 + $this->func1();
            }
        };
    }
}

echo (new Outer)->func2()->func3();

//输出
6

10.::class

关键字 class 除了用于声明类以外还可以用于类名的解析。

class 是类常量,所以可以使用 类名::class 或 $对象名::class 语法获取 类 或 对象所属类 的完全限定名称。

namespace NS {
    class ClassName {
    }
    
    echo ClassName::class;
}

//输出
NS\ClassName
namespace NS {
    class ClassName {
    }
}
$c = new ClassName();
print $c::class;

//输出
NS\ClassName

11.readonly关键字

readonly 关键字可以用在类、已声明类型的实例属性前,不可以用在静态属性、未声明类型的实例属性前。

11.1用于已声明类型的实例属性前

readonly 类型 $实例属性名;

readonly 关键字用在已声明的实例属性前时,表示此实例属性在初始化后不可更改,但如果值为对象,则支持对象的内部更改。

如果想要在未声明类型的实例属性前使用 readonly 关键字,则可以将类型声明为 mixed 类型。

readonly 实例属性只可以在类内部初始化,且声明时不可初始化,一般通过构造函数初始化。

class Test {
    //声明时不可初始化,否则就等同于常量
    public readonly int $prop = 42;
}

//只可以在类内部初始化,不可以在类外部初始化。
$test = new Test;
$test->prop = "foobar";
class Test {
   public readonly string $prop;
   
   //构造函数初始化是合法的(推荐)。
   public function __construct(string $prop) {
       $this->prop = $prop;
   }
   
   //实例函数初始化也是合法的(不推荐)。
   public function f(string $prop) {
       $this->prop = $prop;
   }
}

11.2用于类前

readonly class Foo {
    //类体
}

readonly 关键字用在类前时,表示所有在此类体中的已声明类型的实例属性前都会被自动添加 readonly 关键字。

由于静态属性和未声明类型的实例属性不支持 readonly 关键字,所以 readonly 类体中不能声明这些成员。

仅当子类也是 readonly 类时,才可以继承 readonly 父类。

12.abstract关键字

12.1抽象方法

abstract static function 函数名(形参);

abstract function 函数名(形参);

abstract 关键字可用于方法,表示此方法为抽象方法。支持静态方法、实例方法等。

抽象方法只可以定义方法签名,不可以定义方法的具体实现。

注意:抽象方法声明末尾无一对花括号 {},必须以分号 ; 结尾。

12.2抽象类

abstract class 类名 {
    //类体
}

abstract 关键字还可用于类,表示此类为抽象类。

抽象类可以包含已实现的方法和抽象方法,但必须至少包含一个抽象方法,子具体类必须实现父抽象类中的所有抽象方法。

抽象类可以继承。

抽象类不可以实例化。

访问抽象类中的已实现的静态方法,可以通过抽象类直接访问,也可以通过继承了抽象类的具体类访问。

访问抽象类中的抽象静态方法、已实现的实例方法、抽象实例方法,因为抽象类不可以实例化,所以只能通过继承了抽象类的具体类访问。

13.final关键字

final 关键字可用于类,表示此类不可被继承。

final class 类名 {
    //类体
}

final 关键字还可用于父类中的 常量、静态方法、实例方法、构造函数、析构函数 这些成员,表示子类可以继承这些成员但不可以覆盖这些成员。

注意:final 关键字不可以用于 private 方法,除了 private 构造函数。

final const 常量名 = 值;

final static function 静态方法名(形参) {
    //静态方法体
}

final function 实例方法名(形参) {
    //实例函数体
}

final function __construct() {
    //构造函数体
}

final function __destruct() {
    //析构函数体
}

final 关键字还可以用于父类的 private 构造函数,表示子类既不可以继承父类的构造函数也不可以声明构造函数。用于使用静态方法创建对象的场景,此时父类使用静态方法创建对象,子类使用从父类继承过来的静态方法创建对象。

final private function __construct() {
    //构造函数体
}

14.self关键字

在类上下文中,哪个类直接包含了 self 关键字 或 __CLASS__ 魔术常量,self 关键字 或 __CLASS__ 魔术常量就指向哪个类。

虽然子类继承间接包含了 self 关键字 或 __CLASS__ 魔术常量,但并不是直接包含。

//创建对象
new self;
//访问成员
self::$静态属性;
self::静态方法();
self::常量;
class A {
    public static function who() {
        echo __CLASS__;
    }
    public static function test() {
        self::who();
    }
}

class B extends A {
    public static function who() {
        echo __CLASS__;
    }
}

A::test();
B::test();

//输出
A
A

15.static关键字

static 关键字除了用于静态属性、静态方法以外,还可以用于以下场景。

在类上下文中,哪个类调用了 static 关键字,static 关键字就指向哪个类。

//创建对象
new static;
//访问成员
static::$静态属性;
static::静态方法();
static::常量;
//函数的返回类型
function 函数名(形参): static {
    //函数体
}
class A {
    public static function who() {
        echo __CLASS__;
    }
    public static function test() {
        static::who();
    }
}

class B extends A {
    public static function who() {
        echo __CLASS__;
    }
}

A::test();
B::test();

//输出
A
B

16.parent关键字

在子类中的静态方法体、实例方法体、构造函数体、析构函数体中可以通过 parent:: 来访问父类中被覆盖的静态属性、常量、静态方法、实例方法、构造函数、析构函数。

注意:不支持实例属性。

//创建对象
new parent;
//访问成员
parent::$父类静态属性;
parent::父类静态方法名();
parent::父类常量;
parent::父类实例方法名();
parent::__construct();
parent::__destruct();
parent::__魔术方法();

17.$this伪变量

在对象上下文中,哪个对象调用了 $this 伪变量,$this 伪变量就指向哪个对象。

//单独使用
var_dump($this);
//访问成员
$this->实例属性;
$this->实例方法();
class A {
    function a() {
        var_dump($this);
    }
}

class B extends A {
}

$a = new A;
$a->a();
$b = new B;
$b->a();

//输出
object(A)#1 (0) {
}
object(B)#2 (0) {
}

原创文章,作者:huoxiaoqiang,如若转载,请注明出处:https://www.huoxiaoqiang.com/php/phplang/28891.html

(0)
上一篇 2023年7月3日
下一篇 2023年7月5日

相关推荐

  • 1.9PHP语言的表达式(Expression)

    在 PHP 语言中,几乎所写的任何东西都是表达式。 1.declare语言结构 declare 语言结构用来为一段代码块设定执行指令(directive)。 目前只支持三个指令(directive):ticks 指令、encoding 指令、strict_types 指令。 代码块中的语句…

    PHP语言教程 2023年6月9日
    0190
  • 2.7PHP类型之枚举(Enum)

    枚举(Enum)本质上也是一个类。 枚举体可以包含常量、静态方法、实例方法、魔术方法(仅支持 __call()、__callStatic()、__invoke()),不可以有 静态属性、实例属性、构造函数、析构函数。 枚举是隐式的 final 类,不可以继承。 枚举不可以像类一样直接使用 new 实例化,条目(case…

    PHP语言教程 2023年7月7日
    0190
  • 1.8PHP语言的函数(Function)

    1.函数声明 2.函数分类 2.1全局函数 名称 修饰符 全局函数 —————— 2.2成员方法 名称 修饰符 静态方法(类) static 实例方法(类) —————— 2.3局部函数 名称 修饰符 嵌套函数 —————— 3.可变(Variable)函数名 函数、静态方法、实例方法的名称可以通过返回值为字符串类型的…

    PHP语言教程 2023年6月8日
    0410

发表回复

登录后才能评论