错误1:循环后在循环中留下悬空指针。 如果我们需要改变迭代的元素或者提高效率,使用引用是一个好方法: $arr = array(1,2,3,4); ($arr as&$value){ $value = $value *2; } // $arr 现在是 array(2, 4, 6, 8) 这里有一个问题很多人都会感到困惑。
错误1:循环后留下悬空指针
在循环中,如果我们需要改变迭代的元素或者提高效率,使用引用是一个好方法:
$arr=数组(1,2,3,4);
($arras&$value){
$值=$值*2;
// $arr 现在是 array(2, 4, 6, 8)
这里有一个问题,很多人都会感到困惑。 循环结束后,$value不会被销毁。 $value 实际上是对数组中最后一个元素的引用。 如果在后续使用$value的时候不知道这一点,会导致一些莫名其妙的错误:)看看下面。 这段代码:
$数组=[1,2,3];
(',',$array),"\n";
($&$value){}// 通过
(',',$array),"\n";
($$value){}//按值(即复制)
(',',$array),"\n";
上述代码运行结果如下:
1,2,3
1,2,3
1,2,2
你猜对了吗? 为什么会是这样的结果呢?
我们来分析一下。 在第一个循环之后,$value 是对数组中最后一个元素的引用。 第二个循环开始:
第一步:将$arr[0]复制到$value(注意$value是对$arr[2]的引用),则数组变为[1,2,1]
第二步:复制$arr[1]到$value,则数组变为[1,2,2]
第三步:复制$arr[2]到$value,则数组变为[1,2,2]
综上,最终结果是1,2,2
避免此错误的最佳方法是在循环后立即使用 unset 函数销毁变量:
$arr=数组(1,2,3,4);
($arras&$value){
$值=$值*2;
unset($value);// $value 没有 $arr[3]
错误 2:对 isset() 函数行为的误解
对于isset()函数,当变量不存在时返回false,当变量值为null时返回false。 这种行为很容易迷惑人们。 。 。 看下面的代码:
$数据=ge($,$);
if(!isset($data['']){
// 如果未设置 '' 则在此执行
写这段代码的人可能本意是,如果$data['']没有设置,就会执行相应的逻辑。 但问题是,即使已经设置了$data[''],但设置的值为null,仍然会执行相应的逻辑,这不符合代码的初衷。
这是另一个例子:
如果($_POST['']){
$=($_POST);
// ...
如果(!isset($)){
echo '不发布';
上面的代码假设$_POST['']为true,那么$应该被设置,所以isset($)将返回true。 相反,上面的代码假设 isset($) 返回 false 的唯一方法是 $_POST[''] 也返回 false。
事实真的如此吗? 当然不是!
即使 $_POST[''] 返回 true,$ 也可能被设置为 null,在这种情况下 isset($) 将返回 false。 这违背了代码的意图。
如果上面代码的目的只是检测$_POST['']是否为true,那么下面的实现会更好:
如果($_POST['']){
$=($_POST);
// ...
如果($_POST['']){
echo '不发布';
要确定一个变量是否真正被设置(以区分未设置和设置值为 null),() 函数可能更好。 将上面的第一个示例重构如下:
$数据=ge($,$);
if(!('',$data)){
// 如果未设置 '' 则执行此操作
另外,结合()函数,我们可以更可靠地检测变量是否在当前作用域内设置:
如果(('',())){
// $ 范围内
错误3:混淆返回值和返回引用
考虑以下代码:
$=[];
(){
$这个->;
$=();
$->()['测试']='测试';
echo$->()['测试'];
运行上面的代码会输出如下内容:
::/path/to/my/.php 上
有什么问题? 问题在于上面的代码混淆了返回值和返回引用。 在 PHP 中,除非显式指定返回引用,否则 PHP 将返回数组的值,该值是数组的副本。 因此,当上面的代码给返回的数组赋值时,它实际上是给复制的数组赋值,而不是给原始数组赋值。
// () $ 数组的副本,因此这会添加一个“测试”
// 复制到 $ 数组,但不复制到 $ 数组。
$->()['测试']='测试';
// () 再次复制 $ 数组,而这个复制没有
// 一个“测试”(这就是我们得到“索引”的原因)。
echo$->()['测试'];
这是输出复制的数组而不是原始数组的可能解决方案:
$vals=$->();
$vals['测试']='测试';
echo$vals['测试'];
如果只是想改变原来的数组,即返回数组引用,应该如何处理呢? 方式是显示指定的返回引用:
$=[];
// a 到 $ 数组
&(){
$这个->;
$=();
$->()['测试']='测试';
echo$->()['测试'];
修改后,上面的代码将按照您的预期输出 test。
让我们看一个会让你更加困惑的例子:
$;
// 使用比数组
(){
$这个->=();
(){
$这个->;
$=();
$->()['测试']='测试';
echo$->()['测试'];
如果您认为“index”错误会像上面那样输出,那您就错了。 代码将正常输出“test”。 原因是 PHP 默认通过引用返回对象,而不是通过值。
综上所述,当我们使用函数返回值时,我们需要弄清楚是值返回还是引用返回。 对于PHP中的对象,默认是按引用返回,数组和内置基本类型默认是按值返回。 这要和其他语言区分开来(很多语言都是通过引用传递数组)。
与其他语言(例如 Java 或 C#)一样,它是使用或访问或设置类属性的更好解决方案。 当然PHP默认是不支持的,需要自己实现:
$=[];
($键,$值){
$this->[$key]=$value;
($键){
$this->[$key];
$=();
$->('','');
echo$->('');// 回显 ''
上面的代码允许调用者访问或设置数组中的任何值,而无需授予数组访问权限。 你感觉如何:)
错误4:循环执行sql查询
在 PHP 编程中,类似以下的代码并不罕见:
$=[];
($$){
$[]=$->($);
当然上面的代码没有任何问题。 问题是$->()在迭代过程中可能每次都会执行sql查询:
$=$->query(" `x`,`y` FROM `` WHERE `value`=".$);
如果迭代10000次,那么就分别执行了10000条sql查询。 如果在多线程程序中调用这样的脚本,很可能你的系统会挂起。 。 。
在编写代码的过程中,您应该知道何时执行SQL查询,并尝试在一个SQL查询中检索所有数据。
有一个业务场景你很可能会犯上述错误。 假设一个表单提交了一系列值(假设为ID),那么为了检索所有ID对应的数据,代码会遍历ID,分别对每个ID执行sql查询。 代码如下:
$数据=[];
($idsas$id){
$=$->query(" `x`, `y` FROM `` WHERE `id` = ".$id);
$数据[]=$->();
但在 SQL 中可以更有效地实现相同的目的。 代码如下:
$数据=[];
如果(计数($ids)){
$=$->query(" `x`, `y` FROM `` WHERE `id` IN (".(',',$ids));
而($行=$->()){
$数据[]=$行;
错误5:内存使用效率低下且错误
在一个 SQL 查询中获取多条记录肯定比在每个查询中获取一条记录更高效。 然而,如果您在PHP中使用MySQL扩展,一次获取多条记录很可能会导致内存溢出。
我们可以编写代码进行实验(测试环境:512MB RAM、MySQL、php-cli):
// 到mysql
$=('','','','');
// 400 的表
$query=' TABLE `test`(`id` INT NOT NULL KEY ';
为($col=0;$col
$query.=", `col$col` CHAR(10) NOT NULL";
$query.=');';
$->查询($查询);
// 写入2行
为($行=0;$行
$query=" INTO `test` ($row";
为($col=0;$col
$query.=', '.(,);
$查询.=')';
$->查询($查询);
现在我们看一下资源消耗:
// 到mysql
$=('','','','');
echo": ".e()."\n";
$res=$->query(' `x`,`y` FROM `test` LIMIT 1');
echo"限制 1: ".e()."\n";
$res=$->query(' `x`,`y` FROM `test` LIMIT 10000');
echo"限制 10000: ".e()."\n";
输出如下:
:
:
:
从内存使用情况来看,一切似乎都很正常。 为了更加确定,请尝试一次获取一项记录。 结果,程序得到以下输出:
:::查询():(HY000/2013):
/root/test.php 上
这是怎么回事?
问题在于 PHP 的 mysql 模块的工作方式。 mysql模块实际上是一个代理。 当查询获取多条记录时,这些记录会直接存储在内存中。 由于这块内存不是由PHP的内存模块管理的,所以我们通过调用e()函数得到的值并不是实际使用的内存值,所以就会出现上面的问题。
我们可以用它代替mysql,编译成PHP自己的扩展,它的内存使用情况由PHP内存管理模块控制。 如果我们用上面的代码来实现的话,会更真实的反映内存的使用情况:
:
:
:
更糟糕的是,根据 PHP 的官方文档,MySQL 扩展使用两倍的内存来存储查询数据,因此原始代码使用的内存大约是上图所示的两倍。
为了避免此类问题,可以考虑分几次完成查询,以减少单次查询的数据量:
$=10000;
$=100;
为($i=0;$(
" `x`,`y` FROM `test` LIMIT $, $");
联系上面提到的错误4,我们可以看出,在实际编码过程中,必须取得一个平衡,既要满足功能需求,又要保证性能。