335 lines
7.4 KiB
PHP
335 lines
7.4 KiB
PHP
<?php
|
|
/**
|
|
* This file is part of the League.csv library
|
|
*
|
|
* @license http://opensource.org/licenses/MIT
|
|
* @link https://github.com/thephpleague/csv/
|
|
* @version 8.2.3
|
|
* @package League.csv
|
|
*
|
|
* For the full copyright and license information, please view the LICENSE
|
|
* file that was distributed with this source code.
|
|
*/
|
|
namespace League\Csv\Modifier;
|
|
|
|
use InvalidArgumentException;
|
|
use Iterator;
|
|
use SplFileObject;
|
|
|
|
/**
|
|
* A Stream Iterator
|
|
*
|
|
* @package League.csv
|
|
* @since 8.2.0
|
|
* @internal used internally to iterate over a stream resource
|
|
*
|
|
*/
|
|
class StreamIterator implements Iterator
|
|
{
|
|
/**
|
|
* Stream pointer
|
|
*
|
|
* @var resource
|
|
*/
|
|
protected $stream;
|
|
|
|
/**
|
|
* Current iterator value
|
|
*
|
|
* @var mixed
|
|
*/
|
|
protected $current_line;
|
|
|
|
/**
|
|
* Current iterator key
|
|
*
|
|
* @var int
|
|
*/
|
|
protected $current_line_number;
|
|
|
|
/**
|
|
* Flags for the StreamIterator
|
|
*
|
|
* @var int
|
|
*/
|
|
protected $flags = 0;
|
|
|
|
/**
|
|
* the field delimiter (one character only)
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $delimiter = ',';
|
|
|
|
/**
|
|
* the field enclosure character (one character only)
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $enclosure = '"';
|
|
|
|
/**
|
|
* the field escape character (one character only)
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $escape = '\\';
|
|
|
|
/**
|
|
* New instance
|
|
*
|
|
* @param resource $stream stream type resource
|
|
*/
|
|
public function __construct($stream)
|
|
{
|
|
if (!is_resource($stream) || 'stream' !== get_resource_type($stream)) {
|
|
throw new InvalidArgumentException(sprintf(
|
|
'Expected resource to be a stream, received %s instead',
|
|
is_object($stream) ? get_class($stream) : gettype($stream)
|
|
));
|
|
}
|
|
|
|
$data = stream_get_meta_data($stream);
|
|
if (!$data['seekable']) {
|
|
throw new InvalidArgumentException('The stream must be seekable');
|
|
}
|
|
|
|
$this->stream = $stream;
|
|
}
|
|
|
|
/**
|
|
* Set CSV control
|
|
*
|
|
* @see http://php.net/manual/en/splfileobject.setcsvcontrol.php
|
|
*
|
|
* @param string $delimiter
|
|
* @param string $enclosure
|
|
* @param string $escape
|
|
*/
|
|
public function setCsvControl($delimiter = ',', $enclosure = '"', $escape = '\\')
|
|
{
|
|
$this->delimiter = $this->filterControl($delimiter, 'delimiter');
|
|
$this->enclosure = $this->filterControl($enclosure, 'enclosure');
|
|
$this->escape = $this->filterControl($escape, 'escape');
|
|
}
|
|
|
|
/**
|
|
* Filter Csv control character
|
|
*
|
|
* @param string $char Csv control character
|
|
* @param string $type Csv control character type
|
|
*
|
|
* @throws InvalidArgumentException If the Csv control character is not one character only.
|
|
*
|
|
* @return string
|
|
*/
|
|
private function filterControl($char, $type)
|
|
{
|
|
if (1 == strlen($char)) {
|
|
return $char;
|
|
}
|
|
|
|
throw new InvalidArgumentException(sprintf('The %s character must be a single character', $type));
|
|
}
|
|
|
|
/**
|
|
* Set Flags
|
|
*
|
|
* @see http://php.net/manual/en/splfileobject.setflags.php
|
|
*
|
|
* @param int $flags
|
|
*/
|
|
public function setFlags($flags)
|
|
{
|
|
if (false === filter_var($flags, FILTER_VALIDATE_INT, ['options' => ['min_range' => 0]])) {
|
|
throw new InvalidArgumentException('The flags must be a positive integer');
|
|
}
|
|
|
|
$this->flags = $flags;
|
|
}
|
|
|
|
/**
|
|
* Write a field array as a CSV line
|
|
*
|
|
* @see http://php.net/manual/en/splfileobject.fputcsv.php
|
|
*
|
|
* @param array $fields
|
|
* @param string $delimiter
|
|
* @param string $enclosure
|
|
* @param string $escape
|
|
*
|
|
* @return int
|
|
*/
|
|
public function fputcsv(array $fields, $delimiter = ',', $enclosure = '"', $escape = '\\')
|
|
{
|
|
return fputcsv(
|
|
$this->stream,
|
|
$fields,
|
|
$this->filterControl($delimiter, 'delimiter'),
|
|
$this->filterControl($enclosure, 'enclosure'),
|
|
$this->filterControl($escape, 'escape')
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Retrieves the current line of the file.
|
|
*
|
|
* @return mixed
|
|
*/
|
|
public function current()
|
|
{
|
|
if (false !== $this->current_line) {
|
|
return $this->current_line;
|
|
}
|
|
|
|
if (($this->flags & SplFileObject::READ_CSV) == SplFileObject::READ_CSV) {
|
|
$this->current_line = $this->getCurrentRecord();
|
|
|
|
return $this->current_line;
|
|
}
|
|
|
|
$this->current_line = $this->getCurrentLine();
|
|
|
|
return $this->current_line;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the current line as a CSV Record
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function getCurrentRecord()
|
|
{
|
|
do {
|
|
$ret = fgetcsv($this->stream, 0, $this->delimiter, $this->enclosure, $this->escape);
|
|
} while ($this->flags & SplFileObject::SKIP_EMPTY && $ret !== false && $ret[0] === null);
|
|
|
|
return $ret;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the current line as a string
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function getCurrentLine()
|
|
{
|
|
do {
|
|
$line = fgets($this->stream);
|
|
} while ($this->flags & SplFileObject::SKIP_EMPTY && $line !== false && rtrim($line, "\r\n") !== '');
|
|
|
|
return $line;
|
|
}
|
|
|
|
/**
|
|
* Get line number
|
|
*
|
|
* @return int
|
|
*/
|
|
public function key()
|
|
{
|
|
return $this->current_line_number;
|
|
}
|
|
|
|
/**
|
|
* Read next line
|
|
*/
|
|
public function next()
|
|
{
|
|
$this->current_line = false;
|
|
$this->current_line_number++;
|
|
}
|
|
|
|
/**
|
|
* Rewind the file to the first line
|
|
*/
|
|
public function rewind()
|
|
{
|
|
rewind($this->stream);
|
|
$this->current_line_number = 0;
|
|
$this->current_line = false;
|
|
if ($this->flags & SplFileObject::READ_AHEAD) {
|
|
$this->current();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Not at EOF
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function valid()
|
|
{
|
|
if ($this->flags & SplFileObject::READ_AHEAD) {
|
|
return $this->current() !== false;
|
|
}
|
|
|
|
return !feof($this->stream);
|
|
}
|
|
|
|
/**
|
|
* Gets line from file
|
|
*
|
|
* @see http://php.net/manual/en/splfileobject.fgets.php
|
|
*
|
|
* @return string
|
|
*/
|
|
public function fgets()
|
|
{
|
|
if (false !== $this->current_line) {
|
|
$this->next();
|
|
}
|
|
return $this->current_line = $this->getCurrentLine();
|
|
}
|
|
|
|
/**
|
|
* Output all remaining data on a file pointer
|
|
*
|
|
* @see http://php.net/manual/en/splfileobject.fpatssthru.php
|
|
*
|
|
* @return int
|
|
*/
|
|
public function fpassthru()
|
|
{
|
|
return fpassthru($this->stream);
|
|
}
|
|
|
|
/**
|
|
* Seek to a position
|
|
*
|
|
* @see http://php.net/manual/en/splfileobject.fseek.php
|
|
*
|
|
* @param int $offset
|
|
* @param int $whence
|
|
*
|
|
* @return int
|
|
*/
|
|
public function fseek($offset, $whence = SEEK_SET)
|
|
{
|
|
return fseek($this->stream, $offset, $whence);
|
|
}
|
|
|
|
/**
|
|
* Write to stream
|
|
*
|
|
* @see http://php.net/manual/en/splfileobject.fwrite.php
|
|
*
|
|
* @param string $str
|
|
* @param int $length
|
|
*
|
|
* @return int
|
|
*/
|
|
public function fwrite($str, $length = 0)
|
|
{
|
|
return fwrite($this->stream, $str, $length);
|
|
}
|
|
|
|
/**
|
|
* close the file pointer
|
|
*/
|
|
public function __destruct()
|
|
{
|
|
$this->stream = null;
|
|
}
|
|
}
|