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
 * File::CSV
6
 *
7
 * PHP versions 4 and 5
8
 *
9
 * Copyright (c) 2002-2008,
10
 *  Tomas V.V.Cox <cox@idecnet.com>,
11
 *  Helgi Þormar Þorbjörnsson <helgi@php.net>
12
 * All rights reserved.
13
 *
14
 * Redistribution and use in source and binary forms, with or without
15
 * modification, are permitted provided that the following conditions are met:
16
 *
17
 *     * Redistributions of source code must retain the above copyright notice,
18
 *       this list of conditions and the following disclaimer.
19
 *     * Redistributions in binary form must reproduce the above copyright
20
 *       notice, this list of conditions and the following disclaimer in the
21
 *       documentation and/or other materials provided with the distribution.
22
 *
23
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
27
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
 *
34
 * @category    File
35
 * @package     File
36
 * @author      Tomas V.V.Cox <cox@idecnet.com>
37
 * @author      Helgi Þormar Þorbjörnsson <helgi@php.net>
38
 * @copyright   2004-2011 The Authors
39
 * @license     http://www.opensource.org/licenses/bsd-license.php New BSD License
40
 * @version     CVS: $Id: CSV.php 309245 2011-03-15 02:02:41Z dufuz $
41
 * @link        http://pear.php.net/package/File
42
 */
43
 
44
require_once 'PEAR.php';
45
require_once 'File.php';
46
 
47
/**
48
 * File class for handling CSV files (Comma Separated Values), a common format
49
 * for exchanging data.
50
 *
51
 * TODO:
52
 *  - Usage example and Doc
53
 *  - Use getPointer() in discoverFormat
54
 *  - Add a line counter for being able to output better error reports
55
 *  - Store the last error in GLOBALS and add File_CSV::getLastError()
56
 *
57
 * Wish:
58
 *  - Other methods like readAll(), writeAll(), numFields(), numRows()
59
 *  - Try to detect if a CSV has header or not in discoverFormat() (not possible with CSV)
60
 *
61
 * Known Bugs:
62
 * (they has been analyzed but for the moment the impact in the speed for
63
 *  properly handle this uncommon cases is too high and won't be supported)
64
 *  - A field which is composed only by a single quoted separator (ie -> ;";";)
65
 *    is not handled properly
66
 *  - When there is exactly one field minus than the expected number and there
67
 *    is a field with a separator inside, the parser will throw the "wrong count" error
68
 *
69
 * Info about CSV and links to other sources
70
 * http://rfc.net/rfc4180.html
71
 *
72
 * @author Tomas V.V.Cox <cox@idecnet.com>
73
 * @author Helgi Þormar Þorbjörnsson <helgi@php.net>
74
 * @package File
75
 * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
76
 */
