Analysis of the 10 most common error _php techniques in PHP programming

Source: Internet
Author: User
Tags coding standards form post ord php and mysql php programming zend zend framework

Currently learning PHP a lot of friends, in peacetime routine development projects will always encounter a variety of problems, this experience to introduce PHP development in 10 of the most common problems, hope to be able to help friends.

Error 1:foreach Loop left hanging pointer

In a foreach loop, if we need to change the elements of an iteration or to improve efficiency, it's a good idea to use references:

$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, a lot of people get confused. After the end of the loop, value is not destroyed, value is actually the last element in the array reference, so in the subsequent use of $value, if you do not know this, will cause some inexplicable strange error: To see 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 results of the above code run as follows:

1,2,3 
1,2,3 
1,2,2

Did you guess right? Why is this result?

Let's analyze it. After the first loop, the $value is the 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 at this point), and the array becomes [1,2,1]
Step two: Copy arr[1] to value, when the array becomes [1,2,2]
Step three: Copy arr[2] to value, when the array becomes [1,2,2]
In summary, the end result is 1,2,2

The best way to avoid this error is to destroy the variable with the unset function 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: Error understanding of Isset () function behavior

For the Isset () function, False is returned when the variable does not exist, and false when the value of the variable is null. This behavior can easily confuse people ... Look at the following code:

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

The person who wrote this code was probably meant to be if data[′keyshouldbeset′] is not set, the corresponding logic is executed. The problem is that even if data[' Keyshouldbeset ' is set, the value set is null or the corresponding logic is executed, which is not in line with the code's 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 code above assumes that the only way that Isset (PostData) returns false is $_post[' active ' returns false.

Is that so? Of course not!

Even if post[′active′] returns TRUE,POSTDATA, it may be set to NULL, when Isset ($postData) returns false. This is not in line with the original code.

If the code above is intended only to detect if $_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 might be better at determining whether a variable is really set (distinguishing between setting and setting the value to null). Refactor 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, combining the Get_defined_vars () function, we can more reliably detect whether a variable is set in the current scope:

if (array_key_exists (' Varshouldbeset ', Get_defined_vars ())) { 
  //variable $varShouldBeSet exists in current scope< c7/>}

Error 3: Confusing return values and returning references

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's the problem? The problem is that the code above confuses the return value and the return reference. In PHP, unless you display the specified return reference, otherwise for the array PHP is the value return, which is the copy of the array. So the above code assigns values to the returned array, which is actually assigned to the copy array and not the original array.

GetValues () Returns a copy of the $values array, so this is adds a ' test ' element 
/to a copy of the $values array, b UT 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 (WHI CH is why we get the "undefined index" message). 
echo $config->getvalues () [' Test '];

Here is a possible workaround for outputting an array of copies instead of the original array:

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

If you want to change the original array, that is, to return the array reference, what should be done? The idea 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 code above 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 is that PHP is returned by reference, not by value, for objects by default.

To sum up, when we use the function return value, we have to figure out whether the value returns or the reference returns. For objects in PHP, the default is to return the reference, and the array and built-in base types are returned by value by default. This is to be distinguished from other languages (many languages refer to arrays as reference passes).

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 default does not support the need to implement themselves:

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 access to or set any value in the array without giving the array public access rights. How it feels:

Error 4: Executing the SQL query in the 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 code above. The problem is that during the iteration, $valuerepository->findbyvalue () may execute the SQL query 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 invoked in a multithreaded program, it's likely your system hangs ...

As you write 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 mistakes. Suppose a form submits a series of values (assumed to be IDs), and then, in order to retrieve the data for all IDs, the code traverses IDs and executes a 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 one SQL, with the following code:

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

Error 5: Memory usage low efficiency and illusion

Once a SQL query gets more than one record per query, it's definitely more efficient, but if you're using MySQL extensions in PHP, getting multiple records at a time can lead to 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 () not NULL; 
} 
$query. = '); 
$connection->query ($query); 
 
Write 2 million rows for 
($row = 0; $row < 2000000 $row + +) { 
  $query = INSERT into ' test ' VALUES ($row) C12/>for ($col = 0; $col < $col + +) { 
    $query. = ', '. Mt_rand (1000000000, 9999999999); 
  } 
  $query. = ') '; 
  $connection->query ($query); 
}

Now look at 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:224704 
Limit 10,000:224,704

According to memory usage, looks like everything is normal. To be more certain, try to get 100,000 records at a time, and the resulting program gets the following output:

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

What the hell is going on here?

The problem is in PHP's MySQL module working way, the MySQL module is actually a libmysqlclient agent. When a query gets multiple records, the records are stored directly in memory. Since this memory is not part of the PHP memory module, the value obtained by invoking 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:324952 
Limit 10,000:32,572,912

Worse, according to PHP's official documentation, the MySQL extended store query data uses twice times more memory than Mysqlnd, so the original code uses about twice times the memory shown above.

In order to avoid such problems, you can consider several times to complete the query, reducing the amount of single query data:

$totalNumberToFetch = 10000; 
$portionSize = m; 
 
for ($i = 0; $i <= ceil ($totalNumberToFetch/$portionSize); $i + +) { 
  $limitFrom = $portionSize * $i; 
  $res = $connection->query ( 
             "Select ' x ', ' Y ' from ' Test ' LIMIT $limitFrom, $portionSize"); 
}

The error 4 mentioned above can be seen in the actual coding process, to achieve a balance to both meet the functional requirements, but also to ensure performance.

Error 6: Ignoring the unicode/utf-8 problem

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. For a simple example, strlen (name), if name contains non-ASCII characters, the result is somewhat unexpected. Here are some suggestions to avoid such problems as far as possible:

If you don't know much about Unicode and utf-8, you should at least understand some basics. Recommend reading this article.
It is best to use the mb_* function to handle strings and avoid using old string-handling functions. Make sure that the "multibyte" extension of PHP is turned on.
It is best to use Unicode encoding for databases and tables.
Know that the Jason_code () function converts non-ASCII characters, but the Serialize () function does not.
PHP code source files are best used without BOM utf-8 format.
This article recommends a more detailed description of this type of issue: UTF-8 Primer for PHP and MySQL

ERROR 7: Assuming $_post always contains post data

$_post in PHP does not always contain the data 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 '
};

