1<?php 2 3/** 4 * Load all internal libraries and setup class autoloader 5 * 6 * @author Andreas Gohr <andi@splitbrain.org> 7 */ 8 9namespace dokuwiki; 10 11use dokuwiki\Extension\PluginController; 12 13return new class { 14 /** @var string[] Common libraries that are always loaded */ 15 protected array $commonLibs = [ 16 'defines.php', 17 'actions.php', 18 'changelog.php', 19 'common.php', 20 'confutils.php', 21 'pluginutils.php', 22 'form.php', 23 'fulltext.php', 24 'html.php', 25 'httputils.php', 26 'indexer.php', 27 'infoutils.php', 28 'io.php', 29 'mail.php', 30 'media.php', 31 'pageutils.php', 32 'parserutils.php', 33 'search.php', 34 'template.php', 35 'toolbar.php', 36 'utf8.php', 37 'auth.php', 38 'compatibility.php', 39 'deprecated.php', 40 'legacy.php', 41 ]; 42 43 /** @var string[] Classname to file mappings */ 44 protected array $fixedClassNames = [ 45 'Diff' => 'DifferenceEngine.php', 46 'UnifiedDiffFormatter' => 'DifferenceEngine.php', 47 'TableDiffFormatter' => 'DifferenceEngine.php', 48 'cache' => 'cache.php', 49 'cache_parser' => 'cache.php', 50 'cache_instructions' => 'cache.php', 51 'cache_renderer' => 'cache.php', 52 'JpegMeta' => 'JpegMeta.php', 53 'FeedParser' => 'FeedParser.php', 54 'SafeFN' => 'SafeFN.class.php', 55 'Mailer' => 'Mailer.class.php', 56 'Doku_Renderer' => 'parser/renderer.php', 57 'Doku_Renderer_xhtml' => 'parser/xhtml.php', 58 'Doku_Renderer_code' => 'parser/code.php', 59 'Doku_Renderer_xhtmlsummary' => 'parser/xhtmlsummary.php', 60 'Doku_Renderer_metadata' => 'parser/metadata.php' 61 ]; 62 63 /** 64 * Load common libs and register autoloader 65 */ 66 public function __construct() 67 { 68 require_once(DOKU_INC . 'vendor/autoload.php'); 69 spl_autoload_register($this->autoload(...)); 70 $this->loadCommonLibs(); 71 } 72 73 /** 74 * require all the common libraries 75 * 76 * @return true 77 */ 78 public function loadCommonLibs() 79 { 80 foreach ($this->commonLibs as $lib) { 81 require_once(DOKU_INC . 'inc/' . $lib); 82 } 83 return true; 84 } 85 86 /** 87 * spl_autoload_register callback 88 * 89 * @param string $className 90 * @return bool 91 */ 92 public function autoload($className) 93 { 94 // namespace to directory conversion 95 $classPath = str_replace('\\', '/', $className); 96 97 return $this->autoloadFixedClass($className) 98 || $this->autoloadTestMockClass($classPath) 99 || $this->autoloadTestClass($classPath) 100 || $this->autoloadPluginClass($classPath) 101 || $this->autoloadTemplateClass($classPath) 102 || $this->autoloadCoreClass($classPath) 103 || $this->autoloadNamedPluginClass($className); 104 } 105 106 /** 107 * Check if the class is one of the fixed names 108 * 109 * @param string $className 110 * @return bool true if the class was loaded, false otherwise 111 */ 112 protected function autoloadFixedClass($className) 113 { 114 if (isset($this->fixedClassNames[$className])) { 115 require($this->fixedClassNames[$className]); 116 return true; 117 } 118 return false; 119 } 120 121 /** 122 * Check if the class is a test mock class 123 * 124 * @param string $classPath The class name using forward slashes as namespace separators 125 * @return bool true if the class was loaded, false otherwise 126 */ 127 protected function autoloadTestMockClass($classPath) 128 { 129 if ($this->prefixStrip($classPath, 'dokuwiki/test/mock/')) { 130 $file = DOKU_INC . '_test/mock/' . $classPath . '.php'; 131 if (file_exists($file)) { 132 require $file; 133 return true; 134 } 135 } 136 return false; 137 } 138 139 /** 140 * Check if the class is a test mock class 141 * 142 * @param string $classPath The class name using forward slashes as namespace separators 143 * @return bool true if the class was loaded, false otherwise 144 */ 145 protected function autoloadTestClass($classPath) 146 { 147 if ($this->prefixStrip($classPath, 'dokuwiki/test/')) { 148 $file = DOKU_INC . '_test/tests/' . $classPath . '.php'; 149 if (file_exists($file)) { 150 require $file; 151 return true; 152 } 153 } 154 return false; 155 } 156 157 /** 158 * Check if the class is a namespaced plugin class 159 * 160 * @param string $classPath The class name using forward slashes as namespace separators 161 * @return bool true if the class was loaded, false otherwise 162 */ 163 protected function autoloadPluginClass($classPath) 164 { 165 global $plugin_controller; 166 167 if ($this->prefixStrip($classPath, 'dokuwiki/plugin/')) { 168 $classPath = str_replace('/test/', '/_test/', $classPath); // no underscore in test namespace 169 $file = DOKU_PLUGIN . $classPath . '.php'; 170 if (file_exists($file)) { 171 $plugin = substr($classPath, 0, strpos($classPath, '/')); 172 // don't load disabled plugin classes (only if plugin controller is available) 173 if (!defined('DOKU_UNITTEST') && $plugin_controller && plugin_isdisabled($plugin)) return false; 174 175 try { 176 require $file; 177 } catch (\Throwable $e) { 178 ErrorHandler::showExceptionMsg($e, "Error loading plugin $plugin"); 179 } 180 return true; 181 } 182 } 183 return false; 184 } 185 186 /** 187 * Check if the class is a namespaced template class 188 * 189 * @param string $classPath The class name using forward slashes as namespace separators 190 * @return bool true if the class was loaded, false otherwise 191 */ 192 protected function autoloadTemplateClass($classPath) 193 { 194 // template namespace 195 if ($this->prefixStrip($classPath, 'dokuwiki/template/')) { 196 $classPath = str_replace('/test/', '/_test/', $classPath); // no underscore in test namespace 197 $file = DOKU_INC . 'lib/tpl/' . $classPath . '.php'; 198 if (file_exists($file)) { 199 $template = substr($classPath, 0, strpos($classPath, '/')); 200 201 try { 202 require $file; 203 } catch (\Throwable $e) { 204 ErrorHandler::showExceptionMsg($e, "Error loading template $template"); 205 } 206 return true; 207 } 208 } 209 return false; 210 } 211 212 /** 213 * Check if the class is a namespaced DokuWiki core class 214 * 215 * @param string $classPath The class name using forward slashes as namespace separators 216 * @return bool true if the class was loaded, false otherwise 217 */ 218 protected function autoloadCoreClass($classPath) 219 { 220 if ($this->prefixStrip($classPath, 'dokuwiki/')) { 221 $file = DOKU_INC . 'inc/' . $classPath . '.php'; 222 if (file_exists($file)) { 223 require $file; 224 return true; 225 } 226 } 227 return false; 228 } 229 230 /** 231 * Check if the class is a un-namespaced plugin class following our naming scheme 232 * 233 * @param string $className 234 * @return bool true if the class was loaded, false otherwise 235 */ 236 protected function autoloadNamedPluginClass($className) 237 { 238 global $plugin_controller; 239 240 if ( 241 preg_match( 242 '/^(' . implode('|', PluginController::PLUGIN_TYPES) . ')_plugin_(' . 243 DOKU_PLUGIN_NAME_REGEX . 244 ')(?:_([^_]+))?$/', 245 $className, 246 $m 247 ) 248 ) { 249 $c = ((count($m) === 4) ? "/{$m[3]}" : ''); 250 $plg = DOKU_PLUGIN . "{$m[2]}/{$m[1]}$c.php"; 251 if (file_exists($plg)) { 252 // don't load disabled plugin classes (only if plugin controller is available) 253 if (!defined('DOKU_UNITTEST') && $plugin_controller && plugin_isdisabled($m[2])) return false; 254 try { 255 require $plg; 256 } catch (\Throwable $e) { 257 ErrorHandler::showExceptionMsg($e, "Error loading plugin {$m[2]}"); 258 } 259 } 260 return true; 261 } 262 return false; 263 } 264 265 /** 266 * Check if the given string starts with the given prefix and strip it 267 * 268 * @param string $string 269 * @param string $prefix 270 * @return bool true if the prefix was found and stripped, false otherwise 271 */ 272 protected function prefixStrip(&$string, $prefix) 273 { 274 if (str_starts_with($string, $prefix)) { 275 $string = substr($string, strlen($prefix)); 276 return true; 277 } 278 return false; 279 } 280}; 281