EncloseField::isValidSequence()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
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 in_array;
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
27
/**
28
 * A stream filter to improve enclosure character usage.
29
 *
30
 * @see https://tools.ietf.org/html/rfc4180#section-2
31
 * @see https://bugs.php.net/bug.php?id=38301
32
 */
33
class EncloseField extends php_user_filter
34
{
35
    const FILTERNAME = 'convert.league.csv.enclosure';
36
37
    /**
38
     * the filter name used to instantiate the class with.
39
     *
40
     * @var string
41
     */
42
    public $filtername;
43
44
    /**
45
     * @var mixed value passed to passed to stream_filter_append or stream_filter_prepend functions.
46
     */
47
    public $params;
48
49
    /**
50
     * Default sequence.
51
     *
52
     * @var string
53
     */
54
    protected $sequence;
55
56
    /**
57
     * Characters that triggers enclosure in PHP.
58
     *
59
     * @var string
60
     */
61
    protected static $force_enclosure = "\n\r\t ";
62
63
    /**
64
     * Static method to return the stream filter filtername.
65
     */
66 3
    public static function getFiltername(): string
67
    {
68 3
        return self::FILTERNAME;
69
    }
70
71
    /**
72
     * Static method to register the class as a stream filter.
73
     */
74 3
    public static function register(): void
75
    {
76 3
        if (!in_array(self::FILTERNAME, stream_get_filters(), true)) {
77 3
            stream_filter_register(self::FILTERNAME, self::class);
78
        }
79 3
    }
80
81
    /**
82
     * Static method to add the stream filter to a {@link Writer} object.
83
     *
84
     * @throws InvalidArgumentException if the sequence is malformed
85
     */
86 6
    public static function addTo(Writer $csv, string $sequence): Writer
87
    {
88 6
        self::register();
89
90 6
        if (!self::isValidSequence($sequence)) {
91 3
            throw new InvalidArgumentException('The sequence must contain at least one character to force enclosure');
92
        }
93
94
        $formatter = static function (array $record) use ($sequence): array {
95 3
            foreach ($record as &$value) {
96 3
                $value = $sequence.$value;
97
            }
98 3
            unset($value);
99
100 3
            return $record;
101 3
        };
102
103
        return $csv
104 3
            ->addFormatter($formatter)
105 3
            ->addStreamFilter(self::FILTERNAME, ['sequence' => $sequence]);
106
    }
107
108
    /**
109
     * Filter type and sequence parameters.
110
     *
111
     * The sequence to force enclosure MUST contains one of the following character ("\n\r\t ")
112
     */
113 6
    protected static function isValidSequence(string $sequence): bool
114
    {
115 6
        return strlen($sequence) != strcspn($sequence, self::$force_enclosure);
116
    }
117
118
    /**
119
     * {@inheritdoc}
120
     */
121 12
    public function onCreate(): bool
122
    {
123 12
        return isset($this->params['sequence'])
124 12
            && self::isValidSequence($this->params['sequence']);
125
    }
126
127
    /**
128
     * @param resource $in
129
     * @param resource $out
130
     * @param int      $consumed
131
     * @param bool     $closing
132
     */
133 3
    public function filter($in, $out, &$consumed, $closing): int
134
    {
135 3
        while ($res = stream_bucket_make_writeable($in)) {
136 3
            $res->data = str_replace($this->params['sequence'], '', $res->data);
137 3
            $consumed += $res->datalen;
138 3
            stream_bucket_append($out, $res);
139
        }
140
141 3
        return PSFS_PASS_ON;
142
    }
143
}
144