1<?php 2 3namespace dokuwiki\plugin\xref; 4 5/** 6 * Figure out what to send to Grok to hopefully show the right, single hit 7 */ 8class Heuristics 9{ 10 /** @var string the definition to search */ 11 protected $def = ''; 12 /** @var string the path to use */ 13 protected $path = ''; 14 /** @var array deprecated classes and their replacements */ 15 protected $deprecations; 16 17 /** 18 * Try to gues what the given reference means and how to best search for it 19 * 20 * @param string $reference 21 */ 22 public function __construct($reference) 23 { 24 $this->loadDeprecations(); 25 26 if ($reference !== '') $reference = $this->checkDeprecation($reference); 27 if ($reference !== '') $reference = $this->checkHash($reference); 28 if ($reference !== '') $reference = $this->checkFilename($reference); 29 if ($reference !== '') $reference = $this->checkNamespace($reference); 30 if ($reference !== '') $reference = $this->checkClassPrefix($reference); 31 if ($reference !== '') $reference = $this->checkVariable($reference); 32 if ($reference !== '') $reference = $this->checkFunction($reference); 33 if ($reference !== '') $reference = $this->checkPSRClass($reference); 34 if ($reference !== '') $this->def = $reference; 35 } 36 37 /** 38 * @return string 39 */ 40 public function getDef() 41 { 42 return trim(preg_replace('/[^\w]+/', '', $this->def)); 43 } 44 45 /** 46 * @return string 47 */ 48 public function getPath() 49 { 50 return trim(preg_replace('/[^\w.]+/', ' ', $this->path)); 51 } 52 53 /** 54 * @return array 55 */ 56 public function getDeprecations() 57 { 58 return $this->deprecations; 59 } 60 61 /** 62 * Replace deprecated classes 63 * 64 * @param string $reference 65 * @return string 66 */ 67 protected function checkDeprecation($reference) 68 { 69 return $this->deprecations[$reference] ?? $reference; 70 } 71 72 /** 73 * Handle things in the form path#symbol 74 * 75 * @param string $reference 76 * @return string 77 */ 78 protected function checkHash($reference) 79 { 80 if (!str_contains($reference, '#')) return $reference; 81 [$this->path, $this->def] = explode('#', $reference, 2); 82 return ''; 83 } 84 85 /** 86 * Known file extension? 87 * 88 * @param string $reference 89 * @return mixed|string 90 */ 91 protected function checkFilename($reference) 92 { 93 if (preg_match('/\.(php|js|css|html)$/', $reference)) { 94 $this->def = ''; 95 $this->path = $reference; 96 return ''; 97 } 98 return $reference; 99 } 100 101 /** 102 * Namespaces are paths 103 * 104 * @param string $reference 105 * @return string 106 */ 107 protected function checkNamespace($reference) 108 { 109 if (!str_contains($reference, '\\')) return $reference; 110 111 $parts = explode('\\', $reference); 112 $parts = array_values(array_filter($parts)); 113 114 $reference = array_pop($parts); // last part may be more than a class 115 116 // our classes are in inc 117 if ($parts[0] == 'dokuwiki') $parts[0] = 'inc'; 118 119 $this->path = implode(' ', $parts); 120 121 return $reference; 122 } 123 124 /** 125 * Is there something called on a class? 126 */ 127 protected function checkClassPrefix($reference) 128 { 129 if ( 130 !str_contains($reference, '::') && 131 !str_contains($reference, '->') 132 ) { 133 return $reference; 134 } 135 [$class, $reference] = preg_split('/(::|->)/', $reference, 2); 136 137 $this->path .= ' ' . $class; 138 $this->def = $reference; 139 return ''; 140 } 141 142 /** 143 * Clearly a variable 144 * 145 * @param string $reference 146 * @return string 147 */ 148 protected function checkVariable($reference) 149 { 150 if ($reference[0] == '$') { 151 $this->def = $reference; 152 return ''; 153 } 154 return $reference; 155 } 156 157 /** 158 * It's a function 159 * 160 * @param string $reference 161 * @return string 162 */ 163 protected function checkFunction($reference) 164 { 165 if (str_ends_with($reference, '()')) { 166 $this->def = $reference; 167 return ''; 168 } 169 if (preg_match('/\(.+?\)$/', $reference)) { 170 [$reference, /* $arguments */] = explode('(', $reference, 2); 171 $this->def = $reference; 172 return ''; 173 } 174 return $reference; 175 } 176 177 /** 178 * Upercase followed by lowercase letter, must be a class 179 * 180 * Those are in their own files, so add it to the path 181 * @param $reference 182 * @return mixed|string 183 */ 184 protected function checkPSRClass($reference) 185 { 186 if (preg_match('/^[A-Z][a-z]/', $reference)) { 187 $this->def = $reference; 188 $this->path .= ' ' . $reference; 189 return ''; 190 } 191 return $reference; 192 } 193 194 /** 195 * Load deprecated classes info 196 */ 197 protected function loadDeprecations() 198 { 199 $this->deprecations = []; 200 201 // class aliases 202 $legacy = file_get_contents(DOKU_INC . 'inc/legacy.php'); 203 if ( 204 preg_match_all( 205 '/class_alias\(\'([^\']+)\', *\'([^\']+)\'\)/', 206 $legacy, 207 $matches, 208 PREG_SET_ORDER 209 ) 210 ) { 211 foreach ($matches as $match) { 212 $this->deprecations[$match[2]] = $match[1]; 213 } 214 } 215 216 // deprecated classes 217 $deprecations = file_get_contents(DOKU_INC . 'inc/deprecated.php'); 218 if ( 219 preg_match_all( 220 '/class (.+?) extends (\\\\dokuwiki\\\\.+?)(\s|$|{)/', 221 $deprecations, 222 $matches, 223 PREG_SET_ORDER 224 ) 225 ) { 226 foreach ($matches as $match) { 227 $this->deprecations[$match[1]] = $match[2]; 228 } 229 } 230 } 231} 232