Failed Conditions
Push — interwiki-remove-golucky ( 52fcdb...768be5 )
by Henry
12:48 queued 09:48
created

helper_plugin_extension_repository::search()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 1
dl 0
loc 22
rs 9.568
c 0
b 0
f 0
1
<?php
2
/**
3
 * DokuWiki Plugin extension (Helper Component)
4
 *
5
 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6
 * @author  Michael Hamann <[email protected]>
7
 */
8
9
use dokuwiki\Cache\Cache;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Cache.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
10
use dokuwiki\HTTP\DokuHTTPClient;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, DokuHTTPClient.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
11
use dokuwiki\Extension\PluginController;
12
13
/**
14
 * Class helper_plugin_extension_repository provides access to the extension repository on dokuwiki.org
15
 */
16
class helper_plugin_extension_repository extends DokuWiki_Plugin
17
{
18
19
    const EXTENSION_REPOSITORY_API = 'http://www.dokuwiki.org/lib/plugins/pluginrepo/api.php';
20
21
    private $loaded_extensions = array();
22
    private $has_access = null;
23
24
    /**
25
     * Initialize the repository (cache), fetches data for all installed plugins
26
     */
27
    public function init()
28
    {
29
        /* @var PluginController $plugin_controller */
30
        global $plugin_controller;
31
        if ($this->hasAccess()) {
32
            $list = $plugin_controller->getList('', true);
33
            $request_data = array('fmt' => 'php');
34
            $request_needed = false;
35
            foreach ($list as $name) {
36
                $cache = new Cache('##extension_manager##'.$name, '.repo');
37
38
                if (!isset($this->loaded_extensions[$name]) &&
39
                    $this->hasAccess() &&
40
                    !$cache->useCache(array('age' => 3600 * 24))
41
                ) {
42
                    $this->loaded_extensions[$name] = true;
43
                    $request_data['ext'][] = $name;
44
                    $request_needed = true;
45
                }
46
            }
47
48
            if ($request_needed) {
49
                $httpclient = new DokuHTTPClient();
50
                $data = $httpclient->post(self::EXTENSION_REPOSITORY_API, $request_data);
51
                if ($data !== false) {
52
                    $extensions = unserialize($data);
53
                    foreach ($extensions as $extension) {
54
                        $cache = new Cache('##extension_manager##'.$extension['plugin'], '.repo');
55
                        $cache->storeCache(serialize($extension));
56
                    }
57
                } else {
58
                    $this->has_access = false;
59
                }
60
            }
61
        }
62
    }
63
64
    /**
65
     * If repository access is available
66
     *
67
     * @param bool $usecache use cached result if still valid
68
     * @return bool If repository access is available
69
     */
70
    public function hasAccess($usecache = true) {
71
        if ($this->has_access === null) {
72
            $cache = new Cache('##extension_manager###hasAccess', '.repo');
73
74
            if (!$cache->useCache(array('age' => 60*10, 'purge' => !$usecache))) {
75
                $httpclient = new DokuHTTPClient();
76
                $httpclient->timeout = 5;
77
                $data = $httpclient->get(self::EXTENSION_REPOSITORY_API.'?cmd=ping');
78
                if ($data !== false) {
79
                    $this->has_access = true;
80
                    $cache->storeCache(1);
81
                } else {
82
                    $this->has_access = false;
83
                    $cache->storeCache(0);
84
                }
85
            } else {
86
                $this->has_access = ($cache->retrieveCache(false) == 1);
87
            }
88
        }
89
        return $this->has_access;
90
    }
91
92
    /**
93
     * Get the remote data of an individual plugin or template
94
     *
95
     * @param string $name  The plugin name to get the data for, template names need to be prefix by 'template:'
96
     * @return array The data or null if nothing was found (possibly no repository access)
97
     */
98
    public function getData($name)
99
    {
100
        $cache = new Cache('##extension_manager##'.$name, '.repo');
101
102
        if (!isset($this->loaded_extensions[$name]) &&
103
            $this->hasAccess() &&
104
            !$cache->useCache(array('age' => 3600 * 24))
105
        ) {
106
            $this->loaded_extensions[$name] = true;
107
            $httpclient = new DokuHTTPClient();
108
            $data = $httpclient->get(self::EXTENSION_REPOSITORY_API.'?fmt=php&ext[]='.urlencode($name));
109
            if ($data !== false) {
110
                $result = unserialize($data);
111
                $cache->storeCache(serialize($result[0]));
112
                return $result[0];
113
            } else {
114
                $this->has_access = false;
115
            }
116
        }
117
        if (file_exists($cache->cache)) {
118
            return unserialize($cache->retrieveCache(false));
119
        }
120
        return array();
121
    }
122
123
    /**
124
     * Search for plugins or templates using the given query string
125
     *
126
     * @param string $q the query string
127
     * @return array a list of matching extensions
128
     */
129
    public function search($q)
130
    {
131
        $query = $this->parseQuery($q);
132
        $query['fmt'] = 'php';
133
134
        $httpclient = new DokuHTTPClient();
135
        $data = $httpclient->post(self::EXTENSION_REPOSITORY_API, $query);
136
        if ($data === false) return array();
137
        $result = unserialize($data);
138
139
        $ids = array();
140
141
        // store cache info for each extension
142
        foreach ($result as $ext) {
143
            $name = $ext['plugin'];
144
            $cache = new Cache('##extension_manager##'.$name, '.repo');
145
            $cache->storeCache(serialize($ext));
146
            $ids[] = $name;
147
        }
148
149
        return $ids;
150
    }
151
152
    /**
153
     * Parses special queries from the query string
154
     *
155
     * @param string $q
156
     * @return array
157
     */
158
    protected function parseQuery($q)
159
    {
160
        $parameters = array(
161
            'tag'  => array(),
162
            'mail' => array(),
163
            'type' => array(),
164
            'ext'  => array()
165
        );
166
167
        // extract tags
168
        if (preg_match_all('/(^|\s)(tag:([\S]+))/', $q, $matches, PREG_SET_ORDER)) {
169
            foreach ($matches as $m) {
170
                $q = str_replace($m[2], '', $q);
171
                $parameters['tag'][] = $m[3];
172
            }
173
        }
174
        // extract author ids
175
        if (preg_match_all('/(^|\s)(authorid:([\S]+))/', $q, $matches, PREG_SET_ORDER)) {
176
            foreach ($matches as $m) {
177
                $q = str_replace($m[2], '', $q);
178
                $parameters['mail'][] = $m[3];
179
            }
180
        }
181
        // extract extensions
182
        if (preg_match_all('/(^|\s)(ext:([\S]+))/', $q, $matches, PREG_SET_ORDER)) {
183
            foreach ($matches as $m) {
184
                $q = str_replace($m[2], '', $q);
185
                $parameters['ext'][] = $m[3];
186
            }
187
        }
188
        // extract types
189
        if (preg_match_all('/(^|\s)(type:([\S]+))/', $q, $matches, PREG_SET_ORDER)) {
190
            foreach ($matches as $m) {
191
                $q = str_replace($m[2], '', $q);
192
                $parameters['type'][] = $m[3];
193
            }
194
        }
195
196
        // FIXME make integer from type value
197
198
        $parameters['q'] = trim($q);
199
        return $parameters;
200
    }
201
}
202
203
// vim:ts=4:sw=4:et:
204