mirror of
https://github.com/dokuwiki/dokuwiki-plugin-xref.git
synced 2025-07-25 16:11:59 +00:00
new mechanisms to reference Grok instead of phprxref
This implements a mechanism that transforms our current references into search terms understood by Grok. Using the API we can even check if there's any hits (ideally it should be exactly one)
This commit is contained in:
52
.github/workflows/phpTestLinux.yml
vendored
Normal file
52
.github/workflows/phpTestLinux.yml
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
name: PHP Tests on Linux
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
testLinux:
|
||||
name: PHP ${{ matrix.php-versions }} DokuWiki ${{ matrix.dokuwiki-branch }}
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-versions: ['7.2', '7.3', '7.4', '8.0']
|
||||
dokuwiki-branch: [ 'master', 'stable']
|
||||
exclude:
|
||||
- dokuwiki-branch: 'stable'
|
||||
php-versions: '8.0'
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
extensions: mbstring, intl, PDO, pdo_sqlite, bz2
|
||||
|
||||
- name: Setup problem matchers
|
||||
run: |
|
||||
echo ::add-matcher::${{ runner.tool_cache }}/php.json
|
||||
echo ::add-matcher::${{ runner.tool_cache }}/phpunit.json
|
||||
|
||||
- name: Download DokuWiki Test-setup
|
||||
run: wget https://raw.github.com/splitbrain/dokuwiki-travis/master/travis.sh
|
||||
|
||||
- name: Run DokuWiki Test-setup
|
||||
env:
|
||||
CI_SERVER: 1
|
||||
DOKUWIKI: ${{ matrix.dokuwiki-branch }}
|
||||
run: sh travis.sh
|
||||
|
||||
- name: Setup PHPUnit
|
||||
run: |
|
||||
php _test/fetchphpunit.php
|
||||
cd _test
|
||||
|
||||
- name: Run PHPUnit
|
||||
run: |
|
||||
cd _test
|
||||
php phpunit.phar --verbose --stderr --group plugin_xref
|
72
Grok.php
Normal file
72
Grok.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\plugin\xref;
|
||||
|
||||
use dokuwiki\HTTP\DokuHTTPClient;
|
||||
|
||||
class Grok
|
||||
{
|
||||
|
||||
protected $baseUrl;
|
||||
protected $def;
|
||||
protected $path;
|
||||
|
||||
public function __construct($reference, $baseUrl = 'https://codesearch.dokuwiki.org')
|
||||
{
|
||||
$heuristic = new Heuristics($reference);
|
||||
$this->def = $heuristic->getDef();
|
||||
$this->path = $heuristic->getPath();
|
||||
$this->baseUrl = rtrim($baseUrl, '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the URL that leads to the search interface
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSearchUrl()
|
||||
{
|
||||
$url = $this->baseUrl . '/search?';
|
||||
$param = [
|
||||
'project' => 'dokuwiki',
|
||||
'defs' => $this->def,
|
||||
'path' => $this->path,
|
||||
];
|
||||
$url .= buildURLparams($param, '&');
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the URL that allows to query the API
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAPIUrl()
|
||||
{
|
||||
$url = $this->baseUrl . '/api/v1/search?';
|
||||
$param = [
|
||||
'projects' => 'dokuwiki',
|
||||
'def' => $this->def,
|
||||
'path' => $this->path,
|
||||
];
|
||||
$url .= buildURLparams($param, '&');
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of results to expect
|
||||
*
|
||||
* @return false|int false on errors
|
||||
*/
|
||||
public function getResultCount()
|
||||
{
|
||||
$http = new DokuHTTPClient();
|
||||
$http->timeout = 5;
|
||||
$json = $http->get($this->getAPIUrl());
|
||||
if (!$json) return false;
|
||||
$data = json_decode($json, true);
|
||||
if (!$data) return false;
|
||||
if (!isset($data['resultCount'])) return false;
|
||||
return $data['resultCount'];
|
||||
}
|
||||
}
|
164
Heuristics.php
Normal file
164
Heuristics.php
Normal file
@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\plugin\xref;
|
||||
|
||||
/**
|
||||
* Figure out what to send to Grok to hopefully show the right, single hit
|
||||
*/
|
||||
class Heuristics
|
||||
{
|
||||
|
||||
/** @var string the definition to search */
|
||||
protected $def = '';
|
||||
/** @var string the path to use */
|
||||
protected $path = '';
|
||||
|
||||
/**
|
||||
* Try to gues what the given reference means and how to best search for it
|
||||
*
|
||||
* @param string $reference
|
||||
*/
|
||||
public function __construct($reference)
|
||||
{
|
||||
if ($reference !== '') $reference = $this->checkHash($reference);
|
||||
if ($reference !== '') $reference = $this->checkFilename($reference);
|
||||
if ($reference !== '') $reference = $this->checkNamespace($reference);
|
||||
if ($reference !== '') $reference = $this->checkClassPrefix($reference);
|
||||
if ($reference !== '') $reference = $this->checkVariable($reference);
|
||||
if ($reference !== '') $reference = $this->checkFunction($reference);
|
||||
if ($reference !== '') $reference = $this->checkPSRClass($reference);
|
||||
if ($reference !== '') $this->def = $reference;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDef()
|
||||
{
|
||||
return trim(preg_replace('/[^\w]+/', '', $this->def));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPath()
|
||||
{
|
||||
return trim(preg_replace('/[^\w.]+/', ' ', $this->path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle things in the form path#symbol
|
||||
*
|
||||
* @param string $reference
|
||||
* @return string
|
||||
*/
|
||||
protected function checkHash($reference)
|
||||
{
|
||||
if (strpos($reference, '#') === false) return $reference;
|
||||
list($this->path, $this->def) = explode('#', $reference, 2);
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Known file extension?
|
||||
*
|
||||
* @param string $reference
|
||||
* @return mixed|string
|
||||
*/
|
||||
protected function checkFilename($reference)
|
||||
{
|
||||
if (preg_match('/\.(php|js|css|html)$/', $reference)) {
|
||||
$this->def = '';
|
||||
$this->path = $reference;
|
||||
return '';
|
||||
}
|
||||
return $reference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Namespaces are paths
|
||||
*
|
||||
* @param string $reference
|
||||
* @return string
|
||||
*/
|
||||
protected function checkNamespace($reference)
|
||||
{
|
||||
if (strpos($reference, '\\') === false) return $reference;
|
||||
|
||||
$parts = explode('\\', $reference);
|
||||
$parts = array_filter($parts);
|
||||
$reference = array_pop($parts); // last part may be more than a class
|
||||
|
||||
// our classes are in inc
|
||||
if($parts[0] == 'dokuwiki') $parts[0] = 'inc';
|
||||
|
||||
$this->path = join(' ', $parts);
|
||||
|
||||
return $reference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is there something called on a class?
|
||||
*/
|
||||
protected function checkClassPrefix($reference)
|
||||
{
|
||||
if (
|
||||
strpos($reference, '::') === false &&
|
||||
strpos($reference, '->') === false
|
||||
) {
|
||||
return $reference;
|
||||
}
|
||||
list($class, $reference) = preg_split('/(::|->)/', $reference, 2);
|
||||
|
||||
$this->path .= ' ' . $class;
|
||||
$this->def = $reference;
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Clearly a variable
|
||||
*
|
||||
* @param string $reference
|
||||
* @return string
|
||||
*/
|
||||
protected function checkVariable($reference)
|
||||
{
|
||||
if ($reference[0] == '$') {
|
||||
$this->def = $reference;
|
||||
return '';
|
||||
}
|
||||
return $reference;
|
||||
}
|
||||
|
||||
/**
|
||||
* It's a function
|
||||
*
|
||||
* @param string $reference
|
||||
* @return string
|
||||
*/
|
||||
protected function checkFunction($reference)
|
||||
{
|
||||
if (substr($reference, -2) == '()') {
|
||||
$this->def = $reference;
|
||||
return '';
|
||||
}
|
||||
return $reference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Upercase followed by lowercase letter, must be a class
|
||||
*
|
||||
* Those are in their own files, so add it to the path
|
||||
* @param $reference
|
||||
* @return mixed|string
|
||||
*/
|
||||
protected function checkPSRClass($reference)
|
||||
{
|
||||
if (preg_match('/^[A-Z][a-z]/', $reference)) {
|
||||
$this->def = $reference;
|
||||
$this->path .= ' ' . $reference;
|
||||
return '';
|
||||
}
|
||||
return $reference;
|
||||
}
|
||||
}
|
86
_test/GeneralTest.php
Normal file
86
_test/GeneralTest.php
Normal file
@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\plugin\xref\test;
|
||||
|
||||
use DokuWikiTest;
|
||||
|
||||
/**
|
||||
* General tests for the xref plugin
|
||||
*
|
||||
* @group plugin_xref
|
||||
* @group plugins
|
||||
*/
|
||||
class GeneralTest extends DokuWikiTest
|
||||
{
|
||||
|
||||
/**
|
||||
* Simple test to make sure the plugin.info.txt is in correct format
|
||||
*/
|
||||
public function testPluginInfo(): void
|
||||
{
|
||||
$file = __DIR__ . '/../plugin.info.txt';
|
||||
$this->assertFileExists($file);
|
||||
|
||||
$info = confToHash($file);
|
||||
|
||||
$this->assertArrayHasKey('base', $info);
|
||||
$this->assertArrayHasKey('author', $info);
|
||||
$this->assertArrayHasKey('email', $info);
|
||||
$this->assertArrayHasKey('date', $info);
|
||||
$this->assertArrayHasKey('name', $info);
|
||||
$this->assertArrayHasKey('desc', $info);
|
||||
$this->assertArrayHasKey('url', $info);
|
||||
|
||||
$this->assertEquals('xref', $info['base']);
|
||||
$this->assertRegExp('/^https?:\/\//', $info['url']);
|
||||
$this->assertTrue(mail_isvalid($info['email']));
|
||||
$this->assertRegExp('/^\d\d\d\d-\d\d-\d\d$/', $info['date']);
|
||||
$this->assertTrue(false !== strtotime($info['date']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to ensure that every conf['...'] entry in conf/default.php has a corresponding meta['...'] entry in
|
||||
* conf/metadata.php.
|
||||
*/
|
||||
public function testPluginConf(): void
|
||||
{
|
||||
$conf_file = __DIR__ . '/../conf/default.php';
|
||||
$meta_file = __DIR__ . '/../conf/metadata.php';
|
||||
|
||||
if (!file_exists($conf_file) && !file_exists($meta_file)) {
|
||||
self::markTestSkipped('No config files exist -> skipping test');
|
||||
}
|
||||
|
||||
if (file_exists($conf_file)) {
|
||||
include($conf_file);
|
||||
}
|
||||
if (file_exists($meta_file)) {
|
||||
include($meta_file);
|
||||
}
|
||||
|
||||
$this->assertEquals(
|
||||
gettype($conf),
|
||||
gettype($meta),
|
||||
'Both ' . DOKU_PLUGIN . 'xref/conf/default.php and ' . DOKU_PLUGIN . 'xref/conf/metadata.php have to exist and contain the same keys.'
|
||||
);
|
||||
|
||||
if ($conf !== null && $meta !== null) {
|
||||
foreach ($conf as $key => $value) {
|
||||
$this->assertArrayHasKey(
|
||||
$key,
|
||||
$meta,
|
||||
'Key $meta[\'' . $key . '\'] missing in ' . DOKU_PLUGIN . 'xref/conf/metadata.php'
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($meta as $key => $value) {
|
||||
$this->assertArrayHasKey(
|
||||
$key,
|
||||
$conf,
|
||||
'Key $conf[\'' . $key . '\'] missing in ' . DOKU_PLUGIN . 'xref/conf/default.php'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
45
_test/GrokTest.php
Normal file
45
_test/GrokTest.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\plugin\xref\test;
|
||||
|
||||
use dokuwiki\plugin\xref\Grok;
|
||||
use DokuWikiTest;
|
||||
|
||||
/**
|
||||
* Grok tests for the xref plugin
|
||||
*
|
||||
* @group plugin_xref
|
||||
* @group plugins
|
||||
* @group internet
|
||||
*/
|
||||
class GrokTest extends DokuWikiTest
|
||||
{
|
||||
/**
|
||||
* @return \string[][]
|
||||
* @see testResultCount
|
||||
*/
|
||||
public function provideData()
|
||||
{
|
||||
// These should all be unique enough to have only one result
|
||||
return [
|
||||
['auth.php#auth_setup'],
|
||||
['inc/Menu/Item/AbstractItem.php'],
|
||||
['dokuwiki\Menu\Item\AbstractItem'],
|
||||
['dokuwiki\Menu\Item\AbstractItem::getLabel()'],
|
||||
['dokuwiki\Menu\Item\AbstractItem->getLabel()'],
|
||||
['AbstractItem::getLabel()'],
|
||||
['AbstractItem->getLabel()'],
|
||||
['AbstractItem'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideData
|
||||
* @param string $reference
|
||||
*/
|
||||
public function testResultCount($reference)
|
||||
{
|
||||
$grok = new Grok($reference);
|
||||
$this->assertEquals(1, $grok->getResultCount(), $grok->getSearchUrl());
|
||||
}
|
||||
}
|
51
_test/HeuristicsTest.php
Normal file
51
_test/HeuristicsTest.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\plugin\xref\test;
|
||||
|
||||
use dokuwiki\plugin\xref\Heuristics;
|
||||
use DokuWikiTest;
|
||||
|
||||
/**
|
||||
* Heuristics tests for the xref plugin
|
||||
*
|
||||
* @group plugin_xref
|
||||
* @group plugins
|
||||
*/
|
||||
class HeuristicsTest extends DokuWikiTest
|
||||
{
|
||||
/**
|
||||
* @return \string[][]
|
||||
* @see testHeuristics
|
||||
*/
|
||||
public function provideData()
|
||||
{
|
||||
return [
|
||||
['auth.php#auth_setup', 'auth_setup', 'auth.php'],
|
||||
['inc/Menu/Item/AbstractItem.php', '', 'inc Menu Item AbstractItem.php'],
|
||||
['dokuwiki\Menu\Item\AbstractItem', 'AbstractItem', 'inc Menu Item AbstractItem'],
|
||||
['dokuwiki\Menu\Item\AbstractItem::getLabel()', 'getLabel', 'inc Menu Item AbstractItem'],
|
||||
['dokuwiki\Menu\Item\AbstractItem->getLabel()', 'getLabel', 'inc Menu Item AbstractItem'],
|
||||
['AbstractItem::getLabel()', 'getLabel', 'AbstractItem'],
|
||||
['AbstractItem->getLabel()', 'getLabel', 'AbstractItem'],
|
||||
['$INFO', 'INFO', ''],
|
||||
['foobar()', 'foobar', ''],
|
||||
['FooBar()', 'FooBar', ''],
|
||||
['AbstractItem', 'AbstractItem', 'AbstractItem'],
|
||||
['abstractItem', 'abstractItem', ''],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideData
|
||||
* @param string $reference
|
||||
* @param string $expDef
|
||||
* @param string $expPath
|
||||
*/
|
||||
public function testHeuristics($reference, $expDef, $expPath)
|
||||
{
|
||||
$heur = new Heuristics($reference);
|
||||
|
||||
$this->assertEquals($expDef, $heur->getDef(), 'definition is wrong');
|
||||
$this->assertEquals($expPath, $heur->getPath(), 'path is wrong');
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user