Chris@13
|
1 <?php declare(strict_types=1);
|
Chris@0
|
2
|
Chris@0
|
3 namespace PhpParser\Node\Scalar;
|
Chris@0
|
4
|
Chris@0
|
5 use PhpParser\Error;
|
Chris@0
|
6 use PhpParser\Node\Scalar;
|
Chris@0
|
7
|
Chris@0
|
8 class String_ extends Scalar
|
Chris@0
|
9 {
|
Chris@0
|
10 /* For use in "kind" attribute */
|
Chris@0
|
11 const KIND_SINGLE_QUOTED = 1;
|
Chris@0
|
12 const KIND_DOUBLE_QUOTED = 2;
|
Chris@0
|
13 const KIND_HEREDOC = 3;
|
Chris@0
|
14 const KIND_NOWDOC = 4;
|
Chris@0
|
15
|
Chris@0
|
16 /** @var string String value */
|
Chris@0
|
17 public $value;
|
Chris@0
|
18
|
Chris@13
|
19 protected static $replacements = [
|
Chris@0
|
20 '\\' => '\\',
|
Chris@0
|
21 '$' => '$',
|
Chris@0
|
22 'n' => "\n",
|
Chris@0
|
23 'r' => "\r",
|
Chris@0
|
24 't' => "\t",
|
Chris@0
|
25 'f' => "\f",
|
Chris@0
|
26 'v' => "\v",
|
Chris@0
|
27 'e' => "\x1B",
|
Chris@13
|
28 ];
|
Chris@0
|
29
|
Chris@0
|
30 /**
|
Chris@0
|
31 * Constructs a string scalar node.
|
Chris@0
|
32 *
|
Chris@0
|
33 * @param string $value Value of the string
|
Chris@0
|
34 * @param array $attributes Additional attributes
|
Chris@0
|
35 */
|
Chris@13
|
36 public function __construct(string $value, array $attributes = []) {
|
Chris@0
|
37 parent::__construct($attributes);
|
Chris@0
|
38 $this->value = $value;
|
Chris@0
|
39 }
|
Chris@0
|
40
|
Chris@13
|
41 public function getSubNodeNames() : array {
|
Chris@13
|
42 return ['value'];
|
Chris@0
|
43 }
|
Chris@0
|
44
|
Chris@0
|
45 /**
|
Chris@0
|
46 * @internal
|
Chris@0
|
47 *
|
Chris@0
|
48 * Parses a string token.
|
Chris@0
|
49 *
|
Chris@0
|
50 * @param string $str String token content
|
Chris@0
|
51 * @param bool $parseUnicodeEscape Whether to parse PHP 7 \u escapes
|
Chris@0
|
52 *
|
Chris@0
|
53 * @return string The parsed string
|
Chris@0
|
54 */
|
Chris@13
|
55 public static function parse(string $str, bool $parseUnicodeEscape = true) : string {
|
Chris@0
|
56 $bLength = 0;
|
Chris@0
|
57 if ('b' === $str[0] || 'B' === $str[0]) {
|
Chris@0
|
58 $bLength = 1;
|
Chris@0
|
59 }
|
Chris@0
|
60
|
Chris@0
|
61 if ('\'' === $str[$bLength]) {
|
Chris@0
|
62 return str_replace(
|
Chris@13
|
63 ['\\\\', '\\\''],
|
Chris@13
|
64 ['\\', '\''],
|
Chris@0
|
65 substr($str, $bLength + 1, -1)
|
Chris@0
|
66 );
|
Chris@0
|
67 } else {
|
Chris@0
|
68 return self::parseEscapeSequences(
|
Chris@0
|
69 substr($str, $bLength + 1, -1), '"', $parseUnicodeEscape
|
Chris@0
|
70 );
|
Chris@0
|
71 }
|
Chris@0
|
72 }
|
Chris@0
|
73
|
Chris@0
|
74 /**
|
Chris@0
|
75 * @internal
|
Chris@0
|
76 *
|
Chris@0
|
77 * Parses escape sequences in strings (all string types apart from single quoted).
|
Chris@0
|
78 *
|
Chris@0
|
79 * @param string $str String without quotes
|
Chris@0
|
80 * @param null|string $quote Quote type
|
Chris@0
|
81 * @param bool $parseUnicodeEscape Whether to parse PHP 7 \u escapes
|
Chris@0
|
82 *
|
Chris@0
|
83 * @return string String with escape sequences parsed
|
Chris@0
|
84 */
|
Chris@13
|
85 public static function parseEscapeSequences(string $str, $quote, bool $parseUnicodeEscape = true) : string {
|
Chris@0
|
86 if (null !== $quote) {
|
Chris@0
|
87 $str = str_replace('\\' . $quote, $quote, $str);
|
Chris@0
|
88 }
|
Chris@0
|
89
|
Chris@0
|
90 $extra = '';
|
Chris@0
|
91 if ($parseUnicodeEscape) {
|
Chris@0
|
92 $extra = '|u\{([0-9a-fA-F]+)\}';
|
Chris@0
|
93 }
|
Chris@0
|
94
|
Chris@0
|
95 return preg_replace_callback(
|
Chris@0
|
96 '~\\\\([\\\\$nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3}' . $extra . ')~',
|
Chris@0
|
97 function($matches) {
|
Chris@0
|
98 $str = $matches[1];
|
Chris@0
|
99
|
Chris@0
|
100 if (isset(self::$replacements[$str])) {
|
Chris@0
|
101 return self::$replacements[$str];
|
Chris@0
|
102 } elseif ('x' === $str[0] || 'X' === $str[0]) {
|
Chris@0
|
103 return chr(hexdec($str));
|
Chris@0
|
104 } elseif ('u' === $str[0]) {
|
Chris@0
|
105 return self::codePointToUtf8(hexdec($matches[2]));
|
Chris@0
|
106 } else {
|
Chris@0
|
107 return chr(octdec($str));
|
Chris@0
|
108 }
|
Chris@0
|
109 },
|
Chris@0
|
110 $str
|
Chris@0
|
111 );
|
Chris@0
|
112 }
|
Chris@0
|
113
|
Chris@13
|
114 /**
|
Chris@13
|
115 * Converts a Unicode code point to its UTF-8 encoded representation.
|
Chris@13
|
116 *
|
Chris@13
|
117 * @param int $num Code point
|
Chris@13
|
118 *
|
Chris@13
|
119 * @return string UTF-8 representation of code point
|
Chris@13
|
120 */
|
Chris@13
|
121 private static function codePointToUtf8(int $num) : string {
|
Chris@0
|
122 if ($num <= 0x7F) {
|
Chris@0
|
123 return chr($num);
|
Chris@0
|
124 }
|
Chris@0
|
125 if ($num <= 0x7FF) {
|
Chris@0
|
126 return chr(($num>>6) + 0xC0) . chr(($num&0x3F) + 0x80);
|
Chris@0
|
127 }
|
Chris@0
|
128 if ($num <= 0xFFFF) {
|
Chris@0
|
129 return chr(($num>>12) + 0xE0) . chr((($num>>6)&0x3F) + 0x80) . chr(($num&0x3F) + 0x80);
|
Chris@0
|
130 }
|
Chris@0
|
131 if ($num <= 0x1FFFFF) {
|
Chris@0
|
132 return chr(($num>>18) + 0xF0) . chr((($num>>12)&0x3F) + 0x80)
|
Chris@0
|
133 . chr((($num>>6)&0x3F) + 0x80) . chr(($num&0x3F) + 0x80);
|
Chris@0
|
134 }
|
Chris@0
|
135 throw new Error('Invalid UTF-8 codepoint escape sequence: Codepoint too large');
|
Chris@0
|
136 }
|
Chris@13
|
137
|
Chris@13
|
138 public function getType() : string {
|
Chris@13
|
139 return 'Scalar_String';
|
Chris@13
|
140 }
|
Chris@0
|
141 }
|