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