属性与方法

属性和方法是类的核心组成部分。属性(也称为成员变量)用于存储对象的状态数据,而方法(也称为成员函数)用于定义对象的行为。理解属性和方法的正确使用是掌握面向对象编程的关键。

学习目标

完成本节学习后,你将能够:

  • 理解属性的定义和使用
  • 掌握不同访问修饰符的作用
  • 学会创建和使用各种类型的方法
  • 理解$this关键字的使用
  • 掌握静态属性和静态方法
  • 学会使用类型声明和返回类型
  • 了解魔术方法的原理和应用

属性(Properties)

属性的基本概念

属性是类中用于存储数据的变量。每个对象实例都有自己的一组属性,这些属性构成了对象的状态。

<?php
class Product {
    // 公共属性 - 可以从任何地方访问
    public $name;
    public $price;

    // 受保护的属性 - 只能在类内部和子类中访问
    protected $category;
    protected $stock;

    // 私有属性 - 只能在类内部访问
    private $id;
    private $createdAt;

    public function __construct($name, $price, $category) {
        $this->id = uniqid('product_');  // 使用$this访问属性
        $this->name = $name;
        $this->price = $price;
        $this->category = $category;
        $this->stock = 0;
        $this->createdAt = date('Y-m-d H:i:s');
    }

    // 获取器方法(Getter)
    public function getId() {
        return $this->id;
    }

    public function getCategory() {
        return $this->category;
    }

    public function getStock() {
        return $this->stock;
    }

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

    // 设置器方法(Setter)
    public function setCategory($category) {
        $this->category = $category;
    }

    public function setStock($stock) {
        if ($stock >= 0) {
            $this->stock = $stock;
            return true;
        }
        return false;
    }

    // 业务方法
    public function isInStock() {
        return $this->stock > 0;
    }

    public function getInfo() {
        $stockStatus = $this->isInStock() ? "有库存" : "缺货";
        return "产品:{$this->name},价格:¥{$this->price},库存状态:{$stockStatus}";
    }
}

// 使用示例
$product = new Product("笔记本电脑", 5999.99, "电子产品");

// 访问公共属性
echo "产品名称:" . $product->name . "\n";
echo "产品价格:" . $product->price . "\n";

// 通过getter访问私有/受保护属性
echo "产品ID:" . $product->getId() . "\n";
echo "产品类别:" . $product->getCategory() . "\n";

// 通过setter修改属性
$product->setStock(50);
echo "产品库存:" . $product->getStock() . "\n";

// 调用业务方法
echo $product->getInfo() . "\n";

// 错误示例 - 不能直接访问私有属性
// echo $product->id;        // 错误:私有属性
// echo $product->createdAt;  // 错误:私有属性
// echo $product->category;   // 错误:受保护属性
?>

属性的类型声明

<?php
class Employee {
    // PHP 7.4+ 支持属性类型声明
    private int $id;
    private string $name;
    private float $salary;
    private bool $isActive;
    private array $skills;
    private ?DateTime $hireDate;  // 可空类型
    private string $department = 'IT';  // 默认值

    public function __construct(int $id, string $name, float $salary) {
        $this->id = $id;
        $this->name = $name;
        $this->salary = $salary;
        $this->isActive = true;
        $this->skills = [];
        $this->hireDate = null;
    }

    public function addSkill(string $skill): void {
        if (!in_array($skill, $this->skills)) {
            $this->skills[] = $skill;
        }
    }

    public function setHireDate(DateTime $date): void {
        $this->hireDate = $date;
    }

    public function getProfile(): array {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'salary' => $this->salary,
            'isActive' => $this->isActive,
            'skills' => $this->skills,
            'hireDate' => $this->hireDate ? $this->hireDate->format('Y-m-d') : null,
            'department' => $this->department
        ];
    }
}

// 使用示例
$employee = new Employee(1, "张三", 8000.00);
$employee->addSkill("PHP");
$employee->addSkill("JavaScript");
$employee->addSkill("MySQL");
$employee->setHireDate(new DateTime('2020-01-15'));

$profile = $employee->getProfile();
print_r($profile);
?>

