Tutorial :PHP landmines in general [closed]



Question:

What surprises have other people found with writing PHP web applications? There's the well known and to be fixed issue with compile time class inheritance but I know of a couple others and wanted to try and build a list of the top gotcha's of the language.

Note:

I've held several positions as a Sr. PHP5 developer so PHP work pays my bills, this question is not meant to bust on PHP as a language as every single language I've worked with has some well known or not so well known surprises.


Solution:1

I'm not sure if this counts, but the need to compile PHP scripts is a huge performance issue. In any serious PHP project you need some kind of compiler cache like APC, eAccelerator, PHP Accelerator, or the (commercial) Zend Platform.


Solution:2

Recursive references leak memory

If you create two objects and store them inside properties of each other, the garbage collector will never touch them:

$a = new stdClass;  $b = new stdClass;  $a->b = $b;  $b->a = $a;  

This is actually quite easy to do when a large class creates a small helper object which usually stores the main class:

// GC will never clean up any instance of Big.  class Big {    function __construct() {      $this->helper = new LittleHelper($this);    }  }  class LittleHelper {    function __construct(Big $big) {      $this->big = $big;    }  }  

As long as PHP is targeted at short fast page requests, they are not likely to fix this issue. This means that PHP can't be depended on for daemons or other applications that have a long lifespan.


Solution:3

require_once and include_once can often result in major performance killers when used excessively. If your including/require a file that holds a class... a pattern like so can save some serious processing time.

class_exists("myFoo") or require("myFoo.someClass.php");  

Update: This is still a issue - http://www.techyouruniverse.com/software/php-performance-tip-require-versus-require_once

Update: Read the selected answer for the following question: Would performance suffer using autoload in php and searching for the class file? If implemented along these lines, you pretty much minimize as best as possible the penalties for file include/requires.


Solution:4

A fun landmine: Global variables can affect $_SESSION when register_globals is on. But i guess thats what happens when register_globals, a land mine itself, is turned on.


Solution:5

NULL and the "0" string are pure evil in Php

if ("0" == false) //true  if ("0" == NULL)  //true  if ("0" == "NULL")//true  


Solution:6

  • foreach() is silently copying the array in the background and iterating thru that copy. If you have a large array this will degrade performance. In those cases, the by-reference options of foreach() that are new to php5 or use a for() loop.

  • Be aware of equality (==) vs. identity (===).

  • Be aware of what constitutes empty() vs. what constitutes isset().


More landmines now that I have some more time:

  • Don't compare floats for equality. PHP isn't matlab and it simply isn't designed for precise floating point arithmetic. Try this one:
if (0.1 + 0.2 == 0.3)    echo "equal";  else    echo "nope"; // <-- ding ding  
  • Similarly, don't forget your octals! An int w/ a leading zero is cast as an octal.
if (0111 == 111)    echo "equal";  else    echo "nope"; // <-- ding ding  


Solution:7

It was kind of obvious after the fact but a well known gotcha has to do with scope and references when used in foreach.

