← Back to Index

Chapter 3: Functions & OOP

Functions, Closures, Classes & Modern PHP OOP

1 Function Definitions

Basic Functions & Type Declarations

PHP supports scalar type declarations (int, float, string, bool) and return type hints, similar to TypeScript / Python type hints.

function add(int $a, int $b): int
{
    return $a + $b;
}

// 联合类型(PHP 8.0+)
function format(int|float $value): string
{
    return number_format($value, 2);
}

// 交叉类型(PHP 8.1+)
function process(Countable&Iterator $collection): void
{
    // $collection 必须同时实现 Countable 和 Iterator
}

// 返回 never 类型(PHP 8.1+)— 函数永不正常返回
function abort(string $msg): never
{
    throw new RuntimeException($msg);
}

Default Values & Named Arguments

function createUser(
    string $name,
    string $role = 'member',   // 默认值
    bool $active = true
): array {
    return compact('name', 'role', 'active');
}

// 命名参数(PHP 8.0+)— 可跳过中间参数、顺序无关
$user = createUser(name: '张三', active: false);
// ['name' => '张三', 'role' => 'member', 'active' => false]

Variadic Parameters & Pass by Reference

// 可变参数(variadic)
function sum(int ...$numbers): int
{
    return array_sum($numbers);
}
echo sum(1, 2, 3, 4); // 10

// 展开操作符传参
$nums = [10, 20, 30];
echo sum(...$nums); // 60

// 引用传递 — 函数内修改会影响外部变量
function increment(int &$value): void
{
    $value++;
}
$count = 5;
increment($count);
echo $count; // 6

⚠️ PHP 8.4: Implicit Nullable Types Deprecated

In PHP 8.4, when a parameter has a type declaration and a default value of null, you must explicitly use ?Type or union type Type|null, otherwise a deprecation warning will be raised.

// ❌ PHP 8.4 弃用 — 隐式可空
function find(string $key, array $default = null): mixed { ... }

// ✅ 正确写法 — 显式可空
function find(string $key, ?array $default = null): mixed { ... }
// 或者
function find(string $key, array|null $default = null): mixed { ... }

2 Anonymous Functions & Arrow Functions

Anonymous Functions (Closures)

PHP anonymous functions must explicitly capture outer variables via the use keyword (captured by value by default).

$greeting = '你好';

$sayHello = function (string $name) use ($greeting): string {
    return "$greeting, $name!";
};

echo $sayHello('世界'); // 你好, 世界!

// 按引用捕获 — 可修改外部变量
$counter = 0;
$inc = function () use (&$counter): void {
    $counter++;
};
$inc();
$inc();
echo $counter; // 2

// 作为回调使用
$numbers = [3, 1, 4, 1, 5];
usort($numbers, function (int $a, int $b): int {
    return $a - $b;
});

Arrow Functions (PHP 7.4+)

Arrow functions are shorthand closures for single expressions that automatically capture outer variables by value, without needing use.

$multiplier = 3;

// 箭头函数 — 自动捕获 $multiplier
$triple = fn(int $x): int => $x * $multiplier;
echo $triple(5); // 15

// 在数组操作中非常好用
$prices = [100, 200, 300];
$withTax = array_map(fn($p) => $p * 1.13, $prices);
// [113.0, 226.0, 339.0]

// 箭头函数可嵌套
$addThenMultiply = fn($x) => fn($y) => ($x + $y) * 2;

🔄 Cross-Language Comparison: Anonymous Functions

PHP

// 匿名函数
$fn = function($x) use ($y) {
    return $x + $y;
};
// 箭头函数
$fn = fn($x) => $x + $y;

JavaScript

// 箭头函数自动捕获
// const fn = (x) => x + y;
// 普通函数也自动捕获
// const fn = function(x) {
//     return x + y;
// };

Python

// lambda 仅限单表达式
// fn = lambda x: x + y
// def fn(x):
//     return x + y

Key difference: JS/Python closures automatically capture outer variables; PHP anonymous functions must use use to explicitly declare them, while arrow functions capture automatically (read-only).

3 Namespaces

Namespaces are used to organize code and avoid class name conflicts, similar to Java's package or Python's module system.

Declaration & Usage

// 文件: src/Models/User.php
namespace App\Models;