只读属性(PHP 8.1+)

<?php
// PHP 8.1+ 支持只读属性
class User {
    public readonly int $id;
    public readonly string $email;
    public readonly DateTime $createdAt;

    public function __construct(int $id, string $email) {
        $this->id = $id;
        $this->email = $email;
        $this->createdAt = new DateTime();
    }

    // 只读属性只能在构造函数或声明时赋值
    // 下面的方法会导致错误:
    // public function setEmail(string $email): void {
    //     $this->email = $email;  // 错误:只读属性不能修改
    // }
}

$user = new User(1, "user@example.com");

echo "用户ID:" . $user->id . "\n";
echo "用户邮箱:" . $user->email . "\n";
echo "创建时间:" . $user->createdAt->format('Y-m-d H:i:s') . "\n";

// $user->email = "new@example.com"; // 错误:不能修改只读属性
?>

方法(Methods)

方法的定义和使用

方法是类中定义的函数,用于执行特定的操作或实现特定的功能。

<?php
class Calculator {
    private float $result = 0.0;
    private array $history = [];

    // 基本方法
    public function add(float $number): float {
        $this->result += $number;
        $this->addToHistory("add", $number);
        return $this->result;
    }

    public function subtract(float $number): float {
        $this->result -= $number;
        $this->addToHistory("subtract", $number);
        return $this->result;
    }

    public function multiply(float $number): float {
        $this->result *= $number;
        $this->addToHistory("multiply", $number);
        return $this->result;
    }

    public function divide(float $number): float {
        if ($number == 0) {
            throw new InvalidArgumentException("除数不能为零");
        }
        $this->result /= $number;
        $this->addToHistory("divide", $number);
        return $this->result;
    }

    // 可变参数方法
    public function addMultiple(float ...$numbers): float {
        foreach ($numbers as $number) {
            $this->result += $number;
        }
        $this->addToHistory("addMultiple", $numbers);
        return $this->result;
    }

    // 带默认参数的方法
    public function power(float $exponent = 2): float {
        $this->result = pow($this->result, $exponent);
        $this->addToHistory("power", $exponent);
        return $this->result;
    }

    // 返回数组的方法
    public function getHistory(): array {
        return $this->history;
    }

    // 返回对象的方法
    public function clone(): Calculator {
        $newCalc = new Calculator();
        $newCalc->result = $this->result;
        $newCalc->history = $this->history;
        return $newCalc;
    }

    // 链式调用方法
    public function reset(): Calculator {
        $this->result = 0;
        $this->history = [];
        return $this; // 返回$this支持链式调用
    }

    public function getResult(): float {
        return $this->result;
    }

    // 私有辅助方法
    private function addToHistory(string $operation, $value): void {
        $this->history[] = [
            'operation' => $operation,
            'value' => $value,
            'result' => $this->result,
            'timestamp' => date('Y-m-d H:i:s')
        ];
    }

    // 静态方法
    public static function factorial(int $n): int {
        if ($n < 0) {
            throw new InvalidArgumentException("阶乘不能为负数");
        }
        if ($n === 0 || $n === 1) {
            return 1;
        }
        return $n * self::factorial($n - 1);
    }

    public static function isEven(int $number): bool {
        return $number % 2 === 0;
    }
}

// 使用示例
echo "=== 计算器示例 ===\n";

$calc = new Calculator();

// 链式调用
$result = $calc->add(10)->multiply(2)->subtract(5)->divide(3)->getResult();
echo "计算结果:(10 + 5) * 2 / 3 = $result\n";

// 可变参数
$calc->reset()->addMultiple(1, 2, 3, 4, 5);
echo "多参数加法结果:" . $calc->getResult() . "\n";

// 默认参数
$calc->reset()->add(4)->power(); // 使用默认参数2
echo "4的平方:" . $calc->getResult() . "\n";

$calc->reset()->add(2)->power(3); // 指定参数
echo "2的3次方:" . $calc->getResult() . "\n";

// 静态方法
echo "5的阶乘:" . Calculator::factorial(5) . "\n";
echo "6是否为偶数:" . (Calculator::isEven(6) ? "是" : "否") . "\n";