Notice 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 shown below:

Array (0) {}

Why is this the result? Where is our JSON data {A: ' A ', B: ' B '}?

The answer is that PHP only resolves HTTP requests that are content-type as 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 no automatic processing in PHP.

Because post is a global variable, changing the _post is globally valid. So for Content-type-Application/json requests, we need to manually parse the JSON data and then modify the $_post variable.

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

At this point, we're going to output the $_post variable, and we'll get the output we expect:

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

Error 8: Think PHP supports character data types

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.

Char data type is not present in PHP, only string type. Understand this, then the ' z ' increment operation, the result is ' AA '. For the size of the string, the C should know that ' AA ' is less than ' Z '. This also explains why there is a result of the above output.

If we want to output ' a ' to ' Z ', the following implementations are a good idea:

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

Or it's OK:

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

Error 9: Ignoring the encoding standard

While ignoring coding standards does not lead to errors or bugs, it is important to follow certain coding standards.

No unified coding Standard will cause many problems in your project. 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 is less productive, including doing a lot of meaningless work.

For PHP developers, it is more fortunate. 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: Auto Load
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, is now a part of 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 special reason to do it yourself. Now more and more projects are starting to use PSR, most PHP developers are also using PSR, so using PSR will make new members of your team become familiar with the project faster, write code will be more comfortable.

Error 10: Error using the empty () function

Some PHP developers like to use the empty () function to make Boolean judgments about variables or expressions, but in some cases they can be confusing.

First, let's look at the array arrays in PHP and the arrayobject of the objects. It looks like it's no different, it's all 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, 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 Zend Framework 2, Zend\db\tablegateway does this when calling the current () method on a tablegateway::select () result set to return a dataset. It's easy for developers to step on this hole.

To avoid these problems, check whether an array is empty the last way is to use the count () function:

This is 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)

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

Let's look at a very dangerous example of using the empty () function. When using the empty () function in the Magic Method __get (), it is also dangerous. Let's define two classes, each of which has a test property.

First we define the Regular class with a test property:

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

Then we 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 properties 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 confused.

But what happens when you use the empty () function on the test property?

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

Did it turn out to be a surprise?

Unfortunately, if a class uses the Magic __get () function to access the value of a class property, there is no simple way to check whether the property value is empty or does not exist. Outside of a class action, you can only check whether to return a null value, but that does not necessarily mean that the corresponding key is not set because the key value can be set to null.

By contrast, if we access a nonexistent 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 call 
 
Stack: 
  0.0012   234704  1. {main} ()/path/to/test.php:0

Therefore, for the empty () function, we have to be careful to use, otherwise it will result in unexpected, 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.