1<?php
2// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
3//
4// All Rights Reserved. See copyright.txt for details and a complete list of authors.
5// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
6// $Id$
7
8namespace Tiki\Command;
9
10use Symfony\Component\Console\Command\Command;
11use Symfony\Component\Console\Input\InputArgument;
12use Symfony\Component\Console\Input\InputInterface;
13use Symfony\Component\Console\Input\InputOption;
14use Symfony\Component\Console\Output\OutputInterface;
15
16class BackupDBCommand extends Command
17{
18	protected function configure()
19	{
20		$this
21			->setName('database:backup')
22			->setDescription('Create a database backup (with mysqldump)')
23			->addArgument(
24				'path',
25				InputArgument::REQUIRED,
26				'Path to save backup (relative to console.php, or absolute)'
27			)
28			->addArgument(
29				'dateFormat',
30				InputArgument::OPTIONAL,
31				'Format to use for the date part of the backup file. Defaults to "Y-m-d_H-i-s" and uses the PHP date function format'
32			)
33		;
34	}
35
36	protected function execute(InputInterface $input, OutputInterface $output)
37	{
38		$path = $input->getArgument('path');
39		if (substr($path, -1) == '/') {
40			$path = substr($path, 0, strlen($path) - 1);
41		}
42
43		if (! is_dir($path)) {
44			$output->writeln('<error>Error: Provided path not found</error>');
45			return;
46		}
47
48		$local = \TikiInit::getCredentialsFile();
49		if (! is_readable($local)) {
50			$output->writeln('<error>Error: "' . $local . '" not readable.</error>');
51			return;
52		}
53
54		$dateFormat = $input->getArgument('dateFormat');
55		if (! $dateFormat) {
56			$dateFormat = 'Y-m-d_H-i-s';
57		}
58
59		$user_tiki = $pass_tiki = $host_tiki = $dbs_tiki = '';
60
61		require $local;
62
63		$args = [];
64		if ($user_tiki) {
65			$args[] = "-u" . escapeshellarg($user_tiki);
66		}
67		if ($pass_tiki) {
68			$args[] = "-p" . escapeshellarg($pass_tiki);
69		}
70		if ($host_tiki) {
71			$args[] = "-h" . escapeshellarg($host_tiki);
72		}
73
74		// Find out how many non-InnoDB tables exist in the schema
75		$db = \TikiDb::get();
76		$query = "SELECT count(TABLE_NAME) FROM information_schema.TABLES WHERE TABLE_SCHEMA = '$dbs_tiki' AND engine <> 'InnoDB'";
77		$numTables = $db->getOne($query);
78
79		if ($numTables === '0') {
80			$args[] = "--single-transaction";
81		} else {
82			$dbOpenFilesLimit = 0;
83			$result = $db->fetchAll('SHOW GLOBAL VARIABLES LIKE "open_files_limit"');
84			if (count($result) > 0) {
85				$dbOpenFilesLimit = (int)$result[0]['Value'];
86			}
87			if ($dbOpenFilesLimit > 0 && $dbOpenFilesLimit < 2000) {
88				// some distributions bring a lower limit of open files, so lock all tables during backup might fail the backup
89				$output->writeln('<info>Mysql database has open_files_limit=' . $dbOpenFilesLimit . ', skipping lock tables to avoid failing the backup</info>');
90			} else {
91				$args[] = "--lock-tables";
92			}
93		}
94
95		$args[] = $dbs_tiki;
96
97		$args = implode(' ', $args);
98		$outputFile = $path . '/' . $dbs_tiki . '_' . date($dateFormat) . '.sql.gz';
99		$command = "mysqldump --quick --create-options --extended-insert $args | gzip -5 > " . escapeshellarg($outputFile);
100		exec($command);
101		$output->writeln('<comment>Database backup completed: ' . $outputFile . '</comment>');
102	}
103}
104