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