Page MenuHomePhabricator
Diviner Phabricator Tech Docs PhabricatorPolicyAwareQuery

abstract class PhabricatorPolicyAwareQuery
Phabricator Technical Documentation ()

A PhabricatorQuery which filters results according to visibility policies for the querying user. Broadly, this class allows you to implement a query that returns only objects the user is allowed to see.

$results = id(new ExampleQuery())
  ->setViewer($user)
  ->withConstraint($example)
  ->execute();

Normally, you should extend PhabricatorCursorPagedPolicyAwareQuery, not this class. PhabricatorCursorPagedPolicyAwareQuery provides a more practical interface for building usable queries against most object types.

NOTE: Although this class extends PhabricatorOffsetPagedQuery, offset paging with policy filtering is not efficient. All results must be loaded into the application and filtered here: skipping N rows via offset is an O(N) operation with a large constant. Prefer cursor-based paging with PhabricatorCursorPagedPolicyAwareQuery, which can filter far more efficiently in MySQL.

Tasks

Formatting Query Clauses

Query Configuration

  • final public function setViewer($viewer) — Set the viewer who is executing the query. Results will be filtered according to the viewer's capabilities. You must set a viewer to execute a policy query.
  • final public function getViewer() — Get the query's viewer.
  • final public function setParentQuery($query) — Set the parent query of this query. This is useful for nested queries so that configuration like whether or not to raise policy exceptions is seamlessly passed along to child queries.
  • final public function getParentQuery() — Get the parent query. See @{method:setParentQuery} for discussion.
  • final public function setRaisePolicyExceptions($bool) — Hook to configure whether this query should raise policy exceptions.
  • final public function shouldRaisePolicyExceptions()
  • final public function requireCapabilities($capabilities)

Executing Queries

  • final public function execute() — Execute the query, loading all visible results.
  • final public function executeOne() — Execute the query, expecting a single result. This method simplifies loading objects for detail pages or edit views.
  • public function getPolicyFilteredPHIDs() — Return a map of all object PHIDs which were loaded in the query but filtered out by policy constraints. This allows a caller to distinguish between objects which do not exist (or, at least, were filtered at the content level) and objects which exist but aren't visible.

Policy Query Implementation

  • final protected function getRawResultLimit() — Get the number of results @{method:loadPage} should load. If the value is 0, @{method:loadPage} should load all available results.
  • protected function willExecute() — Hook invoked before query execution. Generally, implementations should reset any internal cursors.
  • abstract protected function loadPage() — Load a raw page of results. Generally, implementations should load objects from the database. They should attempt to return the number of results hinted by @{method:getRawResultLimit}.
  • abstract protected function nextPage($page) — Update internal state so that the next call to @{method:loadPage} will return new results. Generally, you should adjust a cursor position based on the provided result page.
  • protected function willFilterPage($page) — Hook for applying a page filter prior to the privacy filter. This allows you to drop some items from the result set without creating problems with pagination or cursor updates. You can also load and attach data which is required to perform policy filtering.
  • protected function didFilterResults($results) — Hook for removing filtered results from alternate result sets. This hook will be called with any objects which were returned by the query but filtered for policy reasons. The query should remove them from any cached or partial result sets.
  • protected function didLoadResults($results) — Hook for applying final adjustments before results are returned. This is used by @{class:PhabricatorCursorPagedPolicyAwareQuery} to reverse results that are queried during reverse paging.
  • protected function shouldDisablePolicyFiltering() — Allows a subclass to disable policy filtering. This method is dangerous. It should be used only if the query loads data which has already been filtered (for example, because it wraps some other query which uses normal policy filtering).

Other Methods

workspace

  • public function putObjectsInWorkspace($objects) — Put a map of objects into the query workspace. Many queries perform subqueries, which can eventually end up loading the same objects more than once (often to perform policy checks).
  • public function getObjectsFromWorkspace($phids) — Retrieve objects from the query workspace. For more discussion about the workspace mechanism, see @{method:putObjectsInWorkspace}. This method searches both the current query's workspace and the workspaces of parent queries.

Methods

final public function execute()

Execute the query, loading all visible results.

Return
list<PhabricatorPolicyInterface>Result objects.

protected function formatWhereClause($conn, $parts)
Inherited

This method is not documented.
Parameters
AphrontDatabaseConnection$conn
array$parts
Return
wild

protected function formatSelectClause($conn, $parts)
Inherited

This method is not documented.
Parameters
AphrontDatabaseConnection$conn
array$parts
Return
wild

