CsvFormatter.php 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * @package Grav\Framework\File\Formatter
  5. *
  6. * @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
  7. * @license MIT License; see LICENSE file for details.
  8. */
  9. namespace Grav\Framework\File\Formatter;
  10. use Grav\Framework\File\Interfaces\FileFormatterInterface;
  11. class CsvFormatter extends AbstractFormatter
  12. {
  13. /**
  14. * IniFormatter constructor.
  15. * @param array $config
  16. */
  17. public function __construct(array $config = [])
  18. {
  19. $config += [
  20. 'file_extension' => ['.csv', '.tsv'],
  21. 'delimiter' => ','
  22. ];
  23. parent::__construct($config);
  24. }
  25. /**
  26. * Returns delimiter used to both encode and decode CSV.
  27. *
  28. * @return string
  29. */
  30. public function getDelimiter(): string
  31. {
  32. // Call fails on bad configuration.
  33. return $this->getConfig('delimiter');
  34. }
  35. /**
  36. * {@inheritdoc}
  37. * @see FileFormatterInterface::encode()
  38. */
  39. public function encode($data, $delimiter = null): string
  40. {
  41. if (count($data) === 0) {
  42. return '';
  43. }
  44. $delimiter = $delimiter ?? $this->getDelimiter();
  45. $header = array_keys(reset($data));
  46. // Encode the field names
  47. $string = $this->encodeLine($header, $delimiter);
  48. // Encode the data
  49. foreach ($data as $row) {
  50. $string .= $this->encodeLine($row, $delimiter);
  51. }
  52. return $string;
  53. }
  54. /**
  55. * {@inheritdoc}
  56. * @see FileFormatterInterface::decode()
  57. */
  58. public function decode($data, $delimiter = null): array
  59. {
  60. $delimiter = $delimiter ?? $this->getDelimiter();
  61. $lines = preg_split('/\r\n|\r|\n/', $data);
  62. if ($lines === false) {
  63. throw new \RuntimeException('Decoding CSV failed');
  64. }
  65. // Get the field names
  66. $header = str_getcsv(array_shift($lines), $delimiter);
  67. // Get the data
  68. $list = [];
  69. foreach ($lines as $line) {
  70. $list[] = array_combine($header, str_getcsv($line, $delimiter));
  71. }
  72. return $list;
  73. }
  74. protected function encodeLine(array $line, $delimiter = null): string
  75. {
  76. foreach ($line as $key => &$value) {
  77. $value = $this->escape((string)$value);
  78. }
  79. unset($value);
  80. return implode($delimiter, $line). "\n";
  81. }
  82. protected function escape(string $value)
  83. {
  84. if (preg_match('/[,"\r\n]/u', $value)) {
  85. $value = '"' . preg_replace('/"/', '""', $value) . '"';
  86. }
  87. return $value;
  88. }
  89. }