PHP __invoke 方法
上次修改于 2025 年 5 月 28 日
在本文中,我们将探讨 PHP 的 __invoke 魔术方法,该方法允许将对象作为函数调用。当一个类实现了 __invoke 方法时,该类的实例就成为了可调用对象,可以像函数一样被调用。
__invoke 方法使对象能够在调用之间保持状态,同时提供类似函数的功能。 这一强大特性弥合了 PHP 中面向对象编程和函数式编程范式之间的差距。
PHP 的 __invoke 方法的优点是
- 可调用对象: 对象可以像函数一样被调用
- 有状态函数: 在调用之间保持状态
- 灵活的接口: 可以在任何接受可调用对象的地方使用
- 更简洁的代码: 将相关行为封装在对象中
- 多态性: 不同的类可以实现它们自己的调用逻辑
通过使用 __invoke,开发人员可以创建更具表达性和灵活性的代码,结合对象和函数的优点。
__invoke 方法在类中定义,当一个对象像函数一样被调用时,该方法就会被调用。 它能接受任意数量的参数并返回任何值,就像一个普通函数一样。
简单的 Greeter 示例
__invoke 方法通常用于创建行为类似于函数的对象。 当在需要函数的上下文中使用对象时,这允许使用更自然的语法,例如回调或高阶函数。
<?php
declare(strict_types=1);
class Greeter {
public function __invoke(string $name): string {
return "Hello, $name!";
}
}
$greet = new Greeter();
echo $greet('John') . "\n";
// Check if object is callable
var_dump(is_callable($greet));
// Using as a callback
$names = ['Alice', 'Bob', 'Charlie'];
$greetings = array_map($greet, $names);
print_r($greetings);
此示例演示了一个简单的 Greeter 类,其中实例可以像函数一样被调用以产生问候语。 它还展示了该对象作为回调函数与 array_map 一起使用。
λ php greeter_invoke.php
Hello, John!
bool(true)(
[0] => Hello, Alice!
[1] => Hello, Bob!
[2] => Hello, Charlie!
)
带有多个参数的计算器
__invoke 方法通常用于创建行为类似于函数的对象。 当在需要函数的上下文中使用对象时,这允许使用更自然的语法,例如回调或高阶函数。
<?php
declare(strict_types=1);
class Calculator {
public function __invoke(float $a, float $b, string $operation): float {
return match($operation) {
'+' => $a + $b,
'-' => $a - $b,
'*' => $a * $b,
'/' => $a / $b,
default => throw new InvalidArgumentException("Unknown operation")
};
}
}
$calc = new Calculator();
echo "5 + 3 = " . $calc(5, 3, '+') . "\n";
echo "5 * 3 = " . $calc(5, 3, '*') . "\n";
Calculator 类展示了 __invoke 如何处理多个参数以执行算术运算。
λ php calculator_invoke.php 5 + 3 = 8 5 * 3 = 15
与普通函数不同,具有 __invoke 的对象可以在调用之间保持内部状态。 这允许使用强大的模式,例如可配置函数或记住先前交互的类似函数对象。
带状态的计数器
__invoke 方法通常用于创建行为类似于函数的对象。 当在需要函数的上下文中使用对象时,这允许使用更自然的语法,例如回调或高阶函数。
<?php
declare(strict_types=1);
class Counter {
private int $count = 0;
public function __invoke(): int {
return ++$this->count;
}
public function reset(): void {
$this->count = 0;
}
}
$counter = new Counter();
echo $counter() . "\n";
echo $counter() . "\n";
echo $counter() . "\n";
$counter->reset();
echo $counter() . "\n";
Counter 类维护一个内部计数器,每次调用时都会递增,并通过一个方法允许重置。
λ php counter_invoke.php 1 2 3 1
可配置的记录器
__invoke 方法通常用于创建行为类似于函数的对象。 当在需要函数的上下文中使用对象时,这允许使用更自然的语法,例如回调或高阶函数。
<?php
declare(strict_types=1);
class Logger {
private string $prefix;
public function __construct(string $prefix) {
$this->prefix = $prefix;
}
public function __invoke(string $message): void {
echo "[{$this->prefix}] " . date('Y-m-d H:i:s') . " - $message\n";
}
}
$infoLogger = new Logger('INFO');
$infoLogger('Application started');
$errorLogger = new Logger('ERROR');
$errorLogger('Something went wrong!');
Logger 类可通过构造函数进行配置,允许不同的实例使用不同的前缀进行日志记录。
λ php logger_invoke.php [INFO] 2025-05-28 00:00:00 - Application started [ERROR] 2025-05-28 00:00:00 - Something went wrong!
缓存函数结果
__invoke 方法通常用于创建行为类似于函数的对象。 当在需要函数的上下文中使用对象时,这允许使用更自然的语法,例如回调或高阶函数。
<?php
declare(strict_types=1);
class CachedFunction {
private array $cache = [];
private $function;
public function __construct(callable $function) {
$this->function = $function;
}
public function __invoke($arg) {
if (!array_key_exists($arg, $this->cache)) {
$this->cache[$arg] = ($this->function)($arg);
}
return $this->cache[$arg];
}
}
$expensiveFunction = function($x) {
sleep(1); // Simulate expensive computation
return $x * 2;
};
$cached = new CachedFunction($expensiveFunction);
echo "First call (slow): ";
$start = microtime(true);
echo $cached(5) . " - ";
echo round(microtime(true) - $start, 3) . "s\n";
echo "Second call (fast): ";
$start = microtime(true);
echo $cached(5) . " - ";
echo round(microtime(true) - $start, 3) . "s\n";
CachedFunction 类对结果进行记忆,以优化昂贵计算的性能。
λ php cached_function_invoke.php First call (slow): 10 - 1.003s Second call (fast): 10 - 0.000s
实际用例
__invoke 方法在 PHP 开发中有很多实际应用。 以下是一些证明其特别有用的常见场景。
策略模式实现
__invoke 方法通常用于创建行为类似于函数的对象。 当在需要函数的上下文中使用对象时,这允许使用更自然的语法,例如回调或高阶函数。
<?php
declare(strict_types=1);
interface DiscountStrategy {
public function __invoke(float $amount): float;
}
class NoDiscount implements DiscountStrategy {
public function __invoke(float $amount): float {
return $amount;
}
}
class PercentageDiscount implements DiscountStrategy {
private float $percentage;
public function __construct(float $percentage) {
$this->percentage = $percentage;
}
public function __invoke(float $amount): float {
return $amount * (1 - $this->percentage / 100);
}
}
class FixedDiscount implements DiscountStrategy {
private float $discount;
public function __construct(float $discount) {
$this->discount = $discount;
}
public function __invoke(float $amount): float {
return max(0, $amount - $this->discount);
}
}
function applyDiscount(float $amount, DiscountStrategy $discount): float {
return $discount($amount);
}
$orderAmount = 100;
echo "No discount: " . applyDiscount($orderAmount, new NoDiscount()) . "\n";
echo "10% discount: " . applyDiscount($orderAmount, new PercentageDiscount(10)) . "\n";
echo "$20 discount: " . applyDiscount($orderAmount, new FixedDiscount(20)) . "\n";
此示例展示了使用 __invoke 的策略模式,以多态的方式应用不同的折扣策略。
λ php discount_strategy.php No discount: 100 10% discount: 90 $20 discount: 80
中间件管道
__invoke 方法通常用于创建行为类似于函数的对象。 当在需要函数的上下文中使用对象时,这允许使用更自然的语法,例如回调或高阶函数。
<?php
declare(strict_types=1);
class Middleware {
private $next;
private $action;
public function __construct(callable $action, ?callable $next = null) {
$this->action = $action;
$this->next = $next;
}
public function __invoke($request) {
$result = ($this->action)($request);
return $this->next ? ($this->next)($result) : $result;
}
}
$middleware1 = new Middleware(
fn($x) => $x * 2,
new Middleware(
fn($x) => $x + 5,
new Middleware(
fn($x) => $x / 3
)
)
);
echo "Middleware result: " . $middleware1(10) . "\n";
Middleware 类演示了一个简单的中间件管道,其中每个中间件都可以转换请求并将其传递给下一个中间件。 这种模式通常用于 Web 框架中,以模块化的方式处理请求和响应。
λ php middleware_invoke.php Middleware result: 8.3333333333333
具有函数行为的值对象
__invoke 方法通常用于创建行为类似于函数的对象。 当在需要函数的上下文中使用对象时,这允许使用更自然的语法,例如回调或高阶函数。
<?php
declare(strict_types=1);
class Money {
private float $amount;
private string $currency;
public function __construct(float $amount, string $currency) {
$this->amount = $amount;
$this->currency = $currency;
}
public function __invoke(float $rate): Money {
return new Money($this->amount * $rate, $this->currency);
}
public function getAmount(): float {
return $this->amount;
}
public function getCurrency(): string {
return $this->currency;
}
}
$usd = new Money(100, 'USD');
$converted = $usd(0.85); // Convert to EUR at 0.85 rate
echo "Converted amount: " . $converted->getAmount() . " " . $converted->getCurrency() . "\n";
Money 类使用 __invoke 根据给定的汇率转换金额,从而允许使用自然的类似函数语法。
λ php money_invoke.php Converted amount: 85 USD
使用 __invoke 进行柯里化
__invoke 方法通常用于创建行为类似于函数的对象。 当在需要函数的上下文中使用对象时,这允许使用更自然的语法,例如回调或高阶函数。
__invoke 方法可用于实现柯里化,这是一种函数式编程技术,允许部分应用函数。 这意味着您可以通过固定现有函数的一些参数来创建新函数,从而实现更灵活和可重用的代码。 这对于从更通用的函数创建专用函数特别有用,从而实现更简洁和模块化的代码。
<?php
declare(strict_types=1);
class Curry {
private $function;
private array $args;
public function __construct(callable $function, array $args = []) {
$this->function = $function;
$this->args = $args;
}
public function __invoke(...$args) {
$newArgs = array_merge($this->args, $args);
$reflection = new ReflectionFunction($this->function);
if (count($newArgs) >= $reflection->getNumberOfRequiredParameters()) {
return $reflection->invokeArgs($newArgs);
}
return new self($this->function, $newArgs);
}
}
$add = new Curry(function($a, $b, $c) {
return $a + $b + $c;
});
$add5 = $add(5);
$add5And10 = $add5(10);
echo "Curried result: " . $add5And10(15) . "\n"; // 30
Curry 类允许部分应用函数。 它接受一个可调用对象并累积参数,直到提供了足够的参数来调用原始函数。 这使得可以从更通用的函数创建专用函数,从而实现更简洁和模块化的代码。
λ php curry_invoke.php Curried result: 30
函数组合
__invoke 方法通常用于创建行为类似于函数的对象。 当在需要函数的上下文中使用对象时,这允许使用更自然的语法,例如回调或高阶函数。
__invoke 方法可用于将多个函数组合成一个可调用对象。 这允许链式操作,其中一个函数的输出成为下一个函数的输入。 这种技术对于创建数据转换或操作的管道非常有用。
<?php
declare(strict_types=1);
class Compose {
private array $functions;
public function __construct(callable ...$functions) {
$this->functions = $functions;
}
public function __invoke($value) {
return array_reduce(
array_reverse($this->functions),
fn($carry, $fn) => $fn($carry),
$value
);
}
}
$composed = new Compose(
fn($x) => $x * 2,
fn($x) => $x + 3,
fn($x) => $x / 4
);
echo "Composed result: " . $composed(10) . "\n"; // ((10/4)+3)*2 = 11
Compose 类将多个函数链接在一起,按顺序应用它们。 每个函数的输出成为下一个函数的输入,从而以简洁易读的方式实现复杂的转换。
λ php compose_invoke.php Composed result: 11
特定于领域的语言 (DSL)
__invoke 方法通常用于创建行为类似于函数的对象。 当在需要函数的上下文中使用对象时,这允许使用更自然的语法,例如回调或高阶函数。
__invoke 方法可用于创建流畅的接口或特定于领域的语言 (DSL),用于构建复杂的查询或配置。 这允许开发人员以更自然、更具可读性的方式表达操作。 例如,可以使用 __invoke 以流畅的风格构建 SQL 查询,从而实现查询构建器。
<?php
declare(strict_types=1);
class QueryBuilder {
private array $query = [];
public function select(...$fields): self {
$this->query['select'] = $fields;
return $this;
}
public function where(string $field, string $operator, $value): self {
$this->query['where'][] = [$field, $operator, $value];
return $this;
}
public function __invoke(): string {
$select = implode(', ', $this->query['select'] ?? ['*']);
$where = '';
if (!empty($this->query['where'])) {
$conditions = array_map(
fn($cond) => "{$cond[0]} {$cond[1]} '{$cond[2]}'",
$this->query['where']
);
$where = ' WHERE ' . implode(' AND ', $conditions);
}
return "SELECT $select FROM table$where";
}
}
$query = new QueryBuilder();
$sql = $query
->select('id', 'name', 'email')
->where('age', '>', 18)
->where('status', '=', 'active')
(); // Invoke to get SQL
echo "Generated SQL: $sql\n";
QueryBuilder 创建一个流畅的 DSL,用于构建 SQL 查询。 它允许链接方法以指定字段和条件,并且 __invoke 方法在调用时生成最终的 SQL 字符串。
λ php query_builder_invoke.php Generated SQL: SELECT id, name, email FROM table WHERE age > '18' AND status = 'active'
记忆化装饰器
__invoke 方法通常用于创建行为类似于函数的对象。 当在需要函数的上下文中使用对象时,这允许使用更自然的语法,例如回调或高阶函数。
__invoke 方法也可用于创建记忆化装饰器,用于缓存昂贵函数调用的结果。 这对于递归函数或计算密集型操作特别有用。 这种技术可以通过避免冗余计算来显着提高性能,尤其是在斐波那契数列或其他递归算法等情况下。
<?php
declare(strict_types=1);
class Memoize {
private $function;
private array $cache = [];
public function __construct(callable $function) {
$this->function = $function;
}
public function __invoke(...$args) {
$key = serialize($args);
if (!array_key_exists($key, $this->cache)) {
$this->cache[$key] = ($this->function)(...$args);
}
return $this->cache[$key];
}
}
$fibonacci = new Memoize(function($n) use (&$fibonacci) {
return $n <= 1 ? $n : $fibonacci($n - 1) + $fibonacci($n - 2);
});
echo "Fibonacci(10): " . $fibonacci(10) . "\n";
Memoize 类通过缓存结果来优化斐波那契数列等递归函数。
λ php memoize_invoke.php Fibonacci(10): 55
最佳实践
使用 PHP 的 __invoke 方法时,请遵循这些最佳实践,以确保代码的清洁、可维护和有效。
最佳实践示例
__invoke 方法通常用于创建行为类似于函数的对象。 当在需要函数的上下文中使用对象时,这允许使用更自然的语法,例如回调或高阶函数。
<?php
declare(strict_types=1);
// 1. Use for single-purpose callable objects
class DiscountCalculator {
public function __invoke(float $amount): float {
return $amount * 0.9; // Example 10% discount
}
}
// 2. Document behavior clearly
/**
* Class that transforms strings according to configured rules
* @method string __invoke(string $input) Transforms input string
*/
class StringTransformer {
public function __invoke(string $input): string {
return strtoupper($input);
}
}
// 3. Consider implementing other interfaces too
interface LoggerInterface {
public function log(string $message): void;
}
class Logger implements LoggerInterface {
public function log(string $message): void {
echo "LOG: $message\n";
}
public function __invoke(string $message): void {
$this->log($message);
}
}
// 4. Use type hints for parameters and return values
class Validator {
public function __invoke(mixed $value): bool {
return !empty($value);
}
}
// 5. Keep __invoke focused
class OrderProcessor {
public function __invoke(Order $order): void {
$this->validate($order);
$this->applyDiscounts($order);
$this->save($order);
}
private function validate(Order $order): void { /* ... */ }
private function applyDiscounts(Order $order): void { /* ... */ }
private function save(Order $order): void { /* ... */ }
}
// 6. Consider alternatives for simple cases
$simpleGreet = fn($name) => "Hello, $name!";
echo $simpleGreet('Alice') . "\n";
// 7. Be consistent with invocation patterns
$logger = new Logger();
$logger('message'); // Preferred
// 8. Use for natural function-like behavior
$isPositive = new class {
public function __invoke(float $n): bool {
return $n > 0;
}
};
var_dump($isPositive(5)); // true
var_dump($isPositive(-3)); // false
// 9. Use with PHP's functional programming features
$numbers = [1, -2, 3, -4, 5];
$positiveNumbers = array_filter($numbers, $isPositive);
print_r($positiveNumbers);
这些实践有助于确保您有效地使用 __invoke,同时避免常见的陷阱。 主要考虑因素包括单一职责、清晰的文档和适当的用例。
λ php best_practices.php
Hello, Alice!
LOG: message
bool(true)
bool(false)
Array
(
[0] => 1
[2] => 3
[4] => 5
)
PHP 的 __invoke 魔术方法是一个强大的特性,它弥合了面向对象编程和函数式编程之间的差距。 需要记住的要点是
- 可调用对象: 使实例像函数一样可调用
- 状态维护: 对象可以在调用之间保留状态
- 灵活使用: 适用于任何接受可调用对象的地方
- 设计模式: 启用模式(如策略)的优雅实现
- 函数式编程: 支持柯里化、组合和其他 FP 技术
- 清晰的意图: 声明一个对象代表一个动作或转换
- 多态性: 不同的类可以实现它们自己的调用行为
当您需要将状态与类似函数行为相结合的对象时,__invoke 方法特别有价值。 当适当地使用时,它使代码具有表现力,并且既强大又易于理解。
作者
列出 所有 PHP 教程。