PluginProbe ʕ •ᴥ•ʔ
WP All Import – Drag & Drop Import for CSV, XML, Excel & Google Sheets / 2.13
WP All Import – Drag & Drop Import for CSV, XML, Excel & Google Sheets v2.13
4.1.1 3.9.5 3.9.6 4.0.0 4.0.1 4.1.0 trunk 2.12 2.13 2.14 3.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.2.0 3.2.1 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.2.7 3.2.8 3.2.9 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.3.8 3.3.9 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.6 3.4.7 3.4.8 3.4.9 3.5.0 3.5.1 3.5.2 3.5.3 3.5.4 3.5.5 3.5.6 3.5.7 3.5.8 3.5.9 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.6.8 3.6.9 3.7.0 3.7.1 3.7.2 3.7.3 3.7.3-beta-1.0 3.7.4 3.7.4-beta-1.0 3.7.5 3.7.6 3.7.7 3.7.8 3.7.9 3.8.0 3.9.0 3.9.1 3.9.2 3.9.3 3.9.4
wp-all-import / libraries / XmlImportCsvParse.php
wp-all-import / libraries Last commit date
ast 13 years ago cache 13 years ago XmlImportConfig.php 13 years ago XmlImportCsvParse.php 13 years ago XmlImportException.php 13 years ago XmlImportParser.php 13 years ago XmlImportReaderInterface.php 13 years ago XmlImportStringReader.php 13 years ago XmlImportTemplate.php 13 years ago XmlImportTemplateCodeGenerator.php 13 years ago XmlImportTemplateParser.php 13 years ago XmlImportTemplateScanner.php 13 years ago XmlImportToken.php 13 years ago
XmlImportCsvParse.php
2428 lines
1 <?php
2
3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5 /**
6 * contains a few csv file data access tools.
7 *
8 * PHP VERSION 5
9 *
10 * LICENSE: The MIT License
11 *
12 * Copyright (c) <2008> <Kazuyoshi Tlacaelel>
13 *
14 * Permission is hereby granted, free of charge, to any person obtaining a copy
15 * of this software and associated documentation files (the "Software"), to deal
16 * in the Software without restriction, including without limitation the rights
17 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18 * copies of the Software, and to permit persons to whom the Software is
19 * furnished to do so, subject to the following conditions:
20 *
21 * The above copyright notice and this permission notice shall be included in
22 * all copies or substantial portions of the Software.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
30 * THE SOFTWARE.
31 *
32 * @category File
33 * @package File_CSV_DataSource
34 * @author Kazuyoshi Tlacaelel <kazu.dev@gmail.com>
35 * @copyright 2008 Kazuyoshi Tlacaelel
36 * @license The MIT License
37 * @version SVN: $Id: DataSource.php 285574 2009-03-09 15:22:24Z ktlacaelel $
38 * @link http://code.google.com/p/php-csv-parser/
39 */
40
41 /**
42 * csv data fetcher
43 *
44 * Sample snippets refer to this csv file for demonstration.
45 * <code>
46 * name,age,skill
47 * john,13,knows magic
48 * tanaka,8,makes sushi
49 * jose,5,dances salsa
50 * </code>
51 *
52 * @category File
53 * @package Csv_parse
54 * @author Kazuyoshi Tlacaelel <kazu.dev@gmail.com>
55 * @copyright 2008 Kazuyoshi Tlacaelel
56 * @license The MIT License
57 * @link http://code.google.com/p/php-csv-parser/
58 */
59 class PMXI_CsvParser
60 {
61 public
62
63 /**
64 * csv parsing default-settings
65 *
66 * @var array
67 * @access public
68 */
69 $settings = array(
70 'delimiter' => ',',
71 'eol' => '',
72 'length' => 999999,
73 'escape' => '"'
74 );
75
76 protected
77
78 /**
79 * imported data from csv
80 *
81 * @var array
82 * @access protected
83 */
84 $rows = array(),
85
86 /**
87 * csv file to parse
88 *
89 * @var string
90 * @access protected
91 */
92 $_filename = '',
93
94 /**
95 * csv headers to parse
96 *
97 * @var array
98 * @access protected
99 */
100 $headers = array();
101
102 /**
103 * data load initialize
104 *
105 * @param mixed $filename please look at the load() method
106 *
107 * @access public
108 * @see load()
109 * @return void
110 */
111 public function __construct($filename = null)
112 {
113 ini_set('auto_detect_line_endings', true);
114
115 $file_params = self::analyse_file($filename, 10);
116
117 $this->set_settings(array('delimiter' => $file_params['delimiter']['value'], 'eol' => $file_params['line_ending']['value']));
118
119 unset($file_params);
120
121 $this->load($filename);
122 }
123
124 /**
125 * csv file loader
126 *
127 * indicates the object which file is to be loaded
128 *
129 * <code>
130 *
131 * require_once 'File/CSV/DataSource.php';
132 *
133 * $csv = new File_CSV_DataSource;
134 * $csv->load('my_cool.csv');
135 * var_export($csv->connect());
136 *
137 * array (
138 * 0 =>
139 * array (
140 * 'name' => 'john',
141 * 'age' => '13',
142 * 'skill' => 'knows magic',
143 * ),
144 * 1 =>
145 * array (
146 * 'name' => 'tanaka',
147 * 'age' => '8',
148 * 'skill' => 'makes sushi',
149 * ),
150 * 2 =>
151 * array (
152 * 'name' => 'jose',
153 * 'age' => '5',
154 * 'skill' => 'dances salsa',
155 * ),
156 * )
157 *
158 * </code>
159 *
160 * @param string $filename the csv filename to load
161 *
162 * @access public
163 * @return boolean true if file was loaded successfully
164 * @see isSymmetric(), getAsymmetricRows(), symmetrize()
165 */
166 public function load($filename)
167 {
168 $this->_filename = $filename;
169 $this->flush();
170 return $this->parse();
171 }
172
173 /**
174 * settings alterator
175 *
176 * lets you define different settings for scanning
177 *
178 * Given array will override the internal settings
179 *
180 * <code>
181 * $settings = array(
182 * 'delimiter' => ',',
183 * 'eol' => ";",
184 * 'length' => 999999,
185 * 'escape' => '"'
186 * );
187 * </code>
188 *
189 * @param mixed $array containing settings to use
190 *
191 * @access public
192 * @return boolean true if changes where applyed successfully
193 * @see $settings
194 */
195 public function set_settings($array)
196 {
197 $this->settings = array_merge($this->settings, $array);
198 }
199
200 /**
201 * header fetcher
202 *
203 * gets csv headers into an array
204 *
205 * <code>
206 *
207 * var_export($csv->getHeaders());
208 *
209 * array (
210 * 0 => 'name',
211 * 1 => 'age',
212 * 2 => 'skill',
213 * )
214 *
215 * </code>
216 *
217 * @access public
218 * @return array
219 */
220 public function getHeaders()
221 {
222 return $this->headers;
223 }
224
225 /**
226 * header counter
227 *
228 * retrives the total number of loaded headers
229 *
230 * @access public
231 * @return integer gets the length of headers
232 */
233 public function countHeaders()
234 {
235 return count($this->headers);
236 }
237
238 /**
239 * header and row relationship builder
240 *
241 * Attempts to create a relationship for every single cell that
242 * was captured and its corresponding header. The sample below shows
243 * how a connection/relationship is built.
244 *
245 * sample of a csv file "my_cool.csv"
246 *
247 * <code>
248 * name,age,skill
249 * john,13,knows magic
250 * tanaka,8,makes sushi
251 * jose,5,dances salsa
252 * </code>
253 *
254 * php implementation
255 *
256 * <code>
257 *
258 * $csv = new File_CSV_DataSource;
259 * $csv->load('my_cool.csv');
260 *
261 * if (!$csv->isSymmetric()) {
262 * die('file has headers and rows with different lengths
263 * cannot connect');
264 * }
265 *
266 * var_export($csv->connect());
267 *
268 * array (
269 * 0 =>
270 * array (
271 * 'name' => 'john',
272 * 'age' => '13',
273 * 'skill' => 'knows magic',
274 * ),
275 * 1 =>
276 * array (
277 * 'name' => 'tanaka',
278 * 'age' => '8',
279 * 'skill' => 'makes sushi',
280 * ),
281 * 2 =>
282 * array (
283 * 'name' => 'jose',
284 * 'age' => '5',
285 * 'skill' => 'dances salsa',
286 * ),
287 * )
288 *
289 * </code>
290 *
291 *
292 * You can pass a collection of headers in an array to build
293 * a connection for those columns only!
294 *
295 * <code>
296 *
297 * var_export($csv->connect(array('age')));
298 *
299 * array (
300 * 0 =>
301 * array (
302 * 'age' => '13',
303 * ),
304 * 1 =>
305 * array (
306 * 'age' => '8',
307 * ),
308 * 2 =>
309 * array (
310 * 'age' => '5',
311 * ),
312 * )
313 *
314 * </code>
315 *
316 * @param array $columns the columns to connect, if nothing
317 * is given all headers will be used to create a connection
318 *
319 * @access public
320 * @return array If the data is not symmetric an empty array
321 * will be returned instead
322 * @see isSymmetric(), getAsymmetricRows(), symmetrize(), getHeaders()
323 */
324 public function connect($columns = array())
325 {
326 if (!$this->isSymmetric()) {
327 return array();
328 }
329 if (!is_array($columns)) {
330 return array();
331 }
332 if ($columns === array()) {
333 $columns = $this->headers;
334 }
335
336 $ret_arr = array();
337
338 foreach ($this->rows as $record) {
339 $item_array = array();
340 foreach ($record as $column => $value) {
341 $header = $this->headers[$column];
342 if (in_array($header, $columns)) {
343 $item_array[$header] = $value;
344 }
345 }
346
347 // do not append empty results
348 if ($item_array !== array()) {
349 array_push($ret_arr, $item_array);
350 }
351 }
352
353 return $ret_arr;
354 }
355
356 /**
357 * data length/symmetry checker
358 *
359 * tells if the headers and all of the contents length match.
360 * Note: there is a lot of methods that won't work if data is not
361 * symmetric this method is very important!
362 *
363 * @access public
364 * @return boolean
365 * @see symmetrize(), getAsymmetricRows(), isSymmetric()
366 */
367 public function isSymmetric()
368 {
369 $hc = count($this->headers);
370 foreach ($this->rows as $row) {
371 if (count($row) != $hc) {
372 return false;
373 }
374 }
375 return true;
376 }
377
378 /**
379 * asymmetric data fetcher
380 *
381 * finds the rows that do not match the headers length
382 *
383 * lets assume that we add one more row to our csv file.
384 * that has only two values. Something like
385 *
386 * <code>
387 * name,age,skill
388 * john,13,knows magic
389 * tanaka,8,makes sushi
390 * jose,5,dances salsa
391 * niki,6
392 * </code>
393 *
394 * Then in our php code
395 *
396 * <code>
397 * $csv->load('my_cool.csv');
398 * var_export($csv->getAsymmetricRows());
399 * </code>
400 *
401 * The result
402 *
403 * <code>
404 *
405 * array (
406 * 0 =>
407 * array (
408 * 0 => 'niki',
409 * 1 => '6',
410 * ),
411 * )
412 *
413 * </code>
414 *
415 * @access public
416 * @return array filled with rows that do not match headers
417 * @see getHeaders(), symmetrize(), isSymmetric(),
418 * getAsymmetricRows()
419 */
420 public function getAsymmetricRows()
421 {
422 $ret_arr = array();
423 $hc = count($this->headers);
424 foreach ($this->rows as $row) {
425 if (count($row) != $hc) {
426 $ret_arr[] = $row;
427 }
428 }
429 return $ret_arr;
430 }
431
432 /**
433 * all rows length equalizer
434 *
435 * makes the length of all rows and headers the same. If no $value is given
436 * all unexistent cells will be filled with empty spaces
437 *
438 * @param mixed $value the value to fill the unexistent cells
439 *
440 * @access public
441 * @return array
442 * @see isSymmetric(), getAsymmetricRows(), symmetrize()
443 */
444 public function symmetrize($value = '')
445 {
446 $max_length = 0;
447 $headers_length = count($this->headers);
448
449 foreach ($this->rows as $row) {
450 $row_length = count($row);
451 if ($max_length < $row_length) {
452 $max_length = $row_length;
453 }
454 }
455
456 if ($max_length < $headers_length) {
457 $max_length = $headers_length;
458 }
459
460 foreach ($this->rows as $key => $row) {
461 $this->rows[$key] = array_pad($row, $max_length, $value);
462 }
463
464 $this->headers = array_pad($this->headers, $max_length, $value);
465 }
466
467 /**
468 * grid walker
469 *
470 * travels through the whole dataset executing a callback per each
471 * cell
472 *
473 * Note: callback functions get the value of the cell as an
474 * argument, and whatever that callback returns will be used to
475 * replace the current value of that cell.
476 *
477 * @param string $callback the callback function to be called per
478 * each cell in the dataset.
479 *
480 * @access public
481 * @return void
482 * @see walkColumn(), walkRow(), fillColumn(), fillRow(), fillCell()
483 */
484 public function walkGrid($callback)
485 {
486 foreach (array_keys($this->getRows()) as $key) {
487 if (!$this->walkRow($key, $callback)) {
488 return false;
489 }
490 }
491 return true;
492 }
493
494 /**
495 * column fetcher
496 *
497 * gets all the data for a specific column identified by $name
498 *
499 * Note $name is the same as the items returned by getHeaders()
500 *
501 * sample of a csv file "my_cool.csv"
502 *
503 * <code>
504 * name,age,skill
505 * john,13,knows magic
506 * tanaka,8,makes sushi
507 * jose,5,dances salsa
508 * </code>
509 *
510 * php implementation
511 *
512 * <code>
513 * $csv = new File_CSV_DataSource;
514 * $csv->load('my_cool.csv');
515 * var_export($csv->getColumn('name'));
516 * </code>
517 *
518 * the above example outputs something like
519 *
520 * <code>
521 *
522 * array (
523 * 0 => 'john',
524 * 1 => 'tanaka',
525 * 2 => 'jose',
526 * )
527 *
528 * </code>
529 *
530 * @param string $name the name of the column to fetch
531 *
532 * @access public
533 * @return array filled with values of a column
534 * @see getHeaders(), fillColumn(), appendColumn(), getCell(), getRows(),
535 * getRow(), hasColumn()
536 */
537 public function getColumn($name)
538 {
539 if (!in_array($name, $this->headers)) {
540 return array();
541 }
542 $ret_arr = array();
543 $key = array_search($name, $this->headers, true);
544 foreach ($this->rows as $data) {
545 $ret_arr[] = $data[$key];
546 }
547 return $ret_arr;
548 }
549
550 /**
551 * column existance checker
552 *
553 * checks if a column exists, columns are identified by their
554 * header name.
555 *
556 * sample of a csv file "my_cool.csv"
557 *
558 * <code>
559 * name,age,skill
560 * john,13,knows magic
561 * tanaka,8,makes sushi
562 * jose,5,dances salsa
563 * </code>
564 *
565 * php implementation
566 *
567 * <code>
568 * $csv = new File_CSV_DataSource;
569 * $csv->load('my_cool.csv');
570 * $headers = $csv->getHeaders();
571 * </code>
572 *
573 * now lets check if the columns exist
574 *
575 * <code>
576 * var_export($csv->hasColumn($headers[0])); // true
577 * var_export($csv->hasColumn('age')); // true
578 * var_export($csv->hasColumn('I dont exist')); // false
579 * </code>
580 *
581 * @param string $string an item returned by getHeaders()
582 *
583 * @access public
584 * @return boolean
585 * @see getHeaders()
586 */
587 public function hasColumn($string)
588 {
589 return in_array($string, $this->headers);
590 }
591
592 /**
593 * column appender
594 *
595 * Appends a column and each or all values in it can be
596 * dinamically filled. Only when the $values argument is given.
597 * <code>
598 *
599 *
600 * var_export($csv->fillColumn('age', 99));
601 * true
602 *
603 * var_export($csv->appendColumn('candy_ownership', array(99, 44, 65)));
604 * true
605 *
606 * var_export($csv->appendColumn('import_id', 111111111));
607 * true
608 *
609 * var_export($csv->connect());
610 *
611 * array (
612 * 0 =>
613 * array (
614 * 'name' => 'john',
615 * 'age' => 99,
616 * 'skill' => 'knows magic',
617 * 'candy_ownership' => 99,
618 * 'import_id' => 111111111,
619 * ),
620 * 1 =>
621 * array (
622 * 'name' => 'tanaka',
623 * 'age' => 99,
624 * 'skill' => 'makes sushi',
625 * 'candy_ownership' => 44,
626 * 'import_id' => 111111111,
627 * ),
628 * 2 =>
629 * array (
630 * 'name' => 'jose',
631 * 'age' => 99,
632 * 'skill' => 'dances salsa',
633 * 'candy_ownership' => 65,
634 * 'import_id' => 111111111,
635 * ),
636 * )
637 *
638 * </code>
639 *
640 * @param string $column an item returned by getHeaders()
641 * @param mixed $values same as fillColumn()
642 *
643 * @access public
644 * @return boolean
645 * @see getHeaders(), fillColumn(), fillCell(), createHeaders(),
646 * setHeaders()
647 */
648 public function appendColumn($column, $values = null)
649 {
650 if ($this->hasColumn($column)) {
651 return false;
652 }
653 $this->headers[] = $column;
654 $length = $this->countHeaders();
655 $rows = array();
656
657 foreach ($this->rows as $row) {
658 $rows[] = array_pad($row, $length, '');
659 }
660
661 $this->rows = $rows;
662
663 if ($values === null) {
664 $values = '';
665 }
666
667 return $this->fillColumn($column, $values);
668 }
669
670 /**
671 * collumn data injector
672 *
673 * fills alll the data in the given column with $values
674 *
675 * sample of a csv file "my_cool.csv"
676 *
677 * <code>
678 * name,age,skill
679 * john,13,knows magic
680 * tanaka,8,makes sushi
681 * jose,5,dances salsa
682 * </code>
683 *
684 * php implementation
685 *
686 * <code>
687 * $csv = new File_CSV_DataSource;
688 * $csv->load('my_cool.csv');
689 *
690 * // if the csv file loads
691 * if ($csv->load('my_cool.csv')) {
692 *
693 * // grab all data within the age column
694 * var_export($csv->getColumn('age'));
695 *
696 * // rename all values in it with the number 99
697 * var_export($csv->fillColumn('age', 99));
698 *
699 * // grab all data within the age column
700 * var_export($csv->getColumn('age'));
701 *
702 * // rename each value in a column independently
703 * $data = array(1, 2, 3);
704 * $csv->fillColumn('age', $data);
705 *
706 * var_export($csv->getColumn('age'));
707 * }
708 * </code>
709 *
710 * standard output
711 *
712 * <code>
713 * array (
714 * 0 => '13',
715 * 1 => '8',
716 * 2 => '5',
717 * )
718 * </code>
719 *
720 * <code>
721 * true
722 * </code>
723 *
724 * <code>
725 * array (
726 * 0 => 99,
727 * 1 => 99,
728 * 2 => 99,
729 * )
730 * </code>
731 *
732 * <code>
733 * array (
734 * 0 => 1,
735 * 1 => 2,
736 * 2 => 3,
737 * )
738 * </code>
739 *
740 * @param mixed $column the column identified by a string
741 * @param mixed $values ither one of the following
742 * - (Number) will fill the whole column with the value of number
743 * - (String) will fill the whole column with the value of string
744 * - (Array) will fill the while column with the values of array
745 * the array gets ignored if it does not match the length of rows
746 *
747 * @access public
748 * @return void
749 */
750 public function fillColumn($column, $values = null)
751 {
752 if (!$this->hasColumn($column)) {
753 return false;
754 }
755
756 if ($values === null) {
757 return false;
758 }
759
760 if (!$this->isSymmetric()) {
761 return false;
762 }
763
764 $y = array_search($column, $this->headers);
765
766 if (is_numeric($values) || is_string($values)) {
767 foreach (range(0, $this->countRows() -1) as $x) {
768 $this->fillCell($x, $y, $values);
769 }
770 return true;
771 }
772
773 if ($values === array()) {
774 return false;
775 }
776
777 $length = $this->countRows();
778 if (is_array($values) && $length == count($values)) {
779 for ($x = 0; $x < $length; $x++) {
780 $this->fillCell($x, $y, $values[$x]);
781 }
782 return true;
783 }
784
785 return false;
786 }
787
788 /**
789 * column remover
790 *
791 * Completly removes a whole column identified by $name
792 * Note: that this function will only work if data is symmetric.
793 *
794 * sample of a csv file "my_cool.csv"
795 *
796 * <code>
797 * name,age,skill
798 * john,13,knows magic
799 * tanaka,8,makes sushi
800 * jose,5,dances salsa
801 * </code>
802 *
803 * load the library and csv file
804 *
805 * <code>
806 * require_once 'File/CSV/DataSource.php';
807 * $csv = new File_CSV_DataSource;
808 * $csv->load('my_cool.csv');
809 * </code>
810 *
811 * lets dump currently loaded data
812 * <code>
813 * var_export($csv->connect());
814 * </code>
815 *
816 * output
817 *
818 * <code>
819 * array (
820 * 0 =>
821 * array (
822 * 'name' => 'john',
823 * 'age' => '13',
824 * 'skill' => 'knows magic',
825 * ),
826 * 1 =>
827 * array (
828 * 'name' => 'tanaka',
829 * 'age' => '8',
830 * 'skill' => 'makes sushi',
831 * ),
832 * 2 =>
833 * array (
834 * 'name' => 'jose',
835 * 'age' => '5',
836 * 'skill' => 'dances salsa',
837 * ),
838 * )
839 * </code>
840 *
841 * and now let's remove the second column
842 *
843 * <code>
844 * var_export($csv->removeColumn('age'));
845 * </code>
846 *
847 * output
848 *
849 * <code>
850 * true
851 * </code>
852 *
853 * those changes made let's dump the data again and see what we got
854 *
855 * <code>
856 * array (
857 * 0 =>
858 * array (
859 * 'name' => 'john',
860 * 'skill' => 'knows magic',
861 * ),
862 * 1 =>
863 * array (
864 * 'name' => 'tanaka',
865 * 'skill' => 'makes sushi',
866 * ),
867 * 2 =>
868 * array (
869 * 'name' => 'jose',
870 * 'skill' => 'dances salsa',
871 * ),
872 * )
873 * </code>
874 *
875 * @param string $name same as the ones returned by getHeaders();
876 *
877 * @access public
878 * @return boolean
879 * @see hasColumn(), getHeaders(), createHeaders(), setHeaders(),
880 * isSymmetric(), getAsymmetricRows()
881 */
882 public function removeColumn($name)
883 {
884 if (!in_array($name, $this->headers)) {
885 return false;
886 }
887
888 if (!$this->isSymmetric()) {
889 return false;
890 }
891
892 $key = array_search($name, $this->headers);
893 unset($this->headers[$key]);
894 $this->resetKeys($this->headers);
895
896 foreach ($this->rows as $target => $row) {
897 unset($this->rows[$target][$key]);
898 $this->resetKeys($this->rows[$target]);
899 }
900
901 return $this->isSymmetric();
902 }
903
904 /**
905 * column walker
906 *
907 * goes through the whole column and executes a callback for each
908 * one of the cells in it.
909 *
910 * Note: callback functions get the value of the cell as an
911 * argument, and whatever that callback returns will be used to
912 * replace the current value of that cell.
913 *
914 * @param string $name the header name used to identify the column
915 * @param string $callback the callback function to be called per
916 * each cell value
917 *
918 * @access public
919 * @return boolean
920 * @see getHeaders(), fillColumn(), appendColumn()
921 */
922 public function walkColumn($name, $callback)
923 {
924 if (!$this->isSymmetric()) {
925 return false;
926 }
927
928 if (!$this->hasColumn($name)) {
929 return false;
930 }
931
932 if (!function_exists($callback)) {
933 return false;
934 }
935
936 $column = $this->getColumn($name);
937 foreach ($column as $key => $cell) {
938 $column[$key] = $callback($cell);
939 }
940 return $this->fillColumn($name, $column);
941 }
942
943 /**
944 * cell fetcher
945 *
946 * gets the value of a specific cell by given coordinates
947 *
948 * Note: That indexes start with zero, and headers are not
949 * searched!
950 *
951 * For example if we are trying to grab the cell that is in the
952 * second row and the third column
953 *
954 * <code>
955 * name,age,skill
956 * john,13,knows magic
957 * tanaka,8,makes sushi
958 * jose,5,dances salsa
959 * </code>
960 *
961 * we would do something like
962 * <code>
963 * var_export($csv->getCell(1, 2));
964 * </code>
965 *
966 * and get the following results
967 * <code>
968 * 'makes sushi'
969 * </code>
970 *
971 * @param integer $x the row to fetch
972 * @param integer $y the column to fetch
973 *
974 * @access public
975 * @return mixed|false the value of the cell or false if the cell does
976 * not exist
977 * @see getHeaders(), hasCell(), getRow(), getRows(), getColumn()
978 */
979 public function getCell($x, $y)
980 {
981 if ($this->hasCell($x, $y)) {
982 $row = $this->getRow($x);
983 return $row[$y];
984 }
985 return false;
986 }
987
988 /**
989 * cell value filler
990 *
991 * replaces the value of a specific cell
992 *
993 * sample of a csv file "my_cool.csv"
994 *
995 * <code>
996 * name,age,skill
997 * john,13,knows magic
998 * tanaka,8,makes sushi
999 * jose,5,dances salsa
1000 * </code>
1001 *
1002 * php implementation
1003 *
1004 * <code>
1005 *
1006 * $csv = new File_CSV_DataSource;
1007 *
1008 * // load the csv file
1009 * $csv->load('my_cool.csv');
1010 *
1011 * // find out if the given coordinate is valid
1012 * if($csv->hasCell(1, 1)) {
1013 *
1014 * // if so grab that cell and dump it
1015 * var_export($csv->getCell(1, 1)); // '8'
1016 *
1017 * // replace the value of that cell
1018 * $csv->fillCell(1, 1, 'new value'); // true
1019 *
1020 * // output the new value of the cell
1021 * var_export($csv->getCell(1, 1)); // 'new value'
1022 *
1023 * }
1024 * </code>
1025 *
1026 * now lets try to grab the whole row
1027 *
1028 * <code>
1029 * // show the whole row
1030 * var_export($csv->getRow(1));
1031 * </code>
1032 *
1033 * standard output
1034 *
1035 * <code>
1036 * array (
1037 * 0 => 'tanaka',
1038 * 1 => 'new value',
1039 * 2 => 'makes sushi',
1040 * )
1041 * </code>
1042 *
1043 * @param integer $x the row to fetch
1044 * @param integer $y the column to fetch
1045 * @param mixed $value the value to fill the cell with
1046 *
1047 * @access public
1048 * @return boolean
1049 * @see hasCell(), getRow(), getRows(), getColumn()
1050 */
1051 public function fillCell($x, $y, $value)
1052 {
1053 if (!$this->hasCell($x, $y)) {
1054 return false;
1055 }
1056 $row = $this->getRow($x);
1057 $row[$y] = $value;
1058 $this->rows[$x] = $row;
1059 return true;
1060 }
1061
1062 /**
1063 * checks if a coordinate is valid
1064 *
1065 * sample of a csv file "my_cool.csv"
1066 *
1067 * <code>
1068 * name,age,skill
1069 * john,13,knows magic
1070 * tanaka,8,makes sushi
1071 * jose,5,dances salsa
1072 * </code>
1073 *
1074 * load the csv file
1075 *
1076 * <code>
1077 * $csv = new File_CSV_DataSource;
1078 * var_export($csv->load('my_cool.csv')); // true if file is
1079 * // loaded
1080 * </code>
1081 *
1082 * find out if a coordinate is valid
1083 *
1084 * <code>
1085 * var_export($csv->hasCell(99, 3)); // false
1086 * </code>
1087 *
1088 * check again for a know valid coordinate and grab that cell
1089 *
1090 * <code>
1091 * var_export($csv->hasCell(1, 1)); // true
1092 * var_export($csv->getCell(1, 1)); // '8'
1093 * </code>
1094 *
1095 * @param mixed $x the row to fetch
1096 * @param mixed $y the column to fetch
1097 *
1098 * @access public
1099 * @return void
1100 */
1101 public function hasCell($x, $y)
1102 {
1103 $has_x = array_key_exists($x, $this->rows);
1104 $has_y = array_key_exists($y, $this->headers);
1105 return ($has_x && $has_y);
1106 }
1107
1108 /**
1109 * row fetcher
1110 *
1111 * Note: first row is zero
1112 *
1113 * sample of a csv file "my_cool.csv"
1114 *
1115 * <code>
1116 * name,age,skill
1117 * john,13,knows magic
1118 * tanaka,8,makes sushi
1119 * jose,5,dances salsa
1120 * </code>
1121 *
1122 * load the library and csv file
1123 *
1124 * <code>
1125 * require_once 'File/CSV/DataSource.php';
1126 * $csv = new File_CSV_DataSource;
1127 * $csv->load('my_cool.csv');
1128 * </code>
1129 *
1130 * lets dump currently loaded data
1131 * <code>
1132 * var_export($csv->connect());
1133 * </code>
1134 *
1135 * output
1136 *
1137 * <code>
1138 * array (
1139 * 0 =>
1140 * array (
1141 * 'name' => 'john',
1142 * 'age' => '13',
1143 * 'skill' => 'knows magic',
1144 * ),
1145 * 1 =>
1146 * array (
1147 * 'name' => 'tanaka',
1148 * 'age' => '8',
1149 * 'skill' => 'makes sushi',
1150 * ),
1151 * 2 =>
1152 * array (
1153 * 'name' => 'jose',
1154 * 'age' => '5',
1155 * 'skill' => 'dances salsa',
1156 * ),
1157 * )
1158 * </code>
1159 *
1160 * Now let's fetch the second row
1161 *
1162 * <code>
1163 * var_export($csv->getRow(1));
1164 * </code>
1165 *
1166 * output
1167 *
1168 * <code>
1169 * array (
1170 * 0 => 'tanaka',
1171 * 1 => '8',
1172 * 2 => 'makes sushi',
1173 * )
1174 * </code>
1175 *
1176 * @param integer $number the row number to fetch
1177 *
1178 * @access public
1179 * @return array the row identified by number, if $number does
1180 * not exist an empty array is returned instead
1181 */
1182 public function getRow($number)
1183 {
1184 $raw = $this->rows;
1185 if (array_key_exists($number, $raw)) {
1186 return $raw[$number];
1187 }
1188 return array();
1189 }
1190
1191 /**
1192 * multiple row fetcher
1193 *
1194 * Extracts a rows in the following fashion
1195 * - all rows if no $range argument is given
1196 * - a range of rows identified by their key
1197 * - if rows in range are not found nothing is retrived instead
1198 * - if no rows were found an empty array is returned
1199 *
1200 * sample of a csv file "my_cool.csv"
1201 *
1202 * <code>
1203 * name,age,skill
1204 * john,13,knows magic
1205 * tanaka,8,makes sushi
1206 * jose,5,dances salsa
1207 * </code>
1208 *
1209 * load the library and csv file
1210 *
1211 * <code>
1212 * require_once 'File/CSV/DataSource.php';
1213 * $csv = new File_CSV_DataSource;
1214 * $csv->load('my_cool.csv');
1215 * </code>
1216 *
1217 * lets dump currently loaded data
1218 * <code>
1219 * var_export($csv->connect());
1220 * </code>
1221 *
1222 * output
1223 *
1224 * <code>
1225 * array (
1226 * 0 =>
1227 * array (
1228 * 'name' => 'john',
1229 * 'age' => '13',
1230 * 'skill' => 'knows magic',
1231 * ),
1232 * 1 =>
1233 * array (
1234 * 'name' => 'tanaka',
1235 * 'age' => '8',
1236 * 'skill' => 'makes sushi',
1237 * ),
1238 * 2 =>
1239 * array (
1240 * 'name' => 'jose',
1241 * 'age' => '5',
1242 * 'skill' => 'dances salsa',
1243 * ),
1244 * )
1245 * </code>
1246 *
1247 * now get the second and thirdh row
1248 *
1249 * <code>
1250 * var_export($csv->getRows(array(1, 2)));
1251 * </code>
1252 *
1253 * output
1254 *
1255 * <code>
1256 * array (
1257 * 0 =>
1258 * array (
1259 * 0 => 'tanaka',
1260 * 1 => '8',
1261 * 2 => 'makes sushi',
1262 * ),
1263 * 1 =>
1264 * array (
1265 * 0 => 'jose',
1266 * 1 => '5',
1267 * 2 => 'dances salsa',
1268 * ),
1269 * )
1270 * </code>
1271 *
1272 * now lets try something odd and the goodie third row
1273 *
1274 * <code>
1275 * var_export($csv->getRows(array(9, 2)));
1276 * </code>
1277 *
1278 * output
1279 *
1280 * <code>
1281 * array (
1282 * 0 =>
1283 * array (
1284 * 0 => 'jose',
1285 * 1 => '5',
1286 * 2 => 'dances salsa',
1287 * ),
1288 * )
1289 * </code>
1290 *
1291 * @param array $range a list of rows to retrive
1292 *
1293 * @access public
1294 * @return array
1295 */
1296 public function getRows($range = array())
1297 {
1298 if (is_array($range) && ($range === array())) {
1299 return $this->rows;
1300 }
1301
1302 if (!is_array($range)) {
1303 return $this->rows;
1304 }
1305
1306 $ret_arr = array();
1307 foreach ($this->rows as $key => $row) {
1308 if (in_array($key, $range)) {
1309 $ret_arr[] = $row;
1310 }
1311 }
1312 return $ret_arr;
1313 }
1314
1315 /**
1316 * row counter
1317 *
1318 * This function will exclude the headers
1319 *
1320 * sample of a csv file "my_cool.csv"
1321 *
1322 * <code>
1323 * name,age,skill
1324 * john,13,knows magic
1325 * tanaka,8,makes sushi
1326 * jose,5,dances salsa
1327 * </code>
1328 *
1329 * php implementation
1330 *
1331 * <code>
1332 * $csv = new File_CSV_DataSource;
1333 * $csv->load('my_cool.csv');
1334 * var_export($csv->countRows()); // returns 3
1335 * </code>
1336 *
1337 * @access public
1338 * @return integer
1339 */
1340 public function countRows()
1341 {
1342 return count($this->rows);
1343 }
1344
1345 /**
1346 * row appender
1347 *
1348 * Aggregates one more row to the currently loaded dataset
1349 *
1350 * sample of a csv file "my_cool.csv"
1351 *
1352 * <code>
1353 * name,age,skill
1354 * john,13,knows magic
1355 * tanaka,8,makes sushi
1356 * jose,5,dances salsa
1357 * </code>
1358 *
1359 *
1360 * first let's load the file and output whatever was retrived.
1361 *
1362 * <code>
1363 * require_once 'File/CSV/DataSource.php';
1364 * $csv = new File_CSV_DataSource;
1365 * $csv->load('my_cool.csv');
1366 * var_export($csv->connect());
1367 * </code>
1368 *
1369 * output
1370 *
1371 * <code>
1372 *
1373 * array (
1374 * 0 =>
1375 * array (
1376 * 'name' => 'john',
1377 * 'age' => '13',
1378 * 'skill' => 'knows magic',
1379 * ),
1380 * 1 =>
1381 * array (
1382 * 'name' => 'tanaka',
1383 * 'age' => '8',
1384 * 'skill' => 'makes sushi',
1385 * ),
1386 * 2 =>
1387 * array (
1388 * 'name' => 'jose',
1389 * 'age' => '5',
1390 * 'skill' => 'dances salsa',
1391 * ),
1392 * )
1393 * </code>
1394 *
1395 * now lets do some modifications, let's try adding three rows.
1396 *
1397 * <code>
1398 * var_export($csv->appendRow(1));
1399 * var_export($csv->appendRow('2'));
1400 * var_export($csv->appendRow(array(3, 3, 3)));
1401 * </code>
1402 *
1403 * output
1404 *
1405 * <code>
1406 * true
1407 * true
1408 * true
1409 * </code>
1410 *
1411 * and now let's try to see what has changed
1412 *
1413 * <code>
1414 * var_export($csv->connect());
1415 * </code>
1416 *
1417 * output
1418 *
1419 * <code>
1420 * array (
1421 * 0 =>
1422 * array (
1423 * 'name' => 'john',
1424 * 'age' => '13',
1425 * 'skill' => 'knows magic',
1426 * ),
1427 * 1 =>
1428 * array (
1429 * 'name' => 'tanaka',
1430 * 'age' => '8',
1431 * 'skill' => 'makes sushi',
1432 * ),
1433 * 2 =>
1434 * array (
1435 * 'name' => 'jose',
1436 * 'age' => '5',
1437 * 'skill' => 'dances salsa',
1438 * ),
1439 * 3 =>
1440 * array (
1441 * 'name' => 1,
1442 * 'age' => 1,
1443 * 'skill' => 1,
1444 * ),
1445 * 4 =>
1446 * array (
1447 * 'name' => '2',
1448 * 'age' => '2',
1449 * 'skill' => '2',
1450 * ),
1451 * 5 =>
1452 * array (
1453 * 'name' => 3,
1454 * 'age' => 3,
1455 * 'skill' => 3,
1456 * ),
1457 * )
1458 * </code>
1459 *
1460 * @param array $values the values to be appended to the row
1461 *
1462 * @access public
1463 * @return boolean
1464 */
1465 public function appendRow($values)
1466 {
1467 $this->rows[] = array();
1468 $this->symmetrize();
1469 return $this->fillRow($this->countRows() - 1, $values);
1470 }
1471
1472 /**
1473 * fillRow
1474 *
1475 * Replaces the contents of cells in one given row with $values.
1476 *
1477 * sample of a csv file "my_cool.csv"
1478 *
1479 * <code>
1480 * name,age,skill
1481 * john,13,knows magic
1482 * tanaka,8,makes sushi
1483 * jose,5,dances salsa
1484 * </code>
1485 *
1486 * if we load the csv file and fill the second row with new data?
1487 *
1488 * <code>
1489 * // load the library
1490 * require_once 'File/CSV/DataSource.php';
1491 * $csv = new File_CSV_DataSource;
1492 *
1493 * // load csv file
1494 * $csv->load('my_cool.csv');
1495 *
1496 * // fill exitent row
1497 * var_export($csv->fillRow(1, 'x'));
1498 * </code>
1499 *
1500 * output
1501 *
1502 * <code>
1503 * true
1504 * </code>
1505 *
1506 * now let's dump whatever we have changed
1507 *
1508 * <code>
1509 * var_export($csv->connect());
1510 * </code>
1511 *
1512 * output
1513 *
1514 * <code>
1515 * array (
1516 * 0 =>
1517 * array (
1518 * 'name' => 'john',
1519 * 'age' => '13',
1520 * 'skill' => 'knows magic',
1521 * ),
1522 * 1 =>
1523 * array (
1524 * 'name' => 'x',
1525 * 'age' => 'x',
1526 * 'skill' => 'x',
1527 * ),
1528 * 2 =>
1529 * array (
1530 * 'name' => 'jose',
1531 * 'age' => '5',
1532 * 'skill' => 'dances salsa',
1533 * ),
1534 * )
1535 * </code>
1536 *
1537 * now lets try to fill the row with specific data for each cell
1538 *
1539 * <code>
1540 * var_export($csv->fillRow(1, array(1, 2, 3)));
1541 * </code>
1542 *
1543 * output
1544 *
1545 * <code>
1546 * true
1547 * </code>
1548 *
1549 * and dump the results
1550 *
1551 * <code>
1552 * var_export($csv->connect());
1553 * </code>
1554 *
1555 * output
1556 *
1557 * <code>
1558 *
1559 * array (
1560 * 0 =>
1561 * array (
1562 * 'name' => 'john',
1563 * 'age' => '13',
1564 * 'skill' => 'knows magic',
1565 * ),
1566 * 1 =>
1567 * array (
1568 * 'name' => 1,
1569 * 'age' => 2,
1570 * 'skill' => 3,
1571 * ),
1572 * 2 =>
1573 * array (
1574 * 'name' => 'jose',
1575 * 'age' => '5',
1576 * 'skill' => 'dances salsa',
1577 * ),
1578 * )
1579 * </code>
1580 *
1581 * @param integer $row the row to fill identified by its key
1582 * @param mixed $values the value to use, if a string or number
1583 * is given the whole row will be replaced with this value.
1584 * if an array is given instead the values will be used to fill
1585 * the row. Only when the currently loaded dataset is symmetric
1586 *
1587 * @access public
1588 * @return boolean
1589 * @see isSymmetric(), getAsymmetricRows(), symmetrize(), fillColumn(),
1590 * fillCell(), appendRow()
1591 */
1592 public function fillRow($row, $values)
1593 {
1594 if (!$this->hasRow($row)) {
1595 return false;
1596 }
1597
1598 if (is_string($values) || is_numeric($values)) {
1599 foreach ($this->rows[$row] as $key => $cell) {
1600 $this->rows[$row][$key] = $values;
1601 }
1602 return true;
1603 }
1604
1605 $eql_to_headers = ($this->countHeaders() == count($values));
1606 if (is_array($values) && $this->isSymmetric() && $eql_to_headers) {
1607 $this->rows[$row] = $values;
1608 return true;
1609 }
1610
1611 return false;
1612 }
1613
1614 /**
1615 * row existance checker
1616 *
1617 * Scans currently loaded dataset and
1618 * checks if a given row identified by $number exists
1619 *
1620 * sample of a csv file "my_cool.csv"
1621 *
1622 * <code>
1623 * name,age,skill
1624 * john,13,knows magic
1625 * tanaka,8,makes sushi
1626 * jose,5,dances salsa
1627 * </code>
1628 *
1629 * load library and csv file
1630 *
1631 * <code>
1632 * require_once 'File/CSV/DataSource.php';
1633 * $csv = new File_CSV_DataSource;
1634 * $csv->load('my_cool.csv');
1635 * </code>
1636 *
1637 * build a relationship and dump it so we can see the rows we will
1638 * be working with
1639 *
1640 * <code>
1641 * var_export($csv->connect());
1642 * </code>
1643 *
1644 * output
1645 *
1646 * <code>
1647 * array (
1648 * 0 =>
1649 * array (
1650 * 'name' => 'john',
1651 * 'age' => '13',
1652 * 'skill' => 'knows magic',
1653 * ),
1654 * 1 => // THIS ROW EXISTS!!!
1655 * array (
1656 * 'name' => 'tanaka',
1657 * 'age' => '8',
1658 * 'skill' => 'makes sushi',
1659 * ),
1660 * 2 =>
1661 * array (
1662 * 'name' => 'jose',
1663 * 'age' => '5',
1664 * 'skill' => 'dances salsa',
1665 * ),
1666 * )
1667 * </code>
1668 *
1669 * now lets check for row existance
1670 *
1671 * <code>
1672 * var_export($csv->hasRow(1));
1673 * var_export($csv->hasRow(-1));
1674 * var_export($csv->hasRow(9999));
1675 * </code>
1676 *
1677 * output
1678 *
1679 * <code>
1680 * true
1681 * false
1682 * false
1683 * </code>
1684 *
1685 * @param mixed $number a numeric value that identifies the row
1686 * you are trying to fetch.
1687 *
1688 * @access public
1689 * @return boolean
1690 * @see getRow(), getRows(), appendRow(), fillRow()
1691 */
1692 public function hasRow($number)
1693 {
1694 return (in_array($number, array_keys($this->rows)));
1695 }
1696
1697 /**
1698 * row remover
1699 *
1700 * removes one row from the current data set.
1701 *
1702 * sample of a csv file "my_cool.csv"
1703 *
1704 * <code>
1705 * name,age,skill
1706 * john,13,knows magic
1707 * tanaka,8,makes sushi
1708 * jose,5,dances salsa
1709 * </code>
1710 *
1711 * first let's load the file and output whatever was retrived.
1712 *
1713 * <code>
1714 * require_once 'File/CSV/DataSource.php';
1715 * $csv = new File_CSV_DataSource;
1716 * $csv->load('my_cool.csv');
1717 * var_export($csv->connect());
1718 * </code>
1719 *
1720 * output
1721 *
1722 * <code>
1723 *
1724 * array (
1725 * 0 =>
1726 * array (
1727 * 'name' => 'john',
1728 * 'age' => '13',
1729 * 'skill' => 'knows magic',
1730 * ),
1731 * 1 =>
1732 * array (
1733 * 'name' => 'tanaka',
1734 * 'age' => '8',
1735 * 'skill' => 'makes sushi',
1736 * ),
1737 * 2 =>
1738 * array (
1739 * 'name' => 'jose',
1740 * 'age' => '5',
1741 * 'skill' => 'dances salsa',
1742 * ),
1743 * )
1744 * </code>
1745 *
1746 * now lets remove the second row
1747 *
1748 * <code>
1749 * var_export($csv->removeRow(1));
1750 * </code>
1751 *
1752 * output
1753 *
1754 * <code>
1755 * true
1756 * </code>
1757 *
1758 * now lets dump again the data and see what changes have been
1759 * made
1760 *
1761 * <code>
1762 * var_export($csv->connect());
1763 * </code>
1764 *
1765 * output
1766 *
1767 * <code>
1768 * array (
1769 * 0 =>
1770 * array (
1771 * 'name' => 'john',
1772 * 'age' => '13',
1773 * 'skill' => 'knows magic',
1774 * ),
1775 * 1 =>
1776 * array (
1777 * 'name' => 'jose',
1778 * 'age' => '5',
1779 * 'skill' => 'dances salsa',
1780 * ),
1781 * )
1782 * </code>
1783 *
1784 * @param mixed $number the key that identifies that row
1785 *
1786 * @access public
1787 * @return boolean
1788 * @see hasColumn(), getHeaders(), createHeaders(), setHeaders(),
1789 * isSymmetric(), getAsymmetricRows()
1790 */
1791 public function removeRow($number)
1792 {
1793 $cnt = $this->countRows();
1794 $row = $this->getRow($number);
1795 if (is_array($row) && ($row != array())) {
1796 unset($this->rows[$number]);
1797 } else {
1798 return false;
1799 }
1800 $this->resetKeys($this->rows);
1801 return ($cnt == ($this->countRows() + 1));
1802 }
1803
1804 /**
1805 * row walker
1806 *
1807 * goes through one full row of data and executes a callback
1808 * function per each cell in that row.
1809 *
1810 * Note: callback functions get the value of the cell as an
1811 * argument, and whatever that callback returns will be used to
1812 * replace the current value of that cell.
1813 *
1814 * @param string|integer $row anything that is numeric is a valid row
1815 * identificator. As long as it is within the range of the currently
1816 * loaded dataset
1817 *
1818 * @param string $callback the callback function to be executed
1819 * per each cell in a row
1820 *
1821 * @access public
1822 * @return boolean
1823 * - false if callback does not exist
1824 * - false if row does not exits
1825 */
1826 public function walkRow($row, $callback)
1827 {
1828 if (!function_exists($callback)) {
1829 return false;
1830 }
1831 if ($this->hasRow($row)) {
1832 foreach ($this->getRow($row) as $key => $value) {
1833 $this->rows[$row][$key] = $callback($value);
1834 }
1835 return true;
1836 }
1837 return false;
1838 }
1839
1840 /**
1841 * raw data as array
1842 *
1843 * Gets the data that was retrived from the csv file as an array
1844 *
1845 * Note: that changes and alterations made to rows, columns and
1846 * values will also reflect on what this function retrives.
1847 *
1848 * @access public
1849 * @return array
1850 * @see connect(), getHeaders(), getRows(), isSymmetric(), getAsymmetricRows(),
1851 * symmetrize()
1852 */
1853 public function getRawArray()
1854 {
1855 $ret_arr = array();
1856
1857 foreach ($this->rows as $key => $row) {
1858 $item = array();
1859 foreach ($this->headers as $col => $value) {
1860 $item[$value] = $row[$col];
1861 }
1862 array_push($ret_arr, $item);
1863 unset($item);
1864 }
1865 return $ret_arr;
1866 }
1867
1868 /**
1869 * header creator
1870 *
1871 * uses prefix and creates a header for each column suffixed by a
1872 * numeric value
1873 *
1874 * by default the first row is interpreted as headers but if we
1875 * have a csv file with data only and no headers it becomes really
1876 * annoying to work with the current loaded data.
1877 *
1878 * this function will create a set dinamically generated headers
1879 * and make the current headers accessable with the row handling
1880 * functions
1881 *
1882 * Note: that the csv file contains only data but no headers
1883 * sample of a csv file "my_cool.csv"
1884 *
1885 * <code>
1886 * john,13,knows magic
1887 * tanaka,8,makes sushi
1888 * jose,5,dances salsa
1889 * </code>
1890 *
1891 * checks if the csv file was loaded
1892 *
1893 * <code>
1894 * $csv = new File_CSV_DataSource;
1895 * if (!$csv->load('my_cool.csv')) {
1896 * die('can not load csv file');
1897 * }
1898 * </code>
1899 *
1900 * dump current headers
1901 *
1902 * <code>
1903 * var_export($csv->getHeaders());
1904 * </code>
1905 *
1906 * standard output
1907 *
1908 * <code>
1909 * array (
1910 * 0 => 'john',
1911 * 1 => '13',
1912 * 2 => 'knows magic',
1913 * )
1914 * </code>
1915 *
1916 * generate headers named 'column' suffixed by a number and interpret
1917 * the previous headers as rows.
1918 *
1919 * <code>
1920 * $csv->createHeaders('column')
1921 * </code>
1922 *
1923 * dump current headers
1924 *
1925 * <code>
1926 * var_export($csv->getHeaders());
1927 * </code>
1928 *
1929 * standard output
1930 *
1931 * <code>
1932 * array (
1933 * 0 => 'column_1',
1934 * 1 => 'column_2',
1935 * 2 => 'column_3',
1936 * )
1937 * </code>
1938 *
1939 * build a relationship and dump it
1940 *
1941 * <code>
1942 * var_export($csv->connect());
1943 * </code>
1944 *
1945 * output
1946 *
1947 * <code>
1948 *
1949 * array (
1950 * 0 =>
1951 * array (
1952 * 'column_1' => 'john',
1953 * 'column_2' => '13',
1954 * 'column_3' => 'knows magic',
1955 * ),
1956 * 1 =>
1957 * array (
1958 * 'column_1' => 'tanaka',
1959 * 'column_2' => '8',
1960 * 'column_3' => 'makes sushi',
1961 * ),
1962 * 2 =>
1963 * array (
1964 * 'column_1' => 'jose',
1965 * 'column_2' => '5',
1966 * 'column_3' => 'dances salsa',
1967 * ),
1968 * )
1969 * </code>
1970 *
1971 * @param string $prefix string to use as prefix for each
1972 * independent header
1973 *
1974 * @access public
1975 * @return boolean fails if data is not symmetric
1976 * @see isSymmetric(), getAsymmetricRows()
1977 */
1978 public function createHeaders($prefix)
1979 {
1980 if (!$this->isSymmetric()) {
1981 return false;
1982 }
1983
1984 $length = count($this->headers) + 1;
1985 $this->moveHeadersToRows();
1986
1987 $ret_arr = array();
1988 for ($i = 1; $i < $length; $i ++) {
1989 $ret_arr[] = $prefix . "_$i";
1990 }
1991 $this->headers = $ret_arr;
1992 return $this->isSymmetric();
1993 }
1994
1995 /**
1996 * header injector
1997 *
1998 * uses a $list of values which wil be used to replace current
1999 * headers.
2000 *
2001 * Note: that given $list must match the length of all rows.
2002 * known as symmetric. see isSymmetric() and getAsymmetricRows() methods
2003 *
2004 * Also, that current headers will be used as first row of data
2005 * and consecuently all rows order will change with this action.
2006 *
2007 * sample of a csv file "my_cool.csv"
2008 *
2009 * <code>
2010 * name,age,skill
2011 * john,13,knows magic
2012 * tanaka,8,makes sushi
2013 * jose,5,dances salsa
2014 * </code>
2015 *
2016 * load the library and csv file
2017 *
2018 * <code>
2019 * require_once 'File/CSV/DataSource.php';
2020 * $csv = new File_CSV_DataSource;
2021 * $csv->load('my_cool.csv');
2022 * </code>
2023 *
2024 * lets dump currently loaded data
2025 * <code>
2026 * var_export($csv->connect());
2027 * </code>
2028 *
2029 * output
2030 *
2031 * <code>
2032 * array (
2033 * 0 =>
2034 * array (
2035 * 'name' => 'john',
2036 * 'age' => '13',
2037 * 'skill' => 'knows magic',
2038 * ),
2039 * 1 =>
2040 * array (
2041 * 'name' => 'tanaka',
2042 * 'age' => '8',
2043 * 'skill' => 'makes sushi',
2044 * ),
2045 * 2 =>
2046 * array (
2047 * 'name' => 'jose',
2048 * 'age' => '5',
2049 * 'skill' => 'dances salsa',
2050 * ),
2051 * )
2052 * </code>
2053 *
2054 * And now lets create a new set of headers and attempt to inject
2055 * them into the current loaded dataset
2056 *
2057 * <code>
2058 * $new_headers = array('a', 'b', 'c');
2059 * var_export($csv->setHeaders($new_headers));
2060 * </code>
2061 *
2062 * output
2063 *
2064 * <code>
2065 * true
2066 * </code>
2067 *
2068 * Now lets try the same with some headers that do not match the
2069 * current headers length. (this should fail)
2070 *
2071 * <code>
2072 * $new_headers = array('a', 'b');
2073 * var_export($csv->setHeaders($new_headers));
2074 * </code>
2075 *
2076 * output
2077 *
2078 * <code>
2079 * false
2080 * </code>
2081 *
2082 * now let's dump whatever we have changed
2083 *
2084 * <code>
2085 * var_export($csv->connect());
2086 * </code>
2087 *
2088 * output
2089 *
2090 * <code>
2091 * array (
2092 * 0 =>
2093 * array (
2094 * 'a' => 'name',
2095 * 'b' => 'age',
2096 * 'c' => 'skill',
2097 * ),
2098 * 1 =>
2099 * array (
2100 * 'a' => 'john',
2101 * 'b' => '13',
2102 * 'c' => 'knows magic',
2103 * ),
2104 * 2 =>
2105 * array (
2106 * 'a' => 'tanaka',
2107 * 'b' => '8',
2108 * 'c' => 'makes sushi',
2109 * ),
2110 * 3 =>
2111 * array (
2112 * 'a' => 'jose',
2113 * 'b' => '5',
2114 * 'c' => 'dances salsa',
2115 * ),
2116 * )
2117 * </code>
2118 *
2119 * @param array $list a collection of names to use as headers,
2120 *
2121 * @access public
2122 * @return boolean fails if data is not symmetric
2123 * @see isSymmetric(), getAsymmetricRows(), getHeaders(), createHeaders()
2124 */
2125 public function setHeaders($list)
2126 {
2127 if (!$this->isSymmetric()) {
2128 return false;
2129 }
2130 if (!is_array($list)) {
2131 return false;
2132 }
2133 if (count($list) != count($this->headers)) {
2134 return false;
2135 }
2136 $this->moveHeadersToRows();
2137 $this->headers = $list;
2138 return true;
2139 }
2140
2141 /**
2142 * csv parser
2143 *
2144 * reads csv data and transforms it into php-data
2145 *
2146 * @access protected
2147 * @return boolean
2148 */
2149 protected function parse()
2150 {
2151 if (!$this->validates()) {
2152 return false;
2153 }
2154
2155 $c = 0;
2156 $d = $this->settings['delimiter'];
2157 $e = $this->settings['escape'];
2158 $l = $this->settings['length'];
2159
2160 $res = fopen($this->_filename, 'r');
2161
2162 $create_new_headers = false;
2163
2164 while ($keys = fgetcsv($res, $l, $d, $e)) {
2165
2166 if ($c == 0) {
2167 foreach ($keys as $key => $value) {
2168 if (preg_match('%\W(http:|https:|ftp:)$%i', $value) or strlen($value) > 15 or is_numeric($value)) {
2169 $create_new_headers = true;
2170 break;
2171 }
2172 }
2173 $this->headers = $keys;
2174 } else {
2175 array_push($this->rows, $keys);
2176 }
2177
2178 $c ++;
2179 }
2180
2181 fclose($res);
2182 $this->removeEmpty();
2183
2184 if ($create_new_headers) $this->createHeaders('column');
2185
2186 return true;
2187 }
2188
2189 /**
2190 * empty row remover
2191 *
2192 * removes all records that have been defined but have no data.
2193 *
2194 * @access protected
2195 * @return array containing only the rows that have data
2196 */
2197 protected function removeEmpty()
2198 {
2199 $ret_arr = array();
2200 foreach ($this->rows as $row) {
2201 $line = trim(join('', $row));
2202 if (!empty($line)) {
2203 $ret_arr[] = $row;
2204 }
2205 }
2206 $this->rows = $ret_arr;
2207 }
2208
2209 /**
2210 * csv file validator
2211 *
2212 * checks wheather if the given csv file is valid or not
2213 *
2214 * @access protected
2215 * @return boolean
2216 */
2217 protected function validates()
2218 {
2219 // file existance
2220 if (!file_exists($this->_filename)) {
2221 return false;
2222 }
2223
2224 // file readability
2225 if (!is_readable($this->_filename)) {
2226 return false;
2227 }
2228
2229 return true;
2230 }
2231
2232 /**
2233 * header relocator
2234 *
2235 * @access protected
2236 * @return void
2237 */
2238 protected function moveHeadersToRows()
2239 {
2240 $arr = array();
2241 $arr[] = $this->headers;
2242 foreach ($this->rows as $row) {
2243 $arr[] = $row;
2244 }
2245 $this->rows = $arr;
2246 $this->headers = array();
2247 }
2248
2249 /**
2250 * array key reseter
2251 *
2252 * makes sure that an array's keys are setted in a correct numerical order
2253 *
2254 * Note: that this function does not return anything, all changes
2255 * are made to the original array as a reference
2256 *
2257 * @param array &$array any array, if keys are strings they will
2258 * be replaced with numeric values
2259 *
2260 * @access protected
2261 * @return void
2262 */
2263 protected function resetKeys(&$array)
2264 {
2265 $arr = array();
2266 foreach ($array as $item) {
2267 $arr[] = $item;
2268 }
2269 $array = $arr;
2270 }
2271
2272 /**
2273 * object data flusher
2274 *
2275 * tells this object to forget all data loaded and start from
2276 * scratch
2277 *
2278 * @access protected
2279 * @return void
2280 */
2281 protected function flush()
2282 {
2283 $this->rows = array();
2284 $this->headers = array();
2285 }
2286
2287 public function toXml() {
2288 $this->symmetrize();
2289 return ArrayToXML::toXml($this->getRawArray());
2290 }
2291
2292 function analyse_file($file, $capture_limit_in_kb = 10) {
2293 // capture starting memory usage
2294 $output['peak_mem']['start'] = memory_get_peak_usage(true);
2295
2296 // log the limit how much of the file was sampled (in Kb)
2297 $output['read_kb'] = $capture_limit_in_kb;
2298
2299 // read in file
2300 $fh = fopen($file, 'r');
2301 $contents = fread($fh, ($capture_limit_in_kb * 1024)); // in KB
2302 fclose($fh);
2303
2304 // specify allowed field delimiters
2305 $delimiters = array(
2306 'comma' => ',',
2307 'semicolon' => ';',
2308 'tab' => "\t",
2309 'pipe' => '|',
2310 'colon' => ':'
2311 );
2312
2313 // specify allowed line endings
2314 $line_endings = array(
2315 'rn' => "\r\n",
2316 'n' => "\n",
2317 'r' => "\r",
2318 'nr' => "\n\r"
2319 );
2320
2321 // loop and count each line ending instance
2322 foreach ($line_endings as $key => $value) {
2323 $line_result[$key] = substr_count($contents, $value);
2324 }
2325
2326 // sort by largest array value
2327 asort($line_result);
2328
2329 // log to output array
2330 $output['line_ending']['results'] = $line_result;
2331 $output['line_ending']['count'] = end($line_result);
2332 $output['line_ending']['key'] = key($line_result);
2333 $output['line_ending']['value'] = $line_endings[$output['line_ending']['key']];
2334 $lines = explode($output['line_ending']['value'], $contents);
2335
2336 // remove last line of array, as this maybe incomplete?
2337 array_pop($lines);
2338
2339 // create a string from the legal lines
2340 $complete_lines = implode(' ', $lines);
2341
2342 // log statistics to output array
2343 $output['lines']['count'] = count($lines);
2344 $output['lines']['length'] = strlen($complete_lines);
2345
2346 // loop and count each delimiter instance
2347 foreach ($delimiters as $delimiter_key => $delimiter) {
2348 $delimiter_result[$delimiter_key] = substr_count($complete_lines, $delimiter);
2349 }
2350
2351 // sort by largest array value
2352 asort($delimiter_result);
2353
2354 // log statistics to output array with largest counts as the value
2355 $output['delimiter']['results'] = $delimiter_result;
2356 $output['delimiter']['count'] = end($delimiter_result);
2357 $output['delimiter']['key'] = key($delimiter_result);
2358 $output['delimiter']['value'] = $delimiters[$output['delimiter']['key']];
2359
2360 // capture ending memory usage
2361 $output['peak_mem']['end'] = memory_get_peak_usage(true);
2362 return $output;
2363 }
2364
2365 }
2366
2367 class ArrayToXML
2368 {
2369 /**
2370 * The main function for converting to an XML document.
2371 * Pass in a multi dimensional array and this recrusively loops through and builds up an XML document.
2372 *
2373 * @param array $data
2374 * @param string $rootNodeName - what you want the root node to be - defaultsto data.
2375 * @param SimpleXMLElement $xml - should only be used recursively
2376 * @return string XML
2377 */
2378 public static function toXml($data, $rootNodeName = 'data', $xml=null)
2379 {
2380 // turn off compatibility mode as simple xml throws a wobbly if you don't.
2381 if (ini_get('zend.ze1_compatibility_mode') == 1)
2382 {
2383 ini_set ('zend.ze1_compatibility_mode', 0);
2384 }
2385
2386 if ($xml == null)
2387 {
2388 $xml = simplexml_load_string('<?xml version="1.0" encoding="utf-8"?><'.$rootNodeName .'/>');
2389 }
2390
2391 // loop through the data passed in.
2392 foreach($data as $key => $value)
2393 {
2394 // no numeric keys in our xml please!
2395 if (is_numeric($key))
2396 {
2397 // make string key...
2398 $key = "node";
2399 }
2400
2401 // replace anything not alpha numeric
2402 $key = preg_replace('/[^a-z0-9_]/i', '', $key);
2403
2404 // if there is another array found recrusively call this function
2405 if (is_array($value) or is_object($value))
2406 {
2407 $node = $xml->addChild($key);
2408 // recrusive call.
2409 ArrayToXML::toXml($value, $rootNodeName, $node);
2410 }
2411 else
2412 {
2413 // add single node.
2414 //$value = htmlentities($value);
2415 $xml->addChild($key,$value);
2416 }
2417
2418
2419 }
2420 // pass back as string. or simple xml object if you want!
2421 return $xml->asXML();
2422 }
2423
2424
2425 }
2426
2427 ?>
2428