Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
namespace Illuminate\Database\Console;
4
 
5
use Doctrine\DBAL\Platforms\AbstractPlatform;
6
use Illuminate\Console\Command;
7
use Illuminate\Database\ConnectionInterface;
8
use Illuminate\Database\MySqlConnection;
9
use Illuminate\Database\PostgresConnection;
10
use Illuminate\Database\QueryException;
11
use Illuminate\Database\SQLiteConnection;
12
use Illuminate\Database\SqlServerConnection;
13
use Illuminate\Support\Arr;
14
use Illuminate\Support\Composer;
15
use Symfony\Component\Process\Exception\ProcessSignaledException;
16
use Symfony\Component\Process\Exception\RuntimeException;
17
use Symfony\Component\Process\Process;
18
 
19
abstract class DatabaseInspectionCommand extends Command
20
{
21
    /**
22
     * A map of database column types.
23
     *
24
     * @var array
25
     */
26
    protected $typeMappings = [
27
        'bit' => 'string',
28
        'citext' => 'string',
29
        'enum' => 'string',
30
        'geometry' => 'string',
31
        'geomcollection' => 'string',
32
        'linestring' => 'string',
33
        'ltree' => 'string',
34
        'multilinestring' => 'string',
35
        'multipoint' => 'string',
36
        'multipolygon' => 'string',
37
        'point' => 'string',
38
        'polygon' => 'string',
39
        'sysname' => 'string',
40
    ];
41
 
42
    /**
43
     * The Composer instance.
44
     *
45
     * @var \Illuminate\Support\Composer
46
     */
47
    protected $composer;
48
 
49
    /**
50
     * Create a new command instance.
51
     *
52
     * @param  \Illuminate\Support\Composer|null  $composer
53
     * @return void
54
     */
55
    public function __construct(Composer $composer = null)
56
    {
57
        parent::__construct();
58
 
59
        $this->composer = $composer ?? $this->laravel->make(Composer::class);
60
    }
61
 
62
    /**
63
     * Register the custom Doctrine type mappings for inspection commands.
64
     *
65
     * @param  \Doctrine\DBAL\Platforms\AbstractPlatform  $platform
66
     * @return void
67
     */
68
    protected function registerTypeMappings(AbstractPlatform $platform)
69
    {
70
        foreach ($this->typeMappings as $type => $value) {
71
            $platform->registerDoctrineTypeMapping($type, $value);
72
        }
73
    }
74
 
75
    /**
76
     * Get a human-readable platform name for the given platform.
77
     *
78
     * @param  \Doctrine\DBAL\Platforms\AbstractPlatform  $platform
79
     * @param  string  $database
80
     * @return string
81
     */
82
    protected function getPlatformName(AbstractPlatform $platform, $database)
83
    {
84
        return match (class_basename($platform)) {
85
            'MySQLPlatform' => 'MySQL <= 5',
86
            'MySQL57Platform' => 'MySQL 5.7',
87
            'MySQL80Platform' => 'MySQL 8',
88
            'PostgreSQL100Platform', 'PostgreSQLPlatform' => 'Postgres',
89
            'SqlitePlatform' => 'SQLite',
90
            'SQLServerPlatform' => 'SQL Server',
91
            'SQLServer2012Platform' => 'SQL Server 2012',
92
            default => $database,
93
        };
94
    }
95
 
96
    /**
97
     * Get the size of a table in bytes.
98
     *
99
     * @param  \Illuminate\Database\ConnectionInterface  $connection
100
     * @param  string  $table
101
     * @return int|null
102
     */
103
    protected function getTableSize(ConnectionInterface $connection, string $table)
104
    {
105
        return match (true) {
106
            $connection instanceof MySqlConnection => $this->getMySQLTableSize($connection, $table),
107
            $connection instanceof PostgresConnection => $this->getPostgresTableSize($connection, $table),
108
            $connection instanceof SQLiteConnection => $this->getSqliteTableSize($connection, $table),
109
            default => null,
110
        };
111
    }
112
 
113
    /**
114
     * Get the size of a MySQL table in bytes.
115
     *
116
     * @param  \Illuminate\Database\ConnectionInterface  $connection
117
     * @param  string  $table
118
     * @return mixed
119
     */
120
    protected function getMySQLTableSize(ConnectionInterface $connection, string $table)
121
    {
122
        $result = $connection->selectOne('SELECT (data_length + index_length) AS size FROM information_schema.TABLES WHERE table_schema = ? AND table_name = ?', [
123
            $connection->getDatabaseName(),
124
            $table,
125
        ]);
126
 
127
        return Arr::wrap((array) $result)['size'];
128
    }
129
 
130
    /**
131
     * Get the size of a Postgres table in bytes.
132
     *
133
     * @param  \Illuminate\Database\ConnectionInterface  $connection
134
     * @param  string  $table
135
     * @return mixed
136
     */
137
    protected function getPostgresTableSize(ConnectionInterface $connection, string $table)
138
    {
139
        $result = $connection->selectOne('SELECT pg_total_relation_size(?) AS size;', [
140
            $table,
141
        ]);
142
 
143
        return Arr::wrap((array) $result)['size'];
144
    }
145
 
146
    /**
147
     * Get the size of a SQLite table in bytes.
148
     *
149
     * @param  \Illuminate\Database\ConnectionInterface  $connection
150
     * @param  string  $table
151
     * @return mixed
152
     */
153
    protected function getSqliteTableSize(ConnectionInterface $connection, string $table)
154
    {
155
        try {
156
            $result = $connection->selectOne('SELECT SUM(pgsize) AS size FROM dbstat WHERE name=?', [
157
                $table,
158
            ]);
159
 
160
            return Arr::wrap((array) $result)['size'];
161
        } catch (QueryException $e) {
162
            return null;
163
        }
164
    }
165
 
166
    /**
167
     * Get the number of open connections for a database.
168
     *
169
     * @param  \Illuminate\Database\ConnectionInterface  $connection
170
     * @return int|null
171
     */
172
    protected function getConnectionCount(ConnectionInterface $connection)
173
    {
174
        $result = match (true) {
175
            $connection instanceof MySqlConnection => $connection->selectOne('show status where variable_name = "threads_connected"'),
176
            $connection instanceof PostgresConnection => $connection->selectOne('select count(*) AS "Value" from pg_stat_activity'),
177
            $connection instanceof SqlServerConnection => $connection->selectOne('SELECT COUNT(*) Value FROM sys.dm_exec_sessions WHERE status = ?', ['running']),
178
            default => null,
179
        };
180
 
181
        if (! $result) {
182
            return null;
183
        }
184
 
185
        return Arr::wrap((array) $result)['Value'];
186
    }
187
 
188
    /**
189
     * Get the connection configuration details for the given connection.
190
     *
191
     * @param  string  $database
192
     * @return array
193
     */
194
    protected function getConfigFromDatabase($database)
195
    {
196
        $database ??= config('database.default');
197
 
198
        return Arr::except(config('database.connections.'.$database), ['password']);
199
    }
200
 
201
    /**
202
     * Ensure the dependencies for the database commands are available.
203
     *
204
     * @return bool
205
     */
206
    protected function ensureDependenciesExist()
207
    {
208
        return tap(interface_exists('Doctrine\DBAL\Driver'), function ($dependenciesExist) {
209
            if (! $dependenciesExist && $this->components->confirm('Inspecting database information requires the Doctrine DBAL (doctrine/dbal) package. Would you like to install it?')) {
210
                $this->installDependencies();
211
            }
212
        });
213
    }
214
 
215
    /**
216
     * Install the command's dependencies.
217
     *
218
     * @return void
219
     *
220
     * @throws \Symfony\Component\Process\Exception\ProcessSignaledException
221
     */
222
    protected function installDependencies()
223
    {
224
        $command = collect($this->composer->findComposer())
225
            ->push('require doctrine/dbal')
226
            ->implode(' ');
227
 
228
        $process = Process::fromShellCommandline($command, null, null, null, null);
229
 
230
        if ('\\' !== DIRECTORY_SEPARATOR && file_exists('/dev/tty') && is_readable('/dev/tty')) {
231
            try {
232
                $process->setTty(true);
233
            } catch (RuntimeException $e) {
234
                $this->components->warn($e->getMessage());
235
            }
236
        }
237
 
238
        try {
239
            $process->run(fn ($type, $line) => $this->output->write($line));
240
        } catch (ProcessSignaledException $e) {
241
            if (extension_loaded('pcntl') && $e->getSignal() !== SIGINT) {
242
                throw $e;
243
            }
244
        }
245
    }
246
}