Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2891811
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
13 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
Mode
rP Phorge
Attached
Detach File
Event Timeline
Log In to Comment