被忽略的魔法——php引用之推迟赋值(后期数据延迟绑定)
被忽略的魔法——php引用之延迟赋值(后期数据延迟绑定)
看到这个主题大家知道我今天要说的是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);}

热AI工具

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

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

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

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

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

“你的组织要求你更改PIN消息”将显示在登录屏幕上。当在使用基于组织的帐户设置的电脑上达到PIN过期限制时,就会发生这种情况,在该电脑上,他们可以控制个人设备。但是,如果您使用个人帐户设置了Windows,则理想情况下不应显示错误消息。虽然情况并非总是如此。大多数遇到错误的用户使用个人帐户报告。为什么我的组织要求我在Windows11上更改我的PIN?可能是您的帐户与组织相关联,您的主要方法应该是验证这一点。联系域管理员会有所帮助!此外,配置错误的本地策略设置或不正确的注册表项也可能导致错误。即

Windows11将清新优雅的设计带到了最前沿;现代界面允许您个性化和更改最精细的细节,例如窗口边框。在本指南中,我们将讨论分步说明,以帮助您在Windows操作系统中创建反映您的风格的环境。如何更改窗口边框设置?按+打开“设置”应用。WindowsI转到个性化,然后单击颜色设置。颜色更改窗口边框设置窗口11“宽度=”643“高度=”500“>找到在标题栏和窗口边框上显示强调色选项,然后切换它旁边的开关。若要在“开始”菜单和任务栏上显示主题色,请打开“在开始”菜单和任务栏上显示主题

默认情况下,Windows11上的标题栏颜色取决于您选择的深色/浅色主题。但是,您可以将其更改为所需的任何颜色。在本指南中,我们将讨论三种方法的分步说明,以更改它并个性化您的桌面体验,使其具有视觉吸引力。是否可以更改活动和非活动窗口的标题栏颜色?是的,您可以使用“设置”应用更改活动窗口的标题栏颜色,也可以使用注册表编辑器更改非活动窗口的标题栏颜色。若要了解这些步骤,请转到下一部分。如何在Windows11中更改标题栏的颜色?1.使用“设置”应用按+打开设置窗口。WindowsI前往“个性化”,然

您是否在Windows安装程序页面上看到“出现问题”以及“OOBELANGUAGE”语句?Windows的安装有时会因此类错误而停止。OOBE表示开箱即用的体验。正如错误提示所表示的那样,这是与OOBE语言选择相关的问题。没有什么可担心的,你可以通过OOBE屏幕本身的漂亮注册表编辑来解决这个问题。快速修复–1.单击OOBE应用底部的“重试”按钮。这将继续进行该过程,而不会再打嗝。2.使用电源按钮强制关闭系统。系统重新启动后,OOBE应继续。3.断开系统与互联网的连接。在脱机模式下完成OOBE的所

任务栏缩略图可能很有趣,但它们也可能分散注意力或烦人。考虑到您将鼠标悬停在该区域的频率,您可能无意中关闭了重要窗口几次。另一个缺点是它使用更多的系统资源,因此,如果您一直在寻找一种提高资源效率的方法,我们将向您展示如何禁用它。不过,如果您的硬件规格可以处理它并且您喜欢预览版,则可以启用它。如何在Windows11中启用任务栏缩略图预览?1.使用“设置”应用点击键并单击设置。Windows单击系统,然后选择关于。点击高级系统设置。导航到“高级”选项卡,然后选择“性能”下的“设置”。在“视觉效果”选

在Windows11上的显示缩放方面,我们都有不同的偏好。有些人喜欢大图标,有些人喜欢小图标。但是,我们都同意拥有正确的缩放比例很重要。字体缩放不良或图像过度缩放可能是工作时真正的生产力杀手,因此您需要知道如何对其进行自定义以充分利用系统功能。自定义缩放的优点:对于难以阅读屏幕上的文本的人来说,这是一个有用的功能。它可以帮助您一次在屏幕上查看更多内容。您可以创建仅适用于某些监视器和应用程序的自定义扩展配置文件。可以帮助提高低端硬件的性能。它使您可以更好地控制屏幕上的内容。如何在Windows11

屏幕亮度是使用现代计算设备不可或缺的一部分,尤其是当您长时间注视屏幕时。它可以帮助您减轻眼睛疲劳,提高易读性,并轻松有效地查看内容。但是,根据您的设置,有时很难管理亮度,尤其是在具有新UI更改的Windows11上。如果您在调整亮度时遇到问题,以下是在Windows11上管理亮度的所有方法。如何在Windows11上更改亮度[10种方式解释]单显示器用户可以使用以下方法在Windows11上调整亮度。这包括使用单个显示器的台式机系统以及笔记本电脑。让我们开始吧。方法1:使用操作中心操作中心是访问

Windows上的激活过程有时会突然转向显示包含此错误代码0xc004f069的错误消息。虽然激活过程已经联机,但一些运行WindowsServer的旧系统可能会遇到此问题。通过这些初步检查,如果这些检查不能帮助您激活系统,请跳转到主要解决方案以解决问题。解决方法–关闭错误消息和激活窗口。然后,重新启动计算机。再次从头开始重试Windows激活过程。修复1–从终端激活从cmd终端激活WindowsServerEdition系统。阶段–1检查Windows服务器版本您必须检查您使用的是哪种类型的W
