@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3final class PhabricatorDestructionEngine extends Phobject {
4
5 private $rootLogID;
6 private $collectNotes;
7 private $notes = array();
8 private $depth = 0;
9 private $destroyedObjects = array();
10 private $waitToFinalizeDestruction = false;
11
12 public function setCollectNotes($collect_notes) {
13 $this->collectNotes = $collect_notes;
14 return $this;
15 }
16
17 public function getNotes() {
18 return $this->notes;
19 }
20
21 public function getViewer() {
22 return PhabricatorUser::getOmnipotentUser();
23 }
24
25 public function setWaitToFinalizeDestruction($wait) {
26 $this->waitToFinalizeDestruction = $wait;
27 return $this;
28 }
29
30 public function getWaitToFinalizeDestruction() {
31 return $this->waitToFinalizeDestruction;
32 }
33
34 public function destroyObject(PhabricatorDestructibleInterface $object) {
35 $this->depth++;
36
37 $log = id(new PhabricatorSystemDestructionLog())
38 ->setEpoch(PhabricatorTime::getNow())
39 ->setObjectClass(get_class($object));
40
41 if ($this->rootLogID) {
42 $log->setRootLogID($this->rootLogID);
43 }
44
45 $object_phid = $this->getObjectPHID($object);
46 if ($object_phid) {
47 $log->setObjectPHID($object_phid);
48 }
49
50 if (method_exists($object, 'getMonogram')) {
51 try {
52 $log->setObjectMonogram($object->getMonogram());
53 } catch (Exception $ex) {
54 // Ignore.
55 }
56 }
57
58 $log->save();
59
60 if (!$this->rootLogID) {
61 $this->rootLogID = $log->getID();
62 }
63
64 if ($this->collectNotes) {
65 if ($object instanceof PhabricatorDestructibleCodexInterface) {
66 $codex = PhabricatorDestructibleCodex::newFromObject(
67 $object,
68 $this->getViewer());
69
70 foreach ($codex->getDestructionNotes() as $note) {
71 $this->notes[] = $note;
72 }
73 }
74 }
75
76 if ($object_phid) {
77 // Allow extension to do something before the destruction.
78 $before_extensions =
79 PhabricatorBeforeDestructionEngineExtension::getAllExtensions();
80 foreach ($before_extensions as $key => $before_extension) {
81 if ($before_extension->canBeforeDestroyObject($this, $object)) {
82 $before_extension->beforeDestroyObject($this, $object);
83 }
84 }
85 }
86
87 $object->destroyObjectPermanently($this);
88
89 if ($object_phid) {
90 $extensions = PhabricatorDestructionEngineExtension::getAllExtensions();
91 foreach ($extensions as $key => $extension) {
92 if (!$extension->canDestroyObject($this, $object)) {
93 unset($extensions[$key]);
94 continue;
95 }
96 }
97
98 foreach ($extensions as $key => $extension) {
99 $extension->destroyObject($this, $object);
100 }
101
102 $this->destroyedObjects[] = $object;
103 }
104
105 $this->depth--;
106
107 // If this is a root-level invocation of "destroyObject()", flush the
108 // queue of destroyed objects and fire "didDestroyObject()" hooks. This
109 // hook allows extensions to do things like queue cache updates which
110 // might race if we fire them during object destruction.
111
112 if (!$this->depth) {
113 if (!$this->getWaitToFinalizeDestruction()) {
114 $this->finalizeDestruction();
115 }
116 }
117
118 return $this;
119 }
120
121 public function finalizeDestruction() {
122 $extensions = PhabricatorDestructionEngineExtension::getAllExtensions();
123
124 foreach ($this->destroyedObjects as $object) {
125 foreach ($extensions as $extension) {
126 if (!$extension->canDestroyObject($this, $object)) {
127 continue;
128 }
129
130 $extension->didDestroyObject($this, $object);
131 }
132 }
133
134 $this->destroyedObjects = array();
135
136 return $this;
137 }
138
139 private function getObjectPHID($object) {
140 if (!is_object($object)) {
141 return null;
142 }
143
144 if (!method_exists($object, 'getPHID')) {
145 return null;
146 }
147
148 try {
149 return $object->getPHID();
150 } catch (Exception $ex) {
151 return null;
152 }
153 }
154
155}