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