Completed
Push — master ( 80fdf3...351fbc )
by Vladimir
02:43
created

src/Composer/ScriptHandler.php (1 issue)

Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * This file contains scripts that are run on composer events and commands - for
4
 * example, you might want to regenerate the cache after updating
5
 *
6
 * @license    https://github.com/allejo/bzion/blob/master/LICENSE.md GNU General Public License Version 3
7
 */
8
9
namespace BZIon\Composer;
10
11
use Composer\IO\ConsoleIO;
12
use Composer\IO\IOInterface;
13
use Composer\Script\Event;
14
use Phinx\Console\PhinxApplication;
15
use Symfony\Component\Console\Helper\HelperSet;
16
use Symfony\Component\Console\Helper\QuestionHelper;
17
use Symfony\Component\Console\Input\ArrayInput;
18
use Symfony\Component\Console\Output\ConsoleOutput;
19
use Symfony\Component\Filesystem\Filesystem;
20
use Symfony\Component\Finder\Finder;
21
use Symfony\Component\Process\Process;
22
use Symfony\Component\Yaml\Yaml;
23
24
/**
25
 * A manager for composer events
26
 */
27
class ScriptHandler
28
{
29
    /**
30
     * Shows what changed since the last update
31
     *
32
     * @param $event Event Composer's event
33
     */
34
    public static function showChangelog(Event $event)
35
    {
36
        static::executeCommand($event, 'bzion:changes');
37
    }
38
39
    /**
40
     * Clears the Symfony cache.
41
     *
42
     * This command won't fail if the current cache prevents the kernel from
43
     * booting
44
     *
45
     * @param $event Event       Composer's event
46
     * @param $env   string|null The environment to clear the cache for, 'all'
47
     *                           to clear the cache for all environments, null
48
     *                           to pick an environment based on command line
49
     *                           arguments (defaults to 'all')
50
     */
51
    public static function clearCache(Event $event, $env = null)
52
    {
53
        $io = $event->getIO();
54
        $args = $event->getArguments();
55
56
        if ($env === null) {
57
            if (isset($args[0])) {
58
                $env = $args[0];
59
            } elseif (getenv('SYMFONY_ENV')) {
60
                $env = getenv('SYMFONY_ENV');
61
            } else {
62
                $env = 'all';
63
            }
64
        }
65
66
    // Delete all cache files
67
    $finder = new Finder();
68
        $cacheDirectory = __DIR__ . '/../../app/cache/';
69
70
        $fs = new Filesystem();
71
72
        $clear = true;
73
74
    // We make sure that the root directories for each environment aren't
75
    // removed, so that permission settings are kept
76
    if ($env === 'all') {
77
        $io->write("Clearing cache for <fg=green;bold>all</> environments");
78
        $finder->in($cacheDirectory)->depth('== 1');
79
    } else {
80
        $directory = $cacheDirectory . str_replace('/', '', $env);
81
82
        if ($fs->exists($directory)) {
83
            $io->write("Clearing cache for the <fg=green>$env</> environment");
84
            $finder->in($directory)->depth('== 0');
85
        } else {
86
            $io->write("Cache directory for the <fg=green>$env</> environment doesn't exist and won't be cleared");
87
            $clear = false;
88
        }
89
    }
90
91
        if ($clear) {
92
            // We use Symfony's Filesystem component to delete files recursively
93
        $fs->remove($finder);
0 ignored issues
show
$finder is of type object<Symfony\Component\Finder\Finder>, but the function expects a string|object<Symfony\Co...nt\Filesystem\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
94
        }
95
96
        if ($env === 'prod' || $env === 'all') {
97
            static::executeCommand($event, 'cache:warmup');
98
        }
99
    }
100
101
    /**
102
     * Clear the cache for all environments
103
     *
104
     * @param $event Event Composer's event
105
     */
106
    public static function clearAllCaches(Event $event)
107
    {
108
        return static::clearCache($event, 'all');
109
    }
110
111
    /**
112
     * Initialize the last update file so that when the user updates and asks
113
     * for the changelog, the entries added before the installation are not shown
114
     *
115
     * @param $event Event Composer's event
116
     */
117
    public static function initializeChangelog(Event $event)
118
    {
119
        static::executeCommand($event, 'bzion:changes --read');
120
    }
121
122
    /**
123
     * Migrate the config.yml file
124
     *
125
     * @param $event Event Composer's event
126
     */
127
    public static function buildConfig(Event $event)
128
    {
129
        $configHandler = new ConfigHandler($event);
130
        $configHandler->build();
131
    }
132
133
    /*
134
     * Create and update the database schema
135
     *
136
     * @param $event   Event|null Composer's event
137
     * @param $testing boolean    Whether to migrate the testing database (only applicable when $event is null)
138
     */
139 1
    public static function migrateDatabase(Event $event = null, $testing = false)
140
    {
141 1
        if ($event) {
142
            // Use the event's IO
143
            $io = $event->getIO();
144
145
            $arguments = $event->getArguments();
146
147
            $testingArguments = array('testing', '--testing', '-t');
148
            $testing = count(array_intersect($arguments, $testingArguments)) > 0;
149
        } else {
150
            // Create our own IO
151 1
            $input = new ArrayInput(array());
152 1
            $output = new ConsoleOutput();
153 1
            $helperSet = new HelperSet(array(new QuestionHelper()));
154
155 1
            $io = new ConsoleIO($input, $output, $helperSet);
156
        }
157
158
        try {
159 1
            $config = self::getDatabaseConfig($testing);
160
        } catch (\Exception $e) {
161
            $io->write("<bg=red>\n\n [WARNING] " . $e->getMessage() . ", the database won't be updated\n</>");
162
163
            return;
164
        }
165
166
        // If the database doesn't exist, ask the user to create it and perform
167
        // the necessary migrations (unless the user didn't agree to
168
        // create the database)
169 1
        if (self::createDatabase($io, $config['host'], $config['username'], $config['password'], $config['database'])) {
170 1
            $io->write(''); // newline
171
172 1
            $arguments = array('migrate', '-e' => ($testing) ? 'test' : 'main');
173 1
            $app = new PhinxApplication();
174 1
            $app->doRun(new ArrayInput($arguments), new ConsoleOutput());
175
        }
176 1
    }
177
178
    /**
179
     * Shows an installation success message
180
     *
181
     * @param $event Event Composer's event
182
     */
183
    public static function showSuccessMessage(Event $event)
184
    {
185
        static::executeCommand($event, 'bzion:success');
186
    }
187
188
    /**
189
     * Create the database schema if needed
190
     *
191
     * @param IOInterface $io       Composer's IO interface
192
     * @param string      $host     The database host
193
     * @param string      $username The username for the MySQL user
194
     * @param string      $password The password for the MySQL user
195
     * @param string      $database The name of the database
196
     *
197
     * @return bool Whether the database was created
198
     */
199 1
    private static function createDatabase(IOInterface $io, $host, $username, $password, $database)
200
    {
201 1
        $io->write(" Connecting to MySQL database $database@$host");
202
203 1
        $dsn = 'mysql:host=' . $host . ';charset=UTF8';
204 1
        $pdo = new \PDO($dsn, $username, $password);
205
206 1
        $statement = $pdo->prepare("USE `$database`");
207 1
        $status = $statement->execute();
208 1
        $errors = $statement->errorInfo();
209
210
        // Throw an exception on error for any query that will be sent next
211 1
        $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
212
213
        // 1049 is the error code thrown when the database doesn't exist, but
214
        // the MySQL user has the privilege to see it
215 1
        if ($errors[1] == 1049) {
216
            $answer = $io->askConfirmation(
217
                " <fg=green>The $database database doesn't exist. Would you like to have it created? (yes/no)</> [<comment>yes</comment>]\n > ",
218
                true);
219
220
            if ($answer) {
221
                $pdo->query("CREATE DATABASE `$database` COLLATE utf8_unicode_ci");
222
                $pdo->query("USE `$database`");
223
224
                $io->write(" <fg=green>New database created</>");
225
            } else {
226
                return false;
227
            }
228 1
        } elseif (!$status) {
229
            throw new \Exception("Unable to connect to database: " . $errors[2]);
230
        }
231
232
        // If the database is empty, fill it
233 1
        if ($pdo->query('SHOW TABLES')->rowCount() === 0) {
234 1
            $io->write(" <fg=green>Creating database schema...</> ", false);
235
236 1
            $sqlPath = realpath(__DIR__ . '/../../migrations/' . 'DATABASE.sql');
237 1
            $pdo->exec(file_get_contents($sqlPath));
238
239 1
            $io->write("<fg=green>done.</>");
240
        }
241
242 1
        return true;
243
    }
244
245
    /**
246
     * Execute a symfony console command
247
     *
248
     * @param  Event  $event   Composer's event
249
     * @param  string $command The command to execute
250
     * @param  int    $timeout The timeout of the command in seconds
251
     * @return void
252
     */
253
    protected static function executeCommand(Event $event, $command, $timeout = 300)
254
    {
255
        $console = escapeshellarg(__DIR__ . '/../../app/console') . ' --env=prod';
256
257
        if ($event->getIO()->isDecorated()) {
258
            $console .= ' --ansi';
259
        }
260
261
        $process = new Process("$console $command", null, null, null, $timeout);
262
        $process->run(function ($type, $buffer) use ($event) { $event->getIO()->write($buffer, false); });
263
264
        if (!$process->isSuccessful()) {
265
            throw new \RuntimeException(sprintf('An error occurred when executing the "%s" command.', escapeshellarg($command)));
266
        }
267
    }
268
269
    /**
270
     * Get the database's configuration
271
     *
272
     * @param  bool    $testing Whether to retrieve the test database credentials
273
     * @return array|null The configuration as defined in the config.yml file, null if no configuration was found
274
     */
275 1
    public static function getDatabaseConfig($testing = false)
276
    {
277 1
        $configPath = ConfigHandler::getConfigurationPath();
278 1
        if (!is_file($configPath)) {
279
            throw new \Exception("The configuration file could not be read");
280
        }
281
282 1
        $path = $testing ? 'testing' : 'mysql';
283
284 1
        $config = Yaml::parse(file_get_contents($configPath));
285
286 1
        if (isset($config['bzion'][$path])) {
287 1
            return $config['bzion'][$path];
288
        }
289
290
        return null;
291
    }
292
}
293