// 查看历史记录
$history = $calc->getHistory();
echo "计算历史:\n";
foreach ($history as $entry) {
    echo "  {$entry['timestamp']} - {$entry['operation']}({$entry['value']}) = {$entry['result']}\n";
}

// 克隆计算器
$clonedCalc = $calc->clone();
echo "克隆计算器的结果:" . $clonedCalc->getResult() . "\n";
?>

$this关键字

$this是一个特殊的变量,它指向当前对象的实例,用于在类的内部访问对象的属性和方法。

<?php
class Person {
    private string $name;
    private int $age;
    private array $hobbies = [];

    public function __construct(string $name, int $age) {
        $this->name = $name;  // $this指向当前对象
        $this->age = $age;
    }

    public function getName(): string {
        return $this->name;  // 访问当前对象的属性
    }

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

    public function addHobby(string $hobby): void {
        $this->hobbies[] = $hobby;  // 修改当前对象的属性
    }

    public function getHobbies(): array {
        return $this->hobbies;
    }

    public function introduce(): string {
        $hobbyStr = empty($this->hobbies) ? "没有特殊爱好" : implode("、", $this->hobbies);
        return "大家好,我叫{$this->name},今年{$this->age}岁。我的爱好是{$hobbyStr}。";
    }

    public function celebrateBirthday(): void {
        $this->age++;  // 增加年龄
        echo "祝{$this->name}生日快乐!现在{$this->age}岁了!\n";
    }

    // $this也可以用于调用其他方法
    public function getDetailedInfo(): array {
        return [
            'name' => $this->getName(),
            'age' => $this->getAge(),
            'hobbies' => $this->getHobbies(),
            'introduction' => $this->introduce()
        ];
    }

    // $this在方法间传递
    public function compareAge(Person $other): string {
        if ($this->age > $other->age) {
            return "{$this->name}比{$other->name}大" . ($this->age - $other->age) . "岁";
        } elseif ($this->age < $other->age) {
            return "{$this->name}比{$other->name}小" . ($other->age - $this->age) . "岁";
        } else {
            return "{$this->name}和{$other->name}同岁";
        }
    }

    // $this用于回调函数
    public function processWithCallback(callable $callback) {
        $result = $callback($this);  // 将当前对象传递给回调函数
        return $result;
    }
}

// 使用示例
$person1 = new Person("张三", 25);
$person2 = new Person("李四", 28);

$person1->addHobby("阅读");
$person1->addHobby("游泳");

echo $person1->introduce() . "\n";

$person1->celebrateBirthday();

echo $person1->compareAge($person2) . "\n";

// 使用回调函数
$result = $person1->processWithCallback(function($person) {
    return $person->getName() . "的处理结果";
});
echo "回调处理结果:$result\n";

$info = $person1->getDetailedInfo();
print_r($info);
?>

访问修饰符详解

public(公共)

<?php
class PublicExample {
    public $publicProperty = "公共属性";
    public $counter = 0;

    public function publicMethod() {
        $this->counter++;
        return "这是一个公共方法,调用次数:{$this->counter}";
    }

    public function accessFromOutside() {
        echo "在类内部访问公共属性:{$this->publicProperty}\n";
        echo "在类内部调用公共方法:" . $this->publicMethod() . "\n";
    }
}

$publicExample = new PublicExample();

// 从外部访问
echo $publicExample->publicProperty . "\n";  // 直接访问公共属性
echo $publicExample->publicMethod() . "\n";  // 直接调用公共方法

$publicExample->publicProperty = "修改后的公共属性";
echo $publicExample->publicProperty . "\n";

$publicExample->accessFromOutside();
?>

protected(受保护)

<?php
class ParentClass {
    protected $protectedProperty = "受保护属性";
    protected $counter = 0;

    protected function protectedMethod() {
        $this->counter++;
        return "这是一个受保护方法,调用次数:{$this->counter}";
    }

    public function accessFromInside() {
        echo "在父类内部访问受保护属性:{$this->protectedProperty}\n";
        echo "在父类内部调用受保护方法:" . $this->protectedMethod() . "\n";
    }
}

