6 private function __construct() { }
8 public static function readFile($file, $enclose = '"', $terminate = ',', $escape = '\\', $pascalEscapes = true, $charset = 'UTF-8') {
9 return self::readString(file_get_contents($file), $enclose, $terminate, $escape, $pascalEscapes, $charset);
12 public static function readString($string, $enclose = '"', $terminate = ',', $escape = '\\', $pascalEscapes = true, $charset = 'UTF-8') {
15 $quoteInQuote = false;
18 $len = strlen($string);
21 $result = new CSVReader();
27 for ($i = 0; $i < $len; ++$i) {
38 if ($quoteInQuote && $c != $enclose) {
39 $quoteInQuote = false;
42 if ($lastWasCR && $c != "\n") {
44 $line[] = iconv($charset, 'UTF-8//TRANSLIT', $value);
45 $result->rows[] = $line;
52 } else if ($pascalEscapes) {
54 $quoteInQuote = false;
68 if ($c == $terminate) {
69 $line[] = iconv($charset, 'UTF-8//TRANSLIT', $value);
71 } else if ($c == "\r") {
73 } else if ($c == "\n") {
74 $line[] = iconv($charset, 'UTF-8//TRANSLIT', $value);
75 $result->rows[] = $line;
84 $line[] = iconv($charset, 'UTF-8//TRANSLIT', $value);
87 $result->rows[] = $line;
92 public function free() {
93 $this->rows = array();
96 public function countFields() {
97 if ($this->rowValid()) {
98 return count($this->currentRow());
99 } else if (!empty($this->rows)) {
100 return count($this->rows[0]);
106 public function countRows() {
107 return count($this->rows);
110 public function getFieldName($columnOffset) {
111 if (isset($this->names[$columnOffset])) {
112 return $this->names[$columnOffset];
114 throw OutOfBoundsException('cannot determine name for column at offset '.$columnOffset);
118 public function next($forSure = false) {
120 if ($this->rowValid()) {
122 } else if ($forSure) {
123 throw new OutOfBoundsException('there is no next row in this result set');
129 public function has($columnOffset) {
130 return $this->rowValid() && array_key_exists($columnOffset, $this->currentRow());
133 public function get($columnOffset) {
134 if (!isset($this->rows[$this->rowCursor])) {
135 throw new OutOfBoundsException('cannot get column '.$columnOffset.' of row '.$this->rowCursor.': invalid row');
137 if (!isset($this->rows[$this->rowCursor][$columnOffset])) {
138 throw new OutOfBoundsException('cannot get column '.$columnOffset.' of row '.$this->rowCursor.': invalid column');
140 return $this->rows[$this->rowCursor][$columnOffset];
143 public function loadColumnNamesFromFirstRow() {
144 if (isset($this->rows[0])) {
145 $this->setColumnNames($this->rows[0]);
147 throw new RuntimeException('there is no first row');
151 public function setColumnNames($names) {
152 $this->names = $names;
155 public function getNamed($name) {
156 if (in_array($name, $this->names)) {
157 return $this->get(array_search($name, $this->names));
159 throw new OutOfBoundsException($name.' is not a valid column name');
163 public function getNamedInt($name) {
164 return intval($this->getNamed($name));
167 public function getRowNumber() {
168 return $this->rowCursor + 1;
171 private function rowValid() {
172 return $this->rowCursor > -1 && $this->rowCursor < $this->countRows();
175 private function currentRow() {
176 return $this->rows[$this->rowCursor];
179 private $names = array();
180 private $rows = array();
181 private $rowCursor = -1;
185 function writeTicket(CSVReader $csv, $filename) {
186 $file = fopen($filename, 'w');
187 fputs($file, $csv->getNamed('Tracker').' #'.$csv->getnamed('#')
188 .' - '.$csv->getNamed('Subject').PHP_EOL.PHP_EOL);
189 fputs($file, ' '.str_pad(' Status: '.$csv->getNamed('Status'), 35)
190 .' Start date: '.$csv->getNamed('Start date').PHP_EOL);
191 fputs($file, ' '.str_pad('Priority: '.$csv->getNamed('Priority'), 35)
192 .' Due date: '.$csv->getNamed('Due date').PHP_EOL);
193 fputs($file, ' '.str_pad('Assignee: '.$csv->getNamed('Assignee'), 35)
194 .' Done: '.$csv->getNamed('% Done').'%'.PHP_EOL);
195 fputs($file, ' '.str_pad('Category: '.$csv->getNamed('Category'), 35)
196 .'Target version: '.$csv->getNamed('Target version').PHP_EOL.PHP_EOL);
197 $description = $csv->getNamed('Description');
198 $description = wordwrap(trim(str_replace(
199 array("\r\n", "\r", "\n"), array("\n", "\n", PHP_EOL), $description)));
200 fputs($file, $description.PHP_EOL);
202 $updated = DateTime::createFromFormat(
203 'm/d/Y h:i a', // 01/03/2013 05:56 am
204 $csv->getNamed('Updated'),
205 new DateTimeZone('Europe/Berlin'))->getTimestamp();
206 touch($filename, $updated);
209 $dir = dirname(dirname(__FILE__)).'/issues';
212 echo 'creating: ', $dir, PHP_EOL;
216 $csv = CSVReader::readFile('http://luke.redirectme.net/redmine/projects/l2e/issues.csv?columns=all&description=1',
217 '"', ',', '\\', true, 'CP1252');
218 $csv->loadColumnNamesFromFirstRow();
223 while ($csv->next()) {
224 $ids[] = $csv->getNamed('#');
225 $filename = $csv->getNamed('#').' '.$csv->getNamed('Subject');
226 $filepath = $dir.'/'.$filename;
227 if (!file_exists($filepath)) {
228 echo 'writing: ', $filename, PHP_EOL;
229 writeTicket($csv, $filepath);
232 $modified = filemtime($filepath);
233 $updated = DateTime::createFromFormat(
234 'm/d/Y h:i a', // 01/03/2013 05:56 am
235 $csv->getNamed('Updated'),
236 new DateTimeZone('Europe/Berlin'))->getTimestamp();
237 if ($updated > $modified) {
238 echo 'updating: ', $filename, PHP_EOL;
239 writeTicket($csv, $filepath);
243 $existing = scandir($dir);
245 foreach ($existing as $name) {
246 if (!is_file($dir.'/'.$name)) continue;
248 if ($id > 0 && !in_array($id, $ids)) {
249 echo 'removing: ', $name, PHP_EOL;
250 unlink($dir.'/'.$name);