First external release
This commit is contained in:
commit
5e199009e6
81 files changed
+5447
No files matched your search
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Common\Escaper;
|
||||
|
||||
/**
|
||||
* Class CSV
|
||||
* Provides functions to escape and unescape data for CSV files
|
||||
*
|
||||
* @package Box\Spout\Common\Escaper
|
||||
*/
|
||||
class CSV implements EscaperInterface
|
||||
{
|
||||
/**
|
||||
* Escapes the given string to make it compatible with CSV
|
||||
*
|
||||
* @param string $string The string to escape
|
||||
* @return string The escaped string
|
||||
*/
|
||||
public function escape($string)
|
||||
{
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unescapes the given string to make it compatible with CSV
|
||||
*
|
||||
* @param string $string The string to unescape
|
||||
* @return string The unescaped string
|
||||
*/
|
||||
public function unescape($string)
|
||||
{
|
||||
return $string;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Common\Escaper;
|
||||
|
||||
/**
|
||||
* Interface EscaperInterface
|
||||
*
|
||||
* @package Box\Spout\Common\Escaper
|
||||
*/
|
||||
interface EscaperInterface
|
||||
{
|
||||
/**
|
||||
* Escapes the given string to make it compatible with PHP
|
||||
*
|
||||
* @param string $string The string to escape
|
||||
* @return string The escaped string
|
||||
*/
|
||||
public function escape($string);
|
||||
|
||||
/**
|
||||
* Unescapes the given string to make it compatible with PHP
|
||||
*
|
||||
* @param string $string The string to unescape
|
||||
* @return string The unescaped string
|
||||
*/
|
||||
public function unescape($string);
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Common\Escaper;
|
||||
|
||||
/**
|
||||
* Class XLSX
|
||||
* Provides functions to escape and unescape data for XLSX files
|
||||
*
|
||||
* @package Box\Spout\Common\Escaper
|
||||
*/
|
||||
class XLSX implements EscaperInterface
|
||||
{
|
||||
/** @var string[] Control characters to be escaped */
|
||||
protected $controlCharactersEscapingMap;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->controlCharactersEscapingMap = $this->getControlCharactersEscapingMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes the given string to make it compatible with XLSX
|
||||
*
|
||||
* @param string $string The string to escape
|
||||
* @return string The escaped string
|
||||
*/
|
||||
public function escape($string)
|
||||
{
|
||||
$escapedString = $this->escapeControlCharacters($string);
|
||||
$escapedString = htmlspecialchars($escapedString, ENT_QUOTES, 'utf-8');
|
||||
|
||||
return $escapedString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unescapes the given string to make it compatible with XLSX
|
||||
*
|
||||
* @param string $string The string to unescape
|
||||
* @return string The unescaped string
|
||||
*/
|
||||
public function unescape($string)
|
||||
{
|
||||
$unescapedString = htmlspecialchars_decode($string, ENT_QUOTES);
|
||||
$unescapedString = $this->unescapeControlCharacters($unescapedString);
|
||||
|
||||
return $unescapedString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the map containing control characters to be escaped
|
||||
* mapped to their escaped values.
|
||||
* "\t", "\r" and "\n" don't need to be escaped.
|
||||
*
|
||||
* NOTE: the logic has been adapted from the XlsxWriter library (BSD License)
|
||||
* @link https://github.com/jmcnamara/XlsxWriter/blob/f1e610f29/xlsxwriter/sharedstrings.py#L89
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function getControlCharactersEscapingMap()
|
||||
{
|
||||
$controlCharactersEscapingMap = [];
|
||||
$whitelistedControlCharacters = ["\t", "\r", "\n"];
|
||||
|
||||
// control characters values are from 0 to 1F (hex values) in the ASCII table
|
||||
for ($charValue = 0x0; $charValue <= 0x1F; $charValue++) {
|
||||
if (!in_array(chr($charValue), $whitelistedControlCharacters)) {
|
||||
$charHexValue = dechex($charValue);
|
||||
$escapedChar = '_x' . sprintf('%04s' , strtoupper($charHexValue)) . '_';
|
||||
$controlCharactersEscapingMap[$escapedChar] = chr($charValue);
|
||||
}
|
||||
}
|
||||
|
||||
return $controlCharactersEscapingMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts PHP control characters from the given string to OpenXML escaped control characters
|
||||
*
|
||||
* Excel escapes control characters with _xHHHH_ and also escapes any
|
||||
* literal strings of that type by encoding the leading underscore.
|
||||
* So "\0" -> _x0000_ and "_x0000_" -> _x005F_x0000_.
|
||||
*
|
||||
* NOTE: the logic has been adapted from the XlsxWriter library (BSD License)
|
||||
* @link https://github.com/jmcnamara/XlsxWriter/blob/f1e610f29/xlsxwriter/sharedstrings.py#L89
|
||||
*
|
||||
* @param string $string String to escape
|
||||
* @return string
|
||||
*/
|
||||
protected function escapeControlCharacters($string)
|
||||
{
|
||||
$escapedString = $this->escapeEscapeCharacter($string);
|
||||
return str_replace(array_values($this->controlCharactersEscapingMap), array_keys($this->controlCharactersEscapingMap), $escapedString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes the escape character: "_x0000_" -> "_x005F_x0000_"
|
||||
*
|
||||
* @param string $string String to escape
|
||||
* @return string The escaped string
|
||||
*/
|
||||
protected function escapeEscapeCharacter($string)
|
||||
{
|
||||
return preg_replace('/_(x[\dA-F]{4})_/', '_x005F_$1_', $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts OpenXML escaped control characters from the given string to PHP control characters
|
||||
*
|
||||
* Excel escapes control characters with _xHHHH_ and also escapes any
|
||||
* literal strings of that type by encoding the leading underscore.
|
||||
* So "_x0000_" -> "\0" and "_x005F_x0000_" -> "_x0000_"
|
||||
*
|
||||
* NOTE: the logic has been adapted from the XlsxWriter library (BSD License)
|
||||
* @link https://github.com/jmcnamara/XlsxWriter/blob/f1e610f29/xlsxwriter/sharedstrings.py#L89
|
||||
*
|
||||
* @param string $string String to unescape
|
||||
* @return string
|
||||
*/
|
||||
protected function unescapeControlCharacters($string)
|
||||
{
|
||||
$unescapedString = $string;
|
||||
foreach ($this->controlCharactersEscapingMap as $escapedCharValue => $charValue) {
|
||||
// only unescape characters that don't contain the escaped escape character for now
|
||||
$unescapedString = preg_replace("/(?<!_x005F)($escapedCharValue)/", $charValue, $unescapedString);
|
||||
}
|
||||
|
||||
return $this->unescapeEscapeCharacter($unescapedString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unecapes the escape character: "_x005F_x0000_" => "_x0000_"
|
||||
*
|
||||
* @param string $string String to unescape
|
||||
* @return string The unescaped string
|
||||
*/
|
||||
protected function unescapeEscapeCharacter($string)
|
||||
{
|
||||
return preg_replace('/_x005F(_x[\dA-F]{4}_)/', '$1', $string);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Common\Exception;
|
||||
|
||||
/**
|
||||
* Class BadUsageException
|
||||
*
|
||||
* @package Box\Spout\Common\Exception
|
||||
*/
|
||||
class BadUsageException extends SpoutException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Common\Exception;
|
||||
|
||||
/**
|
||||
* Class IOException
|
||||
*
|
||||
* @package Box\Spout\Common\Exception
|
||||
*/
|
||||
class IOException extends SpoutException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Common\Exception;
|
||||
|
||||
/**
|
||||
* Class InvalidArgumentException
|
||||
*
|
||||
* @package Box\Spout\Common\Exception
|
||||
*/
|
||||
class InvalidArgumentException extends SpoutException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Common\Exception;
|
||||
|
||||
/**
|
||||
* Class SpoutException
|
||||
*
|
||||
* @package Box\Spout\Common\Exception
|
||||
*/
|
||||
abstract class SpoutException extends \Exception
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Common\Exception;
|
||||
|
||||
/**
|
||||
* Class UnsupportedTypeException
|
||||
*
|
||||
* @package Box\Spout\Common\Exception
|
||||
*/
|
||||
class UnsupportedTypeException extends SpoutException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Common\Helper;
|
||||
|
||||
use Box\Spout\Common\Exception\IOException;
|
||||
|
||||
/**
|
||||
* Class FileSystemHelper
|
||||
* This class provides helper functions to help with the file system operations
|
||||
* like files/folders creation & deletion
|
||||
*
|
||||
* @package Box\Spout\Common\Helper
|
||||
*/
|
||||
class FileSystemHelper
|
||||
{
|
||||
/** @var string Path of the base folder where all the I/O can occur */
|
||||
protected $baseFolderPath;
|
||||
|
||||
/**
|
||||
* @param string $baseFolderPath The path of the base folder where all the I/O can occur
|
||||
*/
|
||||
public function __construct($baseFolderPath)
|
||||
{
|
||||
$this->baseFolderPath = $baseFolderPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an empty folder with the given name under the given parent folder.
|
||||
*
|
||||
* @param string $parentFolderPath The parent folder path under which the folder is going to be created
|
||||
* @param string $folderName The name of the folder to create
|
||||
* @return string Path of the created folder
|
||||
* @throws \Box\Spout\Common\Exception\IOException If unable to create the folder or if the folder path is not inside of the base folder
|
||||
*/
|
||||
public function createFolder($parentFolderPath, $folderName)
|
||||
{
|
||||
$this->throwIfOperationNotInBaseFolder($parentFolderPath);
|
||||
|
||||
$folderPath = $parentFolderPath . DIRECTORY_SEPARATOR . $folderName;
|
||||
|
||||
$wasCreationSuccessful = mkdir($folderPath, 0777, true);
|
||||
if (!$wasCreationSuccessful) {
|
||||
throw new IOException('Unable to create folder: ' . $folderPath);
|
||||
}
|
||||
|
||||
return $folderPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a file with the given name and content in the given folder.
|
||||
* The parent folder must exist.
|
||||
*
|
||||
* @param string $parentFolderPath The parent folder path where the file is going to be created
|
||||
* @param string $fileName The name of the file to create
|
||||
* @param string $fileContents The contents of the file to create
|
||||
* @return string Path of the created file
|
||||
* @throws \Box\Spout\Common\Exception\IOException If unable to create the file or if the file path is not inside of the base folder
|
||||
*/
|
||||
public function createFileWithContents($parentFolderPath, $fileName, $fileContents)
|
||||
{
|
||||
$this->throwIfOperationNotInBaseFolder($parentFolderPath);
|
||||
|
||||
$filePath = $parentFolderPath . DIRECTORY_SEPARATOR . $fileName;
|
||||
|
||||
$wasCreationSuccessful = file_put_contents($filePath, $fileContents);
|
||||
if (!$wasCreationSuccessful) {
|
||||
throw new IOException('Unable to create file: ' . $filePath);
|
||||
}
|
||||
|
||||
return $filePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the file at the given path
|
||||
*
|
||||
* @param string $filePath Path of the file to delete
|
||||
* @return void
|
||||
* @throws \Box\Spout\Common\Exception\IOException If the file path is not inside of the base folder
|
||||
*/
|
||||
public function deleteFile($filePath)
|
||||
{
|
||||
$this->throwIfOperationNotInBaseFolder($filePath);
|
||||
|
||||
if (file_exists($filePath) && is_file($filePath)) {
|
||||
unlink($filePath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the folder at the given path as well as all its contents
|
||||
*
|
||||
* @param string $folderPath Path of the folder to delete
|
||||
* @return void
|
||||
* @throws \Box\Spout\Common\Exception\IOException If the folder path is not inside of the base folder
|
||||
*/
|
||||
public function deleteFolderRecursively($folderPath)
|
||||
{
|
||||
$this->throwIfOperationNotInBaseFolder($folderPath);
|
||||
|
||||
$itemIterator = new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($folderPath, \RecursiveDirectoryIterator::SKIP_DOTS),
|
||||
\RecursiveIteratorIterator::CHILD_FIRST
|
||||
);
|
||||
|
||||
foreach ($itemIterator as $item) {
|
||||
if ($item->isDir()) {
|
||||
rmdir($item->getPathname());
|
||||
} else {
|
||||
unlink($item->getPathname());
|
||||
}
|
||||
}
|
||||
|
||||
rmdir($folderPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* All I/O operations must occur inside the base folder, for security reasons.
|
||||
* This function will throw an exception if the folder where the I/O operation
|
||||
* should occur is not inside the base folder.
|
||||
*
|
||||
* @param string $operationFolderPath The path of the folder where the I/O operation should occur
|
||||
* @return void
|
||||
* @throws \Box\Spout\Common\Exception\IOException If the folder where the I/O operation should occur is not inside the base folder
|
||||
*/
|
||||
protected function throwIfOperationNotInBaseFolder($operationFolderPath)
|
||||
{
|
||||
$isInBaseFolder = (strpos($operationFolderPath, $this->baseFolderPath) === 0);
|
||||
if (!$isInBaseFolder) {
|
||||
throw new IOException('Cannot perform I/O operation outside of the base folder: ' . $this->baseFolderPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Common\Helper;
|
||||
|
||||
/**
|
||||
* Class GlobalFunctionsHelper
|
||||
* This class wraps global functions to facilitate testing
|
||||
*
|
||||
* @package Box\Spout\Common\Helper
|
||||
*/
|
||||
class GlobalFunctionsHelper
|
||||
{
|
||||
/**
|
||||
* Wrapper around global function fopen()
|
||||
* @see fopen()
|
||||
*
|
||||
* @param string $fileName
|
||||
* @param string $mode
|
||||
* @return resource|bool
|
||||
*/
|
||||
public function fopen($fileName, $mode)
|
||||
{
|
||||
return fopen($fileName, $mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function fgets()
|
||||
* @see fgets()
|
||||
*
|
||||
* @param resource $handle
|
||||
* @param int|void $length
|
||||
* @return string
|
||||
*/
|
||||
public function fgets($handle, $length = null)
|
||||
{
|
||||
return fgets($handle, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function fputs()
|
||||
* @see fputs()
|
||||
*
|
||||
* @param resource $handle
|
||||
* @param string $string
|
||||
* @return int
|
||||
*/
|
||||
public function fputs($handle, $string)
|
||||
{
|
||||
return fputs($handle, $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function fflush()
|
||||
* @see fflush()
|
||||
*
|
||||
* @param resource $handle
|
||||
* @return bool
|
||||
*/
|
||||
public function fflush($handle)
|
||||
{
|
||||
return fflush($handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function fseek()
|
||||
* @see fseek()
|
||||
*
|
||||
* @param resource $handle
|
||||
* @param int $offset
|
||||
* @return int
|
||||
*/
|
||||
public function fseek($handle, $offset)
|
||||
{
|
||||
return fseek($handle, $offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function fgetcsv()
|
||||
* @see fgetcsv()
|
||||
*
|
||||
* @param resource $handle
|
||||
* @param int|void $length
|
||||
* @param string|void $delimiter
|
||||
* @param string|void $enclosure
|
||||
* @return array
|
||||
*/
|
||||
public function fgetcsv($handle, $length = null, $delimiter = null, $enclosure = null)
|
||||
{
|
||||
return fgetcsv($handle, $length, $delimiter, $enclosure);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function fclose()
|
||||
* @see fclose()
|
||||
*
|
||||
* @param resource $handle
|
||||
* @return bool
|
||||
*/
|
||||
public function fclose($handle)
|
||||
{
|
||||
return fclose($handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function rewind()
|
||||
* @see rewind()
|
||||
*
|
||||
* @param resource $handle
|
||||
* @return bool
|
||||
*/
|
||||
public function rewind($handle)
|
||||
{
|
||||
return rewind($handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function file_exists()
|
||||
* @see file_exists()
|
||||
*
|
||||
* @param string $filename
|
||||
* @return bool
|
||||
*/
|
||||
public function file_exists($fileName)
|
||||
{
|
||||
return file_exists($fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function is_readable()
|
||||
* @see is_readable()
|
||||
*
|
||||
* @param string $filename
|
||||
* @return bool
|
||||
*/
|
||||
public function is_readable($fileName)
|
||||
{
|
||||
return is_readable($fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function basename()
|
||||
* @see basename()
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
public function basename($path)
|
||||
{
|
||||
return basename($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function header()
|
||||
* @see header()
|
||||
*
|
||||
* @param string $string
|
||||
* @return void
|
||||
*/
|
||||
public function header($string)
|
||||
{
|
||||
header($string);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Common;
|
||||
|
||||
/**
|
||||
* Class Type
|
||||
* This class references the supported types
|
||||
*/
|
||||
abstract class Type
|
||||
{
|
||||
const CSV = "csv";
|
||||
const XLSX = "xlsx";
|
||||
}
|
||||
Reference in new issue
Block a user