Skip to content

Commit

Permalink
Display String support
Browse files Browse the repository at this point in the history
  • Loading branch information
gapple authored Jan 2, 2024
1 parent 6113e97 commit fea0bb5
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 0 deletions.
21 changes: 21 additions & 0 deletions src/DisplayString.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace gapple\StructuredFields;

class DisplayString
{
/**
* @var string
*/
private $value;

public function __construct(string $value)
{
$this->value = $value;
}

public function __toString(): string
{
return $this->value;
}
}
46 changes: 46 additions & 0 deletions src/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ private static function parseBareItem(string &$string)
$value = self::parseBoolean($string);
} elseif ($string[0] == '@') {
$value = self::parseDate($string);
} elseif ($string[0] == '%') {
$value = self::parseDisplayString($string);
} elseif (preg_match('/^([a-z*])/i', $string)) {
$value = self::parseToken($string);
} else {
Expand Down Expand Up @@ -276,6 +278,50 @@ private static function parseString(string &$string): string
throw new ParseException("Invalid end of string");
}

private static function parseDisplayString(string &$string): DisplayString
{
if (strpos($string, '%"') !== 0) {
throw new ParseException("Invalid start of display string");
}

$string = substr($string, 2);

$encoded_string = '';
while (strlen($string)) {
$char = $string[0];
$string = substr($string, 1);

if (ord($char) <= 0x1f || ord($char) >= 0x7f) {
throw new ParseException('Invalid character in display string');
} elseif ($char == '%') {
if (strlen($string) < 2) {
throw new ParseException("Invalid end of display string");
}

$hex = substr($string, 0, 2);
$string = substr($string, 2);

if (!preg_match('/^[0-9a-z]{2}$/', $hex)) {
throw new ParseException('Invalid hex values in display string');
}

$encoded_string .= $char . $hex;
} elseif ($char == '"') {
$display_string = new DisplayString(rawurldecode($encoded_string));
// An invalid UTF-8 subject will cause the preg_* function to match nothing.
// @see https://www.php.net/manual/en/reference.pcre.pattern.modifiers.php
if (!preg_match('/^\X*$/u', $display_string)) {
throw new ParseException("Invalid byte sequence in display string");
}
return $display_string;
} else {
$encoded_string .= $char;
}
}

throw new ParseException("Invalid end of display string");
}

private static function parseToken(string &$string): Token
{
// Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing
Expand Down
12 changes: 12 additions & 0 deletions src/Serializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ private static function serializeBareItem($value): string
return self::serializeByteSequence($value);
} elseif ($value instanceof Date) {
return self::serializeDate($value);
} elseif ($value instanceof DisplayString) {
return self::serializeDisplayString($value);
} elseif (is_string($value) || (is_object($value) && method_exists($value, '__toString'))) {
return self::serializeString((string) $value);
}
Expand Down Expand Up @@ -209,6 +211,16 @@ private static function serializeString(string $value): string
return '"' . preg_replace('/(["\\\])/', '\\\$1', $value) . '"';
}

private static function serializeDisplayString(DisplayString $value): string
{
$encode_pattern = '/[%"\x00-\x1F\x7F-\xFF]/';
$encode_callback = function ($matches) {
return strtolower(rawurlencode($matches[0]));
};

return '%"' . preg_replace_callback($encode_pattern, $encode_callback, $value) . '"';
}

private static function serializeToken(Token $value): string
{
// Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing
Expand Down
8 changes: 8 additions & 0 deletions tests/Httpwg/DisplayStringTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace gapple\Tests\StructuredFields\Httpwg;

class DisplayStringTest extends HttpwgTest
{
protected $ruleset = 'display-string';
}
3 changes: 3 additions & 0 deletions tests/Httpwg/HttpwgTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use gapple\StructuredFields\Bytes;
use gapple\StructuredFields\Date;
use gapple\StructuredFields\Dictionary;
use gapple\StructuredFields\DisplayString;
use gapple\StructuredFields\InnerList;
use gapple\StructuredFields\Item;
use gapple\StructuredFields\OuterList;
Expand Down Expand Up @@ -186,6 +187,8 @@ private static function convertValue($data)
return new Bytes(Base32::decodeUpper($data->value));
case 'date':
return new Date($data->value);
case 'displaystring':
return new DisplayString($data->value);
}
}

Expand Down

0 comments on commit fea0bb5

Please sign in to comment.