class User
{
    public function __construct(
        public readonly string $name,
        public readonly string $email,
    ) {}
}

// 文件: src/Services/UserService.php
namespace App\Services;

use App\Models\User;              // 导入类
use App\Models\{User, Product};   // 分组导入
use function App\Helpers\format;  // 导入函数
use const App\Config\VERSION;     // 导入常量

class UserService
{
    public function create(string $name, string $email): User
    {
        return new User($name, $email);
    }
}

PSR-4 Autoloading

With Composer's PSR-4 standard, namespaces map one-to-one to directory structures, eliminating the need for manual require.

// composer.json
{
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    }
}

📁 Directory Structure Mapping

project/
├── composer.json
├── vendor/             # Composer 自动生成
│   └── autoload.php
└── src/                # 对应 App\ 命名空间
    ├── Models/
    │   ├── User.php    # App\Models\User
    │   └── Product.php # App\Models\Product
    ├── Services/
    │   └── UserService.php  # App\Services\UserService
    └── Helpers/
        └── functions.php
// 入口文件 index.php
require __DIR__ . '/vendor/autoload.php';

use App\Models\User;
use App\Services\UserService;

$service = new UserService();
$user = $service->create('张三', 'zhang@example.com');

💡 Tip: Run composer dump-autoload to generate/update the autoload mapping. In production, use composer dump-autoload --optimize to generate a classmap for better performance.

4 Classes & Objects

Basic Class Definition

class Product
{
    // 属性声明
    public string $name;
    protected float $price;
    private int $stock;

    // 静态属性
    private static int $instanceCount = 0;

    public function __construct(string $name, float $price, int $stock = 0)
    {
        $this->name = $name;
        $this->price = $price;
        $this->stock = $stock;
        self::$instanceCount++;
    }

    // 方法
    public function getTotal(int $qty): float
    {
        return $this->price * $qty;
    }

    // 静态方法
    public static function getInstanceCount(): int
    {
        return self::$instanceCount;
    }

    // 魔术方法
    public function __toString(): string
    {
        return "{$this->name} (¥{$this->price})";
    }
}

Constructor Promotion (PHP 8.0+)

Adding visibility modifiers before constructor parameters automatically declares and assigns properties — greatly reducing boilerplate code.

// ❌ 传统写法 — 大量重复
class User
{
    private string $name;
    private string $email;
    private string $role;

    public function __construct(string $name, string $email, string $role = 'member')
    {
        $this->name = $name;
        $this->email = $email;
        $this->role = $role;
    }
}

// ✅ 构造函数提升 — 简洁等价
class User
{
    public function __construct(
        private string $name,
        private string $email,
        private string $role = 'member',
    ) {}
    // 自动生成 $this->name, $this->email, $this->role
}

Readonly Properties (PHP 8.1+) & Readonly Classes (PHP 8.2+)

// 只读属性 — 初始化后不可修改
class Coordinate
{
    public function __construct(
        public readonly float $lat,
        public readonly float $lng,
    ) {}
}

$point = new Coordinate(39.9042, 116.4074);
echo $point->lat;      // 39.9042
// $point->lat = 0;    // ❌ Error: 只读属性不可修改

// 只读类(PHP 8.2+)— 所有属性自动 readonly
readonly class Money
{
    public function __construct(
        public float $amount,
        public string $currency,
    ) {}
}

💡 Visibility Summary:

  • public — Accessible from anywhere (default)
  • protected — Accessible only from the current class and subclasses
  • private — Accessible only from the current class
  • • PHP 8.4 adds public(set) asymmetric visibility: read and write can have different access levels

5 Inheritance & Interfaces

Abstract Classes & Inheritance

PHP is a single-inheritance language (same as Java), using extends for inheritance and abstract to define abstract classes.

abstract class Shape
{
    public function __construct(
        protected string $color = 'red',
    ) {}

    // 抽象方法 — 子类必须实现
    abstract public function area(): float;

    // 普通方法 — 可被继承
    public function describe(): string
    {
        return "一个 {$this->color} 的 " . static::class;
    }
}

class Circle extends Shape
{
    public function __construct(
        private float $radius,
        string $color = 'blue',
    ) {
        parent::__construct($color);
    }

    public function area(): float
    {
        return M_PI * $this->radius ** 2;
    }
}

class Rectangle extends Shape
{
    public function __construct(
        private float $width,
        private float $height,
        string $color = 'green',
    ) {
        parent::__construct($color);
    }

    public function area(): float
    {
        return $this->width * $this->height;
    }
}

Interfaces

Interfaces define contracts; a class can implement multiple interfaces.

interface Printable
{
    public function toString(): string;
}

interface JsonSerializable
{
    public function toJson(): string;
}

// 实现多个接口
class Invoice implements Printable, \JsonSerializable
{
    public function __construct(
        private string $id,
        private float $total,
    ) {}

    public function toString(): string
    {
        return "Invoice #{$this->id}: ¥{$this->total}";
    }

    // \JsonSerializable 接口要求的方法
    public function jsonSerialize(): mixed
    {
        return ['id' => $this->id, 'total' => $this->total];
    }

    public function toJson(): string
    {
        return json_encode($this->jsonSerialize());
    }
}

Practical Example: Strategy Pattern

interface PaymentGateway
{
    public function charge(float $amount): bool;
    public function getName(): string;
}

class AlipayGateway implements PaymentGateway
{
    public function charge(float $amount): bool
    {
        echo "支付宝收款 ¥{$amount}\n";
        return true;
    }

    public function getName(): string { return '支付宝'; }
}

class WechatGateway implements PaymentGateway
{
    public function charge(float $amount): bool
    {
        echo "微信支付收款 ¥{$amount}\n";
        return true;
    }

    public function getName(): string { return '微信支付'; }
}

// 使用接口作为类型约束
class OrderService
{
    public function checkout(PaymentGateway $gateway, float $amount): void
    {
        if ($gateway->charge($amount)) {
            echo "通过 {$gateway->getName()} 支付成功\n";
        }
    }
}

$service = new OrderService();
$service->checkout(new AlipayGateway(), 99.9);
$service->checkout(new WechatGateway(), 199.0);

6 Traits

Traits are PHP's code reuse mechanism that addresses single-inheritance limitations. Think of them as "copy-pasting" methods into a class — composable code snippets.

Basic Usage

trait HasTimestamps
{
    public ?string $createdAt = null;
    public ?string $updatedAt = null;

    public function setCreatedAt(): void
    {
        $this->createdAt = date('Y-m-d H:i:s');
    }

    public function setUpdatedAt(): void
    {
        $this->updatedAt = date('Y-m-d H:i:s');
    }
}

trait SoftDeletes
{
    public ?string $deletedAt = null;

    public function softDelete(): void
    {
        $this->deletedAt = date('Y-m-d H:i:s');
    }

    public function isDeleted(): bool
    {
        return $this->deletedAt !== null;
    }
}

// 在类中使用多个 Trait
class Article
{
    use HasTimestamps, SoftDeletes;

    public function __construct(
        public string $title,
        public string $content,
    ) {
        $this->setCreatedAt();
    }
}

$article = new Article('PHP 教程', '内容...');
echo $article->createdAt;  // 2026-03-31 12:00:00
$article->softDelete();
echo $article->isDeleted(); // true

Conflict Resolution

When multiple Traits contain methods with the same name, use insteadof and as to resolve conflicts.

trait Logger
{
    public function log(string $msg): void
    {
        echo "[LOG] $msg\n";
    }
}

trait Debugger
{
    public function log(string $msg): void
    {
        echo "[DEBUG] $msg\n";
    }
}

class App
{
    use Logger, Debugger {
        Logger::log insteadof Debugger;   // 优先使用 Logger 的 log
        Debugger::log as debugLog;         // 将 Debugger 的 log 重命名为 debugLog
    }
}

$app = new App();
$app->log('启动');       // [LOG] 启动
$app->debugLog('详情');  // [DEBUG] 详情

🔄 Cross-Language Comparison: Code Reuse Mechanisms

PHP Trait

trait Cacheable {
    public function cache(): void { }
}
class User {
    use Cacheable;
}

Python Mixin

// class CacheMixin:
//     def cache(self): ...
//
// class User(CacheMixin, Base):
//     pass
// (通过多继承实现)

Java default methods

// interface Cacheable {
//     default void cache() { }
// }
// class User implements Cacheable
//     { }

