Generator.php 60 KB

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