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