class ChildClass extends ParentClass {
    public function accessFromChild() {
        // 子类可以访问父类的受保护成员
        echo "在子类中访问父类受保护属性:{$this->protectedProperty}\n";
        echo "在子类中调用父类受保护方法:" . $this->protectedMethod() . "\n";

        // 修改受保护属性
        $this->protectedProperty = "被子类修改的受保护属性";
        echo "修改后的受保护属性:{$this->protectedProperty}\n";
    }
}

$parent = new ParentClass();
$child = new ChildClass();

// 从外部无法直接访问受保护成员
// echo $parent->protectedProperty;  // 错误
// echo $parent->protectedMethod();  // 错误

// 通过公共方法间接访问
$parent->accessFromInside();

// 子类可以访问受保护成员
$child->accessFromChild();
?>

private(私有)

<?php
class PrivateExample {
    private $privateProperty = "私有属性";
    private $counter = 0;

    private function privateMethod() {
        $this->counter++;
        return "这是一个私有方法,调用次数:{$this->counter}";
    }

    public function accessFromInside() {
        // 类内部可以访问私有成员
        echo "在类内部访问私有属性:{$this->privateProperty}\n";
        echo "在类内部调用私有方法:" . $this->privateMethod() . "\n";
    }

    public function modifyPrivateProperty() {
        $this->privateProperty = "修改后的私有属性";
    }

    public function getPrivateProperty() {
        return $this->privateProperty;
    }

    public function callPrivateMethod() {
        return $this->privateMethod();
    }
}

class ChildOfPrivate extends PrivateExample {
    public function tryAccessPrivate() {
        // 子类无法访问父类的私有成员
        // echo $this->privateProperty;  // 错误
        // echo $this->privateMethod();  // 错误

        echo "子类无法直接访问父类的私有成员\n";
    }
}

$privateExample = new PrivateExample();
$child = new ChildOfPrivate();

// 从外部无法直接访问私有成员
// echo $privateExample->privateProperty;  // 错误
// echo $privateExample->privateMethod();   // 错误

// 通过公共方法间接访问
$privateExample->accessFromInside();

$privateExample->modifyPrivateProperty();
echo "通过getter访问私有属性:" . $privateExample->getPrivateProperty() . "\n";

echo "通过公共方法调用私有方法:" . $privateExample->callPrivateMethod() . "\n";

$child->tryAccessPrivate();
?>

静态属性和静态方法

静态属性

<?php
class Car {
    // 静态属性 - 属于类,不属于任何实例
    private static $totalCars = 0;
    private static $manufacturer = "示例汽车厂";

    // 实例属性 - 属于每个对象实例
    private $model;
    private $color;
    private $price;

    public function __construct($model, $color, $price) {
        $this->model = $model;
        $this->color = $color;
        $this->price = $price;

        // 访问和修改静态属性
        self::$totalCars++;
        echo "创建了一辆{$this->color}的{$this->model},总产量:" . self::$totalCars . "\n";
    }

    public function __destruct() {
        self::$totalCars--;
        echo "销毁了一辆{$this->model},当前总产量:" . self::$totalCars . "\n";
    }

    // 静态方法访问静态属性
    public static function getTotalCars() {
        return self::$totalCars;
    }

    public static function getManufacturer() {
        return self::$manufacturer;
    }

    public static function setManufacturer($manufacturer) {
        self::$manufacturer = $manufacturer;
    }

    public static function getProductionStats() {
        return [
            'manufacturer' => self::$manufacturer,
            'total_produced' => self::$totalCars
        ];
    }

    // 实例方法访问静态属性
    public function getModel() {
        return $this->model . " (" . self::$manufacturer . ")";
    }
}

// 使用示例
echo "=== 静态属性示例 ===\n";

// 访问静态属性
echo "初始产量:" . Car::getTotalCars() . "\n";
echo "制造商:" . Car::getManufacturer() . "\n";

// 修改静态属性
Car::setManufacturer("高级汽车厂");
echo "修改后制造商:" . Car::getManufacturer() . "\n";

