Summary of the most common mistakes in building PHP stations

Source: Internet
Author: User
Tags coding standards form post zend framework

Error 1:foreach left hanging pointer after loop

In a foreach loop, if we need to change the elements of an iteration or to improve efficiency, applying a reference is a good idea:

$arr = Array (1, 2, 3, 4);

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

There's a problem here, and a lot of people get confused. After the loop is over, the $value is not destroyed, $value is actually a reference to the last element in the array, so that in the subsequent use of $value, if you do not know this, it will cause some strange and strange error:)

Take a look at the following code:

$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 ';

The result of the above code is as follows:

The

The
1,2,2

Have you guessed it right? Why is this result?

Let's analyze that. After the first loop, $value is a reference to the last element in the array. The second loop begins: The first step: Copy $arr[0] to $value (note that $value is a reference to $arr[2] when the array becomes [1,2,1] The second step: Copy $arr[1] to $value, when the array becomes [1,2,2] The third step: Copy $arr[ 2] to $value, when the array becomes [1,2,2], the end result is that 1,2,2 the best way to avoid this error is to use the unset function to destroy the variable immediately after the loop:

$arr = Array (1, 2, 3, 4);

foreach ($arr as & $value) {
$value = $value * 2;
}

Unset ($value); $value no longer references $arr [3]

Error 2: Incorrect understanding of the behavior of the isset () function

For the Isset () function, False is returned when the variable does not exist, and false when the value of the variable is null. It's easy to confuse people with this behavior ...

Look at the following code:

$data = Fetchrecordfromstorage ($storage, $identifier);

