PHP
PHP (PHP: Hypertext Preprocessor) is an open-source, dynamically typed scripting language built for the web. It was created by Rasmus Lerdorf in 1994-95 as a set of tools for his personal home page and grew into one of the most widely deployed server-side languages. WordPress, Drupal, Wikipedia's MediaWiki and large parts of Facebook's early stack are PHP, and modern frameworks such as Laravel and Symfony have made the language far more disciplined than its reputation suggests.
The examples assume a current PHP 8.x release. The newest features (8.4 and later) are marked where they appear; check php.net/supported-versions for which versions still get security fixes.
1. How PHP Runs
Unlike a long-running Node.js or Java server, classic PHP is shared-nothing: each HTTP request starts a fresh script, runs it top to bottom, sends the response and throws everything away.
sequenceDiagram
participant B as Browser
participant W as Web server (nginx or Apache)
participant F as PHP-FPM worker
participant D as Database
B->>W: GET /products.php?id=7
W->>F: Hand the request to PHP over FastCGI
F->>D: Prepared query for product 7
D-->>F: Row
F-->>W: Generated HTML
W-->>B: 200 OK
Note over F: Variables and objects are discarded when the request ends
- PHP-FPM keeps a pool of PHP worker processes; the web server hands requests to them.
- OPcache stores compiled bytecode in shared memory so scripts are not re-parsed on every request. Enable it in production.
- Consequence: state must live outside the script (database, cache, session store). That makes PHP easy to scale horizontally and hard to leak memory in, but it pays the start-up cost on every request.
- Long-running alternatives exist (Swoole, RoadRunner, FrankenPHP worker mode, Laravel Octane) that boot the application once and reuse it.
- PHP is interpreted at the language level; see Interpreters (the engine is the Zend Engine).
2. Basic Structure of a PHP Program
<?php
declare(strict_types=1);
echo "Hello, World!\n";
<?phpopens PHP mode. In files that contain only PHP, leave out the closing?>so no stray whitespace is sent.declare(strict_types=1);makes scalar type declarations strict for that file (astringwill no longer be silently accepted where anintis required). Put it at the top of every new file.- PHP files can mix HTML and PHP, which is how templates work:
<ul>
<?php foreach ($users as $user): ?>
<li><?= htmlspecialchars($user['name']) ?></li>
<?php endforeach; ?>
</ul>
<?= ... ?> is shorthand for <?php echo ... ?>.
Run a script or a quick dev server straight from the terminal:
$ php hello.php
$ php -S localhost:8000 -t public # built-in dev server (not for production)
$ php -r 'echo PHP_VERSION, "\n";'
$ php -a # interactive shell
3. Variables and Data Types
Variables start with $ and are case-sensitive; the convention is $camelCase or $snake_case (follow the project).
$name = "Alice"; // string
$age = 25; // int
$height = 1.68; // float
$isActive = true; // bool
$nothing = null; // null
$items = [1, 2, 3]; // array
| Type | Notes |
|---|---|
int, float |
64-bit integers on most platforms; large results overflow into float |
string |
Byte strings; use the mb_* functions for multi-byte text such as UTF-8 |
bool |
false, 0, "", "0", [] and null are falsy |
array |
An ordered map; serves as list and dictionary |
object, callable, iterable |
Class instances, anything callable, anything loopable |
null |
No value |
Strings
$greeting = "Hello, $name!"; // interpolation in double quotes
$expr = "Next year: {$age}y"; // braces for complex expressions
$raw = 'No $interpolation here\n'; // single quotes: literal
$joined = "Hello" . " " . "World"; // concatenation uses a dot
echo strlen($name), strtoupper($name), str_contains($name, "li");
echo sprintf("%s is %d years old", $name, $age);
Heredocs (<<<EOT ... EOT) give multi-line strings with interpolation; nowdocs (<<<'EOT') do not interpolate.
Arrays
PHP's array is one structure that acts as a list, a dictionary or both.
$list = ["apple", "banana", "cherry"];
$list[] = "date"; // append
$user = ["name" => "Alice", "age" => 25]; // associative
echo $user["name"];
echo $user["email"] ?? "no email"; // null coalescing: default when missing
[$first, $second] = $list; // destructuring
['name' => $n, 'age' => $a] = $user;
$squares = array_map(fn(int $x) => $x * $x, [1, 2, 3]); // [1, 4, 9]
$even = array_filter([1, 2, 3, 4], fn($x) => $x % 2 === 0);
$sum = array_sum([1, 2, 3]);
sort($list); // sorts in place
$merged = [...$list, ...["extra"]]; // spread
4. Operators and Control Flow
if ($age >= 18) {
echo "Adult";
} elseif ($age >= 13) {
echo "Teen";
} else {
echo "Child";
}
foreach ($user as $key => $value) {
echo "$key = $value\n";
}
for ($i = 0; $i < 3; $i++) { echo $i; }
while ($queue) { array_shift($queue); }
match (PHP 8.0+): a strict, expression-style switch
$label = match (true) {
$age >= 65 => "Senior",
$age >= 18 => "Adult",
default => "Minor",
};
match uses strict comparison (===), returns a value and throws UnhandledMatchError if nothing matches and there is no default.
Handy operators
| Operator | Meaning |
|---|---|
=== / !== |
Identical (same value and type); prefer these |
== / != |
Loose comparison with type juggling |
?? |
Null coalescing: $a ?? $b |
??= |
Assign if null |
?-> |
Nullsafe: $user?->address?->city |
<=> |
Spaceship: returns -1, 0 or 1 |
... |
Spread / variadic |
5. Functions
function greet(string $name, string $greeting = "Hello"): string
{
return "$greeting, $name!";
}
echo greet("Alice");
echo greet(greeting: "Hi", name: "Bob"); // named arguments (8.0+)
- Types on parameters and return values are optional but should be used.
void,never,?string(nullable),int|string(union, 8.0+) andmixedall exist. - Arrow functions capture the surrounding scope automatically:
fn($x) => $x * $factor. - Variadics:
function sum(int ...$nums): int { return array_sum($nums); } - First-class callables (8.1+):
$f = strlen(...);or$f = $object->method(...); - Functions do not see outer variables unless passed in or declared with
use:
$tax = 0.15;
$withTax = function (float $price) use ($tax): float {
return $price * (1 + $tax);
};
6. Object-Oriented PHP
<?php
declare(strict_types=1);
interface Shape
{
public function area(): float;
}
final class Circle implements Shape
{
public function __construct(private float $radius) {} // constructor promotion (8.0+)
public function area(): float
{
return M_PI * $this->radius ** 2;
}
}
$c = new Circle(2.0);
echo round($c->area(), 2); // 12.57
| Feature | Example / note |
|---|---|
| Visibility | public, protected, private |
| Constructor promotion | Declares and assigns properties in the constructor signature |
readonly (8.1+; whole classes in 8.2+) |
Property can be set once, then never changed |
abstract, final |
Force subclassing or forbid it |
static |
Shared members, called as Class::method() |
| Interfaces | Contract of methods a class promises to implement |
| Traits | Reusable method bundles included with use, PHP's answer to multiple inheritance |
| Enums (8.1+) | Fixed sets of cases, optionally with values and methods |
| Attributes (8.0+) | #[Route('/x')] metadata read by frameworks, replaces docblock annotations |
| Magic methods | __toString, __get, __call, __invoke |
| Property hooks and asymmetric visibility (8.4) | Define getter/setter logic on the property itself; for example public private(set) string $name |
enum Status: string
{
case Draft = 'draft';
case Published = 'published';
public function label(): string
{
return ucfirst($this->value);
}
}
$s = Status::from('draft'); // throws on an unknown value; tryFrom() returns null
echo $s->label(); // Draft
trait Timestamps
{
public ?string $createdAt = null;
public function touch(): void { $this->createdAt = date('c'); }
}
class Post { use Timestamps; }
7. Errors and Exceptions
PHP 7 and later throw Error and Exception objects, both of which implement Throwable.
try {
$result = intdiv(10, 0);
} catch (DivisionByZeroError $e) {
error_log($e->getMessage());
} catch (InvalidArgumentException | TypeError $e) {
// handle several types
} finally {
echo "always runs";
}
throw new InvalidArgumentException("Age cannot be negative");
- Configure errors in
php.ini:display_errors=Onin development,Offin production withlog_errors=Onso users never see stack traces. error_reporting(E_ALL)during development catches notices and deprecations early.
8. Namespaces, Autoloading and Composer
<?php
namespace App\Billing;
use App\Models\User;
use InvalidArgumentException;
final class Invoice { /* ... */ }
Composer is PHP's dependency manager (like npm or pip). Packages are listed on Packagist.
$ composer init
$ composer require guzzlehttp/guzzle # add a dependency
$ composer require --dev phpunit/phpunit # dev-only dependency
$ composer install # install exactly what composer.lock says
$ composer update # upgrade within the version constraints
$ composer dump-autoload # rebuild the class map
composer.json maps namespaces to folders (the PSR-4 standard) so classes load themselves:
{
"autoload": { "psr-4": { "App\\": "src/" } },
"require": { "php": ">=8.2" }
}
require __DIR__ . '/vendor/autoload.php'; // once, in the entry point
$invoice = new App\Billing\Invoice();
Commit composer.lock for applications so every machine installs identical versions.
9. Web Basics: Requests, Forms and Sessions
<?php
// POST /login handled by a script
$email = trim($_POST['email'] ?? '');
$query = $_GET['page'] ?? 1;
$agent = $_SERVER['HTTP_USER_AGENT'] ?? '';
session_start();
$_SESSION['user_id'] = 42;
header('Content-Type: application/json');
echo json_encode(['ok' => true, 'user' => $email]);
| Superglobal | Holds |
|---|---|
$_GET, $_POST |
Query string and form fields |
$_REQUEST |
GET, POST and cookies merged; avoid |
$_SERVER |
Headers and server details |
$_COOKIE, $_SESSION |
Cookies and server-side session data |
$_FILES |
Uploaded files |
Frameworks wrap these in request and response objects so you rarely read superglobals directly.
10. Databases with PDO
PDO is the standard, driver-independent database interface (MySQL, PostgreSQL, SQLite and others).
$pdo = new PDO(
'mysql:host=localhost;dbname=shop;charset=utf8mb4',
$user,
$pass,
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]
);
$stmt = $pdo->prepare('SELECT id, name FROM products WHERE price < :max AND active = :active');
$stmt->execute(['max' => 20, 'active' => 1]);
$products = $stmt->fetchAll();
Always use prepared statements with bound parameters. Concatenating user input into SQL is how SQL injection happens:
// WRONG: injectable
$pdo->query("SELECT * FROM users WHERE email = '$email'");
// RIGHT
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ?');
$stmt->execute([$email]);
Wrap multi-step changes in a transaction: $pdo->beginTransaction(); ... $pdo->commit(); and rollBack() in the catch.
11. Security Checklist
| Risk | Defence |
|---|---|
| SQL injection | PDO prepared statements |
| Cross-site scripting (XSS) | Escape output: htmlspecialchars($value, ENT_QUOTES, 'UTF-8'), or use a template engine that escapes by default (Twig, Blade) |
| Weak passwords | password_hash($pw, PASSWORD_DEFAULT) and password_verify(); never MD5 or SHA-1 |
| Session fixation | session_regenerate_id(true) after login; set cookie_httponly, cookie_secure, cookie_samesite |
| CSRF | Per-form tokens (frameworks provide middleware) |
| File uploads | Check the real MIME type, rename files, store outside the web root |
| Code execution | Never eval() or include a path built from user input; avoid unserialize() on untrusted data |
| Exposed errors and files | display_errors=Off, keep .env and vendor/ outside the web root, serve only public/ |
| Outdated runtime | Run a supported PHP version and update dependencies (composer audit) |
12. Quirks and Gotchas
- Loose comparison.
==converts types. PHP 8 fixed the worst case (0 == "abc"is nowfalse), but"1e3" == "1000"is stilltrue. Use===. - Inconsistent standard library naming.
strposvsstr_contains,array_map($fn, $arr)vsarray_filter($arr, $fn)(argument orders differ). A legacy of organic growth; keep the manual open (php.net/functionname). - Arrays are copied on assignment (copy-on-write), whereas objects are handled by reference-like handles.
- Needle and haystack order differs between functions, a classic trip-up.
- Everything is a string in
$_POST. Cast and validate input. nulland missing keys raise warnings in PHP 8; use??orisset().
13. Frameworks and Tooling
| Tool | Purpose |
|---|---|
| Laravel | Full-stack framework with routing, Eloquent ORM, queues, Blade templates; the most popular today |
| Symfony | Component-based framework and library set; many other projects (including Laravel and Drupal) build on its components |
| WordPress | CMS powering a large share of the web; plugins and themes are PHP |
| Slim | Micro framework for small APIs |
| PHPUnit / Pest | Testing |
| PHPStan / Psalm | Static analysis that catches type errors without running the code |
| PHP-CS-Fixer / PHP_CodeSniffer | Coding-style enforcement (PSR-12) |
| Xdebug | Step debugging and profiling |
See Frameworks for how these compare with Django, Rails and Spring.
$ composer create-project laravel/laravel shop
$ cd shop && php artisan serve
$ php artisan make:model Product -m # model plus migration
$ php artisan migrate
$ vendor/bin/phpunit
$ vendor/bin/phpstan analyse src --level=6
14. When to Choose PHP
| Strengths | Weaknesses |
|---|---|
| Built for the web: forms, sessions and HTML output are first-class | Standard library naming and argument order are inconsistent |
| Cheap, ubiquitous hosting; deploy by copying files | Shared-nothing start-up cost on each request (mitigated by OPcache and worker modes) |
| Mature ecosystem: Composer, Laravel, Symfony, WordPress | Legacy code and old tutorials teach unsafe habits |
| Modern language: types, enums, attributes, JIT, fibers | Not suited to CPU-heavy or long-running compute (use Go, Rust, Java) |
| Fast to write CRUD applications | Weaker concurrency story than Go or Node.js, though async libraries exist |
Good fit: content sites, WordPress or Drupal work, CRUD back offices, APIs for small and medium products, anywhere cheap hosting matters. Look elsewhere for: data science (Python), systems programming (Rust, Go), large-scale services with long-lived connections.
Compare with Ruby (Rails) and Python (Django): all three offer batteries-included web development, but PHP has by far the easiest hosting and the largest installed base.
15. Quick Reference
// Null and defaults
$v = $input ?? 'default';
// Array helpers
count($a); in_array($x, $a, true); array_keys($a); array_unique($a); usort($a, fn($x, $y) => $x <=> $y);
// Strings
trim($s); explode(',', $s); implode(',', $a); str_replace('a', 'b', $s); substr($s, 0, 5);
// Dates (prefer DateTimeImmutable)
$d = new DateTimeImmutable('2026-01-31');
echo $d->modify('+1 month')->format('Y-m-d');
// JSON
$data = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
$json = json_encode($data, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR);
// Files
$text = file_get_contents('notes.txt');
file_put_contents('out.txt', $text, LOCK_EX);