Common errors

A very large percentage of errors are actually quite simple to spot and solve once you are sufficiently experienced, however they continue to crop up time and time again to plague us. This next section includes hints and tips on how to solve specific common problems in code - if you are encountering an error in your script that you just cannot track down, see if you find your answer here.

19.12.3.1 I see the source code of my PHP script

This normally occurs when you have misconfigured Apache; check your install and try again.

19.12.3.2 My script does nothing

If you don't get the output you were expecting, it is possible that you have an invisible error somewhere. There are two ways to check this is not the case:

  1. Check your php.ini file and make sure "display_errors" is set to On.

  2. If your script is output content inside a HTML page, the errors may be obscured by the HTML - use the View Source functionality of your browser to search for "PHP". This should bring up any errors, if there are any.

If you still have nothing, try these possibilities:

  • Make sure you don't have output buffering hiding your output

  • Make sure "error_reporting" is set to E_ALL in your php.ini file

  • Make sure you have saved the latest version of your script

  • Make sure all your includes are working

If you are still having trouble, you need to seek out others to help.

19.12.3.3 Fatal error: Cannot redeclare (function)

If you find PHP telling it cannot redeclare a function of yours, there are two possibilities:

You're using a name which is already being used by PHP. For example, in_array() is a standard PHP function - you cannot make your own function called in_array(). Try searching the PHP manual for the function name, and if it matches, change yours to a different name.

If the function name is not in the PHP manual, then the problem is because you have had the function defined twice. This often happens when you put a function into a file, then include() that file more than once.

Consider this script:

if ($foo > 50) { include("myfuncs.php"); }
if ($foo > 100) { include("myfuncs.php"); }

Here, if $foo is 1000, myfuncs.php will be included twice, which means the chance of a function being redeclared is quite likely. There are two ways around this:

  • Include a file once, and never again. This can be tricky to enforce, particularly as it means you need to keep checking whether a given file has been included elsewhere.

  • Use the include_once() function rather than include() - include_once() will include a file if it hasn't been included before, but will not include it again.

19.12.3.4 Call to undefined function

This error message is fairly self-explanatory, and basically means that you have called a function which PHP hasn't heard of. There are three possible reasons for this:

  • You typed in the wrong function, e.g. inarray() rather than in_array(). Check the line number PHP reports and make sure you have the right function in there.

  • You're using variable functions, e.g. $foo(), and the variable does not include the right text. Print out the value of the variable to make sure you have it set correctly. Occasionally, usually very late at night, I end up typing such rubbish as if ($isset(foo)) instead of if (isset($foo)), which results in the same error.

  • Your function is in an external file that is not being included, potentially because the include() call is inside an if statement that is not executed. Check the function is included properly.

19.12.3.5 Parse error

This is a very generic error message that simply means "PHP failed to understand your script", which in turn means that there is a syntax error in your script. Possible causes for this include:

  • Forgetting a semi-colon at the end of the line before

  • Not escaping quotes in a string

  • You used "case default:" rather than just "default:" in a switch/case statement.

19.12.3.6 Heredoc is not working correctly

The heredoc string syntax in PHP is very specific - one small error and it goes wrong, often in ways that are hard to understand. So, when using heredoc, you need to be particularly precise! Here's an example of properly formed heredoc code:

<?php
    $foo = <<<END My text
    More text
        Even more text END;    echo $foo;
?>

There are two things to note in there: first, the three < signs followed by the string terminator, "END". You can use anything you want in place of END, as long as you know it won't appear in the text. Terminators are case-sensitive, unsurprisingly.

Second, the heredoc string is terminated by using END at column 0 in the code. That is, there is no text on the line before END - not even a space or a tab. So, to end your heredoc string, put your terminator at the very start of a line by itself, with a semicolon.

If you break either of these two rules, things go haywire: if you change the second END to "EnD" (note the lower-case "n"), it no longer matches as the terminator, and you'll get an error like this:

Parse error: parse error, unexpected $end in /usr/paul/bad_heredoc.php on line 8

You'll also get the same error if you put a single space before the terminating END. This, then, is the most common problem with heredoc: using the terminator incorrectly, or, worse, using it inside the string itself!

If you get any heredoc errors, this is what it will come down to. However, note that the error message is pretty vague: it doesn't mention that a heredoc string wasn't terminated, or indeed anything about strings at all. If you get an error like the one above, chances are it's either heredoc that's at fault (or other string quoting problems) or you haven't matched up your braces properly.

