The summary.
PARAMETERISE EVERY QUERY
Without exception. Dynamic column and table names cannot be parameterised, so validate those against a fixed list.
ESCAPE EVERYTHING DISPLAYED
Content in markup, attributes, URLs and scripts each need different escaping.
Never trust values from your database — they were once input.
NEVER DISABLE CROSS-SITE REQUEST PROTECTION TO MAKE SOMETHING WORK
And never let a retrieval change state, or a link or image can trigger it.
THE CHECK THAT PREVENTS THE MOST DATA EXPOSURE
Verifying the signed-in user is entitled to the record they named.
Hidden fields, disabled fields and identifiers in the page are all under the visitor's control.
FOR PASSWORDS
Use PHP's built-in password hashing. General-purpose hashing is designed to be fast, which is exactly wrong.
Never limit length or restrict characters.
FOR UPLOADS
Determine type from content, generate your own filename, and store outside any directory that executes code.
KEEP CONFIGURATION OUT OF THE WEB ROOT, AND TRY REQUESTING IT
If a secret was ever committed, rotate it. Removing it from history does not help.
IN PRODUCTION
Errors logged, never displayed. Debug mode off, always.