Dotclear

source: inc/core/class.dc.store.reader.php @ 3874:ab8368569446

Revision 3874:ab8368569446, 7.3 KB checked in by franck <carnet.franck.paul@…>, 7 years ago (diff)

short notation for array (array() → [])

Line 
1<?php
2/**
3 * @brief Repository modules XML feed reader
4 *
5 * Provides an object to parse XML feed of modules from repository.
6 *
7 * @package Dotclear
8 * @subpackage Core
9 *
10 * @copyright Olivier Meunier & Association Dotclear
11 * @copyright GPL-2.0-only
12 *
13 * @since 2.6
14 */
15
16if (!defined('DC_RC_PATH')) {return;}
17
18class dcStoreReader extends netHttp
19{
20    /** @var    string    User agent used to query repository */
21    protected $user_agent = 'DotClear.org RepoBrowser/0.1';
22    /** @var    integer    User agent used to query repository */
23    protected $timeout = 5;
24    /** @var    array     HTTP Cache validators */
25    protected $validators = null;
26    /** @var    string    Cache temporary directory */
27    protected $cache_dir = null;
28    /** @var    string    Cache file prefix */
29    protected $cache_file_prefix = 'dcrepo';
30    /** @var    integer    Cache TTL */
31    protected $cache_ttl = '-30 minutes';
32    /** @var    boolean    'Cache' TTL on server failed */
33    protected $cache_touch_on_fail = true;
34    /** @var    boolean    Force query server */
35    protected $force = false;
36
37    /**
38     * Constructor.
39     *
40     * Bypass first argument of clearbricks netHttp constructor.
41     */
42    public function __construct()
43    {
44        parent::__construct('');
45        $this->setUserAgent(sprintf('Dotclear/%s)', DC_VERSION));
46    }
47
48    /**
49     * Parse modules feed.
50     *
51     * @param    string    $url        XML feed URL
52     * @return    object    dcStore instance
53     */
54    public function parse($url)
55    {
56        $this->validators = [];
57
58        if ($this->cache_dir) {
59            return $this->withCache($url);
60        } elseif (!$this->getModulesXML($url) || $this->getStatus() != '200') {
61            return false;
62        }
63
64        return new dcStoreParser($this->getContent());
65    }
66
67    /**
68     * Quick parse modules feed.
69     *
70     * @param    string    $url        XML feed URL
71     * @param    string    $cache_dir    Cache directoy or null for no cache
72     * @param    boolean    $force        Force query repository
73     * @return    object    Self instance
74     */
75    public static function quickParse($url, $cache_dir = null, $force = false)
76    {
77        $parser = new self();
78        if ($cache_dir) {
79            $parser->setCacheDir($cache_dir);
80        }
81        if ($force) {
82            $parser->setForce($force);
83        }
84
85        return $parser->parse($url);
86    }
87
88    /**
89     * Set cache directory.
90     *
91     * @param    string    $dir        Cache directory
92     * @return    boolean    True if cache dierctory is useable
93     */
94    public function setCacheDir($dir)
95    {
96        $this->cache_dir = null;
97
98        if (!empty($dir) && is_dir($dir) && is_writeable($dir)) {
99            $this->cache_dir = $dir;
100            return true;
101        }
102
103        return false;
104    }
105
106    /**
107     * Set cache TTL.
108     *
109     * @param    string    $str        Cache TTL
110     */
111    public function setCacheTTL($str)
112    {
113        $str = trim($str);
114
115        if (!empty($str)) {
116            $this->cache_ttl = substr($str, 0, 1) == '-' ? $str : '-' . $str;
117        }
118    }
119
120    /**
121     * Set force query repository.
122     *
123     * @param    boolean    $force    True to force query
124     */
125    public function setForce($force)
126    {
127        $this->force = $force;
128    }
129
130    /**
131     * Get repository XML feed URL content.
132     *
133     * @param    string    $url        XML feed URL
134     * @return    string    Feed content
135     */
136    protected function getModulesXML($url)
137    {
138        if (!self::readURL($url, $ssl, $host, $port, $path, $user, $pass)) {
139            return false;
140        }
141        $this->setHost($host, $port);
142        $this->useSSL($ssl);
143        $this->setAuthorization($user, $pass);
144
145        try {
146            return $this->get($path);
147        } catch (Exception $e) {
148            // @todo Log error when repository query fail
149            return false;
150        }
151    }
152
153    /**
154     * Get repository modules list using cache.
155     *
156     * @param    string    $url        XML feed URL
157     * @return    array    Feed content or False on fail
158     */
159    protected function withCache($url)
160    {
161        $url_md5     = md5($url);
162        $cached_file = sprintf('%s/%s/%s/%s/%s.ser',
163            $this->cache_dir,
164            $this->cache_file_prefix,
165            substr($url_md5, 0, 2),
166            substr($url_md5, 2, 2),
167            $url_md5
168        );
169
170        $may_use_cached = false;
171
172        # Use cache file ?
173        if (@file_exists($cached_file) && !$this->force) {
174            $may_use_cached = true;
175            $ts             = @filemtime($cached_file);
176            if ($ts > strtotime($this->cache_ttl)) {
177                # Direct cache
178                return unserialize(file_get_contents($cached_file));
179            }
180            $this->setValidator('IfModifiedSince', $ts);
181        }
182
183        # Query repository
184        if (!$this->getModulesXML($url)) {
185            if ($may_use_cached) {
186                # Touch cache TTL even if query failed ?
187                if ($this->cache_touch_on_fail) {
188                    @files::touch($cached_file);
189                }
190                # Connection failed - fetched from cache
191                return unserialize(file_get_contents($cached_file));
192            }
193            return false;
194        }
195
196        # Parse response
197        switch ($this->getStatus()) {
198            # Not modified, use cache
199            case '304':
200                @files::touch($cached_file);
201                return unserialize(file_get_contents($cached_file));
202            # Ok, parse feed
203            case '200':
204                if ($modules = new dcStoreParser($this->getContent())) {
205                    try {
206                        files::makeDir(dirname($cached_file), true);
207                    } catch (Exception $e) {
208                        return $modules;
209                    }
210
211                    if (($fp = @fopen($cached_file, 'wb'))) {
212                        fwrite($fp, serialize($modules));
213                        fclose($fp);
214                        files::inheritChmod($cached_file);
215                    }
216                    return $modules;
217                }
218        }
219
220        return false;
221    }
222
223    /**
224     * Prepare query.
225     *
226     * @return    array    Query headers
227     */
228    protected function buildRequest()
229    {
230        $headers = parent::buildRequest();
231
232        # Cache validators
233        if (!empty($this->validators)) {
234            if (isset($this->validators['IfModifiedSince'])) {
235                $headers[] = 'If-Modified-Since: ' . $this->validators['IfModifiedSince'];
236            }
237            if (isset($this->validators['IfNoneMatch'])) {
238                if (is_array($this->validators['IfNoneMatch'])) {
239                    $etags = implode(',', $this->validators['IfNoneMatch']);
240                } else {
241                    $etags = $this->validators['IfNoneMatch'];
242                }
243                $headers[] = '';
244            }
245        }
246
247        return $headers;
248    }
249
250    /**
251     * Tweak query cache validator.
252     *
253     * @param    string    $key        Validator key
254     * @param    string    $value        Validator value
255     */
256    private function setValidator($key, $value)
257    {
258        if ($key == 'IfModifiedSince') {
259            $value = gmdate('D, d M Y H:i:s', $value) . ' GMT';
260        }
261
262        $this->validators[$key] = $value;
263    }
264}
Note: See TracBrowser for help on using the repository browser.

Sites map