首页 后端开发 php教程 被忽略的魔法--php引用之延迟赋值(后期数据延迟绑定)

被忽略的魔法--php引用之延迟赋值(后期数据延迟绑定)

Jun 23, 2016 pm 01:34 PM

       看到这个主题大家知道我今天要说的是php的变量引用特性,但是延迟赋值又是怎么回事呢?这个主要是我近期优化一些功能时的一个想法,我觉得还算不错,就打算记录下来。看一下下面的伪代码:

// 这段代码有人会说为啥不用联表,因为有些业务需求不用联表的效率是联表的3到20倍// 我的项目里基本都是此类写法,比之前联表效率提升很多$a = DB::query("select id from a");$aid = "";foreach($a as $v){	$aid .= $v['id'].','; }$aid = substr($aid, 0 , -1);if($aid){	$b = DB::query("select * from b where aid in ({$aid})");		// 此处省略}
登录后复制

之所以用这段代码举例,因为类似这样的代码很多,大家比较容易理解,但不一定适合用后期延迟赋值,因为这样更容理解,且效率差不多,不过可以和后期延迟赋值形成鲜明对比,让大家更容理解下面的实现的方式。

看完上面的例子我们再看一个复杂的需求,要求数据是获取一人的最近10篇文章列表,且读取每篇文章5条评论,并包含文章发起者和评论人的id,姓名,把是数据打包成指定格式的json返回给客户端,看下面的代码:

// 这种需求用联表获取用户信息远没有搜集用户id做in查询效率高$data = array();$article = DB::query("select id,uid,title,content from article where uid={$_GET['uid']} order by id desc limit 10");foreach($article as $v){	$uid = $v['uid'];	$comment = DB::query("select id,uid,content from comment where aid={$v['id']} order by id asc limit 5");		foreach($comment as $value){		$uid .= ','.$value['uid'];	}	// 这里第二个参数我们要求DB类返回的数组以uid为索引	$member = DB::query("select uid,username from user where uid in({$uid})", 'uid');	$commentList = array();	$data[] = array(		'id' => $v['id'],		'title' => $v['title'],		'content' => $v['content'],		'uid' => $v['uid'],		'username' => $member[$v['uid']]['username'],		'comment' => &$commentList	);	foreach($comment as $value){		$commentList[] = array(			'id' => $value['id'],			'content' => $value['content'],			'uid' => $value['uid'],			'username' => $member[$value['uid']]['username']		)	}}	echo json_encode($data);exit;
登录后复制

细心看这段代码就会发现其中$data[]['comment']的值最开始就引用了变量$commentList,之后在后面更改了$commentList的值,同时导致$data[]['comment']值跟着一起发生了改变,这里也是后期延迟赋值,但是比较简单,可以据此了解一下这个实现原理。

我相信大多数人都写过类似的代码,也很少有人觉得这段代码会有问题。我来分析一下这块的逻辑,评论信息因为要每篇文章获取5条,这个没法用简单的sql合并成一条,由于需求只是获取10篇文章,写复杂的sql处理反而不如循环查询的效率(如果你内网延迟较低的情况下),为啥说复杂sql的处理效率慢,如果你考虑的是几千条数据都一样没啥需要注意,但是如果处理的是千万级别的数据量,复杂sql很多情况下不如简单的sql效率高,那么这里就采用循环查询没法继续优化。但是我们看用户信息也在循环里进行查询,这个很不好了,我的项目组里很不希望见到这样的代码的,10篇帖子都是一个人发起的,近50条评论会有很多活跃用户的数据,这样每次循环查询其实查询到重复用户信息的概率非常高,在一个业务逻辑里最好不要从数据库获取重复信息而是复用。如何能让让用户信息达到复用呢?可以在组装最终数据前循环获取文章的评论信息,之后收集用户id,然后再获取用户信息,之后组装最终的数据。这是一种比较简单的解决方案,不是我们今天的重点,下面看一种优雅的处理方式??延迟赋值。

