Completed
Push — master ( 5c1aea...9ef1c4 )
by Luke
03:03
created

src/CSVelte/Flavor.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * CSVelte: Slender, elegant CSV for PHP
4
 *
5
 * Inspired by Python's CSV module and Frictionless Data and the W3C's CSV
6
 * standardization efforts, CSVelte was written in an effort to take all the
7
 * suck out of working with CSV.
8
 *
9
 * @version   v0.2.1
10
 * @copyright Copyright (c) 2016 Luke Visinoni <[email protected]>
11
 * @author    Luke Visinoni <[email protected]>
12
 * @license   https://github.com/deni-zen/csvelte/blob/master/LICENSE The MIT License (MIT)
13
 */
14
namespace CSVelte;
15
16
use \InvalidArgumentException;
17
use CSVelte\Exception\ImmutableException;
18
19
/**
20
 * CSV Flavor
21
 *
22
 * Represents a particular "flavor"  of CSV. Inspired by python's csv "dialects".
23
 * Also inspired by Frictionless Data's "dialect description" format and the W3C's
24
 * CSV on the Web Working Group and their work on CSV dialects.
25
 *
26
 * @package CSVelte
27
 * @subpackage Flavor
28
 * @since v0.1
29
 * @property-read string $delimiter The delimiter character
30
 * @property-read string $quoteChar The quoting character
31
 * @property-read string $lineTerminator The character sequence used to terminate rows of data
32
 * @property-read string $escapeChar The character used to escape quotes within a quoted string
33
 *     Mutually exclusive to $doubleQuote
34
 * @property-read bool $doubleQuote If true, quote characters will be escaped by preceding them
35
 *     with another quote character. Mutually exclusive to $escapeChar
36
 * @property-read string $quoteStyle One of four class constants that determine which cells are quoted
37
 * @property-read bool $header If true, first row should be treated as a header row
38
 */