if (!isset ($data [' Keyshouldbeset ']) {
Do something here if ' keyshouldbeset ' isn't set
From:www.phper note.com
}

The person who wrote this code might have intended to execute the corresponding logic if $data[' Keyshouldbeset ' was not set. The problem is that even if $data[' Keyshouldbeset ' is set, but the value is set to NULL, the corresponding logic is executed, which does not conform to the code's original intent.

Here is another example:

if ($_post[' active ']) {

$postData = extractsomething ($_post);
}
// ...
if (!isset ($postData)) {
Echo ' Post not active ';
}

The above code assumes that $_post[' active ' is true, then $postdata should be set, so Isset ($postData) returns True. Conversely, the above code assumes that the only way Isset ($postData) returns false is that $_post[' active ' also returns false. Is that true? Of course not! Even if $_post[' active ' returns True, $postData may also be set to NULL, Isset ($postData) will return false. This does not conform to the original meaning of the code. If the above code is intended only to detect whether $_post[' active ' is true, the following implementation would be better:

if ($_post[' active ']) {

$postData = extractsomething ($_post);
}
// ...
if ($_post[' active ']) {
Echo ' Post not active ';
}

The array_key_exists () function may be better to determine whether a variable is actually set (the distinction is not set and the value is set to null). Refactoring the first example above, as follows:

$data = Fetchrecordfromstorage ($storage, $identifier);

if (! array_key_exists (' Keyshouldbeset ', $data)) {
Do this if ' keyshouldbeset ' isn ' t set
}

In addition, with the Get_defined_vars () function, we can more reliably detect whether a variable is set within the current scope:

if (array_key_exists (' Varshouldbeset ', Get_defined_vars ())) {

Variable $varShouldBeSet exists in current scope
}

Error 3: Confusing return value and returning reference

Consider the following code:

Class config{

Private $values = [];
Public Function getValues () {
return $this->values;
}
}
$config = new Config ();
$config->getvalues () [' test '] = ' test ';
echo $config->getvalues () [' Test '];

Running the above code will output the following:

PHP notice:undefined index:test in/path/to/my/script.php on line 21

What is the problem?

The problem is that the code above confuses the return value and returns the reference. In PHP, unless you display the specified return reference, otherwise for the array PHP is the value returned, which is the copy of the array. So the above code assigns a value to the returned array, which is actually the assignment of the copy array, not the original array assignment.

GetValues () returns a COPY of the $values array, so this adds a ' test ' element

To a COPY of the $values array, and not the $values array itself.
From:www.phper note.com
$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 '];

The following is a possible workaround for outputting a copy of an array, rather than the original array:

$vals = $config->getvalues ();

$vals [' test '] = ' test ';
echo $vals [' Test '];

If you want to change the original array, that is, to reverse the array reference, what should be done? The solution is to display the specified return reference:

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 '];

After the transformation, the above code will output test as you would expect. Let's look at an example that will make you more confused:

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 '];

If you want to output the "Undefined index" error as above, you are wrong. The code will output "test" normally. The reason for this is that PHP returns the object by reference by default, rather than by value.

In summary, when we use the function return value, we need to figure out whether the value returns or the reference is returned. In PHP, for objects, the default is a reference return, and the array and built-in base types are returned by value by default. This is to be distinguished from other languages (many languages are reference to arrays). Like other languages, such as Java or C #, using Getter or setter to access or set class properties is a better solution, of course, PHP is not supported by default and requires its own implementation:

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 '

The above code gives the caller the ability to access or set any value in the array without giving access to the array public. How it feels:)

Error 4: Executing SQL query in loop

It is not uncommon to find code similar to the following in PHP programming:

$models = [];

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

Of course, there is nothing wrong with the above code. The problem is that we are in the process of iterating

$valueRepository->findbyvalue ()

The SQL query may have been executed every time:

$result = $connection->query ("Select ' x ', ' y ' from ' values ' WHERE ' value ' =". $inputValue);

If you iterate 10,000 times, you execute 10,000 SQL queries separately. If such a script is called in a multithreaded program, it is likely that your system will hang up ... In the process of writing your code, you should be aware of when you should execute the SQL query and take all the data out of the SQL query as soon as possible. There is a business scenario where you are likely to make the above mistake. Suppose a form submits a series of values (assuming IDs), and then in order to fetch the data for all IDs, the code iterates through IDs and executes the SQL query for each ID, as shown in the following code:

$data = [];

foreach ($ids as $id) {
$result = $connection->query ("Select ' x ', ' y ' from ' values ' WHERE ' id ' =". $ID);
$data [] = $result->fetch_row ();
}

But the same goal can be done more efficiently in a SQL, with the following code:

$data = [];

if (count ($ids)) {
$result = $connection->query ("Select ' x ', ' y ' from ' values ' WHERE ' ID ' in ("). Implode (', ', $ids));
while ($row = $result->fetch_row ()) {
$data [] = $row;
}
}

Error 5: Memory usage inefficient and illusion

Once a SQL query gets multiple records, it is certainly more efficient to get one record per query, but if you are using MySQL extensions in PHP, getting multiple records at one time is likely to cause a memory overflow. We can write code to experiment (test environment: 512MB RAM, MySQL, PHP-CLI):

Connect to MySQL

$connection = new mysqli (' localhost ', ' username ', ' password ', ' database ');
CREATE TABLE of columns
$query = ' CREATE TABLE ' test ' (' ID ' INT not NULL PRIMARY KEY auto_increment ';
for ($col = 0; $col < $col + +) {
$query. = ", ' Col$col ' CHAR (Ten) 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 < $col + +) {
$query. = ', '. Mt_rand (1000000000, 9999999999);
}
$query. = ') ';
$connection->query ($query);
}


Now look at the resource consumption://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";

The output results are as follows: before:224704
Limit 1:224,704
Limit 10,000:224,704

Depending on the amount of memory used, everything seems to be fine. In order to be more certain, try to get 100,000 records at a time, the result of the program is the following output:

PHP warning:mysqli::query (): (hy000/2013):

Lost connection to MySQL server during query in/root/test.php on line 11

What's going on here?

The problem is that the MySQL module works in PHP, and the MySQL module is actually a proxy for libmysqlclient. These records are stored directly in memory while the query gets multiple records. Since this memory is not managed by PHP's memory modules, the value obtained by calling the Memory_get_peak_usage () function does not actually use the memory value, so the above problem arises. We can use MYSQLND instead of MYSQL,MYSQLND to build PHP itself, and its memory usage is controlled by the PHP memory management module. If we use MYSQLND to implement the above code, it will be more realistic to respond to memory usage: before:232048

Limit 1:324,952
Limit 10,000:32,572,912

Even worse, according to PHP's official documentation, the MySQL extended storage query data uses twice times more memory than Mysqlnd, so the original code uses about twice times the memory shown above. To avoid such problems, consider completing the query several times to reduce the amount of data in a single query:

$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");
}

Contact the above mentioned error 4 can be seen, in the actual coding process, to achieve a balance, can not only meet the functional requirements, but also to ensure performance. Error 6: Ignoring UNICODE/UTF-8 problems in PHP programming, when dealing with non-ASCII characters, you will encounter some problems, to be very careful to treat, otherwise it will be wrong everywhere.

As a simple example, strlen ($name), if $name contains non-ASCII characters, the result is somewhat unexpected. Here are some suggestions to avoid such problems: if you are not familiar with Unicode and utf-8, you should at least understand some of the basics. It is best to use the mb_* function to handle strings, avoiding the use of old string-handling functions. This is to make sure that PHP's "multibyte" extension is turned on. Databases and tables are best used with Unicode encoding. It is known that the Jason_code () function converts non-ASCII characters, but the Serialize () function does not. PHP code source files are best used in a utf-8 format that does not contain a BOM.

ERROR 7: Assume that $_post always contains post data

$_post in PHP does not always contain data that is submitted by the form post. Suppose we send a POST request to the server through the Jquery.ajax () method:

Js
$.ajax ({
URL: ' Http://my.site/some/path ',
Method: ' Post ',
Data:JSON.stringify ({A: ' A ', B: ' B '}),
ContentType: ' Application/json '
});

Note The ContentType in the code: ' Application/json ', we are sending data in JSON data format. On the server side, we only output $_post arrays://php
Var_dump ($_post);
You will be surprised to find that the result is as follows: Array (0) {}

Why is this the result?

Our JSON data {A: ' A ', B: ' B '} where is it? The answer is that PHP only resolves HTTP requests content-type to application/x-www-form-urlencoded or Multipart/form-data. This is because of historical reasons, when PHP first implemented $_post, the most popular is the above two types. So while some types (such as Application/json) are popular today, there is still no automatic processing in PHP. Because $_post is a global variable, changing $_post is globally valid. So for the request of Content-type to Application/json, we need to parse the JSON data manually and then modify the $_post variable. Php

$_post = Json_decode (file_get_contents (' Php://input '), true);

At this point, we are going to output the $_post variable, and we will get the output we expect:

Array (2) {["a"]=> string (1) "A" ["B"]=> string (1) "B"}

Error 8: Think PHP supports character data types

Take a look at the following code and guess what it will output:

for ($c = ' a '; $c <= ' z '; $c + +) {

Echo $c. "\ n";
}

If your answer is output ' a ' to ' Z ', then you will be surprised to find that your answer is wrong. Yes, the above code does output ' a ' to ' Z ', but in addition, it will output ' AA ' to ' YZ '. Let's analyze why this is the result. There is no char data type in PHP, only the string type. Understand this, then the ' Z ' is incremented, the result is ' AA '. For the string comparison size, learn C should know that ' AA ' is less than ' Z '. This also explains why the above output is the result. If we want to output ' a ' to ' Z ', the following implementation is a good idea:

for ($i = Ord (' a '); $i <= ord (' z '); $i + +) {

echo chr ($i). "\ n";
}

Or This is OK:

$letters = Range (' A ', ' Z ');

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

Error 9: Encoding standard ignored

Although ignoring the coding standard does not result in errors or bugs, it is important to follow certain coding standards. There are a lot of problems with your project without a unified coding standard. The most obvious is that your project code is not consistent. The worse part is that your code will be more difficult to debug, extend, and maintain. This also means that your team will be less efficient, including doing a lot of meaningless work. For PHP developers, it's a bit of a lucky one. Because of the PHP coding standard Recommendation (PSR), consists of the following 5 parts: PSR-0: Automatic loading Standard PSR-1: Basic coding Standard PSR-2: Coding style Guide PSR-3: Log interface standard PSR-4: Automatic loading PSR was originally created and followed by several large groups in the PHP community. Zend, Drupal, Symfony, Joomla and other platforms have contributed to and followed this standard. Even pear, who wanted to be a standard in the early years, has now joined the PSR Camp. In some cases, it doesn't matter what coding standards you use, as long as you use a coding style and stick with it. But it's a good idea to follow the PSR Standard unless you have a specific reason to do it yourself. More and more projects are now using PSR, and most PHP developers are using PSR, so using PSR will make it easier for new members of your team to become familiar with the project and write code more comfortably. Error 10: Error using empty () function some PHP developers prefer to use the empty () function to make Boolean judgments about variables or expressions, but in some cases they can be confusing. First, let's take a look at the array arrays in PHP and the Arrayobject object. It looks like there's no difference, it's the same. Is that true?

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?

To make things more complicated, take a look at the following code:

Prior to PHP 5.0:

$array = [];
Var_dump (Empty ($array)); outputs bool (FALSE)
$array = new Arrayobject ();
Var_dump (Empty ($array)); outputs bool (FALSE)

Unfortunately, the above method is very popular. For example, in the Zend Framework 2, Zend\db\tablegateway calls the current () method on the Tablegateway::select () result set to return the DataSet as it did. It's easy for developers to step on the pit. To avoid these problems, check if an array is empty the last way is to use the count () function:

Note that this is 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)

By the way, because PHP will value 0 as the Boolean value false, so the count () function can be directly used in the condition judgment of the IF condition statement to determine whether the array is empty. In addition, the count () function is an O (1) for an array, so using the count () function is a wise choice.

Let's look at an example in which the empty () function is dangerous. When combined with the empty () function in the Magic Method __get (), it is also dangerous. Let's define two classes, each with a test property. First we define the Regular class, which has a test property:

Class regular{

Public $test = ' value ';
}

We then define the Magic class and use the __get () Magic method to access its Test property:

Class magic{

Private $values = [' Test ' = ' value '];
Public Function __get ($key) {
if (Isset ($this->values[$key])) {
return $this->values[$key];
}
}
}

All right. Let's take a look at what happens when you access the test property of each class:

$regular = new Regular ();

Var_dump ($regular->test); Outputs string (4) "Value"
$magic = new Magic ();
Var_dump ($magic->test); Outputs string (4) "Value"

So far, it's normal, and it doesn't make us feel confused. But what about using the empty () function on the test property?

Var_dump (Empty ($regular->test)); outputs bool (FALSE)

Var_dump (Empty ($magic->test)); outputs bool (TRUE)


The result is not very surprising? Unfortunately, if a class uses the Magic __get () function to access the value of a class property, there is no easy way to check whether the property value is empty or does not exist. Outside of the class, you can only check if null values are returned, but this does not necessarily mean that the corresponding key is not set because the key value can be set to null. In contrast, if we access a non-existent property of the Regular class, we get a Notice message similar to the following: 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
Therefore, for the empty () function, we should be careful to use, otherwise it will result unexpectedly, even potentially misleading you.
  • Contact Us

    The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

    If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.