Add DBAL 1.0.1 to TYPO3core. Do NOT make changes inside! See misc/core_svn_rules...
[Packages/TYPO3.CMS.git] / typo3 / sysext / dbal / handlers / class.tx_dbal_handler_xmldb.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2004-2009 Kasper Skaarhoj (kasper@typo3.com)
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27 /**
28 * Contains an example DBAL handler class
29 *
30 * $Id: class.tx_dbal_handler_xmldb.php 25889 2009-10-27 10:09:11Z xperseguers $
31 *
32 * @author Kasper Skaarhoj <kasper@typo3.com>
33 */
34 /**
35 * [CLASS/FUNCTION INDEX of SCRIPT]
36 *
37 *
38 *
39 * 74: class tx_dbal_handler_xmldb extends tx_dbal_sqlengine
40 * 91: function init($config, &$pObj)
41 * 128: function readDataSource($table)
42 * 157: function saveDataSource($table)
43 * 184: function xmlDB_writeStructure()
44 * 193: function xmlDB_readStructure()
45 *
46 * SECTION: SQL admin functions
47 * 217: function admin_get_tables()
48 * 242: function admin_get_fields($tableName)
49 * 276: function admin_get_keys($tableName)
50 * 314: function admin_query($query)
51 *
52 * TOTAL FUNCTIONS: 9
53 * (This index is automatically created/updated by the extension "extdeveval")
54 *
55 */
56
57
58
59
60
61
62
63
64
65
66 /**
67 * Example DBAL handler class
68 * Stores data in XML, not a database.
69 *
70 * @author Kasper Skaarhoj <kasper@typo3.com>
71 * @package TYPO3
72 * @subpackage tx_dbal
73 */
74 class tx_dbal_handler_xmldb extends tx_dbal_sqlengine {
75
76 var $config = array();
77 var $pObj; // Set from DBAL class.
78
79 // Database Storage directory:
80 var $DBdir = '';
81 var $DBstructure = array(
82 'tables' => array()
83 );
84
85 /**
86 * Initialize handler
87 *
88 * @param array Configuration from DBAL
89 * @param object Parent object
90 * @return void
91 */
92 function init($config, $pObj) {
93 $this->config = $config['config'];
94
95 $dbStorage = t3lib_div::getFileAbsFileName($this->config['DBstorageDir']);
96 if ($dbStorage && @is_dir($dbStorage) && ($dbStorage{strlen($dbStorage)-1} == '/')) {
97 $this->DBdir = $dbStorage;
98
99 // Read structure file:
100 if (@is_file($this->DBdir.'_STRUCTURE.xml')) {
101 $this->xmlDB_readStructure();
102 if (is_array($this->DBstructure)) {
103 return TRUE;
104 } else {
105 $this->errorStatus = 'The database structure array could not be loaded correctly. "_STRUCTURE.xml" may be corrupt';
106 }
107 } else {
108 $this->xmlDB_writeStructure();
109 if (@is_file($this->DBdir.'_STRUCTURE.xml')) {
110 return TRUE;
111 } else {
112 $this->errorStatus = 'The database structure file could not be created in dir "'.$dbStorage.'"';
113 }
114 }
115
116
117 } else $this->errorStatus = 'The database storage dir "'.$dbStorage.'" did not exist!';
118
119 debug($this->errorStatus,'XMLDB connect ERROR:');
120 return FALSE;
121 }
122
123 /**
124 * Setting table data (overriding function)
125 *
126 * @param string Table name
127 * @return void
128 */
129 function readDataSource($table) {
130
131 if (!$this->DBdir) {
132 $this->errorStatus = 'XMLdatabase not connected';
133 return FALSE;
134 }
135
136 // Reading table:
137 if (is_array($this->DBstructure['tables'][$table])) {
138 if (!isset($this->data[$table])) { // Checking if it has already been read
139 $newTableFile = 'TABLE_'.$table.'.xml';
140 if (@is_file($this->DBdir.$newTableFile)) {
141 $this->data[$table] = t3lib_div::xml2array(t3lib_div::getUrl($this->DBdir.$newTableFile));
142 if (!is_array($this->data[$table])) $this->data[$table] = array();
143 return TRUE;
144 } else {
145 $this->data[$table] = array();
146 $this->errorStatus = 'Tablefile for "'.$table.'" not found';
147 }
148 }
149 } else $this->errorStatus = 'Table "'.$table.'" not found';
150 }
151
152 /**
153 * Saving data source
154 *
155 * @param string Table name
156 * @return boolean True on success
157 */
158 function saveDataSource($table) {
159
160 if (!$this->DBdir) {
161 $this->errorStatus = 'XMLdatabase not connected';
162 return FALSE;
163 }
164
165 // Writing table:
166 if (is_array($this->DBstructure['tables'][$table])) {
167 $newTableFile = 'TABLE_'.$table.'.xml';
168 if (t3lib_div::getFileAbsFileName($this->DBdir.$newTableFile) && @is_file($this->DBdir.$newTableFile)) {
169
170 $storeInCharset = $GLOBALS['LANG']->charSet;
171 $xmlValue = t3lib_div::array2xml($this->data[$table],'',0,'T3xmlDB',0,array('useIndexTagForNum'=>'rec'));
172 $content = '<?xml version="1.0" encoding="'.$storeInCharset.'" standalone="yes" ?>'.chr(10).$xmlValue;
173 t3lib_div::writeFile($this->DBdir.$newTableFile,$content);
174
175 return TRUE;
176 } else $this->errorStatus = 'Tablefile for "'.$table.'" not found';
177 } else $this->errorStatus = 'Table "'.$table.'" not found';
178 }
179
180 /**
181 * Writing database structure
182 *
183 * @return void
184 */
185 function xmlDB_writeStructure() {
186 t3lib_div::writeFile($this->DBdir.'_STRUCTURE.xml', t3lib_div::array2xml($this->DBstructure,'',0,'T3xmlDBStructure',0,array('useIndexTagForNum'=>'item')));
187 }
188
189 /**
190 * Reading database structure
191 *
192 * @return void
193 */
194 function xmlDB_readStructure() {
195 $this->DBstructure = t3lib_div::xml2array(t3lib_div::getUrl($this->DBdir.'_STRUCTURE.xml'));
196 }
197
198
199
200
201
202
203
204
205
206 /**************************************
207 *
208 * SQL admin functions
209 * (For use in the Install Tool and Extension Manager)
210 *
211 **************************************/
212
213 /**
214 * Returns the list of tables from the database
215 *
216 * @return array Tables in an array (tablename is in both key and value)
217 * @todo Should return table details in value! see t3lib_db::admin_get_tables()
218 */
219 function admin_get_tables() {
220
221 if (!$this->DBdir) {
222 $this->errorStatus = 'XMLdatabase not connected';
223 return FALSE;
224 }
225
226 $whichTables = array();
227
228 // Traverse tables:
229 if (is_array($this->DBstructure['tables'])) {
230 foreach($this->DBstructure['tables'] as $tableName => $tableInfo) {
231 $whichTables[$tableName] = $tableName;
232 }
233 }
234
235 return $whichTables;
236 }
237
238 /**
239 * Returns information about each field in the $table
240 *
241 * @param string Table name
242 * @return array Field information in an associative array with fieldname => field row
243 */
244 function admin_get_fields($tableName) {
245
246 if (!$this->DBdir) {
247 $this->errorStatus = 'XMLdatabase not connected';
248 return FALSE;
249 }
250
251 $output = array();
252
253 // Traverse fields in table:
254 if (is_array($this->DBstructure['tables'][$tableName]) && is_array($this->DBstructure['tables'][$tableName]['FIELDS'])) {
255 foreach($this->DBstructure['tables'][$tableName]['FIELDS'] as $fieldName => $fieldInfo) {
256 $output[$fieldName] = array(
257 'Field' => $fieldName,
258 'Type' => $fieldInfo['definition']['fieldType'].
259 ($fieldInfo['definition']['value']?'('.$fieldInfo['definition']['value'].')':'').
260 (isset($fieldInfo['definition']['featureIndex']['UNSIGNED']) ? ' '.$fieldInfo['definition']['featureIndex']['UNSIGNED']['keyword'] : ''),
261 'Null' => isset($fieldInfo['definition']['featureIndex']['NOTNULL']) ? '' : 'Yes',
262 'Key' => '',
263 'Default' => $fieldInfo['definition']['featureIndex']['DEFAULT']['value'][0],
264 'Extra' => isset($fieldInfo['definition']['featureIndex']['AUTO_INCREMENT']) ? 'auto_increment' : '',
265 );
266 }
267 }
268
269 return $output;
270 }
271
272 /**
273 * Returns information about each index key in the $table
274 *
275 * @param string Table name
276 * @return array Key information in a numeric array
277 */
278 function admin_get_keys($tableName) {
279
280 if (!$this->DBdir) {
281 $this->errorStatus = 'XMLdatabase not connected';
282 return FALSE;
283 }
284
285 $output = array();
286
287 // Traverse fields in table:
288 if (is_array($this->DBstructure['tables'][$tableName]) && is_array($this->DBstructure['tables'][$tableName]['KEYS'])) {
289 foreach($this->DBstructure['tables'][$tableName]['KEYS'] as $keyName => $keyInfo) {
290 foreach($keyInfo as $seq => $keyField) {
291 $output[] = array(
292 'Table' => $tableName,
293 'Non_unique' => ($keyName=='PRIMARYKEY' ? 0 : 1),
294 'Key_name' => ($keyName=='PRIMARYKEY' ? 'PRIMARY' : $keyName),
295 'Seq_in_index' => $seq+1,
296 'Column_name' => $keyField,
297 'Collation' => 'A',
298 'Cardinality' => '',
299 'Sub_part' => '',
300 'Packed' => '',
301 'Comment' => '',
302 );
303 }
304 }
305 }
306
307 return $output;
308 }
309
310 /**
311 * mysql() wrapper function, used by the Install Tool and EM for all queries regarding management of the database!
312 *
313 * @param string Query to execute
314 * @return pointer Result pointer
315 */
316 function admin_query($query) {
317
318 if (!$this->DBdir) {
319 $this->errorStatus = 'XMLdatabase not connected';
320 return FALSE;
321 }
322
323 $parsedQuery = $this->parseSQL($query);
324 $table = $parsedQuery['TABLE'];
325
326 if (is_array($parsedQuery)) {
327 // Process query based on type:
328 switch($parsedQuery['type']) {
329 case 'CREATETABLE':
330 if (!is_array($this->DBstructure['tables'][$table])) {
331 $newTableFile = 'TABLE_'.$table.'.xml';
332 if (!@is_file($this->DBdir.$newTableFile)) {
333
334 // Write table file:
335 t3lib_div::writeFile($this->DBdir.$newTableFile, ''); // Create file
336 if (@is_file($this->DBdir.$newTableFile)) {
337
338 // Set and write structure
339 if (!is_array($this->DBstructure['tables'])) $this->DBstructure['tables']=array();
340 $this->DBstructure['tables'][(string)$table] = $parsedQuery; // I have some STRANGE behaviours with this variable - had to do this trick to make it work!
341
342 $this->xmlDB_writeStructure();
343 return TRUE;
344 } else $this->errorStatus = 'Table file "'.$this->DBdir.$newTableFile.'" could not be created! Cannot create table!';
345 } else $this->errorStatus = 'Table file "'.$this->DBdir.$newTableFile.'" already exists! Cannot create table!';
346 } else $this->errorStatus = 'Table "'.$table.'" already exists!';
347 break;
348 case 'ALTERTABLE':
349 if (is_array($this->DBstructure['tables'][$table])) {
350 switch($parsedQuery['action']) {
351 case 'ADD':
352 if (!is_array($this->DBstructure['tables'][$table]['FIELDS'][$parsedQuery['FIELD']])) {
353 $this->DBstructure['tables'][$table]['FIELDS'][$parsedQuery['FIELD']]['definition'] = $parsedQuery['definition']; // Adding field in the end of list.
354 $this->xmlDB_writeStructure();
355 return TRUE;
356
357 // TODO: Should traverse all data an add that field in arrays!
358 } else $this->errorStatus = 'Field "'.$parsedQuery['FIELD'].'" already exists!';
359 break;
360 case 'CHANGE':
361 if (is_array($this->DBstructure['tables'][$table]['FIELDS'])) {
362 if (is_array($this->DBstructure['tables'][$table]['FIELDS'][$parsedQuery['FIELD']])) {
363 $newFieldInfo = array();
364 foreach($this->DBstructure['tables'][$table]['FIELDS'] as $fieldName => $fieldDefinition) {
365 if (!strcmp($fieldName,$parsedQuery['FIELD'])) {
366
367 // New fieldname?
368 if ($parsedQuery['newField']) {
369 if (!is_array($this->DBstructure['tables'][$table]['FIELDS'][$parsedQuery['newField']])) {
370 $fieldName = $parsedQuery['newField'];
371 } else {
372 $this->errorStatus = 'A field in the table was already named "'.$parsedQuery['newField'].'"';
373 return FALSE;
374 }
375 }
376 // Set new field definition:
377 $fieldDefinition['definition'] = $parsedQuery['definition'];
378 }
379
380 // Set the whole thing in new var:
381 $newFieldInfo[$fieldName] = $fieldDefinition;
382 }
383 $this->DBstructure['tables'][$table]['FIELDS'] = $newFieldInfo;
384 $this->xmlDB_writeStructure();
385 return TRUE;
386
387 // TODO: Should traverse all data an remove that field in arrays!
388 } else $this->errorStatus = 'Field "'.$parsedQuery['FIELD'].'" does not exist!';
389 } else $this->errorStatus = 'There are not fields in the table - strange!';
390 break;
391 case 'DROP':
392 if (is_array($this->DBstructure['tables'][$table]['FIELDS'][$parsedQuery['FIELD']])) {
393 unset($this->DBstructure['tables'][$table]['FIELDS'][$parsedQuery['FIELD']]); // Removing it...
394 $this->xmlDB_writeStructure();
395 return TRUE;
396
397 // TODO: Should traverse all data an remove that field in arrays!
398 } else $this->errorStatus = 'Field "'.$parsedQuery['FIELD'].'" does not exist!';
399 break;
400 }
401 } else $this->errorStatus = 'Table "'.$table.'" does not exist!';
402 break;
403 case 'DROPTABLE':
404
405 // TODO:
406 debug($parsedQuery);
407
408
409 break;
410 default:
411 $this->errorStatus = 'Query type "'.$parsedQuery['type'].'" was not supported!';
412 break;
413 }
414
415 } else $this->errorStatus = 'SQL parse error: '.$parsedQuery;
416
417 return FALSE;
418 }
419 }
420
421
422 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/dbal/handlers/class.tx_dbal_handler_xmldb.php']) {
423 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/dbal/handlers/class.tx_dbal_handler_xmldb.php']);
424 }
425 ?>