39
class Flavor
40
{
41
    /**
42
     * Quote all cells.
43
     * Set Flavor::$quoteStyle to this to quote all cells, regardless of data type
44
     * @var string
45
     */
46
    const QUOTE_ALL = 'quote_all';
47
48
    /**
49
     * Quote no cells.
50
     * Set Flavor::$quoteStyle to this to quote no columns, regardless of data type
51
     * @var string
52
     */
53
    const QUOTE_NONE = 'quote_none';
54
55
    /**
56
     * Quote minimal columns.
57
     * Set Flavor::$quoteStyle to this to quote only cells that contain special
58
     * characters such as newlines or the delimiter character
59
     * @var string
60
     */
61
    const QUOTE_MINIMAL = 'quote_minimal';
62
63
    /**
64
     * Quote non-numeric cells.
65
     * Set Flavor::$quoteStyle to this to quote only cells that contain
66
     * non-numeric data
67
     * @var string
68
     */
69
    const QUOTE_NONNUMERIC = 'quote_nonnumeric';
70
71
    /**
72
     * Delimiter character.
73
     * This is the character that will be used to separate data cells within a
74
     * row of CSV data. Usually a comma.
75
     * @var string
76
     */
77
    protected $delimiter = ",";
78
79
    /**
80
     * Quote character.
81
     * This is the character that will be used to enclose (quote) data cells. It
82
     * is usually a double quote character but single quote is allowed.
83
     * @var string
84
     */
85
    protected $quoteChar = '"';
86
87
    /**
88
     * Escape character.
89
     * This character will be used to escape quotes within quoted text. It is
90
     * mutually exclusive to the doubleQuote attribute. Usually a backspace.
91
     * @var string
92
     */
93
    protected $escapeChar = '\\';
94
95
    /**
96
     * Double quote escape mode.
97
     * If set to true, quote characters within quoted text will be escaped by
98
     * preceding them with the same quote character.
99
     * @var boolean
100
     */
101
    protected $doubleQuote = true;
102
103
    /**
104
     * Not yet implemented
105
     * @ignore
106
     */
107
    // protected $skipInitialSpace = false;
1 ignored issue
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
108
109
    /**
110
     * Quoting style.
111
     * This may be set to one of four values:
112
     *     * *Flavor::QUOTE_NONE* - To never quote data cells
113
     *     * *Flavor::QUOTE_ALL* - To always quote data cells
114
     *     * *Flavor::QUOTE_MINIMAL* - To only quote data cells that contain special characters such as quote character or delimiter character
115
     *     * *Flavor::QUOTE_NONNUMERIC* - To quote data cells that contain non-numeric data
116
     * @var string
117
     */
118
    protected $quoteStyle = self::QUOTE_MINIMAL;
119
120
    /**
121
     * Line terminator string sequence.
122
     * This is a character or sequence of characters that will be used to denote
123
     * the end of a row within the data
124
     * @var string
125
     */
126
    protected $lineTerminator = "\r\n";
127
128
    /**
129
     * Header.
130
     * If set to true, this means the first line of the CSV data is to be treated
131
     * as the column headers.
132
     * @var boolean
133
     */
134
    protected $header;
135
136
    /**
137
     * Class constructor
138
     *
139
     * The attributes that make up a flavor object can only be specified by
140
     * passing them in an array as key => value pairs to the constructor. Once
141
     * the flavor object is created, its attributes cannot be changed.
142
     *
143
     * @param array $attributes The attributes that define this particular flavor. These
144
     *     attributes are immutable. They can only be set here.
145
     */
146 49
    public function __construct($attributes = null)
147
    {
148 49
        if (!is_null($attributes)) {
149 37
            if (!is_array($attributes)) {
150
                // @todo throw exception?
151 1
                return;
152
            }
153 36
            foreach ($attributes as $attr => $val) {
154 36
                $this->assertValidAttribute($attr);
155 36
                $this->$attr = $val;
156 36
            }
157 36
        }
158 49
    }
159
160
    /**
161
     * Does this flavor of CSV have a header row?
162
     *
163
     * The difference between $flavor->header and $flavor->hasHeader() is that
164
     * hasHeader() is always going to give you a boolean value, whereas
165
     * $flavor->header may be null. A null value for header could mean that the
166
     * taster class could not reliably determine whether or not there was a
167
     * header row or it could simply mean that the flavor was instantiated with
168
     * no value for the header property.
169
     *
170
     * @return boolean
171
     */
172 1
    public function hasHeader()
173
    {
174 1
        return (bool) $this->header;
175
    }
176
177
    /**
178
     * Assert valid attribute name.
179
     * Assert that a particular attribute is valid (basically just that it exists)
180
     * and throw an exception otherwise
181
     *
182
     * @param string $attr The attribute to check validity of
183
     * @throws InvalidArgumentException
184
     * @internal
185
     * @todo This should accept a second parameter for value that asserts the value
186
     *     is a valid value
187
     */
188 45
    protected function assertValidAttribute($attr)
189
    {
190 45
        if (!property_exists(self::class, $attr))
191 45
            throw new InvalidArgumentException("Unknown attribute: " . $attr);
192 44
    }
193
194
    /**
195
     * Copy this flavor object
196
     *
197
     * Because flavor attributes are immutable, it is implossible to change their
198
     * attributes. If you need to change a flavor's attributes, call this method
199
     * instead, specifying which attributes are to be changed.
200
     *
201
     * @param array $attribs An array of attribute name/values to change in the copied flavor
202
     * @return Flavor A flavor object with your new attributes
203
     * @todo I may want to remove the array type-hint so that this can accept
204
     *     array-like objects and iterables as well. Not sure...
205
     */
206 6
    public function copy(array $attribs = array())
207
    {
208 6
        return new Flavor(array_merge($this->toArray(), $attribs));
209
    }
210
211
    /**
212
     * Attribute accessor magic method
213
     *
214
     * @param string $attr The attribute to "get"
215
     * @return string The attribute value
216
     * @internal
217
     * @throws InvalidArgumentException
218
     */
219 38
    public function __get($attr)
220
    {
221 38
        $this->assertValidAttribute($attr);
222 37
        return $this->$attr;
223
    }
224
225
    /**
226
     * Attribute accessor (setter) magic method.
227
     * Disabled because attributes are immutable (read-only)
228
     *
229
     * @param string $attr The attribute name you're attempting to set
230
     * @param mixed $val The attribute value
231
     * @throws ImmutableException
232
     * @internal param The $string attribute to "set"
233
     * @internal param The $string attribute value
234
     * @internal
235
     */
236 1
    public function __set($attr, $val)
237
    {
238 1
        throw new ImmutableException("Cannot change attributes on an immutable object: " . self::class . "::\$" . $attr);
239
    }
240
241
    /**
242
     * Get this object as an array
243
     *
244
     * @return array This object as an array
245
     */
246 9
    public function toArray()
247
    {
248 9
        return get_object_vars($this);
249
    }
250
251
}
252