分享一些 PHP 中有用的知识和坑

Posted 唯一丶

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分享一些 PHP 中有用的知识和坑相关的知识,希望对你有一定的参考价值。

前言

在一次偶然查看 php 文档的时候,发现了一些有趣的内容,随着阅读的增加,越发觉得有趣的内容或者说时坑越来越多,所以我决定记录下来,分享出去,下文中一些内容摘录自一些优秀的博客、PHP 文档的用户笔记,或者文档原文。

尤其是文档原文,我发现很多人不会去读,很多东西也不会去注意(是的,我也是这样,所以借着这次机会,一起来学习一下。)

我忘了PHP函数的参数顺序,它们是随机的吗?

PHP is a glue that brings together hundreds of external libraries, so sometimes this gets messy. However, a simple rule of thumb is as follows:

Array functionparameters are ordered as " needle, haystack" whereas String functionsare the opposite, so " haystack, needle".

译:数组相关方法的参数顺序为,「needle, haystack」,字符串相关方法则是相反的 「haystack, needle」,

来源: https://www.php.net/manual/zh/faq.using.php#faq.using.parameterorder

我应该如何保存“盐”?

当使用 password_hash() 或者 crypt() 函数时, “盐”会被作为生成的散列值的一部分返回。 你可以直接把完整的返回值存储到数据库中, 因为这个返回值中已经包含了足够的信息, 可以直接用在 password_verify() 或 crypt() 函数来进行密码验证。

下图展示了 crypt() 或 password_hash() 函数返回值的结构。 如你所见,算法的信息以及“盐”都已经包含在返回值中, 在后续的密码验证中将会用到这些信息。

来源: https://www.php.net/manual/zh/faq.passwords.php#faq.password.storing-salts

下面代码怎么没有分成两行显示?

<pre>
<?php echo "This should be the first line."; ?>
<?php echo "This should show up after the new line above."; ?>
</pre>

在 PHP 中,一段代码的结束标记要么是“?>”要么是“?>\\n”(\\n 表示换行)。因此在上面的例子中,输出的句子将显示在同一行中,因为 PHP 忽略了代码结束标记后面的换行。这意味着如果要输出一个换行符,需要在每段 PHP 代码的结束标记后面多加一个换行。

PHP 为什么这么做呢?因为在格式化正常的 html 时,这样通常会更容易。假如输出了换行而你不需要这个换行时,就不得不用一个非常长的行来达到这样的效果,或者让产生的 HTML 页面的源文件的格式很难读。

来源: https://www.php.net/manual/zh/faq.using.php#faq.using.newlines

字符串连接操作符的优先级问题

如果你运行下面的代码,他将会输出一个警告和结果 3 ,因为字符串连接操作符 . 和 数学运算符 +- 的优先级时一样的,它们将从左往右执行。 Result: 会被强转成数组 0 。如果你在低版本的 PHP 中运行,会告诉你 中边不是一个数字,如果你在 7.4 中运行,会告诉你,在 PHP 8 中 + 、 - 的优先级将会被提高。如果你使用了 PHPSTORM 中的 EA 插件,将会提醒你这个问题。

<php
$var = 3;

echo "Result: " . $var + 3;

如果你不希望这样,那么最好使用括号把它包裹起来,就像下面那样。

<?php
$var = 3;

echo "Result: " . ($var + 3);

来源: https://www.php.net/manual/zh/language.operators.string.php#41950

字符串连接操作符与数字

运行下面代码,尤其是第三行,请注意,如果 . 左右存在空格,那么即使是一个数字,也将会作用成字符串连接。

<?php

echo "thr"."ee";           //prints the string "three"
echo "twe" . "lve";        //prints the string "twelve"
echo 1 . 2;                //prints the string "12"
echo 1.2;                  //prints the number 1.2
echo 1+2;                  //prints the number 3

来源: https://www.php.net/manual/zh/language.operators.string.php#41950

使用 http_build_query

NULL 的值将会被会略