// 这种需求用联表获取用户信息远没有搜集用户id做in查询效率高$data = array();$article = DB::query("select id,uid,title,content from article where uid={$_GET['uid']} order by id desc limit 10");$member = array();foreach($article as $v){	$comment = DB::query("select id,uid,content from comment where aid={$v['id']} order by id asc limit 5");	$commentList = array();	$data[] = array(		'id' => $v['id'],		'title' => $v['title'],		'content' => $v['content'],		'uid' => $v['uid'],		'username' => &$member[$v['uid']]['username'],		'comment' => &$commentList	);	foreach($comment as $value){		$commentList[] = array(			'id' => $value['id'],			'content' => $value['content'],			'uid' => $value['uid'],			'username' => &$member[$value['uid']]['username']		)	}}$uid = array_keys($member);if($uid){	$uid = implode(',', $uid);	$user = DB::query("select uid,username from user where uid in({$uid})", 'uid');	foreach($member as $uid => $value){		$member[$uid]['username'] = $user[$uid]['username'];	}	unset($member,$user);}echo json_encode($data);exit;
登录后复制

这段代码和之前不太一样了,最明显的就是最下面多了一段代码,而且暂时还不知道究竟是干嘛,好像没啥用,我们一步步的看看,首先在循环文章数据前初始化了一个变量$member = array();之后在循环里少了$uid的赋值,以及循环收集评论人的id,并且查询用户数据的sql也不见了,好像到了最下面那段看不懂的代码地方。仔细找了找还发现&$member[$v['uid']]['username']和&$member[$value['uid']]['username']地方多了&引用符号,这就是为啥循环里少了写代码的奥秘。回想一下之前发现$commentList被引用之后在后面进行赋值的,并且改变了$data[]['comment']。这的道理是一样的,先不查询用户信息,只进行一个空的引用,在引用一个不存在的变量时php会先创建这个变量,例如&$member[$v['uid']]['username'],php检测$member是一个数组已经声明,但是$member[$v['uid']]['username']不存在就在内存创建并且值为null。

当循环完文章数据后打印会发现username的信息都是null,当然之前并没用用户信息,php在引用赋值的时候帮我们给了一个null值。之后通过$uid = array_keys($member);获取所有用户的id信息,

为什么array_keys能获取用户id,因为php在引用的时候帮我们创建了$member数组呀,注意一下这里的uid是不重复的哟,之后我们去user表用in检索用户信息,一定注意这里不能把返回的数据赋值给$member因为之前的数据都是引用$member里的数据,如果这里覆盖了$member,内存里两个变量的地址就不一样了,相当于重新创建了一个数组,我们这里赋值给$user,下面的循环是干什么的,当然是修改之前被引用数据的赋值了,我们循环$member变量把$user[$uid]['username']赋值给$member[$uid]['username'],从而改变引用变量的值。在我们把数据绑定到引用变量后千万不要忽略用uset把$member删除了,主要是防止之后的代码里出现操作$member变量的代码,不小心就会把之前绑定好的数据覆盖掉。为啥删除$member之后绑定的数据没有丢失,主要是引用的特性,当多个变量引用一个内存地址时,删除其中一个变量不影响其它变量,除非把所有变量都删除,才会真的删除内存里的数据。php手册是这么解释的“当 unset 一个引用,只是断开了变量名和变量内容之间的绑定。这并不意味着变量内容被销毁了”。unset($user);只是因为$user是一个临时变量,使用完可以直接从内存释放了。

关于这种编程方式我起名为后期数据延迟绑定,之所以标题是延迟变量赋值主要是让大家便于理解。Php中引用的作用非常广泛,本文所举的例子也只局部的一种使用方法,用来解决编程中遇到类似业务需求的一种处理方式,当然后期数据延迟绑定的编程方法也有很广的使用,希望大家不要局限在本文例子的场景上。针对本文例子是我们编程中最常用的一种问题,我编写了一个函数用来处理数据延迟绑定,减少每个地方都要编写数据绑定的逻辑。

/** * 数据延迟绑定通用方法 *  * @access public * @param array $bindingVar		待绑定的变量 * @param array	$data		待绑定的数据 * @param mixed	$default	默认值 * @return void  */function bindingData(&$bindingVar, $data, $default=''){	foreach($bindingVar as $key => $tmp){		foreach($tmp as $k => $v){			$bindingVar[$key][$k] = isset($data[$key][$k]) ? $data[$key][$k] : $default;		}	}		unset($bindingVar);}
登录后复制


采用这个函数我们能把之前处理数据绑定的代码部分改成下面这样:

