Skip to content

Refactoring file handling with minimum backward compatibility breaks #116

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
191 changes: 191 additions & 0 deletions Filesystem/AbstractCommonResource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
<?php declare(strict_types=1);

namespace CleverAge\ProcessBundle\Filesystem;

/**
* Common methods for readers using resources
*/
abstract class AbstractCommonResource implements FileStreamInterface
{
/** @var resource */
protected $handler;

/** @var int|null */
protected $lineCount;

/** @var int */
protected $lineNumber = 1;

/** @var bool */
protected $closed;

/** @var bool */
protected $seekCalled = false;

/**
* @param resource $resource
*
* @throws \UnexpectedValueException
*/
public function __construct($resource)
{
if (!\is_resource($resource)) {
$type = \gettype($resource);
throw new \UnexpectedValueException("Resource argument must be a resource, '{$type}' given");
}
$this->handler = $resource;
}

/**
* @return resource
*/
public function getHandler()
{
return $this->handler;
}

/**
* Warning! This method will rewind the file to the beginning before and after counting the lines!
*
* @throws \RuntimeException
*
* @return int
*/
public function getLineCount(): int
{
if (null === $this->lineCount) {
$this->rewind();
$line = 0;
while (!$this->isEndOfFile()) {
++$line;
$this->readRaw();
}
$this->rewind();

$this->lineCount = $line;
}

return $this->lineCount;
}

/**
* {@inheritDoc}
*/
public function getLineNumber(): int
{
if ($this->seekCalled) {
throw new \LogicException('Cannot get current line number after calling "seek": the line number is lost');
}

return $this->lineNumber;
}

/**
* @throws \RuntimeException
*
* @return bool
*/
public function isEndOfFile(): bool
{
$this->assertOpened();

return feof($this->handler);
}

/**
* Warning, this function will return exactly the same value as the internal function used by the subsystem.
*
* @param null|int $length
*
* @throws \RuntimeException
*
* @return mixed
*/
abstract public function readRaw($length = null);

/**
* This methods rewinds the file to the first line of data, skipping the headers.
*
* @throws \RuntimeException
*/
public function rewind(): void
{
$this->assertOpened();
if (!rewind($this->handler)) {
throw new \RuntimeException("Unable to rewind '{$this->getResourceName()}'");
}
$this->lineNumber = 1;
}

/**
* @throws \RuntimeException
*
* @return int
*/
public function tell(): int
{
$this->assertOpened();

return ftell($this->handler);
}

/**
* @param int $offset
*
* @throws \RuntimeException
*
* @return int
*/
public function seek($offset): int
{
$this->assertOpened();
$this->seekCalled = true;

return fseek($this->handler, $offset);
}

/**
* @return bool
*/
public function close(): bool
{
if ($this->closed) {
return true;
}

$this->closed = fclose($this->handler);

return $this->closed;
}

/**
* @return bool
*/
public function isClosed(): bool
{
return $this->closed;
}

/**
* Closes the resource when the object is destroyed.
*/
public function __destruct()
{
$this->close();
}

/**
* @throws \RuntimeException
*/
protected function assertOpened(): void
{
if ($this->closed) {
throw new \RuntimeException("{$this->getResourceName()} was closed earlier");
}
}

/**
* @return string
*/
abstract protected function getResourceName(): string;
}
14 changes: 3 additions & 11 deletions Filesystem/CsvFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
*/
class CsvFile extends CsvResource
{
use FileHelperTrait;

/** @var string */
protected $filePath;

Expand All @@ -41,18 +43,8 @@ public function __construct(
$mode = 'rb'
) {
$this->filePath = $filePath;
$resource = $this->openResource($filePath, $mode);

if (!\in_array($filePath, ['php://stdin', 'php://stdout', 'php://stderr'])) {
$dirname = \dirname($this->filePath);
if (!@mkdir($dirname, 0755, true) && !is_dir($dirname)) {
throw new \RuntimeException(sprintf('Directory "%s" was not created', $dirname));
}
}

$resource = fopen($filePath, $mode);
if (false === $resource) {
throw new \UnexpectedValueException("Unable to open file: '{$filePath}' in {$mode} mode");
}
// All modes allowing file reading, binary safe modes are handled by stripping out the b during test
$readAllowedModes = ['r', 'r+', 'w+', 'a+', 'x+', 'c+'];
if (null === $headers && !\in_array(str_replace('b', '', $mode), $readAllowedModes, true)) {
Expand Down
Loading