从0开始学PHP面向对象内容之(常用魔术方法续一)

常用魔术方法(续)

上期我们讲到几个常用的魔术方法,但是由于篇幅过程且全是文字性质地东西,就没写完,篇幅太长也会丧失阅读兴趣,我尽量控制一篇文章在5000字左右

一、__isset()&&__unset()

1、在开发中,类通常有一些不想暴露或让外部检查的私有或受保护的属性,比如密码、敏感配置等。通过 __isset(),可以控制这些属性是否能够被 isset() 或 empty() 检查到。
2、__isset() 的主要用途 :是控制对象中是否可以检测到某个属性,通常用于保护敏感数据和实现延迟加载。
__unset() 的主要用途:是定义对象在使用 unset() 时的行为,通常用于控制哪些属性可以被删除或在删除时执行附加逻辑。

1、数据隐藏与控制访问

1、__isset() 和 __unset() 常用于对象中属性的访问控制,尤其是当类有一些私有属性时,这些方法可以决定哪些属性可以被检查或销毁。
2、__isset() 可以用 :来控制 isset() 和 empty() 是否能正确判断属性的存在性。
__unset() 可以用:来决定是否允许 unset() 删除某个属性。

示例
php 复制代码
class User {
    private $data = [
        'username' => 'john_doe',
        'email' => 'john.doe@example.com',
        'password' => 'hashed_password' // 敏感信息
    ];

    public function __isset($name) {
        // 阻止 `password` 属性被 `isset()` 检查
        if ($name === 'password') {
            return false;
        }
        return isset($this->data[$name]);
    }
}

$user = new User();
var_dump(isset($user->username)); // 输出 true
var_dump(isset($user->password)); // 输出 false,即使 `password` 实际上存在
应用场景

1、保护敏感数据,防止它们在调试或其他场景中暴露。

2、控制哪些属性能够被检查是否存在,提升代码安全性。

2、动态属性管理

在很多应用程序中,类的属性是动态存储的,例如配置类、缓存类、或从数据库获取的数据对象。使用 __isset() 和 __unset(),可以灵活地管理这些动态属性。

示例
php 复制代码
class Config {
    private $settings = [];

    public function __set($name, $value) {
        $this->settings[$name] = $value;
    }

    public function __get($name) {
        return $this->settings[$name] ?? null;
    }

    public function __isset($name) {
        // 检查动态属性是否已设置
        return array_key_exists($name, $this->settings);
    }

    public function __unset($name) {
        // 删除动态属性
        unset($this->settings[$name]);
    }
}

$config = new Config();
$config->app_name = 'MyApp';

// 检查属性是否存在
var_dump(isset($config->app_name)); // 输出 true

// 删除属性
unset($config->app_name);
var_dump(isset($config->app_name)); // 输出 false
应用场景

1、用于配置类或设置类,动态管理属性,确保灵活性和可扩展性。

2、控制对象中存储的临时或动态数据的访问和修改。

3、延迟加载与内存优化

在大型系统中,延迟加载(Lazy Loading)是一种重要的设计模式,它允许在需要时才加载对象的某些部分。通过 __isset() 可以实现对属性是否存在的延迟检测,这样只有在实际访问时才执行昂贵的计算或数据加载。

示例
php 复制代码
class DataLoader {
    private $data;

    public function __get($name) {
        // 延迟加载数据
        if ($name === 'largeData' && $this->data === null) {
            $this->data = $this->loadData();
        }
        return $this->data;
    }

    public function __isset($name) {
        // 检查是否需要加载数据
        if ($name === 'largeData') {
            return $this->data !== null;
        }
        return false;
    }

    private function loadData() {
        // 模拟数据加载
        return "Loaded Data";
    }
}

$loader = new DataLoader();

// 检查属性,延迟加载还未触发
var_dump(isset($loader->largeData)); // 输出 false

// 访问属性,触发延迟加载
echo $loader->largeData; // 输出 "Loaded Data"

// 再次检查属性
var_dump(isset($loader->largeData)); // 输出 true
应用场景

1、在访问大数据对象时确保仅在必要时加载,避免不必要的内存占用。

2、结合 __get(),实现对象中某些复杂数据的惰性初始化。

4、复杂对象结构的可控销毁

通过 __unset(),可以确保某些重要数据不会被外部轻易删除,或在删除时执行一些清理操作。

示例
php 复制代码
class Cache {
    private $cache = [
        'item1' => 'data1',
        'item2' => 'data2',
    ];

    public function __unset($name) {
        // 防止 `item1` 被删除
        if ($name === 'item1') {
            echo "item1 不允许被删除。\n";
            return;
        }
        unset($this->cache[$name]);
    }
}

$cache = new Cache();
unset($cache->item2); // 正常删除
unset($cache->item1); // 输出 "item1 不允许被删除。"
应用场景

1、防止关键属性被无意中删除。

