Polyfill
10 months ago
AbstractCsv.php
10 months ago
ByteSequence.php
10 months ago
CannotInsertRecord.php
10 months ago
CharsetConverter.php
10 months ago
ColumnConsistency.php
10 months ago
EncloseField.php
10 months ago
EscapeFormula.php
10 months ago
Exception.php
10 months ago
HTMLConverter.php
10 months ago
Info.php
10 months ago
InvalidArgument.php
10 months ago
MapIterator.php
10 months ago
RFC4180Field.php
10 months ago
Reader.php
10 months ago
ResultSet.php
10 months ago
Statement.php
10 months ago
Stream.php
10 months ago
SyntaxError.php
10 months ago
TabularDataReader.php
10 months ago
UnableToProcessCsv.php
10 months ago
UnavailableFeature.php
10 months ago
UnavailableStream.php
10 months ago
Writer.php
10 months ago
XMLConverter.php
10 months ago
functions.php
10 months ago
functions_include.php
10 months ago
EscapeFormula.php
143 lines
| 1 | <?php |
| 2 | |
| 3 | /** |
| 4 | * League.Csv (https://csv.thephpleague.com) |
| 5 | * |
| 6 | * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com> |
| 7 | * |
| 8 | * For the full copyright and license information, please view the LICENSE |
| 9 | * file that was distributed with this source code. |
| 10 | */ |
| 11 | declare (strict_types=1); |
| 12 | namespace IAWPSCOPED\League\Csv; |
| 13 | |
| 14 | use InvalidArgumentException; |
| 15 | use function array_fill_keys; |
| 16 | use function array_keys; |
| 17 | use function array_map; |
| 18 | use function array_merge; |
| 19 | use function array_unique; |
| 20 | use function is_object; |
| 21 | use function is_string; |
| 22 | use function method_exists; |
| 23 | /** |
| 24 | * A Formatter to tackle CSV Formula Injection. |
| 25 | * |
| 26 | * @see http://georgemauer.net/2017/10/07/csv-injection.html |
| 27 | * @internal |
| 28 | */ |
| 29 | class EscapeFormula |
| 30 | { |
| 31 | /** |
| 32 | * Spreadsheet formula starting character. |
| 33 | */ |
| 34 | const FORMULA_STARTING_CHARS = ['=', '-', '+', '@', "\t", "\r"]; |
| 35 | /** |
| 36 | * Effective Spreadsheet formula starting characters. |
| 37 | * |
| 38 | * @var array |
| 39 | */ |
| 40 | protected $special_chars = []; |
| 41 | /** |
| 42 | * Escape character to escape each CSV formula field. |
| 43 | * |
| 44 | * @var string |
| 45 | */ |
| 46 | protected $escape; |
| 47 | /** |
| 48 | * New instance. |
| 49 | * |
| 50 | * @param string $escape escape character to escape each CSV formula field |
| 51 | * @param string[] $special_chars additional spreadsheet formula starting characters |
| 52 | * |
| 53 | */ |
| 54 | public function __construct(string $escape = "'", array $special_chars = []) |
| 55 | { |
| 56 | $this->escape = $escape; |
| 57 | if ([] !== $special_chars) { |
| 58 | $special_chars = $this->filterSpecialCharacters(...$special_chars); |
| 59 | } |
| 60 | $chars = array_unique(array_merge(self::FORMULA_STARTING_CHARS, $special_chars)); |
| 61 | $this->special_chars = array_fill_keys($chars, 1); |
| 62 | } |
| 63 | /** |
| 64 | * Filter submitted special characters. |
| 65 | * |
| 66 | * @param string ...$characters |
| 67 | * |
| 68 | * @throws InvalidArgumentException if the string is not a single character |
| 69 | * |
| 70 | * @return string[] |
| 71 | */ |
| 72 | protected function filterSpecialCharacters(string ...$characters) : array |
| 73 | { |
| 74 | foreach ($characters as $str) { |
| 75 | if (1 != \strlen($str)) { |
| 76 | throw new InvalidArgumentException('The submitted string ' . $str . ' must be a single character'); |
| 77 | } |
| 78 | } |
| 79 | return $characters; |
| 80 | } |
| 81 | /** |
| 82 | * Returns the list of character the instance will escape. |
| 83 | * |
| 84 | * @return string[] |
| 85 | */ |
| 86 | public function getSpecialCharacters() : array |
| 87 | { |
| 88 | return array_keys($this->special_chars); |
| 89 | } |
| 90 | /** |
| 91 | * Returns the escape character. |
| 92 | */ |
| 93 | public function getEscape() : string |
| 94 | { |
| 95 | return $this->escape; |
| 96 | } |
| 97 | /** |
| 98 | * League CSV formatter hook. |
| 99 | * |
| 100 | * @see escapeRecord |
| 101 | */ |
| 102 | public function __invoke(array $record) : array |
| 103 | { |
| 104 | return $this->escapeRecord($record); |
| 105 | } |
| 106 | /** |
| 107 | * Escape a CSV record. |
| 108 | */ |
| 109 | public function escapeRecord(array $record) : array |
| 110 | { |
| 111 | return array_map([$this, 'escapeField'], $record); |
| 112 | } |
| 113 | /** |
| 114 | * Escape a CSV cell if its content is stringable. |
| 115 | * |
| 116 | * @param int|float|string|object|resource|array $cell the content of the cell |
| 117 | * |
| 118 | * @return mixed the escaped content |
| 119 | */ |
| 120 | protected function escapeField($cell) |
| 121 | { |
| 122 | if (!is_string($cell) && (!is_object($cell) || !method_exists($cell, '__toString'))) { |
| 123 | return $cell; |
| 124 | } |
| 125 | $str_cell = (string) $cell; |
| 126 | if (isset($str_cell[0], $this->special_chars[$str_cell[0]])) { |
| 127 | return $this->escape . $str_cell; |
| 128 | } |
| 129 | return $cell; |
| 130 | } |
| 131 | /** |
| 132 | * @deprecated since 9.7.2 will be removed in the next major release |
| 133 | * |
| 134 | * Tells whether the submitted value is stringable. |
| 135 | * |
| 136 | * @param mixed $value value to check if it is stringable |
| 137 | */ |
| 138 | protected function isStringable($value) : bool |
| 139 | { |
| 140 | return is_string($value) || is_object($value) && method_exists($value, '__toString'); |
| 141 | } |
| 142 | } |
| 143 |