11
11
*/
12
12
final class ArrayParser
13
13
{
14
+ private const WHITESPACE = [" " , "\n" , "\r" , "\t" , "\v" , "\0" ];
15
+
14
16
use ForbidCloning;
15
17
use ForbidSerialization;
16
18
@@ -25,12 +27,10 @@ final class ArrayParser
25
27
*/
26
28
public static function parse (string $ data , \Closure $ cast , string $ delimiter = ', ' ): array
27
29
{
28
- $ data = \trim ($ data );
29
-
30
30
$ parser = new self ($ data , $ cast , $ delimiter );
31
31
$ result = $ parser ->parseToArray ();
32
32
33
- if ($ parser ->data !== '' ) {
33
+ if (isset ( $ parser ->data [ $ parser -> position ]) ) {
34
34
throw new PostgresParseException ("Data left in buffer after parsing " );
35
35
}
36
36
@@ -43,9 +43,10 @@ public static function parse(string $data, \Closure $cast, string $delimiter = '
43
43
* @param string $delimiter Delimiter used to separate values.
44
44
*/
45
45
private function __construct (
46
- private string $ data ,
46
+ private readonly string $ data ,
47
47
private readonly \Closure $ cast ,
48
48
private readonly string $ delimiter = ', ' ,
49
+ private int $ position = 0 ,
49
50
) {
50
51
}
51
52
@@ -58,36 +59,35 @@ private function parseToArray(): array
58
59
{
59
60
$ result = [];
60
61
61
- if ($ this ->data === '' ) {
62
- throw new PostgresParseException ("Unexpected end of data " );
63
- }
62
+ $ this ->position = $ this ->skipWhitespace ($ this ->position );
64
63
65
- if ($ this ->data [0 ] !== '{ ' ) {
64
+ if (! isset ( $ this ->data [$ this -> position ]) || $ this -> data [ $ this -> position ] !== '{ ' ) {
66
65
throw new PostgresParseException ("Missing opening bracket " );
67
66
}
68
67
69
- $ this ->data = \ltrim ( \substr ( $ this ->data , 1 ) );
68
+ $ this ->position = $ this -> skipWhitespace ( $ this ->position + 1 );
70
69
71
70
do {
72
- if ($ this ->data === '' ) {
71
+ if (! isset ( $ this ->data [ $ this -> position ]) ) {
73
72
throw new PostgresParseException ("Unexpected end of data " );
74
73
}
75
74
76
- if ($ this ->data [0 ] === '} ' ) { // Empty array
77
- $ this ->data = \ltrim ( \substr ( $ this ->data , 1 ) );
75
+ if ($ this ->data [$ this -> position ] === '} ' ) { // Empty array
76
+ $ this ->position = $ this -> skipWhitespace ( $ this ->position + 1 );
78
77
break ;
79
78
}
80
79
81
- if ($ this ->data [0 ] === '{ ' ) { // Array
82
- $ parser = new self ($ this ->data , $ this ->cast , $ this ->delimiter );
80
+ if ($ this ->data [$ this -> position ] === '{ ' ) { // Array
81
+ $ parser = new self ($ this ->data , $ this ->cast , $ this ->delimiter , $ this -> position );
83
82
$ result [] = $ parser ->parseToArray ();
84
- $ this ->data = $ parser ->data ;
85
- $ end = $ this ->trim ( 0 );
83
+ $ this ->position = $ parser ->position ;
84
+ $ delimiter = $ this ->moveToNextDelimiter ( $ this -> position );
86
85
continue ;
87
86
}
88
87
89
- if ($ this ->data [0 ] === '" ' ) { // Quoted value
90
- for ($ position = 1 ; isset ($ this ->data [$ position ]); ++$ position ) {
88
+ if ($ this ->data [$ this ->position ] === '" ' ) { // Quoted value
89
+ ++$ this ->position ;
90
+ for ($ position = $ this ->position ; isset ($ this ->data [$ position ]); ++$ position ) {
91
91
if ($ this ->data [$ position ] === '\\' ) {
92
92
++$ position ; // Skip next character
93
93
continue ;
@@ -102,27 +102,30 @@ private function parseToArray(): array
102
102
throw new PostgresParseException ("Could not find matching quote in quoted value " );
103
103
}
104
104
105
- $ yield = \stripslashes (\substr ($ this ->data , 1 , $ position - 1 ));
105
+ $ entry = \stripslashes (\substr ($ this ->data , $ this -> position , $ position - $ this -> position ));
106
106
107
- $ end = $ this ->trim ($ position + 1 );
107
+ $ delimiter = $ this ->moveToNextDelimiter ($ position + 1 );
108
108
} else { // Unquoted value
109
- $ position = 0 ;
110
- while (isset ($ this ->data [$ position ]) && $ this ->data [$ position ] !== $ this ->delimiter && $ this ->data [$ position ] !== '} ' ) {
109
+ $ position = $ this ->position ;
110
+ while (isset ($ this ->data [$ position ])
111
+ && $ this ->data [$ position ] !== $ this ->delimiter
112
+ && $ this ->data [$ position ] !== '} '
113
+ ) {
111
114
++$ position ;
112
115
}
113
116
114
- $ yield = \trim (\substr ($ this ->data , 0 , $ position ));
117
+ $ entry = \trim (\substr ($ this ->data , $ this -> position , $ position - $ this -> position ));
115
118
116
- $ end = $ this ->trim ($ position );
119
+ $ delimiter = $ this ->moveToNextDelimiter ($ position );
117
120
118
- if (\strcasecmp ($ yield , "NULL " ) === 0 ) { // Literal NULL is always unquoted.
121
+ if (\strcasecmp ($ entry , "NULL " ) === 0 ) { // Literal NULL is always unquoted.
119
122
$ result [] = null ;
120
123
continue ;
121
124
}
122
125
}
123
126
124
- $ result [] = ($ this ->cast )($ yield );
125
- } while ($ end !== '} ' );
127
+ $ result [] = ($ this ->cast )($ entry );
128
+ } while ($ delimiter !== '} ' );
126
129
127
130
return $ result ;
128
131
}
@@ -134,22 +137,31 @@ private function parseToArray(): array
134
137
*
135
138
* @throws PostgresParseException
136
139
*/
137
- private function trim (int $ position ): string
140
+ private function moveToNextDelimiter (int $ position ): string
138
141
{
139
- $ this -> data = \ltrim ( \substr ( $ this ->data , $ position) );
142
+ $ position = $ this ->skipWhitespace ( $ position );
140
143
141
- if ($ this ->data === '' ) {
144
+ if (! isset ( $ this ->data [ $ position ]) ) {
142
145
throw new PostgresParseException ("Unexpected end of data " );
143
146
}
144
147
145
- $ end = $ this ->data [0 ];
148
+ $ delimiter = $ this ->data [$ position ];
146
149
147
- if ($ end !== $ this ->delimiter && $ end !== '} ' ) {
150
+ if ($ delimiter !== $ this ->delimiter && $ delimiter !== '} ' ) {
148
151
throw new PostgresParseException ("Invalid delimiter " );
149
152
}
150
153
151
- $ this ->data = \ltrim (\substr ($ this ->data , 1 ));
154
+ $ this ->position = $ this ->skipWhitespace ($ position + 1 );
155
+
156
+ return $ delimiter ;
157
+ }
158
+
159
+ private function skipWhitespace (int $ position ): int
160
+ {
161
+ while (isset ($ this ->data [$ position ]) && \in_array ($ this ->data [$ position ], self ::WHITESPACE , true )) {
162
+ ++$ position ;
163
+ }
152
164
153
- return $ end ;
165
+ return $ position ;
154
166
}
155
167
}
0 commit comments