$uid = array_keys($member);if($uid){	$user = DB::query("select uid,username from user where uid in({$uid})", 'uid');	bindingData($member, $user);	unset($member,$user);}
登录后复制


 

 

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

<🎜>:泡泡胶模拟器无穷大 - 如何获取和使用皇家钥匙
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系统,解释
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆树的耳语 - 如何解锁抓钩
3 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

热门话题

Java教程
1676
14
CakePHP 教程
1429
52
Laravel 教程
1333
25
PHP教程
1278
29
C# 教程
1257
24
说明PHP中的安全密码散列(例如,password_hash,password_verify)。为什么不使用MD5或SHA1? 说明PHP中的安全密码散列(例如,password_hash,password_verify)。为什么不使用MD5或SHA1? Apr 17, 2025 am 12:06 AM

在PHP中,应使用password_hash和password_verify函数实现安全的密码哈希处理,不应使用MD5或SHA1。1)password_hash生成包含盐值的哈希,增强安全性。2)password_verify验证密码,通过比较哈希值确保安全。3)MD5和SHA1易受攻击且缺乏盐值,不适合现代密码安全。

PHP类型提示如何起作用,包括标量类型,返回类型,联合类型和无效类型? PHP类型提示如何起作用,包括标量类型,返回类型,联合类型和无效类型? Apr 17, 2025 am 12:25 AM

PHP类型提示提升代码质量和可读性。1)标量类型提示:自PHP7.0起,允许在函数参数中指定基本数据类型,如int、float等。2)返回类型提示:确保函数返回值类型的一致性。3)联合类型提示:自PHP8.0起,允许在函数参数或返回值中指定多个类型。4)可空类型提示:允许包含null值,处理可能返回空值的函数。

PHP和Python:解释了不同的范例 PHP和Python:解释了不同的范例 Apr 18, 2025 am 12:26 AM

PHP主要是过程式编程,但也支持面向对象编程(OOP);Python支持多种范式,包括OOP、函数式和过程式编程。PHP适合web开发,Python适用于多种应用,如数据分析和机器学习。

您如何防止PHP中的SQL注入? (准备的陈述,PDO) 您如何防止PHP中的SQL注入? (准备的陈述,PDO) Apr 15, 2025 am 12:15 AM

在PHP中使用预处理语句和PDO可以有效防范SQL注入攻击。1)使用PDO连接数据库并设置错误模式。2)通过prepare方法创建预处理语句,使用占位符和execute方法传递数据。3)处理查询结果并确保代码的安全性和性能。

PHP和Python:代码示例和比较 PHP和Python:代码示例和比较 Apr 15, 2025 am 12:07 AM

PHP和Python各有优劣,选择取决于项目需求和个人偏好。1.PHP适合快速开发和维护大型Web应用。2.Python在数据科学和机器学习领域占据主导地位。

PHP:处理数据库和服务器端逻辑 PHP:处理数据库和服务器端逻辑 Apr 15, 2025 am 12:15 AM

PHP在数据库操作和服务器端逻辑处理中使用MySQLi和PDO扩展进行数据库交互,并通过会话管理等功能处理服务器端逻辑。1)使用MySQLi或PDO连接数据库,执行SQL查询。2)通过会话管理等功能处理HTTP请求和用户状态。3)使用事务确保数据库操作的原子性。4)防止SQL注入,使用异常处理和关闭连接来调试。5)通过索引和缓存优化性能,编写可读性高的代码并进行错误处理。

PHP的目的:构建动态网站 PHP的目的:构建动态网站 Apr 15, 2025 am 12:18 AM

PHP用于构建动态网站,其核心功能包括:1.生成动态内容,通过与数据库对接实时生成网页;2.处理用户交互和表单提交,验证输入并响应操作;3.管理会话和用户认证,提供个性化体验;4.优化性能和遵循最佳实践,提升网站效率和安全性。

在PHP和Python之间进行选择:指南 在PHP和Python之间进行选择:指南 Apr 18, 2025 am 12:24 AM

PHP适合网页开发和快速原型开发,Python适用于数据科学和机器学习。1.PHP用于动态网页开发,语法简单,适合快速开发。2.Python语法简洁,适用于多领域,库生态系统强大。

See all articles