PHP is generally a nice language: it's quick n' dirty enough to make it real easy to hack together simple utilities, happens to avoid the drawbacks of the mother of all hack-together languages, Perl (as PHP code does not tend to converge to its own MD5 hash), and its resemblance to C in syntax and its forgiveness for smaller programmer mistakes makes the learning path extremely short. On the other hand its object oriented language elements and the good templating support make it usable for larger projects -- such as SSB and SCB at us.
However, there're some real bad quirks that can make your life miserable if you forget that your're dealing with PHP and not with a more "mature" object-oriented language like C++ or Java. These are not news for anyone seriously involved in PHP development but can raise an eyebrow for someone experienced in other languages.
One such group of problems originates from types in PHP. PHP is weakly typed language: you don't have to care about specifying types for variables and conversions happen dynamically. At first, that seems to make things easier, but that's not necessarily true. To quote
one of our fine PHP developers:
"The time you save on not having to care about types is exactly the time you lose trying to hunt down errors caused by types". A good example of how confusing things can get is the following piece of code:
if (0 == false)
echo "first\n";
if ("foo" == 0)
echo "second\n";
if ("foo" == false)
echo "third\n";
The output will be "first, second", but not "third". It's not illogical if you think about it: the first is an int --> bool cast, where everything that's not zero is true, so that experession's going to be true. The second one is a string --> int cast, where PHP tries to interpret the string as an integer and if it fails to do so, it results in zero, so this expression is going to be true as well. The third is a string --> bool cast, where the empty string, and the string "0" are interpreted as false, everything else is true. All steps make sense and they couldn't be done better, but in overall, this means that the "==" operator is not transitive in PHP. Which feels just plain wrong.
The other big issue is the handling of references. In C, however complex can all the pointer wizardy be for a newbie programmer, after you get a grip of it it's real simple and straightforward. In PHP, lots of things happen automatically based on context and some heuristics, which helps to avoid large and bad segfaults, but makes it much harder to do what you really want.
By default, PHP passes function arguments by value, right? Assigning a variable to another using a mere "=" creates a copy, doesn't it? Yeah, well, not always. The huge exception is objects. Take the following example code:
class TestClass
{
public $testData;
function __construct($data) {
$this->testData = $data;
}
}
$testobj1 = new TestClass("foo");
$testobj2 = $testobj1;
$testobj1->testData = "bar";
echo $testobj1->testData."|".$testobj2->testData."\n";
$testarr1 = array("foo");
$testarr2 = $testarr1;
$testarr1[0] = "bar";
echo $testarr1[0]."|".$testarr2[0]."\n";
It will output "bar|bar bar|foo", which clearly shows that assigning an object to another does not create a copy. You can use the "clone" keyword for that (and define the __clone() magic function for a copy constructor if you will). On the other hand, unlike in C, arrays behave like nice normal variables and get copied by value. The very same thing happens at passing function parameters:
class TestClass
{
public $testData;
function __construct($data) {
$this->testData = $data;
}
}
function testFunc1($obj) {
$obj->testData = "bar";
}
function testFunc2($arr) {
$arr[0] = "bar";
}
$testobj = new TestClass("foo");
$testarr = array ("foo");
testFunc1($testobj);
testFunc2($testarr);
echo $testobj->testData."|".$testarr[0]."\n";
This will output "bar|foo".
The last odd thing I'd like to share is the way you can return a reference from a function. You have to 1) prepend the function name at its definition with "&" and 2) use "=&" when you assign its return value to a variable. Forgetting any of these will not, in any way, trigger a warning or an error -- your variable just gets copied. Good luck hunting down the missing & over a trail of 10+ embedded function calls. The following code will write "bar|bar|foo|foo":
function &func (&$value) {
return $value;
}
function func2 (&$value) {
return $value;
}
$testvar = "foo";
$testvar2 =& func($testvar);
$testvar3 = func($testvar);
$testvar4 =& func2($testvar);
$testvar = "bar";
echo "$testvar|$testvar2|$testvar3|$testvar4\n";
As a last journey into PHP oddity I'd like to give the following as an excercise -- what does it print? (No cheating, please)
$a = 'asdasd';
var_dump($a);
var_dump(isset($a['foo']));
var_dump($a['foo']);