侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

php语言中对象是通过引用传递的吗?不是的,因为一个对象只会存在一次

php  /  管理员 发布于 2年前   388

PHP中的引用

PHP 文档告诉我们引用是“别名”,这意味着我们可以告诉 PHP 我们希望两个变量指向相同的数据

(文档还告诉我们,尽管看起来它们不是指针,这并不是特别有用,除非你'已经精通C)。

创建参考的符号是&。

所以我们可以这样做:

$a = 'foo'; // $a contains 'foo'
$b = &$a;   // $b is now a reference, or an alias of $a
$b = 'bar' // We're actually changing the content of $a
echo $a    // displays 'bar' (because we just changed the value)
echo $b    // displays 'bar' (because it's still a reference to $a)

这是一个不错的小怪事,但在您的日常代码中确实没那么有用。

现在更有用的是,您可以通过引用函数来传递变量,这样函数就可以在自己的范围之外实际修改变量的内容。

这是通过&在函数声明中的参数前面附加的来完成的。

/**
 * Decrements a number if it's > 0.
 * Returns true if it was decremented, false if it was already 0 or negative;
 */
function decrement(int &$a) {
    if ($a > 0) {
        $a = $a - 1;
        return true;
    }
    return false;
}
$timeLeft = 10;
$wasModified = decrement($timeLeft);
echo $timeLeft; // will output 9

如您所见,这允许我们返回一些内容,并直接修改传递给函数的内容。

这在preg_match()函数中特别使用,如果匹配,它将返回 true,并接受$m将被修改以包含匹配内容的参数。这是一个有用的功能,但它也可能导致难以跟踪错误,因此您应该谨慎使用它。

引用传递

为什么对象看起来像是通过引用传递的?

您可能已经在某处读到过在 PHP 中对象总是通过引用传递的。

它确实看起来像。举个简单的例子:

function doStuff($object) {
    $object->data = 'newValue';
}

$object = (object)['data' => 'value'];
doStuff($object);
echo $object->data; // displays 'newValue'

如您所见,&我们的函数声明中没有,但$object->data现在包含“newValue”而不是“value”。

所以这个函数确实修改了对象,它看起来确实很像它是通过引用传递的。


修改对象的问题

这是一个更具体的示例,它是实际生产代码库中错误的来源。

假设我们想向用户发送一封包含下个月收据的电子邮件,并且我们希望标题以用户友好的方式显示期间的开始和结束,例如“从 7 月 1 日星期四到 7 月星期六31 ",以及电子邮件页脚中的当前日期。

我们将创建一些函数来获取与当前日期对应的月份的开始和结束,并对其进行格式化,然后在字符串(或更实际的模板)中使用结果。

它看起来像这样:

function getFirstDay($date) {
    return $date->modify('first day of this month')->format('l F j');
}

function getLastDay($date) {
    return $date->modify('last day of this month')->format('l F j');
}

$today = new DateTime('2022-07-15');
$firstday = getFirstDay($today);
$lastDay = getLastDay($today);

$emailTitle = "Your receipt for the period from $firstday to $lastDay";
$emailFooter = "Sent on " .  $today->format("l F j");

标题会说

“ Your receive for the period from Thursday July 1 to Saturday July 31 ”

这是我们想要的,但页脚说

“Sent on Saturday July 31 ”

这绝对不是我们想要的。

问题是当我们在日期调用 ->modify() 时 $today 实际上被函数修改了,

所以一旦我们在日期使用它,我们就会永远失去日期的原始值......

有两种解决方法:

1.在处理之前克隆对象:

function getFirstDay($today) {
    $date = clone $today;
    return $date->modify('first day of this month')
        ->format('l F j');
}

2.使用像 Carbon 2 这样提供不可变对象的库,它们本质上是对象,其中每个方法总是返回一个克隆而不是修改当前对象。


为什么对象不是真正通过引用传递的?

所以我们已经看到它看起来很像对象是通过引用传递的,以及如何避免潜在的问题,

但这篇文章的重点是表明它们不是,所以这里有几个例子可以很明显.


示例 1:数组中的对象

让我们假设一个 User 类,它在构造函数中获取用户的名称并将其分配给 name 属性。

现在让我们看看如果我们将其中几个用户放在一个数组中并将其传递给一个函数,该函数将每个元素的名称设为大写,会发生什么情况:

Class User
{
    public function __construct(public string $name){}
}

function capitalizeNames(array $users) {
    foreach ($users as $user) {
        $user->name = strtoupper($user->name);
    }
}

$users = [
    new User('John'),
    new User('Jack'),
];

capitalizeNames($users);
echo json_encode($users); // [{"name":"JOHN"},{"name":"JACK"}]

如您所见,数组中的每个用户都由 capitalizeNames() 函数修改。

我们没有使用引用,我们从未将对象传递给函数,因为我们传递了一个数组(按值),所以我们可能认为我们是安全的。然而,数组中的每个对象都被修改了。


示例 2:将一个对象分配给另一个变量

现在让我们看看如果我们将一个对象的实例分配给一个新变量会发生什么:

$user1 = new User('name');
$user2 = $user1;
$user1->name = 'Jack';
echo $user1->name; // Jack
echo $user2->name; // Jack

所以基本上只要我们这样做$user2 = $user1,我们就可以互换使用,改变一个会改变另一个。


所以发生了什么事?

问题是变量永远不会“包含”对象。每当我们调用new时,PHP都会返回一个句柄,它是一个参考... 一个指针...是的,让我们坚持使用手柄。该句柄基本上是一个标识对象的数字。

使用时实际上可以看到句柄var_dump(),它是转储对象时前面带有#的数字,var_dump($user1, $user2)从我们之前的示例中,我们可以看到它们都是同一个对象,因为它们具有相同的句柄:

object(App\Console\Commends\User)#538 (1) {
   ["name"]=>
   string(4) "Jack"
}

