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

在这里插入图片描述

一、什么是魔术方法

PHP中的魔术方法是以__两个下划线开头的方法,这些方法提供了一种机制,可以在类的生命周期中拦截某些事件或者进行一些操作

二、魔术方法有哪些

一、__construct()&&__destruct()

  1. __construct()构造函数,__destruct()析构函数,这两个上期文章中讲到过在这就不再赘述了

二、__call()&&__callStatic()

用于处理未定义的方法调用,尝试调用不存在的方法或静态方法时PHP解释器会自动调用__call()方法
参数:将原本要调用的方法名和参数列表作为参数传递给该方法。这样可以在运行时动态处理方法调用,从而实现更灵活的对象行为

  1. __call():当调用一个对象中不存在或不可访问的方法时触发

  2. __callStatic():当调用一个类中不存在或不可访问的静态方法时触发。

示例:

class MagicMethods {public function __call($name, $arguments) {echo "尝试调用方法 '$name',参数为:" . implode(', ', $arguments) . "\n";}public static function __callStatic($name, $arguments) {echo "尝试调用静态方法 '$name',参数为:" . implode(', ', $arguments) . "\n";}
}$obj = new MagicMethods();
$obj->nonExistentMethod('param1', 'param2');  // 输出 "尝试调用方法 'nonExistentMethod',参数为:param1, param2"
MagicMethods::nonExistentStaticMethod('staticParam1');  // 输出 "尝试调用静态方法 'nonExistentStaticMethod',参数为:staticParam1"

应用场景:

在这里就不做具体介绍了,因为我没咋用过,等我研究研究在再进行详细介绍,用到最多的功能就是他的拦截日志方法的调用,记录那些方法被调用,以便进行调试或监控。

  1. 实现动态代理

在面向对象编程中,__call() 和 __callStatic() 可以用于实现方法调用的动态代理,将方法请求委托给另一个对象。这在构建装饰器或代理模式时非常有用。

class Proxy {private $realObject;public function __construct($realObject) {$this->realObject = $realObject;}public function __call($name, $arguments) {echo "代理对象调用方法 '$name',参数:" . implode(', ', $arguments) . "\n";return call_user_func_array([$this->realObject, $name], $arguments);}public static function __callStatic($name, $arguments) {echo "代理对象调用静态方法 '$name',参数:" . implode(', ', $arguments) . "\n";// 在静态代理中处理逻辑}
}class RealObject {public function doSomething($param) {return "真实对象处理了:$param";}
}$realObj = new RealObject();
$proxy = new Proxy($realObj);echo $proxy->doSomething('Task');  // 输出 "代理对象调用方法 'doSomething',参数:Task" 和 "真实对象处理了:Task"

在大型应用程序中使用代理模式,为对象的调用添加日志、缓存或权限验证逻辑。
为第三方 API 客户端提供动态代理以封装请求。

  1. 实现多态行为

在需要通过不同的方法名称来触发类似操作的场景中,__call() 和 __callStatic() 可以简化代码,比如创建动态的 getter 和 setter 方法。

class DynamicMethods {private $data = [];public function __call($name, $arguments) {if (preg_match('/^get(.+)$/', $name, $matches)) {$property = strtolower($matches[1]);return $this->data[$property] ?? null;}if (preg_match('/^set(.+)$/', $name, $matches)) {$property = strtolower($matches[1]);$this->data[$property] = $arguments[0];return $this;}throw new BadMethodCallException("方法 '$name' 不存在");}public static function __callStatic($name, $arguments) {echo "静态方法 '$name' 被调用,参数:" . implode(', ', $arguments) . "\n";// 静态方法处理逻辑}
}$obj = new DynamicMethods();
$obj->setName('John')->setAge(30);
echo $obj->getName();  // 输出 "John"
echo $obj->getAge();   // 输出 "30"

快速实现对象属性的动态 getter 和 setter 方法,减少重复代码。
创建灵活的工厂方法,动态生成实例。

