Tweaks to Input File Validation (#2217)

* Tweaks to Input File Validation

This started as a response to issue #1718, for which it is a partial (not complete) solution. The following changes are made:
- canRead can currently throw an exception. This seems wrong. It should just return true/false.
- Breaking change of sorts. When AssertFile encounters a non-existent or unreadable file, it throws InvalidArgumentException. This does not make sense. I have changed it to throw PhpSpreadsheet/Reader/Exception.
- Since the previous bullet item required changing of most of the Reader files anyhow, this is a good time to add explicit typing for canRead in the function signature rather than the DocBlock. Since all the canRead functions inherit from an abstract version in IReader, they all have to be changed simulatneously. Except for Xlsx and Ods, most of the Reader files are otherwise unchanged.
- AssertFile is changed to add an optional "zip member" parameter. It will check for the existence of an appropriate member in what is supposed to be a zip file. It is used by Xlsx and Ods.
- Verifying that a given file is a valid zip ought to be a feature of ZipArchive. Thanks to a particularly nasty bug in php/libzip (see https://bugs.php.net/bug.php?id=81222), it is unsafe to attempt to open a zero-length file as a zip archive. There is a solution, but it does not apply to all the PHP releases which we support, and isn't even necessarily supported on all the point versions of the PHP versions which we do support. I have coded up a manual test for "valid zip", with a comment pointing to the spec.
- In theory, tests now cover 100% of the code in Shared/File. In practice ... One of the tests require that chmod works properly, which is not quite true on Windows systems, so that test is skipped on Windows. Another test requires that php.ini uses a non-default value for upload_temp_dir (can't be overridden in application code), which is probably not the case when Github runs the unit tests, so that test is skipped when appropriate. I have run tests for both on systems where they are not skipped.

* Update File.php

* Scrutinizer Timeout

It's not actually timing out, it's just waiting for something to finish that finished ages ago. Making a meaningless comment change in hopes that will clear the jam. Not particularly hopeful.
This commit is contained in:
oleibman 2021-07-24 20:44:04 -07:00 committed by GitHub
parent 3c5750bddc
commit 51163713c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 467 additions and 161 deletions

View File

@ -3625,21 +3625,6 @@ parameters:
count: 1
path: src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer.php
-
message: "#^Strict comparison using \\=\\=\\= between string\\|false and null will always evaluate to false\\.$#"
count: 1
path: src/PhpSpreadsheet/Shared/File.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\File\\:\\:realpath\\(\\) should return string but returns string\\|false\\.$#"
count: 1
path: src/PhpSpreadsheet/Shared/File.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\File\\:\\:sysGetTempDir\\(\\) should return string but returns string\\|false\\.$#"
count: 2
path: src/PhpSpreadsheet/Shared/File.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Font\\:\\:\\$autoSizeMethods has no typehint specified\\.$#"
count: 1

View File

@ -1,6 +1,7 @@
<?php
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
require __DIR__ . '/../Header.php';
@ -9,6 +10,6 @@ $helper->log('Loading file ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' u
try {
$spreadsheet = IOFactory::load($inputFileName);
} catch (InvalidArgumentException $e) {
} catch (ReaderException $e) {
$helper->log('Error loading file "' . pathinfo($inputFileName, PATHINFO_BASENAME) . '": ' . $e->getMessage());
}

View File

@ -2,9 +2,9 @@
namespace PhpOffice\PhpSpreadsheet\Reader;
use InvalidArgumentException;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Reader\Csv\Delimiter;
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
@ -35,7 +35,7 @@ class Csv extends BaseReader
private $inputEncoding = 'UTF-8';
/**
* Fallback encoding if 'guess' strikes out.
* Fallback encoding if guess strikes out.
*
* @var string
*/
@ -410,17 +410,13 @@ class Csv extends BaseReader
/**
* Can the current IReader read the file?
*
* @param string $pFilename
*
* @return bool
*/
public function canRead($pFilename)
public function canRead(string $pFilename): bool
{
// Check if file exists
try {
$this->openFile($pFilename);
} catch (InvalidArgumentException $e) {
} catch (ReaderException $e) {
return false;
}

View File

@ -77,18 +77,12 @@ class Gnumeric extends BaseReader
/**
* Can the current IReader read the file?
*
* @param string $pFilename
*
* @return bool
*/
public function canRead($pFilename)
public function canRead(string $pFilename): bool
{
File::assertFile($pFilename);
// Check if gzlib functions are available
$data = '';
if (function_exists('gzread')) {
// Check if gzlib functions are available
if (File::testFileNoThrow($pFilename) && function_exists('gzread')) {
// Read signature data (first 3 bytes)
$fh = fopen($pFilename, 'rb');
if ($fh !== false) {

View File

@ -137,12 +137,8 @@ class Html extends BaseReader
/**
* Validate that the current file is an HTML file.
*
* @param string $pFilename
*
* @return bool
*/
public function canRead($pFilename)
public function canRead(string $pFilename): bool
{
// Check if file exists
try {

View File

@ -13,12 +13,8 @@ interface IReader
/**
* Can the current IReader read the file?
*
* @param string $pFilename
*
* @return bool
*/
public function canRead($pFilename);
public function canRead(string $pFilename): bool;
/**
* Read data only?

View File

@ -10,7 +10,6 @@ use DOMNode;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
use PhpOffice\PhpSpreadsheet\Reader\Ods\AutoFilter;
use PhpOffice\PhpSpreadsheet\Reader\Ods\DefinedNames;
use PhpOffice\PhpSpreadsheet\Reader\Ods\PageSettings;
@ -28,6 +27,8 @@ use ZipArchive;
class Ods extends BaseReader
{
const INITIAL_FILE = 'content.xml';
/**
* Create a new Ods Reader instance.
*/
@ -39,46 +40,42 @@ class Ods extends BaseReader
/**
* Can the current IReader read the file?
*
* @param string $pFilename
*
* @return bool
*/
public function canRead($pFilename)
public function canRead(string $pFilename): bool
{
File::assertFile($pFilename);
$mimeType = 'UNKNOWN';
// Load file
$zip = new ZipArchive();
if ($zip->open($pFilename) === true) {
// check if it is an OOXML archive
$stat = $zip->statName('mimetype');
if ($stat && ($stat['size'] <= 255)) {
$mimeType = $zip->getFromName($stat['name']);
} elseif ($zip->statName('META-INF/manifest.xml')) {
$xml = simplexml_load_string(
$this->securityScanner->scan($zip->getFromName('META-INF/manifest.xml')),
'SimpleXMLElement',
Settings::getLibXmlLoaderOptions()
);
$namespacesContent = $xml->getNamespaces(true);
if (isset($namespacesContent['manifest'])) {
$manifest = $xml->children($namespacesContent['manifest']);
foreach ($manifest as $manifestDataSet) {
$manifestAttributes = $manifestDataSet->attributes($namespacesContent['manifest']);
if ($manifestAttributes->{'full-path'} == '/') {
$mimeType = (string) $manifestAttributes->{'media-type'};
if (File::testFileNoThrow($pFilename)) {
$zip = new ZipArchive();
if ($zip->open($pFilename) === true) {
// check if it is an OOXML archive
$stat = $zip->statName('mimetype');
if ($stat && ($stat['size'] <= 255)) {
$mimeType = $zip->getFromName($stat['name']);
} elseif ($zip->statName('META-INF/manifest.xml')) {
$xml = simplexml_load_string(
$this->securityScanner->scan($zip->getFromName('META-INF/manifest.xml')),
'SimpleXMLElement',
Settings::getLibXmlLoaderOptions()
);
$namespacesContent = $xml->getNamespaces(true);
if (isset($namespacesContent['manifest'])) {
$manifest = $xml->children($namespacesContent['manifest']);
foreach ($manifest as $manifestDataSet) {
$manifestAttributes = $manifestDataSet->attributes($namespacesContent['manifest']);
if ($manifestAttributes->{'full-path'} == '/') {
$mimeType = (string) $manifestAttributes->{'media-type'};
break;
break;
}
}
}
}
}
$zip->close();
$zip->close();
}
}
return $mimeType === 'application/vnd.oasis.opendocument.spreadsheet';
@ -93,18 +90,13 @@ class Ods extends BaseReader
*/
public function listWorksheetNames($pFilename)
{
File::assertFile($pFilename);
$zip = new ZipArchive();
if ($zip->open($pFilename) !== true) {
throw new ReaderException('Could not open ' . $pFilename . ' for reading! Error opening file.');
}
File::assertFile($pFilename, self::INITIAL_FILE);
$worksheetNames = [];
$xml = new XMLReader();
$xml->xml(
$this->securityScanner->scanFile('zip://' . realpath($pFilename) . '#content.xml'),
$this->securityScanner->scanFile('zip://' . realpath($pFilename) . '#' . self::INITIAL_FILE),
null,
Settings::getLibXmlLoaderOptions()
);
@ -145,18 +137,13 @@ class Ods extends BaseReader
*/
public function listWorksheetInfo($pFilename)
{
File::assertFile($pFilename);
File::assertFile($pFilename, self::INITIAL_FILE);
$worksheetInfo = [];
$zip = new ZipArchive();
if ($zip->open($pFilename) !== true) {
throw new ReaderException('Could not open ' . $pFilename . ' for reading! Error opening file.');
}
$xml = new XMLReader();
$xml->xml(
$this->securityScanner->scanFile('zip://' . realpath($pFilename) . '#content.xml'),
$this->securityScanner->scanFile('zip://' . realpath($pFilename) . '#' . self::INITIAL_FILE),
null,
Settings::getLibXmlLoaderOptions()
);
@ -253,12 +240,10 @@ class Ods extends BaseReader
*/
public function loadIntoExisting($pFilename, Spreadsheet $spreadsheet)
{
File::assertFile($pFilename);
File::assertFile($pFilename, self::INITIAL_FILE);
$zip = new ZipArchive();
if ($zip->open($pFilename) !== true) {
throw new Exception("Could not open {$pFilename} for reading! Error opening file.");
}
$zip->open($pFilename);
// Meta
@ -289,7 +274,7 @@ class Ods extends BaseReader
$dom = new DOMDocument('1.01', 'UTF-8');
$dom->loadXML(
$this->securityScanner->scan($zip->getFromName('content.xml')),
$this->securityScanner->scan($zip->getFromName(self::INITIAL_FILE)),
Settings::getLibXmlLoaderOptions()
);

View File

@ -2,7 +2,6 @@
namespace PhpOffice\PhpSpreadsheet\Reader;
use InvalidArgumentException;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
@ -65,16 +64,12 @@ class Slk extends BaseReader
/**
* Validate that the current file is a SYLK file.
*
* @param string $pFilename
*
* @return bool
*/
public function canRead($pFilename)
public function canRead(string $pFilename): bool
{
try {
$this->openFile($pFilename);
} catch (InvalidArgumentException $e) {
} catch (ReaderException $e) {
return false;
}

View File

@ -419,14 +419,12 @@ class Xls extends BaseReader
/**
* Can the current IReader read the file?
*
* @param string $pFilename
*
* @return bool
*/
public function canRead($pFilename)
public function canRead(string $pFilename): bool
{
File::assertFile($pFilename);
if (!File::testFileNoThrow($pFilename)) {
return false;
}
try {
// Use ParseXL for the hard work.

View File

@ -41,6 +41,8 @@ use ZipArchive;
class Xlsx extends BaseReader
{
const INITIAL_FILE = '_rels/.rels';
/**
* ReferenceHelper instance.
*
@ -72,14 +74,12 @@ class Xlsx extends BaseReader
/**
* Can the current IReader read the file?
*
* @param string $pFilename
*
* @return bool
*/
public function canRead($pFilename)
public function canRead(string $pFilename): bool
{
File::assertFile($pFilename);
if (!File::testFileNoThrow($pFilename, self::INITIAL_FILE)) {
return false;
}
$result = false;
$this->zip = $zip = new ZipArchive();
@ -168,7 +168,7 @@ class Xlsx extends BaseReader
*/
public function listWorksheetNames($pFilename)
{
File::assertFile($pFilename);
File::assertFile($pFilename, self::INITIAL_FILE);
$worksheetNames = [];
@ -176,7 +176,7 @@ class Xlsx extends BaseReader
$zip->open($pFilename);
// The files we're looking at here are small enough that simpleXML is more efficient than XMLReader
$rels = $this->loadZip('_rels/.rels', Namespaces::RELATIONSHIPS);
$rels = $this->loadZip(self::INITIAL_FILE, Namespaces::RELATIONSHIPS);
foreach ($rels->Relationship as $relx) {
$rel = self::getAttributes($relx);
$relType = (string) $rel['Type'];
@ -207,14 +207,14 @@ class Xlsx extends BaseReader
*/
public function listWorksheetInfo($pFilename)
{
File::assertFile($pFilename);
File::assertFile($pFilename, self::INITIAL_FILE);
$worksheetInfo = [];
$this->zip = $zip = new ZipArchive();
$zip->open($pFilename);
$rels = $this->loadZip('_rels/.rels', Namespaces::RELATIONSHIPS);
$rels = $this->loadZip(self::INITIAL_FILE, Namespaces::RELATIONSHIPS);
foreach ($rels->Relationship as $relx) {
$rel = self::getAttributes($relx);
$relType = (string) $rel['Type'];
@ -389,7 +389,7 @@ class Xlsx extends BaseReader
*/
public function load(string $pFilename, int $flags = 0): Spreadsheet
{
File::assertFile($pFilename);
File::assertFile($pFilename, self::INITIAL_FILE);
$this->processFlags($flags);
// Initialisations
@ -444,7 +444,7 @@ class Xlsx extends BaseReader
}
}
$rels = $this->loadZip('_rels/.rels', Namespaces::RELATIONSHIPS);
$rels = $this->loadZip(self::INITIAL_FILE, Namespaces::RELATIONSHIPS);
$propertyReader = new PropertyReader($this->securityScanner, $excel->getProperties());
foreach ($rels->Relationship as $relx) {
@ -1957,7 +1957,7 @@ class Xlsx extends BaseReader
$xmlNamespaceBase = '';
// check if it is an OOXML archive
$rels = $this->loadZip('_rels/.rels');
$rels = $this->loadZip(self::INITIAL_FILE);
foreach ($rels->children(Namespaces::RELATIONSHIPS)->Relationship as $rel) {
$rel = self::getAttributes($rel);
switch ($rel['Type']) {

View File

@ -53,12 +53,8 @@ class Xml extends BaseReader
/**
* Can the current IReader read the file?
*
* @param string $pFilename
*
* @return bool
*/
public function canRead($pFilename)
public function canRead(string $pFilename): bool
{
// Office xmlns:o="urn:schemas-microsoft-com:office:office"
// Excel xmlns:x="urn:schemas-microsoft-com:office:excel"

View File

@ -2,8 +2,8 @@
namespace PhpOffice\PhpSpreadsheet\Shared;
use InvalidArgumentException;
use PhpOffice\PhpSpreadsheet\Exception;
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
use ZipArchive;
class File
@ -17,47 +17,57 @@ class File
/**
* Set the flag indicating whether the File Upload Temp directory should be used for temporary files.
*
* @param bool $useUploadTempDir Use File Upload Temporary directory (true or false)
*/
public static function setUseUploadTempDirectory($useUploadTempDir): void
public static function setUseUploadTempDirectory(bool $useUploadTempDir): void
{
self::$useUploadTempDirectory = (bool) $useUploadTempDir;
}
/**
* Get the flag indicating whether the File Upload Temp directory should be used for temporary files.
*
* @return bool Use File Upload Temporary directory (true or false)
*/
public static function getUseUploadTempDirectory()
public static function getUseUploadTempDirectory(): bool
{
return self::$useUploadTempDirectory;
}
// https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
// Section 4.3.7
// Looks like there might be endian-ness considerations
private const ZIP_FIRST_4 = [
"\x50\x4b\x03\x04", // what it looks like on my system
"\x04\x03\x4b\x50", // what it says in documentation
];
private static function validateZipFirst4(string $zipFile): bool
{
$contents = @file_get_contents($zipFile, false, null, 0, 4);
return in_array($contents, self::ZIP_FIRST_4, true);
}
/**
* Verify if a file exists.
*
* @param string $pFilename Filename
*
* @return bool
*/
public static function fileExists($pFilename)
public static function fileExists(string $pFilename): bool
{
// Sick construction, but it seems that
// file_exists returns strange values when
// doing the original file_exists on ZIP archives...
if (strtolower(substr($pFilename, 0, 3)) == 'zip') {
if (strtolower(substr($pFilename, 0, 6)) == 'zip://') {
// Open ZIP file and verify if the file exists
$zipFile = substr($pFilename, 6, strpos($pFilename, '#') - 6);
$archiveFile = substr($pFilename, strpos($pFilename, '#') + 1);
$zip = new ZipArchive();
if ($zip->open($zipFile) === true) {
$returnValue = ($zip->getFromName($archiveFile) !== false);
$zip->close();
if (self::validateZipFirst4($zipFile)) {
$zip = new ZipArchive();
$res = $zip->open($zipFile, ZipArchive::CHECKCONS);
if ($res === true) {
$returnValue = ($zip->getFromName($archiveFile) !== false);
$zip->close();
return $returnValue;
return $returnValue;
}
}
return false;
@ -68,23 +78,19 @@ class File
/**
* Returns canonicalized absolute pathname, also for ZIP archives.
*
* @param string $pFilename
*
* @return string
*/
public static function realpath($pFilename)
public static function realpath(string $pFilename): string
{
// Returnvalue
$returnValue = '';
// Try using realpath()
if (file_exists($pFilename)) {
$returnValue = realpath($pFilename);
$returnValue = realpath($pFilename) ?: '';
}
// Found something?
if ($returnValue == '' || ($returnValue === null)) {
if ($returnValue === '') {
$pathArray = explode('/', $pFilename);
while (in_array('..', $pathArray) && $pathArray[0] != '..') {
$iMax = count($pathArray);
@ -105,24 +111,23 @@ class File
/**
* Get the systems temporary directory.
*
* @return string
*/
public static function sysGetTempDir()
public static function sysGetTempDir(): string
{
$path = sys_get_temp_dir();
if (self::$useUploadTempDirectory) {
// use upload-directory when defined to allow running on environments having very restricted
// open_basedir configs
if (ini_get('upload_tmp_dir') !== false) {
if ($temp = ini_get('upload_tmp_dir')) {
if (file_exists($temp)) {
return realpath($temp);
$path = $temp;
}
}
}
}
return realpath(sys_get_temp_dir());
return realpath($path) ?: '';
}
public static function temporaryFilename(): string
@ -137,17 +142,45 @@ class File
/**
* Assert that given path is an existing file and is readable, otherwise throw exception.
*
* @param string $filename
*/
public static function assertFile($filename): void
public static function assertFile(string $filename, string $zipMember = ''): void
{
if (!is_file($filename)) {
throw new InvalidArgumentException('File "' . $filename . '" does not exist.');
throw new ReaderException('File "' . $filename . '" does not exist.');
}
if (!is_readable($filename)) {
throw new InvalidArgumentException('Could not open "' . $filename . '" for reading.');
throw new ReaderException('Could not open "' . $filename . '" for reading.');
}
if ($zipMember !== '') {
$zipfile = "zip://$filename#$zipMember";
if (!self::fileExists($zipfile)) {
throw new ReaderException("Could not find zip member $zipfile");
}
}
}
/**
* Same as assertFile, except return true/false and don't throw Exception.
*/
public static function testFileNoThrow(string $filename, string $zipMember = ''): bool
{
if (!is_file($filename)) {
return false;
}
if (!is_readable($filename)) {
return false;
}
if ($zipMember !== '') {
$zipfile = "zip://$filename#$zipMember";
if (!self::fileExists($zipfile)) {
return false;
}
}
return true;
}
}

View File

@ -2,9 +2,9 @@
namespace PhpOffice\PhpSpreadsheetTests;
use InvalidArgumentException;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Reader;
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer;
use PHPUnit\Framework\TestCase;
@ -136,14 +136,14 @@ class IOFactoryTest extends TestCase
public function testIdentifyNonExistingFileThrowException(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectException(ReaderException::class);
IOFactory::identify('/non/existing/file');
}
public function testIdentifyExistingDirectoryThrowExceptions(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectException(ReaderException::class);
IOFactory::identify('.');
}

View File

@ -0,0 +1,52 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Reader\Ods;
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
use PhpOffice\PhpSpreadsheet\Reader\Ods;
use PhpOffice\PhpSpreadsheet\Shared\File;
use PHPUnit\Framework\TestCase;
class EmptyFileTest extends TestCase
{
/** @var string */
private $tempfile = '';
protected function tearDown(): void
{
if ($this->tempfile !== '') {
unlink($this->tempfile);
$this->tempfile = '';
}
}
public function testEmptyFileLoad(): void
{
$this->expectException(ReaderException::class);
$this->expectExceptionMessage('Could not find zip member');
$this->tempfile = $temp = File::temporaryFileName();
file_put_contents($temp, '');
$reader = new Ods();
$reader->load($temp);
}
public function testEmptyFileNames(): void
{
$this->expectException(ReaderException::class);
$this->expectExceptionMessage('Could not find zip member');
$this->tempfile = $temp = File::temporaryFileName();
file_put_contents($temp, '');
$reader = new Ods();
$reader->listWorksheetNames($temp);
}
public function testEmptyInfo(): void
{
$this->expectException(ReaderException::class);
$this->expectExceptionMessage('Could not find zip member');
$this->tempfile = $temp = File::temporaryFileName();
file_put_contents($temp, '');
$reader = new Ods();
$reader->listWorksheetInfo($temp);
}
}

View File

@ -0,0 +1,64 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Reader\Ods;
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
use PhpOffice\PhpSpreadsheet\Reader\Ods;
use PHPUnit\Framework\TestCase;
class InvalidFileTest extends TestCase
{
public function testInvalidFileLoad(): void
{
$this->expectException(ReaderException::class);
$this->expectExceptionMessage('Could not find zip member');
$temp = __FILE__;
$reader = new Ods();
$reader->load($temp);
}
public function testInvalidFileNames(): void
{
$this->expectException(ReaderException::class);
$this->expectExceptionMessage('Could not find zip member');
$temp = __FILE__;
$reader = new Ods();
$reader->listWorksheetNames($temp);
}
public function testInvalidInfo(): void
{
$this->expectException(ReaderException::class);
$this->expectExceptionMessage('Could not find zip member');
$temp = __FILE__;
$reader = new Ods();
$reader->listWorksheetInfo($temp);
}
public function testXlsxFileLoad(): void
{
$this->expectException(ReaderException::class);
$this->expectExceptionMessage('Could not find zip member');
$temp = 'samples/templates/26template.xlsx';
$reader = new Ods();
$reader->load($temp);
}
public function testXlsxFileNames(): void
{
$this->expectException(ReaderException::class);
$this->expectExceptionMessage('Could not find zip member');
$temp = 'samples/templates/26template.xlsx';
$reader = new Ods();
$reader->listWorksheetNames($temp);
}
public function testXlsxInfo(): void
{
$this->expectException(ReaderException::class);
$this->expectExceptionMessage('Could not find zip member');
$temp = 'samples/templates/26template.xlsx';
$reader = new Ods();
$reader->listWorksheetInfo($temp);
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xls;
use PhpOffice\PhpSpreadsheet\Reader\Xls;
use PhpOffice\PhpSpreadsheet\Shared\File;
use PHPUnit\Framework\TestCase;
class NonExistentFileTest extends TestCase
{
public function testNonExistentFile(): void
{
$temp = File::temporaryFileName();
file_put_contents($temp, '');
unlink($temp);
$reader = new Xls();
self::assertFalse($reader->canRead($temp));
}
}

View File

@ -0,0 +1,52 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Shared\File;
use PHPUnit\Framework\TestCase;
class EmptyFileTest extends TestCase
{
/** @var string */
private $tempfile = '';
protected function tearDown(): void
{
if ($this->tempfile !== '') {
unlink($this->tempfile);
$this->tempfile = '';
}
}
public function testEmptyFileLoad(): void
{
$this->expectException(ReaderException::class);
$this->expectExceptionMessage('Could not find zip member');
$this->tempfile = $temp = File::temporaryFileName();
file_put_contents($temp, '');
$reader = new Xlsx();
$reader->load($temp);
}
public function testEmptyFileNames(): void
{
$this->expectException(ReaderException::class);
$this->expectExceptionMessage('Could not find zip member');
$this->tempfile = $temp = File::temporaryFileName();
file_put_contents($temp, '');
$reader = new Xlsx();
$reader->listWorksheetNames($temp);
}
public function testEmptyInfo(): void
{
$this->expectException(ReaderException::class);
$this->expectExceptionMessage('Could not find zip member');
$this->tempfile = $temp = File::temporaryFileName();
file_put_contents($temp, '');
$reader = new Xlsx();
$reader->listWorksheetInfo($temp);
}
}

View File

@ -0,0 +1,64 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PHPUnit\Framework\TestCase;
class InvalidFileTest extends TestCase
{
public function testInvalidFileLoad(): void
{
$this->expectException(ReaderException::class);
$this->expectExceptionMessage('Could not find zip member');
$temp = __FILE__;
$reader = new Xlsx();
$reader->load($temp);
}
public function testInvalidFileNames(): void
{
$this->expectException(ReaderException::class);
$this->expectExceptionMessage('Could not find zip member');
$temp = __FILE__;
$reader = new Xlsx();
$reader->listWorksheetNames($temp);
}
public function testInvalidInfo(): void
{
$this->expectException(ReaderException::class);
$this->expectExceptionMessage('Could not find zip member');
$temp = __FILE__;
$reader = new Xlsx();
$reader->listWorksheetInfo($temp);
}
public function testOdsFileLoad(): void
{
$this->expectException(ReaderException::class);
$this->expectExceptionMessage('Could not find zip member');
$temp = 'samples/templates/OOCalcTest.ods';
$reader = new Xlsx();
$reader->load($temp);
}
public function testOdsFileNames(): void
{
$this->expectException(ReaderException::class);
$this->expectExceptionMessage('Could not find zip member');
$temp = 'samples/templates/OOCalcTest.ods';
$reader = new Xlsx();
$reader->listWorksheetNames($temp);
}
public function testOdsInfo(): void
{
$this->expectException(ReaderException::class);
$this->expectExceptionMessage('Could not find zip member');
$temp = 'samples/templates/OOCalcTest.ods';
$reader = new Xlsx();
$reader->listWorksheetInfo($temp);
}
}

View File

@ -2,11 +2,32 @@
namespace PhpOffice\PhpSpreadsheetTests\Shared;
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
use PhpOffice\PhpSpreadsheet\Shared\File;
use PHPUnit\Framework\TestCase;
class FileTest extends TestCase
{
/** @var bool */
private $uploadFlag = false;
/** @var string */
private $tempfile = '';
protected function setUp(): void
{
$this->uploadFlag = File::getUseUploadTempDirectory();
}
protected function tearDown(): void
{
File::setUseUploadTempDirectory($this->uploadFlag);
if ($this->tempfile !== '') {
unlink($this->tempfile);
$this->tempfile = '';
}
}
public function testGetUseUploadTempDirectory(): void
{
$expectedResult = false;
@ -21,12 +42,71 @@ class FileTest extends TestCase
true,
false,
];
$temp = ini_get('upload_tmp_dir') ?: '';
$badArray = ['', sys_get_temp_dir()];
foreach ($useUploadTempDirectoryValues as $useUploadTempDirectoryValue) {
File::setUseUploadTempDirectory($useUploadTempDirectoryValue);
$result = File::getUseUploadTempDirectory();
self::assertEquals($useUploadTempDirectoryValue, $result);
$result = File::sysGetTempDir();
if (!$useUploadTempDirectoryValue || in_array($temp, $badArray, true)) {
self::assertSame(realpath(sys_get_temp_dir()), $result);
} else {
self::assertSame(realpath($temp), $result);
}
}
}
public function testUploadTmpDir(): void
{
$temp = ini_get('upload_tmp_dir') ?: '';
$badArray = ['', sys_get_temp_dir()];
if (in_array($temp, $badArray, true)) {
self::markTestSkipped('upload_tmp_dir setting unusable for this test');
} else {
File::setUseUploadTempDirectory(true);
$result = File::sysGetTempDir();
self::assertSame(realpath($temp), $result);
}
}
public function testNotExists(): void
{
$temp = File::temporaryFileName();
file_put_contents($temp, '');
File::assertFile($temp);
self::assertTrue(File::testFileNoThrow($temp));
unlink($temp);
self::assertFalse(File::testFileNoThrow($temp));
$this->expectException(ReaderException::class);
$this->expectExceptionMessage('does not exist');
File::assertFile($temp);
}
public function testNotReadable(): void
{
if (PHP_OS_FAMILY === 'Windows') {
self::markTestSkipped('chmod does not work reliably on Windows');
}
$this->tempfile = $temp = File::temporaryFileName();
file_put_contents($temp, '');
chmod($temp, 0070);
self::assertFalse(File::testFileNoThrow($temp));
$this->expectException(ReaderException::class);
$this->expectExceptionMessage('for reading');
File::assertFile($temp);
}
public function testZip(): void
{
$temp = 'samples/templates/26template.xlsx';
File::assertFile($temp, 'xl/workbook.xml');
self::assertTrue(File::testFileNoThrow($temp, 'xl/workbook.xml'));
self::assertFalse(File::testFileNoThrow($temp, 'xl/xworkbook.xml'));
$this->expectException(ReaderException::class);
$this->expectExceptionMessage('Could not find zip member');
File::assertFile($temp, 'xl/xworkbook.xml');
}
}