Page MenuHomePhorge

No OneTemporary

diff --git a/src/applications/phid/query/PhabricatorObjectQuery.php b/src/applications/phid/query/PhabricatorObjectQuery.php
index 65e9938311..8364f0210d 100644
--- a/src/applications/phid/query/PhabricatorObjectQuery.php
+++ b/src/applications/phid/query/PhabricatorObjectQuery.php
@@ -1,217 +1,227 @@
<?php
final class PhabricatorObjectQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $phids = array();
private $names = array();
private $types;
private $namedResults;
public function withPHIDs(array $phids) {
$this->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();
}
- $types = PhabricatorPHIDType::getAllTypes();
- if ($this->types) {
- $types = array_select_keys($types, $this->types);
- }
-
$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 (!strncmp($name, 'PHID-', 5)) {
$actually_phids[] = $name;
$phids[] = $name;
unset($names[$key]);
}
}
}
- $phids = array_unique($phids);
-
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.
* @param list<phid> List of ostensibly valid PHIDs.
* @return list<phid> 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;
}
}
diff --git a/src/applications/phid/type/PhabricatorPHIDType.php b/src/applications/phid/type/PhabricatorPHIDType.php
index 99deb84763..2f82dcf169 100644
--- a/src/applications/phid/type/PhabricatorPHIDType.php
+++ b/src/applications/phid/type/PhabricatorPHIDType.php
@@ -1,197 +1,208 @@
<?php
abstract class PhabricatorPHIDType extends Phobject {
final public function getTypeConstant() {
$const = $this->getPhobjectClassConstant('TYPECONST');
if (!is_string($const) || !preg_match('/^[A-Z]{4}$/', $const)) {
throw new Exception(
pht(
'%s class "%s" has an invalid %s property. PHID '.
'constants must be a four character uppercase string.',
__CLASS__,
get_class($this),
'TYPECONST'));
}
return $const;
}
abstract public function getTypeName();
public function getTypeIcon() {
// Default to the application icon if the type doesn't specify one.
$application_class = $this->getPHIDTypeApplicationClass();
if ($application_class) {
$application = newv($application_class, array());
return $application->getIcon();
}
return null;
}
public function newObject() {
return null;
}
/**
* Get the class name for the application this type belongs to.
*
* @return string|null Class name of the corresponding application, or null
* if the type is not bound to an application.
*/
abstract public function getPHIDTypeApplicationClass();
/**
* Build a @{class:PhabricatorPolicyAwareQuery} to load objects of this type
* by PHID.
*
* If you can not build a single query which satisfies this requirement, you
* can provide a dummy implementation for this method and overload
* @{method:loadObjects} instead.
*
* @param PhabricatorObjectQuery Query being executed.
* @param list<phid> PHIDs to load.
* @return PhabricatorPolicyAwareQuery Query object which loads the
* specified PHIDs when executed.
*/
abstract protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids);
/**
* Load objects of this type, by PHID. For most PHID types, it is only
* necessary to implement @{method:buildQueryForObjects} to get object
* loading to work.
*
* @param PhabricatorObjectQuery Query being executed.
* @param list<phid> PHIDs to load.
* @return list<wild> Corresponding objects.
*/
public function loadObjects(
PhabricatorObjectQuery $query,
array $phids) {
$object_query = $this->buildQueryForObjects($query, $phids)
->setViewer($query->getViewer())
->setParentQuery($query);
// If the user doesn't have permission to use the application at all,
// just mark all the PHIDs as filtered. This primarily makes these
// objects show up as "Restricted" instead of "Unknown" when loaded as
// handles, which is technically true.
if (!$object_query->canViewerUseQueryApplication()) {
$object_query->addPolicyFilteredPHIDs(array_fuse($phids));
return array();
}
return $object_query->execute();
}
/**
* Populate provided handles with application-specific data, like titles and
* URIs.
*
* NOTE: The `$handles` and `$objects` lists are guaranteed to be nonempty
* and have the same keys: subclasses are expected to load information only
* for handles with visible objects.
*
* Because of this guarantee, a safe implementation will typically look like*
*
* foreach ($handles as $phid => $handle) {
* $object = $objects[$phid];
*
* $handle->setStuff($object->getStuff());
* // ...
* }
*
* In general, an implementation should call `setName()` and `setURI()` on
* each handle at a minimum. See @{class:PhabricatorObjectHandle} for other
* handle properties.
*
* @param PhabricatorHandleQuery Issuing query object.
* @param list<PhabricatorObjectHandle> Handles to populate with data.
* @param list<Object> Objects for these PHIDs loaded by
* @{method:buildQueryForObjects()}.
* @return void
*/
abstract public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects);
public function canLoadNamedObject($name) {
return false;
}
public function loadNamedObjects(
PhabricatorObjectQuery $query,
array $names) {
throw new PhutilMethodNotImplementedException();
}
/**
* Get all known PHID types.
*
* To get PHID types a given user has access to, see
* @{method:getAllInstalledTypes}.
*
* @return dict<string, PhabricatorPHIDType> Map of type constants to types.
*/
final public static function getAllTypes() {
+ return self::newClassMapQuery()
+ ->execute();
+ }
+
+ final public static function getTypes(array $types) {
+ return id(new PhabricatorCachedClassMapQuery())
+ ->setClassMapQuery(self::newClassMapQuery())
+ ->setMapKeyMethod('getTypeConstant')
+ ->loadClasses($types);
+ }
+
+ private static function newClassMapQuery() {
return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
- ->setUniqueMethod('getTypeConstant')
- ->execute();
+ ->setUniqueMethod('getTypeConstant');
}
/**
* Get all PHID types of applications installed for a given viewer.
*
* @param PhabricatorUser Viewing user.
* @return dict<string, PhabricatorPHIDType> Map of constants to installed
* types.
*/
public static function getAllInstalledTypes(PhabricatorUser $viewer) {
$all_types = self::getAllTypes();
$installed_types = array();
$app_classes = array();
foreach ($all_types as $key => $type) {
$app_class = $type->getPHIDTypeApplicationClass();
if ($app_class === null) {
// If the PHID type isn't bound to an application, include it as
// installed.
$installed_types[$key] = $type;
continue;
}
// Otherwise, we need to check if this application is installed before
// including the PHID type.
$app_classes[$app_class][$key] = $type;
}
if ($app_classes) {
$apps = id(new PhabricatorApplicationQuery())
->setViewer($viewer)
->withInstalled(true)
->withClasses(array_keys($app_classes))
->execute();
foreach ($apps as $app_class => $app) {
$installed_types += $app_classes[$app_class];
}
}
return $installed_types;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Jan 19, 15:46 (2 w, 6 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1126151
Default Alt Text
(13 KB)

Event Timeline