One obvious problem with PHP is that you can't reliably call external binaries; the "best" way is to use
proc_open(), but that function has all sorts of problems and is difficult to use.
Indeed, much of the standard library has problems; take reading files for example, this is one of the most basic operations you can do, but PHP does not properly implement this.
The only function you have is
fopen() (
file_get_contents() etc. are all wrappers), this works pretty much the same as
fopen() in C, with some dubious enhancements (like stream wrappers, which is a tale for another time). The problems start when there is an error;
fopen() just returns
False, and emits one of those magic uncatchable
E_WARNING or
E_ERROR errors; and
there is no way to figure out what went wrong. In C, you can use the
errno variable to check what went wrong, for example, the
EEXISTS indicates the filename already exists,
EACCESS indicates you don't have permission,
ELOOP indicated there are too many symlinks, and so forth (there are many more). This is
very useful, for example, for an
ENAMETOOLONG, you could trim the pathname and try again, for
EEXISTS, you could try again with a different filename, etc. You can do something sane, rather than just quit and say "unable to proceed". In fact, for some things, this is even
required, there is no way to make a safe temporary file
without this feature (the default functions in PHP for that are also inadequate,
see this page).
There is no way to do this in PHP, other than "silence" the error with the
@ operator, use
error_get_last(), and do a string search on that. This is so ugly that I would never use it, the error might change in the future, and is even locale-independent (e.g. French locale gets French errors). You do need the
@ +
error_get_last() "trick" to present an error to the user, though; or install your own error handler, the default is to just quit the interpreter (yikes) or continue after an error (even more yikes).
There are some functions that get the
errno for specific modules (
curl_errno(),
posix_errno()), but those only work for the functions from that module, not for
fopen() (for example).
Now, contrast this with some other popular high-level languages:
In Ruby, you get an exception which is mapped to the C
errno variable (e.g.
Errno::EEXISTS). This is arguably not perfect, since some
errno values are defined in POSIX, not all of them are, and Linux/BSD/etc. implement some OS-specific
errno values; but for most things, it works okay. This is how it looks:
Code:
begin
File.open '/etc/passwd', 'w'
rescue Errno::EACCESS
puts 'Access denied'
exit 1
# Or maybe ask user for different filename and try again? Or maybe execute myself again with sudo? Or maybe try a different filename?
end
In Python, there is more abstraction, and you might get a
FileNotFoundError or
PermissionError, and it's
even flexible enough to get the C errno variable; the code looks similar to the Ruby code, except it's less OS-dependent and arguably more readable:
Code:
try:
open('/etc/passwd', 'w')
except PermissionError:
print('Access denied')
sys.exit(1)
# Or maybe ask user for different filename and try again? Or maybe execute myself again with sudo? Or maybe try a different filename?
There are many more examples of basic functionality that's either incomplete, or downright broken. I'm not going to list them all, and hope the
fopen() examples convinces you that PHP is not suitable for reliable and robust systems programming. If C has features that your "high-level" programming language does not have, then you have FAILED.