[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/infrastructure/storage/patch/ -> PhabricatorSQLPatchList.php (source)

   1  <?php
   2  
   3  abstract class PhabricatorSQLPatchList {
   4  
   5    abstract function getNamespace();
   6    abstract function getPatches();
   7  
   8    /**
   9     * Examine a directory for `.php` and `.sql` files and build patch
  10     * specifications for them.
  11     */
  12    protected function buildPatchesFromDirectory($directory) {
  13      $patch_list = Filesystem::listDirectory(
  14        $directory,
  15        $include_hidden = false);
  16  
  17      sort($patch_list);
  18      $patches = array();
  19  
  20      foreach ($patch_list as $patch) {
  21        $matches = null;
  22        if (!preg_match('/\.(sql|php)$/', $patch, $matches)) {
  23          throw new Exception(
  24            pht(
  25              'Unknown patch "%s" in "%s", expected ".php" or ".sql" suffix.',
  26              $patch,
  27              $directory));
  28        }
  29  
  30        $patches[$patch] = array(
  31          'type' => $matches[1],
  32          'name' => rtrim($directory, '/').'/'.$patch,
  33        );
  34      }
  35  
  36      return $patches;
  37    }
  38  
  39    final public static function buildAllPatches() {
  40      $patch_lists = id(new PhutilSymbolLoader())
  41        ->setAncestorClass('PhabricatorSQLPatchList')
  42        ->setConcreteOnly(true)
  43        ->selectAndLoadSymbols();
  44  
  45      $specs = array();
  46      $seen_namespaces = array();
  47  
  48      foreach ($patch_lists as $patch_class) {
  49        $patch_class = $patch_class['name'];
  50        $patch_list = newv($patch_class, array());
  51  
  52        $namespace = $patch_list->getNamespace();
  53        if (isset($seen_namespaces[$namespace])) {
  54          $prior = $seen_namespaces[$namespace];
  55          throw new Exception(
  56            "PatchList '{$patch_class}' has the same namespace, '{$namespace}', ".
  57            "as another patch list class, '{$prior}'. Each patch list MUST have ".
  58            "a unique namespace.");
  59        }
  60  
  61        $last_key = null;
  62        foreach ($patch_list->getPatches() as $key => $patch) {
  63          if (!is_array($patch)) {
  64            throw new Exception(
  65              "PatchList '{$patch_class}' has a patch '{$key}' which is not ".
  66              "an array.");
  67          }
  68  
  69          $valid = array(
  70            'type'    => true,
  71            'name'    => true,
  72            'after'   => true,
  73            'legacy'  => true,
  74            'dead'    => true,
  75          );
  76  
  77          foreach ($patch as $pkey => $pval) {
  78            if (empty($valid[$pkey])) {
  79              throw new Exception(
  80                "PatchList '{$patch_class}' has a patch, '{$key}', with an ".
  81                "unknown property, '{$pkey}'. Patches must have only valid ".
  82                "keys: ".implode(', ', array_keys($valid)).'.');
  83            }
  84          }
  85  
  86          if (is_numeric($key)) {
  87            throw new Exception(
  88              "PatchList '{$patch_class}' has a patch with a numeric key, ".
  89              "'{$key}'. Patches must use string keys.");
  90          }
  91  
  92          if (strpos($key, ':') !== false) {
  93            throw new Exception(
  94              "PatchList '{$patch_class}' has a patch with a colon in the ".
  95              "key name, '{$key}'. Patch keys may not contain colons.");
  96          }
  97  
  98          $full_key = "{$namespace}:{$key}";
  99  
 100          if (isset($specs[$full_key])) {
 101            throw new Exception(
 102              "PatchList '{$patch_class}' has a patch '{$key}' which ".
 103              "duplicates an existing patch key.");
 104          }
 105  
 106          $patch['key']     = $key;
 107          $patch['fullKey'] = $full_key;
 108          $patch['dead']    = (bool)idx($patch, 'dead', false);
 109  
 110          if (isset($patch['legacy'])) {
 111            if ($namespace != 'phabricator') {
 112              throw new Exception(
 113                "Only patches in the 'phabricator' namespace may contain ".
 114                "'legacy' keys.");
 115            }
 116          } else {
 117            $patch['legacy'] = false;
 118          }
 119  
 120          if (!array_key_exists('after', $patch)) {
 121            if ($last_key === null) {
 122              throw new Exception(
 123                "Patch '{$full_key}' is missing key 'after', and is the first ".
 124                "patch in the patch list '{$patch_class}', so its application ".
 125                "order can not be determined implicitly. The first patch in a ".
 126                "patch list must list the patch or patches it depends on ".
 127                "explicitly.");
 128            } else {
 129              $patch['after'] = array($last_key);
 130            }
 131          }
 132          $last_key = $full_key;
 133  
 134          foreach ($patch['after'] as $after_key => $after) {
 135            if (strpos($after, ':') === false) {
 136              $patch['after'][$after_key] = $namespace.':'.$after;
 137            }
 138          }
 139  
 140          $type = idx($patch, 'type');
 141          if (!$type) {
 142            throw new Exception(
 143              "Patch '{$namespace}:{$key}' is missing key 'type'. Every patch ".
 144              "must have a type.");
 145          }
 146  
 147          switch ($type) {
 148            case 'db':
 149            case 'sql':
 150            case 'php':
 151              break;
 152            default:
 153              throw new Exception(
 154                "Patch '{$namespace}:{$key}' has unknown patch type '{$type}'.");
 155          }
 156  
 157          $specs[$full_key] = $patch;
 158        }
 159      }
 160  
 161      foreach ($specs as $key => $patch) {
 162        foreach ($patch['after'] as $after) {
 163          if (empty($specs[$after])) {
 164            throw new Exception(
 165              "Patch '{$key}' references nonexistent dependency, '{$after}'. ".
 166              "Patches may only depend on patches which actually exist.");
 167          }
 168        }
 169      }
 170  
 171      $patches = array();
 172      foreach ($specs as $full_key => $spec) {
 173        $patches[$full_key] = new PhabricatorStoragePatch($spec);
 174      }
 175  
 176      // TODO: Detect cycles?
 177  
 178      return $patches;
 179    }
 180  
 181  }


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