4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\Serializer\Encoder;
14 use Symfony\Component\Serializer\Exception\InvalidArgumentException;
19 * @author Kévin Dunglas <dunglas@gmail.com>
21 class CsvEncoder implements EncoderInterface, DecoderInterface
28 private $keySeparator;
31 * @param string $delimiter
32 * @param string $enclosure
33 * @param string $escapeChar
34 * @param string $keySeparator
36 public function __construct($delimiter = ',', $enclosure = '"', $escapeChar = '\\', $keySeparator = '.')
38 $this->delimiter = $delimiter;
39 $this->enclosure = $enclosure;
40 $this->escapeChar = $escapeChar;
41 $this->keySeparator = $keySeparator;
47 public function encode($data, $format, array $context = array())
49 $handle = fopen('php://temp,', 'w+');
51 if (!is_array($data)) {
52 $data = array(array($data));
53 } elseif (empty($data)) {
54 $data = array(array());
56 // Sequential arrays of arrays are considered as collections
58 foreach ($data as $key => $value) {
59 if ($i !== $key || !is_array($value)) {
69 foreach ($data as $value) {
71 $this->flatten($value, $result);
73 if (null === $headers) {
74 $headers = array_keys($result);
75 fputcsv($handle, $headers, $this->delimiter, $this->enclosure, $this->escapeChar);
76 } elseif (array_keys($result) !== $headers) {
77 throw new InvalidArgumentException('To use the CSV encoder, each line in the data array must have the same structure. You may want to use a custom normalizer class to normalize the data format before passing it to the CSV encoder.');
80 fputcsv($handle, $result, $this->delimiter, $this->enclosure, $this->escapeChar);
84 $value = stream_get_contents($handle);
93 public function supportsEncoding($format)
95 return self::FORMAT === $format;
101 public function decode($data, $format, array $context = array())
103 $handle = fopen('php://temp', 'r+');
104 fwrite($handle, $data);
111 while (false !== ($cols = fgetcsv($handle, 0, $this->delimiter, $this->enclosure, $this->escapeChar))) {
112 $nbCols = count($cols);
114 if (null === $headers) {
115 $nbHeaders = $nbCols;
117 foreach ($cols as $col) {
118 $headers[] = explode($this->keySeparator, $col);
125 for ($i = 0; ($i < $nbCols) && ($i < $nbHeaders); ++$i) {
126 $depth = count($headers[$i]);
128 for ($j = 0; $j < $depth; ++$j) {
129 // Handle nested arrays
130 if ($j === ($depth - 1)) {
131 $arr[$headers[$i][$j]] = $cols[$i];
136 if (!isset($arr[$headers[$i][$j]])) {
137 $arr[$headers[$i][$j]] = array();
140 $arr = &$arr[$headers[$i][$j]];
148 if (empty($result) || isset($result[1])) {
152 // If there is only one data line in the document, return it (the line), the result is not considered as a collection
159 public function supportsDecoding($format)
161 return self::FORMAT === $format;
165 * Flattens an array and generates keys including the path.
167 * @param array $array
168 * @param array $result
169 * @param string $parentKey
171 private function flatten(array $array, array &$result, $parentKey = '')
173 foreach ($array as $key => $value) {
174 if (is_array($value)) {
175 $this->flatten($value, $result, $parentKey.$key.$this->keySeparator);
177 $result[$parentKey.$key] = $value;