Completed
Push — master ( 6f82c7...df6d51 )
by ignace nyamagana
05:17 queued 03:20
created

EncloseField   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 140
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
dl 0
loc 140
ccs 31
cts 31
cp 1
rs 10
c 0
b 0
f 0
wmc 13
lcom 1
cbo 1

8 Methods

Rating   Name   Duplication   Size   Complexity  
A register() 0 6 2
A addTo() 0 8 1
A sequence() 0 15 3
A isValidSequence() 0 4 1
A __invoke() 0 4 1
A forceEnclosure() 0 4 1
A onCreate() 0 5 2
A filter() 0 10 2
1
<?php
2
/**
3
* This file is part of the League.csv library
4
*
5
* @license http://opensource.org/licenses/MIT
6
* @link https://github.com/thephpleague/csv/
7
* @version 9.0.1
8
* @package League.csv
9
*
10
* For the full copyright and license information, please view the LICENSE
11
* file that was distributed with this source code.
12
*/
13
declare(strict_types=1);
14
15
namespace League\Csv;
16
17
use OutOfRangeException;
18
use php_user_filter;
19
20
/**
21
 * A stream filter to improve enclosure character usage
22
 *
23
 * @see https://tools.ietf.org/html/rfc4180#section-2
24
 * @see https://bugs.php.net/bug.php?id=38301
25
 *
26
 * @package League.csv
27
 * @since   9.0.0
28
 * @author  Ignace Nyamagana Butera <[email protected]>
29
 */
30
class EncloseField extends php_user_filter
31
{
32
    const FILTERNAME = 'convert.league.csv.enclosure';
33
34
    /**
35
     * the filter name used to instantiate the class with
36
     *
37
     * @var string
38
     */
39
    public $filtername;
40
41
    /**
42
     * Contents of the params parameter passed to stream_filter_append
43
     * or stream_filter_prepend functions
44
     *
45
     * @var mixed
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 register the class as a stream filter
65
     */
66 2
    public static function register()
67
    {
68 2
        if (!in_array(self::FILTERNAME, stream_get_filters())) {
69 2
            stream_filter_register(self::FILTERNAME, __CLASS__);
70
        }
71 2
    }
72
73
    /**
74
     * Static method to add the stream filter to a {@link Writer} object
75
     *
76
     * @param Writer $csv
77
     * @param string $sequence
78
     *
79
     * @return AbstractCsv
80
     */
81 2
    public static function addTo(Writer $csv, string $sequence): Writer
82
    {
83 2
        self::register();
84
85
        return $csv
86 2
            ->addFormatter((new self())->sequence($sequence))
87 2
            ->addStreamFilter(self::FILTERNAME, ['sequence' => $sequence]);
88
    }
89
90
    /**
91
     * Set the Sequence to be used to update enclosure usage
92
     *
93
     * @param string $sequence sequence used to work around fputcsv limitation
94
     *
95
     * @return self
96
     */
97 4
    public function sequence(string $sequence): self
98
    {
99 4
        if ($sequence === $this->sequence) {
100 2
            return $this;
101
        }
102
103 4
        if (!$this->isValidSequence($sequence)) {
104 2
            throw new OutOfRangeException('The sequence must contain at least one character to force enclosure');
105
        }
106
107 4
        $clone = clone $this;
108 4
        $clone->sequence = $sequence;
109
110 4
        return $clone;
111
    }
112
113
    /**
114
     * Filter type and sequence parameters
115
     *
116
     * - The sequence to force enclosure MUST contains one of the following character ("\n\r\t ")
117
     *
118
     * @param string $sequence
119
     *
120
     * @return bool
121
     */
122 4
    protected static function isValidSequence(string $sequence): bool
123
    {
124 4
        return strlen($sequence) != strcspn($sequence, self::$force_enclosure);
125
    }
126
127
    /**
128
     * @inheritdoc
129
     */
130 2
    public function __invoke(array $record): array
131
    {
132 2
        return array_map([$this, 'forceEnclosure'], $record);
133
    }
134
135
    /**
136
     * Format the record field to force the addition of the enclosure by fputcsv
137
     *
138
     * @param mixed $value
139
     *
140
     * @return string
141
     */
142 2
    protected function forceEnclosure($value)
143
    {
144 2
        return $this->sequence.$value;
145
    }
146
147
    /**
148
     * @inheritdoc
149
     */
150 8
    public function onCreate()
151
    {
152 8
        return isset($this->params['sequence'])
153 8
            && $this->isValidSequence($this->params['sequence']);
154
    }
155
156
    /**
157
     * @inheritdoc
158
     */
159 2
    public function filter($in, $out, &$consumed, $closing)
160
    {
161 2
        while ($res = stream_bucket_make_writeable($in)) {
162 2
            $res->data = str_replace($this->params['sequence'], '', $res->data);
163 2
            $consumed += $res->datalen;
164 2
            stream_bucket_append($out, $res);
165
        }
166
167 2
        return PSFS_PASS_ON;
168
    }
169
}
170