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
这是怎么回事呢?