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
RFC4180Field.php
163 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 php_user_filter; |
| 16 | use function array_map; |
| 17 | use function in_array; |
| 18 | use function is_string; |
| 19 | use function str_replace; |
| 20 | use function strcspn; |
| 21 | use function stream_bucket_append; |
| 22 | use function stream_bucket_make_writeable; |
| 23 | use function stream_filter_register; |
| 24 | use function stream_get_filters; |
| 25 | use function strlen; |
| 26 | use const STREAM_FILTER_READ; |
| 27 | use const STREAM_FILTER_WRITE; |
| 28 | /** |
| 29 | * A stream filter to conform the CSV field to RFC4180. |
| 30 | * |
| 31 | * DEPRECATION WARNING! This class will be removed in the next major point release |
| 32 | * |
| 33 | * @deprecated since version 9.2.0 |
| 34 | * @see AbstractCsv::setEscape |
| 35 | * |
| 36 | * @see https://tools.ietf.org/html/rfc4180#section-2 |
| 37 | * @internal |
| 38 | */ |
| 39 | class RFC4180Field extends php_user_filter |
| 40 | { |
| 41 | public const FILTERNAME = 'convert.league.csv.rfc4180'; |
| 42 | /** |
| 43 | * The value being search for. |
| 44 | * |
| 45 | * @var string[] |
| 46 | */ |
| 47 | protected $search; |
| 48 | /** |
| 49 | * The replacement value that replace found $search values. |
| 50 | * |
| 51 | * @var string[] |
| 52 | */ |
| 53 | protected $replace; |
| 54 | /** |
| 55 | * Characters that triggers enclosure with PHP fputcsv. |
| 56 | * |
| 57 | * @var string |
| 58 | */ |
| 59 | protected static $force_enclosure = "\n\r\t "; |
| 60 | /** |
| 61 | * Static method to add the stream filter to a {@link AbstractCsv} object. |
| 62 | */ |
| 63 | public static function addTo(AbstractCsv $csv, string $whitespace_replace = '') : AbstractCsv |
| 64 | { |
| 65 | self::register(); |
| 66 | $params = ['enclosure' => $csv->getEnclosure(), 'escape' => $csv->getEscape(), 'mode' => $csv->getStreamFilterMode()]; |
| 67 | if ($csv instanceof Writer && '' != $whitespace_replace) { |
| 68 | self::addFormatterTo($csv, $whitespace_replace); |
| 69 | $params['whitespace_replace'] = $whitespace_replace; |
| 70 | } |
| 71 | return $csv->addStreamFilter(self::FILTERNAME, $params); |
| 72 | } |
| 73 | /** |
| 74 | * Add a formatter to the {@link Writer} object to format the record |
| 75 | * field to avoid enclosure around a field with an empty space. |
| 76 | */ |
| 77 | public static function addFormatterTo(Writer $csv, string $whitespace_replace) : Writer |
| 78 | { |
| 79 | if ('' == $whitespace_replace || strlen($whitespace_replace) != strcspn($whitespace_replace, self::$force_enclosure)) { |
| 80 | throw new InvalidArgumentException('The sequence contains a character that enforces enclosure or is a CSV control character or is the empty string.'); |
| 81 | } |
| 82 | $mapper = static function ($value) use($whitespace_replace) { |
| 83 | if (is_string($value)) { |
| 84 | return str_replace(' ', $whitespace_replace, $value); |
| 85 | } |
| 86 | return $value; |
| 87 | }; |
| 88 | $formatter = static function (array $record) use($mapper) : array { |
| 89 | return array_map($mapper, $record); |
| 90 | }; |
| 91 | return $csv->addFormatter($formatter); |
| 92 | } |
| 93 | /** |
| 94 | * Static method to register the class as a stream filter. |
| 95 | */ |
| 96 | public static function register() : void |
| 97 | { |
| 98 | if (!in_array(self::FILTERNAME, stream_get_filters(), \true)) { |
| 99 | stream_filter_register(self::FILTERNAME, self::class); |
| 100 | } |
| 101 | } |
| 102 | /** |
| 103 | * Static method to return the stream filter filtername. |
| 104 | */ |
| 105 | public static function getFiltername() : string |
| 106 | { |
| 107 | return self::FILTERNAME; |
| 108 | } |
| 109 | /** |
| 110 | * @param resource $in |
| 111 | * @param resource $out |
| 112 | * @param int $consumed |
| 113 | * @param bool $closing |
| 114 | */ |
| 115 | public function filter($in, $out, &$consumed, $closing) : int |
| 116 | { |
| 117 | while ($bucket = stream_bucket_make_writeable($in)) { |
| 118 | $bucket->data = str_replace($this->search, $this->replace, $bucket->data); |
| 119 | $consumed += $bucket->datalen; |
| 120 | stream_bucket_append($out, $bucket); |
| 121 | } |
| 122 | return \PSFS_PASS_ON; |
| 123 | } |
| 124 | /** |
| 125 | * {@inheritdoc} |
| 126 | */ |
| 127 | public function onCreate() : bool |
| 128 | { |
| 129 | if (!$this->isValidParams($this->params)) { |
| 130 | return \false; |
| 131 | } |
| 132 | $this->search = [$this->params['escape'] . $this->params['enclosure']]; |
| 133 | $this->replace = [$this->params['enclosure'] . $this->params['enclosure']]; |
| 134 | if (STREAM_FILTER_WRITE != $this->params['mode']) { |
| 135 | return \true; |
| 136 | } |
| 137 | $this->search = [$this->params['escape'] . $this->params['enclosure']]; |
| 138 | $this->replace = [$this->params['escape'] . $this->params['enclosure'] . $this->params['enclosure']]; |
| 139 | if ($this->isValidSequence($this->params)) { |
| 140 | $this->search[] = $this->params['whitespace_replace']; |
| 141 | $this->replace[] = ' '; |
| 142 | } |
| 143 | return \true; |
| 144 | } |
| 145 | /** |
| 146 | * Validate params property. |
| 147 | */ |
| 148 | protected function isValidParams(array $params) : bool |
| 149 | { |
| 150 | static $mode_list = [STREAM_FILTER_READ => 1, STREAM_FILTER_WRITE => 1]; |
| 151 | return isset($params['enclosure'], $params['escape'], $params['mode'], $mode_list[$params['mode']]) && 1 == strlen($params['enclosure']) && 1 == strlen($params['escape']); |
| 152 | } |
| 153 | /** |
| 154 | * Is Valid White space replaced sequence. |
| 155 | * |
| 156 | * @return bool |
| 157 | */ |
| 158 | protected function isValidSequence(array $params) |
| 159 | { |
| 160 | return isset($params['whitespace_replace']) && strlen($params['whitespace_replace']) == strcspn($params['whitespace_replace'], self::$force_enclosure); |
| 161 | } |
| 162 | } |
| 163 |