xref: /dokuwiki/inc/load.php (revision 71096e46fcbfaeaa808667aba794e77fe2780169)
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