// 创建对象(会影响静态属性)
$car1 = new Car("Model S", "红色", 500000);
$car2 = new Car("Model X", "蓝色", 600000);
$car3 = new Car("Model 3", "白色", 350000);

echo "当前产量:" . Car::getTotalCars() . "\n";

// 实例方法访问静态属性
echo "汽车1型号:" . $car1->getModel() . "\n";

// 销毁对象(也会影响静态属性)
unset($car1);
echo "销毁一辆车后产量:" . Car::getTotalCars() . "\n";
?>

静态方法

<?php
class MathHelper {
    // 静态常量
    const PI = 3.14159265359;
    const E = 2.71828182846;

    // 静态方法 - 不需要创建实例就可以调用
    public static function circleArea(float $radius): float {
        return self::PI * $radius * $radius;
    }

    public static function circleCircumference(float $radius): float {
        return 2 * self::PI * $radius;
    }

    public static function rectangleArea(float $width, float $height): float {
        return $width * $height;
    }

    public static function rectanglePerimeter(float $width, float $height): float {
        return 2 * ($width + $height);
    }

    // 静态方法调用其他静态方法
    public static function circleInfo(float $radius): array {
        return [
            'radius' => $radius,
            'area' => self::circleArea($radius),
            'circumference' => self::circleCircumference($radius)
        ];
    }

    // 静态工厂方法 - 创建对象实例
    public static function createCircle(float $radius): Circle {
        return new Circle($radius);
    }

    public static function createRectangle(float $width, float $height): Rectangle {
        return new Rectangle($width, $height);
    }

    // 工具方法
    public static function formatNumber(float $number, int $decimals = 2): string {
        return number_format($number, $decimals);
    }

    // 数值验证
    public static function isPositive(float $number): bool {
        return $number > 0;
    }

    public static function isEven(int $number): bool {
        return $number % 2 === 0;
    }

    public static function isPrime(int $number): bool {
        if ($number <= 1) return false;
        if ($number <= 3) return true;
        if ($number % 2 === 0 || $number % 3 === 0) return false;

        for ($i = 5; $i * $i <= $number; $i += 6) {
            if ($number % $i === 0 || $number % ($i + 2) === 0) {
                return false;
            }
        }
        return true;
    }
}

// 配套的形状类
class Circle {
    private float $radius;

    public function __construct(float $radius) {
        $this->radius = $radius;
    }

    public function getRadius(): float {
        return $this->radius;
    }

    public function getArea(): float {
        return MathHelper::circleArea($this->radius);
    }

    public function getCircumference(): float {
        return MathHelper::circleCircumference($this->radius);
    }
}

class Rectangle {
    private float $width;
    private float $height;

    public function __construct(float $width, float $height) {
        $this->width = $width;
        $this->height = $height;
    }

    public function getWidth(): float {
        return $this->width;
    }

    public function getHeight(): float {
        return $this->height;
    }

    public function getArea(): float {
        return MathHelper::rectangleArea($this->width, $this->height);
    }

    public function getPerimeter(): float {
        return MathHelper::rectanglePerimeter($this->width, $this->height);
    }
}

// 使用示例
echo "=== 静态方法示例 ===\n";

// 直接调用静态方法,无需创建对象
$radius = 5;
$area = MathHelper::circleArea($radius);
$circumference = MathHelper::circleCircumference($radius);

echo "半径为{$radius}的圆:\n";
echo "面积:" . MathHelper::formatNumber($area) . "\n";
echo "周长:" . MathHelper::formatNumber($circumference) . "\n";

// 调用返回数组的静态方法
$circleInfo = MathHelper::circleInfo(3);
echo "\n半径为3的圆的详细信息:\n";
print_r($circleInfo);

// 使用静态工厂方法创建对象
$circle = MathHelper::createCircle(4);
$rectangle = MathHelper::createRectangle(6, 4);

echo "\n使用工厂方法创建的形状:\n";
echo "圆的半径:" . $circle->getRadius() . ",面积:" . MathHelper::formatNumber($circle->getArea()) . "\n";
echo "矩形的尺寸:" . $rectangle->getWidth() . "x" . $rectangle->getHeight() . ",面积:" . $rectangle->getArea() . "\n";

