@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3final class AphrontHTTPHeaderParser extends Phobject {
4
5 private $name;
6 private $content;
7 private $pairs;
8
9 public function parseRawHeader($raw_header) {
10 $this->name = null;
11 $this->content = null;
12
13 $parts = explode(':', $raw_header, 2);
14 $this->name = trim($parts[0]);
15 if (count($parts) > 1) {
16 $this->content = trim($parts[1]);
17 }
18
19 $this->pairs = null;
20
21 return $this;
22 }
23
24 public function getHeaderName() {
25 $this->requireParse();
26 return $this->name;
27 }
28
29 public function getHeaderContent() {
30 $this->requireParse();
31 return $this->content;
32 }
33
34 public function getHeaderContentAsPairs() {
35 $content = $this->getHeaderContent();
36
37
38 $state = 'prekey';
39 $length = strlen($content);
40
41 $pair_name = null;
42 $pair_value = null;
43
44 $pairs = array();
45 $ii = 0;
46 while ($ii < $length) {
47 $c = $content[$ii];
48
49 switch ($state) {
50 case 'prekey':
51 // We're eating space in front of a key.
52 if ($c == ' ') {
53 $ii++;
54 break;
55 }
56 $pair_name = '';
57 $state = 'key';
58 break;
59 case 'key':
60 // We're parsing a key name until we find "=" or ";".
61 if ($c == ';') {
62 $state = 'done';
63 break;
64 }
65
66 if ($c == '=') {
67 $ii++;
68 $state = 'value';
69 break;
70 }
71
72 $ii++;
73 $pair_name .= $c;
74 break;
75 case 'value':
76 // We found an "=", so now figure out if the value is quoted
77 // or not.
78 if ($c == '"') {
79 $ii++;
80 $state = 'quoted';
81 break;
82 }
83 $state = 'unquoted';
84 break;
85 case 'quoted':
86 // We're in a quoted string, parse until we find the closing quote.
87 if ($c == '"') {
88 $ii++;
89 $state = 'done';
90 break;
91 }
92
93 $ii++;
94 $pair_value .= $c;
95 break;
96 case 'unquoted':
97 // We're in an unquoted string, parse until we find a space or a
98 // semicolon.
99 if ($c == ' ' || $c == ';') {
100 $state = 'done';
101 break;
102 }
103 $ii++;
104 $pair_value .= $c;
105 break;
106 case 'done':
107 // We parsed something, so eat any trailing whitespace and semicolons
108 // and look for a new value.
109 if ($c == ' ' || $c == ';') {
110 $ii++;
111 break;
112 }
113
114 $pairs[] = array(
115 $pair_name,
116 $pair_value,
117 );
118
119 $pair_name = null;
120 $pair_value = null;
121
122 $state = 'prekey';
123 break;
124 }
125 }
126
127 if ($state == 'quoted') {
128 throw new Exception(
129 pht(
130 'Header has unterminated double quote for key "%s".',
131 $pair_name));
132 }
133
134 if ($pair_name !== null) {
135 $pairs[] = array(
136 $pair_name,
137 $pair_value,
138 );
139 }
140
141 return $pairs;
142 }
143
144 private function requireParse() {
145 if ($this->name === null) {
146 throw new PhutilInvalidStateException('parseRawHeader');
147 }
148 }
149
150}