mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-30 01:10:58 +01:00
Make edge types modular
Summary: Ref T5245. I want to add a new capability to edge types, which is a good opportunity to move away from `PhabricatorEdgeConfig`, which isn't modular. This is basically the same as the modularization of PHID types, which has worked well. Add `PhabricatorEdgeType` and provide an adaption layer for the existing code. This has no runtime changes, except the fixed edge constant. Test Plan: Ran `var_dump(PhabricatorEdgeType::getAllTypes())` and got reasonable looking output. Reviewers: chad, btrahan, joshuaspence Reviewed By: joshuaspence Subscribers: epriestley Maniphest Tasks: T5245 Differential Revision: https://secure.phabricator.com/D9837
This commit is contained in:
parent
7deec8208f
commit
7afb770cbe
4 changed files with 402 additions and 1 deletions
|
@ -1562,6 +1562,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorEdgeGraph' => 'infrastructure/edges/util/PhabricatorEdgeGraph.php',
|
||||
'PhabricatorEdgeQuery' => 'infrastructure/edges/query/PhabricatorEdgeQuery.php',
|
||||
'PhabricatorEdgeTestCase' => 'infrastructure/edges/__tests__/PhabricatorEdgeTestCase.php',
|
||||
'PhabricatorEdgeType' => 'infrastructure/edges/type/PhabricatorEdgeType.php',
|
||||
'PhabricatorEditor' => 'infrastructure/PhabricatorEditor.php',
|
||||
'PhabricatorEmailLoginController' => 'applications/auth/controller/PhabricatorEmailLoginController.php',
|
||||
'PhabricatorEmailVerificationController' => 'applications/auth/controller/PhabricatorEmailVerificationController.php',
|
||||
|
@ -1712,6 +1713,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorJavelinLinter' => 'infrastructure/lint/linter/PhabricatorJavelinLinter.php',
|
||||
'PhabricatorJumpNavHandler' => 'applications/search/engine/PhabricatorJumpNavHandler.php',
|
||||
'PhabricatorKeyValueDatabaseCache' => 'applications/cache/PhabricatorKeyValueDatabaseCache.php',
|
||||
'PhabricatorLegacyEdgeType' => 'infrastructure/edges/type/PhabricatorLegacyEdgeType.php',
|
||||
'PhabricatorLegalpadConfigOptions' => 'applications/legalpad/config/PhabricatorLegalpadConfigOptions.php',
|
||||
'PhabricatorLegalpadPHIDTypeDocument' => 'applications/legalpad/phid/PhabricatorLegalpadPHIDTypeDocument.php',
|
||||
'PhabricatorLipsumArtist' => 'applications/lipsum/image/PhabricatorLipsumArtist.php',
|
||||
|
@ -4356,6 +4358,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorEdgeGraph' => 'AbstractDirectedGraph',
|
||||
'PhabricatorEdgeQuery' => 'PhabricatorQuery',
|
||||
'PhabricatorEdgeTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorEdgeType' => 'Phobject',
|
||||
'PhabricatorEditor' => 'Phobject',
|
||||
'PhabricatorEmailLoginController' => 'PhabricatorAuthController',
|
||||
'PhabricatorEmailVerificationController' => 'PhabricatorAuthController',
|
||||
|
@ -4509,6 +4512,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorIteratedMD5PasswordHasher' => 'PhabricatorPasswordHasher',
|
||||
'PhabricatorJavelinLinter' => 'ArcanistLinter',
|
||||
'PhabricatorKeyValueDatabaseCache' => 'PhutilKeyValueCache',
|
||||
'PhabricatorLegacyEdgeType' => 'PhabricatorEdgeType',
|
||||
'PhabricatorLegalpadConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorLegalpadPHIDTypeDocument' => 'PhabricatorPHIDType',
|
||||
'PhabricatorLipsumGenerateWorkflow' => 'PhabricatorLipsumManagementWorkflow',
|
||||
|
|
|
@ -78,6 +78,13 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants {
|
|||
const TYPE_OBJECT_NEEDS_SIGNATURE = 49;
|
||||
const TYPE_SIGNATURE_NEEDED_BY_OBJECT = 50;
|
||||
|
||||
/* !!!! STOP !!!! STOP !!!! STOP !!!! STOP !!!! STOP !!!! STOP !!!! STOP !!!! */
|
||||
|
||||
// HEY! DO NOT ADD NEW CONSTANTS HERE!
|
||||
// Instead, subclass PhabricatorEdgeType.
|
||||
|
||||
/* !!!! STOP !!!! STOP !!!! STOP !!!! STOP !!!! STOP !!!! STOP !!!! STOP !!!! */
|
||||
|
||||
const TYPE_TEST_NO_CYCLE = 9000;
|
||||
|
||||
const TYPE_PHOB_HAS_ASANATASK = 80001;
|
||||
|
@ -89,6 +96,47 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants {
|
|||
const TYPE_PHOB_HAS_JIRAISSUE = 80004;
|
||||
const TYPE_JIRAISSUE_HAS_PHOB = 80005;
|
||||
|
||||
|
||||
/**
|
||||
* Build @{class:PhabricatorLegacyEdgeType} objects for edges which have not
|
||||
* yet been modernized. This allows code to act as though we've completed
|
||||
* the edge type migration before we actually do all the work, by building
|
||||
* these fake type objects.
|
||||
*
|
||||
* @param list<const> List of edge types that objects should not be built for.
|
||||
* This is used to avoid constructing duplicate objects for edge constants
|
||||
* which have migrated and already have a real object.
|
||||
* @return list<PhabricatorLegacyEdgeType> Real-looking edge type objects for
|
||||
* unmigrated edge types.
|
||||
*/
|
||||
public static function getLegacyTypes(array $exclude) {
|
||||
$consts = array_merge(
|
||||
range(1, 50),
|
||||
array(9000),
|
||||
range(80000, 80005));
|
||||
$consts = array_diff($consts, $exclude);
|
||||
|
||||
$map = array();
|
||||
foreach ($consts as $const) {
|
||||
$prevent_cycles = self::shouldPreventCycles($const);
|
||||
$inverse_constant = self::getInverse($const);
|
||||
|
||||
$map[$const] = id(new PhabricatorLegacyEdgeType())
|
||||
->setEdgeConstant($const)
|
||||
->setShouldPreventCycles($prevent_cycles)
|
||||
->setInverseEdgeConstant($inverse_constant)
|
||||
->setStrings(
|
||||
array(
|
||||
self::getAddStringForEdgeType($const),
|
||||
self::getRemoveStringForEdgeType($const),
|
||||
self::getEditStringForEdgeType($const),
|
||||
self::getFeedStringForEdgeType($const),
|
||||
));
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
public static function getInverse($edge_type) {
|
||||
static $map = array(
|
||||
self::TYPE_TASK_HAS_COMMIT => self::TYPE_COMMIT_HAS_TASK,
|
||||
|
@ -136,7 +184,7 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants {
|
|||
self::TYPE_DREV_HAS_COMMIT => self::TYPE_COMMIT_HAS_DREV,
|
||||
self::TYPE_COMMIT_HAS_DREV => self::TYPE_DREV_HAS_COMMIT,
|
||||
|
||||
self::TYPE_OBJECT_HAS_CONTRIBUTOR => self::TYPE_SUBSCRIBED_TO_OBJECT,
|
||||
self::TYPE_OBJECT_HAS_CONTRIBUTOR => self::TYPE_CONTRIBUTED_TO_OBJECT,
|
||||
self::TYPE_CONTRIBUTED_TO_OBJECT => self::TYPE_OBJECT_HAS_CONTRIBUTOR,
|
||||
|
||||
self::TYPE_TASK_HAS_MOCK => self::TYPE_MOCK_HAS_TASK,
|
||||
|
|
230
src/infrastructure/edges/type/PhabricatorEdgeType.php
Normal file
230
src/infrastructure/edges/type/PhabricatorEdgeType.php
Normal file
|
@ -0,0 +1,230 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Defines an edge type.
|
||||
*
|
||||
* Edges are typed, directed connections between two objects. They are used to
|
||||
* represent most simple relationships, like when a user is subscribed to an
|
||||
* object or an object is a member of a project.
|
||||
*
|
||||
* @task load Loading Types
|
||||
*/
|
||||
abstract class PhabricatorEdgeType extends Phobject {
|
||||
|
||||
// TODO: Make this final after we remove PhabricatorLegacyEdgeType.
|
||||
/* final */ public function getEdgeConstant() {
|
||||
$class = new ReflectionClass($this);
|
||||
|
||||
$const = $class->getConstant('EDGECONST');
|
||||
if ($const === false) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'EdgeType class "%s" must define an EDGECONST property.',
|
||||
get_class($this)));
|
||||
}
|
||||
|
||||
if (!is_int($const) || ($const <= 0)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'EdgeType class "%s" has an invalid EDGECONST property. Edge '.
|
||||
'constants must be positive integers.',
|
||||
get_class($this)));
|
||||
}
|
||||
|
||||
return $const;
|
||||
}
|
||||
|
||||
public function getInverseEdgeConstant() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function shouldPreventCycles() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getTransactionAddString(
|
||||
$actor,
|
||||
$add_count,
|
||||
$add_edges) {
|
||||
|
||||
return pht(
|
||||
'%s added %s edge(s): %s.',
|
||||
$actor,
|
||||
$add_count,
|
||||
$add_edges);
|
||||
}
|
||||
|
||||
public function getTransactionRemoveString(
|
||||
$actor,
|
||||
$rem_count,
|
||||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s removed %s edge(s): %s.',
|
||||
$actor,
|
||||
$rem_count,
|
||||
$rem_edges);
|
||||
}
|
||||
|
||||
public function getTransactionEditString(
|
||||
$actor,
|
||||
$total_count,
|
||||
$add_count,
|
||||
$add_edges,
|
||||
$rem_count,
|
||||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s edited %s edge(s), added %s: %s; removed %s: %s.',
|
||||
$actor,
|
||||
$total_count,
|
||||
$add_count,
|
||||
$add_edges,
|
||||
$rem_count,
|
||||
$rem_edges);
|
||||
}
|
||||
|
||||
public function getFeedAddString(
|
||||
$actor,
|
||||
$object,
|
||||
$add_count,
|
||||
$add_edges) {
|
||||
|
||||
return pht(
|
||||
'%s added %s edge(s) to %s: %s.',
|
||||
$actor,
|
||||
$add_count,
|
||||
$object,
|
||||
$add_edges);
|
||||
}
|
||||
|
||||
public function getFeedRemoveString(
|
||||
$actor,
|
||||
$object,
|
||||
$rem_count,
|
||||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s removed %s edge(s) from %s: %s.',
|
||||
$actor,
|
||||
$rem_count,
|
||||
$object,
|
||||
$rem_edges);
|
||||
}
|
||||
|
||||
public function getFeedEditString(
|
||||
$actor,
|
||||
$object,
|
||||
$total_count,
|
||||
$add_count,
|
||||
$add_edges,
|
||||
$rem_count,
|
||||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s edited %s edge(s) for %s, added %s: %s; removed %s: %s.',
|
||||
$actor,
|
||||
$total_count,
|
||||
$object,
|
||||
$add_count,
|
||||
$add_edges,
|
||||
$rem_count,
|
||||
$rem_edges);
|
||||
}
|
||||
|
||||
|
||||
/* -( Loading Types )------------------------------------------------------ */
|
||||
|
||||
|
||||
/**
|
||||
* @task load
|
||||
*/
|
||||
public static function getAllTypes() {
|
||||
static $type_map;
|
||||
|
||||
if ($type_map === null) {
|
||||
$types = id(new PhutilSymbolLoader())
|
||||
->setAncestorClass(__CLASS__)
|
||||
->loadObjects();
|
||||
|
||||
$map = array();
|
||||
|
||||
|
||||
// TODO: Remove this once everything is migrated.
|
||||
$exclude = mpull($types, 'getEdgeConstant');
|
||||
$map = PhabricatorEdgeConfig::getLegacyTypes($exclude);
|
||||
unset($types['PhabricatorLegacyEdgeType']);
|
||||
|
||||
|
||||
foreach ($types as $class => $type) {
|
||||
$const = $type->getEdgeConstant();
|
||||
|
||||
if (isset($map[$const])) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Two edge types ("%s", "%s") share the same edge constant '.
|
||||
'(%d). Each edge type must have a unique constant.',
|
||||
$class,
|
||||
get_class($map[$const]),
|
||||
$const));
|
||||
}
|
||||
|
||||
$map[$const] = $type;
|
||||
}
|
||||
|
||||
// Check that all the inverse edge definitions actually make sense. If
|
||||
// edge type A says B is its inverse, B must exist and say that A is its
|
||||
// inverse.
|
||||
|
||||
foreach ($map as $const => $type) {
|
||||
$inverse = $type->getInverseEdgeConstant();
|
||||
if ($inverse === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (empty($map[$inverse])) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Edge type "%s" ("%d") defines an inverse type ("%d") which '.
|
||||
'does not exist.',
|
||||
get_class($type),
|
||||
$const,
|
||||
$inverse));
|
||||
}
|
||||
|
||||
$inverse_inverse = $map[$inverse]->getInverseEdgeConstant();
|
||||
if ($inverse_inverse !== $const) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Edge type "%s" ("%d") defines an inverse type ("%d"), but that '.
|
||||
'inverse type defines a different type ("%d") as its '.
|
||||
'inverse.',
|
||||
get_class($type),
|
||||
$const,
|
||||
$inverse,
|
||||
$inverse_inverse));
|
||||
}
|
||||
}
|
||||
|
||||
$type_map = $map;
|
||||
}
|
||||
|
||||
return $type_map;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @task load
|
||||
*/
|
||||
public static function getByConstant($const) {
|
||||
$type = idx(self::getAllTypes(), $const);
|
||||
|
||||
if (!$type) {
|
||||
throw new Exception(
|
||||
pht('Unknown edge constant "%s"!', $const));
|
||||
}
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
}
|
119
src/infrastructure/edges/type/PhabricatorLegacyEdgeType.php
Normal file
119
src/infrastructure/edges/type/PhabricatorLegacyEdgeType.php
Normal file
|
@ -0,0 +1,119 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Supports legacy edges. Do not use or extend this class!
|
||||
*
|
||||
* TODO: Move all edge constants out of @{class:PhabricatorEdgeConfig}, then
|
||||
* throw this away.
|
||||
*/
|
||||
final class PhabricatorLegacyEdgeType extends PhabricatorEdgeType {
|
||||
|
||||
private $edgeConstant;
|
||||
private $inverseEdgeConstant;
|
||||
private $shouldPreventCycles;
|
||||
private $strings;
|
||||
|
||||
public function getEdgeConstant() {
|
||||
return $this->edgeConstant;
|
||||
}
|
||||
|
||||
public function getInverseEdgeConstant() {
|
||||
return $this->inverseEdgeConstant;
|
||||
}
|
||||
|
||||
public function shouldPreventCycles() {
|
||||
return $this->shouldPreventCycles;
|
||||
}
|
||||
|
||||
public function setEdgeConstant($edge_constant) {
|
||||
$this->edgeConstant = $edge_constant;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setInverseEdgeConstant($inverse_edge_constant) {
|
||||
$this->inverseEdgeConstant = $inverse_edge_constant;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setShouldPreventCycles($should_prevent_cycles) {
|
||||
$this->shouldPreventCycles = $should_prevent_cycles;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setStrings(array $strings) {
|
||||
$this->strings = $strings;
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function getString($idx, array $argv) {
|
||||
array_unshift($argv, idx($this->strings, $idx, ''));
|
||||
|
||||
// TODO: Burn this class in a fire. Just hiding this from lint for now.
|
||||
$pht_func = 'pht';
|
||||
return call_user_func_array($pht_func, $argv);
|
||||
}
|
||||
|
||||
public function getTransactionAddString(
|
||||
$actor,
|
||||
$add_count,
|
||||
$add_edges) {
|
||||
|
||||
$args = func_get_args();
|
||||
return $this->getString(0, $args);
|
||||
}
|
||||
|
||||
public function getTransactionRemoveString(
|
||||
$actor,
|
||||
$rem_count,
|
||||
$rem_edges) {
|
||||
|
||||
$args = func_get_args();
|
||||
return $this->getString(1, $args);
|
||||
}
|
||||
|
||||
public function getTransactionEditString(
|
||||
$actor,
|
||||
$total_count,
|
||||
$add_count,
|
||||
$add_edges,
|
||||
$rem_count,
|
||||
$rem_edges) {
|
||||
|
||||
$args = func_get_args();
|
||||
return $this->getString(2, $args);
|
||||
}
|
||||
|
||||
public function getFeedAddString(
|
||||
$actor,
|
||||
$object,
|
||||
$add_count,
|
||||
$add_edges) {
|
||||
|
||||
$args = func_get_args();
|
||||
return $this->getString(3, $args);
|
||||
}
|
||||
|
||||
public function getFeedRemoveString(
|
||||
$actor,
|
||||
$object,
|
||||
$rem_count,
|
||||
$rem_edges) {
|
||||
|
||||
$args = func_get_args();
|
||||
return $this->getString(3, $args);
|
||||
}
|
||||
|
||||
public function getFeedEditString(
|
||||
$actor,
|
||||
$object,
|
||||
$total_count,
|
||||
$add_count,
|
||||
$add_edges,
|
||||
$rem_count,
|
||||
$rem_edges) {
|
||||
|
||||
$args = func_get_args();
|
||||
return $this->getString(3, $args);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue