Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
 
4
/**
5
 * Abstract base class for all the readers
6
 *
7
 * A reader is a compilation of serveral files that can be read
8
 *
9
 * PHP versions 4 and 5
10
 *
11
 * This library is free software; you can redistribute it and/or
12
 * modify it under the terms of the GNU Lesser General Public
13
 * License as published by the Free Software Foundation; either
14
 * version 2.1 of the License, or (at your option) any later version.
15
 *
16
 * This library is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19
 * Lesser General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU Lesser General Public
22
 * License along with this library; if not, write to the Free Software
23
 * Foundation, Inc., 59 Temple Place, Suite 330,Boston,MA 02111-1307 USA
24
 *
25
 * @category   File Formats
26
 * @package    File_Archive
27
 * @author     Vincent Lascaux <vincentlascaux@php.net>
28
 * @copyright  1997-2005 The PHP Group
29
 * @license    http://www.gnu.org/copyleft/lesser.html  LGPL
30
 * @version    CVS: $Id: Reader.php,v 1.36 2007/01/15 21:43:54 pfischer Exp $
31
 * @link       http://pear.php.net/package/File_Archive
32
 */
33
 
34
require_once "PEAR.php";
35
 
36
/**
37
 * Abstract base class for all the readers
38
 *
39
 * A reader is a compilation of serveral files that can be read
40
 */
