[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/applications/diffusion/protocol/ -> DiffusionSubversionWireProtocol.php (source)

   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  }


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1