Skip to content

htmlspecialchars_decode() ENT_SUBSTITUTE flag has no effect #9582

Open

Description

@lucaswerkmeister

Description

The htmlspecialchars() function has an ENT_SUBSTITUTE flag (enabled by default since PHP 8.1) that changes its behavior when encountering invalid code unit sequences:

php > var_dump( htmlspecialchars( substr( 'abcdeä', 0, 6 ), 0 ) );
php shell code:1:
string(0) ""
php > var_dump( htmlspecialchars( substr( 'abcdeä', 0, 6 ), ENT_SUBSTITUTE ) );
php shell code:1:
string(8) "abcde�"

htmlspecialchars_decode() is documented (archive) to have the same flag, with the same effect, but actually it does nothing:

php > var_dump( htmlspecialchars_decode( substr( 'abcdeä', 0, 6 ), 0 ) );
php shell code:1:
string(6) "abcde"
php > var_dump( htmlspecialchars_decode( substr( 'abcdeä', 0, 6 ), ENT_SUBSTITUTE ) );
php shell code:1:
string(6) "abcde"

(Appearance on your terminal may vary; it actually just returns the incomplete code unit sequence. On my system, in php -a it looks empty, while PsySH renders it as Ã.)

According to 3v4l, this behavior has been the same since htmlspecialchars_decode() was introduced (from 5.1.0 all the way to 8.2rc2), so perhaps instead of changing the behavior, this should just be considered a documentation bug (remove the flag from the documentation)?

PHP Version

PHP 8.1.2

Operating System

Ubuntu 22.04

Activity

  1. lucaswerkmeister commented on Sep 20, 2022

    @lucaswerkmeister
    Author

    (The code example assumes UTF-8. substr( 'abcdeä', 0, 6 ) might not produce an incomplete code unit sequence under other encodings.)

  2. hormus commented on Sep 24, 2022

    @hormus

    There is no encoding parameter in htmlspecialchars_decode and therefore ENT_SUBSTITUTE cannot be applied.
    If you don't know your input string you will have to validate with htmlspecialchars and after htmlspecialchars_decode

    <?php
    
    $hexstring = "\x61\x62\x63\x64\x65\xC3";
    $output1 = htmlspecialchars( substr( $hexstring, 0, 6 ), ENT_SUBSTITUTE, 'UTF-8' ); //abcde(U+FFFD EF BF BD) 
    $output2 = htmlspecialchars_decode( $output1, ENT_SUBSTITUTE );
    var_dump( $output1, $output2 );
    
    ?>

    Example 2:

    <?php
    
    ob_start();
    ini_set( 'default_charset', 'EUC-JP' );
    header( 'Content-Type: text/plain; charset=EUC-JP' );
    ob_end_clean();
    $hexstring = "\x61\x62\x63\x64\x65\xC3";
    $output1 = htmlspecialchars( substr( $hexstring, 0, 6 ), ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, 'UTF-8' ); //abcde(U+FFFD EF BF BD) validate input hexstring
    $output2 = htmlspecialchars_decode( $output1 ); //decode
    $output2 = htmlspecialchars( $output2, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, 'EUC-JP' ); //convert from UTF-8 to EUC-JP
    $output2 = htmlspecialchars_decode( $output2 );
    var_dump( urlencode( $output1 ), urlencode( $output2 ) );
    
    ?>

    Expected result:

    string(14) "abcde%EF%BF%BD"
    string(25) "abcde%EF%BF%26%23xFFFD%3B"
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions