PageRenderTime 33ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/main/class/backup/backup.php

http://github.com/FSB/Fire-Soft-Board-2
PHP | 580 lines | 360 code | 77 blank | 143 comment | 56 complexity | 2b0498195cfc841e5c3fe142a3f18e33 MD5 | raw file
  1. <?php
  2. /**
  3. * Fire-Soft-Board version 2
  4. *
  5. * @package FSB2
  6. * @author Genova <genova@fire-soft-board.com>
  7. * @version $Id$
  8. * @license http://opensource.org/licenses/gpl-2.0.php GNU GPL 2
  9. */
  10. /**
  11. * Classe permettant de realiser des backups de la base de donnee
  12. */
  13. abstract class Backup extends Fsb_model
  14. {
  15. /**
  16. * Type de base de donnee utilisee
  17. *
  18. * @var string
  19. */
  20. private $dbal = '';
  21. /**
  22. * Gestion des multi insertions
  23. *
  24. * @var bool
  25. */
  26. public $multi_insert = false;
  27. /**
  28. * Methode utilisee pour le dump des donnees
  29. *
  30. * @var string
  31. */
  32. protected $dump_method;
  33. /**
  34. * Affiche le backup
  35. */
  36. const OUTPUT = 1;
  37. /**
  38. * Lance le telechargement du backup
  39. */
  40. const DOWNLOAD = 2;
  41. /**
  42. * Sauve le backup dans un fichier
  43. */
  44. const FTP = 3;
  45. /**
  46. * Retourne le contenu du backup
  47. */
  48. const GET = 4;
  49. /**
  50. * Fait un backup de la structure
  51. */
  52. const STRUCT = 1;
  53. /**
  54. * Fait un backup des donnees
  55. *
  56. */
  57. const DATA = 2;
  58. /**
  59. * Fait un backup des donnees et de la structure
  60. */
  61. const ALL = 255;
  62. /**
  63. * Ouvre le gestionaire de sortie pour le backup
  64. *
  65. * @param string $filename Nom du fichier
  66. */
  67. abstract public function open($filename);
  68. /**
  69. * Ecrit des donnees dans le gestionaire de sortie
  70. *
  71. * @param string $str Donnees a ecrire
  72. */
  73. abstract public function write($str);
  74. /**
  75. * Ferme le gestionaire de sortie
  76. */
  77. abstract public function close();
  78. /**
  79. * Design pattern factory, retourne une instance de la classe backup en fonction du gestionaire de sortie utilise
  80. *
  81. * @param string $sgbd Type de base de donnee utilisee
  82. * @param int $output Type de gestionaire de sortie
  83. * @return Backup Objet backup
  84. */
  85. public static function &factory($sgbd, $output)
  86. {
  87. // Gestion du buffer
  88. switch ($output)
  89. {
  90. case self::OUTPUT :
  91. $obj =& new Backup_print();
  92. break;
  93. case self::DOWNLOAD :
  94. $obj =& new Backup_download();
  95. break;
  96. case self::FTP :
  97. $obj =& new Backup_ftp();
  98. break;
  99. case self::GET :
  100. default :
  101. $obj =& new Backup_get();
  102. break;
  103. }
  104. if (!method_exists($obj, 'dump_' . $sgbd))
  105. {
  106. trigger_error('Base de donnee incorecte dans la classe Backup() : ' . $sgbd, FSB_ERROR);
  107. }
  108. $obj->dump_method = $sgbd;
  109. return ($obj);
  110. }
  111. /**
  112. * Demarre le backup
  113. *
  114. * @param int $type Type des donnees a sauver (structure, donnees, tout)
  115. * @param array $tables Liste des tables sur lesquelles effectuer le backup
  116. * @return string Donnees du backup en cas de methode Backup::GET
  117. */
  118. public function save($type, $tables)
  119. {
  120. $filename = $this->generate_filename();
  121. $this->open($filename);
  122. $this->create_header();
  123. $this->{'dump_' . $this->dump_method}($type, $tables);
  124. return ($this->close());
  125. }
  126. /**
  127. * Cree le header pour les backups
  128. *
  129. * @return string
  130. */
  131. private function create_header()
  132. {
  133. $header = sprintf("#\n# FSB version %s :: `%s` dump\n# Cree le %s\n#\n\n", Fsb::$cfg->get('fsb_version'), $this->dump_method, date("d/m/y H:i", CURRENT_TIME));
  134. return ($header);
  135. }
  136. /**
  137. * Genere un nom pour le fichier du backup
  138. *
  139. * @return string
  140. */
  141. private function generate_filename()
  142. {
  143. return ('backup_' . $this->dump_method . '_' . date('d_m_y_H_i', CURRENT_TIME) . '.sql');
  144. }
  145. /**
  146. * Lance un dump des tables MySQL du forum
  147. * La methode de dump de la base de donnee MySQL a ete inspiree par cette source :
  148. * @link http://www.developpez.net/forums/viewtopic.php?p=1405354#1405354
  149. *
  150. * @param int $type Le type de donnees qu'on veut sauvegarder (structure, contenu ou les deux)
  151. * @param array $save_table Les tables a sauvegarder
  152. * @param bool $comment Ajoute un commentaire en debut de table
  153. */
  154. public function dump_mysql($type, $save_table, $comment = true)
  155. {
  156. if (is_array($save_table))
  157. {
  158. $sql = "SHOW TABLES";
  159. $result = Fsb::$db->query($sql);
  160. while ($table = Fsb::$db->row($result, 'row'))
  161. {
  162. if (in_array($table[0], $save_table))
  163. {
  164. $struct = '';
  165. $data = '';
  166. if ($type & self::STRUCT)
  167. {
  168. if ($comment)
  169. {
  170. $this->write("#\n# Structure de la table MySQL `${table[0]}`\n#\n");
  171. }
  172. $sql = 'SHOW CREATE TABLE ' . $table[0];
  173. $create_result = Fsb::$db->query($sql);
  174. while ($create = Fsb::$db->row($create_result, 'row'))
  175. {
  176. $this->write($create[1] . ";\n");
  177. }
  178. $this->write("\n");
  179. }
  180. if ($type & self::DATA)
  181. {
  182. $this->dump_database($table[0], "MySQL", $comment, $this->multi_insert);
  183. }
  184. }
  185. }
  186. }
  187. else
  188. {
  189. trigger_error('La variable $save_table doit etre un tableau dans la classe backup() : ' . $save_table, FSB_ERROR);
  190. }
  191. }
  192. /**
  193. * Lance un dump des tables PostGreSQL du forum
  194. * Les methodes de dump PostgreSQL ont ete realisees a partir de celles de phpBB3 et de phpPgAdmin
  195. * @link http://www.phpbb.com
  196. * @link http://phppgadmin.sourceforge.net/
  197. *
  198. * @param int $type Le type de donnees qu'on veut sauvegarder (structure, contenu ou les deux)
  199. * @param array $save_table Les tables a sauvegarder
  200. * @param bool $comment Ajoute un commentaire en debut de table
  201. */
  202. public function dump_pgsql($type, $save_table, $comment = true)
  203. {
  204. if ($type & self::STRUCT)
  205. {
  206. $this->write($this->pgsql_get_sequence());
  207. }
  208. foreach ($save_table AS $tablename)
  209. {
  210. if ($type & self::STRUCT)
  211. {
  212. if ($comment)
  213. {
  214. $this->write("\n#\n# Structure de la table PostgreSQL `$tablename`\n#\n");
  215. }
  216. $this->write($this->pgsql_get_create_table($tablename, "\n", false));
  217. }
  218. if ($type & self::DATA)
  219. {
  220. $this->write($this->dump_database($tablename, "PostgreSQL", $comment, $this->multi_insert));
  221. }
  222. }
  223. }
  224. /**
  225. * Cree le schema CREATE d'une table PostGreSQL
  226. *
  227. * @param string $table Nom de la table pour la creation du schema
  228. * @param string $crlf Caractere de retour a la ligne
  229. * @param bool $drop true pour inserer des ennonces DROP avant la creation des tables
  230. * @return string Schema de la table
  231. */
  232. private function pgsql_get_create_table($table, $crlf, $drop)
  233. {
  234. $schema_create = '';
  235. // Recupere les champs de la table et leur type
  236. $field_query = "SELECT a.attnum, a.attname AS field, t.typname as type, a.attlen AS length, a.atttypmod as lengthvar, a.attnotnull as notnull
  237. FROM pg_class c, pg_attribute a, pg_type t
  238. WHERE c.relname = '$table'
  239. AND a.attnum > 0
  240. AND a.attrelid = c.oid
  241. AND a.atttypid = t.oid
  242. ORDER BY a.attnum";
  243. $result = Fsb::$db->query($field_query);
  244. if ($drop)
  245. {
  246. $schema_create .= "DROP TABLE $table;$crlf";
  247. }
  248. $schema_create .= "CREATE TABLE $table($crlf";
  249. while ($row = Fsb::$db->row($result))
  250. {
  251. // On recupere la valeur par defaut
  252. $sql_get_default = "SELECT d.adsrc AS rowdefault
  253. FROM pg_attrdef d, pg_class c
  254. WHERE (c.relname = '$table')
  255. AND (c.oid = d.adrelid)
  256. AND d.adnum = " . $row['attnum'];
  257. $def_res = Fsb::$db->simple_query($sql_get_default);
  258. if (!$def_res)
  259. {
  260. unset($row['rowdefault']);
  261. }
  262. else
  263. {
  264. $row_default = Fsb::$db->row($def_res);
  265. $row['rowdefault'] = $row_default['rowdefault'];
  266. }
  267. if ($row['type'] == 'bpchar')
  268. {
  269. $row['type'] = 'char';
  270. }
  271. $schema_create .= ' ' . $row['field'] . ' ' . $row['type'];
  272. if (eregi('char', $row['type']))
  273. {
  274. if ($row['lengthvar'] > 0)
  275. {
  276. $schema_create .= '(' . ($row['lengthvar'] -4) . ')';
  277. }
  278. }
  279. if (eregi('numeric', $row['type']))
  280. {
  281. $schema_create .= '(';
  282. $schema_create .= sprintf("%s,%s", (($row['lengthvar'] >> 16) & 0xffff), (($row['lengthvar'] - 4) & 0xffff));
  283. $schema_create .= ')';
  284. }
  285. if (!empty($row['rowdefault']))
  286. {
  287. $schema_create .= ' DEFAULT ' . $row['rowdefault'];
  288. }
  289. if ($row['notnull'] == 't')
  290. {
  291. $schema_create .= ' NOT null';
  292. }
  293. $schema_create .= ",$crlf";
  294. }
  295. // Liste des clefs primaires
  296. $sql_pri_keys = "SELECT ic.relname AS index_name, bc.relname AS tab_name, ta.attname AS column_name, i.indisunique AS unique_key, i.indisprimary AS primary_key
  297. FROM pg_class bc, pg_class ic, pg_index i, pg_attribute ta, pg_attribute ia
  298. WHERE (bc.oid = i.indrelid)
  299. AND (ic.oid = i.indexrelid)
  300. AND (ia.attrelid = i.indexrelid)
  301. AND (ta.attrelid = bc.oid)
  302. AND (bc.relname = '$table')
  303. AND (ta.attrelid = i.indrelid)
  304. AND (ta.attnum = i.indkey[ia.attnum-1])
  305. ORDER BY index_name, tab_name, column_name ";
  306. $result = Fsb::$db->query($sql_pri_keys);
  307. $primary_key = '';
  308. while ($row = Fsb::$db->row($result))
  309. {
  310. if ($row['primary_key'] == 't')
  311. {
  312. if (!empty($primary_key))
  313. {
  314. $primary_key .= ', ';
  315. }
  316. $primary_key .= $row['column_name'];
  317. $primary_key_name = $row['index_name'];
  318. }
  319. else
  320. {
  321. $index_rows[$row['index_name']]['table'] = $table;
  322. $index_rows[$row['index_name']]['unique'] = ($row['unique_key'] == 't') ? ' UNIQUE ' : '';
  323. if (!isset($index_rows[$row['index_name']]['column_names']))
  324. {
  325. $index_rows[$row['index_name']]['column_names'] = '';
  326. }
  327. $index_rows[$row['index_name']]['column_names'] .= $row['column_name'] . ', ';
  328. }
  329. }
  330. $index_create = '';
  331. if (!empty($index_rows))
  332. {
  333. foreach ($index_rows AS $idx_name => $props)
  334. {
  335. $props['column_names'] = ereg_replace(", $", "" , $props['column_names']);
  336. $index_create .= 'CREATE ' . $props['unique'] . " INDEX $idx_name ON $table (" . $props['column_names'] . ");$crlf";
  337. }
  338. }
  339. if (!empty($primary_key))
  340. {
  341. $schema_create .= " CONSTRAINT $primary_key_name PRIMARY KEY ($primary_key),$crlf";
  342. }
  343. $schema_create = ereg_replace(',' . $crlf . '$', '', $schema_create);
  344. $index_create = ereg_replace(',' . $crlf . '$', '', $index_create);
  345. $schema_create .= "$crlf);$crlf";
  346. if (!empty($index_create))
  347. {
  348. $schema_create .= $index_create;
  349. }
  350. return (stripslashes($schema_create));
  351. }
  352. /**
  353. * Permet de recuperer les sequences Postgresql
  354. *
  355. * @return string
  356. */
  357. private function pgsql_get_sequence()
  358. {
  359. $content = '';
  360. $sql = 'SELECT c.relname AS seqname FROM pg_catalog.pg_class c, pg_catalog.pg_user u, pg_catalog.pg_namespace n
  361. WHERE c.relowner = u.usesysid
  362. AND c.relnamespace = n.oid
  363. AND c.relkind = \'S\'
  364. AND n.nspname = \'public\'
  365. ORDER BY seqname';
  366. $result = Fsb::$db->query($sql);
  367. if (!$row = Fsb::$db->row($result))
  368. {
  369. return ($content);
  370. }
  371. else
  372. {
  373. $content .= "# Sequences \n";
  374. do
  375. {
  376. $row = Fsb::$db->row($result);
  377. $sequence_name = $row['seqname'];
  378. $sql_sequence = "SELECT * FROM $sequence_name";
  379. $result_sequence = Fsb::$db->query($sql_sequence);
  380. if ($row_sequence = Fsb::$db->row($result_sequence))
  381. {
  382. $content .= "CREATE SEQUENCE $sequence_name start " . $row_sequence['last_value'] . ' increment ' . $row_sequence['increment_by'] . ' maxvalue ' . $row_sequence['max_value'] . ' minvalue ' . $row_sequence['min_value'] . ' cache ' . $row_sequence['cache_value'] . ";\n";
  383. }
  384. if ($row_sequence['last_value'] > 1)
  385. {
  386. $content .= "SELECT NEXTVALE('$sequence_name'); \n";
  387. }
  388. Fsb::$db->free($result_sequence);
  389. }
  390. while ($row = Fsb::$db->row($result));
  391. }
  392. Fsb::$db->free($result);
  393. return ($content);
  394. }
  395. /**
  396. * Lance un dump des tables SQLite du forum
  397. *
  398. * @param int $type Le type de donnees qu'on veut sauvegarder (structure, contenu ou les deux)
  399. * @param array $save_table Les tables a sauvegarder
  400. * @param bool $comment Ajoute un commentaire en debut de table
  401. */
  402. public function dump_sqlite($type, $save_table, $comment = true)
  403. {
  404. $content = '';
  405. foreach ($save_table AS $tablename)
  406. {
  407. if ($type & self::STRUCT)
  408. {
  409. $sql = "SELECT sql
  410. FROM sqlite_master
  411. WHERE tbl_name='$tablename'";
  412. $result = Fsb::$db->query($sql);
  413. if ($comment)
  414. {
  415. $this->write("\n#\n# Structure de la table SQLite `$tablename`\n#\n");
  416. }
  417. while ($row = Fsb::$db->row($result))
  418. {
  419. if ($row['sql'])
  420. {
  421. $content .= $row['sql'] . ";\n";
  422. }
  423. }
  424. }
  425. if ($type & self::DATA)
  426. {
  427. $this->write($this->dump_database($tablename, "SQLite", $comment, $this->multi_insert));
  428. }
  429. }
  430. }
  431. /**
  432. * Cree un dump du contenu d'une table
  433. *
  434. * @param string $tablename Nom de la table
  435. * @param string $sgbd_name Type de base de donnee
  436. * @param bool $comment Commentaire pour le dump
  437. * @param bool $multi_insert Gestion des multi insertions
  438. * @param array $except Contient la liste des champs a ne pas prendre en compte
  439. */
  440. public function dump_database($tablename, $sgbd_name, $comment = true, $multi_insert = false, $except = array())
  441. {
  442. // Si la SGBD ne supporte pas les multi insertions on force le parametre a false
  443. if (!Fsb::$db->can_use_multi_insert)
  444. {
  445. $multi_insert = false;
  446. }
  447. $get_fields = false;
  448. $fields_type = array();
  449. $content = '';
  450. if ($comment)
  451. {
  452. $this->write("\n#\n# Contenu de la table $sgbd_name `$tablename`\n#\n");
  453. }
  454. // Donnees de la table
  455. $sql = "SELECT *
  456. FROM $tablename";
  457. $result = Fsb::$db->query($sql);
  458. $multi_values = '';
  459. $k = 0;
  460. while ($row = Fsb::$db->row($result))
  461. {
  462. $values = '';
  463. foreach ($row AS $field => $value)
  464. {
  465. // Si on ne prend pas en compte le champ
  466. if ($except && in_array($field, $except))
  467. {
  468. continue;
  469. }
  470. if (!$get_fields)
  471. {
  472. // On recupere les champs si cela n'a pas deja ete fait
  473. $fields_type[$field] = Fsb::$db->get_field_type($result, $field, $tablename);
  474. }
  475. // On recupere les valeurs de la ligne courante
  476. $values .= (($values) ? ', ' : '') . (($fields_type[$field] == 'string') ? '\'' . Fsb::$db->escape($value) . '\'' : $value);
  477. }
  478. if (!$get_fields)
  479. {
  480. $fields = implode(', ', array_keys($fields_type));
  481. }
  482. if (!$multi_insert || !$get_fields)
  483. {
  484. $this->write("INSERT INTO $tablename ($fields) VALUES ");
  485. }
  486. if ($multi_insert && $get_fields)
  487. {
  488. $this->write(",\n");
  489. }
  490. $this->write("($values)");
  491. if (!$multi_insert)
  492. {
  493. $this->write(";\n");
  494. }
  495. $get_fields = true;
  496. }
  497. if ($multi_insert)
  498. {
  499. $this->write(";\n");
  500. }
  501. }
  502. }
  503. /* EOF */