PHP: Not as easy as you thought
Just like there are good reasons for running and maintaining old software
written in COBOL, there are many good reasons to use and develop software
written in PHP. However, being "an easy language to learn" is not one of
them. PHP gives the impression of being an easy language, while hiding
unexpected behaviour that can impair the correct functioning of your
software. It takes a great deal more knowledge and meticulousness to write
good PHP code than it takes to write good code in other languages.
I have done a lot of software development using PHP, and I'll probably do
a lot more. This page lists things I don't like about PHP, and also some
scripts I use to maintain my sanity while writing PHP code.
PHP 4
- Magic quotes. Not only does the option exist, but you can't disable it on a script-by-script basis. (Workaround)
- Session contamination: Unless you reconfigure the server, every PHP session is stored in the same place, and so session data from one website can be injected into another. This can be done, even if you use set_session_name. (I have a workaround that uses HMAC-SHA1 to authenticate session data, but it won't work unless you're careful.)
- Session fixation: PHP session IDs can be chosen and then reused by malicious attackers (For a workaround, the "session contamination" workaround above might work.)
- Complete lack of safe database API. (Workaround)
- Don't ever trust anything that's suggested in the "User Contributed Notes" in the online PHP Manual. The user-contributed notes section looks like a dumping ground for everyone's shoddy, application-specific, brain-damaged code.
- Don't trust the official documentation, either. Random example: The documentation for session_set_save_handler neglects to mention locking, even though the internal file-based handler does its own locking. Race conditions? What are those?
- Globals vs. superglobals. And apparently $GLOBALS can be replaced.
- The addslashes function exists, even though using it is probably always a bad idea.
- Don't use mysql_escape_string. That's wrong. Use mysql_real_escape_string. [Update: This is actually a quirk of the MySQL C API.]
- register_globals exists. Luckily, most people disable it these days.
- register_globals can't be disabled from within a script. Good luck trying to write PHP code that is portable to a wide variety of system configurations.
- You can't refer to &$this in an object's constructor and expect it to work. Of course, PHP doesn't give you any indication that something's wrong. (Apparently, this is fixed in PHP 5.)
- Callbacks in PHP consist of a string containing the name of the function you're calling, with some hacks for object and class methods.
- "one" === 0 is false and "one" !== 0 is true, but also "one" == 0 is true and "one" != 0 is false.
- allow_url_fopen: A nice idea, but too dangerous to be worth it because it mixes the filesystem namespace with the URL namespace. A separate "urlopen" function would have made much more sense.
- No inverse function is provided for parse_url.
- urlencode and rawurlencode. Guess which one complies with the RFC. Guess which one most people use. Result? Subtle bugs.
- The function you probably use most often, htmlspecialchars, is 16 characters long.
- Objects (i.e. class instances) given as function parameters are, by default, passed by value. Yeah.
- There's no goto, no labeled break, and no unconditional try..finally construct. If you want to ensure that your functions always clean up after themselves, well, good luck!
- imagecreatefromstring auto-detects the image format, but the functions that can read from a file (imagecreatefrompng, imagecreatefromjpeg, etc) do not.
- It's confusing and inconsistent.
- Syntax changes between minor versions. Apparently, $foo->bar()->baz() is legal in some versions of PHP 4 and illegal in others.
- No tail-call optimization, and apparently no way to fake it.
PHP 5
PHP 5 was first released on July 13, 2004, and it's better than PHP 4. Better doesn't mean good.
- It still doesn't have namespaces.
- There is still no way to disable register_globals or magic quotes from within a script.
- In PHP 5, the htmlentities and html_entity_decode functions take a charset parameter, so they can decode and encode UTF-8, respectively. However, get_html_translation_table has no charset parameter, so you can only get the ISO-8859-1 translation table. Thus, get_html_translation_table is not nearly as useful as it could be in a Unicode environment.