Dotclear

source: inc/core/class.dc.store.reader.php @ 2222:30fe556de226

Revision 2222:30fe556de226, 5.9 KB checked in by Denis Jean-Chirstian <contact@…>, 12 years ago (diff)

Enhance repository search engine (and some fix)

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          }
65          elseif (!$this->getModulesXML($url) || $this->getStatus() != '200') {
66               return false;
67          }
68
69          return new dcStoreParser($this->getContent());
70     }
71
72     /**
73      * Quick parse modules feed.
74      *
75      * @param string    $url      XML feed URL
76      * @param string    $cache_dir     Cache directoy or null for no cache
77      * @param boolean   $force         Force query repository
78      * @return     object    Self instance
79      */
80     public static function quickParse($url, $cache_dir=null, $force=false)
81     {
82          $parser = new self();
83          if ($cache_dir) {
84               $parser->setCacheDir($cache_dir);
85          }
86          if ($force) {
87               $parser->setForce($force);
88          }
89
90          return $parser->parse($url);
91     }
92
93     /**
94      * Set cache directory.
95      *
96      * @param string    $dir      Cache directory
97      * @return     boolean   True if cache dierctory is useable
98      */
99     public function setCacheDir($dir)
100     {
101          $this->cache_dir = null;
102
103          if (!empty($dir) && is_dir($dir) && is_writeable($dir)) {
104               $this->cache_dir = $dir;
105               return true;
106          }
107
108          return false;
109     }
110
111     /**
112      * Set cache TTL.
113      *
114      * @param string    $str      Cache TTL
115      */
116     public function setCacheTTL($str)
117     {
118          $str = trim($str);
119
120          if (!empty($str)) {
121               $this->cache_ttl = substr($str, 0, 1) == '-' ? $str : '-'.$str;
122          }
123     }
124
125     /**
126      * Set force query repository.
127      *
128      * @param boolean   $force    True to force query
129      */
130     public function setForce($force)
131     {
132          $this->force = $force;
133     }
134
135     /**
136      * Get repository XML feed URL content.
137      *
138      * @param string    $url      XML feed URL
139      * @return     string    Feed content
140      */
141     protected function getModulesXML($url)
142     {
143          if (!self::readURL($url, $ssl, $host, $port, $path, $user, $pass)) {
144               return false;
145          }
146          $this->setHost($host, $port);
147          $this->useSSL($ssl);
148          $this->setAuthorization($user, $pass);
149
150          return $this->get($path);
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          {
199               # Not modified, use cache
200               case '304':
201                    @files::touch($cached_file);
202                    return unserialize(file_get_contents($cached_file));
203               # Ok, parse feed
204               case '200':
205                    if ($modules = new dcStoreParser($this->getContent())) {
206                         try {
207                              files::makeDir(dirname($cached_file), true);
208                         }
209                         catch (Exception $e) {
210                              return $modules;
211                         }
212
213                         if (($fp = @fopen($cached_file, 'wb'))) {
214                              fwrite($fp, serialize($modules));
215                              fclose($fp);
216                              files::inheritChmod($cached_file);
217                         }
218                         return $modules;
219                    }
220          }
221
222          return false;
223     }
224
225     /**
226      * Prepare query.
227      *
228      * @return     array     Query headers
229      */
230     protected function buildRequest()
231     {
232          $headers = parent::buildRequest();
233
234          # Cache validators
235          if (!empty($this->validators)) {
236               if (isset($this->validators['IfModifiedSince'])) {
237                    $headers[] = 'If-Modified-Since: '.$this->validators['IfModifiedSince'];
238               }
239               if (isset($this->validators['IfNoneMatch'])) {
240                    if (is_array($this->validators['IfNoneMatch'])) {
241                         $etags = implode(',', $this->validators['IfNoneMatch']);
242                    }
243                    else {
244                         $etags = $this->validators['IfNoneMatch'];
245                    }
246                    $headers[] = '';
247               }
248          }
249
250          return $headers;
251     }
252
253     /**
254      * Tweak query cache validator.
255      *
256      * @param string    $key      Validator key
257      * @param string    $value         Validator value
258      */
259     private function setValidator($key, $value)
260     {
261          if ($key == 'IfModifiedSince') {
262               $value = gmdate('D, d M Y H:i:s', $value).' GMT';
263          }
264
265          $this->validators[$key] = $value;
266     }
267}
Note: See TracBrowser for help on using the repository browser.

Sites map