77
class File_CSV
78
{
79
    /**
80
    * This raiseError method works in a different way. It will always return
81
    * false (an error occurred) but it will call PEAR::raiseError() before
82
    * it. If no default PEAR global handler is set, will trigger an error.
83
    *
84
    * @param string $error The error message
85
    * @return bool always false
86
    */
87
    function raiseError($error)
88
    {
89
        // If a default PEAR Error handler is not set trigger the error
90
        // XXX Add a PEAR::isSetHandler() method?
91
        if ($GLOBALS['_PEAR_default_error_mode'] == PEAR_ERROR_RETURN) {
92
            PEAR::raiseError($error, null, PEAR_ERROR_TRIGGER, E_USER_WARNING);
93
        } else {
94
            PEAR::raiseError($error);
95
        }
96
        return false;
97
    }
98
 
99
    /**
100
    * Checks the configuration given by the user
101
    *
102
    * @access private
103
    * @param string &$error The error will be written here if any
104
    * @param array  &$conf  The configuration assoc array
105
    * @return string error    Returns a error message
106
    */
107
    function _conf(&$error, &$conf)
108
    {
109
        // check conf
110
        if (!is_array($conf)) {
111
            return $error = 'Invalid configuration';
112
        }
113
 
114
        if (!isset($conf['fields']) || !(int)$conf['fields']) {
115
            return $error = 'The number of fields must be numeric (the "fields" key)';
116
        }
117
 
118
        if (isset($conf['sep'])) {
119
            if (strlen($conf['sep']) !== 1) {
120
                return $error = 'Separator can only be one char';
121
            }
122
        } elseif ($conf['fields'] > 1) {
123
            return $error = 'Missing separator (the "sep" key)';
124
        } else {
125
            // to avoid undefined index notices
126
            $conf['sep'] = ',';
127
        }
128
 
129
        if (isset($conf['quote'])) {
130
            if (strlen($conf['quote']) !== 1) {
131
                return $error = 'The quote char must be one char (the "quote" key)';
132
            }
133
        } else {
134
            $conf['quote'] = '"';
135
        }
136
 
137
        if (!isset($conf['crlf'])) {
138
            $conf['crlf'] = "\n";
139
        }
140
 
141
        if (!isset($conf['eol2unix'])) {
142
            $conf['eol2unix'] = true;
143
        }
144
    }
145
 
146
    /**
147
    * Return or create the file descriptor associated with a file
148
    *
149
    * @param string $file The name of the file
150
    * @param array  &$conf The configuration
151
    * @param string $mode The open node (ex: FILE_MODE_READ or FILE_MODE_WRITE)
152
    * @param boolean $reset if passed as true and resource for the file exists
153
    *                       than the file pointer will be moved to the beginning
154
    *
155
    * @return mixed A file resource or false
156
    */
157
    function getPointer($file, &$conf, $mode = FILE_MODE_READ, $reset = false)
158
    {
159
        static $resources = array();
160
        if (isset($resources[$file][$mode])) {
161
            if ($reset) {
162
                fseek($resources[$file][$mode], 0);
163
            }
164
 
165
            return $resources[$file][$mode];
166
        }
167
 
168
        File_CSV::_conf($error, $conf);
169
        if ($error) {
170
            return File_CSV::raiseError($error);
171
        }
172
 
173
        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
174
        $fp = File::_getFilePointer($file, $mode);
175
        PEAR::popErrorHandling();
176
        if (PEAR::isError($fp)) {
177
            return File_CSV::raiseError($fp);
178
        }
179
 
180
        $resources[$file][$mode] = $fp;
181
        return $fp;
182
    }
183
 
184
    /**
185
     * Unquote data
186
     *
187
     * @param array|string $data The data to unquote
188
     * @param string $quote The quote char
189
     * @return string the unquoted data
190
     */
191
    function unquote($data, $quote)
192
    {
193
        $isString = false;
194
        if (!is_array($data)) {
195
            $data = array($data);
196
            $isString = true;
197
        }
198
 
199
        // Get rid of escaped quotes
200
        $data = str_replace($quote.$quote, $quote, $data);
201
 
202
        $tmp = array();
203
        foreach ($data as $key => $field) {
204
            // Trim first the string.
205
            $field = trim($field);
206
 
207
            // Incase null fields (form: ;;)
208
            $field_len = strlen($field);
209
            if (!$field_len) {
210
                if ($isString) {
211
                    return $field;
212
                }
213
                $tmp[$key] = $field;
214
                continue;
215
            }
216
 
217
            // excel compat
218
            if ($field[0] === '=' && $field[1] === '"') {
219
                $field = str_replace('="', '"', $field);
220
                --$field_len;
221
            }
222
 
223
            if ($field[0] === $quote && $field[$field_len - 1] === $quote) {
224
                // Get rid of quotes around the field
225
                $field = substr($field, 1, -1);
226
            }
227
 
228
            if ($isString) {
229
                $tmp = $field;
230
            } else {
231
                $tmp[$key] = $field;
232
            }
233
        }
234
 
235
        return $tmp;
236
    }
237
 
238
    /**
239
    * Reads a row of data as an array from a CSV file. It's able to
240
    * read memo fields with multiline data.
241
    *
242
    * @param string $file   The filename where to write the data
243
    * @param array  &$conf   The configuration of the dest CSV
244
    *
245
    * @return mixed Array with the data read or false on error/no more data
246
    */
247
    function readQuoted($file, &$conf)
248
    {
249
        if (!$fp = File_CSV::getPointer($file, $conf, FILE_MODE_READ)) {
250
            return false;
251
        }
252
 
253
        $buff = $old = $prev = $c = '';
254
        $ret    = array();
255
        $fields = 1;
256
        $in_quote = false;
257
        $quote    = $conf['quote'];
258
        $f        = (int)$conf['fields'];
259
        $sep      = $conf['sep'];
260
        while (false !== $ch = fgetc($fp)) {
261
            $old  = $prev;
262
            $prev = $c;
263
            $c    = $ch;
264
 
265
            // Common case
266
            if ($c != $quote && $c != $sep && $c != "\n" && $c != "\r") {
267
                $buff .= $c;
268
                continue;
269
            }
270
 
271
            // Start quote.
272
            if (
273
                $in_quote === false &&
274
                $quote && $c == $quote &&
275
                (
276
                 $prev == $sep || $prev == "\n" || $prev === null ||
277
                 $prev == "\r" || $prev == '' || $prev == ' '
278
                 || $prev == '=' //excel compat
279
                )
280
            ) {
281
                $in_quote = true;
282
                // excel compat, removing the = part but only if we are in a quote
283
                if ($prev == '=') {
284
                    $buff{strlen($buff) - 1} = '';
285
                }
286
            }
287
 
288
            if ($in_quote) {
289
                // When does the quote end, make sure it's not double quoted
290
                if ($c == $sep && $prev == $quote && $old != $quote) {
291
                    $in_quote = false;
292
                } elseif ($c == $sep && $buff == $quote.$quote) {
293
                    // In case we are dealing with double quote but empty value
294
                    $in_quote = false;
295
                } elseif ($c == "\n" || $c == "\r") {
296
                    $sub = ($prev == "\r") ? 2 : 1;
297
                    $buff_len = strlen($buff);
298
                    if (
299
                        $buff_len >= $sub &&
300
                        $buff[$buff_len - $sub] == $quote
301
                    ) {
302
                        $in_quote = false;
303
                    }
304
                }
305
            }
306
 
307
            if (!$in_quote && ($c == $sep || $c == "\n" || $c == "\r")) {
308
                $return = File_CSV::_readQuotedFillers($fp, $f, $fields, $ret,
309
                                                       $buff, $quote, $c, $sep);
310
                if ($return !== false) {
311
                    return $return;
312
                }
313
 
314
                if ($prev == "\r") {
315
                    $buff = substr($buff, 0, -1);
316
                }
317
 
318
                // Convert EOL character to Unix EOL (LF).
319
                if ($conf['eol2unix']) {
320
                    $buff = preg_replace('/(\r\n|\r)$/', "\n", $buff);
321
                    // Below replaces things everywhere not just EOL
322
                    //$buff = str_replace(array("\r\n", "\r"), "\n", $buff);
323
                }
324
 
325
                $ret[] = File_CSV::unquote($buff, $quote);
326
                if (count($ret) === $f) {
327
                    return $ret;
328
                }
329
                $buff = '';
330
                ++$fields;
331
                continue;
332
            }
333
 
334
            $buff .= $c;
335
        }
336
 
337
        /* If it's the end of the file and we still have something in buffer
338
         * then we process it since files can have no CL/FR at the end
339
         */
340
        $feof = feof($fp);
341
        if ($feof && strlen($buff) > 0 && !in_array($buff, array("\r", "\n"))) {
342
            $ret[] = File_CSV::unquote($buff, $quote);
343
            if (count($ret) == $f) {
344
                return $ret;
345
            }
346
        }
347
 
348
        if ($feof && count($ret) !== $f) {
349
            $return = File_CSV::_readQuotedFillers($fp, $f, $fields, $ret,
350
                                                   $buff, $quote, $c, $sep);
351
            if ($return !== false) {
352
                return $return;
353
            }
354
        }
355
 
356
        return !$feof ? $ret : false;
357
    }
358
 
359
    /**
360
     * Adds missing fields (empty ones)
361
     *
362
     * @param resource $fp the file resource
363
     * @param string   $f
364
     * @param integer  $fields the field count
365
     * @param array    $ret    the processed fields in a array
366
     * @param string   $buff   the buffer before it gets put through unquote
367
     * @param string   $quote  Quote in use
368
     * @param string   $c      the char currently being worked with
369
     * @param string   $sep    Separator in use
370
     *
371
     * @access private
372
     * @return array | boolean returns false if no data should return out.
373
     */
374
    function _readQuotedFillers($fp, $f, $fields, $ret, $buff, $quote, &$c, $sep)
375
    {
376
        // More fields than expected
377
        if ($c == $sep && (count($ret) + 1) === $f) {
378
            // Seek the pointer into linebreak character.
379
            while (true) {
380
                $c = fgetc($fp);
381
                if  ($c == "\n" || $c == "\r" || $c == '') {
382
                    break;
383
                }
384
            }
385
 
386
            // Insert last field value.
387
            $ret[] = File_CSV::unquote($buff, $quote);
388
            return $ret;
389
        }
390
 
391
        // Less fields than expected
392
        if (($c == "\n" || $c == "\r") && $fields !== $f) {
393
            // Insert last field value.
394
            $ret[] = File_CSV::unquote($buff, $quote);
395
            if (count($ret) === 1 && empty($ret[0])) {
396
                return array();
397
            }
398
 
399
            // Pair the array elements to fields count. - inserting empty values
400
            $ret_count = count($ret);
401
            $sum = ($f - 1) - ($ret_count - 1);
402
            $data = array_merge($ret, array_fill($ret_count, $sum, ''));
403
            return $data;
404
        }
405
 
406
        return false;
407
    }
408
 
409
    /**
410
    * Reads a "row" from a CSV file and return it as an array
411
    *
412
    * @param string $file The CSV file
413
    * @param array  &$conf The configuration of the dest CSV
414
    *
415
    * @return mixed Array or false
416
    */
417
    function read($file, &$conf)
418
    {
419
        if (!$fp = File_CSV::getPointer($file, $conf, FILE_MODE_READ)) {
420
            return false;
421
        }
422
 
423
        // The size is limited to 4K
424
        if (!$line = fgets($fp, 4096)) {
425
            return false;
426
        }
427
 
428
        if ($conf['fields'] === 1) {
429
            $fields      = array($line);
430
            $field_count = 1;
431
        } else {
432
            $fields      = explode($conf['sep'], $line);
433
            $field_count = count($fields);
434
        }
435
 
436
        $real_field_count = $field_count - 1;
437
        $check_char = $fields[$real_field_count];
438
        if ($check_char === "\n" || $check_char === "\r") {
439
            array_pop($fields);
440
            --$field_count;
441
        }
442
 
443
        $last =& $fields[$real_field_count];
444
        if (
445
            $field_count !== $conf['fields'] || $conf['quote']
446
            && (
447
                $last !== ''
448
                && (
449
                    ($last[0] === $conf['quote'] && $last[strlen(rtrim($last)) - 1] !== $conf['quote'])
450
                    // excel support
451
                    || ($last[0] === '=' && $last[1] === $conf['quote'])
452
                    // if the row has spaces or other extra chars before the quote
453
                    //|| preg_match('|^\s+\\' . $conf['quote'] .'|', $last)
454
                )
455
            )
456
            // XXX perhaps there is a separator inside a quoted field
457
            // || preg_match("|{$conf['quote']}.*{$conf['sep']}.*{$conf['quote']}|", $line)
458
            // The regex above is really slow
459
             || ((count(explode(',', $line))) > $field_count)
460
        ) {
461
            fseek($fp, -1 * strlen($line), SEEK_CUR);
462
            $fields = File_CSV::readQuoted($file, $conf);
463
            $fields = File_CSV::_processHeaders($fields, $conf);
464
            return $fields;
465
        }
466
 
467
        $fields = File_CSV::unquote($fields, $conf['quote']);
468
 
469
        if ($field_count != $conf['fields']) {
470
            File_CSV::raiseError("Read wrong fields number count: '". $field_count .
471
                                  "' expected ".$conf['fields']);
472
            return true;
473
        }
474
 
475
        $fields = File_CSV::_processHeaders($fields, $conf);
476
        return $fields;
477
    }
478
 
479
    /**
480
     * Process the field array being passed in and map the array over to
481
     * the header values if the configuration is set on.
482
     *
483
     * @param array $fields The CSV row columns
484
     * @param array $conf File_CSV configuration
485
     *
486
     * @return array Processed array
487
     */
488
    function _processHeaders($fields, &$conf)
489
    {
490
        static $headers = array();
491
 
492
        if (isset($conf['header']) && $conf['header'] == true && empty($headers)) {
493
            // read the first row and assign to $headers
494
            $headers = $fields;
495
            return $headers;
496
        }
497
 
498
        if (!empty($headers)) {
499
            $tmp = array();
500
            foreach ($fields as $k => $v) {
501
                if (isset($headers[$k])) {
502
                    $tmp[$headers[$k]] = $v;
503
                }
504
            }
505
            $fields = $tmp;
506
        }
507
 
508
        return $fields;
509
    }
510
 
511
    /**
512
    * Internal use only, will be removed in the future
513
    *
514
    * @param string $str The string to debug
515
    * @access private
516
    */
517
    function _dbgBuff($str)
518
    {
519
        if (strpos($str, "\r") !== false) {
520
            $str = str_replace("\r", "_r_", $str);
521
        }
522
        if (strpos($str, "\n") !== false) {
523
            $str = str_replace("\n", "_n_", $str);
524
        }
525
        if (strpos($str, "\t") !== false) {
526
            $str = str_replace("\t", "_t_", $str);
527
        }
528
        if ($str === null) {
529
            $str = '_NULL_';
530
        }
531
        if ($str === '') {
532
            $str = 'Empty string';
533
        }
534
        echo "buff: ($str)\n";
535
    }
536
 
537
    /**
538
    * Writes a struc (array) in a file as CSV
539
    *
540
    * @param string $file   The filename where to write the data
541
    * @param array  $fields Ordered array with the data
542
    * @param array  &$conf   The configuration of the dest CSV
543
    *
544
    * @return bool True on success false otherwise
545
    */
546
    function write($file, $fields, &$conf)
547
    {
548
        if (!$fp = File_CSV::getPointer($file, $conf, FILE_MODE_WRITE)) {
549
            return false;
550
        }
551
 
552
        $field_count = count($fields);
553
        if ($field_count != $conf['fields']) {
554
            File_CSV::raiseError("Wrong fields number count: '". $field_count .
555
                                  "' expected ".$conf['fields']);
556
            return true;
557
        }
558
 
559
        $write = '';
560
        $quote = $conf['quote'];
561
        for ($i = 0; $i < $field_count; ++$i) {
562
            // Write a single field
563
 
564
            $quote_field = false;
565
            // Only quote this field in the following cases:
566
            if (is_numeric($fields[$i])) {
567
                // Numeric fields should not be quoted
568
            } elseif (isset($conf['sep']) && (strpos($fields[$i], $conf['sep']) !== false)) {
569
                // Separator is present in field
570
                $quote_field = true;
571
            } elseif (strpos($fields[$i], $quote) !== false) {
572
                // Quote character is present in field
573
                $quote_field = true;
574
            } elseif (
575
                   strpos($fields[$i], "\n") !== false
576
                || strpos($fields[$i], "\r") !== false
577
            ) {
578
                // Newline is present in field
579
                $quote_field = true;
580
            } elseif (!is_numeric($fields[$i]) && (substr($fields[$i], 0, 1) == " " || substr($fields[$i], -1) == " ")) {
581
                // Space found at beginning or end of field value
582
                $quote_field = true;
583
            }
584
 
585
            if ($quote_field) {
586
                // Escape the quote character within the field (e.g. " becomes "")
587
                $quoted_value = str_replace($quote, $quote.$quote, $fields[$i]);
588
 
589
                $write .= $quote . $quoted_value . $quote;
590
            } else {
591
                $write .= $fields[$i];
592
            }
593
 
594
            $write .= ($i < ($field_count - 1)) ? $conf['sep']: $conf['crlf'];
595
        }
596
 
597
        if (!fwrite($fp, $write, strlen($write))) {
598
            return File_CSV::raiseError('Can not write to file');
599
        }
600
 
601
        return true;
602
    }
603
 
604
    /**
605
    * Discover the format of a CSV file (the number of fields, the separator
606
    * and if it quote string fields)
607
    *
608
    * @param string the CSV file name
609
    * @param array extra separators that should be checked for.
610
    * @return mixed Assoc array or false
611
    */
612
    function discoverFormat($file, $extraSeps = array())
613
    {
614
        if (!$fp = @fopen($file, 'rb')) {
615
            return File_CSV::raiseError("Could not open file: $file");
616
        }
617
 
618
        // Set auto detect line ending for Mac EOL support
619
        $oldini = ini_get('auto_detect_line_endings');
620
        if ($oldini != '1') {
621
            ini_set('auto_detect_line_endings', '1');
622
        }
623
 
624
        // Take the first 30 lines and store the number of occurrences
625
        // for each separator in each line
626
        $lines = '';
627
        for ($i = 0; $i < 30 && !feof($fp) && $line = fgets($fp, 4096); $i++) {
628
            $lines .= $line;
629
        }
630
        fclose($fp);
631
 
632
        if ($oldini != '1') {
633
            ini_set('auto_detect_line_endings', $oldini);
634
        }
635
 
636
        $seps = array("\t", ';', ':', ',');
637
        $seps = array_merge($seps, $extraSeps);
638
        $matches = array();
639
        $quotes = '"\'';
640
 
641
        while ($lines != ($newLines = preg_replace('|((["\'])[^"]*(\2))|', '\2_\2', $lines))) {
642
            $lines = $newLines;
643
        }
644
 
645
        $eol   = strpos($lines, "\r") ? "\r" : "\n";
646
        $lines = explode($eol, $lines);
647
        foreach ($lines as $line) {
648
            $orgLine = $line;
649
            foreach ($seps as $sep) {
650
                $line = preg_replace("|^[$quotes$sep]*$sep*([$quotes][^$quotes]*[$quotes])|sm", '_', $orgLine);
651
                // Find all seps that are within qoutes
652
                ///FIXME ... counts legitimit lines as bad ones
653
 
654
                 // In case there's a whitespace infront the field
655
                $regex = '|\s*?';
656
                 // Match the first quote (optional), also optionally match = since it's excel stuff
657
                $regex.= "(?:\=?[$quotes])";
658
                $regex.= '(.*';
659
                // Don't match a sep if we are inside a quote
660
                // also don't accept the sep if it has a quote on the either side
661
                ///FIXME has to be possible if we are inside a quote! (tests fail because of this)
662
                $regex.= "(?:[^$quotes])$sep(?:[^$quotes])";
663
                $regex.= '.*)';
664
                // Close quote (if it's present) and the sep (optional, could be end of line)
665
                $regex.= "(?:[$quotes](?:$sep?))|Ums";
666
 
667
                preg_match_all($regex, $line, $match);
668
                // Finding all seps, within quotes or not
669
                $sep_count = substr_count($line, $sep);
670
                // Real count
671
                $matches[$sep][] = $sep_count - count($match[0]);
672
            }
673
        }
674
 
675
        $final = array();
676
        // Group the results by amount of equal ocurrences
677
        foreach ($matches as $sep => $res) {
678
            $times = array();
679
            $times[0] = 0;
680
            foreach ($res as $k => $num) {
681
                if ($num > 0) {
682
                    $times[$num] = isset($times[$num]) ? $times[$num] + $num : 1;
683
                }
684
            }
685
            arsort($times);
686
 
687
            // Use max fields count.
688
            $fields[$sep] = max(array_flip($times));
689
            $amount[$sep] = $times[key($times)];
690
        }
691
 
692
        arsort($amount);
693
        $sep = key($amount);
694
 
695
        $conf['fields'] = $fields[$sep] + 1;
696
        $conf['sep']    = $sep;
697
 
698
        // Test if there are fields with quotes around in the first 30 lines
699
        $quote  = null;
700
 
701
        $string = implode('', $lines);
702
        foreach (array('"', '\'') as $q) {
703
            if (preg_match_all("|$sep(?:\s*?)(\=?[$q]).*([$q])$sep?|Us", $string, $match)) {
704
                if ($match[1][0] == $match[2][0]) {
705
                    $quote = $match[1][0];
706
                    break;
707
                }
708
            }
709
 
710
            if (
711
                preg_match_all("|^(\=?[$q]).*([$q])$sep{0,1}|Ums", $string, $match)
712
                || preg_match_all("|(\=?[$q]).*([$q])$sep\s$|Ums", $string, $match)
713
            ) {
714
                if ($match[1][0] == $match[2][0]) {
715
                    $quote = $match[1][0];
716
                    break;
717
                }
718
            }
719
        }
720
 
721
        $conf['quote'] = $quote;
722
        return $conf;
723
    }
724
 
725
    /**
726
     * Front to call getPointer and moving the resource to the
727
     * beginning of the file
728
     * Reset it if you like.
729
     *
730
     * @param string $file The name of the file
731
     * @param array  &$conf The configuration
732
     * @param string $mode The open node (ex: FILE_MODE_READ or FILE_MODE_WRITE)
733
     *
734
     * @return boolean true on success false on failure
735
     */
736
    function resetPointer($file, &$conf, $mode)
737
    {
738
        if (!File_CSV::getPointer($file, $conf, $mode, true)) {
739
            return false;
740
        }
741
 
742
        return true;
743
    }
744
}