protected function formatJoinClause($conn, $parts)
Inherited

This method is not documented.
Parameters
AphrontDatabaseConnection$conn
array$parts
Return
wild

protected function formatHavingClause($conn, $parts)
Inherited

This method is not documented.
Parameters
AphrontDatabaseConnection$conn
array$parts
Return
wild

private function flattenSubclause($parts)
Inherited

This method is not documented.
Parameters
array$parts
Return
wild

final public function setOffset($offset)
Inherited

This method is not documented.
Parameters
$offset
Return
wild

final public function setLimit($limit)
Inherited

This method is not documented.
Parameters
$limit
Return
wild

final public function getOffset()
Inherited

This method is not documented.
Return
wild

final public function getLimit()
Inherited

This method is not documented.
Return
wild

protected function buildLimitClause($conn)
Inherited

This method is not documented.
Parameters
AphrontDatabaseConnection$conn
Return
wild

final public function executeWithOffsetPager($pager)
Inherited

This method is not documented.
Parameters
PHUIPagerView$pager
Return
wild

final public function setViewer($viewer)

Set the viewer who is executing the query. Results will be filtered according to the viewer's capabilities. You must set a viewer to execute a policy query.

Parameters
PhabricatorUser$viewerThe viewing user.
Return
this

final public function getViewer()

Get the query's viewer.

Return
PhabricatorUserThe viewing user.

final public function setParentQuery($query)

Set the parent query of this query. This is useful for nested queries so that configuration like whether or not to raise policy exceptions is seamlessly passed along to child queries.

Parameters
PhabricatorPolicyAwareQuery$query
Return
this

final public function getParentQuery()

Get the parent query. See setParentQuery() for discussion.

Return
PhabricatorPolicyAwareQueryThe parent query.

final public function setRaisePolicyExceptions($bool)

Hook to configure whether this query should raise policy exceptions.

Parameters
$bool
Return
this

final public function shouldRaisePolicyExceptions()

This method is not documented.
Return
bool

final public function requireCapabilities($capabilities)

This method is not documented.
Parameters
array$capabilities
Return
wild

final public function setReturnPartialResultsOnOverheat($bool)

This method is not documented.
Parameters
$bool
Return
wild

final public function setDisableOverheating($disable_overheating)

This method is not documented.
Parameters
$disable_overheating
Return
wild

final public function executeOne()

Execute the query, expecting a single result. This method simplifies loading objects for detail pages or edit views.

// Load one result by ID.
$obj = id(new ExampleQuery())
  ->setViewer($user)
  ->withIDs(array($id))
  ->executeOne();
if (!$obj) {
  return new Aphront404Response();
}

If zero results match the query, this method returns null. If one result matches the query, this method returns that result.

If two or more results match the query, this method throws an exception. You should use this method only when the query constraints guarantee at most one match (e.g., selecting a specific ID or PHID).

If one result matches the query but it is caught by the policy filter (for example, the user is trying to view or edit an object which exists but which they do not have permission to see) a policy exception is thrown.

Return
mixedSingle result, or null.

private function getPolicyFilter()

This method is not documented.
Return
wild

protected function getRequiredCapabilities()

This method is not documented.
Return
wild

protected function applyPolicyFilter($objects, $capabilities)

This method is not documented.
Parameters
array$objects
array$capabilities
Return
wild

protected function didRejectResult($object)

This method is not documented.
Parameters
PhabricatorPolicyInterface$object
Return
wild

public function addPolicyFilteredPHIDs($phids)

This method is not documented.
Parameters
array$phids
Return
wild

public function getIsOverheated()

This method is not documented.
Return
wild

public function getPolicyFilteredPHIDs()

Return a map of all object PHIDs which were loaded in the query but filtered out by policy constraints. This allows a caller to distinguish between objects which do not exist (or, at least, were filtered at the content level) and objects which exist but aren't visible.

Return
map<phid, phid>Map of object PHIDs which were filtered by policies.

public function putObjectsInWorkspace($objects)

Put a map of objects into the query workspace. Many queries perform subqueries, which can eventually end up loading the same objects more than once (often to perform policy checks).

For example, loading a user may load the user's profile image, which might load the user object again in order to verify that the viewer has permission to see the file.

The "query workspace" allows queries to load objects from elsewhere in a query block instead of refetching them.

When using the query workspace, it's important to obey two rules:

Never put objects into the workspace which the viewer may not be able to see. You need to apply all policy filtering before putting objects in the workspace. Otherwise, subqueries may read the objects and use them to permit access to content the user shouldn't be able to view.

