[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 final class DivinerAtomQuery extends PhabricatorCursorPagedPolicyAwareQuery { 4 5 private $ids; 6 private $phids; 7 private $bookPHIDs; 8 private $names; 9 private $types; 10 private $contexts; 11 private $indexes; 12 private $includeUndocumentable; 13 private $includeGhosts; 14 private $nodeHashes; 15 private $titles; 16 private $nameContains; 17 18 private $needAtoms; 19 private $needExtends; 20 private $needChildren; 21 22 public function withIDs(array $ids) { 23 $this->ids = $ids; 24 return $this; 25 } 26 27 public function withPHIDs(array $phids) { 28 $this->phids = $phids; 29 return $this; 30 } 31 32 public function withBookPHIDs(array $phids) { 33 $this->bookPHIDs = $phids; 34 return $this; 35 } 36 37 public function withTypes(array $types) { 38 $this->types = $types; 39 return $this; 40 } 41 42 public function withNames(array $names) { 43 $this->names = $names; 44 return $this; 45 } 46 47 public function withContexts(array $contexts) { 48 $this->contexts = $contexts; 49 return $this; 50 } 51 52 public function withIndexes(array $indexes) { 53 $this->indexes = $indexes; 54 return $this; 55 } 56 57 public function withNodeHashes(array $hashes) { 58 $this->nodeHashes = $hashes; 59 return $this; 60 } 61 62 public function withTitles($titles) { 63 $this->titles = $titles; 64 return $this; 65 } 66 67 public function withNameContains($text) { 68 $this->nameContains = $text; 69 return $this; 70 } 71 72 public function needAtoms($need) { 73 $this->needAtoms = $need; 74 return $this; 75 } 76 77 public function needChildren($need) { 78 $this->needChildren = $need; 79 return $this; 80 } 81 82 83 /** 84 * Include "ghosts", which are symbols which used to exist but do not exist 85 * currently (for example, a function which existed in an older version of 86 * the codebase but was deleted). 87 * 88 * These symbols had PHIDs assigned to them, and may have other sorts of 89 * metadata that we don't want to lose (like comments or flags), so we don't 90 * delete them outright. They might also come back in the future: the change 91 * which deleted the symbol might be reverted, or the documentation might 92 * have been generated incorrectly by accident. In these cases, we can 93 * restore the original data. 94 * 95 * However, most callers are not interested in these symbols, so they are 96 * excluded by default. You can use this method to include them in results. 97 * 98 * @param bool True to include ghosts. 99 * @return this 100 */ 101 public function withIncludeGhosts($include) { 102 $this->includeGhosts = $include; 103 return $this; 104 } 105 106 public function needExtends($need) { 107 $this->needExtends = $need; 108 return $this; 109 } 110 111 public function withIncludeUndocumentable($include) { 112 $this->includeUndocumentable = $include; 113 return $this; 114 } 115 116 protected function loadPage() { 117 $table = new DivinerLiveSymbol(); 118 $conn_r = $table->establishConnection('r'); 119 120 $data = queryfx_all( 121 $conn_r, 122 'SELECT * FROM %T %Q %Q %Q', 123 $table->getTableName(), 124 $this->buildWhereClause($conn_r), 125 $this->buildOrderClause($conn_r), 126 $this->buildLimitClause($conn_r)); 127 128 return $table->loadAllFromArray($data); 129 } 130 131 protected function willFilterPage(array $atoms) { 132 $books = array_unique(mpull($atoms, 'getBookPHID')); 133 134 $books = id(new DivinerBookQuery()) 135 ->setViewer($this->getViewer()) 136 ->withPHIDs($books) 137 ->execute(); 138 $books = mpull($books, null, 'getPHID'); 139 140 foreach ($atoms as $key => $atom) { 141 $book = idx($books, $atom->getBookPHID()); 142 if (!$book) { 143 unset($atoms[$key]); 144 continue; 145 } 146 $atom->attachBook($book); 147 } 148 149 if ($this->needAtoms) { 150 $atom_data = id(new DivinerLiveAtom())->loadAllWhere( 151 'symbolPHID IN (%Ls)', 152 mpull($atoms, 'getPHID')); 153 $atom_data = mpull($atom_data, null, 'getSymbolPHID'); 154 155 foreach ($atoms as $key => $atom) { 156 $data = idx($atom_data, $atom->getPHID()); 157 if (!$data) { 158 unset($atoms[$key]); 159 continue; 160 } 161 $atom->attachAtom($data); 162 } 163 } 164 165 // Load all of the symbols this symbol extends, recursively. Commonly, 166 // this means all the ancestor classes and interfaces it extends and 167 // implements. 168 169 if ($this->needExtends) { 170 171 // First, load all the matching symbols by name. This does 99% of the 172 // work in most cases, assuming things are named at all reasonably. 173 174 $names = array(); 175 foreach ($atoms as $atom) { 176 foreach ($atom->getAtom()->getExtends() as $xref) { 177 $names[] = $xref->getName(); 178 } 179 } 180 181 if ($names) { 182 $xatoms = id(new DivinerAtomQuery()) 183 ->setViewer($this->getViewer()) 184 ->withNames($names) 185 ->needExtends(true) 186 ->needAtoms(true) 187 ->needChildren($this->needChildren) 188 ->execute(); 189 $xatoms = mgroup($xatoms, 'getName', 'getType', 'getBookPHID'); 190 } else { 191 $xatoms = array(); 192 } 193 194 foreach ($atoms as $atom) { 195 $alang = $atom->getAtom()->getLanguage(); 196 $extends = array(); 197 foreach ($atom->getAtom()->getExtends() as $xref) { 198 199 // If there are no symbols of the matching name and type, we can't 200 // resolve this. 201 if (empty($xatoms[$xref->getName()][$xref->getType()])) { 202 continue; 203 } 204 205 // If we found matches in the same documentation book, prefer them 206 // over other matches. Otherwise, look at all the the matches. 207 $matches = $xatoms[$xref->getName()][$xref->getType()]; 208 if (isset($matches[$atom->getBookPHID()])) { 209 $maybe = $matches[$atom->getBookPHID()]; 210 } else { 211 $maybe = array_mergev($matches); 212 } 213 214 if (!$maybe) { 215 continue; 216 } 217 218 // Filter out matches in a different language, since, e.g., PHP 219 // classes can not implement JS classes. 220 $same_lang = array(); 221 foreach ($maybe as $xatom) { 222 if ($xatom->getAtom()->getLanguage() == $alang) { 223 $same_lang[] = $xatom; 224 } 225 } 226 227 if (!$same_lang) { 228 continue; 229 } 230 231 // If we have duplicates remaining, just pick the first one. There's 232 // nothing more we can do to figure out which is the real one. 233 $extends[] = head($same_lang); 234 } 235 236 $atom->attachExtends($extends); 237 } 238 } 239 240 if ($this->needChildren) { 241 $child_hashes = $this->getAllChildHashes($atoms, $this->needExtends); 242 243 if ($child_hashes) { 244 $children = id(new DivinerAtomQuery()) 245 ->setViewer($this->getViewer()) 246 ->withIncludeUndocumentable(true) 247 ->withNodeHashes($child_hashes) 248 ->needAtoms($this->needAtoms) 249 ->execute(); 250 251 $children = mpull($children, null, 'getNodeHash'); 252 } else { 253 $children = array(); 254 } 255 256 $this->attachAllChildren($atoms, $children, $this->needExtends); 257 } 258 259 return $atoms; 260 } 261 262 private function buildWhereClause(AphrontDatabaseConnection $conn_r) { 263 $where = array(); 264 265 if ($this->ids) { 266 $where[] = qsprintf( 267 $conn_r, 268 'id IN (%Ld)', 269 $this->ids); 270 } 271 272 if ($this->phids) { 273 $where[] = qsprintf( 274 $conn_r, 275 'phid IN (%Ls)', 276 $this->phids); 277 } 278 279 if ($this->bookPHIDs) { 280 $where[] = qsprintf( 281 $conn_r, 282 'bookPHID IN (%Ls)', 283 $this->bookPHIDs); 284 } 285 286 if ($this->types) { 287 $where[] = qsprintf( 288 $conn_r, 289 'type IN (%Ls)', 290 $this->types); 291 } 292 293 if ($this->names) { 294 $where[] = qsprintf( 295 $conn_r, 296 'name IN (%Ls)', 297 $this->names); 298 } 299 300 if ($this->titles) { 301 $hashes = array(); 302 foreach ($this->titles as $title) { 303 $slug = DivinerAtomRef::normalizeTitleString($title); 304 $hash = PhabricatorHash::digestForIndex($slug); 305 $hashes[] = $hash; 306 } 307 308 $where[] = qsprintf( 309 $conn_r, 310 'titleSlugHash in (%Ls)', 311 $hashes); 312 } 313 314 if ($this->contexts) { 315 $with_null = false; 316 $contexts = $this->contexts; 317 foreach ($contexts as $key => $value) { 318 if ($value === null) { 319 unset($contexts[$key]); 320 $with_null = true; 321 continue; 322 } 323 } 324 325 if ($contexts && $with_null) { 326 $where[] = qsprintf( 327 $conn_r, 328 'context IN (%Ls) OR context IS NULL', 329 $contexts); 330 } else if ($contexts) { 331 $where[] = qsprintf( 332 $conn_r, 333 'context IN (%Ls)', 334 $contexts); 335 } else if ($with_null) { 336 $where[] = qsprintf( 337 $conn_r, 338 'context IS NULL'); 339 } 340 } 341 342 if ($this->indexes) { 343 $where[] = qsprintf( 344 $conn_r, 345 'atomIndex IN (%Ld)', 346 $this->indexes); 347 } 348 349 if (!$this->includeUndocumentable) { 350 $where[] = qsprintf( 351 $conn_r, 352 'isDocumentable = 1'); 353 } 354 355 if (!$this->includeGhosts) { 356 $where[] = qsprintf( 357 $conn_r, 358 'graphHash IS NOT NULL'); 359 } 360 361 if ($this->nodeHashes) { 362 $where[] = qsprintf( 363 $conn_r, 364 'nodeHash IN (%Ls)', 365 $this->nodeHashes); 366 } 367 368 if ($this->nameContains) { 369 // NOTE: This CONVERT() call makes queries case-insensitive, since the 370 // column has binary collation. Eventually, this should move into 371 // fulltext. 372 373 $where[] = qsprintf( 374 $conn_r, 375 'CONVERT(name USING utf8) LIKE %~', 376 $this->nameContains); 377 } 378 379 $where[] = $this->buildPagingClause($conn_r); 380 381 return $this->formatWhereClause($where); 382 } 383 384 385 /** 386 * Walk a list of atoms and collect all the node hashes of the atoms' 387 * children. When recursing, also walk up the tree and collect children of 388 * atoms they extend. 389 * 390 * @param list<DivinerLiveSymbol> List of symbols to collect child hashes of. 391 * @param bool True to collect children of extended atoms, 392 * as well. 393 * @return map<string, string> Hashes of atoms' children. 394 */ 395 private function getAllChildHashes(array $symbols, $recurse_up) { 396 assert_instances_of($symbols, 'DivinerLiveSymbol'); 397 398 $hashes = array(); 399 foreach ($symbols as $symbol) { 400 foreach ($symbol->getAtom()->getChildHashes() as $hash) { 401 $hashes[$hash] = $hash; 402 } 403 if ($recurse_up) { 404 $hashes += $this->getAllChildHashes($symbol->getExtends(), true); 405 } 406 } 407 408 return $hashes; 409 } 410 411 412 /** 413 * Attach child atoms to existing atoms. In recursive mode, also attach child 414 * atoms to atoms that these atoms extend. 415 * 416 * @param list<DivinerLiveSymbol> List of symbols to attach children to. 417 * @param map<string, DivinerLiveSymbol> Map of symbols, keyed by node hash. 418 * @param bool True to attach children to extended atoms, as well. 419 * @return void 420 */ 421 private function attachAllChildren( 422 array $symbols, 423 array $children, 424 $recurse_up) { 425 426 assert_instances_of($symbols, 'DivinerLiveSymbol'); 427 assert_instances_of($children, 'DivinerLiveSymbol'); 428 429 foreach ($symbols as $symbol) { 430 $symbol_children = array(); 431 foreach ($symbol->getAtom()->getChildHashes() as $hash) { 432 if (isset($children[$hash])) { 433 $symbol_children[] = $children[$hash]; 434 } 435 } 436 $symbol->attachChildren($symbol_children); 437 if ($recurse_up) { 438 $this->attachAllChildren($symbol->getExtends(), $children, true); 439 } 440 } 441 } 442 443 public function getQueryApplicationClass() { 444 return 'PhabricatorDivinerApplication'; 445 } 446 447 }
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 |