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