Fully enrich objects pulled from the workspace. After pulling objects from the workspace, you still need to load and attach any additional content the query requests. Otherwise, a query might return objects without requested content.

Generally, you do not need to update the workspace yourself: it is automatically populated as a side effect of objects surviving policy filtering.

Parameters
map<phid,$objectsPhabricatorPolicyInterface> Objects to add to the query workspace.
Return
this

public function getObjectsFromWorkspace($phids)

Retrieve objects from the query workspace. For more discussion about the workspace mechanism, see putObjectsInWorkspace(). This method searches both the current query's workspace and the workspaces of parent queries.

Parameters
list<phid>$phidsList of PHIDs to retrieve.
Return
this

public function putPHIDsInFlight($phids)

Mark PHIDs as in flight.

PHIDs which are "in flight" are actively being queried for. Using this list can prevent infinite query loops by aborting queries which cycle.

Parameters
list<phid>$phidsList of PHIDs which are now in flight.
Return
this

public function getPHIDsInFlight()

Get PHIDs which are currently in flight.

PHIDs which are "in flight" are actively being queried for.

Return
map<phid, phid>PHIDs currently in flight.

final protected function getRawResultLimit()

Get the number of results loadPage() should load. If the value is 0, loadPage() should load all available results.

Return
intThe number of results to load, or 0 for all results.

protected function willExecute()

Hook invoked before query execution. Generally, implementations should reset any internal cursors.

Return
void

abstract protected function loadPage()

Load a raw page of results. Generally, implementations should load objects from the database. They should attempt to return the number of results hinted by getRawResultLimit().

Return
list<PhabricatorPolicyInterface>List of filterable policy objects.

abstract protected function nextPage($page)

Update internal state so that the next call to loadPage() will return new results. Generally, you should adjust a cursor position based on the provided result page.

Parameters
list<PhabricatorPolicyInterface>$pageThe current page of results.
Return
void

protected function willFilterPage($page)

Hook for applying a page filter prior to the privacy filter. This allows you to drop some items from the result set without creating problems with pagination or cursor updates. You can also load and attach data which is required to perform policy filtering.

Generally, you should load non-policy data and perform non-policy filtering later, in didFilterPage(). Strictly fewer objects will make it that far (so the program will load less data) and subqueries from that context can use the query workspace to further reduce query load.

This method will only be called if data is available. Implementations do not need to handle the case of no results specially.

Parameters
list<wild>$pageResults from `loadPage()`.
Return
list<PhabricatorPolicyInterface>Objects for policy filtering.

protected function didFilterPage($page)

Hook for performing additional non-policy loading or filtering after an object has satisfied all policy checks. Generally, this means loading and attaching related data.

Subqueries executed during this phase can use the query workspace, which may improve performance or make circular policies resolvable. Data which is not necessary for policy filtering should generally be loaded here.

This callback can still filter objects (for example, if attachable data is discovered to not exist), but should not do so for policy reasons.

This method will only be called if data is available. Implementations do not need to handle the case of no results specially.

Parameters
list<wild>$pageResults from @{method:willFilterPage()}.
Return
list<PhabricatorPolicyInterface>Objects after additional non-policy processing.

protected function didFilterResults($results)

Hook for removing filtered results from alternate result sets. This hook will be called with any objects which were returned by the query but filtered for policy reasons. The query should remove them from any cached or partial result sets.

Parameters
list<wild>$resultsList of objects that should not be returned by alternate result mechanisms.
Return
void

protected function didLoadResults($results)

Hook for applying final adjustments before results are returned. This is used by PhabricatorCursorPagedPolicyAwareQuery to reverse results that are queried during reverse paging.

Parameters
list<PhabricatorPolicyInterface>$resultsQuery results.
Return
list<PhabricatorPolicyInterface>Final results.

protected function shouldDisablePolicyFiltering()

Allows a subclass to disable policy filtering. This method is dangerous. It should be used only if the query loads data which has already been filtered (for example, because it wraps some other query which uses normal policy filtering).

Return
boolTrue to disable all policy filtering.

abstract public function getQueryApplicationClass()

If this query belongs to an application, return the application class name here. This will prevent the query from returning results if the viewer can not access the application.

If this query does not belong to an application, return null.

Return
string|nullApplication class name.

Determine if the viewer has permission to use this query's application. For queries which aren't part of an application, this method always returns true.

Return
boolTrue if the viewer has application-level permission to execute the query.

private function applyWillFilterPageExtensions($page)

This method is not documented.
Parameters
array$page
Return
wild