  1. 日志记录与监控

使用 __call() 和 __callStatic() 实现方法调用的监控和日志记录,以便调试或审计。

class Logger {public function __call($name, $arguments) {echo "调用了实例方法 '$name',参数:" . json_encode($arguments) . "\n";// 在此处添加日志写入逻辑}public static function __callStatic($name, $arguments) {echo "调用了静态方法 '$name',参数:" . json_encode($arguments) . "\n";// 在此处添加静态日志写入逻辑}
}$logger = new Logger();
$logger->trackEvent('userLogin', ['userId' => 123]);  // 输出 "调用了实例方法 'trackEvent',参数:[\"userLogin\",{\"userId\":123}]"Logger::logEvent('systemStart');  // 输出 "调用了静态方法 'logEvent',参数:[\"systemStart\"]"

在开发调试过程中捕获未定义的方法调用并记录日志。
为应用程序添加额外的审计和监控功能,而无需显式定义每个方法。

  1. 简化 API 客户端设计

在构建访问外部服务的 API 客户端时,__call() 可以用于动态构建方法以适应 API 的多样性。

class APIClient {public function __call($name, $arguments) {$endpoint = strtolower($name);$params = !empty($arguments) ? json_encode($arguments[0]) : '{}';echo "调用 API 端点 '$endpoint',参数:$params\n";// 在此处进行实际的 API 请求return "返回的 API 响应";}
}$client = new APIClient();
$response = $client->getUser(['id' => 1]);  // 输出 "调用 API 端点 'getuser',参数:{\"id\":1}"
echo $response;  // 输出 "返回的 API 响应"
  1. 实现通用调用处理

在构建框架或工具库时,__call() 和 __callStatic() 可以作为通用入口,处理对未知或动态行为的请求。

class FrameworkHandler {public function __call($name, $arguments) {echo "处理实例方法调用 '$name',执行通用逻辑\n";// 根据 $name 和 $arguments 进行处理}public static function __callStatic($name, $arguments) {echo "处理静态方法调用 '$name',执行通用逻辑\n";// 根据 $name 和 $arguments 进行静态处理}
}$handler = new FrameworkHandler();
$handler->runTask();  // 输出 "处理实例方法调用 'runTask',执行通用逻辑"FrameworkHandler::startProcess();  // 输出 "处理静态方法调用 'startProcess',执行通用逻辑"

在框架中提供灵活的调用接口,通过名称匹配处理不同的任务。
在工具库中动态响应未知或变化的需求。

三、__get()&&__set()

一般来说,总是把类的属性定义为private,这更符合现实的逻辑。但是,对属性的读取和赋值操作是非常频繁的,因此在中,预定义了两个函数“__get()”和“__set()”来获取和赋值其属性

  1. __get(): 试图读取不可访问或不存在的属性时调用
  2. __set():试图给不可访问或不存在的属性赋值时调用。

示例:

class PropertyTest {private $data = [];public function __get($name) {echo "获取属性 '$name'\n";return isset($this->data[$name]) ? $this->data[$name] : null;}public function __set($name, $value) {echo "设置属性 '$name' 为 '$value'\n";$this->data[$name] = $value;}
}$obj = new PropertyTest();
$obj->name = 'ChatGPT';  // 输出 "设置属性 'name' 为 'ChatGPT'"
echo $obj->name;         // 输出 "获取属性 'name'" 并显示 "ChatGPT"

应用场景:

__get() 和 __set() 是 PHP 中的魔术方法,允许开发者在对象访问或设置未定义或私有属性时,自定义其行为。这在增强对象的灵活性和保护属性访问方面非常有用:

  1. 实现动态属性访问

使用 __get() 和 __set() 可以实现对未定义或私有属性的访问和设置,从而避免显式定义大量的 getter 和 setter 方法。

class DynamicProperties {private $data = [];public function __get($name) {echo "访问属性 '$name'\n";return $this->data[$name] ?? null;}public function __set($name, $value) {echo "设置属性 '$name' 为 '$value'\n";$this->data[$name] = $value;}
}$obj = new DynamicProperties();
$obj->title = 'Hello World';  // 输出 "设置属性 'title' 为 'Hello World'"
echo $obj->title;             // 输出 "访问属性 'title'" 并显示 "Hello World"

在动态处理类属性时,减少显式声明的代码量。
访问未定义的属性时提供默认行为,避免未定义属性引起的错误。

