[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/applications/phragment/conduit/ -> PhragmentGetPatchConduitAPIMethod.php (source)

   1  <?php
   2  
   3  final class PhragmentGetPatchConduitAPIMethod
   4    extends PhragmentConduitAPIMethod {
   5  
   6    public function getAPIMethodName() {
   7      return 'phragment.getpatch';
   8    }
   9  
  10    public function getMethodStatus() {
  11      return self::METHOD_STATUS_UNSTABLE;
  12    }
  13  
  14    public function getMethodDescription() {
  15      return pht('Retrieve the patches to apply for a given set of files.');
  16    }
  17  
  18    public function defineParamTypes() {
  19      return array(
  20        'path' => 'required string',
  21        'state' => 'required dict<string, string>',
  22      );
  23    }
  24  
  25    public function defineReturnType() {
  26      return 'nonempty dict';
  27    }
  28  
  29    public function defineErrorTypes() {
  30      return array(
  31        'ERR_BAD_FRAGMENT' => 'No such fragment exists',
  32      );
  33    }
  34  
  35    protected function execute(ConduitAPIRequest $request) {
  36      $path = $request->getValue('path');
  37      $state = $request->getValue('state');
  38      // The state is an array mapping file paths to hashes.
  39  
  40      $patches = array();
  41  
  42      // We need to get all of the mappings (like phragment.getstate) first
  43      // so that we can detect deletions and creations of files.
  44      $fragment = id(new PhragmentFragmentQuery())
  45        ->setViewer($request->getUser())
  46        ->withPaths(array($path))
  47        ->executeOne();
  48      if ($fragment === null) {
  49        throw new ConduitException('ERR_BAD_FRAGMENT');
  50      }
  51  
  52      $mappings = $fragment->getFragmentMappings(
  53        $request->getUser(),
  54        $fragment->getPath());
  55  
  56      $file_phids = mpull(mpull($mappings, 'getLatestVersion'), 'getFilePHID');
  57      $files = id(new PhabricatorFileQuery())
  58        ->setViewer($request->getUser())
  59        ->withPHIDs($file_phids)
  60        ->execute();
  61      $files = mpull($files, null, 'getPHID');
  62  
  63      // Scan all of the files that the caller currently has and iterate
  64      // over that.
  65      foreach ($state as $path => $hash) {
  66        // If $mappings[$path] exists, then the user has the file and it's
  67        // also a fragment.
  68        if (array_key_exists($path, $mappings)) {
  69          $file_phid = $mappings[$path]->getLatestVersion()->getFilePHID();
  70          if ($file_phid !== null) {
  71            // If the file PHID is present, then we need to check the
  72            // hashes to see if they are the same.
  73            $hash_caller = strtolower($state[$path]);
  74            $hash_current = $files[$file_phid]->getContentHash();
  75            if ($hash_caller === $hash_current) {
  76              // The user's version is identical to our version, so
  77              // there is no update needed.
  78            } else {
  79              // The hash differs, and the user needs to update.
  80              $patches[] = array(
  81                'path' => $path,
  82                'fileOld' => null,
  83                'fileNew' => $files[$file_phid],
  84                'hashOld' => $hash_caller,
  85                'hashNew' => $hash_current,
  86                'patchURI' => null,
  87              );
  88            }
  89          } else {
  90            // We have a record of this as a file, but there is no file
  91            // attached to the latest version, so we consider this to be
  92            // a deletion.
  93            $patches[] = array(
  94              'path' => $path,
  95              'fileOld' => null,
  96              'fileNew' => null,
  97              'hashOld' => $hash_caller,
  98              'hashNew' => PhragmentPatchUtil::EMPTY_HASH,
  99              'patchURI' => null,
 100            );
 101          }
 102        } else {
 103          // If $mappings[$path] does not exist, then the user has a file,
 104          // and we have absolutely no record of it what-so-ever (we haven't
 105          // even recorded a deletion). Assuming most applications will store
 106          // some form of data near their own files, this is probably a data
 107          // file relevant for the application that is not versioned, so we
 108          // don't tell the client to do anything with it.
 109        }
 110      }
 111  
 112      // Check the remaining files that we know about but the caller has
 113      // not reported.
 114      foreach ($mappings as $path => $child) {
 115        if (array_key_exists($path, $state)) {
 116          // We have already evaluated this above.
 117        } else {
 118          $file_phid = $mappings[$path]->getLatestVersion()->getFilePHID();
 119          if ($file_phid !== null) {
 120            // If the file PHID is present, then this is a new file that
 121            // we know about, but the caller does not. We need to tell
 122            // the caller to create the file.
 123            $hash_current = $files[$file_phid]->getContentHash();
 124            $patches[] = array(
 125              'path' => $path,
 126              'fileOld' => null,
 127              'fileNew' => $files[$file_phid],
 128              'hashOld' => PhragmentPatchUtil::EMPTY_HASH,
 129              'hashNew' => $hash_current,
 130              'patchURI' => null,
 131            );
 132          } else {
 133            // We have a record of deleting this file, and the caller hasn't
 134            // reported it, so they've probably deleted it in a previous
 135            // update.
 136          }
 137        }
 138      }
 139  
 140      // Before we can calculate patches, we need to resolve the old versions
 141      // of files so we can draw diffs on them.
 142      $hashes = array();
 143      foreach ($patches as $patch) {
 144        if ($patch['hashOld'] !== PhragmentPatchUtil::EMPTY_HASH) {
 145          $hashes[] = $patch['hashOld'];
 146        }
 147      }
 148      $old_files = array();
 149      if (count($hashes) !== 0) {
 150        $old_files = id(new PhabricatorFileQuery())
 151          ->setViewer($request->getUser())
 152          ->withContentHashes($hashes)
 153          ->execute();
 154      }
 155      $old_files = mpull($old_files, null, 'getContentHash');
 156      foreach ($patches as $key => $patch) {
 157        if ($patch['hashOld'] !== PhragmentPatchUtil::EMPTY_HASH) {
 158          if (array_key_exists($patch['hashOld'], $old_files)) {
 159            $patches[$key]['fileOld'] = $old_files[$patch['hashOld']];
 160          } else {
 161            // We either can't see or can't read the old file.
 162            $patches[$key]['hashOld'] = PhragmentPatchUtil::EMPTY_HASH;
 163            $patches[$key]['fileOld'] = null;
 164          }
 165        }
 166      }
 167  
 168      // Now run through all of the patch entries, calculate the patches
 169      // and return the results.
 170      foreach ($patches as $key => $patch) {
 171        $data = PhragmentPatchUtil::calculatePatch(
 172          $patches[$key]['fileOld'],
 173          $patches[$key]['fileNew']);
 174        unset($patches[$key]['fileOld']);
 175        unset($patches[$key]['fileNew']);
 176  
 177        $file = PhabricatorFile::buildFromFileDataOrHash(
 178          $data,
 179          array(
 180            'name' => 'patch.dmp',
 181            'ttl' => time() + 60 * 60 * 24,
 182          ));
 183        $patches[$key]['patchURI'] = $file->getDownloadURI();
 184      }
 185  
 186      return $patches;
 187    }
 188  
 189  }


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