// 数值验证
echo "\n数值验证:\n";
echo "7是质数吗?" . (MathHelper::isPrime(7) ? "是" : "否") . "\n";
echo "12是偶数吗?" . (MathHelper::isEven(12) ? "是" : "否") . "\n";
echo "-5是正数吗?" . (MathHelper::isPositive(-5) ? "是" : "否") . "\n";
?>

魔术方法

PHP提供了一些以双下划线开头的特殊方法,称为魔术方法。这些方法在特定情况下会被自动调用。

__toString() 方法

<?php
class Book {
    private $title;
    private $author;
    private $price;
    private $year;

    public function __construct($title, $author, $price, $year) {
        $this->title = $title;
        $this->author = $author;
        $this->price = $price;
        $this->year = $year;
    }

    // 魔术方法:当对象被当作字符串使用时调用
    public function __toString() {
        return "《{$this->title}》- {$this->author} ({$this->year}年出版) ¥{$this->price}";
    }

    // 其他魔术方法
    public function __invoke($discount = 0) {
        $finalPrice = $this->price * (1 - $discount);
        return "打折后价格:¥" . number_format($finalPrice, 2);
    }

    public function __debugInfo() {
        return [
            'title' => $this->title,
            'author' => $this->author,
            'price' => $this->price,
            'year' => $this->year,
            'price_formatted' => '¥' . number_format($this->price, 2)
        ];
    }
}

// 使用示例
$book = new Book("PHP从入门到精通", "张三", 89.99, 2023);

// 对象被当作字符串使用时自动调用__toString()
echo $book . "\n";

// 对象被当作函数调用时自动调用__invoke()
echo $book(0.1) . "\n";  // 9折

// var_dump会显示__debugInfo()返回的信息
var_dump($book);
?>

__set() 和 __get() 方法

<?php
class DynamicProperties {
    private $data = [];
    private $allowedProperties = ['name', 'age', 'email'];

    // 当设置未定义或不可访问的属性时调用
    public function __set($name, $value) {
        echo "尝试设置属性:$name = $value\n";

        if (in_array($name, $this->allowedProperties)) {
            $this->data[$name] = $value;
        } else {
            echo "属性 '$name' 不允许设置\n";
        }
    }

    // 当访问未定义或不可访问的属性时调用
    public function __get($name) {
        echo "尝试获取属性:$name\n";

        if (array_key_exists($name, $this->data)) {
            return $this->data[$name];
        }

        echo "属性 '$name' 不存在\n";
        return null;
    }

    // 检查属性是否存在
    public function __isset($name) {
        return isset($this->data[$name]);
    }

    // 删除属性
    public function __unset($name) {
        echo "尝试删除属性:$name\n";
        unset($this->data[$name]);
    }

    // 检查属性是否允许
    public function isPropertyAllowed($name): bool {
        return in_array($name, $this->allowedProperties);
    }
}

// 使用示例
echo "=== __set() 和 __get() 示例 ===\n";

$obj = new DynamicProperties();

// 通过魔术方法设置属性
$obj->name = "张三";     // 允许的属性
$obj->age = 25;          // 允许的属性
$obj->email = "zhang@example.com";  // 允许的属性
$obj->phone = "1234567890";      // 不允许的属性

// 通过魔术方法获取属性
echo "姓名:" . $obj->name . "\n";
echo "年龄:" . $obj->age . "\n";
echo "邮箱:" . $obj->email . "\n";
echo "电话:" . $obj->phone . "\n";

// 检查属性是否存在
echo "姓名属性是否存在:" . (isset($obj->name) ? "是" : "否") . "\n";

// 删除属性
unset($obj->age);
echo "删除后姓名:" . $obj->name . "\n";
echo "删除后年龄:" . $obj->age . "\n";
?>

__call() 和 __callStatic() 方法