object(App\Console\Commends\User)#538 (1) {
   ["name"]=>
   string(4) "Jack"
}

每当我们复制此变量、将其传递给函数或将其放入数组时,我们都不会传递或复制对象的实例,而只是传递或复制它的句柄。

所以一个对象只会存在一次,除非我们使用clone. 有点像现实生活中的物体。


结论

虽然认为对象通过引用传递可能很有用,但我认为最好记住,

当您创建一个对象时,只有一个实例存在,您使用的不是对象,而只是一个句柄。

我希望这对某人有用。

此外,我没有任何 C 语言背景,对PHP核心中的实际实现方式只有模糊的理解。

因此,如果上面有明显的错误,请在评论中告诉我。


  • 上一条:
    一位外国程序员在上班时间做“副业”被抓,然后惨遭解雇之程序员兼职翻车事故分享
    下一条:
    使用Laravel-dynamic-servers包(基于PHP的Kubernetes版)动态创建和销毁服务器
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • 用Time Warden监控PHP中的代码处理时间(0个评论)
    • 在PHP中使用array_pop + yield实现读取超大型目录功能示例(0个评论)
    • Property Hooks RFC在PHP 8.4中越来越接近现实(0个评论)
    • 近期文章
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 在go语言中实现IP/CIDR的ip和netmask互转及IP段形式互转及ip是否存在IP/CIDR(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2017-06
    • 2017-07
    • 2017-08
    • 2017-09
    • 2017-11
    • 2017-12
    • 2018-01
    • 2018-02
    • 2018-03
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2020-07
    • 2020-09
    • 2021-02
    • 2021-03
    • 2021-04
    • 2021-05
    • 2021-06
    • 2021-07
    • 2021-08
    • 2021-09
    • 2021-10
    • 2021-11
    • 2021-12
    • 2022-01
    • 2022-02
    • 2022-05
    • 2022-06
    • 2022-07
    • 2022-08
    • 2022-09
    • 2022-10
    • 2022-11
    • 2022-12
    • 2023-01
    • 2023-02
    • 2023-03
    • 2023-04
    • 2023-05
    • 2023-06
    • 2023-07
    • 2023-08
    • 2023-09
    • 2023-10
    • 2023-11
    • 2023-12
    • 2024-01
    • 2024-02
    • 2024-03
    • 2024-04
    • 2024-05
    • 2024-06
    • 2024-07
    • 2024-09
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客