希望能够对朋友有所帮助

PHP是一种非常红的开源服务器端脚本语言,你在环球网见到的超越1/3网址都以利用php开辟的。本篇阅世将为大家介绍PHP开荒中10个最普及的难题,希望能够对相爱的人有所协助。

乐白家娱乐登录 1

不当1:foreach循环后留下悬挂指针

在foreach循环中,要是大家须要改换迭代的因素或是为了升高效用,运用援引是二个好法子:

$arr = array(1, 2, 3, 4); 
foreach ($arr as &$value) { 
    $value = $value * 2; 
} 
// $arr is now array(2, 4, 6, 8)

此处有个难点多五个人会头晕。循环结束后,$value并未有销毁,$value其实是数组中最后三个成分的引用,那样在一而再三番两次对$value的接受中,假若不精通这点,会迷惑部分莫名美妙的荒诞:State of Qatar看看上面这段代码:

$array = [1, 2, 3]; 
echo implode(',', $array), "n"; 

foreach ($array as &$value) {}    // by reference 
echo implode(',', $array), "n"; 

foreach ($array as $value) {}     // by value (i.e., copy) 
echo implode(',', $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 = array(1, 2, 3, 4); 
foreach ($arr as &$value) { 
    $value = $value * 2; 
} 
unset($value);   // $value no longer references $arr[3]

错误2:对isset(State of Qatar函数行为的失实通晓

对于isset(卡塔尔(قطر‎函数,变量不设不时会再次回到false,变量值为null时也会回去false。这种行为相当的轻易把人弄迷糊。。。看上面包车型客车代码:

$data = fetchRecordFromStorage($storage, $identifier); 
if (!isset($data['keyShouldBeSet']) { 
    // do something here if 'keyShouldBeSet' is not set 
}

写这段代码的人本意可能是只要$data[‘keyShouldBeSet’]未设置,则试行相应逻辑。但难点在于便是$data[‘keyShouldBeSet’]乐白家娱乐登录,已设置,但设置的值为null,依旧会实施相应的逻辑,那就不切合代码的本意了。

下边是别的一个例子:

if ($_POST['active']) { 
    $postData = extractSomething($_POST); 
} 

// ... 

if (!isset($postData)) { 
    echo 'post not active'; 
}

上面的代码假若$_POST[‘active’]为真,那么$postData应该被安装,由此isset($postData卡塔尔(قطر‎会重回true。反之,上面代码如若isset($postData卡塔尔(قطر‎再次回到false的无可比拟路线就是$_POST[‘active’]也返回false。

正是那样呢?当然不是!

即使$_POST[‘active’]归来true,$postData也有十分的大可能率被安装为null,这时候isset($postData卡塔尔(قطر‎就能够回来false。那就不切合代码的原意了。

万大器晚成地点代码的原意仅是检验$_POST[‘active’]是还是不是为真,上面那样达成会越来越好:

if ($_POST['active']) { 
    $postData = extractSomething($_POST); 
} 

// ... 

if ($_POST['active']) { 
    echo 'post not active'; 
}

看清一个变量是或不是真的被安装(区分未安装和设置值为null),array_key_exists(State of Qatar函数或然更加好。重构上面包车型客车率先个例子,如下:

$data = fetchRecordFromStorage($storage, $identifier); 
if (! array_key_exists('keyShouldBeSet', $data)) { 
    // do this if 'keyShouldBeSet' isn't set 
}

另外,结合get_defined_vars(State of Qatar函数,大家得以更上一层楼可信赖的检查实验变量在近些日子效劳域内是还是不是被安装:

if (array_key_exists('varShouldBeSet', get_defined_vars())) { 
    // variable $varShouldBeSet exists in current scope 
}

张冠李戴3:混淆重回值和再次来到援用

假造上边包车型客车代码:

class Config 
{ 
    private $values = []; 

    public function getValues() { 
        return $this->values; 
    } 
} 

$config = new Config(); 

$config->getValues()['test'] = 'test'; 
echo $config->getValues()['test'];

运维方面包车型客车代码,将会输出下边包车型地铁开始和结果:

PHP Notice:  Undefined index: test in /path/to/my/script.php on line 21

主题材料出在哪吧?难点就在于地点的代码混淆了再次回到值和重临引用。在PHP中,除非您显得的内定重返援引,不然对于数组PHP是值重返,也正是数组的正片。由此地点代码对回到数组赋值,实际是对拷贝数组进行赋值,非原数组赋值。

// getValues() returns a COPY of the $values array, so this adds a 'test' element 
// to a COPY of the $values array, but not to the $values array itself. 
$config->getValues()['test'] = 'test'; 

// getValues() again returns ANOTHER COPY of the $values array, and THIS copy doesn't 
// contain a 'test' element (which is why we get the "undefined index" message). 
echo $config->getValues()['test'];

上边是风流倜傥种大概的排除办法,输出拷贝的数组,并非原数组:

$vals = $config->getValues(); 
$vals['test'] = 'test'; 
echo $vals['test'];

设若您不怕想要更动原数组,相当于要反回数组援用,那应该怎么着处理吧?办法就是展现内定重回引用就能够:

class Config 
{ 
    private $values = []; 

    // return a REFERENCE to the actual $values array 
    public function &getValues() { 
        return $this->values; 
    } 
} 

$config = new Config(); 

$config->getValues()['test'] = 'test'; 
echo $config->getValues()['test'];

经过退换后,上边代码将会像你指望那样会输出test。

大家再来看二个事例会让您更迷糊的例子:

class Config 
{ 
    private $values; 

    // using ArrayObject rather than array 
    public function __construct() { 
        $this->values = new ArrayObject(); 
    } 

    public function getValues() { 
        return $this->values; 
    } 
} 

$config = new Config(); 

$config->getValues()['test'] = 'test'; 
echo $config->getValues()['test'];

纵然你想的是会和地点同样输出“ Undefined
index”错误,那您就错了。代码会平常输出“test”。原因在于PHP对于目的暗中认可正是按引用重临的,并非按值再次来到。

归纳,大家在选取函数重回值时,要弄驾驭是值再次回到依旧引用重返。PHP中对此指标,暗许是援用重临,数组和停放基本类型私下认可均按值再次回到。这一个要与其余语言分化开来(超级多语言对于数组是援用传递)。

像其余语言,举例java或C#,利用getter或setter来访谈或设置类属性是风华正茂种越来越好的方案,当然PHP默许不扶助,要求团结落成:

class Config 
{ 
    private $values = []; 

    public function setValue($key, $value) { 
        $this->values[$key] = $value; 
    } 

    public function getValue($key) { 
        return $this->values[$key]; 
    } 
} 

$config = new Config(); 

$config->setValue('testKey', 'testValue'); 
echo $config->getValue('testKey');    // echos 'testValue'

地点的代码给调用者能够访谈或设置数组中的任性值而不用授予数组public访谈权限。认为什么:卡塔尔国

谬误4:在循环中进行sql查询

在PHP编制程序中发觉相近上面包车型地铁代码并不菲见:

$models = []; 

foreach ($inputValues as $inputValue) { 
    $models[] = $valueRepository->findByValue($inputValue); 
}

当然下面的代码是未曾什么样错误的。难点在于大家在迭代经过中$valueRepository->findByValue(卡塔尔(قطر‎或许每一次都实践了sql查询:

$result = $connection->query("SELECT `x`,`y` FROM `values` WHERE `value`=" . $inputValue);

万少年老成迭代了10000次,那么你就分别施行了10000次sql查询。假诺这么的台本在二十四线程程序中被调用,那很恐怕你的系统就挂了。。。

在编制代码进程中,你必须求理解哪些时候应该奉行sql查询,尽或然二遍sql查询收取全数数据。

有大器晚成种业务场景,你很也许会犯上述失实。如果三个表单提交了豆蔻梢头体系值(就算为IDs),然后为了抽出全数ID对应的数据,代码将遍历IDs,分别对每一个ID推行sql查询,代码如下所示:

$data = []; 
foreach ($ids as $id) { 
    $result = $connection->query("SELECT `x`, `y` FROM `values` WHERE `id` = " . $id); 
    $data[] = $result->fetch_row(); 
}

但同样的指标能够在多个sql中国和越南社会主义共和国发神速的完结,代码如下:

$data = []; 
if (count($ids)) { 
    $result = $connection->query("SELECT `x`, `y` FROM `values` WHERE `id` IN (" . implode(',', $ids)); 
    while ($row = $result->fetch_row()) { 
        $data[] = $row; 
    } 
}

荒诞5:内部存款和储蓄器使用低效和错觉

二次sql查询获得多条记下比每一次查询获得一条记下效能必然要高,但借使你采用的是php中的mysql扩张,那么壹遍拿到多条记下就很恐怕会促成内部存款和储蓄器溢出。

小编们能够写代码来尝试下(测量试验意况: 512MB RAM、MySQL、php-cli):

// connect to mysql 
$connection = new mysqli('localhost', 'username', 'password', 'database'); 

// create table of 400 columns 
$query = 'CREATE TABLE `test`(`id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT'; 
for ($col = 0; $col < 400; $col++) { 
    $query .= ", `col$col` CHAR(10) NOT NULL"; 
} 
$query .= ');'; 
$connection->query($query); 

// write 2 million rows 
for ($row = 0; $row < 2000000; $row++) { 
    $query = "INSERT INTO `test` VALUES ($row"; 
    for ($col = 0; $col < 400; $col++) { 
        $query .= ', ' . mt_rand(1000000000, 9999999999); 
    } 
    $query .= ')'; 
    $connection->query($query); 
}

当今来走访能源消耗:

// connect to mysql 
$connection = new mysqli('localhost', 'username', 'password', 'database'); 
echo "Before: " . memory_get_peak_usage() . "n"; 

$res = $connection->query('SELECT `x`,`y` FROM `test` LIMIT 1'); 
echo "Limit 1: " . memory_get_peak_usage() . "n"; 

$res = $connection->query('SELECT `x`,`y` FROM `test` LIMIT 10000'); 
echo "Limit 10000: " . memory_get_peak_usage() . "n";

出口结果如下:

Before: 224704 
Limit 1: 224704 
Limit 10000: 224704

传说内部存款和储蓄器使用量来看,貌似一切符合规律。为了进一层显著,试着三回获得100000条记下,结果程序获得如下输出:

PHP Warning:  mysqli::query(): (HY000/2013): 
              Lost connection to MySQL server during query in /root/test.php on line 11

那是怎么回事呢?

难题出在php的mysql模块的行事方法,mysql模块实际上就是libmysqlclient的四个代理。在查询获得多条记下的同一时间,这一个记录会间接保存在内部存款和储蓄器中。由于那块内部存款和储蓄器不归属php的内部存款和储蓄器模块所管理,所以我们调用memory_get_peak_usage(卡塔尔(قطر‎函数所收获的值并不是真正使用内存值,于是便现身了上边的主题材料。

作者们能够选用mysqlnd来代替mysql,mysqlnd编写翻译为php自己扩大,其内部存款和储蓄器使用由php内部存款和储蓄器管理模块所主宰。假若大家用mysqlnd来落到实处地点的代码,则会愈发真实的反响内部存款和储蓄器使用情形:

Before: 232048 
Limit 1: 324952 
Limit 10000: 32572912

更是倒霉的是,依照php的官方文书档案,mysql扩充存款和储蓄查询数据利用的内部存款和储蓄器是mysqlnd的两倍,由此原本的代码应用的内存是上边显示的两倍左右。

为了制止此类主题素材,能够思考分三遍到位查询,减小单次查询数据量:

$totalNumberToFetch = 10000; 
$portionSize = 100; 

for ($i = 0; $i <= ceil($totalNumberToFetch / $portionSize); $i++) { 
    $limitFrom = $portionSize * $i; 
    $res = $connection->query( 
                         "SELECT `x`,`y` FROM `test` LIMIT $limitFrom, $portionSize"); 
}

关联方面提到的荒谬4能够看看,在实际上的编码进度中,要到位后生可畏种平衡,技能既满意作用要求,又能确认保证品质。

错误6:忽略Unicode/UTF-8问题

php编制程序中,在拍卖非ascii字符时,会凌驾有的难点,要超小心的去对待,要不然就能错误到处。举个轻易的事例,strlen($name卡塔尔(قطر‎,假诺$name富含非ascii字符,那结果就多少意外。在这里付出一些提议,尽量幸免此类难题:

  • 设若您对unicode和utf-8不是很掌握,那么你起码应该明白一些根底。推荐阅读这篇文章。
  • 最棒应用mb_*函数来拍卖字符串,防止选拔老的字符串管理函数。这里要承保PHP的“multibyte”增添已开启。
  • 数据库和表最棒利用unicode编码。
  • 知道jason_code(卡塔尔函数会转变非ascii字符,但serialize(卡塔尔国函数不会。
  • php代码源文件最佳利用不含bom的utf-8格式。

在那推荐生龙活虎篇小说,更详实的牵线了此类主题材料: UTF-8 Primer for PHP and
MySQL

错误7:假定$_POST总是蕴涵POST数据

PHP中的$_POST而不是总是包括表单POST提交过来的数码。若是大家透过
jQuery.ajax(卡塔尔国 方法向服务器发送了POST须要:

// js 
$.ajax({ 
    url: 'http://my.site/some/path', 
    method: 'post', 
    data: JSON.stringify({a: 'a', b: 'b'}), 
    contentType: 'application/json' 
});

潜心代码中的 contentType: ‘application/json’
,大家是以json数据格式来发送的数目。在服务端,大家仅输出$_POST数组:

// php 
var_dump($_POST);

您会很感叹的觉察,结果是上面所示:

array(0) { }

干什么是那般的结果吧?大家的json数据 {a: ‘a’, b: ‘b’} 哪去了呢?

答案就是PHP仅仅分析Content-Type为 application/x-www-form-urlencoded 或
multipart/form-data的Http央求。之所以这么是因为历史原因,PHP最先完毕$_POST时,最流行的正是地点三种档案的次序。由此尽管未来有些种类(举例application/json)极流行,但PHP中要么未有去完毕机关处理。

因为$_POST是全局变量,所以改过$_POST会全局有效。因而对于Content-Type为
application/json
的乞请,大家供给手工业去深入分析json数据,然后校订$_POST变量。

// php 
$_POST = json_decode(file_get_contents('php://input'), true);

那个时候,我们再去输出$_POST变量,则会得到大家意在的出口:

array(2) { ["a"]=> string(1) "a" ["b"]=> string(1) "b" }

不当8:以为PHP扶植字符数据类型

拜访下边的代码,估量下会输出什么:

for ($c = 'a'; $c <= 'z'; $c++) { 
    echo $c . "n"; 
}

大器晚成旦你的对答是出口’a’到’z’,那么您会惊讶的觉察你的答复是谬误的。

科学,上面包车型客车代码的确会输出’a’到’z’,但除了,还有可能会输出’aa’到’yz’。大家来分析下何以会是这么的结果。

在PHP中海市蜃楼char数据类型,只有string类型。通晓那点,那么对’z’进行依次增加操作,结果则为’aa’。对于字符串十分大小,学过C的应当都知晓,’aa’是小于’z’的。这也就解释了怎会有地点的出口结果。

即使我们想出口’a’到’z’,上面包车型大巴兑现是黄金时代种科学的点子:

for ($i = ord('a'); $i <= ord('z'); $i++) { 
    echo chr($i) . "n"; 
}

只怕那样也是OK的:

$letters = range('a', 'z'); 

for ($i = 0; $i < count($letters); $i++) { 
    echo $letters[$i] . "n"; 
}

乖谬9:忽略编码标准

纵然忽视编码标准不会以致错误或然bug,但遵照一定的编码典型还是很要紧的。

还没统大器晚成的编码标准会让你的连串出现好多主题素材。最明显的正是您的体系代码不有所蓬蓬勃勃致性。更坏的地点在于,你的代码将进一层难以调节和测量试验、扩大和保险。那也就表示你的公司效用会减少,蕴含做一些众多无意义的分神。

对于PHP开采者来讲,是相比幸运的。因为有PHP编码标准推荐(PSKuga),由下边5个部分组成:

  • PS迈凯伦570-0:自动加载典型
  • PSENCORE-1:基本编码规范
  • PS昂Cora-2:编码风格指南
  • PSCRUISER-3:日志接口规范
  • PSTucson-4:自动加载

PSHighlander最先由PHP社区的多少个大的组织所开创并依据。Zend, Drupal, Symfony,
Joomla及别的的阳台都为此标准做过进献并依据那一个规范。纵然是PEAENVISION,明年也想让谐和成为三个职业,但今后也加盟了PS宝马X3阵营。

在少数情形下,使用什么编码标准是不屑意气风发顾的,只要您采用风流浪漫种编码风格并直接百折不挠选取就可以。可是依照PSGL450规范不失为三个好法子,除非您有如何独特的来由要
本人弄生龙活虎套。现在更是多的种类都最初接受PSTucson,超过二分之一的PHP开采者也在利用PSR,由此利用PSTiggo会让新加盟你团队的积极分子越来越快的熟谙项目,写代码时
也会愈加酣畅。

张冠李戴10:错误使用empty(卡塔尔(قطر‎函数

有的PHP开采人士心仪用empty(卡塔尔(قطر‎函数去对变量或表明式做布尔决断,但在有些意况下会令人很迷惑。

先是大家来看看PHP中的数组Array和数组对象ArrayObject。看上去就疑似没什么差距,都以生龙活虎致的。真的如此呢?

// PHP 5.0 or later: 
$array = []; 
var_dump(empty($array));        // outputs bool(true)  
$array = new ArrayObject(); 
var_dump(empty($array));        // outputs bool(false) 
// why don't these both produce the same output?

让职业变得更头晕目眩些,看看下边包车型大巴代码:

// Prior to PHP 5.0: 
$array = []; 
var_dump(empty($array));        // outputs bool(false)  
$array = new ArrayObject(); 
var_dump(empty($array));        // outputs bool(false)

很倒霉的是,上边这种方式相当受款待。举个例子,在Zend Framework
第22中学,ZendDbTableGateway 在 TableGateway::select(卡塔尔国 结果集上调用
current(State of Qatar 方法重回数据集时就是如此干的。开采人士超级轻松就能够踩到这么些坑。

为了幸免那一个难题,检查三个数组是还是不是为空最终的点子是用 count(卡塔尔国 函数:

// Note that this work in ALL versions of PHP (both pre and post 5.0): 
$array = []; 
var_dump(count($array));        // outputs int(0) 
$array = new ArrayObject(); 
var_dump(count($array));        // outputs int(0)

在这里顺便提一下,因为PHP中会将数值0以为是布尔值false,由此 count()函数能够一贯用在 if
条件语句的尺度决断中来判定数组是或不是为空。其余,count(卡塔尔国函数对于数组来讲复杂度为O(1卡塔尔国,由此用 count(State of Qatar 函数是叁个睿智的接受。

再来看多少个用 empty(State of Qatar 函数很危险的事例。当在魔术点子
__get(卡塔尔国 中组成使用 empty(卡塔尔(قطر‎函数时,也是很危急的。我们来定义多个类,每一个类都有叁个 test 属性。

率先大家定义 Regular 类,有贰个 test 属性:

class Regular 
{ 
    public $test = 'value'; 
}

下一场大家定义 Magic
类,并用 __get(卡塔尔国 魔术点子来会见它的 test 属性:

class Magic 
{ 
    private $values = ['test' => 'value']; 

    public function __get($key) 
    { 
        if (isset($this->values[$key])) { 
            return $this->values[$key]; 
        } 
    } 
}

好了。大家今日来会见访问各样类的 test 属性会暴发哪些:

$regular = new Regular(); 
var_dump($regular->test);    // outputs string(4) "value" 
$magic = new Magic(); 
var_dump($magic->test);      // outputs string(4) "value"

到最近截止,都依然如常的,未有让大家深感头昏眼花。

但在 test 属性上接纳 empty(卡塔尔(قطر‎ 函数会如何啊?

var_dump(empty($regular->test));    // outputs bool(false) 
var_dump(empty($magic->test));      // outputs bool(true)

结果是否很古怪?

非常不佳的是,假使多个类使用法力 __get()函数来访谈类属性的值,没有简单的形式来检查属性值是还是不是为空或是不真实。在类功用域外,你一定要反省是还是不是重回null 值,但那并不一定意味着没有安装相应的键,因为键值能够棉被服装置为 null

相比较之下,要是我们拜会 Regular
类的四个子虚乌有的习性,则会赢得一个像样上边的Notice音讯:

Notice: Undefined property: Regular::$nonExistantTest in /path/to/test.php on line 10 

Call Stack: 
    0.0012     234704   1. {main}() /path/to/test.php:0

因此,对于 empty(卡塔尔函数,大家要小心的应用,要不然的话就能结出竟然,以至秘密的误导你。

来自:程序猿的资料库

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图