f30d08f941da7cf69850f69296116d9902207b1e
[yaffs-website] / vendor / symfony / serializer / Encoder / CsvEncoder.php
1 <?php
2
3 /*
4  * This file is part of the Symfony package.
5  *
6  * (c) Fabien Potencier <fabien@symfony.com>
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 namespace Symfony\Component\Serializer\Encoder;
13
14 use Symfony\Component\Serializer\Exception\InvalidArgumentException;
15
16 /**
17  * Encodes CSV data.
18  *
19  * @author Kévin Dunglas <dunglas@gmail.com>
20  */
21 class CsvEncoder implements EncoderInterface, DecoderInterface
22 {
23     const FORMAT = 'csv';
24
25     private $delimiter;
26     private $enclosure;
27     private $escapeChar;
28     private $keySeparator;
29
30     /**
31      * @param string $delimiter
32      * @param string $enclosure
33      * @param string $escapeChar
34      * @param string $keySeparator
35      */
36     public function __construct($delimiter = ',', $enclosure = '"', $escapeChar = '\\', $keySeparator = '.')
37     {
38         $this->delimiter = $delimiter;
39         $this->enclosure = $enclosure;
40         $this->escapeChar = $escapeChar;
41         $this->keySeparator = $keySeparator;
42     }
43
44     /**
45      * {@inheritdoc}
46      */
47     public function encode($data, $format, array $context = array())
48     {
49         $handle = fopen('php://temp,', 'w+');
50
51         if (!is_array($data)) {
52             $data = array(array($data));
53         } elseif (empty($data)) {
54             $data = array(array());
55         } else {
56             // Sequential arrays of arrays are considered as collections
57             $i = 0;
58             foreach ($data as $key => $value) {
59                 if ($i !== $key || !is_array($value)) {
60                     $data = array($data);
61                     break;
62                 }
63
64                 ++$i;
65             }
66         }
67
68         $headers = null;
69         foreach ($data as $value) {
70             $result = array();
71             $this->flatten($value, $result);
72
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.');
78             }
79
80             fputcsv($handle, $result, $this->delimiter, $this->enclosure, $this->escapeChar);
81         }
82
83         rewind($handle);
84         $value = stream_get_contents($handle);
85         fclose($handle);
86
87         return $value;
88     }
89
90     /**
91      * {@inheritdoc}
92      */
93     public function supportsEncoding($format)
94     {
95         return self::FORMAT === $format;
96     }
97
98     /**
99      * {@inheritdoc}
100      */
101     public function decode($data, $format, array $context = array())
102     {
103         $handle = fopen('php://temp', 'r+');
104         fwrite($handle, $data);
105         rewind($handle);
106
107         $headers = null;
108         $nbHeaders = 0;
109         $result = array();
110
111         while (false !== ($cols = fgetcsv($handle, 0, $this->delimiter, $this->enclosure, $this->escapeChar))) {
112             $nbCols = count($cols);
113
114             if (null === $headers) {
115                 $nbHeaders = $nbCols;
116
117                 foreach ($cols as $col) {
118                     $headers[] = explode($this->keySeparator, $col);
119                 }
120
121                 continue;
122             }
123
124             $item = array();
125             for ($i = 0; ($i < $nbCols) && ($i < $nbHeaders); ++$i) {
126                 $depth = count($headers[$i]);
127                 $arr = &$item;
128                 for ($j = 0; $j < $depth; ++$j) {
129                     // Handle nested arrays
130                     if ($j === ($depth - 1)) {
131                         $arr[$headers[$i][$j]] = $cols[$i];
132
133                         continue;
134                     }
135
136                     if (!isset($arr[$headers[$i][$j]])) {
137                         $arr[$headers[$i][$j]] = array();
138                     }
139
140                     $arr = &$arr[$headers[$i][$j]];
141                 }
142             }
143
144             $result[] = $item;
145         }
146         fclose($handle);
147
148         if (empty($result) || isset($result[1])) {
149             return $result;
150         }
151
152         // If there is only one data line in the document, return it (the line), the result is not considered as a collection
153         return $result[0];
154     }
155
156     /**
157      * {@inheritdoc}
158      */
159     public function supportsDecoding($format)
160     {
161         return self::FORMAT === $format;
162     }
163
164     /**
165      * Flattens an array and generates keys including the path.
166      *
167      * @param array  $array
168      * @param array  $result
169      * @param string $parentKey
170      */
171     private function flatten(array $array, array &$result, $parentKey = '')
172     {
173         foreach ($array as $key => $value) {
174             if (is_array($value)) {
175                 $this->flatten($value, $result, $parentKey.$key.$this->keySeparator);
176             } else {
177                 $result[$parentKey.$key] = $value;
178             }
179         }
180     }
181 }