RFC4180Field::register()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 0
loc 6
ccs 4
cts 4
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * League.Csv (https://csv.thephpleague.com)
5
 *
6
 * (c) Ignace Nyamagana Butera <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace League\Csv;
15
16
use InvalidArgumentException;
17
use php_user_filter;
18
use function array_map;
19
use function in_array;
20
use function is_string;
21
use function str_replace;
22
use function strcspn;
23
use function stream_bucket_append;
24
use function stream_bucket_make_writeable;
25
use function stream_filter_register;
26
use function stream_get_filters;
27
use function strlen;
28
use const STREAM_FILTER_READ;
29
use const STREAM_FILTER_WRITE;
30
31
/**
32
 * A stream filter to conform the CSV field to RFC4180.
33
 *
34
 * DEPRECATION WARNING! This class will be removed in the next major point release
35
 *
36
 * @deprecated deprecated since version 9.2.0
37
 * @see AbstractCsv::setEscape
38
 *
39
 * @see https://tools.ietf.org/html/rfc4180#section-2
40
 */
41
class RFC4180Field extends php_user_filter
42
{
43
    const FILTERNAME = 'convert.league.csv.rfc4180';
44
45
    /**
46
     * the filter name used to instantiate the class with.
47
     *
48
     * @var string
49
     */
50
    public $filtername;
51
52
    /**
53
     * @var mixed value passed to passed to stream_filter_append or stream_filter_prepend functions.
54
     */
55
    public $params;
56
57
    /**
58
     * The value being search for.
59
     *
60
     * @var string[]
61
     */
62
    protected $search;
63
64
    /**
65
     * The replacement value that replace found $search values.
66
     *
67
     * @var string[]
68
     */
69
    protected $replace;
70
71
    /**
72
     * Characters that triggers enclosure with PHP fputcsv.
73
     *
74
     * @var string
75
     */
76
    protected static $force_enclosure = "\n\r\t ";
77
78
    /**
79
     * Static method to add the stream filter to a {@link AbstractCsv} object.
80
     */
81 15
    public static function addTo(AbstractCsv $csv, string $whitespace_replace = ''): AbstractCsv
82
    {
83 15
        self::register();
84
85
        $params = [
86 15
            'enclosure' => $csv->getEnclosure(),
87 15
            'escape' => $csv->getEscape(),
88 15
            'mode' => $csv->getStreamFilterMode(),
89
        ];
90
91 15
        if ($csv instanceof Writer && '' != $whitespace_replace) {
92 6
            self::addFormatterTo($csv, $whitespace_replace);
93 3
            $params['whitespace_replace'] = $whitespace_replace;
94
        }
95
96 12
        return $csv->addStreamFilter(self::FILTERNAME, $params);
97
    }
98
99
    /**
100
     * Add a formatter to the {@link Writer} object to format the record
101
     * field to avoid enclosure around a field with an empty space.
102
     */
103 6
    public static function addFormatterTo(Writer $csv, string $whitespace_replace): Writer
104
    {
105 6
        if ('' == $whitespace_replace || strlen($whitespace_replace) != strcspn($whitespace_replace, self::$force_enclosure)) {
106 3
            throw new InvalidArgumentException('The sequence contains a character that enforces enclosure or is a CSV control character or is the empty string.');
107
        }
108
109
        $mapper = static function ($value) use ($whitespace_replace) {
110 3
            if (is_string($value)) {
111 3
                return str_replace(' ', $whitespace_replace, $value);
112
            }
113
114 3
            return $value;
115 3
        };
116
117
        $formatter = static function (array $record) use ($mapper): array {
118 3
            return array_map($mapper, $record);
119 3
        };
120
121 3
        return $csv->addFormatter($formatter);
122
    }
123
124
    /**
125
     * Static method to register the class as a stream filter.
126
     */
127 9
    public static function register(): void
128
    {
129 9
        if (!in_array(self::FILTERNAME, stream_get_filters(), true)) {
130 3
            stream_filter_register(self::FILTERNAME, self::class);
131
        }
132 9
    }
133
134
    /**
135
     * Static method to return the stream filter filtername.
136
     */
137 6
    public static function getFiltername(): string
138
    {
139 6
        return self::FILTERNAME;
140
    }
141
142
    /**
143
     * @param resource $in
144
     * @param resource $out
145
     * @param int      $consumed
146
     * @param bool     $closing
147
     */
148 12
    public function filter($in, $out, &$consumed, $closing): int
149
    {
150 12
        while ($bucket = stream_bucket_make_writeable($in)) {
151 12
            $bucket->data = str_replace($this->search, $this->replace, $bucket->data);
152 12
            $consumed += $bucket->datalen;
153 12
            stream_bucket_append($out, $bucket);
154
        }
155
156 12
        return PSFS_PASS_ON;
157
    }
158
159
    /**
160
     * {@inheritdoc}
161
     */
162 30
    public function onCreate(): bool
163
    {
164 30
        if (!$this->isValidParams($this->params)) {
165 15
            return false;
166
        }
167
168 12
        $this->search = [$this->params['escape'].$this->params['enclosure']];
169 12
        $this->replace = [$this->params['enclosure'].$this->params['enclosure']];
170 12
        if (STREAM_FILTER_WRITE != $this->params['mode']) {
171 3
            return true;
172
        }
173
174 9
        $this->search = [$this->params['escape'].$this->params['enclosure']];
175 9
        $this->replace = [$this->params['escape'].$this->params['enclosure'].$this->params['enclosure']];
176 9
        if ($this->isValidSequence($this->params)) {
177 3
            $this->search[] = $this->params['whitespace_replace'];
178 3
            $this->replace[] = ' ';
179
        }
180
181 9
        return true;
182
    }
183
184
    /**
185
     * Validate params property.
186
     */
187 24
    protected function isValidParams(array $params): bool
188
    {
189 24
        static $mode_list = [STREAM_FILTER_READ => 1, STREAM_FILTER_WRITE => 1];
190
191 24
        return isset($params['enclosure'], $params['escape'], $params['mode'], $mode_list[$params['mode']])
192 24
            && 1 == strlen($params['enclosure'])
193 24
            && 1 == strlen($params['escape']);
194
    }
195
196
    /**
197
     * Is Valid White space replaced sequence.
198
     *
199
     * @return bool
200
     */
201 3
    protected function isValidSequence(array $params)
202
    {
203 3
        return isset($params['whitespace_replace'])
204 3
            && strlen($params['whitespace_replace']) == strcspn($params['whitespace_replace'], self::$force_enclosure);
205
    }
206
}
207