41
class File_Archive_Reader
42
{
43
    /**
44
     * Move to the next file or folder in the reader
45
     *
46
     * @return bool false iif no more files are available
47
     */
48
    function next()
49
    {
50
        return false;
51
    }
52
 
53
    /**
54
     * Move to the next file whose name is in directory $filename
55
     * or is exactly $filename
56
     *
57
     * @param string $filename Name of the file to find in the archive
58
     * @param bool $close If true, close the reader and search from the first file
59
     * @return bool whether the file was found in the archive or not
60
     */
61
    function select($filename, $close = true)
62
    {
63
        $std = $this->getStandardURL($filename);
64
        if (substr($std, -1)=='/') {
65
            $std = substr($std, 0, -1);
66
        }
67
 
68
        if ($close) {
69
            $error = $this->close();
70
            if (PEAR::isError($error)) {
71
                return $error;
72
            }
73
        }
74
        while (($error = $this->next()) === true) {
75
            $sourceName = $this->getFilename();
76
            if (
77
                  empty($std) ||
78
 
79
                //$std is a file
80
                  $std == $sourceName ||
81
 
82
                //$std is a directory
83
                  (strncmp($std.'/', $sourceName, strlen($std)+1) == 0 &&
84
                   strlen($sourceName) > strlen($std)+1)
85
               ) {
86
                return true;
87
            }
88
        }
89
        return $error;
90
    }
91
 
92
    /**
93
     * Returns the standard path
94
     * Changes \ to /
95
     * Removes the .. and . from the URL
96
     * @param string $path a valid URL that may contain . or .. and \
97
     * @static
98
     */
99
    function getStandardURL($path)
100
    {
101
        if ($path == '.') {
102
            return '';
103
        }
104
        $std = str_replace("\\", "/", $path);
105
        while ($std != ($std = preg_replace("/[^\/:?]+\/\.\.\//", "", $std))) ;
106
        $std = str_replace("/./", "", $std);
107
        if (strncmp($std, "./", 2) == 0) {
108
            /**
109
             * If return value is going to be / (root on POSIX)
110
             */
111
            if (substr($std, 2) === '/') {
112
                return $std;
113
            }
114
            return substr($std, 2);
115
        } else {
116
            return $std;
117
        }
118
    }
119
 
120
    /**
121
     * Returns the name of the file currently read by the reader
122
     *
123
     * Warning: undefined behaviour if no call to next have been
124
     * done or if last call to next has returned false
125
     *
126
     * @return string Name of the current file
127
     */
128
    function getFilename()
129
    {
130
        return PEAR::raiseError("Reader abstract function call (getFilename)");
131
    }
132
 
133
    /**
134
     * Returns the list of filenames from the current pos to the end of the source
135
     * The source will be closed after having called this function
136
     * This function goes through the whole archive (which may be slow).
137
     * If you intend to work on the reader, doing it in one pass would be faster
138
     *
139
     * @return array filenames from the current pos to the end of the source
140
     */
141
    function getFileList()
142
    {
143
        $result = array();
144
        while ( ($error = $this->next()) === true) {
145
            $result[] = $this->getFilename();
146
        }
147
        $this->close();
148
        if (PEAR::isError($error)) {
149
            return $error;
150
        } else {
151
            return $result;
152
        }
153
    }
154
 
155
    /**
156
     * Returns an array of statistics about the file
157
     * (see the PHP stat function for more information)
158
     *
159
     * The returned array may be empty, even if readers should try
160
     * their best to return as many data as possible
161
     */
162
    function getStat() { return array(); }
163
 
164
    /**
165
     * Returns the MIME associated with the current file
166
     * The default function does that by looking at the extension of the file
167
     */
168
    function getMime()
169
    {
170
        require_once "File/Archive/Reader/MimeList.php";
171
        return File_Archive_Reader_GetMime($this->getFilename());
172
    }
173
 
174
    /**
175
     * If the current file of the archive is a physical file,
176
     *
177
     * @return the name of the physical file containing the data
178
     *         or null if no such file exists
179
     *
180
     * The data filename may not be the same as the filename.
181
     */
182
    function getDataFilename() { return null; }
183
 
184
    /**
185
     * Reads some data from the current file
186
     * If the end of the file is reached, returns null
187
     * If $length is not specified, reads up to the end of the file
188
     * If $length is specified reads up to $length
189
     */
190
    function getData($length = -1)
191
    {
192
        return PEAR::raiseError("Reader abstract function call (getData)");
193
    }
194
 
195
    /**
196
     * Skip some data and returns how many bytes have been skipped
197
     * This is strictly equivalent to
198
     *  return strlen(getData($length))
199
     * But could be far more efficient
200
     */
201
    function skip($length = -1)
202
    {
203
        $data = $this->getData($length);
204
        if (PEAR::isError($data)) {
205
            return $data;
206
        } else {
207
            return strlen($data);
208
        }
209
    }
210
 
211
    /**
212
     * Move the current position back of a given amount of bytes.
213
     * Not all readers may implement this function (a PEAR error will
214
     * be returned if the reader can't rewind)
215
     *
216
     * @param int $length number of bytes to seek before the current pos
217
     *        or -1 to move back to the begining of the current file
218
     * @return the number of bytes really rewinded (which may be less than
219
     *        $length if the current pos is less than $length
220
     */
221
    function rewind($length = -1)
222
    {
223
        return PEAR::raiseError('Rewind function is not implemented on this reader');
224
    }
225
 
226
    /**
227
     * Returns the current offset in the current file
228
     */
229
    function tell()
230
    {
231
        $offset = $this->rewind();
232
        $this->skip($offset);
233
        return $offset;
234
    }
235
 
236
    /**
237
     * Put back the reader in the state it was before the first call
238
     * to next()
239
     */
240
    function close()
241
    {
242
    }
243
 
244
    /**
245
     * Sends the current file to the Writer $writer
246
     * The data will be sent by chunks of at most $bufferSize bytes
247
     * If $bufferSize <= 0 (default), the blockSize option is used
248
     */
249
    function sendData(&$writer, $bufferSize = 0)
250
    {
251
        if (PEAR::isError($writer)) {
252
            return $writer;
253
        }
254
        if ($bufferSize <= 0) {
255
            $bufferSize = File_Archive::getOption('blockSize');
256
        }
257
 
258
        $filename = $this->getDataFilename();
259
        if ($filename !== null) {
260
            $error = $writer->writeFile($filename);
261
            if (PEAR::isError($error)) {
262
                return $error;
263
            }
264
        } else {
265
            while (($data = $this->getData($bufferSize)) !== null) {
266
                if (PEAR::isError($data)) {
267
                    return $data;
268
                }
269
                $error = $writer->writeData($data);
270
                if (PEAR::isError($error)) {
271
                    return $error;
272
                }
273
            }
274
        }
275
    }
276
 
277
    /**
278
     * Sends the whole reader to $writer and close the reader
279
     *
280
     * @param File_Archive_Writer $writer Where to write the files of the reader
281
     * @param bool $autoClose If true, close $writer at the end of the function.
282
     *        Default value is true
283
     * @param int $bufferSize Size of the chunks that will be sent to the writer
284
     *        If $bufferSize <= 0 (default value), the blockSize option is used
285
     */
286
    function extract(&$writer, $autoClose = true, $bufferSize = 0)
287
    {
288
        if (PEAR::isError($writer)) {
289
            $this->close();
290
            return $writer;
291
        }
292
 
293
        while (($error = $this->next()) === true) {
294
            if ($writer->newFileNeedsMIME()) {
295
                $mime = $this->getMime();
296
            } else {
297
                $mime = null;
298
            }
299
 
300
            $error = $writer->newFile(
301
                $this->getFilename(),
302
                $this->getStat(),
303
                $mime
304
            );
305
            if (PEAR::isError($error)) {
306
                break;
307
            }
308
            $error = $this->sendData($writer, $bufferSize);
309
            if (PEAR::isError($error)) {
310
                break;
311
            }
312
        }
313
        $this->close();
314
        if ($autoClose) {
315
            $writer->close();
316
        }
317
        if (PEAR::isError($error)) {
318
            return $error;
319
        }
320
    }
321
 
322
    /**
323
     * Extract only one file (given by the URL)
324
     *
325
     * @param string $filename URL of the file to extract from this
326
     * @param File_Archive_Writer $writer Where to write the file
327
     * @param bool $autoClose If true, close $writer at the end of the function
328
     *        Default value is true
329
     * @param int $bufferSize Size of the chunks that will be sent to the writer
330
     *        If $bufferSize <= 0 (default value), the blockSize option is used
331
     */
332
    function extractFile($filename, &$writer,
333
                         $autoClose = true, $bufferSize = 0)
334
    {
335
        if (PEAR::isError($writer)) {
336
            return $writer;
337
        }
338
 
339
        if (($error = $this->select($filename)) === true) {
340
            $result = $this->sendData($writer, $bufferSize);
341
            if (!PEAR::isError($result)) {
342
                $result = true;
343
            }
344
        } else if ($error === false) {
345
            $result = PEAR::raiseError("File $filename not found");
346
        } else {
347
            $result = $error;
348
        }
349
        if ($autoClose) {
350
            $error = $writer->close();
351
            if (PEAR::isError($error)) {
352
                return $error;
353
            }
354
        }
355
        return $result;
356
    }
357
 
358
    /**
359
     * Return a writer that allows appending files to the archive
360
     * After having called makeAppendWriter, $this is closed and should not be
361
     * used until the returned writer is closed.
362
     *
363
     * @return a writer that will allow to append files to an existing archive
364
     * @see makeWriter
365
     */
366
    function makeAppendWriter()
367
    {
368
        require_once "File/Archive/Predicate/False.php";
369
        return $this->makeWriterRemoveFiles(new File_Archive_Predicate_False());
370
    }
371
 
372
    /**
373
     * Return a writer that has the same properties as the one returned by
374
     * makeAppendWriter, but after having removed all the files that follow a
375
     * given predicate.
376
     * After a call to makeWriterRemoveFiles, $this is closed and should not
377
     * be used until the returned writer is closed
378
     *
379
     * @param File_Archive_Predicate $pred the predicate verified by removed files
380
     * @return File_Archive_Writer that allows to append files to the archive
381
     */
382
    function makeWriterRemoveFiles($pred)
383
    {
384
        return PEAR::raiseError("Reader abstract function call (makeWriterRemoveFiles)");
385
    }
386
 
387
    /**
388
     * Returns a writer that removes the current file
389
     * This is a syntaxic sugar for makeWriterRemoveFiles(new File_Archive_Predicate_Current());
390
     */
391
    function makeWriterRemove()
392
    {
393
        require_once "File/Archive/Predicate/Current.php";
394
        return $this->makeWriterRemoveFiles(new File_Archive_Predicate_Current());
395
    }
396
 
397
    /**
398
     * Removes the current file from the reader
399
     */
400
    function remove()
401
    {
402
        $writer = $this->makeWriterRemove();
403
        if (PEAR::isError($writer)) {
404
            return $writer;
405
        }
406
        $writer->close();
407
    }
408
 
409
    /**
410
     * Return a writer that has the same properties as the one returned by makeWriter, but after
411
     * having removed a block of data from the current file. The writer will append data to the current file
412
     * no data (other than the block) will be removed
413
     *
414
     * @param array Lengths of the blocks. The first one will be discarded, the second one kept, the third
415
     *        one discarded... If the sum of the blocks is less than the size of the file, the comportment is the
416
     *        same as if a last block was set in the array to reach the size of the file
417
     *        if $length is -1, the file is truncated from the specified pos
418
     *        It is possible to specify blocks of size 0
419
     * @param int $seek relative pos of the block
420
     */
421
    function makeWriterRemoveBlocks($blocks, $seek = 0)
422
    {
423
        return PEAR::raiseError("Reader abstract function call (makeWriterRemoveBlocks)");
424
    }
425
}
426
 
427
?>