PageRenderTime 56ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/runtime/lib/parser/PropelCSVParser.php

https://bitbucket.org/aagraz/propel
PHP | 314 lines | 179 code | 29 blank | 106 comment | 21 complexity | 4935031c278986a7b66fd51ba501e301 MD5 | raw file
  1. <?php
  2. /**
  3. * This file is part of the Propel package.
  4. * For the full copyright and license information, please view the LICENSE
  5. * file that was distributed with this source code.
  6. *
  7. * @license MIT License
  8. */
  9. /**
  10. * CSV parser. Converts data between associative array and CSV formats.
  11. * CSV parsing code borrowed from php-csv-utils by Luke Visinoni
  12. * http://code.google.com/p/php-csv-utils/
  13. *
  14. * @author Francois Zaninotto
  15. * @package propel.runtime.parser
  16. */
  17. class PropelCSVParser extends PropelParser
  18. {
  19. const QUOTE_NONE = 0;
  20. const QUOTE_ALL = 1;
  21. const QUOTE_NONNUMERIC = 2;
  22. const QUOTE_MINIMAL = 3;
  23. // these settings are predefined for Excel CSV format
  24. public $delimiter = ',';
  25. public $lineTerminator = "\r\n";
  26. public $quotechar = '"';
  27. public $escapechar = "\\";
  28. public $quoting = self::QUOTE_MINIMAL;
  29. /**
  30. * Converts data from an associative array to CSV.
  31. *
  32. * @param array $array Source data to convert
  33. * @param boolean $isList Whether the input data contains more than one row
  34. * @param boolean $includeHeading Whether the output should contain a heading line
  35. *
  36. * @return string Converted data, as a CSV string
  37. */
  38. public function fromArray($array, $isList = false, $includeHeading = true)
  39. {
  40. $rows = array();
  41. if ($isList) {
  42. if ($includeHeading) {
  43. $rows[] = implode($this->formatRow(array_keys(reset($array))), $this->delimiter);
  44. }
  45. foreach ($array as $row) {
  46. $rows[] = implode($this->formatRow($row), $this->delimiter);
  47. }
  48. } else {
  49. if ($includeHeading) {
  50. $rows[] = implode($this->formatRow(array_keys($array)), $this->delimiter);
  51. }
  52. $rows[] = implode($this->formatRow($array), $this->delimiter);
  53. }
  54. return implode($rows, $this->lineTerminator) . $this->lineTerminator;
  55. }
  56. public function listFromArray($array)
  57. {
  58. return $this->fromArray($array, true);
  59. }
  60. /**
  61. * Accepts a row of data and returns it formatted
  62. *
  63. * @param array $row An array of data to be formatted for output to the file
  64. *
  65. * @return array The formatted array
  66. */
  67. protected function formatRow($row)
  68. {
  69. foreach ($row as &$column) {
  70. if (!is_scalar($column)) {
  71. $column = $this->serialize($column);
  72. }
  73. switch ($this->quoting) {
  74. case self::QUOTE_NONE:
  75. // do nothing... no quoting is happening here
  76. break;
  77. case self::QUOTE_ALL:
  78. $column = $this->quote($this->escape($column));
  79. break;
  80. case self::QUOTE_NONNUMERIC:
  81. if (preg_match("/[^0-9]/", $column)) {
  82. $column = $this->quote($this->escape($column));
  83. }
  84. break;
  85. case self::QUOTE_MINIMAL:
  86. default:
  87. if ($this->containsSpecialChars($column)) {
  88. $column = $this->quote($this->escape($column));
  89. }
  90. break;
  91. }
  92. }
  93. return $row;
  94. }
  95. /**
  96. * Escapes a column (escapes quotechar with escapechar)
  97. *
  98. * @param string $input A single value to be escaped for output
  99. *
  100. * @return string Escaped input value
  101. */
  102. protected function escape($input)
  103. {
  104. return str_replace(
  105. $this->quotechar,
  106. $this->escapechar . $this->quotechar,
  107. $input
  108. );
  109. }
  110. /**
  111. * Quotes a column with quotechar
  112. *
  113. * @param string $input A single value to be quoted for output
  114. *
  115. * @return string Quoted input value
  116. */
  117. protected function quote($input)
  118. {
  119. return $this->quotechar . $input . $this->quotechar;
  120. }
  121. /**
  122. * Returns true if input contains quotechar, delimiter or any of the characters in lineTerminator
  123. *
  124. * @param string $input A single value to be checked for special characters
  125. *
  126. * @return boolean True if contains any special characters
  127. */
  128. protected function containsSpecialChars($input)
  129. {
  130. $special_chars = str_split($this->lineTerminator, 1);
  131. $special_chars[] = $this->quotechar;
  132. $special_chars[] = $this->delimiter;
  133. foreach ($special_chars as $char) {
  134. if (strpos($input, $char) !== false) {
  135. return true;
  136. }
  137. }
  138. return false;
  139. }
  140. /**
  141. * Serializes a value to place it into a CSV output
  142. *
  143. * @param mixed $input
  144. *
  145. * @return string
  146. */
  147. protected function serialize($input)
  148. {
  149. return serialize($input);
  150. }
  151. /**
  152. * Alias for PropelCSVParser::fromArray()
  153. *
  154. * @param array $array Source data to convert
  155. * @param boolean $isList Whether the input data contains more than one row
  156. * @param boolean $includeHeading Whether the output should contain a heading line
  157. *
  158. * @return string Converted data, as a CSV string
  159. */
  160. public function toCSV($array, $isList = false, $includeHeading = true)
  161. {
  162. return $this->fromArray($array, $isList, $includeHeading);
  163. }
  164. /**
  165. * Converts data from CSV to an associative array.
  166. *
  167. * @param string $data Source data to convert, as a CSV string
  168. * @param boolean $isList Whether the input data contains more than one row
  169. * @param boolean $includeHeading Whether the input contains a heading line
  170. *
  171. * @return array Converted data
  172. */
  173. public function toArray($data, $isList = false, $includeHeading = true)
  174. {
  175. $rows = explode($this->lineTerminator, $data);
  176. if ($includeHeading) {
  177. $heading = array_shift($rows);
  178. $keys = explode($this->delimiter, $heading);
  179. } else {
  180. $keys = range(0, count($this->getColumns($rows[0])) - 1);
  181. }
  182. if ($isList) {
  183. $array = array();
  184. foreach ($rows as $row) {
  185. $values = $this->cleanupRow($this->getColumns($row));
  186. if ($values !== array()) {
  187. $array[] = array_combine($keys, $values);
  188. }
  189. }
  190. } else {
  191. $values = $this->cleanupRow($this->getColumns(array_shift($rows)));
  192. if ($keys === array('') && $values === array()) {
  193. $array = array();
  194. } else {
  195. if (count($keys) > count($values)) {
  196. // empty values at the end of the row are not match bu the getColumns() regexp
  197. $values = array_pad($values, count($keys), null);
  198. }
  199. $array = array_combine($keys, $values);
  200. }
  201. }
  202. return $array;
  203. }
  204. public function listToArray($array)
  205. {
  206. return $this->toArray($array, true);
  207. }
  208. protected function getColumns($row)
  209. {
  210. $delim = preg_quote($this->delimiter, '/');
  211. preg_match_all('/(".+?"|[^' . $delim . ']+)(' . $delim . '|$)/', $row, $matches);
  212. return $matches[1];
  213. }
  214. /**
  215. * Accepts a formatted row of data and returns it raw
  216. *
  217. * @param array An array of data from a CSV output
  218. *
  219. * @return array The cleaned up array
  220. */
  221. protected function cleanupRow($row)
  222. {
  223. foreach ($row as $key => $column) {
  224. if ($this->isQuoted($column)) {
  225. $column = $this->unescape($this->unquote($column));
  226. }
  227. if ($this->isSerialized($column)) {
  228. $column = $this->unserialize($column);
  229. }
  230. if ($column === 'N;') {
  231. $column = null;
  232. }
  233. $row[$key] = $column;
  234. }
  235. return $row;
  236. }
  237. protected function isQuoted($input)
  238. {
  239. $quote = preg_quote($this->quotechar, '/');
  240. return preg_match('/^' . $quote . '.*' . $quote . '$/', $input);
  241. }
  242. protected function unescape($input)
  243. {
  244. return str_replace(
  245. $this->escapechar . $this->quotechar,
  246. $this->quotechar,
  247. $input
  248. );
  249. }
  250. protected function unquote($input)
  251. {
  252. return trim($input, $this->quotechar);
  253. }
  254. /**
  255. * Checks whether a value from CSV output is serialized
  256. */
  257. protected function isSerialized($input)
  258. {
  259. return preg_match('/^\w\:\d+\:\{/', $input);
  260. }
  261. /**
  262. * Unserializes a value from CSV output
  263. *
  264. * @param string $input
  265. *
  266. * @return mixed
  267. */
  268. protected function unserialize($input)
  269. {
  270. return unserialize($input);
  271. }
  272. /**
  273. * Alias for PropelCSVParser::toArray()
  274. *
  275. * @param string $data Source data to convert, as a CSV string
  276. * @param boolean $isList Whether the input data contains more than one row
  277. * @param boolean $includeHeading Whether the input contains a heading line
  278. *
  279. * @return array Converted data
  280. */
  281. public function fromCSV($data, $isList = false, $includeHeading = true)
  282. {
  283. return $this->toArray($data, $isList, $includeHeading);
  284. }
  285. }