Dotclear

source: inc/core/class.dc.store.reader.php @ 3730:5c45a5df9a59

Revision 3730:5c45a5df9a59, 7.5 KB checked in by franck <carnet.franck.paul@…>, 7 years ago (diff)

Code formatting (PSR-2)

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

Sites map