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 'Input' => 'Input.class.php', 53 'JpegMeta' => 'JpegMeta.php', 54 'SimplePie' => 'SimplePie.php', 55 'FeedParser' => 'FeedParser.php', 56 'SafeFN' => 'SafeFN.class.php', 57 'Mailer' => 'Mailer.class.php', 58 'Doku_Handler' => 'parser/handler.php', 59 'Doku_Renderer' => 'parser/renderer.php', 60 'Doku_Renderer_xhtml' => 'parser/xhtml.php', 61 'Doku_Renderer_code' => 'parser/code.php', 62 'Doku_Renderer_xhtmlsummary' => 'parser/xhtmlsummary.php', 63 'Doku_Renderer_metadata' => 'parser/metadata.php' 64 ]; 65 66 /** 67 * Load common libs and register autoloader 68 */ 69 public function __construct() 70 { 71 require_once(DOKU_INC . 'vendor/autoload.php'); 72 spl_autoload_register([$this, 'autoload']); 73 $this->loadCommonLibs(); 74 } 75 76 /** 77 * require all the common libraries 78 * 79 * @return true 80 */ 81 public function loadCommonLibs() 82 { 83 foreach ($this->commonLibs as $lib) { 84 require_once(DOKU_INC . 'inc/' . $lib); 85 } 86 return true; 87 } 88 89 /** 90 * spl_autoload_register callback 91 * 92 * @param string $className 93 * @return bool 94 */ 95 public function autoload($className) 96 { 97 // namespace to directory conversion 98 $classPath = str_replace('\\', '/', $className); 99 100 return $this->autoloadFixedClass($className) 101 || $this->autoloadTestMockClass($classPath) 102 || $this->autoloadTestClass($classPath) 103 || $this->autoloadPluginClass($classPath) 104 || $this->autoloadTemplateClass($classPath) 105 || $this->autoloadCoreClass($classPath) 106 || $this->autoloadNamedPluginClass($className); 107 } 108 109 /** 110 * Check if the class is one of the fixed names 111 * 112 * @param string $className 113 * @return bool true if the class was loaded, false otherwise 114 */ 115 protected function autoloadFixedClass($className) 116 { 117 if (isset($this->fixedClassNames[$className])) { 118 require($this->fixedClassNames[$className]); 119 return true; 120 } 121 return false; 122 } 123 124 /** 125 * Check if the class is a test mock class 126 * 127 * @param string $classPath The class name using forward slashes as namespace separators 128 * @return bool true if the class was loaded, false otherwise 129 */ 130 protected function autoloadTestMockClass($classPath) 131 { 132 if ($this->prefixStrip($classPath, 'dokuwiki/test/mock/')) { 133 $file = DOKU_INC . '_test/mock/' . $classPath . '.php'; 134 if (file_exists($file)) { 135 require $file; 136 return true; 137 } 138 } 139 return false; 140 } 141 142 /** 143 * Check if the class is a test mock class 144 * 145 * @param string $classPath The class name using forward slashes as namespace separators 146 * @return bool true if the class was loaded, false otherwise 147 */ 148 protected function autoloadTestClass($classPath) 149 { 150 if ($this->prefixStrip($classPath, 'dokuwiki/test/')) { 151 $file = DOKU_INC . '_test/tests/' . $classPath . '.php'; 152 if (file_exists($file)) { 153 require $file; 154 return true; 155 } 156 } 157 return false; 158 } 159 160 /** 161 * Check if the class is a namespaced plugin class 162 * 163 * @param string $classPath The class name using forward slashes as namespace separators 164 * @return bool true if the class was loaded, false otherwise 165 */ 166 protected function autoloadPluginClass($classPath) 167 { 168 global $plugin_controller; 169 170 if ($this->prefixStrip($classPath, 'dokuwiki/plugin/')) { 171 $classPath = str_replace('/test/', '/_test/', $classPath); // no underscore in test namespace 172 $file = DOKU_PLUGIN . $classPath . '.php'; 173 if (file_exists($file)) { 174 $plugin = substr($classPath, 0, strpos($classPath, '/')); 175 // don't load disabled plugin classes (only if plugin controller is available) 176 if (!defined('DOKU_UNITTEST') && $plugin_controller && plugin_isdisabled($plugin)) return false; 177 178 try { 179 require $file; 180 } catch (\Throwable $e) { 181 ErrorHandler::showExceptionMsg($e, "Error loading plugin $plugin"); 182 } 183 return true; 184 } 185 } 186 return false; 187 } 188 189 /** 190 * Check if the class is a namespaced template class 191 * 192 * @param string $classPath The class name using forward slashes as namespace separators 193 * @return bool true if the class was loaded, false otherwise 194 */ 195 protected function autoloadTemplateClass($classPath) 196 { 197 // template namespace 198 if ($this->prefixStrip($classPath, 'dokuwiki/template/')) { 199 $classPath = str_replace('/test/', '/_test/', $classPath); // no underscore in test namespace 200 $file = DOKU_INC . 'lib/tpl/' . $classPath . '.php'; 201 if (file_exists($file)) { 202 $template = substr($classPath, 0, strpos($classPath, '/')); 203 204 try { 205 require $file; 206 } catch (\Throwable $e) { 207 ErrorHandler::showExceptionMsg($e, "Error loading template $template"); 208 } 209 return true; 210 } 211 } 212 return false; 213 } 214 215 /** 216 * Check if the class is a namespaced DokuWiki core class 217 * 218 * @param string $classPath The class name using forward slashes as namespace separators 219 * @return bool true if the class was loaded, false otherwise 220 */ 221 protected function autoloadCoreClass($classPath) 222 { 223 if ($this->prefixStrip($classPath, 'dokuwiki/')) { 224 $file = DOKU_INC . 'inc/' . $classPath . '.php'; 225 if (file_exists($file)) { 226 require $file; 227 return true; 228 } 229 } 230 return false; 231 } 232 233 /** 234 * Check if the class is a un-namespaced plugin class following our naming scheme 235 * 236 * @param string $className 237 * @return bool true if the class was loaded, false otherwise 238 */ 239 protected function autoloadNamedPluginClass($className) 240 { 241 global $plugin_controller; 242 243 if ( 244 preg_match( 245 '/^(' . implode('|', PluginController::PLUGIN_TYPES) . ')_plugin_(' . 246 DOKU_PLUGIN_NAME_REGEX . 247 ')(?:_([^_]+))?$/', 248 $className, 249 $m 250 ) 251 ) { 252 $c = ((count($m) === 4) ? "/{$m[3]}" : ''); 253 $plg = DOKU_PLUGIN . "{$m[2]}/{$m[1]}$c.php"; 254 if (file_exists($plg)) { 255 // don't load disabled plugin classes (only if plugin controller is available) 256 if (!defined('DOKU_UNITTEST') && $plugin_controller && plugin_isdisabled($m[2])) return false; 257 try { 258 require $plg; 259 } catch (\Throwable $e) { 260 ErrorHandler::showExceptionMsg($e, "Error loading plugin {$m[2]}"); 261 } 262 } 263 return true; 264 } 265 return false; 266 } 267 268 /** 269 * Check if the given string starts with the given prefix and strip it 270 * 271 * @param string $string 272 * @param string $prefix 273 * @return bool true if the prefix was found and stripped, false otherwise 274 */ 275 protected function prefixStrip(&$string, $prefix) 276 { 277 if (str_starts_with($string, $prefix)) { 278 $string = substr($string, strlen($prefix)); 279 return true; 280 } 281 return false; 282 } 283}; 284