<?php
class MethodInterception {
    // 当调用未定义或不可访问的实例方法时调用
    public function __call($method, $arguments) {
        echo "尝试调用实例方法:$method\n";
        echo "参数:" . implode(', ', $arguments) . "\n";

        // 动态处理方法调用
        if (strpos($method, 'get') === 0) {
            $property = strtolower(substr($method, 3));
            return "获取属性 $property 的值";
        }

        if (strpos($method, 'set') === 0) {
            $property = strtolower(substr($method, 3));
            return "设置属性 $property 的值为:" . ($arguments[0] ?? 'null');
        }

        throw new BadMethodCallException("方法 $method 不存在");
    }

    // 当调用未定义或不可访问的静态方法时调用
    public static function __callStatic($method, $arguments) {
        echo "尝试调用静态方法:$method\n";
        echo "参数:" . implode(', ', $arguments) . "\n";

        // 静态方法的动态处理
        switch ($method) {
            case 'calculate':
                $operation = $arguments[0] ?? null;
                $a = $arguments[1] ?? 0;
                $b = $arguments[2] ?? 0;

                switch ($operation) {
                    case 'add':
                        return $a + $b;
                    case 'multiply':
                        return $a * $b;
                    default:
                        return "未知操作";
                }

            case 'format':
                $value = $arguments[0] ?? null;
                return "格式化:" . json_encode($value);

            default:
                throw new BadMethodCallException("静态方法 $method 不存在");
        }
    }

    // 已定义的方法,不会触发__call
    public function existingMethod() {
        return "这是一个已定义的方法";
    }
}

// 使用示例
echo "=== __call() 和 __callStatic() 示例 ===\n";

$obj = new MethodInterception();

// 调用已定义的方法(不会触发__call)
echo "已定义方法:" . $obj->existingMethod() . "\n";

// 调用未定义的方法(会触发__call)
echo $obj->getName("John") . "\n";
echo $obj->setAge(25) . "\n";

// 调用未定义的静态方法(会触发__callStatic)
$result = MethodInterception::calculate('add', 10, 5);
echo "计算结果:$result\n";

$formatted = MethodInterception::format(['name' => 'John', 'age' => 25]);
echo "格式化结果:$formatted\n";

// 尝试调用不存在的方法(会抛出异常)
try {
    $obj->nonExistentMethod();
} catch (BadMethodCallException $e) {
    echo "捕获异常:" . $e->getMessage() . "\n";
}
?>

实际应用示例

简单的用户管理类

<?php
class UserManager {
    private static $users = [];
    private static $nextId = 1;

    // 静态方法:创建用户
    public static function createUser($username, $email, $password) {
        if (self::userExists($username)) {
            throw new InvalidArgumentException("用户名已存在");
        }

        if (self::emailExists($email)) {
            throw new InvalidArgumentException("邮箱已存在");
        }

        $user = [
            'id' => self::$nextId++,
            'username' => $username,
            'email' => $email,
            'password_hash' => password_hash($password, PASSWORD_DEFAULT),
            'created_at' => date('Y-m-d H:i:s'),
            'is_active' => true,
            'last_login' => null
        ];

        self::$users[$user['id']] = $user;
        return new User($user);
    }

    // 静态方法:验证用户
    public static function authenticate($username, $password) {
        $user = self::getUserByUsername($username);
        if (!$user) {
            throw new Exception("用户名或密码错误");
        }

        if (!$user['is_active']) {
            throw new Exception("用户账户已被禁用");
        }

        if (!password_verify($password, $user['password_hash'])) {
            throw new Exception("用户名或密码错误");
        }

        // 更新登录时间
        self::$users[$user['id']]['last_login'] = date('Y-m-d H:i:s');

        return new User($user);
    }

    // 静态方法:获取用户
    public static function getUserById($id) {
        return self::$users[$id] ?? null;
    }

    public static function getUserByUsername($username) {
        foreach (self::$users as $user) {
            if ($user['username'] === $username) {
                return $user;
            }
        }
        return null;
    }

    public static function getUserByEmail($email) {
        foreach (self::$users as $user) {
            if ($user['email'] === $email) {
                return $user;
            }
        }
        return null;
    }