2、在删除对象属性时需要执行额外的清理或日志记录操作

二、__clone()

1、__clone() 是 PHP 中的一个魔术方法,用于控制对象被克隆时的行为。

2、__clone() 方法允许你自定义克隆对象的行为,以实现更复杂的对象复制逻辑。

3、__clone() 方法在使用 clone 关键字克隆对象时自动调用。它可以用于调整新克隆对象的状态,比如复制引用类型的属性、重置一些属性值、或处理特定逻辑。

1、实现深拷贝

当一个对象的属性中包含其他对象时,PHP 默认的 clone 关键字会创建一个浅拷贝,这意味着属性中引用的对象不会被克隆,仍会指向原对象。通过 __clone() 方法,可以确保深拷贝,从而避免原始对象和克隆对象共享引用。

示例
php 复制代码
class Node {
    public $data;
    public $child;

    public function __clone() {
        if ($this->child !== null) {
            $this->child = clone $this->child; // 确保子对象也被克隆
        }
    }
}

$node1 = new Node();
$node1->data = "Parent";
$node1->child = new Node();
$node1->child->data = "Child";

$node2 = clone $node1; // 深拷贝,`child` 属性被单独克隆
$node2->child->data = "Modified Child";

echo $node1->child->data; // 输出 "Child"
echo $node2->child->data; // 输出 "Modified Child"
应用场景

1、处理复杂数据结构,如树形结构或图结构。

2、需要在克隆后修改子对象,以避免意外共享引用。

2、防止共享状态

当一个对象的某些属性指向外部资源或缓存时,克隆时共享这些引用可能导致冲突或意外行为。通过 __clone(),可以确保克隆对象有独立的副本。

示例:
php 复制代码
class Cache {
    public $data;

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

    public function __clone() {
        $this->data = clone $this->data; // 确保 `data` 独立拷贝
    }
}
应用场景:

在克隆时创建独立的数据缓存副本。

避免克隆对象与原对象共享同一连接、文件句柄或其他资源。

3、对象重置和初始化

在一些情况下,克隆后的对象需要进行特定的重置或重新初始化。通过 >__clone(),可以修改属性、生成新的唯一 ID 或重置状态。

示例:
php 复制代码
class Session {
    public $id;
    public $createdAt;

    public function __construct() {
        $this->id = uniqid();
        $this->createdAt = time();
    }

    public function __clone() {
        // 在克隆时重新生成唯一 ID 和时间戳
        $this->id = uniqid();
        $this->createdAt = time();
    }
}

$session1 = new Session();
$session2 = clone $session1; // 克隆并生成新的 ID 和时间戳

echo "Original Session ID: " . $session1->id . "\n";
echo "Cloned Session ID: " . $session2->id . "\n";
应用场景

克隆对象时生成新 ID,确保唯一性。

在克隆后重置时间戳、状态标志等。

4、预防克隆操作

在某些情况下,你可能希望防止对象被克隆。通过在 __clone() 中抛出异常,可以实现这一点。

示例
php 复制代码
class Singleton {
    private static $instance;

    private function __construct() {
        // 私有构造函数,防止外部实例化
    }

    public static function getInstance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    public function __clone() {
        throw new \Exception("Cloning of this object is not allowed.");
    }
}

$instance = Singleton::getInstance();

try {
    $clone = clone $instance;
} catch (\Exception $e) {
    echo $e->getMessage(); // 输出 "Cloning of this object is not allowed."
}
应用场景:

单例模式,确保对象实例是唯一的。

防止意外克隆,保护对象状态。

5、复制复杂对象中的引用属性

在包含其他对象的复杂对象中,使用 __clone() 可以确保这些引用类型的属性被正确地复制,以防止在修改克隆对象时影响原始对象。

示例:
php 复制代码
class Car {
    public $engine;

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

    public function __clone() {
        $this->engine = clone $this->engine; // 确保引擎属性被克隆
    }
}

class Engine {
    public $type;

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

$engine1 = new Engine("V8");
$car1 = new Car($engine1);

$car2 = clone $car1;
$car2->engine->type = "V6";

echo $car1->engine->type; // 输出 "V8"
echo $car2->engine->type; // 输出 "V6"
应用场景:

克隆包含依赖关系的对象。

在克隆时将部分属性复制为新的实例。

相关推荐
ServBay21 小时前
垃圾堆里编码?真的不要怪 PHP 不行
后端·php
用户962377954481 天前
CTF 伪协议
php
BingoGo3 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php
JaguarJack3 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php·服务端
BingoGo4 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php
JaguarJack4 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php·服务端
JaguarJack5 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
后端·php·服务端
BingoGo5 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
php
JaguarJack6 天前
告别 Laravel 缓慢的 Blade!Livewire Blaze 来了,为你的 Laravel 性能提速
后端·php·laravel
郑州光合科技余经理7 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php