5 * This should allow you to dump a series of data, so it could be restored later...
6 * The format will be an SQL file...
9 * php index.php Admin/Dump --table=Project --where="id=123"
10 * --dump-dir=/directory_to_put_sql+shell files
12 * outputs list of generated files.
15 * {DATE}.sql - the recreate sql including all dependancies, run with mysql DB -f < ....
16 * {DATE}.restore.sh - shell script to recreate files that where removed (excluding thumgs)
19 * {DATE}.copy.sh - shell script that backs up all the related files.
22 * {DATE}.delete.sql - the delete data sql.
23 * {DATE}.delete.sh - shell script to delete the files related to these records
26 * Basically it has to output all the records and their dependants. (parent and children)
28 * Then when deleting it deletes record + children..
31 * Each ouput is simple insert statement..
34 * TODO - handle Images table (or similar) where we use tablename=XXXX, tid=.... etc..
39 * DataObjects->archivePaths() - returns array ( sourcedirectory, remainder of path to dependant file )
40 * DataObjects->listThumbs() - returns array ( list of full path to thumbnail urls. )
49 require_once 'Pman.php';
51 class Pman_Admin_Dump extends Pman {
56 if (!HTML_FlexyFramework::get()->cli) {
57 die("Access only permitted from cli");
62 var $deps = array(); // list of dependants
63 var $out = array(); // list of created sql/shell scripts.
67 $argv = $_SERVER['argv'];
71 $opts = explode(',', 'table==,where==,dump-dir==');
72 require_once 'Console/Getopt.php';
73 $go = Console_Getopt::getopt2($argv, '', $opts );
78 foreach($go[0] as $ar) {
79 $args[substr($ar[0],2)] = $ar[1];
82 foreach($opts as $req) {
83 if (empty($args[substr($req,0, -2)])) {
84 $errs[] = "--".substr($req,0, -2) . ' is required';
88 die(print_R($errs,true));
93 $this->discover($this->args['table'], $this->args['where']);
95 if (!file_exists($args['dump-dir'])) {
96 mkdir($args['dump-dir'], 0777, true);
99 $this->generateInsert();
100 $this->generateDelete();
101 $this->generateShell();
103 echo "GENERATED FILES:";
111 var $deletes = array(); // TABLE => [key] => TRUE|FALSE
112 var $dumps = array(); // TABLE => [key] => TRUE|FALSE - if it's been scanned..
116 * a) what depends on it (eg. child elements) - which will be deleted.
117 * b) what it depends on it (eg. parent elements) - which will be dumped..
120 function discover($table, $where, $is_delete = false )
123 if (!isset($this->dumps[$table])) {
124 $this->dumps[$table] = array();
126 if ($is_delete && !isset($this->deletes[$table])) {
127 $this->deletes[$table] = array();
130 $x = DB_DataObject::factory($table);
132 $x->whereAdd($where);
134 // a) id's of elements in this table
135 // b) columns which point to other tables..
136 $links = $x->links();
137 $cols = array_keys($links);
139 $cols = array_push($cols, $key[0]);
143 $x->selectAdd('`'. implode('`,`', $cols) . '`');
145 while ($x->fetch()) {
146 foreach($cols as $k) {
147 if (empty($x->$k)) { // skip blanks.
150 if (isset($links[$k])) {
152 $kv = explode(':', $links[$k]);
153 if (!isset($this->dumps[$kv[0]])) {
154 $this->dumps[$kv[0]] = array();
156 if (!isset($this->dumps[$kv[0]][$x->$k])) {
157 $this->dumps[$kv[0]][$x->$k] = false; // not checked yet..
161 // assume it's the key..
162 if (!isset($this->dumps[$table][$x->$k])) {
163 $this->dumps[$table][$x->$k] = false; // not checked yet..
166 $this->deletes[$table][$x->$k] = false; // not checked yet..
174 print_r($this->dumps);
181 function oldStuff() {
185 $target = $args['dump-dir'] .'/'. date('Y-m-d').'.sql';
187 $this->fh = fopen($target,'w');
189 //DB_DataObject::debugLevel(1);
190 // since we are runnign in cli mode... we will be a bit wild and free with verification
191 $x = DB_DataObject::factory($args['table']);
192 $x->{$args['col']} = $args['val'];
196 while ($x->fetch()) {
198 fwrite($this->fh, $this->toInsert($x));
199 $this->dumpChildren($x);
204 foreach($this->deps as $s=>$status) {
205 if (isset($this->dumped[$s])) {
208 list($tbl, $key, $val) = explode(':', $s);
209 $dd = DB_DataObject::factory($tbl);
210 if ($dd->get($key,$val)) {
211 fwrite($this->fh, $this->toInsert($dd));
218 function generateDelete() {
219 $target = $this->args['dump-dir'] .'/'. date('Y-m-d').'.delete.sql';
220 $this->out[] = $target;
221 $fh = fopen($target, 'w');
222 foreach($this->childscanned as $s=>$v) {
223 list($tbl, $key, $val) = explode(':', $s);
224 fwrite($fh, "DELETE FROM $tbl WHERE $key = $val;\n"); // we assume id's and nice column names...
229 function generateShell() {
232 $target = $this->args['dump-dir'] .'/'. date('Y-m-d').'.copy.sh';
233 $this->out[] = $target;
234 $fh = fopen($target, 'w');
236 $target = $this->args['dump-dir'] .'/'. date('Y-m-d').'.delete.sh';
237 $this->out[] = $target;
238 $fh2 = fopen($target, 'w');
240 $target = $this->args['dump-dir'] .'/'. date('Y-m-d').'.restore.sh';
241 $this->out[] = $target;
242 $fh3 = fopen($target, 'w');
245 foreach($this->childfiles as $s=>$v) {
246 fwrite($fh,"mkdir -p " . escapeshellarg(dirname($args['dump-dir'] .'/'.$v[1])) ."\n" );
247 fwrite($fh,"cp " . escapeshellarg($v[0].'/'.$v[1]) . ' ' . escapeshellarg($args['dump-dir'] .'/'.$v[1]) ."\n" );
249 fwrite($fh3,"mkdir -p " . escapeshellarg(dirname($v[0].'/'.$v[1])) ."\n" );
250 fwrite($fh3,"cp " . escapeshellarg($args['dump-dir'] .'/'.$v[1]) . ' ' . escapeshellarg($v[0].'/'.$v[1]) . "\n" );
252 fwrite($fh2,"rm " . escapeshellarg($v[0].'/'.$v[1]) ."\n" );
255 fclose($fh3); // restore does not need to bother with thumbnails.
259 foreach($this->childthumbs as $s=>$v) {
261 fwrite($fh2,"rm " . escapeshellarg($vv). "\n");
269 var $children = array(); // map of search->checked
270 var $childscanned = array();
271 var $childfiles = array();
272 var $childthumbs = array();
273 function dumpChildren($do)
275 $kcol = array_shift($do->keys());
276 $kid = $do->tableName() . ':' . $kcol . ':' . $do->{$kcol};
277 if (isset($this->childscanned[$kid])) {
280 $this->childscanned[$kid] = true;
282 if (method_exists($do,'archivePaths')) {
283 $ct = $do->archivePaths();
285 $this->childfiles[$kid] = $ct;
288 if (method_exists($do,'listThumbs')) {
289 $ct = $do->listThumbs();
291 $this->childthumbs[$kid] = $ct;
295 global $_DB_DATAOBJECT;
296 $do->links();; //force load
297 $tn = $do->tableName();
300 foreach($_DB_DATAOBJECT['LINKS'][$do->database()] as $tbl => $links) {
301 // hack.. - we should get rid of this hack..
302 if ($tbl == 'database__render') {
305 //if ($tbl == $tn) { // skip same table
308 foreach ($links as $tk => $kv) {
311 list($k,$v) = explode(':', $kv);
315 $add = implode(':', array($tbl, $tk, $do->$v));
316 //echo "ADD $tbl $tk=>$kv : $add\n";
317 $this->children[$add] = 0;
322 // print_r($this->children);exit;
323 $ch = $this->children ;
326 foreach($ch as $s=>$status) {
327 if ($this->children[$s]) {
330 // flag it as being done, so we do not recurse..
331 $this->children[$s] = 1;
333 list($tbl, $key, $val) = explode(':', $s);
334 $dd = DB_DataObject::factory($tbl);
338 while ($dd->fetch()) {
339 $todo [] = clone($dd);
340 // if we have dumped this already.. ignore it..
346 foreach($todo as $dd) {
347 fwrite($this->fh, $this->toInsert($dd));
348 $this->dumpChildren($dd);
356 var $dumped = array();
358 * toInsert - does not handle NULLS...
360 function toInsert($do)
362 $kcol = array_shift($do->keys());
363 $kid = $do->tableName() . ':' . $kcol . ':' . $do->{$kcol};
364 if (isset($this->dumped[$kid])) {
367 //echo "DUMP: $kid\n";
368 $this->dumped[$kid] = true;
370 // for auto_inc column we need to use a 'set argument'...
371 $items = $do->table();
373 $quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
379 $deplinks = $do->links();
381 foreach( $items as $k=>$v)
388 $leftq .= ($quoteIdentifiers ? ($DB->quoteIdentifier($k) . ' ') : "$k ");
390 // only handles numeric links..
391 if (is_numeric($do->$k) && $do->$k && $deplinks && !empty($deplinks[$k])) {
392 // die("got deplink" . $deplinks[$k]);
393 $l = explode(':', $deplinks[$k]);
394 $add = $deplinks[$k].':' . $do->$k;
396 $this->deps[$add] = 0;
401 if ($v & DB_DATAOBJECT_STR) {
402 $rightq .= $do->_quote((string) (
403 ($v & DB_DATAOBJECT_BOOL) ?
404 // this is thanks to the braindead idea of postgres to
405 // use t/f for boolean.
406 (($do->$k === 'f') ? 0 : (int)(bool) $do->$k) :
411 if (is_numeric($do->$k)) {
412 $rightq .=" {$do->$k} ";
415 $rightq .= ' ' . intval($do->$k) . ' ';
417 $table = ($quoteIdentifiers ? $DB->quoteIdentifier($do->__table) : $do->__table);
418 return "INSERT INTO {$table} ($leftq) VALUES ($rightq);\n";