DEV Community

Satoshi Nishimura
Satoshi Nishimura

Posted on

Checks calling echo with no htmlspecialchars when using pure PHP as template with PHPStan

The only and greatest benefit of using a template engine library with PHP is that it runs htmlspecialchars without omission.

https://github.com/phpstan/phpstan/issues/351#issuecomment-381421561

As commented in this issue, I partially agree with using standard PHP classes for type checking.
Partial means that I want to write a mixed HTML and PHP template as shown below.

<?php

namespace App;

class ProductDto
{
    /** @var int */
    public $product_id;
    /** @var string */
    public $name;
    /** @var ?string */
    public $description;
}
Enter fullscreen mode Exit fullscreen mode
<?php
namespace App;
class ProductHtml {
    public function view(ProductDto $product): void {
?>

<div>
  <div>
    <?= $product->product_id ?>
  </div>
  <div>
    <?= $product->name ?>
  </div>
  <div>
    <?= $product->description ?>
  </div>
</div>

<?php
    }
}
Enter fullscreen mode Exit fullscreen mode

PHPStan (or rather PHP-Parser) correctly parses PHP with mixed HTML.
The only complaint about this way is that it cannot check htmlspecialchars exhaustively.
For example, <? = $ Product-> description?> May cause XSS.
PHPStan has a plugin mechanism, so we decided to add a htmlspecialchars check as a trial.

GitHub logo nishphp / phpstan-echo-html-rule

htmlspecialchars checker for PHPStan

PHPStan Echo Html Rule Extension

This package is a PHPStan extension for checking whether htmlspecialchars is called from a pure PHP template.

Install

composer require --dev nish/phpstan-echo-html-rule

How to use

Add to phpstan.neon

includes
  - vendor/nish/phpstan-echo-html-rule/rules.neon
Enter fullscreen mode Exit fullscreen mode

If your composer.json is:

    "autoload": {
        "psr-4": { "App\\": "src" }
        "files": [
            "src/functions.php"
        ]
    },
Enter fullscreen mode Exit fullscreen mode

Value Object class src/ProductDto.php:

<?php
namespace App;

class ProductDto
{
    /** @var
 int */
    public $product_id;
    /** @var
 string */
    public $name;
    /** @var
 ?string */
    public $description;
}
Enter fullscreen mode Exit fullscreen mode

Html Template src/ProductHtml.php:

<?php
namespace App
class ProductHtml {
    public function view(ProductDto $product): void {
?>

<div>
  <div>
    <?= $product->product_id ?>
  </div>
  <div>
    <?= $product
Enter fullscreen mode Exit fullscreen mode

Adding this extension will report an error similar to the following:

 3/3 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%

 ------ ---------------------------------------------------- 
  Line   ProductHtml.php                                     
 ------ ---------------------------------------------------- 
  16     Parameter #1 (string) is not safehtml-string.       
  19     Parameter #1 (string|null) is not safehtml-string.  
 ------ ---------------------------------------------------- 


 [ERROR] Found 2 errors
Enter fullscreen mode Exit fullscreen mode

htmlspecialchars needs return safehtml-string because the extension added a virtual type safehtml-string.

<?php

/**
 * @param int|string|null $input
 * @return safehtml-string
 */
function h($input)
{
    return htmlspecialchars((string)$input);
}

/**
 * @param int|string|null $input
 * @return safehtml-string
 */
function raw($input)
{
    return (string)$input;
}
Enter fullscreen mode Exit fullscreen mode

Then, correct the part that caused the error.

<?php
namespace App;
class ProductHtml {
    public function view(ProductDto $product): void {
?>

<div>
  <div>
    <?= $product->product_id ?>
  </div>
  <div>
    <?= h($product->name) ?>
  </div>
  <div>
    <?= h($product->description) ?>
  </div>
</div>

<?php
    }
}
Enter fullscreen mode Exit fullscreen mode

All other echo calls that do not use htmlspecialchars are now reported as errors too.

Top comments (0)