    // 静态方法:检查用户是否存在
    public static function userExists($username) {
        return self::getUserByUsername($username) !== null;
    }

    public static function emailExists($email) {
        return self::getUserByEmail($email) !== null;
    }

    // 静态方法:获取所有用户
    public static function getAllUsers() {
        $userObjects = [];
        foreach (self::$users as $user) {
            $userObjects[] = new User($user);
        }
        return $userObjects;
    }

    // 静态方法:获取活跃用户数
    public static function getActiveUserCount() {
        $count = 0;
        foreach (self::$users as $user) {
            if ($user['is_active']) {
                $count++;
            }
        }
        return $count;
    }

    // 静态方法:清除所有用户
    public static function clearAllUsers() {
        self::$users = [];
        self::$nextId = 1;
    }
}

// 用户对象类
class User {
    private $data;

    public function __construct($userData) {
        $this->data = $userData;
    }

    public function getId() {
        return $this->data['id'];
    }

    public function getUsername() {
        return $this->data['username'];
    }

    public function getEmail() {
        return $this->data['email'];
    }

    public function getCreatedAt() {
        return $this->data['created_at'];
    }

    public function getLastLogin() {
        return $this->data['last_login'];
    }

    public function isActive() {
        return $this->data['is_active'];
    }

    public function toArray() {
        return [
            'id' => $this->getId(),
            'username' => $this->getUsername(),
            'email' => $this->getEmail(),
            'created_at' => $this->getCreatedAt(),
            'last_login' => $this->getLastLogin(),
            'is_active' => $this->isActive()
        ];
    }

    public function __toString() {
        return "用户[{$this->getId()}]: {$this->getUsername()} ({$this->getEmail()})";
    }
}

// 使用示例
echo "=== 用户管理系统示例 ===\n";

try {
    // 清理现有数据
    UserManager::clearAllUsers();

    // 创建用户
    $user1 = UserManager::createUser("alice", "alice@example.com", "password123");
    $user2 = UserManager::createUser("bob", "bob@example.com", "password456");

    echo "用户创建成功\n";
    echo "活跃用户数:" . UserManager::getActiveUserCount() . "\n";

    // 用户登录
    $loggedInUser = UserManager::authenticate("alice", "password123");
    echo "用户登录成功:" . $loggedInUser . "\n";

    // 获取所有用户
    $allUsers = UserManager::getAllUsers();
    echo "所有用户:\n";
    foreach ($allUsers as $user) {
        echo "- " . $user . "\n";
    }

    // 获取用户详细信息
    $userInfo = $loggedInUser->toArray();
    echo "用户详细信息:\n";
    print_r($userInfo);

} catch (Exception $e) {
    echo "错误:" . $e->getMessage() . "\n";
}
?>

总结

属性和方法是面向对象编程的基础构件,正确理解和使用它们对于构建高质量的面向对象应用程序至关重要。

关键要点:

  1. 属性管理

    • 属性用于存储对象状态
    • 使用访问修饰符控制属性的可访问性
    • 支持类型声明和默认值
    • 提供getter和setter方法进行受控访问
  2. 方法设计

    • 方法定义对象的行为
    • 支持参数类型声明和返回类型
    • 可变参数和默认参数增加灵活性
    • 链式调用提高代码可读性
  3. 访问控制

    • public:完全开放,可从任何地方访问
    • protected:类内部和子类可访问
    • private:仅类内部可访问
  4. 静态成员

    • 静态属性和方法属于类而不是实例
    • 通过类名直接访问,无需创建对象
    • 适合实现工具方法和共享数据
  5. 魔术方法

    • 在特定情况下自动调用的特殊方法
    • 提供对象的高级功能
    • 谨慎使用,避免过度复杂化

最佳实践:

  • 保持属性私有,通过方法进行访问控制
  • 为方法选择清晰的命名和单一职责
  • 合理使用静态方法和实例方法
  • 优先使用类型声明提高代码安全性
  • 避免滥用魔术方法,保持代码简洁明了

通过本节学习,你应该能够熟练地定义和使用类的属性与方法,为后续学习更高级的面向对象特性打下坚实基础。