PHP:类型检查返回值是一种弥补 PHP 泛型不足的好习惯吗?

标签 php generics typechecking type-hinting

注意:为了防止反对票,因为好的做法可能是基于意见的——您也可以将问题改写为:类型检查返回的缺点是什么值来弥补 PHP 缺乏泛型?(我没有使用它,因为它暗示着缺点)。

问题

来自 Java/C# 世界,PHP 的松散类型处理一直有些烦人。当引入输入参数的类型提示时它变得更好,但我仍然缺少 generics以及返回值的类型提示。

我发现自己偶尔会通过在我的代码中显式检查类型来解决这个问题——这感觉有点不对劲,因为语言本身可以为我处理它——我想把这些问题推给社区:

  • 类型检查返回值是否是弥补 PHP 泛型不足的良好做法?
  • 是否有更好/更标准的方法来做到这一点?
  • 目前是否讨论了 future 在 PHP 中实现的泛型?

例子

为了更好地理解为什么这是有问题的,请考虑以下示例:

假设我们正在构建一个框架来将输入数据转换为其他一些输出数据。示例:

通过 xpath 表达式选择所述 DomDocument 的标题,将表示 XML 文档的字符串转换为 DomDocument 中的另一个字符串。

(string) $xml =[TransformToDomDocument]=> (DomDocument) $doc =[TransformToString]=> (string) $title

现在让我们假设输入不是包含 XML 的字符串,而是包含 Json(但包含其他相同数据)。我们现在想要将 Json 输入转换为 Json 对象并使用 JsonPath 选择标题。表达。

(string) $jsonString =[TransformToJson]=> (Json) $jsonObject =[TransformToString]=> (string) $title

(注意:第二个例子应该说明整个框架应该非常灵活。)

转换是通过使用处理从输入到输出的转换的适配器对象链来执行的:

interface AdapterInterface{

  /**
    * Transform some input data into something else.
    * @param mixed $data
    * @return mixed
   */
  public function transform($data);

  /**
    * Set the Adapter that is used to preprocess the $data before calling $this->transform($data)
    * @param AdapterInterface $adapter
   */
  public function setPredecessorAdapter(AdapterInterface $adapter);

}

class XmlToDomDocumentAdapter implements AdapterInterface{

  private $predecessor;

  /**
    * Transform an xml string into a DOMDocument.
    * @param mixed $data
    * @return DomDocument
   */
  public function transform($data){

    if($this->predecessor !== null){
      $data = $this->predecessor->transform($data); 
      // At this point, we just have to "trust" that the predecessor returns a (string)
    }
    $doc = new DomDocument();
    $doc->loadXml($data);
    return $doc;
  }

}

class DomDocumentToStringAdapter implements AdapterInterface{

  private $xpathExpression;

  private $predecessor;

  /**
    * Transform a DomDocument into a string.
    * @param mixed $data
    * @return string
   */
  public function transform($data){

    if($this->predecessor !== null){
      $data = $this->predecessor->transform($data); 
      // At this point, we just have to "trust" that the predecessor returns a (DOMDocument)
    }
    $xpath = new DOMXpath($data);
    $nodes = $xapth->query($this->xpathExpression);
    if($nodes->length > 0){
        throw new UnexpectedValueException("Xpath didn't match");
    }
    $result = $nodes->item(0)->nodeValue;
    return $result;
  }

}

用法:

$input = "..."
$xmlToDom = new XmlToDomDocumentAdapater();
$domToString = DomDocumentToStringAdapter();
$domToString->setPredecessorAdapter($xmlToDom);
$output = $domToString->transform($input);

当适配器依赖于它的前身返回正确的输入时,就会出现有问题的部分。

    if($this->predecessor !== null){
      $data = $this->predecessor->transform($data); 
      // At this point, we just have to "trust" that the predecessor returns a (DOMDocument)
    }

在 C# 中,我会使用 generics 来解决这个问题:

interface AdapterInterface{

  /**
    * Tranform some input data into something else.
    * @param mixed $data
    * @return T
   */
  public function T transform<T>(object data);

}

/* using it */
//...

    if(this.predecessor !== null){
      data = this.predecessor.transform<string>(data); 
      // we now know for sure that the data is of type 'string'
    }
//...

由于 PHP 不支持泛型,我问自己在每次调用 transform($data) 之后添加类型检查是否是一个好习惯,如下所示:

    if($this->predecessor !== null){
      $data = $this->predecessor->transform($data); 
      if(!is_string($data){
        throw new UnexpectedValueException("data is not a string!");
      }
      // we now know for sure that the data is of type 'string'
    }

我目前的解决方法

我目前使用多个接口(interface)来定义 transform 方法的输出,如下所示:

interface ToStringAdapterInterface extends AdapterInterface{

  /**
    * Transform some input data into something else.
    * @param mixed $data
    * @return string <<< define expected output
   */
  public function transform($data);
}

interface ToDomDocumentAdapterInterface extends AdapterInterface{

  /**
    * Transform some input data into something else.
    * @param mixed $data
    * @return DOMDocument<<< define expected output
   */
  public function transform($data);
}

在每个转换器中,我确保只接受合适的接口(interface)作为前身:

class DomDocumentToStringAdapter implements ToStringAdapterInterface {

  private $xpathExpression;

  private $predecessor;

  public function __construct(ToDomDocumentAdapterInterface $predecessor){
      $this->predecessor = $predecessor;
  }
  // ...
}

最佳答案

我会遵循您的方法:测试 $this->predecessor->transform($data) 的返回值数据类型,如果不是预期的类型则抛出异常。

我不知道你是否会对Hack programming language by Facebook感兴趣:

Hack is a programming language for HHVM that interoperates seamlessly with PHP. Hack reconciles the fast development cycle of PHP with the discipline provided by static typing, while adding many features commonly found in other modern programming languages.

关于PHP:类型检查返回值是一种弥补 PHP 泛型不足的好习惯吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25565847/

相关文章:

haskell - GHC发生检查识别哪些情况?

python - 在 Python 3.6 中运行时根据 Union 类型检查变量

php - Imagick::paintOpaqueImage 忽略半透明像素

php - 我可以在 PhalconPHP 中使用多重命名空间吗

php - 如何在网页上加载大量 php 生成的图像

java - 在泛型类中使用泛型对象方法

typescript - 在 TypeScript 中返回多个值

php - 在php中使用策略模式的优点

java - 对于不扩展 Enum 的泛型,Enum.valueOf 的替代品?

java - 关于游戏循环迭代数据结构的问题