[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 final class DiffusionSubversionWireProtocol extends Phobject { 4 5 private $buffer = ''; 6 private $state = 'item'; 7 private $expectBytes = 0; 8 private $byteBuffer = ''; 9 private $stack = array(); 10 private $list = array(); 11 private $raw = ''; 12 13 private function pushList() { 14 $this->stack[] = $this->list; 15 $this->list = array(); 16 } 17 18 private function popList() { 19 $list = $this->list; 20 $this->list = array_pop($this->stack); 21 return $list; 22 } 23 24 private function pushItem($item, $type) { 25 $this->list[] = array( 26 'type' => $type, 27 'value' => $item, 28 ); 29 } 30 31 public function writeData($data) { 32 $this->buffer .= $data; 33 34 $messages = array(); 35 while (true) { 36 if ($this->state == 'item') { 37 $match = null; 38 $result = null; 39 $buf = $this->buffer; 40 if (preg_match('/^([a-z][a-z0-9-]*)\s/i', $buf, $match)) { 41 $this->pushItem($match[1], 'word'); 42 } else if (preg_match('/^(\d+)\s/', $buf, $match)) { 43 $this->pushItem((int)$match[1], 'number'); 44 } else if (preg_match('/^(\d+):/', $buf, $match)) { 45 // NOTE: The "+ 1" includes the space after the string. 46 $this->expectBytes = (int)$match[1] + 1; 47 $this->state = 'bytes'; 48 } else if (preg_match('/^(\\()\s/', $buf, $match)) { 49 $this->pushList(); 50 } else if (preg_match('/^(\\))\s/', $buf, $match)) { 51 $list = $this->popList(); 52 if ($this->stack) { 53 $this->pushItem($list, 'list'); 54 } else { 55 $result = $list; 56 } 57 } else { 58 $match = false; 59 } 60 61 if ($match !== false) { 62 $this->raw .= substr($this->buffer, 0, strlen($match[0])); 63 $this->buffer = substr($this->buffer, strlen($match[0])); 64 65 if ($result !== null) { 66 $messages[] = array( 67 'structure' => $list, 68 'raw' => $this->raw, 69 ); 70 $this->raw = ''; 71 } 72 } else { 73 // No matches yet, wait for more data. 74 break; 75 } 76 } else if ($this->state == 'bytes') { 77 $new_data = substr($this->buffer, 0, $this->expectBytes); 78 if (!strlen($new_data)) { 79 // No more bytes available yet, wait for more data. 80 break; 81 } 82 $this->buffer = substr($this->buffer, strlen($new_data)); 83 84 $this->expectBytes -= strlen($new_data); 85 $this->raw .= $new_data; 86 $this->byteBuffer .= $new_data; 87 88 if (!$this->expectBytes) { 89 $this->state = 'byte-space'; 90 // Strip off the terminal space. 91 $this->pushItem(substr($this->byteBuffer, 0, -1), 'string'); 92 $this->byteBuffer = ''; 93 $this->state = 'item'; 94 } 95 } else { 96 throw new Exception("Invalid state '{$this->state}'!"); 97 } 98 } 99 100 return $messages; 101 } 102 103 /** 104 * Convert a parsed command struct into a wire protocol string. 105 */ 106 public function serializeStruct(array $struct) { 107 $out = array(); 108 109 $out[] = '( '; 110 foreach ($struct as $item) { 111 $value = $item['value']; 112 $type = $item['type']; 113 switch ($type) { 114 case 'word': 115 $out[] = $value; 116 break; 117 case 'number': 118 $out[] = $value; 119 break; 120 case 'string': 121 $out[] = strlen($value).':'.$value; 122 break; 123 case 'list': 124 $out[] = self::serializeStruct($value); 125 break; 126 default: 127 throw new Exception("Unknown SVN wire protocol structure '{$type}'!"); 128 } 129 if ($type != 'list') { 130 $out[] = ' '; 131 } 132 } 133 $out[] = ') '; 134 135 return implode('', $out); 136 } 137 138 public function isReadOnlyCommand(array $struct) { 139 if (empty($struct[0]['type']) || ($struct[0]['type'] != 'word')) { 140 // This isn't what we expect; fail defensively. 141 throw new Exception( 142 pht("Unexpected command structure, expected '( word ... )'.")); 143 } 144 145 switch ($struct[0]['value']) { 146 // Authentication command set. 147 case 'EXTERNAL': 148 149 // The "Main" command set. Some of the commands in this command set are 150 // mutation commands, and are omitted from this list. 151 case 'reparent': 152 case 'get-latest-rev': 153 case 'get-dated-rev': 154 case 'rev-proplist': 155 case 'rev-prop': 156 case 'get-file': 157 case 'get-dir': 158 case 'check-path': 159 case 'stat': 160 case 'update': 161 case 'get-mergeinfo': 162 case 'switch': 163 case 'status': 164 case 'diff': 165 case 'log': 166 case 'get-file-revs': 167 case 'get-locations': 168 169 // The "Report" command set. These are not actually mutation 170 // operations, they just define a request for information. 171 case 'set-path': 172 case 'delete-path': 173 case 'link-path': 174 case 'finish-report': 175 case 'abort-report': 176 177 // These are used to report command results. 178 case 'success': 179 case 'failure': 180 181 // If we get here, we've matched some known read-only command. 182 return true; 183 default: 184 // Anything else isn't a known read-only command, so require write 185 // access to use it. 186 break; 187 } 188 189 return false; 190 } 191 192 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |