@@ -31,23 +31,33 @@ class CsvResponse extends DownloadResponse
3131{
3232 use InjectContentTypeTrait;
3333
34+ const DEFAULT_SEPARATOR = ', ' ;
35+
36+ /**
37+ * @var array
38+ */
39+ private $ options ;
40+
3441 /**
3542 * Create a CSV response.
3643 *
3744 * Produces a CSV response with a Content-Type of text/csv and a default
3845 * status of 200.
3946 *
40- * @param string|StreamInterface $text String or stream for the message body.
47+ * @param array|string|StreamInterface $text String or stream for the message body.
48+ * @param array $options
4149 * @param int $status Integer status code for the response; 200 by default.
4250 * @param string $filename
4351 * @param array $headers Array of headers to use at initialization.
4452 */
45- public function __construct ($ text , int $ status = 200 , string $ filename = '' , array $ headers = [])
53+ public function __construct ($ text , array $ options = [], int $ status = 200 , string $ filename = '' , array $ headers = [])
4654 {
4755 if ($ filename !== '' ) {
4856 $ headers = $ this ->prepareDownloadHeaders ($ filename , $ headers );
4957 }
5058
59+ $ this ->options = $ options ;
60+
5161 parent ::__construct (
5262 $ this ->createBody ($ text ),
5363 $ status ,
@@ -56,19 +66,36 @@ public function __construct($text, int $status = 200, string $filename = '', arr
5666 }
5767
5868 /**
59- * Create the CSV message body.
60- *
69+ * Create the body of the CSV response
6170 * @param string|StreamInterface $text
6271 * @return StreamInterface
6372 * @throws Exception\InvalidArgumentException if $text is neither a string or stream.
6473 */
6574 private function createBody ($ text ) : StreamInterface
6675 {
76+ $ body = null ;
77+
78+ if (is_string ($ text )) {
79+ $ body = $ this ->createBodyFromString ($ text );
80+ }
81+
6782 if ($ text instanceof StreamInterface) {
68- return $ text ;
83+ $ body = $ text ;
6984 }
7085
71- if (! is_string ($ text )) {
86+ return $ body ;
87+ }
88+
89+ /**
90+ * Create the CSV message body from a CSV string.
91+ *
92+ * @param string $text
93+ * @return StreamInterface
94+ * @throws Exception\InvalidArgumentException if $text is neither a string or stream.
95+ */
96+ private function createBodyFromString (string $ text ) : StreamInterface
97+ {
98+ if (empty ($ text )) {
7299 throw new Exception \InvalidArgumentException (sprintf (
73100 'Invalid CSV content (%s) provided to %s ' ,
74101 (is_object ($ text ) ? get_class ($ text ) : gettype ($ text )),
@@ -81,4 +108,43 @@ private function createBody($text) : StreamInterface
81108 $ body ->rewind ();
82109 return $ body ;
83110 }
111+
112+ /**
113+ * Get a fully rendered CSV record
114+ * @param array $row
115+ * @param array $last
116+ * @return string
117+ */
118+ public function getRecord (array $ row , array $ last ): string
119+ {
120+ $ lineEnding = $ this ->getLineEnding ($ row , $ last );
121+ $ row = implode ($ this ->options ['field_separator ' ] ?? self ::DEFAULT_SEPARATOR , $ row );
122+
123+ return $ row . $ lineEnding ;
124+ }
125+
126+ /**
127+ * Is the current row the last one
128+ * @param array $current
129+ * @param array $last
130+ * @return bool
131+ */
132+ public function isLastLine ($ current , $ last )
133+ {
134+ return ($ current == $ last );
135+ }
136+
137+ /**
138+ * @param array $row
139+ * @param array $last
140+ * @return string
141+ */
142+ public function getLineEnding (array $ row , array $ last ): string
143+ {
144+ $ lineEnding = ($ this ->isLastLine ($ row , $ last ))
145+ ? ''
146+ : $ this ->options ['line_ending ' ] ?? self ::DEFAULT_LINE_ENDING ;
147+
148+ return $ lineEnding ;
149+ }
84150}
0 commit comments