1<?php
2namespace Luracast\Restler\Format;
3
4
5use Luracast\Restler\Data\Obj;
6use Luracast\Restler\RestException;
7
8/**
9 * Comma Separated Value Format
10 *
11 * @category   Framework
12 * @package    Restler
13 * @subpackage format
14 * @author     R.Arul Kumaran <arul@luracast.com>
15 * @copyright  2010 Luracast
16 * @license    http://www.opensource.org/licenses/lgpl-license.php LGPL
17 * @link       http://luracast.com/products/restler/
18 *
19 */
20class CsvFormat extends Format implements iDecodeStream
21{
22
23    const MIME = 'text/csv';
24    const EXTENSION = 'csv';
25    public static $delimiter = ',';
26    public static $enclosure = '"';
27    public static $escape = '\\';
28    public static $haveHeaders = null;
29
30    /**
31     * Encode the given data in the csv format
32     *
33     * @param array   $data
34     *            resulting data that needs to
35     *            be encoded in the given format
36     * @param boolean $humanReadable
37     *            set to TRUE when restler
38     *            is not running in production mode. Formatter has to
39     *            make the encoded output more human readable
40     *
41     * @return string encoded string
42     *
43     * @throws RestException 500 on unsupported data
44     */
45    public function encode($data, $humanReadable = false)
46    {
47        $char = Obj::$separatorChar;
48        Obj::$separatorChar = false;
49        $data = Obj::toArray($data);
50        Obj::$separatorChar = $char;
51        if (is_array($data) && array_values($data) == $data) {
52            //if indexed array
53            $lines = array();
54            $row = array_shift($data);
55            if (array_values($row) != $row) {
56                $lines[] = static::putRow(array_keys($row));
57            }
58            $lines[] = static::putRow(array_values($row));
59            foreach ($data as $row) {
60                $lines[] = static::putRow(array_values($row));
61            }
62            return implode(PHP_EOL, $lines) . PHP_EOL;
63        }
64        throw new RestException(
65            500,
66            'Unsupported data for ' . strtoupper(static::EXTENSION) . ' format'
67        );
68    }
69
70    protected static function putRow($data)
71    {
72        $fp = fopen('php://temp', 'r+');
73        fputcsv($fp, $data, static::$delimiter, static::$enclosure);
74        rewind($fp);
75        $data = fread($fp, 1048576);
76        fclose($fp);
77        return rtrim($data, PHP_EOL);
78    }
79
80    /**
81     * Decode the given data from the csv format
82     *
83     * @param string $data
84     *            data sent from client to
85     *            the api in the given format.
86     *
87     * @return array associative array of the parsed data
88     */
89    public function decode($data)
90    {
91        $decoded = array();
92
93        if (empty($data)) {
94            return $decoded;
95        }
96
97        $lines = array_filter(explode(PHP_EOL, $data));
98
99        $keys = false;
100        $row = static::getRow(array_shift($lines));
101
102        if (is_null(static::$haveHeaders)) {
103            //try to guess with the given data
104            static::$haveHeaders = !count(array_filter($row, 'is_numeric'));
105        }
106
107        static::$haveHeaders ? $keys = $row : $decoded[] = $row;
108
109        while (($row = static::getRow(array_shift($lines), $keys)) !== FALSE)
110            $decoded [] = $row;
111
112        $char = Obj::$separatorChar;
113        Obj::$separatorChar = false;
114        $decoded = Obj::toArray($decoded);
115        Obj::$separatorChar = $char;
116        return $decoded;
117    }
118
119    protected static function getRow($data, $keys = false)
120    {
121        if (empty($data)) {
122            return false;
123        }
124        $line = str_getcsv(
125            $data,
126            static::$delimiter,
127            static::$enclosure,
128            static::$escape
129        );
130
131        $row = array();
132        foreach ($line as $key => $value) {
133            if (is_numeric($value))
134                $value = floatval($value);
135            if ($keys) {
136                if (isset($keys [$key]))
137                    $row [$keys [$key]] = $value;
138            } else {
139                $row [$key] = $value;
140            }
141        }
142        if ($keys) {
143            for ($i = count($row); $i < count($keys); $i++) {
144                $row[$keys[$i]] = null;
145            }
146        }
147        return $row;
148    }
149
150    /**
151     * Decode the given data stream
152     *
153     * @param string $stream A stream resource with data
154     *                       sent from client to the api
155     *                       in the given format.
156     *
157     * @return array associative array of the parsed data
158     */
159    public function decodeStream($stream)
160    {
161        $decoded = array();
162
163        $keys = false;
164        $row = static::getRow(stream_get_line($stream, 0, PHP_EOL));
165        if (is_null(static::$haveHeaders)) {
166            //try to guess with the given data
167            static::$haveHeaders = !count(array_filter($row, 'is_numeric'));
168        }
169
170        static::$haveHeaders ? $keys = $row : $decoded[] = $row;
171
172        while (($row = static::getRow(stream_get_line($stream, 0, PHP_EOL), $keys)) !== FALSE)
173            $decoded [] = $row;
174
175        $char = Obj::$separatorChar;
176        Obj::$separatorChar = false;
177        $decoded = Obj::toArray($decoded);
178        Obj::$separatorChar = $char;
179        return $decoded;
180    }
181}