工作小锦囊系列——变量惹的祸
数据库连不上了
因为要迁移数据库的缘故,我需要在服务器上,提前测试下数据库的连通性,所以用原生 PHP 写了一段简单的测试代码,如下:
说明:服务器上没有装 mysql 客户端,所以直接用 PHP 代码进行测试,不然也就不会有接下来发生的事情了。
<?php
// 数据库信息
$servername = "×××××××××";
$username = "manage";
$password = "^qPzPdP$ABC@L6wVh$3";
$database = "test";
// 创建连接
$conn = new mysqli($servername, $username, $password, $database);
// 检查连接
if ($conn->connect_error) {
die("连接失败: " . $conn->connect_error);
}
echo "连接成功";
$conn->close();
代码比较简单,但当我运行代码时,居然报错了!!!
报错信息如下:
PHP Warning: mysqli::__construct(): (HY000/1045): Access denied for user 'manage'@×××.×××.×××.×××'' (using password: YES)
看错误提示,是数据库访问受限了。
碰到数据库连接异常问题,首先想到的是数据库地址的连通性问题。这里分为两种情况:
一种是数据库地址不存在,即无法进行 DNS 解析。这种情况,如果使用 ping 命令进行测试的话,会提示Name or service not known
错误,在 PHP 代码中会提示php_network_getaddresses: getaddrinfo failed: Name or service not known
类似的错误。
另一种是数据库地址存在,但是使用 telnet 检测端口的连通性,会出现超时的情况。这种情况一般是因为服务端没有开启白名单导致。
不过根据上面出现的报错提示,基本能够排除上面所说的两种情况。这种情况已经过了地址检测阶段,一般是连接数据库时出了问题,比如账号或密码错误,或者没有相应的数据库访问权限等。
但账号密码在桌面工具上已经测试过,都是可以正常访问的,数据库权限也是正常的。
那问题出在哪了呢?还真是见鬼了。
意外破案
就在我一筹莫展时,无意间发现,居然忽略了另一行告警信息:
PHP Notice: Undefined variable: ABC in test.php on line 4
可能是注意力全集中在数据库连接的那个报错上了,居然把这一小行告警信息给忽略了。
根据告警提示的位置,我发现是下面这行代码出了问题:
$password = "^qPzPdP$ABC@L6wVh$3";
在这个密码字符串的定义中,我一眼便发现了ABC
,就在它们前面,还有一个$
。
我大概猜到原因了,这里应该是词法分析的时候,将$ABC
当成一个变量的词法单元了。但$ABC
又没有提前定义,所以就会出现变量未定义
报错了。
验证方式很简单,我将双引号换成单引号以后,再进行测试,程序就没有问题了,和我想的一样。
可能有的小伙伴会想到
addslashes
这个函数,能否在这里使用这个函数呢?答案是不可以的。一来它可能会改变字符串的内容,二来词法解析是发生在函数调用之前,所以使用addslashes
函数进行转义时,依然会提示变量未定义
的错误。
诡异的字符串
虽然问题解决了,但还有一个问题我没想明白。
在上面那个密码字符串中,除了$ABC
以外,还有一个$3
,为何$3
没有被识别成变量呢?
呃,或许我应该这样问:PHP 变量名的定义规则是怎样的呢?
写了十几年 PHP 了,我居然问出了一个 PHP 入门第一课的问题,真是惭愧,还好我没有在面试。
我大概还有些印象,变量名称由字母、数字和下划线组成。但为了不犯经验错误,我还是决心查一下手册:
PHP 中的变量用一个美元符号后面跟变量名来表示。变量名是区分大小写的。
变量名与 PHP 中其它的标签一样遵循相同的规则。一个有效的变量名由字母或者下划线开头,后面跟上任意数量的字母,数字,或者下划线。 按照正常的正则表达式,它将被表述为:'^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$'。
这是 [PHP 手册] 中关于变量名称的定义。
看完定义以后,我发现我忽略了很重要的一点:变量名由字母或者下划线开头……
好吧,反应过来了。
我又做了一个测试:
$a = "$A$_$a$1$$\$B";
echo $a;
结果和我预期的一样:
PHP Notice: Undefined variable: A
PHP Notice: Undefined variable: _
PHP Notice: Undefined variable: a
$1$$$B
$A
,$_
和$a
都被识别成了变量,而$1
和$$
都不符合变量名的规则,所以被识别为普通的字符串。
细心的小伙伴会发现,我最后还特意加了一个$B
,为何$B
符合变量名的规则,却也被识别为字符串了呢?没错,$
前面还有个反斜杠\
转义符呢,它将告诉 PHP 词法解析器,后面的$
应该被识别为普通字符串,而不是变量符号。
归于平静
这仅仅是一段测试代码中出现的「小插曲」。不过,通过这个「小插曲」,还是有一些小收获的:
- 定义字符串时尽量使用单引号进行定义;
- 除了避免
$
后面的内容可能被解析为变量外,单引号比双引号少了字符串内部词法分析的过程,效率更高; - 该看看基础了,猥琐发育,别浪。
【前方高能,紧急插播一条广告】
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
我的公众号 [快乐的皮拉夫] 已经开通有一段时间了,感兴趣的朋友可以去捧个场,加个关注,爱你们哟~
公众号内容主要是我目前在写的一本书 ——《写给班吉的书》,欢迎各位看官老爷留言评论。
没错,程序员打算转行当个作家了。
本作品采用《CC 协议》,转载必须注明作者和本文链接
优先使用单引号啦,既可以避免变量的解析效率又高
:see_no_evil:
phpstan + phpcs,这种问题在 CI 阶段就发现了
PhpStorm 和 WebStorm,四个 git 目录,用了 5G 多的内存,卡死了,16G 不够用了。
一开始看吓了一条, 然后反应过来只有代码才有词法分析。 只要写代码时候注意点就安啦。
还有很多特殊字符需要urlencode
:+1:
如无必要 一般使用单引号