foreach($myArray as &$element){     //do something to the element here... maybe trim or something more complicated  }  //Multiple lines or immediately after the loop    $element = $foobar;  

The last cell in your array has now become $foobar because the reference in the foreach above is still in the current context scope.


Solution:8

__autoload() proved to be a major landmine for me recently. Some of our legacy code and libraries use class_exists(), and it tries to autoload classes that were never meant to be loaded in that way. Lots of fatal errors and warnings. class_exists() can still be used if you have autoload, but the second parameter (new since PHP 5.2.0) has to be set to false


Solution:9

Not being aware of the operator precedence can cause some problems:

if ($foo = getSomeValue() && $bar) {      // …  }  // equals  if ($foo = (getSomeValue() && $bar)) {      // …  }  


Solution:10

The @ error silencer should always be avoided.

An example:

// Don't let the user see an error if this unimportant header file is missing:  @include 'header.inc.php';  

With the code above, you will never know about any errors in any of the code in header.inc.php, or any of the functions called from header.inc.php, and if there is a Fatal Error somewhere, your web page will halt with no way to find out what the error was.


Solution:11

The big gotcha I've seen people fall prey to is precision (in php and other languages).

If you want a bit of fun compare any float to a whole with >= and find out how many times you get the expected result.

This has been the downfall of many people working with money inside of PHP and trying to make logic decisions based on comparisons that do not allow rounding to a whole number.

For example - fabric

Fabric is sold in units of 1 yard or 1 half yard as well as maintaining an inventory of exact measurement left of the fabric.

If this system isn't expressed in whole numbers and instead is expressed in floating points it will make it incredibly hard to make solid decisons.

Your best bet is to express 1 half yard as 1, for example if you have 300 yds of fabric, you would have an inventory of 600 (600 half yard units).

Anyways, thats my gotcha - time to refactor 4 months of programming due to not understanding precision....


Solution:12

Numeric strings automatically converted to integers

That's by far the ugliest and most obscure hack in PHP. Whenever you have an string that is all digits, it automatically gets treated as if it was integer in some cases.

php > var_dump("0" == "00");  bool(true)  

This can get really nasty combined with PHP's "associative arrays", leading to weirdness where $a == $b does not imply, that $arr[$a] == $arr[$b];

php > var_dump(array('00'=>'str(zerozero)', '0'=>'str(zero)'));  array(2) {    ["00"]=>    string(13) "str(zerozero)"    [0]=>    string(9) "str(zero)"  }  


Solution:13

My favorite PHP gotcha:

Consider this include:

# ... lots of code ...  $i = 42;  # ... more code ...  

Then use this include somewhere:

for($i = 0; $i < 10; $i++){      # ...      include 'that_other_file.php';  }  

Then try to guess how many times the loop runs. Yup, once. Lexical scoping (and proper dynamic scoping) are both solved problems. But not in PHP.


Solution:14

If you're used to languages with intelligent logical operators, you will try to do things like:

$iShouldTalkTo = $thisObj || $thatObj;  

In PHP, $iShouldTalkTo is now a boolean value. You're forced to write:

$iShouldTalkTo = $thisObj ? $thisObj : $thatObj;  

Out of all the examples of how early design decisions in PHP tried to hold the hands of incompetent programmers in exchange for hobbling competent ones, that may be the one that irritates me the most.

Deep brain-damage in the switch() construct abounds. Consider this:

switch($someVal) {  case true  :      doSomething();      break;  case 20    :      doSomethingElse();      break;  }  

Turns out that doSomethingElse() will never be called, because 'case true' will absorb all true cases of $someVal.

Think that's justifiable, perhaps? Well, try this one:

for($ix = 0; $ix < 10; $ix++) {      switch($ix) {      case 3  :          continue;      default :          echo ':';      }      echo $ix;  }  

Guess what its output is? Should be :0:1:2:4:5:6:7:8:9, right? Nope, it's :0:1:23:4:5:6:7:8:9. That is, it ignores the semantics of the continue statement and treats it as a break.


Solution:15

One of the worst one is the concept of PHP's "associative arrays", which are totally failed hybrid of an array, a dictionary and a list. PHP's authors seem unsure how it should behave in each case, which leads to weirdness such us different behavior of arrays' plus operator and array_merge function.

php > $a = array(1=>'one');  php > $b = array(2=>'two');  php > var_dump($a+$b); /* plus preserves original keys */  array(2) {    [1]=>    string(3) "one"    [2]=>    string(3) "two"  }  php > var_dump(array_merge($a,$b)); /* array_merge reindexes numeric keys */  array(2) {    [0]=>    string(3) "one"    [1]=>    string(3) "two"  }      php > $a = array(1=>'one');  php > $b = array(1=>'another one');  php > var_dump($a+$b);  /* plus ignores duplicate keys, keeping the first value */  array(1) {    [1]=>    string(3) "one"  }  php > var_dump(array_merge($a,$b)); /* array_merge just adds them all, reindexing */  array(2) {    [0]=>    string(3) "one"    [1]=>    string(11) "another one"  }    php > $a = array(1,2,3);  php > $b = array(4,5,6);  /* non-associative arrays are really associative arrays with numeric keys… */  php > var_dump($a+$b);  /* … so plus doesn’t work as you’d normally expect */  array(3) {    [0]=>    int(1)    [1]=>    int(2)    [2]=>    int(3)  }  php > var_dump(array_merge($a,$b));  /* you should use array_merge instead */  array(6) {    [0]=>    int(1)    [1]=>    int(2)    [2]=>    int(3)    [3]=>    int(4)    [4]=>    int(5)    [5]=>    int(6)  }  


Solution:16

Total memory while running PHP. Many large projects just include all the class files and use them when they need them. This adds to the total memory PHP needs to use for each run.

Also projects using Frames or IFrames as this could easily double your memory usage.

So employ a conditional loading of your class files, have nothing loaded that you aren't using


Solution:17

Performance issues with PHP apps are usually one of the following:

  • File system access - reading and writing to disk
    • This is where APC, eAccelerator, etc come in handy, they reduce file system access by caching parsed PHP files in memory
  • Database - slow queries, large datasets
  • Network I/O - accessing external resources

It's quite rare to run into performance issues with PHP (or any web app written in any language). The above issues are usually orders of magnitude slower than code execution.

As always, profile your code!


Solution:18

Another pitfall in PHP, ive seen this error from people who come from other languages but not often.

<?php  /**   * regular   */  echo (true && true); // 1  echo (true && false); // nothing    echo (true || false); // 1  echo (false || false); // nothing    echo (true xor false); // 1  echo (false xor false); // nothing    /**   * bitwise   */  echo (true & true); // 1  echo (true & false); // 0    echo (true | false); // 1  echo (false | false); // 0    echo (true ^ false); // 1  echo (false ^ false); // 0  ?>  


Solution:19

Not getting compiler messages for if/else branches:

if( $foo )  {    some_function();  }  else  {    non_existing_function();   // oops!  }  

PHP won't mention that non_existing_function does not exist until you enter a situation where $foo is false.


Forgetting to set:

error_reporting( E_ALL );  

So notices are not caught, spending time debugging:

  • non existing variables
  • invalid object properties
  • invalid array keys

Pasting strings together of different "types" / sources, without escaping them:

// missing mysql_real_escape_string() or an int cast !  $sql = "SELECT * FROM persons WHERE id=$id";    // missing htmlentities() and urlencode() !  $html = "<a href='?page=$id'>$text</a>";    


Solution:20

As per Why is calling a function (such as strlen, count etc) on a referenced value so slow?

If you pass in a variable to a function by reference, and then call a function on it, it's incredibly slow.

If you loop over the function call and the variable is large it can be many orders of magnitude slower than if the variable is passed by value.

Example:

<?php  function TestCount(&$aArray)  {      $aArray = range(0, 100000);      $fStartTime = microtime(true);        for ($iIter = 0; $iIter < 1000; $iIter++)      {          $iCount = count($aArray);      }        $fTaken = microtime(true) - $fStartTime;        print "took $fTaken seconds\n";  }    $aArray = array();  TestCount($aArray);  ?>  

This consistently takes about 20 seconds to run on my machine (on PHP 5.3).

But if I change the function to pass by value (ie function TestCount($aArray) instead of function TestCount(&$aArray)), then it runs in about 2ms - literally 10,000 times faster!

The same is true for any function that passes by value - both built-in functions such as strlen, and for user-defined functions.

This is a rather scary tarpit that I was previously unaware of!

Fortunately there's a simple workaround that is applicable in many cases - use a temporary local variable inside the loop, and copy to the reference variable at the end.


Solution:21

Just thought of one more surprise. array_map which applies a callback to an array, is a serious performance killer. I'm not totally sure why, but I think it has something to do with PHP's copy on write mechanism for loops.


Solution:22

in the very beginning one could spent a lot of time debugging that kind of code:

$a = 1;  echo $a;      # 1  echo "$a";    # 1  echo '$a';    # $a  

damn quotes! very frustrating :(


Solution:23

Typecasting and triple equal

Generally in most of the languages, when you operate on two different types of data you either get an exception or one of them gets casted to more general one. In language, with exception of PHP, string is considered more general than integer. Only in PHP you have:

php > var_dump('nada' == 0);  bool(true)  

To cope with that PHP introduced triple equality operator. Which by definition returns true if the values are of same type and same value. Works for the example above:

php > var_dump('nada' === 0);  bool(false)  

But it also behaves pretty ugly when you actually would like values to be equal.

php > var_dump(0.0 === 0);  bool(false)  

If you're coming to work with PHP with experience from any other language, you're bound to have problems with this.


Solution:24

$x = array();  $x == null ? "true": "false";  

Output is "true".

$x = array("foo");  $x == null ? "true": "false";  

Output is "false";


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »