phids = $phids; return $this; } public function withNames(array $names) { $this->names = $names; return $this; } public function withTypes(array $types) { $this->types = $types; return $this; } protected function loadPage() { if ($this->namedResults === null) { $this->namedResults = array(); } $names = array_unique($this->names); $phids = $this->phids; // We allow objects to be named by their PHID in addition to their normal // name so that, e.g., CLI tools which accept object names can also accept // PHIDs and work as users expect. $actually_phids = array(); if ($names) { foreach ($names as $key => $name) { if (!phutil_nonempty_string($name) || !strncmp($name, 'PHID-', 5)) { $actually_phids[] = $name; $phids[] = $name; unset($names[$key]); } } } if ($names) { $types = PhabricatorPHIDType::getAllTypes(); if ($this->types) { $types = array_select_keys($types, $this->types); } $name_results = $this->loadObjectsByName($types, $names); } else { $name_results = array(); } if ($phids) { $phids = array_unique($phids); $phid_types = array(); foreach ($phids as $phid) { $phid_type = phid_get_type($phid); $phid_types[$phid_type] = $phid_type; } $types = PhabricatorPHIDType::getTypes($phid_types); if ($this->types) { $types = array_select_keys($types, $this->types); } $phid_results = $this->loadObjectsByPHID($types, $phids); } else { $phid_results = array(); } foreach ($actually_phids as $phid) { if (isset($phid_results[$phid])) { $name_results[$phid] = $phid_results[$phid]; } } $this->namedResults += $name_results; return $phid_results + mpull($name_results, null, 'getPHID'); } public function getNamedResults() { if ($this->namedResults === null) { throw new PhutilInvalidStateException('execute'); } return $this->namedResults; } private function loadObjectsByName(array $types, array $names) { $groups = array(); foreach ($names as $name) { foreach ($types as $type => $type_impl) { if (!$type_impl->canLoadNamedObject($name)) { continue; } $groups[$type][] = $name; break; } } $results = array(); foreach ($groups as $type => $group) { $results += $types[$type]->loadNamedObjects($this, $group); } return $results; } private function loadObjectsByPHID(array $types, array $phids) { $results = array(); $groups = array(); foreach ($phids as $phid) { $type = phid_get_type($phid); $groups[$type][] = $phid; } $in_flight = $this->getPHIDsInFlight(); foreach ($groups as $type => $group) { // We check the workspace for each group, because some groups may trigger // other groups to load (for example, transactions load their objects). $workspace = $this->getObjectsFromWorkspace($group); foreach ($group as $key => $phid) { if (isset($workspace[$phid])) { $results[$phid] = $workspace[$phid]; unset($group[$key]); } } if (!$group) { continue; } // Don't try to load PHIDs which are already "in flight"; this prevents // us from recursing indefinitely if policy checks or edges form a loop. // We will decline to load the corresponding objects. foreach ($group as $key => $phid) { if (isset($in_flight[$phid])) { unset($group[$key]); } } if ($group && isset($types[$type])) { $this->putPHIDsInFlight($group); $objects = $types[$type]->loadObjects($this, $group); $map = mpull($objects, null, 'getPHID'); $this->putObjectsInWorkspace($map); $results += $map; } } return $results; } protected function didFilterResults(array $filtered) { foreach ($this->namedResults as $name => $result) { if (isset($filtered[$result->getPHID()])) { unset($this->namedResults[$name]); } } } /** * This query disables policy filtering if the only required capability is * the view capability. * * The view capability is always checked in the subqueries, so we do not need * to re-filter results. For any other set of required capabilities, we do. */ protected function shouldDisablePolicyFiltering() { $view_capability = PhabricatorPolicyCapability::CAN_VIEW; if ($this->getRequiredCapabilities() === array($view_capability)) { return true; } return false; } public function getQueryApplicationClass() { return null; } /** * Select invalid or restricted PHIDs from a list. * * PHIDs are invalid if their objects do not exist or can not be seen by the * viewer. This method is generally used to validate that PHIDs affected by * a transaction are valid. * * @param PhabricatorUser $viewer Viewer. * @param list $phids List of ostensibly valid PHIDs. * @return list List of invalid or restricted PHIDs. */ public static function loadInvalidPHIDsForViewer( PhabricatorUser $viewer, array $phids) { if (!$phids) { return array(); } $objects = id(new PhabricatorObjectQuery()) ->setViewer($viewer) ->withPHIDs($phids) ->execute(); $objects = mpull($objects, null, 'getPHID'); $invalid = array(); foreach ($phids as $phid) { if (empty($objects[$phid])) { $invalid[] = $phid; } } return $invalid; } }