PHP:类型检查返回值是一个很好的做法,以弥补PHP缺乏泛型?

时间:2014-08-29 09:53:03

标签: php generics typechecking type-hinting

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

问题

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

我发现自己通过明确检查代码中的类型来解决这个问题 - 由于语言本身可以为我处理它而感觉有些错误 - 我想将这些问题推向社区:

  • 类型检查返回值是否是补偿PHP缺少泛型的好方法?
  • 有更好/更标准的方法吗?
  • 目前讨论的通用语是为了将来在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'
    }

我目前的解决方法

我目前正在使用多个接口来定义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);
}

在每个变压器中,我确保只接受合适的接口作为前身:

class DomDocumentToStringAdapter implements ToStringAdapterInterface {

  private $xpathExpression;

  private $predecessor;

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

1 个答案:

答案 0 :(得分:0)

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

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

  

Hack是HHVM的一种编程语言,可以无缝地互操作   用PHP。 Hack 协调PHP的快速开发周期   静态打字提供的纪律,同时添加了许多功能   常见于其他现代编程语言中。