Failed Conditions
Push — master ( 398dce...01f535 )
by Adrien
02:21
created

AbstractDatabase::loadTestUsers()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 3
cp 0
crap 2
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Ecodev\Felix\Service;
6
7
/**
8
 * Tool to reload the entire local database from remote database for a given site
9
 *
10
 * Requirements:
11
 *
12
 * - ssh access to remote server (via ~/.ssh/config)
13
 * - both local and remote sites must be accesible via: /sites/MY_SITE
14
 * - both local and remote config/autoload/local.php files must contains the database connection info
15
 */
16
abstract class AbstractDatabase
17
{
18
    /**
19
     * Dump data from database on $remote server
20
     *
21
     * @param string $remote
22
     * @param string $dumpFile path
23
     */
24
    private static function dumpDataRemotely(string $remote, string $dumpFile): void
25
    {
26
        $sshCmd = <<<STRING
27
        ssh $remote "cd /sites/$remote/ && php7.4 bin/dump-data.php $dumpFile"
28
STRING;
29
30
        echo "dumping data $dumpFile on $remote...\n";
31
        self::executeLocalCommand($sshCmd);
32
    }
33
34
    /**
35
     * Dump data from database
36
     *
37
     * @param string $dumpFile path
38
     */
39
    public static function dumpData(string $dumpFile): void
40
    {
41
        $config = require 'config/autoload/local.php';
42
        $dbConfig = $config['doctrine']['connection']['orm_default']['params'];
43
        $host = $dbConfig['host'];
44
        $username = $dbConfig['user'];
45
        $database = $dbConfig['dbname'];
46
        $password = $dbConfig['password'];
47
48
        echo "dumping $dumpFile...\n";
49
        $dumpCmd = "mysqldump -v --user=$username --password=$password --host=$host $database | sed 's/DEFINER=[^*]*\\*/\\*/g' | gzip > $dumpFile";
50
        self::executeLocalCommand($dumpCmd);
51
    }
52
53
    /**
54
     * Copy a file from $remote
55
     *
56
     * @param string $remote
57
     * @param string $dumpFile
58
     */
59
    private static function copyFile(string $remote, string $dumpFile): void
60
    {
61
        $copyCmd = <<<STRING
62
        rsync -avz --progress $remote:$dumpFile $dumpFile
63
STRING;
64
65
        echo "copying dump to $dumpFile ...\n";
66
        self::executeLocalCommand($copyCmd);
67
    }
68
69
    /**
70
     * Load SQL dump in local database
71
     *
72
     * @param string $dumpFile
73
     */
74
    public static function loadData(string $dumpFile): void
75
    {
76
        $config = require 'config/autoload/local.php';
77
        $dbConfig = $config['doctrine']['connection']['orm_default']['params'];
78
        $host = $dbConfig['host'];
79
        $username = $dbConfig['user'];
80
        $database = $dbConfig['dbname'];
81
        $password = $dbConfig['password'];
82
83
        $dumpFile = realpath($dumpFile);
84
        if ($dumpFile === false) {
85
            throw new \Exception('Cannot find absolute path for file: ' . $dumpFile);
86
        }
87
88
        echo "loading dump $dumpFile...\n";
89
        if (!is_readable($dumpFile)) {
90
            throw new \Exception("Cannot read dump file \"$dumpFile\"");
91
        }
92
93
        self::executeLocalCommand(PHP_BINARY . ' ./vendor/bin/doctrine orm:schema-tool:drop --ansi --full-database --force');
94
        self::executeLocalCommand("gunzip -c \"$dumpFile\" | mysql --user=$username --password=$password --host=$host $database");
95
        self::executeLocalCommand(PHP_BINARY . ' ./vendor/bin/doctrine-migrations --ansi migrations:migrate --no-interaction');
96
        self::loadTriggers();
97
        self::loadTestUsers();
98
    }
99
100
    public static function loadRemoteData(string $remote): void
101
    {
102
        $dumpFile = "/tmp/$remote." . exec('whoami') . '.backup.sql.gz';
103
        self::dumpDataRemotely($remote, $dumpFile);
104
        self::copyFile($remote, $dumpFile);
105
        self::loadData($dumpFile);
106
107
        echo "database updated\n";
108
    }
109
110
    /**
111
     * Execute a shell command and throw exception if fails
112
     *
113
     * @param string $command
114
     *
115
     * @throws \Exception
116
     */
117
    public static function executeLocalCommand(string $command): void
118
    {
119
        $return_var = null;
120
        $fullCommand = "$command 2>&1";
121
        passthru($fullCommand, $return_var);
122
        if ($return_var) {
123
            throw new \Exception('FAILED executing: ' . $command);
124
        }
125
    }
126
127
    /**
128
     * Load test data
129
     */
130
    public static function loadTestData(): void
131
    {
132
        self::executeLocalCommand(PHP_BINARY . ' ./vendor/bin/doctrine orm:schema-tool:drop --ansi --full-database --force');
133
        self::executeLocalCommand(PHP_BINARY . ' ./vendor/bin/doctrine-migrations migrations:migrate --ansi --no-interaction');
134
        self::loadTriggers();
135
        self::loadTestUsers();
136
        self::importFile('tests/data/fixture.sql');
137
    }
138
139
    /**
140
     * Load triggers
141
     */
142
    public static function loadTriggers(): void
143
    {
144
        self::importFile('data/triggers.sql');
145
    }
146
147
    /**
148
     * Load test users
149
     */
150
    private static function loadTestUsers(): void
151
    {
152
        self::importFile('tests/data/users.sql');
153
    }
154
155
    /**
156
     * Import a SQL file into DB
157
     *
158
     * This use mysql command, instead of DBAL methods, to allow to see errors if any, and
159
     * also because it seems trigger creation do not work with DBAL for some unclear reasons.
160
     *
161
     * @param string $file
162
     */
163
    private static function importFile(string $file): void
164
    {
165
        $file = realpath($file);
166
        echo 'importing ' . $file . "\n";
167
        $connection = _em()->getConnection();
168
        $database = $connection->getDatabase();
169
        $username = $connection->getUsername();
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\DBAL\Connection::getUsername() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

169
        $username = /** @scrutinizer ignore-deprecated */ $connection->getUsername();
Loading history...
170
        $password = empty($connection->getPassword()) ? '' : '-p' . $connection->getPassword();
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\DBAL\Connection::getPassword() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

170
        $password = empty(/** @scrutinizer ignore-deprecated */ $connection->getPassword()) ? '' : '-p' . $connection->getPassword();
Loading history...
171
172
        $importCommand = "cat $file | mysql -u $username $password $database";
173
174
        self::executeLocalCommand($importCommand);
175
    }
176
}
177