%PDF- %PDF-
Direktori : /home/lightco1/upgrade.lightco.com.au/administrator/components/com_csvi/helper/file/import/ |
Current File : /home/lightco1/upgrade.lightco.com.au/administrator/components/com_csvi/helper/file/import/csv.php |
<?php /** * @package CSVI * @subpackage File * * @author Roland Dalmulder <contact@csvimproved.com> * @copyright Copyright (C) 2006 - 2016 RolandD Cyber Produksi. All rights reserved. * @license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html * @link http://www.csvimproved.com */ defined('_JEXEC') or die(); /** * CSV file importer. * * @package CSVI * @subpackage File * @since 6.0 */ class CsviHelperFileImportCsv extends CsviHelperFile { /** * Contains the field delimiter * * @var string * @since 6.0 */ private $field_delimiter; /** * Contains the text enclosure * * @var string * @since 6.0 */ private $text_enclosure; /** * Sets to true if a file delimiters have been checked * * @var bool * @since 6.0 */ private $checked_delimiter = false; /** * The file pointer position in the file * * @var int * @since 6.0 */ private $pointer = 0; /** * The fields handler * * @var CsviHelperImportFields * @since 6.0 */ protected $fields; /** * Open the file to read. * * @return bool Return true if file can be opened | False if file cannot be opened. * * @since 6.0 */ public function openFile() { // Open the csv file if (file_exists($this->filename) && $this->fp = fopen($this->filename, 'r')) { $this->closed = false; return true; } return false; } /** * Close the file. * * @param bool $removeFolder Specify if the temporary folder should be removed * * @return void. * * @since 3.0 */ public function closeFile($removeFolder = true) { fclose($this->fp); $this->closed = true; parent::closeFile($removeFolder); } /** * Get the file position. * * @return int The current position in the file. * * @since 3.0 */ public function getFilePos() { return $this->pointer; } /** * Set the file position. * * @param int $position The position to move to * * @return int 0 if success | -1 if not success. * * @since 3.0 */ public function setFilePos($position) { if (!$this->fp) { $this->openFile(); } $result = fseek($this->fp, $position); if ($result === 0) { $this->pointer = $position; } return $result; } /** * Load the column headers from a file. * * @return mixed array when column headers are found | false if column headers cannot be read. * * @since 3.0 * * @throws UnexpectedValueException */ public function loadColumnHeaders() { // Column headers are always the first line of the file // 1. Store current position $currentPosition = $this->getFilePos(); if ($currentPosition > 0) { // 2. Go to the beginning of the file $this->setFilePos(0); } // 3. Read the line $columnHeaders = $this->readNextLine(true); if ($currentPosition > 0) { // 4. Set the position back $this->setFilePos($currentPosition); } $this->linepointer++; return $columnHeaders; } /** * Read the next line in the file. * * @param bool $headers Set if the column headers are being read. * * @return array Array with the line of data read | false if data cannot be read. * * @since 3.0 * * @throws UnexpectedValueException */ public function readNextLine($headers = false) { // Check if the file is still open if ($this->closed || feof($this->fp)) { return false; } // Make sure we have delimiters if ($this->field_delimiter === null) { return false; } // Load some settings $columnHeaders = $this->fields->getAllFieldnames(); // Check for a valid field delimiter if ($this->field_delimiter) { // Ignore empty records $csvData = array(0 => ''); while (is_array($csvData) && count($csvData) === 1 && $csvData[0] === '') { if ($this->text_enclosure !== null) { $csvData = fgetcsv($this->fp, 0, $this->field_delimiter, $this->text_enclosure); } else { $csvData = fgetcsv($this->fp, 0, $this->field_delimiter); } } // We read data, set the file pointer $this->pointer = ftell($this->fp); // Check if we can read the line correctly if (!$this->checked_delimiter && count($csvData) === 1) { $current_field = $this->field_delimiter; $current_text = $this->text_enclosure; $this->findDelimiters(true); if ($current_field !== $this->field_delimiter) { $this->log->addStats('incorrect', JText::sprintf('COM_CSVI_UNEQUAL_FIELD_DELIMITER', $current_field, $this->field_delimiter)); } if ($current_text !== $this->text_enclosure) { $this->log->addStats('incorrect', JText::sprintf('COM_CSVI_UNEQUAL_FIELD_DELIMITER', $current_field, $this->field_delimiter)); } $this->field_delimiter = $current_field; $this->text_enclosure = $current_text; } if ($csvData) { // Do BOM check if ($this->input->get('currentline', 0, 'int') === 1 || $this->input->get('currentline', null, null) === null) { // Remove text delimiters as they are not recognized by fgetcsv $csvData[0] = $this->removeTextDelimiters($this->checkBom($csvData[0])); } $this->linepointer++; if ($headers) { return $csvData; } else { // Add the data to the fields $counters = array(); foreach ($csvData as $key => $value) { if (isset($columnHeaders[$key])) { if (!isset($counters[$columnHeaders[$key]])) { $counters[$columnHeaders[$key]] = 0; } $counters[$columnHeaders[$key]]++; $this->fields->set($columnHeaders[$key], $value, $counters[$columnHeaders[$key]]); } } return true; } } else { return false; } } else { $this->log->addStats('incorrect', 'COM_CSVI_NO_FIELD_DELIMITER_FOUND'); return false; } } /** * Process the file to import. * * @return bool True if file can be processed | False if file cannot be processed. * * @since 3.0 * * @throws UnexpectedValueException */ public function processFile() { // Open the file if ($this->openFile()) { // Load the delimiters $this->findDelimiters(); return true; } else { return false; } } /** * Find the delimiters used. * * @param bool $force Force to read the delimiters from the imported file. * * @return bool True if delimiters found | False if delimiters not found. * * @throws UnexpectedValueException * * @since 3.0 */ private function findDelimiters($force = false) { if (!$this->checked_delimiter) { if (!$force && !$this->template->get('auto_detect_delimiters', true)) { // Set the field delimiter if (strtolower($this->template->get('field_delimiter')) === 't') { $this->field_delimiter = "\t"; } else { $this->field_delimiter = $this->template->get('field_delimiter'); } // Set the text enclosure $this->text_enclosure = $this->template->get('text_enclosure', '') ? $this->template->get('text_enclosure') : null; } else { // Read the first line rewind($this->fp); $line = fgets($this->fp); // 1. Is the user using text enclosures $first_char = substr($line, 0, 1); $pattern = '/[a-zA-Z0-9_]/'; $matches = array(); preg_match($pattern, $first_char, $matches); if (count($matches) === 0) { // User is using text delimiter $this->text_enclosure = $first_char; $this->log->add(JText::sprintf('COM_CSVI_FOUND_TEXT_ENCLOSURE', $first_char), false); // 2. What field delimiter is being used if (strlen($line) > 1) { $match_next_char = strpos($line, $this->text_enclosure, 1); $second_char = substr($line, $match_next_char + 1, 1); } else { $second_char = $first_char; } if ($first_char === $second_char) { throw new UnexpectedValueException(JText::_('COM_CSVI_CANNOT_FIND_TEXT_DELIMITER'), false); } else { $this->field_delimiter = $second_char; } } else { $totalCharacters = strlen($line); // 2. What field delimiter is being used for ($i = 0; $i <= $totalCharacters; $i++) { $current_char = substr($line, $i, 1); preg_match($pattern, $current_char, $matches); if (count($matches) === 0) { $this->field_delimiter = $current_char; $i = $totalCharacters; } } } $this->log->add(JText::sprintf('COM_CSVI_FOUND_FIELD_DELIMITER', $this->field_delimiter), false); rewind($this->fp); } $this->checked_delimiter = true; } return true; } /** * Checks if the uploaded file has a BOM. * * If the uploaded file has a BOM, remove it since it only causes * problems on import. * * @param string $data The string to check for a BOM * * @return string Return the cleaned string. * * @since 3.0 */ private function checkBom($data) { // Check the first three characters if (strlen($data) > 3) { if (ord($data{0}) === 239 && ord($data{1}) === 187 && ord($data{2}) === 191) { return substr($data, 3, strlen($data)); } else { return $data; } } else { return $data; } } /** * Removes the text delimiters when fgetcsv() has failed to do so because the file contains a BOM. * This allows for the possibility that the data value contains embedded text enclosure characters * (which should be doubled up for correct csv file format). * The string [32" TV] (ignore brackets) should be encoded as ["32"" TV"] * This function correctly decodes ["32"" TV"] back to [32" TV]. * * @param string $data The string to clean * * @return string The cleaned string. * * @since 3.0 */ private function removeTextDelimiters($data) { if (strpos($data, $this->text_enclosure) === 0 && strrpos($data, $this->text_enclosure) === 0) { $data = str_replace($this->text_enclosure . $this->text_enclosure, $this->text_enclosure, substr($data, 1, -1)); } return $data; } /** * Sets the file pointer back to beginning. * * @return void. * * @since 3.0 */ public function rewind() { $this->setFilePos(0); } /** * Return the number of lines in a CSV file. * * @return int The number of lines in the CSV file. * * @since 6.0 */ public function lineCount() { $lineCount = 0; if ($this->fp) { // Get the current location $filePosition = $this->getFilePos(); // Rewind the file to be sure we are at the start $this->rewind(); while (!feof($this->fp)) { if (fgets($this->fp)) { $lineCount++; } } // Set the file back to it's original position $this->setFilePos($filePosition); } return $lineCount; } }