<?php
$arr = array(\'test\' => null, \'test2\' => 1);

// test2=1
echo http_build_query($arr); 

来源: https://www.php.net/manual/zh/function.http-build-query.php#60523

True 和 False 将会被转换成数字

<?php
$a = [teste1= true,teste2=false];
// teste1=1&teste2=0
echo http_build_query($a)

来源: https://www.php.net/manual/zh/function.http-build-query.php#122232

空的数组不会出现在结果中

<?php

$post_data = array(\'name\'=>\'miller\', \'address\'=>array(\'address_lines\'=>array()), \'age\'=>23);
// name=miller&age=23
echo http_build_query($post_data);

来源: https://www.php.net/manual/zh/function.http-build-query.php#109466

简述 OpCache 的原理

 PHP执行这段代码会经过如下4个步骤(确切的来说,应该是PHP的语言引擎Zend)

  •    1. Scanning(Lexing) ,将PHP代码转换为语言片段(Tokens)
  •    2. Parsing, 将Tokens转换成简单而有意义的表达式
  •    3. Compilation, 将表达式编译成Opocdes
  •    4. Execution, 顺次执行Opcodes,每次一条,从而实现PHP脚本的功能。

现在有的Cache比如APC,可以使得PHP缓存住Opcodes,这样,每次有请求来临的时候,就不需要重复执行前面3步,从而能大幅的提高PHP的执行速度。

来源: https://www.laruence.com/2008/06/18/221.html

var_dump(1...9)输出什么?

<?php

// 10.9
var_dump(1...9);

输出10.9, 乍一看这个var_dump的输出很奇怪是不是? 为什么呢?

这里教大家,如果看到一段PHP代码感觉输出很奇怪,第一反应是看下这段代码生成的opcodes是啥,虽然这个问题其实是词法分析阶段的问题,不过还是用phpdbg分析下吧(一般为了防止opcache的影响,会传递-n):

phpdbg -n -p /tmp/1.php
function name: (null)
L1-35 {main}() /tmp/1.php - 0x7f56d1a63460 + 4 ops
L2 #0 INIT_FCALL<1> 96 "var_dump"
L2 #1 SEND_VAL "10.9" 1
L2 #2 DO_ICALL
L35 #3 RETURN<-1> 1

所以这么看来,早在生成opcode之前,1...9就变成了常量10.9,考虑到这是字面量,我们现在去看看zend_language_scanner.l, 找到这么一行:

DNUM ({LNUM}?"."{LNUM})|({LNUM}"."{LNUM}?)

这个是词法分析定义的浮点数的格式,到这里也就恍然大悟了:
1...9 会被依次接受为: 1. (浮点数1), 然后是 . (字符串连接符号) 然后是.9(浮点数0.9)

所以在编译阶段就会直接生成 “1” . “0.9” -> 字符串的字面量”10.9”

来源: https://www.laruence.com/2020/02/23/1990.html

HTTPOXY 漏洞

这里有一个核心的背景是, 长久一来我们习惯了使用一个名为"http_proxy"的环境变量来设置我们的请求代理。

http_proxy=127.0.0.1:9999 wget http://www.laruence.com/

如何形成?

在CGI(RFC 3875)的模式的时候, 会把请求中的Header, 加上HTTP_ 前缀, 注册为环境变量, 所以如果你在Header中发送一个Proxy:xxxxxx, 那么 PHP 就会把他注册为HTTP_PROXY环境变量, 于是getenv("HTTP_PROXY")就变成可被控制的了. 那么如果你的所有类似的请求, 都会被代理到攻击者想要的地址,之后攻击者就可以伪造,监听,篡改你的请求了

如何影响?

 所以, 这个漏洞要影响你, 有几个核心前提是:

  • 你的服务会对外请求资源
  • 你的服务使用了HTTP_PROXY(大写的)环境变量来代理你的请求(可能是你自己写,或是使用一些有缺陷的类库)
  • 你的服务跑在PHP的CGI模式下(cgi, php-fpm)

如何处理?

nginx为例, 在配置中加入:

fastcgi_param HTTP_PROXY "";

所以建议, 即使你不受此次漏洞影响, 也应该加入这个配置.
而如果你是一个类库的作者,或者你因为什么原因没有办法修改服务配置, 那么你就需要在代码中加入对sapi的判断, 除非是cli模式, 否则永远不要相信http_proxy环境变量,

<?php
if (php_sapi_name() == \'cli\' && getenv(\'HTTP_PROXY\')) {
  //只有CLI模式下, HTTP_PROXY环境变量才是可控的
}

补充: 从PHP5.5.38开始, getenv增加了第二个参数, local_only = false, 如果这个参数为true, 则只会从系统本地的环境变量表中获取, 从而修复这个问题, 并且默认的PHP将拦截HTTP_PROXY:fix

HTTPOXY漏洞说明 - 风雪之隅
https://www.laruence.com/2016/07/19/3101.html

运算符优先级

&& 和 and 在赋值运算中的问题

运行下面的代码,第一个 $bool 将打印为 false ,预期如此,但是第二个 $bool 将打印 true 。这是因为 = 的优先级高于 and 运算符,所以,第二个 $bool 将会被当成 ($bool = true) and false  执行。

<?php

$bool = true && false;
// false
var_dump($bool);


$bool = true and false;
// true
var_dump($bool);

来源: https://www.php.net/manual/zh/language.operators.precedence.php#117390

instanceof 运算符

你是否曾经写过下面这样的代码?

<?php

class A {

}

$A = new A();

// 1
var_dump(! $A instanceof A); // false

// 2
var_dump((! $A instanceof A)); // false

// 3
var_dump(!( $A instanceof A)); // false

// 其实不用担心,因为 instanceof 的优先级要高于 ! ,你可以放心的使用,
// 不必添加括号,让他们看起来是一个表达式,但是在复杂的情况下例外。
var_dump(! $A instanceof A);

在你需要对 instanceof 运算的结果做取反运算时,因为取反运算符 ! 的优先级低于 instanceof 所以,你不必再它们外面再加上一个圆括号来表明这是一组表达式,但是再复杂情况下例外。

感谢评论区 @Mr_houzi 提出的 第 3 个「反例」

array_map 的有趣用法

通常,我会使用 array_map 来处理一个数组,让他返回一个新的数组,当然,它的用处就是这样的,但是除了这种基础的用法,它其实还有一些有趣的用法,并且,这些用法都存在于 PHP 的手册中。

多个 array 用法

通常你会这样使用它。

<?php

$arr1 = [\'apple\', \'orange\', \'mango\', \'pear\'];
$newArr1 = array_map(\'strtoupper\',$arr1);

这只是一个简单的

以上是关于分享一些 PHP 中有用的知识和坑的主要内容,如果未能解决你的问题,请参考以下文章

10个超级有用必须收藏的PHP代码样例

ionic中遇到的一些问题和坑

比较有用的php代码片段

php 有趣的代码片段在某些时候可能会有用。

程序杰杰正式加入博客,与大家一起努力进步,会分享记录一些杰杰觉得有用的代码知识

分享十二个有用的jQuery代码