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

数据库连不上了

因为要迁移数据库的缘故,我需要在服务器上,提前测试下数据库的连通性,所以用原生 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 协议》,转载必须注明作者和本文链接
你应该了解真相,真相会让你自由。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 13

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

4周前 评论
快乐的皮拉夫 (楼主) 4周前
leo

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

4周前 评论
快乐的皮拉夫 (楼主) 4周前

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

4周前 评论
快乐的皮拉夫 (楼主) 4周前

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

4周前 评论
快乐的皮拉夫 (楼主) 4周前

还有很多特殊字符需要urlencode

3周前 评论

如无必要 一般使用单引号

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

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!