”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 双引号是否过多,这就是问题所在!

双引号是否过多,这就是问题所在!

发布于2024-08-27
浏览:180

最近我又听说 PHP 人们仍然在谈论单引号与双引号,并且使用单引号只是一种微观优化,但如果你习惯一直使用单引号,你会节省大量的 CPU循环!

“一切都已经说过了,但还不是每个人都说的” – Karl Valentin

正是本着这种精神,我正在写一篇关于 Nikita Popov 12 年前已经做过的同一主题的文章(如果您正在阅读他的文章,您可以在这里停止阅读)。

毛茸茸的到底是什么?

PHP 执行字符串插值,在字符串中搜索变量的使用情况,并将其替换为所使用变量的值:

$juice = "apple";
echo "They drank some $juice juice.";
// will output: They drank some apple juice.

此功能仅限于双引号和定界符中的字符串。使用单引号(或 nowdoc)将产生不同的结果:

$juice = "apple";
echo 'They drank some $juice juice.';
// will output: They drank some $juice juice.

请注意:PHP 不会搜索该单引号字符串中的变量。所以我们可以开始在任何地方使用单引号。所以人们开始建议这样的改变..

- $juice = "apple";
  $juice = 'apple';

.. 因为它会更快,并且每次执行该代码都会节省大量 CPU 周期,因为 PHP 不会在单引号字符串中查找变量(无论如何,该示例中不存在这些变量)并且皆大欢喜,案件结案。

案件结案了吗?

显然,使用单引号和双引号是有区别的,但为了理解发生了什么,我们需要更深入地挖掘。

尽管 PHP 是一种解释性语言,但它使用编译步骤,其中某些部分一起运行以获得虚拟机实际可以执行的内容,即操作码。那么我们如何从 PHP 源代码获取操作码呢?

词法分析器

词法分析器扫描源代码文件并将其分解为标记。可以在 token_get_all() 函数文档中找到该含义的简单示例。一个 PHP 源代码只是

