@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
at upstream/main 150 lines 3.2 kB view raw
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}