Generator.php 61 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781
  1. <?php
  2. /**
  3. * Generation tools for DB_DataObject
  4. *
  5. * PHP versions 4 and 5
  6. *
  7. * LICENSE: This source file is subject to version 3.01 of the PHP license
  8. * that is available through the world-wide-web at the following URI:
  9. * http://www.php.net/license/3_01.txt. If you did not receive a copy of
  10. * the PHP License and are unable to obtain it through the web, please
  11. * send a note to license@php.net so we can mail you a copy immediately.
  12. *
  13. * @category Database
  14. * @package DB_DataObject
  15. * @author Alan Knowles <alan@akbkhome.com>
  16. * @copyright 1997-2006 The PHP Group
  17. * @license http://www.php.net/license/3_01.txt PHP License 3.01
  18. * @version CVS: $Id: Generator.php 315531 2011-08-26 02:21:29Z alan_k $
  19. * @link http://pear.php.net/package/DB_DataObject
  20. */
  21. /*
  22. * Security Notes:
  23. * This class uses eval to create classes on the fly.
  24. * The table name and database name are used to check the database before writing the
  25. * class definitions, we now check for quotes and semi-colon's in both variables
  26. * so I cant see how it would be possible to generate code even if
  27. * for some crazy reason you took the classname and table name from User Input.
  28. *
  29. * If you consider that wrong, or can prove it.. let me know!
  30. */
  31. /**
  32. *
  33. * Config _$ptions
  34. * [DB_DataObject]
  35. * ; optional default = DB/DataObject.php
  36. * extends_location =
  37. * ; optional default = DB_DataObject
  38. * extends =
  39. * ; alter the extends field when updating a class (defaults to only replacing DB_DataObject)
  40. * generator_class_rewrite = ANY|specific_name // default is DB_DataObject
  41. *
  42. */
  43. /**
  44. * Needed classes
  45. * We lazy load here, due to problems with the tests not setting up include path correctly.
  46. * FIXME!
  47. */
  48. class_exists('DB_DataObject') ? '' : require_once 'DB/DataObject.php';
  49. //require_once('Config.php');
  50. /**
  51. * Generator class
  52. *
  53. * @package DB_DataObject
  54. */
  55. class DB_DataObject_Generator extends DB_DataObject
  56. {
  57. /* =========================================================== */
  58. /* Utility functions - for building db config files */
  59. /* =========================================================== */
  60. /**
  61. * Array of table names
  62. *
  63. * @var array
  64. * @access private
  65. */
  66. var $tables;
  67. /**
  68. * associative array table -> array of table row objects
  69. *
  70. * @var array
  71. * @access private
  72. */
  73. var $_definitions;
  74. /**
  75. * active table being output
  76. *
  77. * @var string
  78. * @access private
  79. */
  80. var $table; // active tablename
  81. /**
  82. * links (generated)
  83. *
  84. * @var array
  85. * @access private
  86. */
  87. var $_fkeys; // active tablename
  88. /**
  89. * The 'starter' = call this to start the process
  90. *
  91. * @access public
  92. * @return none
  93. */
  94. function start()
  95. {
  96. $options = &PEAR::getStaticProperty('DB_DataObject','options');
  97. $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
  98. $databases = array();
  99. foreach($options as $k=>$v) {
  100. if (substr($k,0,9) == 'database_') {
  101. $databases[substr($k,9)] = $v;
  102. }
  103. }
  104. if (isset($options['database'])) {
  105. if ($db_driver == 'DB') {
  106. require_once 'DB.php';
  107. $dsn = DB::parseDSN($options['database']);
  108. } else {
  109. require_once 'MDB2.php';
  110. $dsn = MDB2::parseDSN($options['database']);
  111. }
  112. if (!isset($database[$dsn['database']])) {
  113. $databases[$dsn['database']] = $options['database'];
  114. }
  115. }
  116. foreach($databases as $databasename => $database) {
  117. if (!$database) {
  118. continue;
  119. }
  120. $this->debug("CREATING FOR $databasename\n");
  121. $class = get_class($this);
  122. $t = new $class;
  123. $t->_database_dsn = $database;
  124. $t->_database = $databasename;
  125. if ($db_driver == 'DB') {
  126. require_once 'DB.php';
  127. $dsn = DB::parseDSN($database);
  128. } else {
  129. require_once 'MDB2.php';
  130. $dsn = MDB2::parseDSN($database);
  131. }
  132. if (($dsn['phptype'] == 'sqlite') && is_file($databasename)) {
  133. $t->_database = basename($t->_database);
  134. }
  135. $t->_createTableList();
  136. $t->_createForiegnKeys();
  137. foreach(get_class_methods($class) as $method) {
  138. if (substr($method,0,8 ) != 'generate') {
  139. continue;
  140. }
  141. $this->debug("calling $method");
  142. $t->$method();
  143. }
  144. }
  145. $this->debug("DONE\n\n");
  146. }
  147. /**
  148. * Output File was config object, now just string
  149. * Used to generate the Tables
  150. *
  151. * @var string outputbuffer for table definitions
  152. * @access private
  153. */
  154. var $_newConfig;
  155. /**
  156. * Build a list of tables;
  157. * and store it in $this->tables and $this->_definitions[tablename];
  158. *
  159. * @access private
  160. * @return none
  161. */
  162. function _createTableList()
  163. {
  164. $this->_connect();
  165. $options = &PEAR::getStaticProperty('DB_DataObject','options');
  166. $__DB= &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
  167. $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
  168. $is_MDB2 = ($db_driver != 'DB') ? true : false;
  169. if (is_object($__DB) && is_a($__DB , 'PEAR_Error')) {
  170. return PEAR::raiseError($__DB->toString(), null, PEAR_ERROR_DIE);
  171. }
  172. if (!$is_MDB2) {
  173. // try getting a list of schema tables first. (postgres)
  174. $__DB->expectError(DB_ERROR_UNSUPPORTED);
  175. $this->tables = $__DB->getListOf('schema.tables');
  176. $__DB->popExpect();
  177. } else {
  178. /**
  179. * set portability and some modules to fetch the informations
  180. */
  181. $db_options = PEAR::getStaticProperty('MDB2','options');
  182. if (empty($db_options)) {
  183. $__DB->setOption('portability', MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_FIX_CASE);
  184. }
  185. $__DB->loadModule('Manager');
  186. $__DB->loadModule('Reverse');
  187. }
  188. if ((empty($this->tables) || (is_object($this->tables) && is_a($this->tables , 'PEAR_Error')))) {
  189. //if that fails fall back to clasic tables list.
  190. if (!$is_MDB2) {
  191. // try getting a list of schema tables first. (postgres)
  192. $__DB->expectError(DB_ERROR_UNSUPPORTED);
  193. $this->tables = $__DB->getListOf('tables');
  194. $__DB->popExpect();
  195. } else {
  196. $this->tables = $__DB->manager->listTables();
  197. $sequences = $__DB->manager->listSequences();
  198. foreach ($sequences as $k => $v) {
  199. $this->tables[] = $__DB->getSequenceName($v);
  200. }
  201. }
  202. }
  203. if (is_object($this->tables) && is_a($this->tables , 'PEAR_Error')) {
  204. return PEAR::raiseError($this->tables->toString(), null, PEAR_ERROR_DIE);
  205. }
  206. // build views as well if asked to.
  207. if (!empty($options['build_views'])) {
  208. if (!$is_MDB2) {
  209. $views = $__DB->getListOf(is_string($options['build_views']) ?
  210. $options['build_views'] : 'views');
  211. } else {
  212. $views = $__DB->manager->listViews();
  213. }
  214. if (is_object($views) && is_a($views,'PEAR_Error')) {
  215. return PEAR::raiseError(
  216. 'Error getting Views (check the PEAR bug database for the fix to DB), ' .
  217. $views->toString(),
  218. null,
  219. PEAR_ERROR_DIE
  220. );
  221. }
  222. $this->tables = array_merge ($this->tables, $views);
  223. }
  224. // declare a temporary table to be filled with matching tables names
  225. $tmp_table = array();
  226. foreach($this->tables as $table) {
  227. if (isset($options['generator_include_regex']) &&
  228. !preg_match($options['generator_include_regex'],$table)) {
  229. $this->debug("SKIPPING (generator_include_regex) : $table");
  230. continue;
  231. }
  232. if (isset($options['generator_exclude_regex']) &&
  233. preg_match($options['generator_exclude_regex'],$table)) {
  234. continue;
  235. }
  236. $strip = empty($options['generator_strip_schema']) ? false : $options['generator_strip_schema'];
  237. $strip = is_numeric($strip) ? (bool) $strip : $strip;
  238. $strip = (is_string($strip) && strtolower($strip) == 'true') ? true : $strip;
  239. // postgres strip the schema bit from the
  240. if (!empty($strip) ) {
  241. if (!is_string($strip) || preg_match($strip, $table)) {
  242. $bits = explode('.', $table,2);
  243. $table = $bits[0];
  244. if (count($bits) > 1) {
  245. $table = $bits[1];
  246. }
  247. }
  248. }
  249. $this->debug("EXTRACTING : $table");
  250. $quotedTable = !empty($options['quote_identifiers_tableinfo']) ?
  251. $__DB->quoteIdentifier($table) : $table;
  252. if (!$is_MDB2) {
  253. $defs = $__DB->tableInfo($quotedTable);
  254. } else {
  255. $defs = $__DB->reverse->tableInfo($quotedTable);
  256. // rename the length value, so it matches db's return.
  257. }
  258. if (is_object($defs) && is_a($defs,'PEAR_Error')) {
  259. // running in debug mode should pick this up as a big warning..
  260. $this->debug("Error reading tableInfo: $table");
  261. $this->raiseError('Error reading tableInfo, '. $defs->toString());
  262. continue;
  263. }
  264. // cast all definitions to objects - as we deal with that better.
  265. foreach($defs as $def) {
  266. if (!is_array($def)) {
  267. continue;
  268. }
  269. // rename the length value, so it matches db's return.
  270. if (isset($def['length']) && !isset($def['len'])) {
  271. $def['len'] = $def['length'];
  272. }
  273. $this->_definitions[$table][] = (object) $def;
  274. }
  275. // we find a matching table, just store it into a temporary array
  276. $tmp_table[] = $table;
  277. }
  278. // the temporary table array is now the right one (tables names matching
  279. // with regex expressions have been removed)
  280. $this->tables = $tmp_table;
  281. //print_r($this->_definitions);
  282. }
  283. /**
  284. * Auto generation of table data.
  285. *
  286. * it will output to db_oo_{database} the table definitions
  287. *
  288. * @access private
  289. * @return none
  290. */
  291. function generateDefinitions()
  292. {
  293. $this->debug("Generating Definitions file: ");
  294. if (!$this->tables) {
  295. $this->debug("-- NO TABLES -- \n");
  296. return;
  297. }
  298. $options = &PEAR::getStaticProperty('DB_DataObject','options');
  299. //$this->_newConfig = new Config('IniFile');
  300. $this->_newConfig = '';
  301. foreach($this->tables as $this->table) {
  302. $this->_generateDefinitionsTable();
  303. }
  304. $this->_connect();
  305. // dont generate a schema if location is not set
  306. // it's created on the fly!
  307. if (empty($options['schema_location']) && empty($options["ini_{$this->_database}"]) ) {
  308. return;
  309. }
  310. if (!empty($options['generator_no_ini'])) { // built in ini files..
  311. return;
  312. }
  313. $base = @$options['schema_location'];
  314. if (isset($options["ini_{$this->_database}"])) {
  315. $file = $options["ini_{$this->_database}"];
  316. } else {
  317. $file = "{$base}/{$this->_database}.ini";
  318. }
  319. if (!file_exists(dirname($file))) {
  320. require_once 'System.php';
  321. System::mkdir(array('-p','-m',0755,dirname($file)));
  322. }
  323. $this->debug("Writing ini as {$file}\n");
  324. //touch($file);
  325. $tmpname = tempnam(session_save_path(),'DataObject_');
  326. //print_r($this->_newConfig);
  327. $fh = fopen($tmpname,'w');
  328. if (!$fh) {
  329. return PEAR::raiseError(
  330. "Failed to create temporary file: $tmpname\n".
  331. "make sure session.save_path is set and is writable\n"
  332. ,null, PEAR_ERROR_DIE);
  333. }
  334. fwrite($fh,$this->_newConfig);
  335. fclose($fh);
  336. $perms = file_exists($file) ? fileperms($file) : 0755;
  337. // windows can fail doing this. - not a perfect solution but otherwise it's getting really kludgy..
  338. if (!@rename($tmpname, $file)) {
  339. unlink($file);
  340. rename($tmpname, $file);
  341. }
  342. chmod($file,$perms);
  343. //$ret = $this->_newConfig->writeInput($file,false);
  344. //if (PEAR::isError($ret) ) {
  345. // return PEAR::raiseError($ret->message,null,PEAR_ERROR_DIE);
  346. // }
  347. }
  348. /**
  349. * create the data for Foreign Keys (for links.ini)
  350. * Currenly only works with mysql / mysqli / posgtreas
  351. * to use, you must set option: generate_links=true
  352. *
  353. * @author Pascal Schöni
  354. */
  355. function _createForiegnKeys()
  356. {
  357. $options = PEAR::getStaticProperty('DB_DataObject','options');
  358. if (empty($options['generate_links'])) {
  359. return false;
  360. }
  361. $__DB = &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
  362. if (!in_array($__DB->phptype, array('mysql', 'mysqli', 'pgsql'))) {
  363. echo "WARNING: cant handle non-mysql and pgsql introspection for defaults.";
  364. return; // cant handle non-mysql introspection for defaults.
  365. }
  366. $this->debug("generateForeignKeys: Start");
  367. $DB = $this->getDatabaseConnection();
  368. $fk = array();
  369. switch ($DB->phptype) {
  370. case 'pgsql':
  371. foreach($this->tables as $this->table) {
  372. $quotedTable = !empty($options['quote_identifiers_tableinfo']) ? $DB->quoteIdentifier($table) : $this->table;
  373. $res =& $DB->query("SELECT
  374. pg_catalog.pg_get_constraintdef(r.oid, true) AS condef
  375. FROM pg_catalog.pg_constraint r,
  376. pg_catalog.pg_class c
  377. WHERE c.oid=r.conrelid
  378. AND r.contype = 'f'
  379. AND c.relname = '" . $quotedTable . "'");
  380. if (PEAR::isError($res)) {
  381. die($res->getMessage());
  382. }
  383. while ($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
  384. $treffer = array();
  385. // this only picks up one of these.. see this for why: http://pear.php.net/bugs/bug.php?id=17049
  386. preg_match(
  387. "/FOREIGN KEY \((\w*)\) REFERENCES (\w*)\((\w*)\)/i",
  388. $row['condef'],
  389. $treffer);
  390. if (!count($treffer)) {
  391. continue;
  392. }
  393. $fk[$this->table][$treffer[1]] = $treffer[2] . ":" . $treffer[3];
  394. }
  395. }
  396. break;
  397. case 'mysql':
  398. case 'mysqli':
  399. default:
  400. foreach($this->tables as $this->table) {
  401. $quotedTable = !empty($options['quote_identifiers_tableinfo']) ? $DB->quoteIdentifier($table) : $this->table;
  402. $res =& $DB->query('SHOW CREATE TABLE ' . $quotedTable );
  403. if (PEAR::isError($res)) {
  404. die($res->getMessage());
  405. }
  406. $text = $res->fetchRow(DB_FETCHMODE_DEFAULT, 0);
  407. $treffer = array();
  408. // Extract FOREIGN KEYS
  409. preg_match_all(
  410. "/FOREIGN KEY \(`(\w*)`\) REFERENCES `(\w*)` \(`(\w*)`\)/i",
  411. $text[1],
  412. $treffer,
  413. PREG_SET_ORDER);
  414. if (!count($treffer)) {
  415. continue;
  416. }
  417. foreach($treffer as $i=> $tref) {
  418. $fk[$this->table][$tref[1]] = $tref[2] . ":" . $tref[3];
  419. }
  420. }
  421. }
  422. $this->_fkeys = $fk;
  423. }
  424. /**
  425. * generate Foreign Keys (for links.ini)
  426. * Currenly only works with mysql / mysqli
  427. * to use, you must set option: generate_links=true
  428. *
  429. * @author Pascal Schöni
  430. */
  431. function generateForeignKeys()
  432. {
  433. $options = PEAR::getStaticProperty('DB_DataObject','options');
  434. if (empty($options['generate_links'])) {
  435. return false;
  436. }
  437. $__DB = &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
  438. if (!in_array($__DB->phptype, array('mysql', 'mysqli', 'pgsql'))) {
  439. echo "WARNING: cant handle non-mysql and pgsql introspection for defaults.";
  440. return; // cant handle non-mysql introspection for defaults.
  441. }
  442. $this->debug("generateForeignKeys: Start");
  443. $fk = $this->_fkeys;
  444. $links_ini = "";
  445. foreach($fk as $table => $details) {
  446. $links_ini .= "[$table]\n";
  447. foreach ($details as $col => $ref) {
  448. $links_ini .= "$col = $ref\n";
  449. }
  450. $links_ini .= "\n";
  451. }
  452. // dont generate a schema if location is not set
  453. // it's created on the fly!
  454. $options = PEAR::getStaticProperty('DB_DataObject','options');
  455. if (!empty($options['schema_location'])) {
  456. $file = "{$options['schema_location']}/{$this->_database}.links.ini";
  457. } elseif (isset($options["ini_{$this->_database}"])) {
  458. $file = preg_replace('/\.ini/','.links.ini',$options["ini_{$this->_database}"]);
  459. } else {
  460. $this->debug("generateForeignKeys: SKIP - schema_location or ini_{database} was not set");
  461. return;
  462. }
  463. if (!file_exists(dirname($file))) {
  464. mkdir(dirname($file),0755, true);
  465. }
  466. $this->debug("Writing ini as {$file}\n");
  467. //touch($file); // not sure why this is needed?
  468. $tmpname = tempnam(session_save_path(),'DataObject_');
  469. $fh = fopen($tmpname,'w');
  470. if (!$fh) {
  471. return PEAR::raiseError(
  472. "Failed to create temporary file: $tmpname\n".
  473. "make sure session.save_path is set and is writable\n"
  474. ,null, PEAR_ERROR_DIE);
  475. }
  476. fwrite($fh,$links_ini);
  477. fclose($fh);
  478. $perms = file_exists($file) ? fileperms($file) : 0755;
  479. // windows can fail doing this. - not a perfect solution but otherwise it's getting really kludgy..
  480. if (!@rename($tmpname, $file)) {
  481. unlink($file);
  482. rename($tmpname, $file);
  483. }
  484. chmod($file, $perms);
  485. }
  486. /**
  487. * The table geneation part
  488. *
  489. * @access private
  490. * @return tabledef and keys array.
  491. */
  492. function _generateDefinitionsTable()
  493. {
  494. global $_DB_DATAOBJECT;
  495. $options = PEAR::getStaticProperty('DB_DataObject','options');
  496. $defs = $this->_definitions[$this->table];
  497. $this->_newConfig .= "\n[{$this->table}]\n";
  498. $keys_out = "\n[{$this->table}__keys]\n";
  499. $keys_out_primary = '';
  500. $keys_out_secondary = '';
  501. if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) {
  502. echo "TABLE STRUCTURE FOR {$this->table}\n";
  503. print_r($defs);
  504. }
  505. $DB = $this->getDatabaseConnection();
  506. $dbtype = $DB->phptype;
  507. $ret = array(
  508. 'table' => array(),
  509. 'keys' => array(),
  510. );
  511. $ret_keys_primary = array();
  512. $ret_keys_secondary = array();
  513. foreach($defs as $t) {
  514. $n=0;
  515. $write_ini = true;
  516. switch (strtoupper($t->type)) {
  517. case 'INT':
  518. case 'INT2': // postgres
  519. case 'INT4': // postgres
  520. case 'INT8': // postgres
  521. case 'SERIAL4': // postgres
  522. case 'SERIAL8': // postgres
  523. case 'INTEGER':
  524. case 'TINYINT':
  525. case 'SMALLINT':
  526. case 'MEDIUMINT':
  527. case 'BIGINT':
  528. $type = DB_DATAOBJECT_INT;
  529. if ($t->len == 1) {
  530. $type += DB_DATAOBJECT_BOOL;
  531. }
  532. break;
  533. case 'REAL':
  534. case 'DOUBLE':
  535. case 'DOUBLE PRECISION': // double precision (firebird)
  536. case 'FLOAT':
  537. case 'FLOAT4': // real (postgres)
  538. case 'FLOAT8': // double precision (postgres)
  539. case 'DECIMAL':
  540. case 'MONEY': // mssql and maybe others
  541. case 'NUMERIC':
  542. case 'NUMBER': // oci8
  543. $type = DB_DATAOBJECT_INT; // should really by FLOAT!!! / MONEY...
  544. break;
  545. case 'YEAR':
  546. $type = DB_DATAOBJECT_INT;
  547. break;
  548. case 'BIT':
  549. case 'BOOL':
  550. case 'BOOLEAN':
  551. $type = DB_DATAOBJECT_BOOL;
  552. // postgres needs to quote '0'
  553. if ($dbtype == 'pgsql') {
  554. $type += DB_DATAOBJECT_STR;
  555. }
  556. break;
  557. case 'STRING':
  558. case 'CHAR':
  559. case 'VARCHAR':
  560. case 'VARCHAR2':
  561. case 'TINYTEXT':
  562. case 'ENUM':
  563. case 'SET': // not really but oh well
  564. case 'POINT': // mysql geometry stuff - not really string - but will do..
  565. case 'TIMESTAMPTZ': // postgres
  566. case 'BPCHAR': // postgres
  567. case 'INTERVAL': // postgres (eg. '12 days')
  568. case 'CIDR': // postgres IP net spec
  569. case 'INET': // postgres IP
  570. case 'MACADDR': // postgress network Mac address.
  571. case 'INTEGER[]': // postgres type
  572. case 'BOOLEAN[]': // postgres type
  573. $type = DB_DATAOBJECT_STR;
  574. break;
  575. case 'TEXT':
  576. case 'MEDIUMTEXT':
  577. case 'LONGTEXT':
  578. case '_TEXT': //postgres (?? view ??)
  579. $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_TXT;
  580. break;
  581. case 'DATE':
  582. $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE;
  583. break;
  584. case 'TIME':
  585. $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_TIME;
  586. break;
  587. case 'DATETIME':
  588. $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME;
  589. break;
  590. case 'TIMESTAMP': // do other databases use this???
  591. $type = ($dbtype == 'mysql') ?
  592. DB_DATAOBJECT_MYSQLTIMESTAMP :
  593. DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME;
  594. break;
  595. case 'BLOB': /// these should really be ignored!!!???
  596. case 'TINYBLOB':
  597. case 'MEDIUMBLOB':
  598. case 'LONGBLOB':
  599. case 'CLOB': // oracle character lob support
  600. case 'BYTEA': // postgres blob support..
  601. $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_BLOB;
  602. break;
  603. default:
  604. echo "*****************************************************************\n".
  605. "** WARNING UNKNOWN TYPE **\n".
  606. "** Found column '{$t->name}', of type '{$t->type}' **\n".
  607. "** Please submit a bug, describe what type you expect this **\n".
  608. "** column to be **\n".
  609. "** ---------POSSIBLE FIX / WORKAROUND -------------------------**\n".
  610. "** Try using MDB2 as the backend - eg set the config option **\n".
  611. "** db_driver = MDB2 **\n".
  612. "*****************************************************************\n";
  613. $write_ini = false;
  614. break;
  615. }
  616. if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $t->name)) {
  617. echo "*****************************************************************\n".
  618. "** WARNING COLUMN NAME UNUSABLE **\n".
  619. "** Found column '{$t->name}', of type '{$t->type}' **\n".
  620. "** Since this column name can't be converted to a php variable **\n".
  621. "** name, and the whole idea of mapping would result in a mess **\n".
  622. "** This column has been ignored... **\n".
  623. "*****************************************************************\n";
  624. continue;
  625. }
  626. if (!strlen(trim($t->name))) {
  627. continue; // is this a bug?
  628. }
  629. if (preg_match('/not[ _]null/i',$t->flags)) {
  630. $type += DB_DATAOBJECT_NOTNULL;
  631. }
  632. if (in_array($t->name,array('null','yes','no','true','false'))) {
  633. echo "*****************************************************************\n".
  634. "** WARNING **\n".
  635. "** Found column '{$t->name}', which is invalid in an .ini file **\n".
  636. "** This line will not be writen to the file - you will have **\n".
  637. "** define the keys()/method manually. **\n".
  638. "*****************************************************************\n";
  639. $write_ini = false;
  640. } else {
  641. $this->_newConfig .= "{$t->name} = $type\n";
  642. }
  643. $ret['table'][$t->name] = $type;
  644. // i've no idea if this will work well on other databases?
  645. // only use primary key or nextval(), cause the setFrom blocks you setting all key items...
  646. // if no keys exist fall back to using unique
  647. //echo "\n{$t->name} => {$t->flags}\n";
  648. $secondary_key_match = isset($options['generator_secondary_key_match']) ? $options['generator_secondary_key_match'] : 'primary|unique';
  649. $m = array();
  650. if (preg_match('/(auto_increment|nextval\(([^)]*))/i',rawurldecode($t->flags),$m)
  651. || (isset($t->autoincrement) && ($t->autoincrement === true))) {
  652. $sn = 'N';
  653. if ($DB->phptype == 'pgsql' && !empty($m[2])) {
  654. $sn = preg_replace('/[("]+/','', $m[2]);
  655. //echo urldecode($t->flags) . "\n" ;
  656. }
  657. // native sequences = 2
  658. if ($write_ini) {
  659. $keys_out_primary .= "{$t->name} = $sn\n";
  660. }
  661. $ret_keys_primary[$t->name] = $sn;
  662. } else if ($secondary_key_match && preg_match('/('.$secondary_key_match.')/i',$t->flags)) {
  663. // keys.. = 1
  664. $key_type = 'K';
  665. if (!preg_match("/(primary)/i",$t->flags)) {
  666. $key_type = 'U';
  667. }
  668. if ($write_ini) {
  669. $keys_out_secondary .= "{$t->name} = {$key_type}\n";
  670. }
  671. $ret_keys_secondary[$t->name] = $key_type;
  672. }
  673. }
  674. $this->_newConfig .= $keys_out . (empty($keys_out_primary) ? $keys_out_secondary : $keys_out_primary);
  675. $ret['keys'] = empty($keys_out_primary) ? $ret_keys_secondary : $ret_keys_primary;
  676. if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) {
  677. print_r(array("dump for {$this->table}", $ret));
  678. }
  679. return $ret;
  680. }
  681. /**
  682. * Convert a table name into a class name -> override this if you want a different mapping
  683. *
  684. * @access public
  685. * @return string class name;
  686. */
  687. function getClassNameFromTableName($table)
  688. {
  689. $options = &PEAR::getStaticProperty('DB_DataObject','options');
  690. $class_prefix = empty($options['class_prefix']) ? '' : $options['class_prefix'];
  691. return $class_prefix.preg_replace('/[^A-Z0-9]/i','_',ucfirst(trim($this->table)));
  692. }
  693. /**
  694. * Convert a table name into a file name -> override this if you want a different mapping
  695. *
  696. * @access public
  697. * @return string file name;
  698. */
  699. function getFileNameFromTableName($table)
  700. {
  701. $options = &PEAR::getStaticProperty('DB_DataObject','options');
  702. $base = $options['class_location'];
  703. if (strpos($base,'%s') !== false) {
  704. $base = dirname($base);
  705. }
  706. if (!file_exists($base)) {
  707. require_once 'System.php';
  708. System::mkdir(array('-p',$base));
  709. }
  710. if (strpos($options['class_location'],'%s') !== false) {
  711. $outfilename = sprintf($options['class_location'],
  712. preg_replace('/[^A-Z0-9]/i','_',ucfirst($this->table)));
  713. } else {
  714. $outfilename = "{$base}/".preg_replace('/[^A-Z0-9]/i','_',ucfirst($this->table)).".php";
  715. }
  716. return $outfilename;
  717. }
  718. /**
  719. * Convert a column name into a method name (usually prefixed by get/set/validateXXXXX)
  720. *
  721. * @access public
  722. * @return string method name;
  723. */
  724. function getMethodNameFromColumnName($col)
  725. {
  726. return ucfirst($col);
  727. }
  728. /*
  729. * building the class files
  730. * for each of the tables output a file!
  731. */
  732. function generateClasses()
  733. {
  734. //echo "Generating Class files: \n";
  735. $options = &PEAR::getStaticProperty('DB_DataObject','options');
  736. $this->_extends = empty($options['extends']) ? $this->_extends : $options['extends'];
  737. $this->_extendsFile = empty($options['extends_location']) ? $this->_extendsFile : $options['extends_location'];
  738. foreach($this->tables as $this->table) {
  739. $this->table = trim($this->table);
  740. $this->classname = $this->getClassNameFromTableName($this->table);
  741. $i = '';
  742. $outfilename = $this->getFileNameFromTableName($this->table);
  743. $oldcontents = '';
  744. if (file_exists($outfilename)) {
  745. // file_get_contents???
  746. $oldcontents = implode('',file($outfilename));
  747. }
  748. $out = $this->_generateClassTable($oldcontents);
  749. $this->debug( "writing $this->classname\n");
  750. $tmpname = tempnam(session_save_path(),'DataObject_');
  751. $fh = fopen($tmpname, "w");
  752. if (!$fh) {
  753. return PEAR::raiseError(
  754. "Failed to create temporary file: $tmpname\n".
  755. "make sure session.save_path is set and is writable\n"
  756. ,null, PEAR_ERROR_DIE);
  757. }
  758. fputs($fh,$out);
  759. fclose($fh);
  760. $perms = file_exists($outfilename) ? fileperms($outfilename) : 0755;
  761. // windows can fail doing this. - not a perfect solution but otherwise it's getting really kludgy..
  762. if (!@rename($tmpname, $outfilename)) {
  763. unlink($outfilename);
  764. rename($tmpname, $outfilename);
  765. }
  766. chmod($outfilename, $perms);
  767. }
  768. //echo $out;
  769. }
  770. /**
  771. * class being extended (can be overridden by [DB_DataObject] extends=xxxx
  772. *
  773. * @var string
  774. * @access private
  775. */
  776. var $_extends = 'DB_DataObject';
  777. /**
  778. * line to use for require('DB/DataObject.php');
  779. *
  780. * @var string
  781. * @access private
  782. */
  783. var $_extendsFile = "DB/DataObject.php";
  784. /**
  785. * class being generated
  786. *
  787. * @var string
  788. * @access private
  789. */
  790. var $_className;
  791. /**
  792. * The table class geneation part - single file.
  793. *
  794. * @access private
  795. * @return none
  796. */
  797. function _generateClassTable($input = '')
  798. {
  799. // title = expand me!
  800. $foot = "";
  801. $head = "<?php\n/**\n * Table Definition for {$this->table}\n";
  802. $head .= $this->derivedHookPageLevelDocBlock();
  803. $head .= " */\n";
  804. $head .= $this->derivedHookExtendsDocBlock();
  805. // requires
  806. $head .= "require_once '{$this->_extendsFile}';\n\n";
  807. // add dummy class header in...
  808. // class
  809. $head .= $this->derivedHookClassDocBlock();
  810. $head .= "class {$this->classname} extends {$this->_extends} \n{";
  811. $body = "\n ###START_AUTOCODE\n";
  812. $body .= " /* the code below is auto generated do not remove the above tag */\n\n";
  813. // table
  814. $p = str_repeat(' ',max(2, (18 - strlen($this->table)))) ;
  815. $options = &PEAR::getStaticProperty('DB_DataObject','options');
  816. $var = (substr(phpversion(),0,1) > 4) ? 'public' : 'var';
  817. $var = !empty($options['generator_var_keyword']) ? $options['generator_var_keyword'] : $var;
  818. $body .= " {$var} \$__table = '{$this->table}'; {$p}// table name\n";
  819. // if we are using the option database_{databasename} = dsn
  820. // then we should add var $_database = here
  821. // as database names may not always match..
  822. if (empty($GLOBALS['_DB_DATAOBJECT']['CONFIG'])) {
  823. DB_DataObject::_loadConfig();
  824. }
  825. // Only include the $_database property if the omit_database_var is unset or false
  826. if (isset($options["database_{$this->_database}"]) && empty($GLOBALS['_DB_DATAOBJECT']['CONFIG']['generator_omit_database_var'])) {
  827. $p = str_repeat(' ', max(2, (16 - strlen($this->_database))));
  828. $body .= " {$var} \$_database = '{$this->_database}'; {$p}// database name (used with database_{*} config)\n";
  829. }
  830. if (!empty($options['generator_novars'])) {
  831. $var = '//'.$var;
  832. }
  833. $defs = $this->_definitions[$this->table];
  834. // show nice information!
  835. $connections = array();
  836. $sets = array();
  837. foreach($defs as $t) {
  838. if (!strlen(trim($t->name))) {
  839. continue;
  840. }
  841. if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $t->name)) {
  842. echo "*****************************************************************\n".
  843. "** WARNING COLUMN NAME UNUSABLE **\n".
  844. "** Found column '{$t->name}', of type '{$t->type}' **\n".
  845. "** Since this column name can't be converted to a php variable **\n".
  846. "** name, and the whole idea of mapping would result in a mess **\n".
  847. "** This column has been ignored... **\n".
  848. "*****************************************************************\n";
  849. continue;
  850. }
  851. $p = str_repeat(' ',max(2, (30 - strlen($t->name))));
  852. $length = empty($t->len) ? '' : '('.$t->len.')';
  853. $body .=" {$var} \${$t->name}; {$p}// {$t->type}$length {$t->flags}\n";
  854. // can not do set as PEAR::DB table info doesnt support it.
  855. //if (substr($t->Type,0,3) == "set")
  856. // $sets[$t->Field] = "array".substr($t->Type,3);
  857. $body .= $this->derivedHookVar($t,strlen($p));
  858. }
  859. $body .= $this->derivedHookPostVar($defs);
  860. // THIS IS TOTALLY BORKED old FC creation
  861. // IT WILL BE REMOVED!!!!! in DataObjects 1.6
  862. // grep -r __clone * to find all it's uses
  863. // and replace them with $x = clone($y);
  864. // due to the change in the PHP5 clone design.
  865. $static = 'static';
  866. if ( substr(phpversion(),0,1) < 5) {
  867. $body .= "\n";
  868. $body .= " /* ZE2 compatibility trick*/\n";
  869. $body .= " function __clone() { return \$this;}\n";
  870. }
  871. // depricated - in here for BC...
  872. if (!empty($options['static_get'])) {
  873. // simple creation tools ! (static stuff!)
  874. $body .= "\n";
  875. $body .= " /* Static get */\n";
  876. $body .= " $static function staticGet(\$k,\$v=NULL) { " .
  877. "return DB_DataObject::staticGet('{$this->classname}',\$k,\$v = null); }\n";
  878. }
  879. // generate getter and setter methods
  880. $body .= $this->_generateGetters($input);
  881. $body .= $this->_generateSetters($input);
  882. $body .= $this->_generateLinkMethods($input);
  883. /*
  884. theoretically there is scope here to introduce 'list' methods
  885. based up 'xxxx_up' column!!! for heiracitcal trees..
  886. */
  887. // set methods
  888. //foreach ($sets as $k=>$v) {
  889. // $kk = strtoupper($k);
  890. // $body .=" function getSets{$k}() { return {$v}; }\n";
  891. //}
  892. if (!empty($options['generator_no_ini'])) {
  893. $def = $this->_generateDefinitionsTable(); // simplify this!?
  894. $body .= $this->_generateTableFunction($def['table']);
  895. $body .= $this->_generateKeysFunction($def['keys']);
  896. $body .= $this->_generateSequenceKeyFunction($def);
  897. $body .= $this->_generateDefaultsFunction($this->table, $def['table']);
  898. } else if (!empty($options['generator_add_defaults'])) {
  899. // I dont really like doing it this way (adding another option)
  900. // but it helps on older projects.
  901. $def = $this->_generateDefinitionsTable(); // simplify this!?
  902. $body .= $this->_generateDefaultsFunction($this->table,$def['table']);
  903. }
  904. $body .= $this->derivedHookFunctions($input);
  905. $body .= "\n /* the code above is auto generated do not remove the tag below */";
  906. $body .= "\n ###END_AUTOCODE\n";
  907. // stubs..
  908. if (!empty($options['generator_add_validate_stubs'])) {
  909. foreach($defs as $t) {
  910. if (!strlen(trim($t->name))) {
  911. continue;
  912. }
  913. $validate_fname = 'validate' . $this->getMethodNameFromColumnName($t->name);
  914. // dont re-add it..
  915. if (preg_match('/\s+function\s+' . $validate_fname . '\s*\(/i', $input)) {
  916. continue;
  917. }
  918. $body .= "\n function {$validate_fname}()\n {\n return false;\n }\n";
  919. }
  920. }
  921. $foot .= "}\n";
  922. $full = $head . $body . $foot;
  923. if (!$input) {
  924. return $full;
  925. }
  926. if (!preg_match('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n)/s',$input)) {
  927. return $full;
  928. }
  929. if (!preg_match('/(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s',$input)) {
  930. return $full;
  931. }
  932. /* this will only replace extends DB_DataObject by default,
  933. unless use set generator_class_rewrite to ANY or a name*/
  934. $class_rewrite = 'DB_DataObject';
  935. $options = &PEAR::getStaticProperty('DB_DataObject','options');
  936. if (empty($options['generator_class_rewrite']) || !($class_rewrite = $options['generator_class_rewrite'])) {
  937. $class_rewrite = 'DB_DataObject';
  938. }
  939. if ($class_rewrite == 'ANY') {
  940. $class_rewrite = '[a-z_]+';
  941. }
  942. $input = preg_replace(
  943. '/(\n|\r\n)class\s*[a-z0-9_]+\s*extends\s*' .$class_rewrite . '\s*(\n|\r\n)\{(\n|\r\n)/si',
  944. "\nclass {$this->classname} extends {$this->_extends} \n{\n",
  945. $input);
  946. $ret = preg_replace(
  947. '/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s',
  948. $body,$input);
  949. if (!strlen($ret)) {
  950. return PEAR::raiseError(
  951. "PREG_REPLACE failed to replace body, - you probably need to set these in your php.ini\n".
  952. "pcre.backtrack_limit=1000000\n".
  953. "pcre.recursion_limit=1000000\n"
  954. ,null, PEAR_ERROR_DIE);
  955. }
  956. return $ret;
  957. }
  958. /**
  959. * hook to add extra methods to all classes
  960. *
  961. * called once for each class, use with $this->table and
  962. * $this->_definitions[$this->table], to get data out of the current table,
  963. * use it to add extra methods to the default classes.
  964. *
  965. * @access public
  966. * @return string added to class eg. functions.
  967. */
  968. function derivedHookFunctions($input = "")
  969. {
  970. // This is so derived generator classes can generate functions
  971. // It MUST NOT be changed here!!!
  972. return "";
  973. }
  974. /**
  975. * hook for var lines
  976. * called each time a var line is generated, override to add extra var
  977. * lines
  978. *
  979. * @param object t containing type,len,flags etc. from tableInfo call
  980. * @param int padding number of spaces
  981. * @access public
  982. * @return string added to class eg. functions.
  983. */
  984. function derivedHookVar(&$t,$padding)
  985. {
  986. // This is so derived generator classes can generate variabels
  987. // It MUST NOT be changed here!!!
  988. return "";
  989. }
  990. /**
  991. * hook for after var lines (
  992. * called at the end of the output of var line have generated, override to add extra var
  993. * lines
  994. *
  995. * @param array cols containing array of objects with type,len,flags etc. from tableInfo call
  996. * @access public
  997. * @return string added to class eg. functions.
  998. */
  999. function derivedHookPostVar($t)
  1000. {
  1001. // This is so derived generator classes can generate variabels
  1002. // It MUST NOT be changed here!!!
  1003. return "";
  1004. }
  1005. /**
  1006. * hook to add extra page-level (in terms of phpDocumentor) DocBlock
  1007. *
  1008. * called once for each class, use it add extra page-level docs
  1009. * @access public
  1010. * @return string added to class eg. functions.
  1011. */
  1012. function derivedHookPageLevelDocBlock() {
  1013. return '';
  1014. }
  1015. /**
  1016. * hook to add extra doc block (in terms of phpDocumentor) to extend string
  1017. *
  1018. * called once for each class, use it add extra comments to extends
  1019. * string (require_once...)
  1020. * @access public
  1021. * @return string added to class eg. functions.
  1022. */
  1023. function derivedHookExtendsDocBlock() {
  1024. return '';
  1025. }
  1026. /**
  1027. * hook to add extra class level DocBlock (in terms of phpDocumentor)
  1028. *
  1029. * called once for each class, use it add extra comments to class
  1030. * string (require_once...)
  1031. * @access public
  1032. * @return string added to class eg. functions.
  1033. */
  1034. function derivedHookClassDocBlock() {
  1035. return '';
  1036. }
  1037. /**
  1038. /**
  1039. * getProxyFull - create a class definition on the fly and instantate it..
  1040. *
  1041. * similar to generated files - but also evals the class definitoin code.
  1042. *
  1043. *
  1044. * @param string database name
  1045. * @param string table name of table to create proxy for.
  1046. *
  1047. *
  1048. * @return object Instance of class. or PEAR Error
  1049. * @access public
  1050. */
  1051. function getProxyFull($database,$table)
  1052. {
  1053. if ($err = $this->fillTableSchema($database,$table)) {
  1054. return $err;
  1055. }
  1056. $options = &PEAR::getStaticProperty('DB_DataObject','options');
  1057. $class_prefix = empty($options['class_prefix']) ? '' : $options['class_prefix'];
  1058. $this->_extends = empty($options['extends']) ? $this->_extends : $options['extends'];
  1059. $this->_extendsFile = empty($options['extends_location']) ? $this->_extendsFile : $options['extends_location'];
  1060. $classname = $this->classname = $this->getClassNameFromTableName($this->table);
  1061. $out = $this->_generateClassTable();
  1062. //echo $out;
  1063. eval('?>'.$out);
  1064. return new $classname;
  1065. }
  1066. /**
  1067. * fillTableSchema - set the database schema on the fly
  1068. *
  1069. *
  1070. *
  1071. * @param string database name
  1072. * @param string table name of table to create schema info for
  1073. *
  1074. * @return none | PEAR::error()
  1075. * @access public
  1076. */
  1077. function fillTableSchema($database,$table)
  1078. {
  1079. global $_DB_DATAOBJECT;
  1080. // a little bit of sanity testing.
  1081. if ((false !== strpos($database,"'")) || (false !== strpos($database,";"))) {
  1082. return PEAR::raiseError("Error: Database name contains a quote or semi-colon", null, PEAR_ERROR_DIE);
  1083. }
  1084. $this->_database = $database;
  1085. $this->_connect();
  1086. $table = trim($table);
  1087. // a little bit of sanity testing.
  1088. if ((false !== strpos($table,"'")) || (false !== strpos($table,";"))) {
  1089. return PEAR::raiseError("Error: Table contains a quote or semi-colon", null, PEAR_ERROR_DIE);
  1090. }
  1091. $__DB= &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
  1092. $options = PEAR::getStaticProperty('DB_DataObject','options');
  1093. $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
  1094. $is_MDB2 = ($db_driver != 'DB') ? true : false;
  1095. if (!$is_MDB2) {
  1096. // try getting a list of schema tables first. (postgres)
  1097. $__DB->expectError(DB_ERROR_UNSUPPORTED);
  1098. $this->tables = $__DB->getListOf('schema.tables');
  1099. $__DB->popExpect();
  1100. } else {
  1101. /**
  1102. * set portability and some modules to fetch the informations
  1103. */
  1104. $__DB->setOption('portability', MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_FIX_CASE);
  1105. $__DB->loadModule('Manager');
  1106. $__DB->loadModule('Reverse');
  1107. }
  1108. $quotedTable = !empty($options['quote_identifiers_tableinfo']) ?
  1109. $__DB->quoteIdentifier($table) : $table;
  1110. if (!$is_MDB2) {
  1111. $defs = $__DB->tableInfo($quotedTable);
  1112. } else {
  1113. $defs = $__DB->reverse->tableInfo($quotedTable);
  1114. if (PEAR::isError($defs)) {
  1115. return $defs;
  1116. }
  1117. foreach ($defs as $k => $v) {
  1118. if (!isset($defs[$k]['length'])) {
  1119. continue;
  1120. }
  1121. $defs[$k]['len'] = $defs[$k]['length'];
  1122. }
  1123. }
  1124. if (PEAR::isError($defs)) {
  1125. return $defs;
  1126. }
  1127. if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) {
  1128. $this->debug("getting def for $database/$table",'fillTable');
  1129. $this->debug(print_r($defs,true),'defs');
  1130. }
  1131. // cast all definitions to objects - as we deal with that better.
  1132. foreach($defs as $def) {
  1133. if (is_array($def)) {
  1134. $this->_definitions[$table][] = (object) $def;
  1135. }
  1136. }
  1137. $this->table = trim($table);
  1138. $ret = $this->_generateDefinitionsTable();
  1139. $_DB_DATAOBJECT['INI'][$database][$table] = $ret['table'];
  1140. $_DB_DATAOBJECT['INI'][$database][$table.'__keys'] = $ret['keys'];
  1141. return false;
  1142. }
  1143. /**
  1144. * Generate getter methods for class definition
  1145. *
  1146. * @param string $input Existing class contents
  1147. * @return string
  1148. * @access public
  1149. */
  1150. function _generateGetters($input)
  1151. {
  1152. $options = &PEAR::getStaticProperty('DB_DataObject','options');
  1153. $getters = '';
  1154. // only generate if option is set to true
  1155. if (empty($options['generate_getters'])) {
  1156. return '';
  1157. }
  1158. // remove auto-generated code from input to be able to check if the method exists outside of the auto-code
  1159. $input = preg_replace('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s', '', $input);
  1160. $getters .= "\n\n";
  1161. $defs = $this->_definitions[$this->table];
  1162. // loop through properties and create getter methods
  1163. foreach ($defs = $defs as $t) {
  1164. // build mehtod name
  1165. $methodName = 'get' . $this->getMethodNameFromColumnName($t->name);
  1166. if (!strlen(trim($t->name)) || preg_match("/function[\s]+[&]?$methodName\(/i", $input)) {
  1167. continue;
  1168. }
  1169. $getters .= " /**\n";
  1170. $getters .= " * Getter for \${$t->name}\n";
  1171. $getters .= " *\n";
  1172. $getters .= (stristr($t->flags, 'multiple_key')) ? " * @return object\n"
  1173. : " * @return {$t->type}\n";
  1174. $getters .= " * @access public\n";
  1175. $getters .= " */\n";
  1176. $getters .= (substr(phpversion(),0,1) > 4) ? ' public '
  1177. : ' ';
  1178. $getters .= "function $methodName() {\n";
  1179. $getters .= " return \$this->{$t->name};\n";
  1180. $getters .= " }\n\n";
  1181. }
  1182. return $getters;
  1183. }
  1184. /**
  1185. * Generate link setter/getter methods for class definition
  1186. *
  1187. * @param string Existing class contents
  1188. * @return string
  1189. * @access public
  1190. */
  1191. function _generateLinkMethods($input)
  1192. {
  1193. $options = &PEAR::getStaticProperty('DB_DataObject','options');
  1194. $setters = '';
  1195. // only generate if option is set to true
  1196. // generate_link_methods true::
  1197. if (empty($options['generate_link_methods'])) {
  1198. //echo "skip lm? - not set";
  1199. return '';
  1200. }
  1201. if (empty($this->_fkeys)) {
  1202. // echo "skip lm? - fkyes empty";
  1203. return '';
  1204. }
  1205. if (empty($this->_fkeys[$this->table])) {
  1206. //echo "skip lm? - no fkeys for {$this->table}";
  1207. return '';
  1208. }
  1209. // remove auto-generated code from input to be able to check if the method exists outside of the auto-code
  1210. $input = preg_replace('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s', '', $input);
  1211. $setters .= "\n";
  1212. $defs = $this->_fkeys[$this->table];
  1213. // $fk[$this->table][$tref[1]] = $tref[2] . ":" . $tref[3];
  1214. // loop through properties and create setter methods
  1215. foreach ($defs as $k => $info) {
  1216. // build mehtod name
  1217. $methodName = is_callable($options['generate_link_methods']) ?
  1218. $options['generate_link_methods']($k) : $k;
  1219. if (!strlen(trim($k)) || preg_match("/function[\s]+[&]?$methodName\(/i", $input)) {
  1220. continue;
  1221. }
  1222. $setters .= " /**\n";
  1223. $setters .= " * Getter / Setter for \${$k}\n";
  1224. $setters .= " *\n";
  1225. $setters .= " * @param mixed (optional) value to assign\n";
  1226. $setters .= " * @access public\n";
  1227. $setters .= " */\n";
  1228. $setters .= (substr(phpversion(),0,1) > 4) ? ' public '
  1229. : ' ';
  1230. $setters .= "function $methodName() {\n";
  1231. $setters .= " return \$this->link('$k', func_get_args());\n";
  1232. $setters .= " }\n\n";
  1233. }
  1234. return $setters;
  1235. }
  1236. /**
  1237. * Generate setter methods for class definition
  1238. *
  1239. * @param string Existing class contents
  1240. * @return string
  1241. * @access public
  1242. */
  1243. function _generateSetters($input)
  1244. {
  1245. $options = &PEAR::getStaticProperty('DB_DataObject','options');
  1246. $setters = '';
  1247. // only generate if option is set to true
  1248. if (empty($options['generate_setters'])) {
  1249. return '';
  1250. }
  1251. // remove auto-generated code from input to be able to check if the method exists outside of the auto-code
  1252. $input = preg_replace('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s', '', $input);
  1253. $setters .= "\n";
  1254. $defs = $this->_definitions[$this->table];
  1255. // loop through properties and create setter methods
  1256. foreach ($defs = $defs as $t) {
  1257. // build mehtod name
  1258. $methodName = 'set' . $this->getMethodNameFromColumnName($t->name);
  1259. if (!strlen(trim($t->name)) || preg_match("/function[\s]+[&]?$methodName\(/i", $input)) {
  1260. continue;
  1261. }
  1262. $setters .= " /**\n";
  1263. $setters .= " * Setter for \${$t->name}\n";
  1264. $setters .= " *\n";
  1265. $setters .= " * @param mixed input value\n";
  1266. $setters .= " * @access public\n";
  1267. $setters .= " */\n";
  1268. $setters .= (substr(phpversion(),0,1) > 4) ? ' public '
  1269. : ' ';
  1270. $setters .= "function $methodName(\$value) {\n";
  1271. $setters .= " \$this->{$t->name} = \$value;\n";
  1272. $setters .= " }\n\n";
  1273. }
  1274. return $setters;
  1275. }
  1276. /**
  1277. * Generate table Function - used when generator_no_ini is set.
  1278. *
  1279. * @param array table array.
  1280. * @return string
  1281. * @access public
  1282. */
  1283. function _generateTableFunction($def)
  1284. {
  1285. $defines = explode(',','INT,STR,DATE,TIME,BOOL,TXT,BLOB,NOTNULL,MYSQLTIMESTAMP');
  1286. $ret = "\n" .
  1287. " function table()\n" .
  1288. " {\n" .
  1289. " return array(\n";
  1290. foreach($def as $k=>$v) {
  1291. $str = '0';
  1292. foreach($defines as $dn) {
  1293. if ($v & constant('DB_DATAOBJECT_' . $dn)) {
  1294. $str .= ' + DB_DATAOBJECT_' . $dn;
  1295. }
  1296. }
  1297. if (strlen($str) > 1) {
  1298. $str = substr($str,3); // strip the 0 +
  1299. }
  1300. // hopefully addslashes is good enough here!!!
  1301. $ret .= ' \''.addslashes($k).'\' => ' . $str . ",\n";
  1302. }
  1303. return $ret . " );\n" .
  1304. " }\n";
  1305. }
  1306. /**
  1307. * Generate keys Function - used generator_no_ini is set.
  1308. *
  1309. * @param array keys array.
  1310. * @return string
  1311. * @access public
  1312. */
  1313. function _generateKeysFunction($def)
  1314. {
  1315. $ret = "\n" .
  1316. " function keys()\n" .
  1317. " {\n" .
  1318. " return array(";
  1319. foreach($def as $k=>$type) {
  1320. // hopefully addslashes is good enough here!!!
  1321. $ret .= '\''.addslashes($k).'\', ';
  1322. }
  1323. $ret = preg_replace('#, $#', '', $ret);
  1324. return $ret . ");\n" .
  1325. " }\n";
  1326. }
  1327. /**
  1328. * Generate sequenceKey Function - used generator_no_ini is set.
  1329. *
  1330. * @param array table and key definition.
  1331. * @return string
  1332. * @access public
  1333. */
  1334. function _generateSequenceKeyFunction($def)
  1335. {
  1336. //print_r($def);
  1337. // DB_DataObject::debugLevel(5);
  1338. global $_DB_DATAOBJECT;
  1339. // print_r($def);
  1340. $dbtype = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype'];
  1341. $realkeys = $def['keys'];
  1342. $keys = array_keys($realkeys);
  1343. $usekey = isset($keys[0]) ? $keys[0] : false;
  1344. $table = $def['table'];
  1345. $seqname = false;
  1346. $ar = array(false,false,false);
  1347. if ($usekey !== false) {
  1348. if (!empty($_DB_DATAOBJECT['CONFIG']['sequence_'.$this->__table])) {
  1349. $usekey = $_DB_DATAOBJECT['CONFIG']['sequence_'.$this->__table];
  1350. if (strpos($usekey,':') !== false) {
  1351. list($usekey,$seqname) = explode(':',$usekey);
  1352. }
  1353. }
  1354. if (in_array($dbtype , array( 'mysql', 'mysqli', 'mssql', 'ifx')) &&
  1355. ($table[$usekey] & DB_DATAOBJECT_INT) &&
  1356. isset($realkeys[$usekey]) && ($realkeys[$usekey] == 'N')
  1357. ) {
  1358. // use native sequence keys.
  1359. $ar = array($usekey,true,$seqname);
  1360. } else {
  1361. // use generated sequence keys..
  1362. if ($table[$usekey] & DB_DATAOBJECT_INT) {
  1363. $ar = array($usekey,false,$seqname);
  1364. }
  1365. }
  1366. }
  1367. $ret = "\n" .
  1368. " function sequenceKey() // keyname, use native, native name\n" .
  1369. " {\n" .
  1370. " return array(";
  1371. foreach($ar as $v) {
  1372. switch (gettype($v)) {
  1373. case 'boolean':
  1374. $ret .= ($v ? 'true' : 'false') . ', ';
  1375. break;
  1376. case 'string':
  1377. $ret .= "'" . $v . "', ";
  1378. break;
  1379. default: // eak
  1380. $ret .= "null, ";
  1381. }
  1382. }
  1383. $ret = preg_replace('#, $#', '', $ret);
  1384. return $ret . ");\n" .
  1385. " }\n";
  1386. }
  1387. /**
  1388. * Generate defaults Function - used generator_add_defaults or generator_no_ini is set.
  1389. * Only supports mysql and mysqli ... welcome ideas for more..
  1390. *
  1391. *
  1392. * @param array table and key definition.
  1393. * @return string
  1394. * @access public
  1395. */
  1396. function _generateDefaultsFunction($table,$defs)
  1397. {
  1398. $__DB= &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
  1399. if (!in_array($__DB->phptype, array('mysql','mysqli'))) {
  1400. return; // cant handle non-mysql introspection for defaults.
  1401. }
  1402. $options = PEAR::getStaticProperty('DB_DataObject','options');
  1403. $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
  1404. $method = $db_driver == 'DB' ? 'getAll' : 'queryAll';
  1405. $res = $__DB->$method('DESCRIBE ' . $table,DB_FETCHMODE_ASSOC);
  1406. $defaults = array();
  1407. foreach($res as $ar) {
  1408. // this is initially very dumb... -> and it may mess up..
  1409. $type = $defs[$ar['Field']];
  1410. switch (true) {
  1411. case (is_null( $ar['Default'])):
  1412. $defaults[$ar['Field']] = 'null';
  1413. break;
  1414. case ($type & DB_DATAOBJECT_DATE):
  1415. case ($type & DB_DATAOBJECT_TIME):
  1416. case ($type & DB_DATAOBJECT_MYSQLTIMESTAMP): // not supported yet..
  1417. break;
  1418. case ($type & DB_DATAOBJECT_BOOL):
  1419. $defaults[$ar['Field']] = (int)(boolean) $ar['Default'];
  1420. break;
  1421. case ($type & DB_DATAOBJECT_STR):
  1422. $defaults[$ar['Field']] = "'" . addslashes($ar['Default']) . "'";
  1423. break;
  1424. default: // hopefully eveything else... - numbers etc.
  1425. if (!strlen($ar['Default'])) {
  1426. continue;
  1427. }
  1428. if (is_numeric($ar['Default'])) {
  1429. $defaults[$ar['Field']] = $ar['Default'];
  1430. }
  1431. break;
  1432. }
  1433. //var_dump(array($ar['Field'], $ar['Default'], $defaults[$ar['Field']]));
  1434. }
  1435. if (empty($defaults)) {
  1436. return;
  1437. }
  1438. $ret = "\n" .
  1439. " function defaults() // column default values \n" .
  1440. " {\n" .
  1441. " return array(\n";
  1442. foreach($defaults as $k=>$v) {
  1443. $ret .= ' \''.addslashes($k).'\' => ' . $v . ",\n";
  1444. }
  1445. return $ret . " );\n" .
  1446. " }\n";
  1447. }
  1448. }