PHP: Are form validation return values ​​good practice to make up for the lack of PHP generics?

Note . To prevent downvotes, because good practice might be opinion based, you can also rephrase the question as follows: What are the disadvantages of type checking return values ​​to compensate for PHP's lack of PHP? (I haven't used this as it means there are drawbacks).

Question

Coming from the Java / C # world, PHP bulk type manipulation has always been somewhat annoying. It got better when type-hinting for input parameters was introduced, but I'm still missing generics and type-hinting for return values.

I sometimes run into this when explicitly checking types within my code - which seems a bit wrong since the language itself can handle this for me - and I'd like to pass these questions on to the community:

  • Are form validation return values ​​good practice to make up for the lack of PHP generics?
  • Is there a better / more standard way to do this?
  • Are general concepts currently being considered for a future PHP implementation?

Example

To better understand why this is problematic, consider the following example:

Let's say we are building a framework to transform input data into some other output. Examples:

Convert a string representing an XML document in a DomDocument to another string by selecting the title of the specified DomDocument using the xpath expression.

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

      

Now, suppose the input is not a string containing XML, but Json (but at the same time contains the same data). Now we want to convert the Json input to a Json object and write the header using the JsonPath expression .

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

      

(Note: In the second example, it should be clear that the whole structure should be really flexible.)

The conversion is done using a chaining of adaptive objects that handle the conversion from input to output:

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;
  }

}

      

Using:

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

      

The problematic part arises when the adapter relies on it by its predecessor to return the correct input.

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

      

In C #, I would solve this problem using 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'
    }
//...

      

Since generics are not supported in PHP, I ask myself if it is worth adding a type check after each call transform($data)

like this:

    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'
    }

      

My current workaround

I am currently using several interfaces to define the output of a method transform

like this:

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);
}

      

In every transformer, I make sure to only accept a suitable interface as a predecessor:

class DomDocumentToStringAdapter implements ToStringAdapterInterface {

  private $xpathExpression;

  private $predecessor;

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

      

+3


source to share


1 answer


I would follow your approach: check the datatype of the return value $this->predecessor->transform($data)

and throw an exception if it is not what was expected.

I don't know if you might be interested in Hacking the Facebook Programming Language :



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

0


source







All Articles