19.12.3.7 0 is not a valid MySQL result index

The key to understanding this error is firstly remembering that PHP considers 0 to be the same as false, and secondly that mysqli_query() returns false if the query was invalid, e.g. "SELECT * FROM usertable WHERE ORDER BY Username" - the error is that there is an empty WHERE clause.

This error is usually caused by executing a fault SQL query, then trying to work with the result (e.g. call mysqli_fetch_assoc() or mysqli_num_rows() ) without first having checked whether the query call succeeded.

To solve this problem, look for the query that generated the result which is causing the error. Then output the SQL of that query to your screen so you can spot the error and fix it. You may find it easiest to copy the SQL into the MySQL monitor to get it working. Alternatively, put a the statement print mysqli_error() after the mysqli_query() line that causes the bad result.

19.12.3.8 SQL query errors

Debugging SQL is much more problematic than debugging PHP code because it is hard to tell where you went wrong - particularly if it is a complex query.

Having said that, MySQL and other DBMSs will try to make your life easy by helping out where they can - if there is a problem with the query you will usually get a message back helping you in the right direction. Take a look at these next two queries and their output, for example:

mysql> SELECT * FROM mbmsg;
ERROR 1146: Table 'phpdb.mbmsg' does not exist
mysql> INSERT INTO guestbook (GuestName, GuestEmail) VALUES (, 'foo@bar.com');
ERROR 1064: You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near ''foo@bar.com')' at line 1

The first error has a meaningful error message, and you can immediately see the problem - the mbmsg does not exist in the database phpdb. The second query is a little more tricky, though -we do not supply a value for the first parameter, which causes the error message you can see. The error message at least gets you close (although this cannot be relied upon), but debugging would be very difficult if you were working through a web browser.

Consider the following piece of code:

$result = mysqlo_query($db, "INSERT INTO guestbook (GuestName, GuestEmail) VALUES ({$_POST['GuestName']}, {$_POST['GuestEmail']});");

Now, what would happen if for some reason we did not get GuestName sent to us as we were hoping? Well, we would end up with the query that generated the confusing error, and we would not be able to make sense of it because as far as we can see GuestName should be in there. Compare this code:

$sql = "INSERT INTO guestbook (GuestName, GuestEmail) VALUES ({$_POST['GuestName']}, {$_POST['GuestEmail']});";
$result = mysqli_query($db, $sql);

If we find there is a problem, we can simply add echo $sql and see exactly what query is being tried out. In this situation, it is usually much easier to paste code into the MySQL monitor and try it out there - you can make changes to the SQL easily and retry it to figure out what the problem is. The monitor is also useful for testing out complex queries as it shows you immediately (and in a nicely formatted way) precisely what rows your query returned so that you can be sure it is doing what you intended it to do.

I hope you also noticed that the code was using $_POST data directly in an SQL query – why might that be a bad thing?

19.12.3.9 MySQL changes your data types

MySQL is programmed to automatically detect and optimise flawed table designs and may silently make table changes behind your back.

19.12.3.10 PHP crashes!

Occasionally you will see Apache crash while trying to serve a PHP script - this is rarely Apache's fault. Instead, it is more likely to be PHP's. If PHP crashes on you, do not think it is a bug with your script - it is not. PHP should not ever crash, and if it does it is a PHP problem, not a script problem. Yes, your script might be doing something really weird or in a roundabout way, but that does not mean it should crash PHP!

Despite coming on leaps and bounds with each release, PHP is not perfect, and may crash from time to time. The only thing you can do is to try the latest version of PHP, and if the bug still exists, submit a bug report to the PHP development team at http://bugs.php.net - that way, they can fix the problem, and also help out other programmers.

19.12.3.11 Can't connect to local MySQL server through socket '/tmp/mysql.sock'

This is a Unix-only problem, and usually means your MySQL server is not started or is not configured correctly. First, make sure your MySQL server is started by typing this:

ps aux | grep mysql