  1. 数据封装和保护

__get() 和 __set() 可以用来实现对私有属性的访问控制,确保数据在访问或修改时进行验证和安全检查。

class User {private $attributes = [];public function __get($name) {if (!array_key_exists($name, $this->attributes)) {throw new Exception("属性 '$name' 不存在");}return $this->attributes[$name];}public function __set($name, $value) {if ($name === 'password') {// 对密码进行加密处理$value = password_hash($value, PASSWORD_DEFAULT);}$this->attributes[$name] = $value;}
}$user = new User();
$user->password = 'my_secure_password';  // 输出 "设置属性 'password' 为加密后的值"
echo $user->password;  // 获取加密后的密码

在设置属性值时进行验证或处理,例如加密密码、格式化日期等。
在获取属性时进行权限检查或其他逻辑控制。

  1. 实现对象的懒加载

使用 __get() 可以在第一次访问某个属性时延迟加载其值,这在需要节省资源或减少延迟加载的应用程序中很有用。

class LazyLoader {private $properties = [];private $loadedProperties = [];public function __get($name) {if (!isset($this->loadedProperties[$name])) {echo "延迟加载属性 '$name'\n";// 模拟从数据库或外部资源加载数据$this->properties[$name] = "Value for $name";$this->loadedProperties[$name] = true;}return $this->properties[$name];}
}$obj = new LazyLoader();
echo $obj->data;  // 输出 "延迟加载属性 'data'" 并显示 "Value for data"

当需要从数据库或远程服务加载数据时,只在属性第一次被访问时加载。
在大型对象或性能敏感的应用中减少初始化时间和内存占用。

  1. 实现对象的配置类

通过 __get() 和 __set() 方法,可以创建支持动态配置和访问的类

class Config {private $settings = [];public function __get($name) {echo "获取配置 '$name'\n";return $this->settings[$name] ?? null;}public function __set($name, $value) {echo "设置配置 '$name' 为 '$value'\n";$this->settings[$name] = $value;}
}$config = new Config();
$config->database = 'mysql';  // 输出 "设置配置 'database' 为 'mysql'"
echo $config->database;       // 输出 "获取配置 'database'" 并显示 "mysql"

设计一个简单的配置管理器,允许在程序运行时动态添加和修改配置。
适用于存储和访问应用程序设置、用户首选项等。

