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 |