Passed
Pull Request — master (#748)
by Stefano
02:53
created

Installer::createAppLocalConfig()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
eloc 5
c 1
b 1
f 0
dl 0
loc 7
rs 10
cc 2
nc 2
nop 2
1
<?php
2
declare(strict_types=1);
3
4
/**
5
 * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
6
 * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
7
 *
8
 * Licensed under The MIT License
9
 * For full copyright and license information, please see the LICENSE.txt
10
 * Redistributions of files must retain the above copyright notice.
11
 *
12
 * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
13
 * @link      https://cakephp.org CakePHP(tm) Project
14
 * @since     3.0.0
15
 * @license   https://opensource.org/licenses/mit-license.php MIT License
16
 */
17
namespace App\Console;
18
19
if (!defined('STDIN')) {
20
    define('STDIN', fopen('php://stdin', 'r'));
21
}
22
23
use Cake\Codeception\Console\Installer as CodeceptionInstaller;
0 ignored issues
show
Bug introduced by
The type Cake\Codeception\Console\Installer was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
24
use Cake\Utility\Security;
25
use Composer\Script\Event;
26
use Exception;
27
28
/**
29
 * Provides installation hooks for when this application is installed through
30
 * composer. Customize this class to suit your needs.
31
 */
32
class Installer
33
{
34
    /**
35
     * An array of directories to be made writable
36
     */
37
    public const WRITABLE_DIRS = [
38
        'logs',
39
        'tmp',
40
        'tmp/cache',
41
        'tmp/cache/models',
42
        'tmp/cache/persistent',
43
        'tmp/cache/views',
44
        'tmp/sessions',
45
        'tmp/tests',
46
    ];
47
48
    /**
49
     * Does some routine installation tasks so people don't have to.
50
     *
51
     * @param \Composer\Script\Event $event The composer event object.
52
     * @throws \Exception Exception raised by validator.
53
     * @return void
54
     */
55
    public static function postInstall(Event $event): void
56
    {
57
        $io = $event->getIO();
58
59
        $rootDir = dirname(dirname(__DIR__));
60
61
        static::createAppLocalConfig($rootDir, $io);
62
        static::createDotEnvConfig($rootDir, $io);
63
        static::createWritableDirectories($rootDir, $io);
64
65
        static::setFolderPermissions($rootDir, $io);
66
        static::setSecuritySalt($rootDir, $io);
67
68
        if (class_exists(CodeceptionInstaller::class)) {
69
            CodeceptionInstaller::customizeCodeceptionBinary($event);
70
        }
71
    }
72
73
    /**
74
     * Create config/app_local.php file if it does not exist.
75
     *
76
     * @param string $dir The application's root directory.
77
     * @param \Composer\IO\IOInterface $io IO interface to write to console.
78
     * @return void
79
     */
80
    public static function createAppLocalConfig($dir, $io): void
81
    {
82
        $appLocalConfig = $dir . '/config/app_local.php';
83
        $appLocalConfigTemplate = $dir . '/config/app_local.example.php';
84
        if (!file_exists($appLocalConfig)) {
85
            copy($appLocalConfigTemplate, $appLocalConfig);
86
            $io->write('Created `config/app_local.php` file');
87
        }
88
    }
89
90
    /**
91
     * Create the config/.env file if it does not exist.
92
     *
93
     * @param string $dir The application's root directory.
94
     * @param \Composer\IO\IOInterface $io IO interface to write to console.
95
     * @return void
96
     */
97
    public static function createDotEnvConfig($dir, $io): void
98
    {
99
        $appConfig = $dir . '/config/.env';
100
        $defaultConfig = $dir . '/config/.env.example';
101
        if (!file_exists($appConfig)) {
102
            copy($defaultConfig, $appConfig);
103
            $io->write('Created `config/.env` file');
104
        }
105
    }
106
107
    /**
108
     * Create the `logs` and `tmp` directories.
109
     *
110
     * @param string $dir The application's root directory.
111
     * @param \Composer\IO\IOInterface $io IO interface to write to console.
112
     * @return void
113
     */
114
    public static function createWritableDirectories($dir, $io): void
115
    {
116
        foreach (static::WRITABLE_DIRS as $path) {
117
            $path = $dir . '/' . $path;
118
            if (!file_exists($path)) {
119
                mkdir($path);
120
                $io->write('Created `' . $path . '` directory');
121
            }
122
        }
123
    }
124
125
    /**
126
     * Set globally writable permissions on the "tmp" and "logs" directory.
127
     *
128
     * This is not the most secure default, but it gets people up and running quickly.
129
     *
130
     * @param string $dir The application's root directory.
131
     * @param \Composer\IO\IOInterface $io IO interface to write to console.
132
     * @return void
133
     */
134
    public static function setFolderPermissions($dir, $io): void
135
    {
136
        // ask if the permissions should be changed
137
        if ($io->isInteractive()) {
138
            $validator = function ($arg) {
139
                if (in_array($arg, ['Y', 'y', 'N', 'n'])) {
140
                    return $arg;
141
                }
142
                throw new Exception('This is not a valid answer. Please choose Y or n.');
143
            };
144
            $setFolderPermissions = $io->askAndValidate(
145
                '<info>Set Folder Permissions ? (Default to Y)</info> [<comment>Y,n</comment>]? ',
146
                $validator,
147
                10,
148
                'Y'
149
            );
150
151
            if (in_array($setFolderPermissions, ['n', 'N'])) {
152
                return;
153
            }
154
        }
155
156
        // Change the permissions on a path and output the results.
157
        $changePerms = function ($path) use ($io) {
158
            $currentPerms = fileperms($path) & 0777;
159
            $worldWritable = $currentPerms | 0007;
160
            if ($worldWritable == $currentPerms) {
161
                return;
162
            }
163
164
            $res = chmod($path, $worldWritable);
165
            if ($res) {
166
                $io->write('Permissions set on ' . $path);
167
            } else {
168
                $io->write('Failed to set permissions on ' . $path);
169
            }
170
        };
171
172
        $walker = function ($dir) use (&$walker, $changePerms) {
173
            $files = array_diff(scandir($dir), ['.', '..']);
174
            foreach ($files as $file) {
175
                $path = $dir . '/' . $file;
176
177
                if (!is_dir($path)) {
178
                    continue;
179
                }
180
181
                $changePerms($path);
182
                $walker($path);
183
            }
184
        };
185
186
        $walker($dir . '/tmp');
187
        $changePerms($dir . '/tmp');
188
        $changePerms($dir . '/logs');
189
    }
190
191
    /**
192
     * Set the security.salt value in the application's config file.
193
     *
194
     * @param string $dir The application's root directory.
195
     * @param \Composer\IO\IOInterface $io IO interface to write to console.
196
     * @return void
197
     */
198
    public static function setSecuritySalt($dir, $io): void
199
    {
200
        $newKey = hash('sha256', Security::randomBytes(64));
201
        static::setSecuritySaltInFile($dir, $io, $newKey, 'app_local.php');
202
    }
203
204
    /**
205
     * Set the security.salt value in a given file
206
     *
207
     * @param string $dir The application's root directory.
208
     * @param \Composer\IO\IOInterface $io IO interface to write to console.
209
     * @param string $newKey key to set in the file
210
     * @param string $file A path to a file relative to the application's root
211
     * @return void
212
     */
213
    public static function setSecuritySaltInFile($dir, $io, $newKey, $file): void
214
    {
215
        $config = $dir . '/config/' . $file;
216
        $content = file_get_contents($config);
217
218
        $content = str_replace('__SALT__', $newKey, $content, $count);
219
220
        if ($count == 0) {
221
            $io->write('No Security.salt placeholder to replace.');
222
223
            return;
224
        }
225
226
        $result = file_put_contents($config, $content);
227
        if ($result) {
228
            $io->write('Updated Security.salt value in config/' . $file);
229
230
            return;
231
        }
232
        $io->write('Unable to update Security.salt value.');
233
    }
234
235
    /**
236
     * Set the APP_NAME value in a given file
237
     *
238
     * @param string $dir The application's root directory.
239
     * @param \Composer\IO\IOInterface $io IO interface to write to console.
240
     * @param string $appName app name to set in the file
241
     * @param string $file A path to a file relative to the application's root
242
     * @return void
243
     */
244
    public static function setAppNameInFile($dir, $io, $appName, $file): void
245
    {
246
        $config = $dir . '/config/' . $file;
247
        $content = file_get_contents($config);
248
        $content = str_replace('__APP_NAME__', $appName, $content, $count);
249
250
        if ($count == 0) {
251
            $io->write('No __APP_NAME__ placeholder to replace.');
252
253
            return;
254
        }
255
256
        $result = file_put_contents($config, $content);
257
        if ($result) {
258
            $io->write('Updated __APP_NAME__ value in config/' . $file);
259
260
            return;
261
        }
262
        $io->write('Unable to update __APP_NAME__ value.');
263
    }
264
}
265