  1. 实现数据模型的属性映射

在 ORM(对象关系映射)或数据模型中,__get() 和 __set() 可以用来映射对象属性到数据库字段。

class DataModel {private $data = [];public function __get($name) {// 将属性名转换为数据库字段名或返回值$field = strtolower($name);echo "从数据库获取字段 '$field'\n";return $this->data[$field] ?? null;}public function __set($name, $value) {$field = strtolower($name);echo "设置字段 '$field' 为 '$value'\n";$this->data[$field] = $value;}
}$model = new DataModel();
$model->Name = 'John Doe';  // 输出 "设置字段 'name' 为 'John Doe'"
echo $model->Name;          // 输出 "从数据库获取字段 'name'" 并显示 "John Doe"

在 ORM 中,自动映射对象属性和数据库字段以减少手动代码编写。
在创建数据模型类时,使对象和数据源(如数据库表)之间的映射更加透明。

总结

这篇文章感觉怪怪的,因为写了那么多年代码很少用到这些魔术方法,还是在上学的时候用过(考试会考到),后来框架用多了就很少关注这些最基本的魔术方法

现在写的时候还需要去翻文档查资料,写着写着自己感觉到这些知识就很陌生,所以以后还是的常看啊,可能这也是我开始写技术学习的初衷,好记性不如烂笔头

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/60203.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

蔚来Android面试题及参考答案(3万字长文)

说一说 MVP 架构,MVVM 架构 MVP(Model - View - Presenter)架构: Model:它主要负责数据的获取和存储,例如从数据库、网络或者其他数据源获取数据。模型层是独立于视图层的,它不关心数据是如何展示的,只专注于数据本身的操作。比如一个新闻类应用,模型层就负责从服务器…

PNG图片批量压缩exe工具+功能纯净+不改变原始尺寸

小编最近有一篇png图片要批量压缩,大小都在5MB之上,在网上找了半天要么就是有广告,要么就是有毒,要么就是功能复杂,整的我心烦意乱。 于是我自己用python写了一个纯净工具,只能压缩png图片,没任…

【论文阅读】小样本学习相关研究

相关文献 Generalizing from a Few Examples: A Survey on Few-Shot Learning Author: YAQING WANG、QUANMING YAO、JAMES T. KWOK、LIONEL M. NIAbstract: Artificial intelligence succeeds in data-intensive applications, but it lacks the ability of learning from a …

【架构论文-1】面向服务架构(SOA)

【摘要】 本文以我参加公司的“生产线数字孪生”项目为例,论述了“面向服务架构设计及其应用”。该项目的目标是构建某车企的数字孪生平台,在虚拟场景中能够仿真还原真实产线的动作和节拍,实现虚实联动,从而提前规避问题&#xff…

常见的排序算法及分类对比

虽然在竞赛和编程语言中用到的排序算法主要是时间复杂度为 O ( n log ⁡ n ) O(n \log n) O(nlogn) 的高效算法,但作为算法学习,我们要从简单到复杂,认识常见的排序算法,并理解其算法思想。本文列出几乎所有的排序算法并进行分类对比。 排序算法总表 以下是一个对比表格…

Kafka-broker处理producer请求-leader篇

一、上下文 《Kafka-生产者源码分析》博客中我们了解了Kafka是如何生产数据的,《Kafka-broker粗粒度启动流程》博客中我们了解了KafkaApis中有各种api和对应处理逻辑,其中PRODUCE请求对应了处理produce请求的逻辑,下面我们跟着源码来看下处理…

mysql占用内存过大问题排查

如果 MySQL 占用内存过高,可以按照以下步骤进行排查: 一、检查 MySQL 配置参数 查看 innodb_buffer_pool_size: 这个参数决定了 InnoDB 存储引擎缓冲池的大小,它会占用大量内存。如果设置得过大,可能导致内存占用过高…

pulsar bookies 磁盘资源无法释放问题排查

查询bookie节点状态 bin/bookkeeper shell bookieinfo 报错如下: Error during flush Nov 11 16:03:05 pulsar-192-0-20-118 pulsar[10500]: java.io.IOException: java.lang.RuntimeException: java.io.FileNotFoundException: /data/bookkeeper/ledgers/current/6…

4-1-2.C# 数据容器 - List 扩展(List 注意事项、List 存储对象的特性、List 与数组的转换)

List 概述 List<T> 存储的元素是有序的 List<T> 存储的元素是可重复的 List<T> 支持泛型&#xff0c;可以指定存储的元素的类型 List<T> 支持索引&#xff0c;可以通过索引获取或修改元素 List<T> 支持动态大小&#xff08;有扩容机制&#…

代码随想录算法训练营第四十三天|Day43 动态规划

300.最长递增子序列 视频讲解&#xff1a;https://www.bilibili.com/video/BV1ng411J7xP https://programmercarl.com/0300.%E6%9C%80%E9%95%BF%E4%B8%8A%E5%8D%87%E5%AD%90%E5%BA%8F%E5%88%97.html 思路 #define max(a, b) ((a) > (b) ? (a) : (b)) int lengthOfLIS(int*…

Axure网络短剧APP端原型图,竖屏微剧视频模版40页

作品概况 页面数量&#xff1a;共 40 页 使用软件&#xff1a;Axure RP 9 及以上&#xff0c;非软件无源码 适用领域&#xff1a;短剧、微短剧、竖屏视频 作品特色 本作品为网络短剧APP的Axure原型设计图&#xff0c;定位属于免费短剧软件&#xff0c;类似红果短剧、河马剧场…

【LeetCode】【算法】560. 和为 K 的子数组

LeetCode 560. 和为 K 的子数组 题目描述 给你一个整数数组 nums 和一个整数 k &#xff0c;请你统计并返回 该数组中和为 k 的子数组的个数 。 子数组是数组中元素的连续非空序列。 思路 思路&#xff1a;前缀和 定义数组preSum[nums.length1]&#xff0c;在里面计算nums…

【Spring】本地事务

一、事务的基本性质 原子性&#xff1a;一系列操作整体不可拆分&#xff0c;要么同时成功&#xff0c;要么同时失败。&#xff08;下订单、减库存、减积分&#xff09;一致性&#xff1a;数据在事务的前后&#xff0c;业务整体一致。&#xff08;存取钱的总数量&#xff09;隔…

从零开始的 Hugging Face 项目:我的首个在线 SQL 查询工具之旅20241111

从零开始的 Hugging Face 项目&#xff1a;我的首个在线 SQL 查询工具之旅 作为一名 AI 初学者&#xff0c;我最近完成了一个意义非凡的项目&#xff1a;在 Hugging Face Spaces 上构建了一个简单却实用的在线 SQL 查询工具。这个项目不仅让我了解了 Hugging Face 平台的核心功…

Windows,虚拟机Ubuntu和开发板三者之间的NFS服务器搭建

Windows,虚拟机Ubuntu和开发板三者之间的NFS服务器搭建 &#xff08;1&#xff09;虚拟机 ubuntu 要使用桥接模式&#xff0c;不能使用其他模式 &#xff08;2&#xff09;通过网线将PC和开发板网口直连:这样的连接&#xff0c;开发板是无法连接外网的 &#xff08;3&#xff…

C语言第十一周课——函数的调用

目录 一、冒泡法排序 二、二分法查找 一、冒泡法排序 通过调用函数来实现冒泡法 #include<stdio.h> // 定义数组长度 #define SIZE 3 void bubbleSort(int arr[], int n);int main() {int arr[SIZE];int i;// 从控制台输入数字到数组printf("请输入%d个整数&#x…

C# 有趣的小程序—桌面精灵详细讲解

C# 桌面精灵详细讲解 最近写了一个简化版桌面精灵&#xff0c;效果如图所示&#xff0c;可以实现切换动画&#xff0c;说话、鼠标拖动&#xff0c;等功能。具体如何做&#xff0c;我发布了一个资源里面包含ppt详解、源代码以及动画素材。放心吧&#xff0c;免费的&#xff0c;…

【系统架构设计师】真题论文: 论软件可靠性设计与应用(包括解题思路和素材)

更多内容请见: 备考系统架构设计师-专栏介绍和目录 文章目录 真题题目(2009年 试题4)解题思路论文素材参考软件可靠性概念软件可靠性的重要意义影响软件可靠性的因素软件可靠性设计方法真题题目(2009年 试题4) 目前在企业中,以软件为核心的产品得到了广泛的应用。随着系…

如何开发查找附近地点的微信小程序

我开发的是找附近卫生间的小程序。 在现代城市生活中&#xff0c;找到一个干净、方便的公共卫生间有时可能是一个挑战。为了解决这个问题&#xff0c;我们可以开发一款微信小程序&#xff0c;帮助用户快速找到附近的卫生间。本文将介绍如何开发这样一款小程序&#xff0c;包…

视觉SLAM数学基础

本文系统梳理从相机成像模型&#xff0c;通过不同图像帧之间的构造几何约束求解位姿变换&#xff0c;再根据位姿变换和匹配点还原三维坐标的过程&#xff0c;可以作为基于特征点法的视觉SLAM的数学基础。 1、相机成像模型 1.1、针孔相机模型 实际相机的成像方式通常很复杂&a…