Path: blob/master/src/infrastructure/graph/PhabricatorObjectGraph.php
12241 views
<?php12abstract class PhabricatorObjectGraph3extends AbstractDirectedGraph {45private $viewer;6private $edges = array();7private $edgeReach = array();8private $seedPHID;9private $objects;10private $loadEntireGraph = false;11private $limit;12private $adjacent;13private $height;1415public function setViewer(PhabricatorUser $viewer) {16$this->viewer = $viewer;17return $this;18}1920public function getViewer() {21if (!$this->viewer) {22throw new PhutilInvalidStateException('setViewer');23}2425return $this->viewer;26}2728public function setLimit($limit) {29$this->limit = $limit;30return $this;31}3233public function getLimit() {34return $this->limit;35}3637public function setHeight($height) {38$this->height = $height;39return $this;40}4142public function getHeight() {43return $this->height;44}4546final public function setRenderOnlyAdjacentNodes($adjacent) {47$this->adjacent = $adjacent;48return $this;49}5051final public function getRenderOnlyAdjacentNodes() {52return $this->adjacent;53}5455abstract protected function getEdgeTypes();56abstract protected function getParentEdgeType();57abstract protected function newQuery();58abstract protected function newTableRow($phid, $object, $trace);59abstract protected function newTable(AphrontTableView $table);60abstract protected function isClosed($object);6162protected function newEllipsisRow() {63return array(64'...',65);66}6768final public function setSeedPHID($phid) {69$this->seedPHID = $phid;70$this->edgeReach[$phid] = array_fill_keys($this->getEdgeTypes(), true);7172return $this->addNodes(73array(74'<seed>' => array($phid),75));76}7778final public function getSeedPHID() {79return $this->seedPHID;80}8182final public function isEmpty() {83return (count($this->getNodes()) <= 2);84}8586final public function isOverLimit() {87$limit = $this->getLimit();8889if (!$limit) {90return false;91}9293return (count($this->edgeReach) > $limit);94}9596final public function getEdges($type) {97$edges = idx($this->edges, $type, array());9899// Remove any nodes which we never reached. We can get these when loading100// only part of the graph: for example, they point at other subtasks of101// parents or other parents of subtasks.102$nodes = $this->getNodes();103foreach ($edges as $src => $dsts) {104foreach ($dsts as $key => $dst) {105if (!isset($nodes[$dst])) {106unset($edges[$src][$key]);107}108}109}110111return $edges;112}113114final public function setLoadEntireGraph($load_entire_graph) {115$this->loadEntireGraph = $load_entire_graph;116return $this;117}118119final public function getLoadEntireGraph() {120return $this->loadEntireGraph;121}122123final protected function loadEdges(array $nodes) {124if ($this->isOverLimit()) {125return array_fill_keys($nodes, array());126}127128$edge_types = $this->getEdgeTypes();129130$query = id(new PhabricatorEdgeQuery())131->withSourcePHIDs($nodes)132->withEdgeTypes($edge_types);133134$query->execute();135136$whole_graph = $this->getLoadEntireGraph();137138$map = array();139foreach ($nodes as $node) {140$map[$node] = array();141142foreach ($edge_types as $edge_type) {143$dst_phids = $query->getDestinationPHIDs(144array($node),145array($edge_type));146147$this->edges[$edge_type][$node] = $dst_phids;148foreach ($dst_phids as $dst_phid) {149if ($whole_graph || isset($this->edgeReach[$node][$edge_type])) {150$map[$node][] = $dst_phid;151}152$this->edgeReach[$dst_phid][$edge_type] = true;153}154}155156$map[$node] = array_values(array_fuse($map[$node]));157}158159return $map;160}161162final public function newGraphTable() {163$viewer = $this->getViewer();164165$ancestry = $this->getEdges($this->getParentEdgeType());166167$only_adjacent = $this->getRenderOnlyAdjacentNodes();168if ($only_adjacent) {169$adjacent = array(170$this->getSeedPHID() => $this->getSeedPHID(),171);172173foreach ($this->getEdgeTypes() as $edge_type) {174$map = $this->getEdges($edge_type);175$direct = idx($map, $this->getSeedPHID(), array());176$adjacent += array_fuse($direct);177}178179foreach ($ancestry as $key => $list) {180if (!isset($adjacent[$key])) {181unset($ancestry[$key]);182continue;183}184185foreach ($list as $list_key => $item) {186if (!isset($adjacent[$item])) {187unset($ancestry[$key][$list_key]);188}189}190}191}192193$objects = $this->newQuery()194->setViewer($viewer)195->withPHIDs(array_keys($ancestry))196->execute();197$objects = mpull($objects, null, 'getPHID');198199$order = id(new PhutilDirectedScalarGraph())200->addNodes($ancestry)201->getNodesInTopologicalOrder();202203$ancestry = array_select_keys($ancestry, $order);204205$graph_view = id(new PHUIDiffGraphView());206207$height = $this->getHeight();208if ($height !== null) {209$graph_view->setHeight($height);210}211212$traces = $graph_view->renderGraph($ancestry);213214$ii = 0;215$rows = array();216$rowc = array();217218if ($only_adjacent) {219$rows[] = $this->newEllipsisRow();220$rowc[] = 'more';221}222223foreach ($ancestry as $phid => $ignored) {224$object = idx($objects, $phid);225$rows[] = $this->newTableRow($phid, $object, $traces[$ii++]);226227$classes = array();228if ($phid == $this->seedPHID) {229$classes[] = 'highlighted';230}231232if ($object) {233if ($this->isClosed($object)) {234$classes[] = 'closed';235}236}237238if ($classes) {239$classes = implode(' ', $classes);240} else {241$classes = null;242}243244$rowc[] = $classes;245}246247if ($only_adjacent) {248$rows[] = $this->newEllipsisRow();249$rowc[] = 'more';250}251252$table = id(new AphrontTableView($rows))253->setClassName('object-graph-table')254->setRowClasses($rowc);255256$this->objects = $objects;257258return $this->newTable($table);259}260261final public function getReachableObjects($edge_type) {262if ($this->objects === null) {263throw new PhutilInvalidStateException('newGraphTable');264}265266$graph = $this->getEdges($edge_type);267268$seen = array();269$look = array($this->seedPHID);270while ($look) {271$phid = array_pop($look);272273$parents = idx($graph, $phid, array());274foreach ($parents as $parent) {275if (isset($seen[$parent])) {276continue;277}278279$seen[$parent] = $parent;280$look[] = $parent;281}282}283284$reachable = array();285foreach ($seen as $phid) {286if ($phid == $this->seedPHID) {287continue;288}289290$object = idx($this->objects, $phid);291if (!$object) {292continue;293}294295$reachable[] = $object;296}297298return $reachable;299}300301}302303304