xref: /plugin/aichat/cli.php (revision 34a1c47875552330ce367360d99f2c3f9f69af94)
18817535bSAndreas Gohr<?php
28817535bSAndreas Gohr
3f6ef2e50SAndreas Gohruse dokuwiki\Extension\CLIPlugin;
4f6ef2e50SAndreas Gohruse dokuwiki\plugin\aichat\Chunk;
501f06932SAndreas Gohruse dokuwiki\Search\Indexer;
6c4584168SAndreas Gohruse splitbrain\phpcli\Colors;
78817535bSAndreas Gohruse splitbrain\phpcli\Options;
83379af09SAndreas Gohruse splitbrain\phpcli\TableFormatter;
98817535bSAndreas Gohr
108817535bSAndreas Gohr/**
118817535bSAndreas Gohr * DokuWiki Plugin aichat (CLI Component)
128817535bSAndreas Gohr *
138817535bSAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
148817535bSAndreas Gohr * @author  Andreas Gohr <gohr@cosmocode.de>
158817535bSAndreas Gohr */
16f6ef2e50SAndreas Gohrclass cli_plugin_aichat extends CLIPlugin
178817535bSAndreas Gohr{
180337f47fSAndreas Gohr    /** @var helper_plugin_aichat */
190337f47fSAndreas Gohr    protected $helper;
200337f47fSAndreas Gohr
210337f47fSAndreas Gohr    public function __construct($autocatch = true)
220337f47fSAndreas Gohr    {
230337f47fSAndreas Gohr        parent::__construct($autocatch);
240337f47fSAndreas Gohr        $this->helper = plugin_load('helper', 'aichat');
253379af09SAndreas Gohr        $this->helper->setLogger($this);
260337f47fSAndreas Gohr    }
270337f47fSAndreas Gohr
288817535bSAndreas Gohr    /** @inheritDoc */
298817535bSAndreas Gohr    protected function setup(Options $options)
308817535bSAndreas Gohr    {
31bddd899cSAndreas Gohr        $options->useCompactHelp();
32bddd899cSAndreas Gohr
335284515dSAndreas Gohr        $options->setHelp(
345284515dSAndreas Gohr            'Manage and query the AI chatbot data. Please note that calls to your LLM provider will be made. ' .
355284515dSAndreas Gohr            'This may incur costs.'
365284515dSAndreas Gohr        );
378817535bSAndreas Gohr
385284515dSAndreas Gohr        $options->registerCommand(
395284515dSAndreas Gohr            'embed',
405284515dSAndreas Gohr            'Create embeddings for all pages. This skips pages that already have embeddings'
415284515dSAndreas Gohr        );
425284515dSAndreas Gohr        $options->registerOption(
435284515dSAndreas Gohr            'clear',
445284515dSAndreas Gohr            'Clear all existing embeddings before creating new ones',
457ebc7895Ssplitbrain            'c',
467ebc7895Ssplitbrain            false,
477ebc7895Ssplitbrain            'embed'
485284515dSAndreas Gohr        );
498817535bSAndreas Gohr
503379af09SAndreas Gohr        $options->registerCommand('maintenance', 'Run storage maintenance. Refert to the documentation for details.');
513379af09SAndreas Gohr
528817535bSAndreas Gohr        $options->registerCommand('similar', 'Search for similar pages');
538817535bSAndreas Gohr        $options->registerArgument('query', 'Look up chunks similar to this query', true, 'similar');
548817535bSAndreas Gohr
558817535bSAndreas Gohr        $options->registerCommand('ask', 'Ask a question');
568817535bSAndreas Gohr        $options->registerArgument('question', 'The question to ask', true, 'ask');
57c4584168SAndreas Gohr
58c4584168SAndreas Gohr        $options->registerCommand('chat', 'Start an interactive chat session');
59ad38c5fdSAndreas Gohr
60e75dc39fSAndreas Gohr        $options->registerCommand('info', 'Get Info about the vector storage and other stats');
618c8b7ba6SAndreas Gohr
62ad38c5fdSAndreas Gohr        $options->registerCommand('split', 'Split a page into chunks (for debugging)');
63ad38c5fdSAndreas Gohr        $options->registerArgument('page', 'The page to split', true, 'split');
645786be46SAndreas Gohr
6501f06932SAndreas Gohr        $options->registerCommand('page', 'Check if chunks for a given page are available (for debugging)');
6601f06932SAndreas Gohr        $options->registerArgument('page', 'The page to check', true, 'page');
67dc355d57SAndreas Gohr        $options->registerOption('dump', 'Dump the chunks', 'd', false, 'page');
6801f06932SAndreas Gohr
698c8b7ba6SAndreas Gohr        $options->registerCommand('tsv', 'Create TSV files for visualizing at http://projector.tensorflow.org/' .
708c8b7ba6SAndreas Gohr            ' Not supported on all storages.');
718c8b7ba6SAndreas Gohr        $options->registerArgument('vector.tsv', 'The vector file', false, 'tsv');
728c8b7ba6SAndreas Gohr        $options->registerArgument('meta.tsv', 'The meta file', false, 'tsv');
738817535bSAndreas Gohr    }
748817535bSAndreas Gohr
758817535bSAndreas Gohr    /** @inheritDoc */
768817535bSAndreas Gohr    protected function main(Options $options)
778817535bSAndreas Gohr    {
783379af09SAndreas Gohr        ini_set('memory_limit', -1);
798817535bSAndreas Gohr        switch ($options->getCmd()) {
808817535bSAndreas Gohr            case 'embed':
815284515dSAndreas Gohr                $this->createEmbeddings($options->getOpt('clear'));
828817535bSAndreas Gohr                break;
833379af09SAndreas Gohr            case 'maintenance':
843379af09SAndreas Gohr                $this->runMaintenance();
853379af09SAndreas Gohr                break;
868817535bSAndreas Gohr            case 'similar':
878817535bSAndreas Gohr                $this->similar($options->getArgs()[0]);
888817535bSAndreas Gohr                break;
897552f1aaSAndreas Gohr            case 'ask':
907552f1aaSAndreas Gohr                $this->ask($options->getArgs()[0]);
917552f1aaSAndreas Gohr                break;
92c4584168SAndreas Gohr            case 'chat':
93c4584168SAndreas Gohr                $this->chat();
94c4584168SAndreas Gohr                break;
95ad38c5fdSAndreas Gohr            case 'split':
96ad38c5fdSAndreas Gohr                $this->split($options->getArgs()[0]);
97ad38c5fdSAndreas Gohr                break;
9801f06932SAndreas Gohr            case 'page':
99dc355d57SAndreas Gohr                $this->page($options->getArgs()[0], $options->getOpt('dump'));
10001f06932SAndreas Gohr                break;
1015786be46SAndreas Gohr            case 'info':
102f6ef2e50SAndreas Gohr                $this->showinfo();
1035786be46SAndreas Gohr                break;
1048c8b7ba6SAndreas Gohr            case 'tsv':
1058c8b7ba6SAndreas Gohr                $args = $options->getArgs();
1068c8b7ba6SAndreas Gohr                $vector = $args[0] ?? 'vector.tsv';
1078c8b7ba6SAndreas Gohr                $meta = $args[1] ?? 'meta.tsv';
1088c8b7ba6SAndreas Gohr                $this->tsv($vector, $meta);
1098c8b7ba6SAndreas Gohr                break;
1108817535bSAndreas Gohr            default:
1118817535bSAndreas Gohr                echo $options->help();
1128817535bSAndreas Gohr        }
1138817535bSAndreas Gohr    }
1148817535bSAndreas Gohr
115c4584168SAndreas Gohr    /**
1165786be46SAndreas Gohr     * @return void
1175786be46SAndreas Gohr     */
118f6ef2e50SAndreas Gohr    protected function showinfo()
1195786be46SAndreas Gohr    {
1203379af09SAndreas Gohr        $stats = [
1213379af09SAndreas Gohr            'model' => $this->getConf('model'),
1223379af09SAndreas Gohr        ];
123e75dc39fSAndreas Gohr        $stats = array_merge(
124e75dc39fSAndreas Gohr            $stats,
125e75dc39fSAndreas Gohr            array_map('dformat', $this->helper->getRunData()),
126e75dc39fSAndreas Gohr            $this->helper->getStorage()->statistics()
127e75dc39fSAndreas Gohr        );
1283379af09SAndreas Gohr        $this->printTable($stats);
1297ee8b02dSAndreas Gohr    }
130911314cdSAndreas Gohr
1313379af09SAndreas Gohr    /**
1323379af09SAndreas Gohr     * Print key value data as tabular data
1333379af09SAndreas Gohr     *
1343379af09SAndreas Gohr     * @param array $data
1353379af09SAndreas Gohr     * @param int $level
1363379af09SAndreas Gohr     * @return void
1373379af09SAndreas Gohr     */
1383379af09SAndreas Gohr    protected function printTable($data, $level = 0)
1393379af09SAndreas Gohr    {
1403379af09SAndreas Gohr        $tf = new TableFormatter($this->colors);
1413379af09SAndreas Gohr        foreach ($data as $key => $value) {
1423379af09SAndreas Gohr            if (is_array($value)) {
1433379af09SAndreas Gohr                echo $tf->format(
144e75dc39fSAndreas Gohr                    [$level * 2, 20, '*'],
1453379af09SAndreas Gohr                    ['', $key, ''],
1463379af09SAndreas Gohr                    [Colors::C_LIGHTBLUE, Colors::C_LIGHTBLUE, Colors::C_LIGHTBLUE]
1473379af09SAndreas Gohr                );
1483379af09SAndreas Gohr                $this->printTable($value, $level + 1);
1493379af09SAndreas Gohr            } else {
1503379af09SAndreas Gohr                echo $tf->format(
151e75dc39fSAndreas Gohr                    [$level * 2, 20, '*'],
1523379af09SAndreas Gohr                    ['', $key, $value],
1533379af09SAndreas Gohr                    [Colors::C_LIGHTBLUE, Colors::C_LIGHTBLUE, Colors::C_LIGHTGRAY]
1543379af09SAndreas Gohr                );
1553379af09SAndreas Gohr            }
1563379af09SAndreas Gohr        }
1575786be46SAndreas Gohr    }
1585786be46SAndreas Gohr
1595786be46SAndreas Gohr    /**
16001f06932SAndreas Gohr     * Check chunk availability for a given page
16101f06932SAndreas Gohr     *
16201f06932SAndreas Gohr     * @param string $page
16301f06932SAndreas Gohr     * @return void
16401f06932SAndreas Gohr     */
165dc355d57SAndreas Gohr    protected function page($page, $dump = false)
16601f06932SAndreas Gohr    {
16701f06932SAndreas Gohr        $indexer = new Indexer();
16801f06932SAndreas Gohr        $pages = $indexer->getPages();
16901f06932SAndreas Gohr        $pos = array_search(cleanID($page), $pages);
17001f06932SAndreas Gohr
17101f06932SAndreas Gohr        if ($pos === false) {
17201f06932SAndreas Gohr            $this->error('Page not found');
17301f06932SAndreas Gohr            return;
17401f06932SAndreas Gohr        }
17501f06932SAndreas Gohr
17601f06932SAndreas Gohr        $storage = $this->helper->getStorage();
17701f06932SAndreas Gohr        $chunks = $storage->getPageChunks($page, $pos * 100);
17801f06932SAndreas Gohr        if ($chunks) {
17901f06932SAndreas Gohr            $this->success('Found ' . count($chunks) . ' chunks');
180dc355d57SAndreas Gohr            if ($dump) {
181dc355d57SAndreas Gohr                echo json_encode($chunks, JSON_PRETTY_PRINT);
182dc355d57SAndreas Gohr            }
18301f06932SAndreas Gohr        } else {
18401f06932SAndreas Gohr            $this->error('No chunks found');
18501f06932SAndreas Gohr        }
18601f06932SAndreas Gohr    }
18701f06932SAndreas Gohr
18801f06932SAndreas Gohr    /**
189ad38c5fdSAndreas Gohr     * Split the given page into chunks and print them
190ad38c5fdSAndreas Gohr     *
191ad38c5fdSAndreas Gohr     * @param string $page
192ad38c5fdSAndreas Gohr     * @return void
193ad38c5fdSAndreas Gohr     * @throws Exception
194ad38c5fdSAndreas Gohr     */
195ad38c5fdSAndreas Gohr    protected function split($page)
196ad38c5fdSAndreas Gohr    {
197ad38c5fdSAndreas Gohr        $text = rawWiki($page);
198ad38c5fdSAndreas Gohr        $chunks = $this->helper->getEmbeddings()->splitIntoChunks($text);
199ad38c5fdSAndreas Gohr        foreach ($chunks as $chunk) {
200ad38c5fdSAndreas Gohr            echo $chunk;
201ad38c5fdSAndreas Gohr            echo "\n";
202ad38c5fdSAndreas Gohr            $this->colors->ptln('--------------------------------', Colors::C_LIGHTPURPLE);
203ad38c5fdSAndreas Gohr        }
204ad38c5fdSAndreas Gohr        $this->success('Split into ' . count($chunks) . ' chunks');
205ad38c5fdSAndreas Gohr    }
206ad38c5fdSAndreas Gohr
207ad38c5fdSAndreas Gohr    /**
208c4584168SAndreas Gohr     * Interactive Chat Session
209c4584168SAndreas Gohr     *
210c4584168SAndreas Gohr     * @return void
211c4584168SAndreas Gohr     * @throws Exception
212c4584168SAndreas Gohr     */
213c4584168SAndreas Gohr    protected function chat()
214c4584168SAndreas Gohr    {
215*34a1c478SAndreas Gohr        if($this->loglevel['debug']['enabled']) {
216*34a1c478SAndreas Gohr            $this->helper->getChatModel()->setDebug(true);
217*34a1c478SAndreas Gohr        }
218*34a1c478SAndreas Gohr
219c4584168SAndreas Gohr        $history = [];
220c4584168SAndreas Gohr        while ($q = $this->readLine('Your Question')) {
2216a18e0f4SAndreas Gohr            $this->helper->getChatModel()->resetUsageStats();
222f6ef2e50SAndreas Gohr            $result = $this->helper->askChatQuestion($q, $history);
223f6ef2e50SAndreas Gohr            $this->colors->ptln("Interpretation: {$result['question']}", Colors::C_LIGHTPURPLE);
224f6ef2e50SAndreas Gohr            $history[] = [$result['question'], $result['answer']];
225c4584168SAndreas Gohr            $this->printAnswer($result);
226c4584168SAndreas Gohr        }
227c4584168SAndreas Gohr    }
228c4584168SAndreas Gohr
229c4584168SAndreas Gohr    /**
230c4584168SAndreas Gohr     * Handle a single, standalone question
231c4584168SAndreas Gohr     *
232c4584168SAndreas Gohr     * @param string $query
233c4584168SAndreas Gohr     * @return void
234c4584168SAndreas Gohr     * @throws Exception
235c4584168SAndreas Gohr     */
236c4584168SAndreas Gohr    protected function ask($query)
237c4584168SAndreas Gohr    {
238*34a1c478SAndreas Gohr        if($this->loglevel['debug']['enabled']) {
239*34a1c478SAndreas Gohr            $this->helper->getChatModel()->setDebug(true);
240*34a1c478SAndreas Gohr        }
241*34a1c478SAndreas Gohr
2420337f47fSAndreas Gohr        $result = $this->helper->askQuestion($query);
243c4584168SAndreas Gohr        $this->printAnswer($result);
2447552f1aaSAndreas Gohr    }
2457552f1aaSAndreas Gohr
246c4584168SAndreas Gohr    /**
247c4584168SAndreas Gohr     * Get the pages that are similar to the query
248c4584168SAndreas Gohr     *
249c4584168SAndreas Gohr     * @param string $query
250c4584168SAndreas Gohr     * @return void
251c4584168SAndreas Gohr     */
2528817535bSAndreas Gohr    protected function similar($query)
2538817535bSAndreas Gohr    {
254e33a1d7aSAndreas Gohr        $langlimit = $this->helper->getLanguageLimit();
255e33a1d7aSAndreas Gohr        if ($langlimit) {
256e33a1d7aSAndreas Gohr            $this->info('Limiting results to {lang}', ['lang' => $langlimit]);
257e33a1d7aSAndreas Gohr        }
258e33a1d7aSAndreas Gohr
259e33a1d7aSAndreas Gohr        $sources = $this->helper->getEmbeddings()->getSimilarChunks($query, $langlimit);
260f6ef2e50SAndreas Gohr        $this->printSources($sources);
2618817535bSAndreas Gohr    }
2628817535bSAndreas Gohr
263c4584168SAndreas Gohr    /**
2643379af09SAndreas Gohr     * Run the maintenance tasks
2653379af09SAndreas Gohr     *
2663379af09SAndreas Gohr     * @return void
2673379af09SAndreas Gohr     */
2683379af09SAndreas Gohr    protected function runMaintenance()
2693379af09SAndreas Gohr    {
2703379af09SAndreas Gohr        $start = time();
2713379af09SAndreas Gohr        $this->helper->getStorage()->runMaintenance();
2723379af09SAndreas Gohr        $this->notice('Peak memory used: {memory}', ['memory' => filesize_h(memory_get_peak_usage(true))]);
2733379af09SAndreas Gohr        $this->notice('Spent time: {time}min', ['time' => round((time() - $start) / 60, 2)]);
274e75dc39fSAndreas Gohr
275e75dc39fSAndreas Gohr        $data = $this->helper->getRunData();
276e75dc39fSAndreas Gohr        $data['maintenance ran at'] = time();
277e75dc39fSAndreas Gohr        $this->helper->setRunData($data);
2783379af09SAndreas Gohr    }
2793379af09SAndreas Gohr
2803379af09SAndreas Gohr    /**
281c4584168SAndreas Gohr     * Recreate chunks and embeddings for all pages
282c4584168SAndreas Gohr     *
283c4584168SAndreas Gohr     * @return void
284c4584168SAndreas Gohr     */
2855284515dSAndreas Gohr    protected function createEmbeddings($clear)
2868817535bSAndreas Gohr    {
287d5c102b3SAndreas Gohr        [$skipRE, $matchRE] = $this->getRegexps();
288d5c102b3SAndreas Gohr
2893379af09SAndreas Gohr        $start = time();
290d5c102b3SAndreas Gohr        $this->helper->getEmbeddings()->createNewIndex($skipRE, $matchRE, $clear);
291ad38c5fdSAndreas Gohr        $this->notice('Peak memory used: {memory}', ['memory' => filesize_h(memory_get_peak_usage(true))]);
2923379af09SAndreas Gohr        $this->notice('Spent time: {time}min', ['time' => round((time() - $start) / 60, 2)]);
293e75dc39fSAndreas Gohr
294e75dc39fSAndreas Gohr        $data = $this->helper->getRunData();
295e75dc39fSAndreas Gohr        $data['embed ran at'] = time();
296e75dc39fSAndreas Gohr        $this->helper->setRunData($data);
2978817535bSAndreas Gohr    }
2988817535bSAndreas Gohr
299c4584168SAndreas Gohr    /**
3008c8b7ba6SAndreas Gohr     * Dump TSV files for debugging
3018c8b7ba6SAndreas Gohr     *
3028c8b7ba6SAndreas Gohr     * @return void
3038c8b7ba6SAndreas Gohr     */
3048c8b7ba6SAndreas Gohr    protected function tsv($vector, $meta)
3058c8b7ba6SAndreas Gohr    {
3068c8b7ba6SAndreas Gohr
3078c8b7ba6SAndreas Gohr        $storage = $this->helper->getStorage();
3088c8b7ba6SAndreas Gohr        $storage->dumpTSV($vector, $meta);
3098c8b7ba6SAndreas Gohr        $this->success('written to ' . $vector . ' and ' . $meta);
3108c8b7ba6SAndreas Gohr    }
3118c8b7ba6SAndreas Gohr
3128c8b7ba6SAndreas Gohr    /**
31355392016SAndreas Gohr     * Print the given detailed answer in a nice way
31455392016SAndreas Gohr     *
31555392016SAndreas Gohr     * @param array $answer
31655392016SAndreas Gohr     * @return void
31755392016SAndreas Gohr     */
31855392016SAndreas Gohr    protected function printAnswer($answer)
31955392016SAndreas Gohr    {
32055392016SAndreas Gohr        $this->colors->ptln($answer['answer'], Colors::C_LIGHTCYAN);
32155392016SAndreas Gohr        echo "\n";
322f6ef2e50SAndreas Gohr        $this->printSources($answer['sources']);
32355392016SAndreas Gohr        echo "\n";
32455392016SAndreas Gohr        $this->printUsage();
32555392016SAndreas Gohr    }
32655392016SAndreas Gohr
32755392016SAndreas Gohr    /**
328f6ef2e50SAndreas Gohr     * Print the given sources
329f6ef2e50SAndreas Gohr     *
330f6ef2e50SAndreas Gohr     * @param Chunk[] $sources
331f6ef2e50SAndreas Gohr     * @return void
332f6ef2e50SAndreas Gohr     */
333f6ef2e50SAndreas Gohr    protected function printSources($sources)
334f6ef2e50SAndreas Gohr    {
335f6ef2e50SAndreas Gohr        foreach ($sources as $source) {
336f6ef2e50SAndreas Gohr            /** @var Chunk $source */
3379b3d1b36SAndreas Gohr            $this->colors->ptln(
3389b3d1b36SAndreas Gohr                "\t" . $source->getPage() . ' ' . $source->getId() . ' (' . $source->getScore() . ')',
3399b3d1b36SAndreas Gohr                Colors::C_LIGHTBLUE
3409b3d1b36SAndreas Gohr            );
341f6ef2e50SAndreas Gohr        }
342f6ef2e50SAndreas Gohr    }
343f6ef2e50SAndreas Gohr
344f6ef2e50SAndreas Gohr    /**
34555392016SAndreas Gohr     * Print the usage statistics for OpenAI
34655392016SAndreas Gohr     *
34755392016SAndreas Gohr     * @return void
34855392016SAndreas Gohr     */
349f6ef2e50SAndreas Gohr    protected function printUsage()
350f6ef2e50SAndreas Gohr    {
35155392016SAndreas Gohr        $this->info(
352f6ef2e50SAndreas Gohr            'Made {requests} requests in {time}s to Model. Used {tokens} tokens for about ${cost}.',
3536a18e0f4SAndreas Gohr            $this->helper->getChatModel()->getUsageStats()
35455392016SAndreas Gohr        );
35555392016SAndreas Gohr    }
35655392016SAndreas Gohr
35755392016SAndreas Gohr    /**
358c4584168SAndreas Gohr     * Interactively ask for a value from the user
359c4584168SAndreas Gohr     *
360c4584168SAndreas Gohr     * @param string $prompt
361c4584168SAndreas Gohr     * @return string
362c4584168SAndreas Gohr     */
363c4584168SAndreas Gohr    protected function readLine($prompt)
364c4584168SAndreas Gohr    {
365c4584168SAndreas Gohr        $value = '';
3668817535bSAndreas Gohr
367c4584168SAndreas Gohr        while ($value === '') {
368c4584168SAndreas Gohr            echo $prompt;
369c4584168SAndreas Gohr            echo ': ';
370c4584168SAndreas Gohr
371c4584168SAndreas Gohr            $fh = fopen('php://stdin', 'r');
372c4584168SAndreas Gohr            $value = trim(fgets($fh));
373c4584168SAndreas Gohr            fclose($fh);
374c4584168SAndreas Gohr        }
375c4584168SAndreas Gohr
376c4584168SAndreas Gohr        return $value;
377c4584168SAndreas Gohr    }
378d5c102b3SAndreas Gohr
379d5c102b3SAndreas Gohr    /**
380d5c102b3SAndreas Gohr     * Read the skip and match regex from the config
381d5c102b3SAndreas Gohr     *
382d5c102b3SAndreas Gohr     * Ensures the regular expressions are valid
383d5c102b3SAndreas Gohr     *
384d5c102b3SAndreas Gohr     * @return string[] [$skipRE, $matchRE]
385d5c102b3SAndreas Gohr     */
386d5c102b3SAndreas Gohr    protected function getRegexps()
387d5c102b3SAndreas Gohr    {
388d5c102b3SAndreas Gohr        $skip = $this->getConf('skipRegex');
389d5c102b3SAndreas Gohr        $skipRE = '';
390d5c102b3SAndreas Gohr        $match = $this->getConf('matchRegex');
391d5c102b3SAndreas Gohr        $matchRE = '';
392d5c102b3SAndreas Gohr
393d5c102b3SAndreas Gohr        if ($skip) {
394d5c102b3SAndreas Gohr            $skipRE = '/' . $skip . '/';
39549a7d3ccSsplitbrain            if (@preg_match($skipRE, '') === false) {
396d5c102b3SAndreas Gohr                $this->error(preg_last_error_msg());
397d5c102b3SAndreas Gohr                $this->error('Invalid regular expression in $conf[\'skipRegex\']. Ignored.');
398d5c102b3SAndreas Gohr                $skipRE = '';
399d5c102b3SAndreas Gohr            } else {
400d5c102b3SAndreas Gohr                $this->success('Skipping pages matching ' . $skipRE);
401d5c102b3SAndreas Gohr            }
402d5c102b3SAndreas Gohr        }
403d5c102b3SAndreas Gohr
404d5c102b3SAndreas Gohr        if ($match) {
405d5c102b3SAndreas Gohr            $matchRE = '/' . $match . '/';
40649a7d3ccSsplitbrain            if (@preg_match($matchRE, '') === false) {
407d5c102b3SAndreas Gohr                $this->error(preg_last_error_msg());
408d5c102b3SAndreas Gohr                $this->error('Invalid regular expression in $conf[\'matchRegex\']. Ignored.');
409d5c102b3SAndreas Gohr                $matchRE = '';
410d5c102b3SAndreas Gohr            } else {
411d5c102b3SAndreas Gohr                $this->success('Only indexing pages matching ' . $matchRE);
412d5c102b3SAndreas Gohr            }
413d5c102b3SAndreas Gohr        }
414d5c102b3SAndreas Gohr        return [$skipRE, $matchRE];
415d5c102b3SAndreas Gohr    }
4168817535bSAndreas Gohr}
417