MDL-14624:
[moodle-linuxchix.git] / lib / csvlib.class.php
blob0cca4b83a240925341435251ef2104f45fb2b59d
1 <?php
2 /*
3 * General csv import library.
4 * @author Petr Skoda
5 * @version $Id$
6 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
7 * @package moodlecore
8 */
10 /**
11 * Utitily class for importing of CSV files.
13 class csv_import_reader {
14 var $_iid;
15 var $_type;
16 var $_error;
17 var $_columns; //cached columns
18 var $_fp;
20 /**
21 * Contructor
22 * @param int $iid import identifier
23 * @param string $type which script imports?
25 function csv_import_reader($iid, $type) {
26 $this->_iid = $iid;
27 $this->_type = $type;
30 /**
31 * Parse this content
32 * @param string &$content passed by ref for memory reasons, unset after return
33 * @param string $encoding content encoding
34 * @param string $delimiter_name separator (comma, semicolon, colon, cfg)
35 * @param string $column_validation name of function for columns validation, must have one param $columns
36 * @return false if error, count of data lines if ok; use get_error() to get error string
38 function load_csv_content(&$content, $encoding, $delimiter_name, $column_validation=null) {
39 global $USER, $CFG;
41 $this->close();
42 $this->_error = null;
44 $textlib = textlib_get_instance();
46 $content = $textlib->convert($content, $encoding, 'utf-8');
47 // remove Unicode BOM from first line
48 $content = $textlib->trim_utf8_bom($content);
49 // Fix mac/dos newlines
50 $content = preg_replace('!\r\n?!', "\n", $content);
51 // is there anyting in file?
52 $columns = strtok($content, "\n");
53 if ($columns === false) {
54 $this->_error = get_string('csvemptyfile', 'error');
55 return false;
57 $csv_delimiter = csv_import_reader::get_delimiter($delimiter_name);
58 $csv_encode = csv_import_reader::get_encoded_delimiter($delimiter_name);
60 // process header - list of columns
61 $columns = explode($csv_delimiter, $columns);
62 $col_count = count($columns);
63 if ($col_count === 0) {
64 $this->_error = get_string('csvemptyfile', 'error');
65 return false;
68 foreach ($columns as $key=>$value) {
69 $columns[$key] = str_replace($csv_encode, $csv_delimiter, trim($value));
71 if ($column_validation) {
72 $result = $column_validation($columns);
73 if ($result !== true) {
74 $this->_error = $result;
75 return false;
78 $this->_columns = $columns; // cached columns
80 // open file for writing
81 $filename = $CFG->dataroot.'/temp/csvimport/'.$this->_type.'/'.$USER->id.'/'.$this->_iid;
82 $fp = fopen($filename, "w");
83 fwrite($fp, serialize($columns)."\n");
85 // again - do we have any data for processing?
86 $line = strtok("\n");
87 $data_count = 0;
88 while ($line !== false) {
89 $line = explode($csv_delimiter, $line);
90 foreach ($line as $key=>$value) {
91 $line[$key] = str_replace($csv_encode, $csv_delimiter, trim($value));
93 if (count($line) !== $col_count) {
94 // this is critical!!
95 $this->_error = get_string('csvweirdcolumns', 'error');
96 fclose($fp);
97 $this->cleanup();
98 return false;
100 fwrite($fp, serialize($line)."\n");
101 $data_count++;
102 $line = strtok("\n");
105 fclose($fp);
106 return $data_count;
110 * Returns list of columns
111 * @return array
113 function get_columns() {
114 if (isset($this->_columns)) {
115 return $this->_columns;
118 global $USER, $CFG;
120 $filename = $CFG->dataroot.'/temp/csvimport/'.$this->_type.'/'.$USER->id.'/'.$this->_iid;
121 if (!file_exists($filename)) {
122 return false;
124 $fp = fopen($filename, "r");
125 $line = fgets($fp);
126 fclose($fp);
127 if ($line === false) {
128 return false;
130 $this->_columns = unserialize($line);
131 return $this->_columns;
135 * Init iterator.
137 function init() {
138 global $CFG, $USER;
140 if (!empty($this->_fp)) {
141 $this->close();
143 $filename = $CFG->dataroot.'/temp/csvimport/'.$this->_type.'/'.$USER->id.'/'.$this->_iid;
144 if (!file_exists($filename)) {
145 return false;
147 if (!$this->_fp = fopen($filename, "r")) {
148 return false;
150 //skip header
151 return (fgets($this->_fp) !== false);
155 * Get next line
156 * @return array of values
158 function next() {
159 if (empty($this->_fp) or feof($this->_fp)) {
160 return false;
162 if ($ser = fgets($this->_fp)) {
163 return unserialize($ser);
164 } else {
165 return false;
170 * Release iteration related resources
172 function close() {
173 if (!empty($this->_fp)) {
174 fclose($this->_fp);
175 $this->_fp = null;
180 * Get last error
181 * @return string error text of null if none
183 function get_error() {
184 return $this->_error;
188 * Cleanup temporary data
189 * @static if $full=true
190 * @param boolean true menasdo a full cleanup - all sessions for current user, false only the active iid
192 function cleanup($full=false) {
193 global $USER, $CFG;
195 if ($full) {
196 @remove_dir($CFG->dataroot.'/temp/csvimport/'.$this->_type.'/'.$USER->id);
197 } else {
198 @unlink($CFG->dataroot.'/temp/csvimport/'.$this->_type.'/'.$USER->id.'/'.$this->_iid);
203 * Get list of cvs delimiters
204 * @static
205 * @return array suitable for selection box
207 function get_delimiter_list() {
208 $delimiters = array('comma'=>',', 'semicolon'=>';', 'colon'=>':', 'tab'=>'\\t');
209 if (isset($CFG->CSV_DELIMITER) and strlen($CFG->CSV_DELIMITER) === 1 and !in_array($CFG->CSV_DELIMITER, $delimiters)) {
210 $delimiters['cfg'] = $CFG->CSV_DELIMITER;
212 return $delimiters;
216 * Get delimiter character
217 * @static
218 * @param string separator name
219 * @return char delimiter char
221 function get_delimiter($delimiter_name) {
222 switch ($delimiter_name) {
223 case 'colon': return ':';
224 case 'semicolon': return ';';
225 case 'tab': return "\t";
226 case 'cfg': if (isset($CFG->CSV_DELIMITER)) { return $CFG->CSV_DELIMITER; } // no break; fall back to comma
227 case 'comma': return ',';
232 * Get encoded delimiter character
233 * @static
234 * @param string separator name
235 * @return char encoded delimiter char
237 function get_encoded_delimiter($delimiter_name) {
238 global $CFG;
239 if ($delimiter_name == 'cfg' and isset($CFG->CSV_ENCODE)) {
240 return $CFG->CSV_ENCODE;
242 $delimiter = csv_import_reader::get_delimiter($delimiter_name);
243 return '&#'.ord($delimiter);
247 * Create new import id
248 * @static
249 * @param string who imports?
250 * @return int iid
252 function get_new_iid($type) {
253 global $USER;
255 if (!$filename = make_upload_directory('temp/csvimport/'.$type.'/'.$USER->id, false)) {
256 error('Can not create temporary upload directory - verify moodledata permissions!');
259 // use current (non-conflicting) time stamp
260 $iiid = time();
261 while (file_exists($filename.'/'.$iiid)) {
262 $iiid--;
265 return $iiid;