@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
at upstream/main 210 lines 5.1 kB view raw
1<?php 2 3final class DivinerAtomRef extends Phobject { 4 5 private $book; 6 private $context; 7 private $type; 8 private $name; 9 private $group; 10 private $summary; 11 private $index; 12 private $title; 13 14 public function getSortKey() { 15 return implode( 16 "\0", 17 array( 18 $this->getName(), 19 $this->getType(), 20 $this->getContext(), 21 $this->getBook(), 22 $this->getIndex(), 23 )); 24 } 25 26 public function setIndex($index) { 27 $this->index = $index; 28 return $this; 29 } 30 31 public function getIndex() { 32 return $this->index; 33 } 34 35 public function setSummary($summary) { 36 $this->summary = $summary; 37 return $this; 38 } 39 40 public function getSummary() { 41 return $this->summary; 42 } 43 44 public function setName($name) { 45 $normal_name = self::normalizeString($name); 46 if (preg_match('/^@\d+\z/', $normal_name)) { 47 throw new Exception( 48 pht( 49 "Atom names must not be in the form '%s'. This pattern is ". 50 "reserved for disambiguating atoms with similar names.", 51 '/@\d+/')); 52 } 53 $this->name = $normal_name; 54 return $this; 55 } 56 57 public function getName() { 58 return $this->name; 59 } 60 61 public function setType($type) { 62 $this->type = self::normalizeString($type); 63 return $this; 64 } 65 66 public function getType() { 67 return $this->type; 68 } 69 70 public function setContext($context) { 71 if ($context === null) { 72 $this->context = $context; 73 } else { 74 $this->context = self::normalizeString($context); 75 } 76 return $this; 77 } 78 79 public function getContext() { 80 return $this->context; 81 } 82 83 public function setBook($book) { 84 if ($book === null) { 85 $this->book = $book; 86 } else { 87 $this->book = self::normalizeString($book); 88 } 89 return $this; 90 } 91 92 public function getBook() { 93 return $this->book; 94 } 95 96 public function setGroup($group) { 97 $this->group = $group; 98 return $this; 99 } 100 101 public function getGroup() { 102 return $this->group; 103 } 104 105 public function setTitle($title) { 106 $this->title = $title; 107 return $this; 108 } 109 110 public function getTitle() { 111 return $this->title; 112 } 113 114 public function getTitleSlug() { 115 return self::normalizeTitleString($this->getTitle()); 116 } 117 118 public function toDictionary() { 119 return array( 120 'book' => $this->getBook(), 121 'context' => $this->getContext(), 122 'type' => $this->getType(), 123 'name' => $this->getName(), 124 'group' => $this->getGroup(), 125 'summary' => $this->getSummary(), 126 'index' => $this->getIndex(), 127 'title' => $this->getTitle(), 128 ); 129 } 130 131 public function toHash() { 132 $dict = $this->toDictionary(); 133 134 unset($dict['group']); 135 unset($dict['index']); 136 unset($dict['summary']); 137 unset($dict['title']); 138 139 ksort($dict); 140 return md5(serialize($dict)).'S'; 141 } 142 143 public static function newFromDictionary(array $dict) { 144 return id(new DivinerAtomRef()) 145 ->setBook(idx($dict, 'book')) 146 ->setContext(idx($dict, 'context')) 147 ->setType(idx($dict, 'type')) 148 ->setName(idx($dict, 'name')) 149 ->setGroup(idx($dict, 'group')) 150 ->setSummary(idx($dict, 'summary')) 151 ->setIndex(idx($dict, 'index')) 152 ->setTitle(idx($dict, 'title')); 153 } 154 155 public static function normalizeString($str) { 156 // These characters create problems on the filesystem or in URIs. Replace 157 // them with non-problematic approximations (instead of simply removing 158 // them) to keep the URIs fairly useful and avoid unnecessary collisions. 159 // These approximations are selected based on some domain knowledge of 160 // common languages: where a character is used as a delimiter, it is more 161 // helpful to replace it with a "." or a ":" or similar, while it's better 162 // if operator overloads read as, e.g., "operator_div". 163 164 $map = array( 165 // Hopefully not used anywhere by anything. 166 '#' => '.', 167 168 // Used in Ruby methods. 169 '?' => 'Q', 170 171 // Used in PHP namespaces. 172 '\\' => '.', 173 174 // Used in "operator +" in C++. 175 '+' => 'plus', 176 177 // Used in "operator %" in C++. 178 '%' => 'mod', 179 180 // Used in "operator /" in C++. 181 '/' => 'div', 182 ); 183 $str = str_replace(array_keys($map), array_values($map), $str); 184 185 // Replace all spaces with underscores. 186 $str = preg_replace('/ +/', '_', $str); 187 188 // Replace control characters with "X". 189 $str = preg_replace('/[\x00-\x19]/', 'X', $str); 190 191 // Replace specific problematic names with alternative names. 192 $alternates = array( 193 '.' => 'dot', 194 '..' => 'dotdot', 195 '' => 'null', 196 ); 197 198 return idx($alternates, $str, $str); 199 } 200 201 public static function normalizeTitleString($str) { 202 // Remove colons from titles. This is mostly to accommodate legacy rules 203 // from the old Diviner, which generated a significant number of article 204 // URIs without colons present in the titles. 205 $str = str_replace(':', '', $str); 206 $str = self::normalizeString($str); 207 return phutil_utf8_strtolower($str); 208 } 209 210}