__set/__get触发条件

本文详细解析了PHP中__set和__get魔术方法的工作原理,尤其是在成员属性不存在或难以访问时如何自动触发这些方法。文章还探讨了__set参数颠倒的情况,并解释了这种情况下产生的两次调用__set的现象及其原因。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

__set/__get多情况讨论,因为涉及成员属性自动创建的问题,比VB中的property get/let/set要复杂

当一个对象变量(包括$this)试图引用一个难以达到的成员属性的时候自动触发__set/__get
所谓难以达到对不同的对象变量有不同的含义:
对类外部实例对象来说包括成员属性不存在和private/protected成员属性
对类内部$this或全局global$class来说仅指成员属性不存在

当最终访问的成员属性不存在时,这里的强调"最终",就是要不包括经过__set/__get的中间过程
如果是赋值,就会自动创建public成员变量
如果是取值,就会"Notice:未定义属性"

当没有可调用的__set/__get时
如果是因为private/protected成员属性,则产生中断错误,无法访问私有属性
如果是因为员属性不存在,同上
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<?
	class test{		
		private $sx;		
		function __set($v, $n){	
			echo "经过Set<br>";	
			$this->$n = $v;							
		}
		function __get($n){						
			echo "经过Get<br>";	
			return $this->$n;	
		}	
	}
	
	$demo = new test;
	$demo->sx = '设置私有成员但是参数倒置';
	echo $demo->sx;	
?>
输出结果:
经过Set
经过Set
经过Get
设置私有成员但是参数倒置

通过上面的总结,分析上面这个类,当__set参数放反后所产生的结果
问题主要集中在为什么会有2次set,而最后又能正确调用get,返回正确的值
执行$demo->sx = "设置私有成员但是参数倒置";由于对象变量访问的是难以达到的私有成员,则自动引发__set
在__set中$n = '设置私有成员但是参数倒置',$v = 'sx'; 然后执行$this->$n = $v,相当于$this->设置私有成员但是参数倒置 = 'sx';
上面的执行过程,会再次调用__set,这是造成2次set原因,而且这种调用是递归性质的,第2次调用__set后,$n/$v再次转置,变成正确的顺序
这也就造成了__get能够正确执行

当把
$demo = new test;
$demo->sx = '设置私有成员但是参数倒置';
echo $demo->sx;
中的sx变成一个类中不存在的属性的时,输出结果中的get便会消失,因为在第2次调用__set时,会创建一个public成员,写入'设置私有成员但是参数倒置'
然后取值的时候,由于是取public成员,自然不经过__get
``` #include <stdio.h> #include <string.h> #include <stdlib.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/queue.h" #include "driver/gpio.h" #define GPIO_OUTPUT_IO_0 18 #define GPIO_OUTPUT_PIN_SEL (1ULL<<GPIO_OUTPUT_IO_0) #define GPIO_INPUT_IO_0 4 #define GPIO_INPUT_PIN_SEL (1ULL<<GPIO_INPUT_IO_0) #define ESP_INTR_FLAG_DEFAULT 0 static xQueueHandle gpio_evt_queue = NULL; //FreeRTOS的队列句柄 static void IRAM_ATTR gpio_isr_handler(void* arg) //函数gpio_isr_handler的调用规范 { uint32_t gpio_num = (uint32_t) arg; xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL); } static void gpio_task_example(void* arg) //构建任务 { uint32_t io_num; for(;;) { if(xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) { //接收队列 printf("GPIO[%d] intr, val: %d\n", io_num, gpio_get_level(io_num)); } } } void app_main(void) //主函数 { gpio_config_t io_conf; //定义结构体 io_conf.intr_type = GPIO_PIN_INTR_DISABLE; //禁用中断 io_conf.mode = GPIO_MODE_OUTPUT; //设置输出模式 io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL; //GPIO18的位掩码 io_conf.pull_down_en = 0; //禁用下拉模式 io_conf.pull_up_en = 0; //禁用上拉模式 gpio_config(&io_conf); //使用以上参数初始化GPIO io_conf.intr_type = GPIO_PIN_INTR_POSEDGE; //上升沿触发中断 io_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL; //GPIO4的位掩码 io_conf.mode = GPIO_MODE_INPUT; //设置输入模式 io_conf.pull_up_en = 1; //使能上拉模式 gpio_config(&io_conf); //使用以上参数配置 gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t)); //创建队列处理中断 xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL); //开启任务 gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT); //安装GPIO中断服务 gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void*) GPIO_INPUT_IO_0); //GPIO引脚挂钩中断服务程序 int cnt = 0; while(1) { printf("cnt: %d\n", cnt++); //输出计数 vTaskDelay(1000 / portTICK_RATE_MS); //延时1s gpio_set_level(GPIO_OUTPUT_IO_0, cnt % 4); //每隔4个计数,输出一次中断 //gpio_set_level(GPIO_OUTPUT_IO_1, cnt % 2); } }```详细解释每一句代码
03-19
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值