Important PHP Concepts to know
PHP (Hypertext Preprocessor) is a powerful C-like scripting language that sometimes suffers from a bad reputation due to its legacy.
While most constructive critics have some points with PHP 4 or 5, PHP 7 and PHP 8 are a new done.
The language has now strong types, syntactic sugars and many other features you will love, especially if you come from other programming languages.
PHP is an interpreted language
Unlike some languages, PHP is not self-contained. Your code will need in addition a third-party webserver (e.g., Apache, Nginx), a compiler and the PHP interpreter to work.
The interpreter reads your .php
file, parses it, executes it, and then send the output to the webserver.
Then, the server sends the output in the HTTP response to the browser (the client).
PHP extensions and reusable functions
PHP works with extensions mostly written in C but some are third party libraries simply written in PHP.
Some compile with the PHP binary and some are optional and must be included in the php.ini
file where you write directives for PHP.
Lifecycle: Lexing
Most of your php code turns into a sequence of tokens (tokenizing), but special symbols such as ?
, =
, or ;
are already tokenized.
The lexer generates lexemes which are a mix of numbers and letters representing the tokens and matching values.
Lifecycle: Parsing / AST
The parser takes the streams of tokens generated with the lexer as input.
It applies grammar rules to validate these tokens and generates an abstract tree (AST) using the php-ast extension.
Lifecycle: Compilation / opcodes
Compilation is the act of consuming the AST. It generates opcodes by traversing the AST and run a few internal optimizations.
These opcodes are not unreadable but it's significantly different from the original source code with, for example, the results of intermediary operations such as true
or false
for a condition like $a === $b
.
Lifecycle: Opcache
Opcache can be enabled to cache opcodes, saving the results of previous operations and optimizing instructions to improve performances.
Lifecycle: Interpretation
The Zend Engine (VM) takes opcodes as input to output the results and execute instructions.
Lifecycle: PHP 8 JIT compilation / opcache
The Just-In-Time compilation works with Opcache (that stores precompiled sequences in memory to bypass parsing and interpretation) and improves the performances of CPU-intensive applications (e.g. Machine Learning, complexe mathematical operations, 3D, etc).
JIT must be fine-tuned and does not fit all projects.
Composer
Composer is the package manager for PHP. It's a smart way to handle and load dependencies.
Data types in PHP 8
Integers
Numbers such as 1, 2, 3, 4, 5, 6, ..., n
Booleans
true
or false
Strings
Sequences of chars such as "Pink Elephants"
Doubles
Floats such as 38.2
Arrays
Collections of values such as ["a","b","c",]
Objects
Instances of PHP classes that can carry lots of functions and values.
Resources
Special references to external resources like remote connections (e.g., databases, cloud services).
NULL
Special type with a unique value of NULL.
Mixed
Anything!
Iterable
Array or traversable
Common Escape Characters
\n
Line
\t
Horizontal tabulation
\v
Vertical tabulation
\r
Carriage return
\e
Escape
Basic PHP Syntax
Always use canonical tags
Opening and ending tags:
<?php
// my_script.php
?>
Commenting code
<?php
// a comment using "//", you can also use "#"
/*
Handle multiple lines
with '/'
and '*'
*/
?>
PHP is case sensitive
$lower
is not the same variable as $Lower
.
Never omit the semi-colon
Unlike with CSS or JavaScript, you cannot skip the ;
at the end of expressions and statements:
<?php
echo 'test';
?>
Indentation is for readability only
Unlike Python, PHP does not care about indents and whitespaces.
Simple quotes
<?php
echo 'test';
?>
Double quotes
Double quotes allows variable interpolation and the use of escaped chars:
<?php
$my_var = 'test';
echo "\t\t\t This is a $my_var \n";
?>
Predefined variables
Also known as superglobals, predefined variables are useful to handle specific treatments:
$GLOBALS
References all variables available in global scope.
$_REQUEST
Associative array of variables passed to $_GET
, $_POST
and $_COOKIE
.
$_POST
Associative array of variables passed to the current script mostly with forms (application/x-www-form-urlencoded
or multipart/form-data
HTTP Content-Type)
$_COOKIE
Associative array of variables passed to the current script via HTTP Cookies.
$_GET
Associative array of variables passed to the current script via the URL parameters.
$_SERVER
Server and execution environment information.
Magic constants
__DIR__
Directory of the current file.
__FILE__
Full path of the current file.
__LINE__
Number of the current line in the file.
__CLASS__
Name of the current class, including its namespace.
__FUNCTION__
Name of the current function.
__METHOD__
Name of the current method.
__NAMESPACE__
Name of the current namespace.
__TRAIT__
Name of the current trait.
Loop, loop, loop
for
<?php
for ($i = 0; $i < 23; $i++) {
echo $i;
}
?>
foreach
<?php
$letters = ["a", "b", "c",]
foreach ($letters as $letter) {
echo "$letter \n";
}
?>
while
Be careful, if the condition is malformed, you may end up with an infinite loop:
<?php
$j = 1;
while($j <= 5) {
echo "$j \n";
$j++;
}
?>
do while
You use it when a test is dependant upon the results of the loop:
<?php
$i = 0;
do {
echo $i;
} while ($i > 0);
?>
Operators
Assignment
Assignment by value =
<?php
$a = 110;
$a += 1;
$b = "Hello ";
$b .= "World";
?>
Assignment by reference &
<?php
$a = 111;
$b = &$a; // $b and $a point to the same data, there's no copy
?>
Arithmetic
+
Addition
-
Substraction
*
Multiplication
/
Division
**
Exponentiation
%
Modulo (reminder of a division)
Logical
AND
And
&&
And
OR
Or
||
Or
!
Not
xor
$a xor $b
means $a
or $b
but not both.
Comparison
===
Identical
!==
Not identical
<>
Not equal
<
Lower than
>
Greater than
<=
Lower than or equal to
>=
Equal to or greater than
<=>
Lower than, equal to, or greater than
Unusual or lesser known
|
or, but inclusive
^
xor
~
not
...
Spread operator to merge elements
<?php
$arr = [1, 2, 3];
$arr2 = [...$arr, 4, 5, 6];
?>
_
<?php
$price = 1_1_1;
echo $price; // 111
?>
Conditions
Classic conditions
<?php
if (CONDITION1) {
} elseif (CONDITION2) {
} else {
}
?>
Switch case
<?php
switch ($n) {
case 1:
$r = "You are alone";
break;
case 2:
$r = "It's a double situation.";
break;
case 3:
$r = "It's the third case.";
break;
default:
$r = "I don't know.";
}
echo $r;
?>
Null Coalescing operator (@since PHP 7)
<?php
$search = $_GET['search'] ?? 'does not exist or is null';
?>
Match expression (@since PHP 8)
<?php
echo match ($n) {
1 => "You are alone",
2 => "It's a double situation.",
3 => "It's the third case.",
default => "I don't know.",
};?>
Ternary condition
<?php
$a = (expression1) ? expression2 : expression3;
?>
Elvis condition (or operator)
<?php
$a = (expression1) ?: expression2; // I never use it, but it exists...
// the same as $a = (expression1) ? expression1 : expression2;
?>
Scopes
global, local and static:
<?php
function test() {
$b = "b"; // local
global $c;
$c = "c"; // global
static $d = 0;
$d++;// $d increment on each function call
}
?>
Date and time formats
D
Days Mon to Sun
d
Days 01 to 31
j
Days 1 to 31
L
Leap year or not (1 or 0)
l
Days Sunday to Saturday
N
Days 1 (Mon) to 7 (Sat)
w
Days 0 (Sun) to 6 (Sat)
M
Months Jan to Dec
m
Months 01 to 12
n
Months 1 to 12
F
Months January to December
Y
Year four digits (e.g., 2022)
y
Year two digits (e.g., 22)
A
AM and PM
a
am and pm
G
Hours 0 to 23
g
Hours 1 to 12
H
Hours 00 to 23
h
Hours 01 to 12
i
Minutes 00 to 59
s
Seconds 00 to 59
See documentation for the complete list.
Filters
Filters flags and constants are particularly helpful to validate or sanitize inputs. For example, instead of using far-fetched regex to filter emails, you can do:
$email = "elon.musk@tesla.com";
if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
// process
}
See all filters.
Advanced PHP syntaxes
Nowdoc and Heredoc syntaxes allows handling multiple lines in a very convenient way.
Heredoc
<?php
echo <<<EOT
Hello, this is a test
Thank you for your time
EOT;
?>
Nowdoc
<?php
$my_var = 'test';
echo <<<EOT
Hello, this is a $my_var
Thank you for your time
EOT;
?>
Working with arrays
Arrays are not specific to PHP, but a PHP developer uses them all the time.
You cannot skip it if you want to learn the language.
Working with Generators
Getting started with files
Reading a file as a string
<?php
echo file_get_contents($file);
?>
Writing in a file
<?php
$file = 'file.txt';
$text = "Test write\n";
file_put_contents($file, $text, FILE_APPEND | LOCK_EX);
?>
Deleting a file
unlink($file);
Working with CSV
<?php
$row = 1;
if (($handle = fopen("test.csv", "r")) !== FALSE) {
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
$num = count($data);
echo "<p> $num fields in line $row: <br /></p>\n";
$row++;
for ($c=0; $c < $num; $c++) {
echo $data[$c] . "<br />\n";
}
}
fclose($handle);
}
?>
Getting started with forms
Forms are probably the most targeted elements by hackers, especially on PHP-based websites, as they're often connected to other instances such as a SQL database.
Websites behind paywalls with various forms are primary targets.
In PHP, data often changes according to URLs (GET requests) or HTTP POST requests. For example, a search form will likely work with URL parameters, whereas a login form will send a POST request.
As a result, form processing may involve super globals $_GET
and $_POST
to catch input values.
See this guide
Getting started with databases
You will likely use a database along with PHP. Regardless of the database management system, you need to use the right PHP tools to connect and interact with the database from your PHP script.
Otherwise, your application might be prone to SQL injections.
Getting started with PDO
<?php
try {
$pdo = new PDO("mysql:host=localhost;dbname=db_username;charset=utf8mb4", "db_username", "db_password");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
if ($pdo) {
$statement = $pdo->query("SELECT some_field FROM some_table");
$row = $statement->fetch(PDO::FETCH_ASSOC);
echo htmlentities($row['some_field']);
}
} catch(\PDOException $e) {
throw new \PDOException($e->getMessage(), (int)$e->getCode());
}
?>
Securing inputs with PDO
PDO is pretty handy to escape and sanitize inputs:
<?php
$q = $pdo->prepare('SELECT name FROM users WHERE id = :id');
$id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT);
$q->bindParam(':id', $id, PDO::PARAM_INT);
$q->execute();
?>
Source: PHP The Right Way - PDO
PHP CLI
You can run PHP from the terminal in various ways:
Executing .php
files
php my_script.php
Starting an interactive shell
php -a
Interactive shell
php > echo "hi";
hi
php >
Passing instructions as arguments
php -r 'echo "hi";'
You can write PHP scripts that are meant to be used from the terminal:
Passing arguments to PHP scripts
php my_script.php "test"
<?php
// my_script.php
print_r($argv[1]);// test
?>
Start a local server
php -S localhost:1001
New in PHP 8
To be fair, PHP 7.4 initiated major evolutions such as typed properties, but PHP 8 puts PHP to the next level.
Be careful if you have to migrate from a previous version, though, as it's much much stricter than before. PHP 8 throws way more fatal errors on purpose to be less permissive. The silence operator @
no longer exists and the default error_reporting level is E_ALL
!
Here are cool new features I like, see release announcement for all details:
Named Parameters
Allows for skipping optional parameters and much readable than positional arguments when using functions:
<?php
function say_hello(string $message = 'Hi', string $name) {
echo "$message $name";
}
say_hello(name: "Zed");
?>
Null-safe Operator
Allows for removing LOTS of unnecessary verbose if/else
conditions:
<?php
return $user->getJob()?->getSalary()?->bitcoins;
?>
Union Types
Allows using multiple types when casting parameters:
<?php
function convert(int|float $value): int|float {}
?>
Constructor property promotion
Allows for skipping LOTS of verbose initializations in PHP classes when passing arguments:
<?php
class Character {
public function __construct(private string $name) {}
}
?>
0 == 'ki' // false
This condition returns true
before PHP 8, which is permissive.
The JIT compiler
See the first section: Important PHP Concepts
str_contains()
<?php
if (str_contains($string, 'target')) {
// code
}
?>
New in PHP 8.1
Here are cool new features I like, see release announcement for all details:
Enumerations
An Enum is a special kind of object that consists of a custom type that is limited to one or a discrete number of possible values:
<?php
enum Colors
{
case Red;
case Blue;
case Green;
}
?>
Array unpacking support for string-keyed arrays
You can use the spread operator with string-keyed arrays (only num-keyed arrays in PHP 7, 8):
<?php
$arr1 = ['a' => 1];
$arr2 = ['b' => 2];
$results = ['a' => 0, ...$arr1, ...$arr2];
print_r($results);
?>
Never return type
The Never return type allows for specifying that the functions won't return any value and will likely die or exit:
<?php
function redirect_uri(string $uri): never {
header('Location: ' . $uri);
exit();
}
?>
Pure Intersection Types (&
)
Pure Intersection Types are a great way to force multiple constraints:
<?php
function count_and_iterate(Iterator&Countable $value) {
foreach ($value as $val) {
echo $val;
}
count($value);
}
?>
Catching errors in PHP 8
In PHP 8, you don't have to capture the exception, for example:
class CustomException extends \Exception {}
try {
// code
} catch (CustomException) { // instead of catch (CustomException $exception)
Log::error("Error");// Log is a custom class too (not native)
}
It should be noted that PHP now throw exceptions frequently:
Most of the internal functions now throw an Error exception if the validation of the parameters fails.
Misc
Destructuring
<?php
$array = ['first', 'second', 'third',];
[, , $c] = $array;
// $c = 'third'
?>
Arrow functions
<?php
$y = 111;
$f = fn($x) => $x + $y;
echo $f(222);// 333
?>
Throw expressions
<?php
$err = fn () => throw new CustomErrors();
?>
::class
Lovely:
<?php
$my_class = new MyClass();
var_dump($my_class::class);// instead of using get_class()
?>
Trailing comma in function or class definition
<?php
public static function(
string $name,
int $number,
) {
// code
}
?>
PSR
PSR are standards for writing PHP code. You might have already seen PSR-0
or PSR-4
, especially when autoloading dependencies through Composer. These are specific versions of PHP standards.
OOP
Object-oriented programming is fun and powerful, but hard to master. The good news is that you find it in most programming languages, so learning it is a pretty good investment.
Some developers think it's the only proper way to develop software, some stick with procedural code. In any case, you can't skip it.
Caching
My favorite caching solutions for PHP
Debugging and profiling
console.log PHP data
<?php
function console_log( $data ){
echo '<script>';
echo 'console.log('. json_encode( $data ) .')';
echo '</script>';
}
?>
xdebug
Helpers
-
print_r()
: accepts one parameter -
var_dump()
: accepts multiple parameters -
var_dump(debug_backtrace())
: generates a backtrace -
debug_print_backtrace()
: prints a backtrace
Profilers
Applications may require profiling tools to prevent memory leaks and other performances issues:
Tests, tests, and tests
Professionals run unit, integration, acceptance tests, and many other variants to prevent nasty regressions: PHPunit
Handling HTTP requests
There are many ways but I prefer using the http-client package: composer require symfony/http-client
.
This low-level HTTP client with support for both PHP stream wrappers and cURL. You can fetch data synchronously OR asynchronously.