【问题标题】:PHP Type hinting callable functionPHP 类型提示可调用函数
【发布时间】:2026-02-12 01:35:01
【问题描述】:

好的,所以我定义了这个类方法来接受回调,并且我已经根据类型提示文档将其类型提示为可调用。

protected function AddTransformData(array $definition,array $where,callable $callback){
    $this->transforms[]=[$definition,$where,$callback];
}

这是我可能调用此函数的示例。使用数组语法将方法和对象作为回调传递。

public function __construct(PostalZoneMapping $pzm){
    $this->pzm=$pzm;
    $this->AddTransformData(Countries::region,['filter'],[$this,'TransformId']);
    $this->AddTransformData(PostalZones::id,['filter'],[$this,'TransformId']);
    $this->ReceiveData();
}

这会引发错误,如下图,抱怨参数 3 不是可调用的,而是一个数组。从逻辑上讲,我想这是有道理的,因为它是数组,但它是一个可调用函数的数组——它肯定必须检测到它是一个回调吗?

这是 PHP 的怪癖还是我做错了什么?

【问题讨论】:

  • 是什么产生了异常?我试图创建类似的东西,但无法重现结果。您能否创建一组简单的代码来重现问题中的此错误。
  • 这不就是IDE错误吗?实际运行代码是否遇到了真正的异常?这是什么 IDE - 也许它不理解可调用数组表示法。
  • 您的“TransformId”方法有哪些可见性?它不是私有的还是受保护的?
  • @IvanShumakov 根据文档:Accessing protected and private methods from within a class is allowed. - 所以即使它在此类的上下文中是私有的,它也应该是有效的可调用对象?
  • 好吧,事实证明它不是在单个类的上下文中,而是在父-子的上下文中。然后它必须至少受到保护,以便它可以在父级中调用。

标签: php


【解决方案1】:
public function __construct(PostalZoneMapping $pzm){
    $this->pzm=$pzm;
    $method = 'TransformId';
    $callable = fn() => $this->$method();
    $this->AddTransformData(Countries::region,['filter'], $callable);
    $this->AddTransformData(PostalZones::id,['filter'], $callable);
    $this->ReceiveData();
}

如果您的 PHP 版本低于 7.4,则不要使用此版本:

$callable = fn() => $this->$method();

这样做:

$callable = function() use ($method) { $this->$method() };

您还可以接收参数:

$callable = fn($param) => $this->$method($param);

$callable = function($param) use ($method) { $this->$method($param)};

【讨论】:

    【解决方案2】:

    看起来TransformId 不是该类的方法。也许它是一个错字,也许它是一个属性但不是一个方法。

    为了使数组成为有效的回调,它必须是:A method of an instantiated object is passed as an array containing an object at index 0 and the method name at index 1.

    这行得通:

    class A {
      function __construct() {
        $this->asd2([$this, 'asd']);
      }
      private function asd() {}
    
      public function asd2(callable $c) {}
    }
    
    $a = new A();
    

    这不是:

    class A {
      function __construct() {
        $this->asd2([$this, 'somethingElse']);
      }
      private function asd() {}
    
      public function asd2(callable $c) {}
    }
    
    Fatal error: Uncaught TypeError: Argument 1 passed to A::asd2() must be callable, array given, called in
    

    如果我错了 - 粘贴整个类代码,包括 TransformId 方法。

    【讨论】:

      【解决方案3】:

      我创建了一些测试代码来重现错误,注意到我将回调声明为私有!此代码不起作用,但如果您将 TransformId 方法更改为 protected 或 public,它将起作用。

      <?php
      
      abstract class CommonDataInterface{
          private $transforms=[];
          /**
           * Adds a callback that transform data 
           *
           * @param array $definition Definition, where, if matches, callback is called.
           * @param array $where Where to transform data, array values one or more of 'set','insert' or'filter'
           * @param callable $callback Function that takes value as parameter, and returns transformed value.
           * @return void
           */
          protected function AddTransformData(array $definition,array $where,callable $callback){
              $this->transforms[]=[$definition,$where,$callback];
          }
      }
      
      
      class Api_PostalZoneMapping extends CommonDataInterface{
          private $pzm;
      
          public function __construct($pzm){
              $this->pzm=$pzm;
              $this->AddTransformData(['blah'],['filter'],[$this,'TransformId']);
              $this->AddTransformData(['blah2'],['filter'],[$this,'TransformId']);
              //$this->ReceiveData();
          }
      
          private function TransformId($data){
              if($data==-1)
                  return null;
              return $data;
          }
      }
      
      $p=new Api_PostalZoneMapping('not relevant for test');
      

      【讨论】: