172 lines
5.3 KiB
PHP
172 lines
5.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace OpenSpout\Reader;
|
|
|
|
use OpenSpout\Common\Exception\IOException;
|
|
use OpenSpout\Reader\Exception\ReaderException;
|
|
use OpenSpout\Reader\Exception\ReaderNotOpenedException;
|
|
|
|
/**
|
|
* @template T of SheetIteratorInterface
|
|
*
|
|
* @implements ReaderInterface<T>
|
|
*/
|
|
abstract class AbstractReader implements ReaderInterface
|
|
{
|
|
/** @var bool Indicates whether the stream is currently open */
|
|
private bool $isStreamOpened = false;
|
|
|
|
/**
|
|
* Prepares the reader to read the given file. It also makes sure
|
|
* that the file exists and is readable.
|
|
*
|
|
* @param string $filePath Path of the file to be read
|
|
*
|
|
* @throws IOException If the file at the given path does not exist, is not readable or is corrupted
|
|
*/
|
|
public function open(string $filePath): void
|
|
{
|
|
if ($this->isStreamWrapper($filePath) && (!$this->doesSupportStreamWrapper() || !$this->isSupportedStreamWrapper($filePath))) {
|
|
throw new IOException("Could not open {$filePath} for reading! Stream wrapper used is not supported for this type of file.");
|
|
}
|
|
|
|
if (!$this->isPhpStream($filePath)) {
|
|
// we skip the checks if the provided file path points to a PHP stream
|
|
if (!file_exists($filePath)) {
|
|
throw new IOException("Could not open {$filePath} for reading! File does not exist.");
|
|
}
|
|
if (!is_readable($filePath)) {
|
|
throw new IOException("Could not open {$filePath} for reading! File is not readable.");
|
|
}
|
|
}
|
|
|
|
try {
|
|
$fileRealPath = $this->getFileRealPath($filePath);
|
|
$this->openReader($fileRealPath);
|
|
$this->isStreamOpened = true;
|
|
} catch (ReaderException $exception) {
|
|
throw new IOException(
|
|
"Could not open {$filePath} for reading!",
|
|
0,
|
|
$exception
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Closes the reader, preventing any additional reading.
|
|
*/
|
|
final public function close(): void
|
|
{
|
|
if ($this->isStreamOpened) {
|
|
$this->closeReader();
|
|
|
|
$this->isStreamOpened = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns whether stream wrappers are supported.
|
|
*/
|
|
abstract protected function doesSupportStreamWrapper(): bool;
|
|
|
|
/**
|
|
* Opens the file at the given file path to make it ready to be read.
|
|
*
|
|
* @param string $filePath Path of the file to be read
|
|
*/
|
|
abstract protected function openReader(string $filePath): void;
|
|
|
|
/**
|
|
* Closes the reader. To be used after reading the file.
|
|
*/
|
|
abstract protected function closeReader(): void;
|
|
|
|
final protected function ensureStreamOpened(): void
|
|
{
|
|
if (!$this->isStreamOpened) {
|
|
throw new ReaderNotOpenedException('Reader should be opened first.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the real path of the given path.
|
|
* If the given path is a valid stream wrapper, returns the path unchanged.
|
|
*/
|
|
private function getFileRealPath(string $filePath): string
|
|
{
|
|
if ($this->isSupportedStreamWrapper($filePath)) {
|
|
return $filePath;
|
|
}
|
|
|
|
// Need to use realpath to fix "Can't open file" on some Windows setup
|
|
$realpath = realpath($filePath);
|
|
\assert(false !== $realpath);
|
|
|
|
return $realpath;
|
|
}
|
|
|
|
/**
|
|
* Returns the scheme of the custom stream wrapper, if the path indicates a stream wrapper is used.
|
|
* For example, php://temp => php, s3://path/to/file => s3...
|
|
*
|
|
* @param string $filePath Path of the file to be read
|
|
*
|
|
* @return null|string The stream wrapper scheme or NULL if not a stream wrapper
|
|
*/
|
|
private function getStreamWrapperScheme(string $filePath): ?string
|
|
{
|
|
$streamScheme = null;
|
|
if (1 === preg_match('/^(\w+):\/\//', $filePath, $matches)) {
|
|
$streamScheme = $matches[1];
|
|
}
|
|
|
|
return $streamScheme;
|
|
}
|
|
|
|
/**
|
|
* Checks if the given path is an unsupported stream wrapper
|
|
* (like local path, php://temp, mystream://foo/bar...).
|
|
*
|
|
* @param string $filePath Path of the file to be read
|
|
*
|
|
* @return bool Whether the given path is an unsupported stream wrapper
|
|
*/
|
|
private function isStreamWrapper(string $filePath): bool
|
|
{
|
|
return null !== $this->getStreamWrapperScheme($filePath);
|
|
}
|
|
|
|
/**
|
|
* Checks if the given path is an supported stream wrapper
|
|
* (like php://temp, mystream://foo/bar...).
|
|
* If the given path is a local path, returns true.
|
|
*
|
|
* @param string $filePath Path of the file to be read
|
|
*
|
|
* @return bool Whether the given path is an supported stream wrapper
|
|
*/
|
|
private function isSupportedStreamWrapper(string $filePath): bool
|
|
{
|
|
$streamScheme = $this->getStreamWrapperScheme($filePath);
|
|
|
|
return null === $streamScheme || \in_array($streamScheme, stream_get_wrappers(), true);
|
|
}
|
|
|
|
/**
|
|
* Checks if a path is a PHP stream (like php://output, php://memory, ...).
|
|
*
|
|
* @param string $filePath Path of the file to be read
|
|
*
|
|
* @return bool Whether the given path maps to a PHP stream
|
|
*/
|
|
private function isPhpStream(string $filePath): bool
|
|
{
|
|
$streamScheme = $this->getStreamWrapperScheme($filePath);
|
|
|
|
return 'php' === $streamScheme;
|
|
}
|
|
}
|