Key difference: PHP Traits are neither classes nor interfaces — they cannot be instantiated and do not participate in the type system (instanceof does not recognize Traits). Python uses multiple inheritance + MRO, while Java uses interface default methods.

7 Enums PHP 8.1+

PHP 8.1 introduced native enums, replacing constant-based simulation. Enums are first-class citizens that support methods, interface implementation, and are a powerful tool for type safety.

Unit Enums

enum Suit
{
    case Hearts;
    case Diamonds;
    case Clubs;
    case Spades;
}

$card = Suit::Hearts;
echo $card->name; // "Hearts"

// 枚举用于类型约束
function playCard(Suit $suit): void
{
    echo "打出 {$suit->name}\n";
}
playCard(Suit::Spades);

// 枚举比较
var_dump(Suit::Hearts === Suit::Hearts);   // true
var_dump(Suit::Hearts === Suit::Diamonds); // false

Backed Enums

Each enum case is associated with a string or int value, suitable for database storage and API serialization.

enum Status: string
{
    case Draft = 'draft';
    case Published = 'published';
    case Archived = 'archived';

    // 枚举可以包含方法
    public function label(): string
    {
        return match($this) {
            self::Draft     => '草稿',
            self::Published => '已发布',
            self::Archived  => '已归档',
        };
    }

    public function color(): string
    {
        return match($this) {
            self::Draft     => 'gray',
            self::Published => 'green',
            self::Archived  => 'red',
        };
    }
}

$status = Status::Published;
echo $status->value;   // "published"
echo $status->name;    // "Published"
echo $status->label(); // "已发布"

// 从值构造枚举
$fromDb = Status::from('draft');          // Status::Draft
$maybe  = Status::tryFrom('unknown');     // null(不会抛异常)

// 获取所有枚举成员
$all = Status::cases();
// [Status::Draft, Status::Published, Status::Archived]

Enums Implementing Interfaces

interface HasDescription
{
    public function description(): string;
}

enum Permission: int implements HasDescription
{
    case Read    = 1;
    case Write   = 2;
    case Execute = 4;

    public function description(): string
    {
        return match($this) {
            self::Read    => '读取权限',
            self::Write   => '写入权限',
            self::Execute => '执行权限',
        };
    }

    // 利用位运算检查权限组合
    public static function check(int $mask, self $perm): bool
    {
        return ($mask & $perm->value) !== 0;
    }
}

$userPerms = Permission::Read->value | Permission::Write->value; // 3
echo Permission::check($userPerms, Permission::Read);    // true
echo Permission::check($userPerms, Permission::Execute); // false

📌 Enum Limitations

  • • Enums cannot have constructors (stateless)
  • • Enums cannot be inherited, nor can they inherit other classes
  • • Enums can implement interfaces and use Traits (but Traits must not contain properties)
  • • Enum cases are singletons — cannot be new'd or clone'd

8 Chapter Summary

🎯 Functions

  • • Parameter and return type declarations
  • • Named arguments to skip defaults
  • ...$args variadic parameters
  • &$var pass by reference
  • • PHP 8.4 implicit nullable deprecated

⚡ Closures

  • • Anonymous functions need use to capture variables
  • • Arrow functions fn() => auto-capture
  • • Arrow functions limited to single expressions
  • • Commonly used with array_map and other callbacks

📦 Namespaces

  • namespace + use to organize code
  • • PSR-4: namespaces map to directories
  • • Composer autoloading
  • composer dump-autoload

🏗️ Classes & Objects

  • • Constructor promotion (8.0+)
  • readonly properties (8.1+)
  • readonly class (8.2+)
  • • Three-level visibility + asymmetric visibility (8.4)

🔗 Inheritance & Interfaces

  • • Single inheritance extends
  • • Multiple interfaces implements
  • abstract classes
  • • Traits compensate for lack of multiple inheritance

🏷️ Enums

  • • Unit enums vs backed enums
  • from() / tryFrom() construction
  • • Enums can have methods, implement interfaces
  • • Type-safe replacement for magic constants

With functions and OOP mastered, you can now write well-structured PHP applications!

In the next chapter, we'll learn about Database Operations, using PDO to connect to MySQL and implement CRUD.