',
'
',
'',
'
',
'',
'',
'
'];
protected $defaultFormat = '%s';
/**
* A better csv reader which handles encoding as well as removes completely empty lines
*
* Options:
* - int length (0 = no limit)
* - string delimiter (null defaults to ,)
* - string enclosure (null defaults to " - do not pass empty string)
* - string mode
* - string force Force open/read the file
* - bool removeEmpty Remove empty lines (simple newline characters without meaning)
* - bool encode Encode to UTF-8
*
* @param array $options Options
* @return array Content or false on failure
*/
public function readCsv($options = [], $delimiter = null, $enclosure = null, $mode = 'rb', $force = false, $removeEmpty = false, $encode = true) {
// For BC
if (!is_array($options)) {
$options = [
'delimiter' => $delimiter !== null ? $delimiter : ',',
'enclosure' => $enclosure !== null ? $enclosure : '"',
'mode' => $mode,
'force' => $force,
'removeEmpty' => $removeEmpty,
'encode' => $encode,
'length' => $options
];
}
$defaults = [
'delimiter' => ',',
'enclosure' => '"',
'escape' => "\\",
'mode' => 'rb',
'force' => false,
'removeEmpty' => false,
'encode' => true,
'length' => 0
];
$options += $defaults;
extract($options);
if ($this->open($mode, $force) === false) {
return false;
}
if ($this->lock !== null && flock($this->handle, LOCK_SH) === false) {
return false;
}
// PHP cannot handle delimiters with more than a single char
if (strlen($delimiter) > 1) {
throw new InternalErrorException('Invalid delimiter');
}
$res = [];
while (true) {
$data = fgetcsv($this->handle, $length, $delimiter, $enclosure, $escape);
if ($data === false) {
break;
}
if ($encode) {
$data = $this->_encode($data);
}
$isEmpty = true;
foreach ($data as $key => $val) {
if (!empty($val)) {
$isEmpty = false;
break;
}
}
if ($isEmpty && $removeEmpty) {
continue;
}
$res[] = $data;
}
if ($this->lock !== null) {
flock($this->handle, LOCK_UN);
}
$this->close();
return $res;
}
/**
* FileLib::readCsvFromString()
*
* @param string $string CSV content
* @param array $options Options array
* @return array Parsed content
*/
public static function readCsvFromString($string, $options = []) {
$file = fopen("php://memory", "rw");
fwrite($file, $string);
fseek($file, 0);
$defaults = [
'delimiter' => ',',
'enclosure' => '"',
'escape' => "\\",
'eol' => "\n",
'encode' => false,
'removeEmpty' => false
];
$options += $defaults;
extract($options);
// PHP cannot handle delimiters with more than a single char
if (strlen($delimiter) > 1) {
throw new InternalErrorException('Invalid delimiter');
}
$res = [];
while (true) {
$data = fgetcsv($file, 0, $delimiter, $enclosure, $escape);
if ($data === false) {
break;
}
if ($encode) {
$data = $this->_encode($data);
}
$isEmpty = true;
foreach ($data as $key => $val) {
if (!empty($val)) {
$isEmpty = false;
break;
}
}
if ($isEmpty && $removeEmpty) {
continue;
}
$res[] = $data;
}
fclose($file);
return $res;
}
/**
* Write an array to a csv file
*
* @param array $data
* @param string $delimiter (null defaults to ,)
* @param string $enclosure (null defaults to " - do not pass empty string)
* @return bool Success
*/
public function writeCsv($data, $delimiter = null, $enclosure = null) {
if ($this->open('w', true) !== true) {
return false;
}
if ($this->lock !== null) {
if (flock($this->handle, LOCK_EX) === false) {
return false;
}
}
$success = true;
foreach ($data as $row) {
if (fputcsv($this->handle, array_values((array)$row), (isset($delimiter) ? $delimiter : ','), (isset($enclosure) ? $enclosure : '"')) === false) {
$success = false;
}
}
if ($this->lock !== null) {
flock($this->handle, LOCK_UN);
}
$this->close();
return $success;
}
/**
* Read files with fscanf() and pattern
*
* @param string $format (e.g. "%s\t%s\t%s\n")
* @param string $mode
* @param string $force Force open/read the file
* @return array Content or false on failure
*/
public function readWithPattern($format = null, $mode = 'rb', $force = false) {
$res = [];
if ($this->open($mode, $force) === false) {
return false;
}
if ($this->lock !== null && flock($this->handle, LOCK_SH) === false) {
return false;
}
if (empty($format)) {
$format = $this->defaultFormat;
}
while (true) {
$data = fscanf($this->handle, $format);
if ($data === false) {
break;
}
$res[] = $data;
}
if ($this->lock !== null) {
flock($this->handle, LOCK_UN);
}
return $res;
}
/**
* Return the contents of this File as a string - but without tags
*
* @param string/array $tags: or array('',...) otherwise default tags are used
* @param string $mode
* @param bool $force If true then the file will be re-opened even if its already opened, otherwise it won't
* @return mixed string on success, false on failure
*/
public function readWithTags($tags = null, $mode = 'rb', $force = false) {
if ($this->open($mode, $force) === false) {
return false;
}
if ($this->lock !== null && flock($this->handle, LOCK_SH) === false) {
return false;
}
if (empty($tags)) {
$tags = implode($this->allowedTags);
} else {
if (is_array($tags)) {
$tags = implode($tags);
}
}
$data = '';
while (!feof($this->handle)) {
$data .= fgetss($this->handle, 4096, $tags);
}
$data = trim($data);
if ($this->lock !== null) {
flock($this->handle, LOCK_UN);
}
return $data;
}
/**
* Transfer array to cake structure
*
* @param data (usually with the first row as keys!)
* @param options
* - keys (defaults to first array content in data otherwise) (order is important!)
* - preserve_keys (do not slug and lowercase)
* @return array Result
*/
public function transfer($data, $options = []) {
$res = [];
if (empty($options['keys'])) {
$keys = array_shift($data);
} else {
$keys = $options['keys'];
}
foreach ($keys as $num => $key) {
if (empty($options['preserve_keys'])) {
$key = strtolower(Inflector::slug($key));
}
foreach ($data as $n => $val) {
$res[$n][$key] = $val[$num];
}
}
return $res;
}
/**
* Assert proper encoding
*
* @param array Input
* @return array Output
*/
protected function _encode(array $array) {
$convertedArray = [];
foreach ($array as $key => $value) {
if (!mb_check_encoding($key, 'UTF-8')) {
$key = utf8_encode($key);
}
if (is_array($value)) {
$value = $this->_encode($value);
} else {
if (!mb_check_encoding($value, 'UTF-8')) {
$value = utf8_encode($value);
}
$value = trim($value);
}
$convertedArray[$key] = $value;
}
return $convertedArray;
}
/**
* Check if a blob string contains the BOM.
* Useful for file_get_contents() + json_decode() that needs the BOM removed.
*
* @param string $content
* @return bool Success
*/
public static function hasByteOrderMark($content) {
return strpos($content, b"\xEF\xBB\xBF") === 0;
}
/**
* Remove BOM from a blob string if detected.
* Useful for file_get_contents() + json_decode() that needs the BOM removed.
*
* @param string $content
* @return string Cleaned content
*/
public static function removeByteOrderMark($content) {
return trim($content, b"\xEF\xBB\xBF");
}
}