T_OPEN_TAG (



我们可以在这个 3v4l.org 代码片段中看到它的实际效果并使用它。

解析器

解析器获取这些标记并从中生成抽象语法树。当表示为 JSON 时,上述示例的 AST 表示如下所示:

{
  "data": [
    {
      "nodeType": "Stmt_Echo",
      "attributes": {
        "startLine": 1,
        "startTokenPos": 1,
        "startFilePos": 6,
        "endLine": 1,
        "endTokenPos": 4,
        "endFilePos": 13
      },
      "exprs": [
        {
          "nodeType": "Scalar_String",
          "attributes": {
            "startLine": 1,
            "startTokenPos": 3,
            "startFilePos": 11,
            "endLine": 1,
            "endTokenPos": 3,
            "endFilePos": 12,
            "kind": 2,
            "rawValue": "\"\""
          },
          "value": ""
        }
      ]
    }
  ]
}

如果你也想玩这个,看看其他代码的 AST 是什么样子,我找到了 Ryan Chandler 的 https://phpast.com/ 和 https://php-ast-viewer.com/ ,其中两者都显示给定 PHP 代码片段的 AST。

编译器

编译器采用 AST 并创建操作码。操作码是虚拟机执行的内容,如果您进行了设置并启用了它,它也会存储在 OPcache 中(我强烈推荐)。

要查看操作码,我们有多个选项(也许更多,但我确实知道这三个):

  1. 使用 vulcan 逻辑转储器扩展。它也被纳入 3v4l.org
  2. 使用 phpdbg -p script.php 转储操作码
  3. 或者使用 OPcache 的 opcache.opt_debug_level INI 设置使其打印出操作码
    • 优化前0x10000的值输出操作码
    • 0x20000的值输出优化后的操作码
$ echo ' foo.php
$ php -dopcache.opt_debug_level=0x10000 foo.php
$_main:
...
0000 ECHO string("")
0001 RETURN int(1)

假设

回到使用单引号与双引号时节省 CPU 周期的最初想法,我想我们都同意,只有当 PHP 在运行时为每个请求评估这些字符串时,这才是正确的。

运行时会发生什么?

那么让我们看看 PHP 为两个不同版本创建了哪些操作码。

双引号:







0000 ECHO string("apple")
0001 RETURN int(1)

对比。单引号:







0000 ECHO string("apple")
0001 RETURN int(1)

嘿等等,奇怪的事情发生了。这看起来一模一样!我的微优化去哪儿了?

好吧,也许 ECHO 操作码处理程序的实现会解析给定的字符串,尽管没有标记或其他东西告诉它这样做......嗯?

让我们尝试不同的方法,看看词法分析器对这两种情况做了什么:

双引号:

T_OPEN_TAG (



对比。单引号:

Line 1: T_OPEN_TAG (



标记仍然区分双引号和单引号,但是检查 AST 将为我们提供两种情况相同的结果 - 唯一的区别是 Scalar_String 节点属性中的 rawValue,它仍然具有单/双引号,但是该值在两种情况下都使用双引号。

新假设

难道字符串插值实际上是在编译时完成的吗?

让我们看一个稍微“复杂”的例子:





此文件的标记是:

T_OPEN_TAG (



看看最后两个标记!字符串插值在词法分析器中处理,因此是编译时的事情,与运行时无关。

Too double quote or not, that

为了完整起见,让我们看一下由此生成的操作码(优化后,使用0x20000):

0000 ASSIGN CV0($juice) string("apple")
0001 T2 = FAST_CONCAT string("juice: ") CV0($juice)
0002 ECHO T2
0003 RETURN int(1)

这与我们简单的

进入正题:我应该连接还是插值?

让我们看看这三个不同的版本:





  • 第一个版本使用字符串插值
  • 第二个是使用逗号分隔(据我所知,它仅适用于 echo,不适用于分配变量或其他任何内容)
  • 第三个选项使用字符串连接

第一个操作码将字符串“apple”分配给变量 $juice:

0000 ASSIGN CV0($juice) string("apple")

第一个版本(字符串插值)使用绳索作为底层数据结构,经过优化以尽可能少地复制字符串。

0001 T2 = ROPE_INIT 4 string("juice: ")
0002 T2 = ROPE_ADD 1 T2 CV0($juice)
0003 T2 = ROPE_ADD 2 T2 string(" ")
0004 T1 = ROPE_END 3 T2 CV0($juice)
0005 ECHO T1

第二个版本是最有效的内存,因为它不创建中间字符串表示形式。相反,它会多次调用 ECHO,从 I/O 角度来看,这是一个阻塞调用,因此根据您的用例,这可能是一个缺点。

0006 ECHO string("juice: ")
0007 ECHO CV0($juice)
0008 ECHO string(" ")
0009 ECHO CV0($juice)

第三个版本使用 CONCAT/FAST_CONCAT 创建中间字符串表示形式,因此可能比绳索版本使用更多的内存。

0010 T1 = CONCAT string("juice: ") CV0($juice)
0011 T2 = FAST_CONCAT T1 string(" ")
0012 T1 = CONCAT T2 CV0($juice)
0013 ECHO T1

那么...这里应该做什么以及为什么是字符串插值?

字符串插值在 echo "juice: $juice" 的情况下使用 FAST_CONCAT;或在 echo "juice: $juice $juice"; 的情况下高度优化的 ROPE_* 操作码;但最重要的是它清楚地传达了意图,并且这些都不是我迄今为止使用过的任何 PHP 应用程序的瓶颈,所以这些实际上都不重要。

总长DR

字符串插值是编译时的事情。诚然,如果没有 OPcache,词法分析器将必须在每个请求上检查双引号字符串中使用的变量,即使没有任何变量,也会浪费 CPU 周期,但说实话:问题不在于双引号字符串,而在于不使用 OPcache!

但是,有一个警告:PHP 最高 4(我相信甚至包括 5.0,甚至可能是 5.1,我不知道)在运行时进行了字符串插值,所以使用这些版本......嗯,我想如果确实有人仍在使用 PHP 5,与上面的情况相同:问题不是双引号字符串,而是使用过时的 PHP 版本。

最终建议

更新PHP最新版本,启用OPcache,从此幸福生活!

版本声明 本文转载于:https://dev.to/realflowcontrol/too-double-quote-or-not-thats-the-question-78l?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 如何从PHP中的数组中提取随机元素?
    如何从PHP中的数组中提取随机元素?
    从阵列中的随机选择,可以轻松从数组中获取随机项目。考虑以下数组:; 从此数组中检索一个随机项目,利用array_rand( array_rand()函数从数组返回一个随机键。通过将$项目数组索引使用此键,我们可以从数组中访问一个随机元素。这种方法为选择随机项目提供了一种直接且可靠的方法。
    编程 发布于2025-04-15
  • 为什么不使用CSS`content'属性显示图像?
    为什么不使用CSS`content'属性显示图像?
    在Firefox extemers属性为某些图像很大,&& && && &&华倍华倍[华氏华倍华氏度]很少见,却是某些浏览属性很少,尤其是特定于Firefox的某些浏览器未能在使用内容属性引用时未能显示图像的情况。这可以在提供的CSS类中看到:。googlepic { 内容:url(&#...
    编程 发布于2025-04-15
  • 在C#中如何高效重复字符串字符用于缩进?
    在C#中如何高效重复字符串字符用于缩进?
    在基于项目的深度下固定字符串时,重复一个字符串以进行凹痕,很方便有效地有一种有效的方法来返回字符串重复指定的次数的字符串。使用指定的次数。 constructor 这将返回字符串“ -----”。 字符串凹痕= new String(' - ',depth); console.Wr...
    编程 发布于2025-04-15
  • 人脸检测失败原因及解决方案:Error -215
    人脸检测失败原因及解决方案:Error -215
    错误处理:解决“ error:( - 215)!empty()in Function openCv in Function MultSiscale中的“检测”中的错误:在功能检测中。”当Face Cascade分类器(即面部检测至关重要的组件)未正确加载时,通常会出现此错误。要解决此问题,必须...
    编程 发布于2025-04-15
  • 如何在无序集合中为元组实现通用哈希功能?
    如何在无序集合中为元组实现通用哈希功能?
    在未订购的集合中的元素要纠正此问题,一种方法是手动为特定元组类型定义哈希函数,例如: template template template 。 struct std :: hash { size_t operator()(std :: tuple const&tuple)const {...
    编程 发布于2025-04-15
  • 如何在鼠标单击时编程选择DIV中的所有文本?
    如何在鼠标单击时编程选择DIV中的所有文本?
    在鼠标上选择div文本单击带有文本内容,用户如何使用单个鼠标单击单击div中的整个文本?这允许用户轻松拖放所选的文本或直接复制它。 在单个鼠标上单击的div元素中选择文本,您可以使用以下Javascript函数: function selecttext(canduterid){ if(do...
    编程 发布于2025-04-15
  • 如何使用替换指令在GO MOD中解析模块路径差异?
    如何使用替换指令在GO MOD中解析模块路径差异?
    在使用GO MOD时,在GO MOD 中克服模块路径差异时,可能会遇到冲突,其中3个Party Package将另一个PAXPANCE带有导入式套件之间的另一个软件包,并在导入式套件之间导入另一个软件包。如回声消息所证明的那样: go.etcd.io/bbolt [&&&&&&&&&&&&&&&&...
    编程 发布于2025-04-15
  • 您可以使用CSS在Chrome和Firefox中染色控制台输出吗?
    您可以使用CSS在Chrome和Firefox中染色控制台输出吗?
    在javascript console 中显示颜色是可以使用chrome的控制台显示彩色文本,例如红色的redors,for for for for错误消息?回答是的,可以使用CSS将颜色添加到Chrome和Firefox中的控制台显示的消息(版本31或更高版本)中。要实现这一目标,请使用以下模...
    编程 发布于2025-04-15
  • 为什么使用固定定位时,为什么具有100%网格板柱的网格超越身体?
    为什么使用固定定位时,为什么具有100%网格板柱的网格超越身体?
    网格超过身体,用100%grid-template-columns 为什么在grid-template-colms中具有100%的显示器,当位置设置为设置的位置时,grid-template-colly修复了?问题: 考虑以下CSS和html: class =“ snippet-code”> g...
    编程 发布于2025-04-15
  • 解决MySQL插入Emoji时出现的\\"字符串值错误\\"异常
    解决MySQL插入Emoji时出现的\\"字符串值错误\\"异常
    Resolving Incorrect String Value Exception When Inserting EmojiWhen attempting to insert a string containing emoji characters into a MySQL database us...
    编程 发布于2025-04-15
  • 为什么我会收到MySQL错误#1089:错误的前缀密钥?
    为什么我会收到MySQL错误#1089:错误的前缀密钥?
    mySQL错误#1089:错误的前缀键错误descript [#1089-不正确的前缀键在尝试在表中创建一个prefix键时会出现。前缀键旨在索引字符串列的特定前缀长度长度,可以更快地搜索这些前缀。了解prefix keys `这将在整个Movie_ID列上创建标准主键。主密钥对于唯一识别...
    编程 发布于2025-04-15
  • 如何从PHP中的Unicode字符串中有效地产生对URL友好的sl。
    如何从PHP中的Unicode字符串中有效地产生对URL友好的sl。
    为有效的slug生成首先,该函数用指定的分隔符替换所有非字母或数字字符。此步骤可确保slug遵守URL惯例。随后,它采用ICONV函数将文本简化为us-ascii兼容格式,从而允许更广泛的字符集合兼容性。接下来,该函数使用正则表达式删除了不需要的字符,例如特殊字符和空格。此步骤可确保slug仅包含...
    编程 发布于2025-04-15
  • 对象拟合:IE和Edge中的封面失败,如何修复?
    对象拟合:IE和Edge中的封面失败,如何修复?
    To resolve this issue, we employ a clever CSS solution that solves the problem:position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%)...
    编程 发布于2025-04-15
  • PHP阵列键值异常:了解07和08的好奇情况
    PHP阵列键值异常:了解07和08的好奇情况
    PHP数组键值问题,使用07&08 在给定数月的数组中,键值07和08呈现令人困惑的行为时,就会出现一个不寻常的问题。运行print_r($月)返回意外结果:键“ 07”丢失,而键“ 08”分配给了9月的值。此问题源于PHP对领先零的解释。当一个数字带有0(例如07或08)的前缀时,PHP将其...
    编程 发布于2025-04-15
  • 如何限制动态大小的父元素中元素的滚动范围?
    如何限制动态大小的父元素中元素的滚动范围?
    在交互式接口中实现垂直滚动元素的CSS高度限制问题:考虑一个布局,其中我们具有与用户垂直滚动一起移动的可滚动地图div,同时与固定的固定sidebar保持一致。但是,地图的滚动无限期扩展,超过了视口的高度,阻止用户访问页面页脚。$("#map").css({ marginT...
    编程 发布于2025-04-15

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3