PHP 面向对象

Classes, constructor promotion, inheritance, interfaces, abstract classes, traits, statics and enums.

Intermediate
Key PointsPHP 8 constructor promotion removes boilerplate; interfaces and traits do the heavy lifting.

Classes and constructor promotion

Before PHP 8 a value object needed property declarations, a constructor and assignments. Now a visibility modifier on the parameter does it all.

PHP
<?php
class Point
{
    public function __construct(
        public readonly float $x,
        public readonly float $y,
    ) {}

    public function distanceTo(Point $other): float {
        return sqrt(($this->x - $other->x) ** 2 + ($this->y - $other->y) ** 2);
    }
}

$p = new Point(0, 0);
$q = new Point(3, 4);

echo $q->x, "\n";
echo $p->distanceTo($q), "\n";
Output
3
5

readonly (8.1) allows a single assignment — ideal for value objects. A second write throws an Error.

PHP
<?php
class Point
{
    public function __construct(public readonly float $x) {}
}

$p = new Point(1);
try {
    $p->x = 2;
} catch (Error $e) {
    echo "赋值失败: " . $e->getMessage(), "\n";
}
Output
赋值失败: Cannot modify readonly property Point::$x

Inheritance and abstract classes

PHP
<?php
interface Shape
{
    public function area(): float;
}

abstract class Base implements Shape
{
    public function __construct(protected string $name) {}

    abstract public function area(): float;

    public function describe(): string {
        return sprintf("%s 面积 %.2f", $this->name, $this->area());
    }
}

class Circle extends Base
{
    public function __construct(private float $r) {
        parent::__construct("圆");
    }

    public function area(): float {
        return M_PI * $this->r ** 2;
    }
}

class Rect extends Base
{
    public function __construct(private float $w, private float $h) {
        parent::__construct("矩形");
    }

    public function area(): float {
        return $this->w * $this->h;
    }
}

foreach ([new Circle(2), new Rect(3, 4)] as $s) {
    echo $s->describe(), "\n";
}

var_dump(new Circle(1) instanceof Shape);
Output
圆 面积 12.57
矩形 面积 12.00
bool(true)
FeatureInterfaceAbstract class
Instantiate不能不能
Method bodies不能有(可有 default 常量)可以有具体方法
Properties不能声明可以
How many一个类可实现多个只能继承一个
Purpose约定能力复用部分实现

trait

PHP is single-inheritance. To share code between unrelated classes, use a trait — a paste-able block of methods.

PHP
<?php
trait Loggable
{
    public function log(string $msg): void {
        echo "[" . static::class . "] $msg\n";
    }
}

trait Timestampable
{
    protected int $createdAt = 0;

    public function touch(): void {
        $this->createdAt = 1700000000;
    }

    public function createdAt(): int {
        return $this->createdAt;
    }
}

class Order
{
    use Loggable, Timestampable;
}

class Invoice
{
    use Loggable;
}

$o = new Order();
$o->log("创建订单");
$o->touch();
echo $o->createdAt(), "\n";

(new Invoice())->log("开票");
Output
[Order] 创建订单
1700000000
[Invoice] 开票
Inside a trait $this and static::class refer to the using class. A trait cannot be used with instanceof — it is not a type.

Statics and constants

PHP
<?php
class Counter
{
    private static int $count = 0;
    public const MAX = 10;

    public static function increment(): int {
        return ++self::$count;
    }

    public static function current(): int {
        return self::$count;
    }
}

Counter::increment();
Counter::increment();
echo Counter::current(), "\n";
echo Counter::MAX, "\n";

// 后期静态绑定
class A
{
    public static function create(): static {
        return new static();
    }
    public function who(): string {
        return static::class;
    }
}
class B extends A {}

echo B::create()->who(), "\n";
echo A::create()->who(), "\n";
Output
2
10
B
A

self:: binds to the defining class at compile time; static:: binds at runtime to the called class. Factories returning subclass instances need new static().

Enums

PHP 8.1 enums are typed constant sets — usable in type declarations, and they can carry methods.

PHP
<?php
enum Status: string
{
    case Active = 'active';
    case Banned = 'banned';

    public function label(): string {
        return match ($this) {
            Status::Active => '正常',
            Status::Banned => '封禁',
        };
    }

    public static function fromLabel(string $label): self {
        foreach (self::cases() as $case) {
            if ($case->label() === $label) {
                return $case;
            }
        }
        throw new ValueError("未知状态: $label");
    }
}

echo Status::Active->label(), "\n";
echo Status::from('banned')->label(), "\n";
echo Status::fromLabel('正常')->value, "\n";
print_r(array_column(Status::cases(), 'value'));
Output
正常
封禁
active
Array
(
    [0] => active
    [1] => banned
)

Status::from() throws ValueError when missing; tryFrom() returns null. Enum cases are singletons — identity comparison works.

Visibility

ModifierVisible from
public任何地方
protected本类与子类
private仅本类(子类也看不到)
final可修饰类(不可继承)或方法(不可覆写)
readonly属性赋值一次后锁定(8.1+)
static属于类而非实例
private members are invisible to subclasses, even via parent::. Use protected. Private methods also skip dynamic dispatch — a subclass method of the same name is never called.

Friends & Partners