Here's some tips/snippets for PHP7 (knowledge of PHP5 required).
Please install Github Mermaid extension for Firefox/Chrome to display the diagrams in this document.
-
print_r()
; -
implode(', ', $myArray)
; -
return array_unique($myArray);
; -
base_convert($number, $fromBase, $toBase)
returns a string; -
unlink($file)
returns true if the$file
has been deleted; -
move_uploaded_file($_FILES["photo"]["tmp_name"], "upload/" . $filename);
to move an upload file from its temporary state on the server; -
filter_var($field, FILTER_{SANITIZE_STRING,SANITIZE_EMAIL,VALIDATE_EMAIL})
is a very useful dummy tester. You also can try:$field = filter_var(trim($field), FILTER_SANITIZE_STRING); $okField = (filter_var($field, FILTER_VALIDATE_REGEXP, array("options"=>array("regexp" => "/^[a-zA-Z\s]+$/")))) ? $field : FALSE;
-
$_REQUEST
is a superglobal variable containing the values of other global variables; -
you can set your own custom error handler with
set_error_handler()
; REMINDER:trigger_error()
throws the error; -
class MyNewException extends Exception {}
; -
mysqli_real_escape_string()
to provide security against injections.
<=>
is a combined comparison operator, returns 0 if both operands are equal, 1 if the left is greater:
echo 1 <=> 1; // 0
echo 1.5 <=> 2.5; // -1
echo "y" <=> "x"; // 1
It's a shorthand where you need to use a ternary operator in conjunction with isset()
function:
$name = isset($_GET['name']) ? $_GET['name'] : 'anonymous';
$name = $_GET['name'] ?? 'anonymous';
REMINDER: here's the syntax:
foreach($colors as $value){ {...} }
foreach($superhero as $key => $value) { {...} }
function selfMultiply(&$number){
$number *= $number;
return $number;
}
It's recommended to use the require()
statement if you're including the library files of files containing the functions and configuration variables that are essential for running your application, such as database configuration file.
class MyClass
{
public function __construct(){
echo __CLASS__ . " was initiated!<br>";
}
public function __destruct(){
echo __CLASS__ . " was destroyed!<br>";
}
}
$obj = new MyClass;
unset($obj);
class HelloClass
{
public static $greeting = "Yo!";
public static function sayHello(){
echo self::$greeting;
}
}
echo HelloClass::$greeting; // Strict Warning
$hello->sayHello(); // Yo!
NOTE: PDO supports named placeholders (not MySQLi).
$sql = "INSERT INTO persons (first_name, last_name, email) VALUES (?, ?, ?)";
if ($stmt = mysqli_prepare($link, $sql)){
/* sss = string, string, string; type definition also accepts:
* b: binary
* d: double
* i: integer */
mysqli_stmt_bind_param($stmt, "sss", $first_name, $last_name, $email);
$first_name = "Hermione";
$last_name = "Granger";
$email = "[email protected]";
mysqli_stmt_execute($stmt);
} else {
echo "ERROR: Could not prepare query: $sql . " . mysqli_error($link);
}
mysqli_stmt_close($stmt);
mysqli_close($link);
config.php
:- database heuristics;
- open a connection with
mysqli_connect()
(with a die onfalse
$link
with amysqli_connect_error()
stack);
- index.php: landing page with using Bootstrap:
require_once "config.php"
(this line must be at the beginning of each CRUD files);- a
.page-header
with a.btn
link tocreate.php
; - a table from:
mysqli_query($link, "SELECT * FROM employees");
(otherwise display an errormysqli_error($link)
);- and
mysqli_num_rows($result)
not zero; - using
mysqli_fetch_array($result)
to create the cells of a row while there's data; - at the end of each row, three icons like
"<a href='RUD.php?id=".$row['id']."' data-toggle='tooltip'>"
whereRUD
isread
,update
,delete
respectively; - at the end of the table, IMPORTANT: don't forget to free the result set
mysqli_free_result($result)
;
- and finally, a
mysqli_close($link)
(this line must be at the end of each CRUD files).
create.php
:- start the processing form data with
if ($_SERVER["REQUEST_METHOD"] == "POST")
:- open connection;
- for each input:
- trim data string from
$_POST
; - set error variable if
empty()
; - (optional) set error variable if not valid input (check with
filter_var()
orctype_digit()
); - set variable from input if valid.
- trim data string from
- if not error variable detected:
- prepare statement for
INSERT INTO
; - bind variables to the statement and set the parameters;
- attempt to execute the statement and
header("location: index.php");exit();
to redirect to the landing page on success; - close the statement.
- prepare statement for
- close connection;
- in the HTML code, create a
<form action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>" method="POST">
and for each<input>
or<textarea>
:.form-group
;<label>
;<input>
with.form-control
;<span>
with the error variable.
- add a
Cancel
link toindex.php
after the<input type="submit">
.
- start the processing form data with
- the RUD files called from each row in the table in
index.php
and the row index is passed in the URL (GET method):read.php
:header("location: error.php");exit();
if the row index in$_GET["id"]
is empty;- trim the index;
- open connection (
require_once "config.php";
); - prepare statement
SELECT * FROM employees WHERE id = ?
; - bind variables to the statement and set the parameters;
- attempt to execute the statement;
- retrieve field value if
mysqli_num_rows($result) == 1
(otherwiseerror.php
redirect); - close the statement;
- close the connection;
- in the HTML code, create a simple form to display the fields:
.form-group
;<label>
with the name of the column;.form-control-static
with the field value.
- at the end of the form, add a Back
.btn
to aimindex.php
.
update.php
(inspired byindex.php
andread.php
):header("location: error.php");exit();
if the row index in$_{POST,GET}["id"]
is empty;- POST case:
- open connection (
require_once "config.php";
); - for each input:
- set error variable if
empty()
; - (optional) set error variable if not valid input (check with
filter_var()
orctype_digit()
); - set variable from input if valid.
- set error variable if
- if not error variable detected:
- prepare statement for
UPDATE employees SET name=?, address=?, salary=? WHERE id=?
; - bind variables to the statement (in a
sssi
pattern) and set the parameters; - attempt to execute the statement and
header("location: index.php");exit();
to redirect to the landing page on success; - close the statement.
- prepare statement for
- open connection (
- close the connection.
- GET case:
- trim the index;
- open connection (
require_once "config.php";
); - prepare statement
SELECT * FROM employees WHERE id = ?
; - bind variables to the statement and set the parameters;
- attempt to execute the statement;
- retrieve field value if
mysqli_num_rows($result) == 1
(otherwiseerror.php
redirect); - close the statement;
- close connection.
- in the HTML code, create a
<form action="<?php echo htmlspecialchars(basename($_SERVER['REQUEST_URI']));?>" method="POST">
and for each<input>
or<textarea>
:.form-group
;<label>
;<input>
with.form-control
;<span>
with the error variable.
- add a
Cancel
link toindex.php
after the<input type="submit">
.
delete.php
(inspired bycreate.php
but GET catchid
to pass unto the POST'd form via a hidden input):header("location: error.php");exit();
if the row index in$_{POST|GET}["id"]
is empty;- open connection (
require_once "config.php";
); - prepare statement
DELETE FROM employees WHERE id = ?
; - bind variables to the statement and set the parameters (BEWARE:
i
for id); - attempt to execute the statement;
- in the HTML code:
- create a
<form action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>" method="POST">
:<div class="alert alert-danger fade in">
;<input type="hidden" name="id" value"<?php echo trim($_GET["id"]);?>"/>
;.btn
toindex.php
as No or<input type="submit">
as Yes.
- create a
error.php
: just a go back toindex.php
.
REMEMBER: open the connection with require_once(config.php)
AND manually close it in each page of the CRUD application.
NOTE: no need to prepare a SQL statement in index.php
as the SELECT *
as no variables. Since the result set may contain only one row, we don't need a while loop but a mysqli_fetch_array($result, MYSQLI_ASSOC)
.
config.php
;register.php
:- start the processing form data with
if ($_SERVER["REQUEST_METHOD"] == "POST")
:- open connection (
require_once "config.php"
); - for
username
:- trim data string from
$_POST
; - set error variable if
empty()
; - in a
SELECT
statement,mysqli_stmt_store_result($stmt)
and check if the row exists:- set error variable if
1
; - set variable from input if valid.
- set error variable if
- trim data string from
- for
password
andconfirm_password
:- trim data strings from
$_POST
; - set error variable if
password
isempty()
or too short; - set error variable if
confirm_password
is:empty()
or;- different from
password
when nopassword
error;
- trim data strings from
- if not error variable detected:
- prepare statement for
INSERT INTO
; - bind variables to the statement and set the parameters: BEWARE: use the
password_hash($password, PASSWORD_DEFAULT)
to create a password hash; - attempt to execute the statement and
header("location: login.php")
to redirect to the login page on success; - close the statement.
- prepare statement for
- close connection.
- open connection (
- in the HTML code, create a
<form action"<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>" method="POST">
and for each<input>
s (ieusername
,password
andconfirm_password
):.form-group
;<label>
;<input>
with.form-control
;<span>
with the error variable.
- add a link to
login.php
after the<input type="{submit|reset}">
.
- start the processing form data with
login.php
:session_start()
;header("location: welcome.php");exit;
if the user is already logged in ieisset($_SESSION["loggedin"]) && $_SESSION["loggedin"] === true)
;- start the processing form data with
if ($_SERVER["REQUEST_METHOD"] == "POST")
:- open connection (
require_once "config.php"
); - for each input:
- trim data string from
$_POST
; - set error variable if
empty()
; - set variable from input if valid.
- trim data string from
- if not error variable detected:
- prepare statement for
SELECT id, username, password FROM users WHERE username = ?
; - bind variables to the statement and set the parameters (
$hashed_password
is bound to thepassword
from the table); - attempt to execute the statement; here's 3 cases:
- "No account found" as
username
error if notmysqli_stmt_fetch($stmt)
; - "No valid password" as
password
error ifpassword_verify($password, $hashed_password)
; - on success:
session_start()
;$_SESSION["loggedin"] = true
;$_SESSION["id"] = $id
;- `$_SESSION["username"] = $username;
header("location: welcome.php")
.
- "No account found" as
- close the statement.
- prepare statement for
- close connection.
- open connection (
- in the HTML code, create a
<form action"<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>" method="POST">
and for each<input>
s (ieusername
andpassword
):.form-group
;<label>
;<input>
with.form-control
;<span>
with the error variable.
- add a link to
register.php
after the<input type="submit">
.
welcome.php
(dummy hello page):session_start();
header("location: login.php");exit;
if(!isset($_SESSION["loggedin"]) || $_SESSION["loggedin"] !== true)
;- in the HTML code:
- use
echo htmlspecialchars($_SESSION["username"])
to print the logged user's name; - set a link to
reset-password.php
; - set a link to
logout.php
.
- use
logout.php
(to kill the session and redirect to thelogin.php
page):session_start()
- reset all the variables with
$_SESSION = array()
; session_destroy()
;header("location: login.php");exit;
.
reset-password.php
:session_start()
;header("location: login.php");exit;
if(!isset($_SESSION["loggedin"]) || $_SESSION["loggedin"] !== true)
;- start the processing form data with
if ($_SERVER["REQUEST_METHOD"] == "POST")
:- open connection (
require_once "config.php"
); - for
new_password
andconfirm_password
:- trim data string from
$_POST
; - set error variable if
new_password
isempty()
or too short; - set error variable if
confirm_password
is:empty()
or;- different from
new_password
when nonew_password
error;
- set variable from input if valid.
- trim data string from
- if not error variable detected:
- prepare statement for
UPDATE users SET password = ? WHERE id = ?
; - bind variables to the statement and set the parameters: BEWARE: use the
password_hash($new_password, PASSWORD_DEFAULT)
to create a password hash and use the session id as id ie$_SESSION["id"]
; - attempt to execute the statement, on success:
session_destroy()
;header("location: login.php");exit()
to redirect to the login page.
- close the statement.
- prepare statement for
- close connection.
- open connection (
- in the HTML code, create a
<form action"<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>" method="POST">
and for each<input>
s (ienew_password
andconfirm_password
):.form-group
;<label>
;<input>
with.form-control
;<span>
with the error variable.
- add a link to
welcome.php
after the<input type="submit">
.
Use session_start();
at the beginning of the files you want to make private (welcome.php
, reset_password.php
...); don't use it on public pages like register.php
except login.php
to redirect to the welcome.php
page if logged in (so you need to be able to check the $_SESSION["loggedin"]
status); NOTE: the session variables are set when the login.php
POST
processing is a success (account found, valid password).
Let's see what's new in a CRUD application or a Login system if we use the mysqli
OO interface:
mysqli_connect()
:new mysqli
as$mysqli
;mysqli_connect_error()
:$mysqli->connect_error
;mysqli_query()
:$mysqli->query()
as$result
;mysqli_num_rows()
:$result->num_rows
;mysqli_fetch_array($result, MYSQLI_ASSOC)
:$result->fetch_array(MYSQLI_ASSOC)
;mysqli_free_result()
:$result->free()
;mysqli_error()
:$mysqli->error
;mysqli_close()
:$mysqli->close()
;mysqli_prepare()
:$mysqli->prepare()
as$stmt
;mysqli_stmt_bind_param()
:$stmt->bind_param()
;mysqli_stmt_execute()
:$stmt->execute()
;mysqli_stmt_store_result()
:$stmt->store_result()
;mysqli_stmt_get_result()
:$stmt->get_result()
as$result
;mysqli_stmt_close()
:$stmt->close()
.
Let's see what's new in our previous examples when you use PDO. First, here's the new config file:
<?php
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'root');
define('DB_PASSWORD', 'root');
define('DB_NAME', 'crud_demo'); // or whatever
/* Attempt to connect to MySQL database */
try{
$pdo = new PDO("mysql:host=" . DB_SERVER . ";dbname=" . DB_NAME, DB_USERNAME, DB_PASSWORD);
// Set the PDO error mode to exception
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e){
die("ERROR: Could not connect. " . $e->getMessage());
}
?>
Here's what changes in the core of the program with our $pdo
instance (compared to the OO format):
$mysqli->query()
as$result
:$pdo->query()
as $result;$result->num_rows
:$result->rowCount()
;$result->fetch_array(MYSQLI_ASSOC)
:$result->fetch(PDO::FETCH_ASSOC)
;$result->free()
:unset($result)
;$mysqli->error
: use acatch(PDOException $e)
after atry
block;$mysqli->close()
:unset($pdo)
;$mysqli->prepare()
as$stmt
:$pdo->prepare()
as$stmt
;$stmt->bind_param()
:$stmt->bindParam(":name", $param_name)
; BEWARE::
format as a prepared statement use:name
instead of a?
;$stmt->execute()
:$stmt->execute()
;$stmt->store_result()
: OBSOLETE: there's absolutely no point in using neitherstore_result()
norbind_result()
with PDO;$stmt->get_result()
as$result
: OBSOLETE: you get the$stmt->rowCount()
directly after$stmt_execute()
;$stmt->close()
:unset($stmt)
.
Digest by Martial BONIOU, 2021