If you see mysqld in there somewhere, you are all go. If not, start your MySQL server (how this is done changes from distribution to distribution, but it is likely to be something like "/etc/init.d/mysql start".

Note carefully what the output of starting MySQL is - if you see any errors, make sure and try and resolve them. In a worse-case scenario you will see the server end a second or two after you start it - this is usually because you have not installed it properly, and the default authentication tables are not created. In this situation, it's probably easiest to use your package manager to reinstall.

In particularly odd cases, you may find that your MySQL socket is in an unusual place - that is, not where PHP would expect it to be. Try and locate your MySQL .sock file (it might be called mysql.sock or mysqld.sock), then edit your php.ini file and edit the line "mysql.default_socket" to point to the correct place.

19.12.3.12 I can't get transactions to work

In any databases that contain important information, transactions become crucial to the equation. However, the default table type in MySQL (known as MyISAM) does not support them at this time, so you need to use the InnoDB table type. Earlier we did this by using this command:

CREATE TABLE foo (ID INT) TYPE = InnoDB;

That forces the InnoDB table type. If you already have a MyISAM table and want to convert it to InnoDB, you can use this:

ALTER TABLE foo Type = InnoDB;

The problem is that you may not have the InnoDB table handler installed, which means that if you ask MySQL to use the InnoDB table handler it will silently failover to the MyISAM handler - it won't even log the error in your MySQL error log. If you find that transactions simply aren't working for you, chances are you're using the MyISAM handler.

To find out for sure what table handlers your tables are using, issue the command "SHOW TABLE STATUS". This prints out the names and table handlers for each of the tables in the current database - the second column is where it will say MyISAM or InnoDB. If you see "NULL" for your table type that means you had the InnoDB table handler installed at one time, created tables successfully with it, but no longer have the handler available so MySQL doesn't understand how to use the table.

If you are specifying InnoDB but the table ends up being MyISAM, it means you do not have the InnoDB table handler available. The InnoDB table handler is included as standard with all versions of MySQL, so unless you built your own version of MySQL (not recommended!), you should have it available. However, you may not have it configured to work - if you have a my.cnf file, it may have the line "skip-innodb" in under the [mysqld] header, which disables that handler. Remove this if you have it.

The skip-innodb option may also be passed directly to the MySQL server when it is started up, so check your startup scripts for this option and remove it if it's there. If you've done these two and are definitely using the correct query to create the table, you should be able to create and use InnoDB tables easily.

If you have InnoDB tables and just can't get transactions to work, that's a different matter entirely! Transactions have three stages: you begin the transaction, send your data, then either commit it or rollback. Here's an example transaction:

mysql> desc mytable;
+-------+----------+------+-----+---------+-------+
| Field | Type     | Null | Key | Default | Extra |
+-------+----------+------+-----+---------+-------+
| ID    | int(11)  | YES  |     | NULL    |       |
| Foo   | char(10) | YES  |     | NULL    |       |
| Bar   | char(20) | YES  |     | NULL    |       |
+-------+----------+------+-----+---------+-------+
3 rows in set (0.03 sec)

mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO mytable (ID, Foo, Bar) VALUES (1, 'Hello', 'World');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO mytable (ID, Foo, Bar) VALUES (2, 'Wom', 'Bat');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO mytable (ID, Foo, Bar) VALUES (2, 'PC', 'Format');
Query OK, 1 row affected (0.00 sec)

mysql> UPDATE mytable SET ID = 3 WHERE Foo = 'PC';
Query OK, 1 row affected (0.04 sec)
Rows matched: 1  Changed: 1  Warnings: 0

Note that I put the wrong ID in for the third query and corrected it with the fourth query - a transaction can contain all the usual types of queries. However, at this point nothing has actually been written to the table. You can confirm this by opening up another MySQL Monitor connection and running "SELECT * FROM mytable;". If you are indeed using InnoDB for your table handler, no rows should be returned from that query. Note that it is important to open up a new connection to the server because the connection that is actually performing the transaction does see the state of the table as if the transaction had been committed. This is necessary so that you can issue commands such as UPDATE on data that has yet to be committed.

The reason for that is because we have only completed two of the three phases of a transaction. What we have yet to do is decide whether to accept ("commit") or reject ("rollback") the transaction. For example, if we wanted to go ahead and write those three records, we'd need to send the commit "COMMIT;". That ends the transaction, and enacts all the changes you requested. With the transaction ended, you need to call BEGIN again to start a new one.

If you decide you don't want to write the changes, you can instead call "ROLLBACK;". This essentially aborts all the changes in that transaction, and also ends it. Rolling back your transaction is saved for when one of a batch of queries goes wrong and, as a result, you'd rather not have any queries saved.

19.12.3.13 "Fatal error: Call to a member function on a non-object" or "Notice: Trying to get property of non-object"

Both of these errors are caused by you treating a non-object variable - such as a string, an integer, or an array - as if it were an object. For example:

$foo = 1;
echo $foo->value;

While that code might work if $foo was an object that had a property called value, it is just a normal variable holding a number and so the error pops up. However, things are rarely that easy: if you assign a number directly to a variable, you'd be pretty crazy to then treat that variable as an object!

A more common scenario is when you expect a function to return an object, but it instead returns another type of variable, and you don't do any error checking. For example, running a query in PEAR::DB returns an object if the query was selecting data, but a simple value if you are inserting data. Similarly, calling simplexml_load_file() on a file that doesn't exist won't return a SimpleXML object with no nodes in. Instead, it will return a boolean value of false, which is clearly not an object and so will react badly to being used as such.

These kinds of problems are usually very to find, and even easier to solve: use some error checking!

19.12.3.14 Warning: session_start() [function.session-start]: open(/tmp\foobarbaz, O_RDWR) failed: No such file or directory (2)

This error is usually caused by the directive "session.save_path" being incorrectly set in our php.ini file. By default, session.save_path is set to /tmp, which should work fine for most Unix systems. On Windows, however, there is no directory /tmp by default, so you need to change this.

The easiest thing to do is to create a directory in your PHP installation directory called "sessiondata", then change your php.ini file so that session.save_path points to it, e.g.:

session.save_path = "c:/php/sessiondata"

If you still have problems, it is possible that the session directory has restrictive permissions on it - check the permissions on the folder, and make sure it is world readable and world writeable so that your PHP scripts can use it wherever they may be called from.

19.12.3.15 Notice: A session had already been started - ignoring session_start()

This error is self-explanatory - you have one session already started, and you tried to start another. However, there is a particularly situation where you may only be able to count one session_start() call, which is where people get confused. There is a directive in the php.ini file called session.auto_start, that when enabled will implicitly call session_start(). If your server has this setting enabled and you do not realise this, the chances are you will unknowingly call session_start() yourself and receive the above error.

If you have session.auto_start enabled (look for it in the output of phpinfo() ), simply stop calling session_start() yourself.

19.12.3.16 Warning: mail(): "sendmail_from" not set in php.ini or custom "From:" header missing

This one is fairly easy to clear up: open your php.ini file, and set the sendmail_from value to something good for a default for your site. Alternatively, add an extra parameter to your call to mail() that supplies the From header. This has already been discussed elsewhere: please go back and check the Networks chapter for more information.

19.12.3.17 Fatal error: Function name must be a string

This error usually rears its ugly head when you are using variable functions. If you use a variable function and the variable you are using is unset, this error will result - I often get this when I am typing faster than I can think and I accidentally hit $ before a function name. PHP naturally interprets this is a variable-function call, and tries to look up the function name inside the variable.

19.12.3.18 My script is doing something crazy!

If you are finding your scripts doing something particularly weird, the chances are it's a particularly silly coding error. The most popular example of this is mixing up include files for PEAR classes. For example, if you want to use PEAR::Mail you would include Mail.php. However, if you already created (and forget about) your own Mail.php in the same directory as the other script, PHP would include that original script instead of PEAR::Mail, leading to all sorts of mix-ups.

If you fall into this trap, relax - it happens to all of us!

19.12.3.19 And finally, the most common error...

Whether it is due to making a typo, a slip of the mind (often less delicately referred to as a "brain fart"), or just lack of understanding, the following code has been written numerous times:

if ($foo = 3) {
    // $foo is equal to 3, so do stuff here 
}

Of course, the author meant to use two equals signs there, as the current code assigns 3 to $foo rather than comparing 3 and $foo. Such a small error as this can generate huge headaches for programmers irrespective of their experience, so do not feel bad if you fall into this trap. Many people try to work around the problem by reversing their if statements when possible, like this:

if (3 == $foo) {
    // $foo is equal to 3, so do stuff here
}

It does exactly the same comparison, except that if they accidentally type one equals rather than two, PHP will issue an error rather than letting the mistake slide. The reason for this is because you cannot assign the value of $foo to the number 3 - 3 is always 3 no matter what. This is a safe route to take, however it obviously does not work when you have variables on both sides!

 

Want to learn PHP 7?

Hacking with PHP has been fully updated for PHP 7, and is now available as a downloadable PDF. Get over 1200 pages of hands-on PHP learning today!

If this was helpful, please take a moment to tell others about Hacking with PHP by tweeting about it!

Next chapter: Getting Help >>

Previous chapter: Choosing what types of errors you see

Jump to:

 

Home: Table of Contents

Copyright ©2015 Paul Hudson. Follow me: @twostraws.