@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 DivinerAtom extends Phobject {
4
5 const TYPE_ARTICLE = 'article';
6 const TYPE_CLASS = 'class';
7 const TYPE_ENUM = 'enum';
8 const TYPE_FILE = 'file';
9 const TYPE_FUNCTION = 'function';
10 const TYPE_INTERFACE = 'interface';
11 const TYPE_METHOD = 'method';
12 const TYPE_TRAIT = 'trait';
13
14 private $type;
15 private $name;
16 private $file;
17 private $line;
18 private $hash;
19 private $contentRaw;
20 private $length;
21 private $language;
22 private $docblockRaw;
23 private $docblockText;
24 private $docblockMeta;
25 private $warnings = array();
26 private $parent;
27 private $parentHash;
28 private $children = array();
29 private $childHashes = array();
30 private $context;
31 private $extends = array();
32 private $links = array();
33 private $book;
34 private $properties = array();
35
36 /**
37 * Returns a sorting key which imposes an unambiguous, stable order on atoms.
38 */
39 public function getSortKey() {
40 return implode(
41 "\0",
42 array(
43 $this->getBook(),
44 $this->getType(),
45 $this->getContext(),
46 $this->getName(),
47 $this->getFile(),
48 sprintf('%08d', $this->getLine()),
49 ));
50 }
51
52 public function setBook($book) {
53 $this->book = $book;
54 return $this;
55 }
56
57 public function getBook() {
58 return $this->book;
59 }
60
61 public function setContext($context) {
62 $this->context = $context;
63 return $this;
64 }
65
66 public function getContext() {
67 return $this->context;
68 }
69
70 public static function getAtomSerializationVersion() {
71 return 2;
72 }
73
74 public function addWarning($warning) {
75 $this->warnings[] = $warning;
76 return $this;
77 }
78
79 public function getWarnings() {
80 return $this->warnings;
81 }
82
83 public function setDocblockRaw($docblock_raw) {
84 $this->docblockRaw = $docblock_raw;
85
86 $parser = new PhutilDocblockParser();
87 list($text, $meta) = $parser->parse($docblock_raw);
88 $this->docblockText = $text;
89 $this->docblockMeta = $meta;
90
91 return $this;
92 }
93
94 public function getDocblockRaw() {
95 return $this->docblockRaw;
96 }
97
98 public function getDocblockText() {
99 if ($this->docblockText === null) {
100 throw new PhutilInvalidStateException('setDocblockRaw');
101 }
102 return $this->docblockText;
103 }
104
105 public function getDocblockMeta() {
106 if ($this->docblockMeta === null) {
107 throw new PhutilInvalidStateException('setDocblockRaw');
108 }
109 return $this->docblockMeta;
110 }
111
112 public function getDocblockMetaValue($key, $default = null) {
113 $meta = $this->getDocblockMeta();
114 return idx($meta, $key, $default);
115 }
116
117 public function setDocblockMetaValue($key, $value) {
118 $meta = $this->getDocblockMeta();
119 $meta[$key] = $value;
120 $this->docblockMeta = $meta;
121 return $this;
122 }
123
124 public function setType($type) {
125 $this->type = $type;
126 return $this;
127 }
128
129 public function getType() {
130 return $this->type;
131 }
132
133 public function setName($name) {
134 $this->name = $name;
135 return $this;
136 }
137
138 public function getName() {
139 return $this->name;
140 }
141
142 public function setFile($file) {
143 $this->file = $file;
144 return $this;
145 }
146
147 public function getFile() {
148 return $this->file;
149 }
150
151 public function setLine($line) {
152 $this->line = $line;
153 return $this;
154 }
155
156 public function getLine() {
157 return $this->line;
158 }
159
160 public function setContentRaw($content_raw) {
161 $this->contentRaw = $content_raw;
162 return $this;
163 }
164
165 public function getContentRaw() {
166 return $this->contentRaw;
167 }
168
169 public function setHash($hash) {
170 $this->hash = $hash;
171 return $this;
172 }
173
174 public function addLink(DivinerAtomRef $ref) {
175 $this->links[] = $ref;
176 return $this;
177 }
178
179 public function addExtends(DivinerAtomRef $ref) {
180 $this->extends[] = $ref;
181 return $this;
182 }
183
184 public function getLinkDictionaries() {
185 return mpull($this->links, 'toDictionary');
186 }
187
188 public function getExtendsDictionaries() {
189 return mpull($this->extends, 'toDictionary');
190 }
191
192 public function getExtends() {
193 return $this->extends;
194 }
195
196 public function getHash() {
197 if ($this->hash) {
198 return $this->hash;
199 }
200
201 $parts = array(
202 $this->getBook(),
203 $this->getType(),
204 $this->getName(),
205 $this->getFile(),
206 $this->getLine(),
207 $this->getLength(),
208 $this->getLanguage(),
209 $this->getContentRaw(),
210 $this->getDocblockRaw(),
211 $this->getProperties(),
212 $this->getChildHashes(),
213 mpull($this->extends, 'toHash'),
214 mpull($this->links, 'toHash'),
215 );
216
217 $this->hash = md5(serialize($parts)).'N';
218 return $this->hash;
219 }
220
221 public function setLength($length) {
222 $this->length = $length;
223 return $this;
224 }
225
226 public function getLength() {
227 return $this->length;
228 }
229
230 public function setLanguage($language) {
231 $this->language = $language;
232 return $this;
233 }
234
235 public function getLanguage() {
236 return $this->language;
237 }
238
239 public function addChildHash($child_hash) {
240 $this->childHashes[] = $child_hash;
241 return $this;
242 }
243
244 public function getChildHashes() {
245 if (!$this->childHashes && $this->children) {
246 $this->childHashes = mpull($this->children, 'getHash');
247 }
248 return $this->childHashes;
249 }
250
251 public function setParentHash($parent_hash) {
252 if ($this->parentHash) {
253 throw new Exception(pht('Atom already has a parent!'));
254 }
255 $this->parentHash = $parent_hash;
256 return $this;
257 }
258
259 public function hasParent() {
260 return $this->parent || $this->parentHash;
261 }
262
263 public function setParent(DivinerAtom $atom) {
264 if ($this->parentHash) {
265 throw new Exception(pht('Parent hash has already been computed!'));
266 }
267 $this->parent = $atom;
268 return $this;
269 }
270
271 public function getParentHash() {
272 if ($this->parent && !$this->parentHash) {
273 $this->parentHash = $this->parent->getHash();
274 }
275 return $this->parentHash;
276 }
277
278 public function addChild(DivinerAtom $atom) {
279 if ($this->childHashes) {
280 throw new Exception(pht('Child hashes have already been computed!'));
281 }
282
283 $atom->setParent($this);
284 $this->children[] = $atom;
285 return $this;
286 }
287
288 public function getURI() {
289 $parts = array();
290 $parts[] = phutil_escape_uri_path_component($this->getType());
291 if ($this->getContext()) {
292 $parts[] = phutil_escape_uri_path_component($this->getContext());
293 }
294 $parts[] = phutil_escape_uri_path_component($this->getName());
295 $parts[] = null;
296 return implode('/', $parts);
297 }
298
299 public function toDictionary() {
300 // NOTE: If you change this format, bump the format version in
301 // @{method:getAtomSerializationVersion}.
302 return array(
303 'book' => $this->getBook(),
304 'type' => $this->getType(),
305 'name' => $this->getName(),
306 'file' => $this->getFile(),
307 'line' => $this->getLine(),
308 'hash' => $this->getHash(),
309 'uri' => $this->getURI(),
310 'length' => $this->getLength(),
311 'context' => $this->getContext(),
312 'language' => $this->getLanguage(),
313 'docblockRaw' => $this->getDocblockRaw(),
314 'warnings' => $this->getWarnings(),
315 'parentHash' => $this->getParentHash(),
316 'childHashes' => $this->getChildHashes(),
317 'extends' => $this->getExtendsDictionaries(),
318 'links' => $this->getLinkDictionaries(),
319 'ref' => $this->getRef()->toDictionary(),
320 'properties' => $this->getProperties(),
321 );
322 }
323
324 public function getRef() {
325 $title = null;
326 if ($this->docblockMeta) {
327 $title = $this->getDocblockMetaValue('title');
328 }
329
330 return id(new DivinerAtomRef())
331 ->setBook($this->getBook())
332 ->setContext($this->getContext())
333 ->setType($this->getType())
334 ->setName($this->getName())
335 ->setTitle($title)
336 ->setGroup($this->getProperty('group'));
337 }
338
339 public static function newFromDictionary(array $dictionary) {
340 $atom = id(new DivinerAtom())
341 ->setBook(idx($dictionary, 'book'))
342 ->setType(idx($dictionary, 'type'))
343 ->setName(idx($dictionary, 'name'))
344 ->setFile(idx($dictionary, 'file'))
345 ->setLine(idx($dictionary, 'line'))
346 ->setHash(idx($dictionary, 'hash'))
347 ->setLength(idx($dictionary, 'length'))
348 ->setContext(idx($dictionary, 'context'))
349 ->setLanguage(idx($dictionary, 'language'))
350 ->setParentHash(idx($dictionary, 'parentHash'))
351 ->setDocblockRaw(coalesce(idx($dictionary, 'docblockRaw'), ''))
352 ->setProperties(idx($dictionary, 'properties'));
353
354 foreach (idx($dictionary, 'warnings', array()) as $warning) {
355 $atom->addWarning($warning);
356 }
357
358 foreach (idx($dictionary, 'childHashes', array()) as $child) {
359 $atom->addChildHash($child);
360 }
361
362 foreach (idx($dictionary, 'extends', array()) as $extends) {
363 $atom->addExtends(DivinerAtomRef::newFromDictionary($extends));
364 }
365
366 return $atom;
367 }
368
369 public function getProperty($key, $default = null) {
370 return idx($this->properties, $key, $default);
371 }
372
373 public function setProperty($key, $value) {
374 $this->properties[$key] = $value;
375 return $this;
376 }
377
378 public function getProperties() {
379 return $this->properties;
380 }
381
382 public function setProperties(array $properties) {
383 $this->properties = $properties;
384 return $this;
385 }
386
387 public static function getThisAtomIsNotDocumentedString($type) {
388 switch ($type) {
389 case self::TYPE_ARTICLE:
390 return pht('This article is not documented.');
391 case self::TYPE_CLASS:
392 return pht('This class is not documented.');
393 case self::TYPE_FILE:
394 return pht('This file is not documented.');
395 case self::TYPE_FUNCTION:
396 return pht('This function is not documented.');
397 case self::TYPE_INTERFACE:
398 return pht('This interface is not documented.');
399 case self::TYPE_METHOD:
400 return pht('This method is not documented.');
401 case self::TYPE_TRAIT:
402 return pht('This trait is not documented.');
403 case self::TYPE_ENUM:
404 return pht('This enum is not documented.');
405 default:
406 phlog(pht("Need translation for '%s'.", $type));
407 return pht('This %s is not documented.', $type);
408 }
409 }
410
411 public static function getAllTypes() {
412 return array(
413 self::TYPE_ARTICLE,
414 self::TYPE_CLASS,
415 self::TYPE_FILE,
416 self::TYPE_FUNCTION,
417 self::TYPE_INTERFACE,
418 self::TYPE_METHOD,
419 self::TYPE_TRAIT,
420 self::TYPE_ENUM,
421 );
422 }
423
424 public static function getAtomTypeNameString($type) {
425 switch ($type) {
426 case self::TYPE_ARTICLE:
427 return pht('Article');
428 case self::TYPE_CLASS:
429 return pht('Class');
430 case self::TYPE_FILE:
431 return pht('File');
432 case self::TYPE_FUNCTION:
433 return pht('Function');
434 case self::TYPE_INTERFACE:
435 return pht('Interface');
436 case self::TYPE_TRAIT:
437 return pht('Trait');
438 case self::TYPE_ENUM:
439 return pht('Enum');
440 case self::TYPE_METHOD:
441 return pht('Method');
442 default:
443 phlog(pht("Need translation for '%s'.", $type));
444 return ucwords($type);
445 }
446 }
447
448}