工作小锦囊系列——变量惹的祸

数据库连不上了

因为要迁移数据库的缘故,我需要在服务器上,提前测试下数据库的连通性,所以用原生 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 词法解析器,后面的$应该被识别为普通字符串,而不是变量符号。

归于平静

这仅仅是一段测试代码中出现的「小插曲」。不过,通过这个「小插曲」,还是有一些小收获的:

  • 定义字符串时尽量使用单引号进行定义;
  • 除了避免$后面的内容可能被解析为变量外,单引号比双引号少了字符串内部词法分析的过程,效率更高;
  • 该看看基础了,猥琐发育,别浪。

【前方高能,紧急插播一条广告】

↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

我的公众号 [快乐的皮拉夫] 已经开通有一段时间了,感兴趣的朋友可以去捧个场,加个关注,爱你们哟~

公众号内容主要是我目前在写的一本书 ——《写给班吉的书》,欢迎各位看官老爷留言评论。
没错,程序员打算转行当个作家了。

1efPc7PCEW.jpg!large

本作品采用《CC 协议》,转载必须注明作者和本文链接
你应该了解真相,真相会让你自由。
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 13

优先使用单引号啦,既可以避免变量的解析效率又高

3个月前 评论
快乐的皮拉夫 (楼主) 3个月前
leo

phpstan + phpcs,这种问题在 CI 阶段就发现了

3个月前 评论
快乐的皮拉夫 (楼主) 3个月前

PhpStorm 和 WebStorm,四个 git 目录,用了 5G 多的内存,卡死了,16G 不够用了。

3个月前 评论
快乐的皮拉夫 (楼主) 3个月前

一开始看吓了一条, 然后反应过来只有代码才有词法分析。 只要写代码时候注意点就安啦。

3个月前 评论
快乐的皮拉夫 (楼主) 3个月前

还有很多特殊字符需要urlencode

3个月前 评论

如无必要 一般使用单引号

3个月前 评论
快乐的皮拉夫 (楼主) 3个月前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
文章
39
粉丝
112
喜欢
674
收藏
735
排名:268
访问:3.6 万
私信
所有博文
社区赞助商