Failed Conditions
Pull Request — psr2 (#2725)
by Michael
09:53 queued 06:49
created

helper_plugin_extension_repository   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 186
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
dl 0
loc 186
rs 9.92
c 0
b 0
f 0
wmc 31
lcom 1
cbo 4

5 Methods

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