PHP Cheatsheet
OOP and Modern PHP
Use this PHP reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
Classes
final class User { public function __construct( public readonly string $email, private string $passwordHash, ) {} public function verify(string $password): bool { return password_verify($password, $this->passwordHash); } }
Constructor property promotion creates and assigns properties directly from constructor parameters. readonly prevents reassignment after initialization.
Interfaces and Inheritance
interface Mailer { public const MAX_RETRIES = 3; // interface constants public function send(string $to, string $body): void; } final class SmtpMailer implements Mailer { public function send(string $to, string $body): void {} } echo Mailer::MAX_RETRIES;
Prefer interfaces for dependencies. Use final when a class is not designed for extension. Interface constants are inherited by implementing classes.
Abstract Classes
abstract class Report { abstract protected function rows(): array; // subclasses must implement public function render(): string { // shared behavior return implode("\n", array_map(fn($r) => implode(',', $r), $this->rows())); } } final class SalesReport extends Report { protected function rows(): array { return [['q1', 100]]; } }
Abstract classes cannot be instantiated directly. Use them when subclasses share real code, and interfaces when they only share a contract.
Static Properties and Methods
final class Counter { private static int $count = 0; public static function increment(): int { return ++self::$count; } } Counter::increment(); $obj::class; // class name of an instance static::create(); // late static binding: resolves to the called subclass
Statics belong to the class, not an instance. Prefer instance state plus dependency injection for anything you want to test or swap.
Traits
trait Timestamps { protected ?DateTimeImmutable $createdAt = null; public function touch(): void { $this->createdAt = new DateTimeImmutable(); } } final class Post { use Timestamps; }
Traits copy methods and properties into a class, which sidesteps single inheritance. Resolve name collisions with use A, B { A::save insteadof B; }.
Magic Methods
final class Money { public function __construct(private int $cents) {} public function __toString(): string { return sprintf('$%.2f', $this->cents / 100); } public function __get(string $name): mixed { return $this->data[$name] ?? null; // called for undefined properties } } echo new Money(1999); // $19.99
Common magic methods: __toString, __get/__set (undefined property access), __call/__callStatic (undefined methods), __invoke (call the object like a function), __clone. On PHP 8.4+, prefer property hooks (public string $name { get => ...; }) over __get for known properties.
Attributes
#[Attribute(Attribute::TARGET_METHOD)] final class Route { public function __construct(public string $path, public array $methods = ['GET']) {} } final class UserController extends Controller { #[Route('/users/{id}', methods: ['GET'])] public function show(int $id): void {} #[\Override] // PHP 8.3+: errors unless a parent declares this method protected function middleware(): array { return []; } } $attrs = (new ReflectionMethod(UserController::class, 'show'))->getAttributes(Route::class); $route = $attrs[0]->newInstance();
Attributes (#[...]) attach structured metadata read via reflection. Frameworks use them for routing, DI wiring, validation, and ORM mapping.
Enums
enum Role: string {
case Student = 'student';
case Admin = 'admin';
public function label(): string {
return ucfirst($this->value);
}
}
$role = Role::Student;
$role->value; // student
Role::from('admin'); // throws on unknown value
Role::tryFrom('nope'); // null on unknown valueBacked enums are safer than passing raw strings around. Enums may hold methods, constants, and interfaces, but no mutable state.
Exceptions
if ($id <= 0) { throw new InvalidArgumentException('id must be positive'); } try { run(); } catch (PDOException | JsonException $e) { error_log($e->getMessage()); } catch (Throwable $e) { error_log($e->getMessage()); } finally { cleanup(); }
Catch Throwable at application boundaries; throw specific exception types inside domain code.
Namespaces and Autoloading
namespace App\Billing; use App\Database\Connection; use App\Database\{Migrator, Seeder}; use function App\Support\slugify;
Composer autoloading maps namespaces to directories so you do not hand-write require lines everywhere.