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 教程。