Chris@0
|
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@0
|
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@0
|
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@0
|
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@0
|
41 public function getSubNodeNames() : array {
|
Chris@0
|
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@0
|
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@0
|
63 ['\\\\', '\\\''],
|
Chris@0
|
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@0
|
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@0
|
114 /**
|
Chris@0
|
115 * Converts a Unicode code point to its UTF-8 encoded representation.
|
Chris@0
|
116 *
|
Chris@0
|
117 * @param int $num Code point
|
Chris@0
|
118 *
|
Chris@0
|
119 * @return string UTF-8 representation of code point
|
Chris@0
|
120 */
|
Chris@0
|
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@0
|
137
|
Chris@0
|
138 /**
|
Chris@0
|
139 * @internal
|
Chris@0
|
140 *
|
Chris@0
|
141 * Parses a constant doc string.
|
Chris@0
|
142 *
|
Chris@0
|
143 * @param string $startToken Doc string start token content (<<<SMTHG)
|
Chris@0
|
144 * @param string $str String token content
|
Chris@0
|
145 * @param bool $parseUnicodeEscape Whether to parse PHP 7 \u escapes
|
Chris@0
|
146 *
|
Chris@0
|
147 * @return string Parsed string
|
Chris@0
|
148 */
|
Chris@0
|
149 public static function parseDocString(string $startToken, string $str, bool $parseUnicodeEscape = true) : string {
|
Chris@0
|
150 // strip last newline (thanks tokenizer for sticking it into the string!)
|
Chris@0
|
151 $str = preg_replace('~(\r\n|\n|\r)\z~', '', $str);
|
Chris@0
|
152
|
Chris@0
|
153 // nowdoc string
|
Chris@0
|
154 if (false !== strpos($startToken, '\'')) {
|
Chris@0
|
155 return $str;
|
Chris@0
|
156 }
|
Chris@0
|
157
|
Chris@0
|
158 return self::parseEscapeSequences($str, null, $parseUnicodeEscape);
|
Chris@0
|
159 }
|
Chris@0
|
160
|
Chris@0
|
161 public function getType() : string {
|
Chris@0
|
162 return 'Scalar_String';
|
Chris@0
|
163 }
|
Chris@0
|
164 }
|