Issues (326)

src/Console/Installer.php (1 issue)

Labels
Severity
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
7
 * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
8
 *
9
 * Licensed under The MIT License
10
 * For full copyright and license information, please see the LICENSE.txt
11
 * Redistributions of files must retain the above copyright notice.
12
 *
13
 * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
14
 * @link      https://cakephp.org CakePHP(tm) Project
15
 * @since     3.0.0
16
 * @license   https://opensource.org/licenses/mit-license.php MIT License
17
 */
18
namespace App\Console;
19
20
if (!defined('STDIN')) {
21
    define('STDIN', fopen('php://stdin', 'r'));
22
}
23
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 via
30
 * composer. Customize this class to suit your needs.
31
 */
32
class Installer
33
{
34
35
    /**
36
     * An array of directories to be made writable
37
     */
38
    const WRITABLE_DIRS = [
39
        'logs',
40
        'tmp',
41
        'tmp/cache',
42
        'tmp/cache/models',
43
        'tmp/cache/persistent',
44
        'tmp/cache/views',
45
        'tmp/sessions',
46
        'tmp/tests',
47
    ];
48
49
    /**
50
     * Does some routine installation tasks so people don't have to.
51
     *
52
     * @param \Composer\Script\Event $event The composer event object.
53
     * @throws \Exception Exception raised by validator.
54
     * @return void
55
     */
56
    public static function postInstall(Event $event)
57
    {
58
        $io = $event->getIO();
59
60
        $rootDir = dirname(dirname(__DIR__));
61
62
        static::createAppConfig($rootDir, $io);
63
        static::createWritableDirectories($rootDir, $io);
64
65
        // ask if the permissions should be changed
66
        if ($io->isInteractive()) {
67
            $validator = function ($arg) {
68
                if (in_array($arg, ['Y', 'y', 'N', 'n'])) {
69
                    return $arg;
70
                }
71
                throw new Exception('This is not a valid answer. Please choose Y or n.');
72
            };
73
            $setFolderPermissions = $io->askAndValidate(
74
                '<info>Set Folder Permissions ? (Default to Y)</info> [<comment>Y,n</comment>]? ',
75
                $validator,
76
                10,
77
                'Y'
78
            );
79
80
            if (in_array($setFolderPermissions, ['Y', 'y'])) {
81
                static::setFolderPermissions($rootDir, $io);
82
            }
83
        } else {
84
            static::setFolderPermissions($rootDir, $io);
85
        }
86
87
        static::setSecuritySalt($rootDir, $io);
88
89
        $class = 'Cake\Codeception\Console\Installer';
90
        if (class_exists($class)) {
91
            $class::customizeCodeceptionBinary($event);
92
        }
93
    }
94
95
    /**
96
     * Create the config/app.php file if it does not exist.
97
     *
98
     * @param string $dir The application's root directory.
99
     * @param \Composer\IO\IOInterface $io IO interface to write to console.
100
     * @return void
101
     */
102
    public static function createAppConfig($dir, $io)
103
    {
104
        $appConfig = $dir . '/config/app.php';
105
        $defaultConfig = $dir . '/config/app.default.php';
106
        if (!file_exists($appConfig)) {
107
            copy($defaultConfig, $appConfig);
108
            $io->write('Created `config/app.php` file');
109
        }
110
    }
111
112
    /**
113
     * Create the `logs` and `tmp` directories.
114
     *
115
     * @param string $dir The application's root directory.
116
     * @param \Composer\IO\IOInterface $io IO interface to write to console.
117
     * @return void
118
     */
119
    public static function createWritableDirectories($dir, $io)
120
    {
121
        foreach (static::WRITABLE_DIRS as $path) {
122
            $path = $dir . '/' . $path;
123
            if (!file_exists($path)) {
124
                mkdir($path);
125
                $io->write('Created `' . $path . '` directory');
126
            }
127
        }
128
    }
129
130
    /**
131
     * Set globally writable permissions on the "tmp" and "logs" directory.
132
     *
133
     * This is not the most secure default, but it gets people up and running quickly.
134
     *
135
     * @param string $dir The application's root directory.
136
     * @param \Composer\IO\IOInterface $io IO interface to write to console.
137
     * @return void
138
     */
139
    public static function setFolderPermissions($dir, $io)
140
    {
141
        // Change the permissions on a path and output the results.
142
        $changePerms = function ($path) use ($io) {
143
            $currentPerms = fileperms($path) & 0777;
144
            $worldWritable = $currentPerms | 0007;
145
            if ($worldWritable == $currentPerms) {
146
                return;
147
            }
148
149
            $res = chmod($path, $worldWritable);
150
            if ($res) {
151
                $io->write('Permissions set on ' . $path);
152
            } else {
153
                $io->write('Failed to set permissions on ' . $path);
154
            }
155
        };
156
157
        $walker = function ($dir) use (&$walker, $changePerms) {
158
            $files = array_diff(scandir($dir), ['.', '..']);
0 ignored issues
show
It seems like scandir($dir) can also be of type false; however, parameter $array1 of array_diff() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

158
            $files = array_diff(/** @scrutinizer ignore-type */ scandir($dir), ['.', '..']);
Loading history...
159
            foreach ($files as $file) {
160
                $path = $dir . '/' . $file;
161
162
                if (!is_dir($path)) {
163
                    continue;
164
                }
165
166
                $changePerms($path);
167
                $walker($path);
168
            }
169
        };
170
171
        $walker($dir . '/tmp');
172
        $changePerms($dir . '/tmp');
173
        $changePerms($dir . '/logs');
174
    }
175
176
    /**
177
     * Set the security.salt value in the application's config file.
178
     *
179
     * @param string $dir The application's root directory.
180
     * @param \Composer\IO\IOInterface $io IO interface to write to console.
181
     * @return void
182
     */
183
    public static function setSecuritySalt($dir, $io)
184
    {
185
        $newKey = hash('sha256', Security::randomBytes(64));
186
        static::setSecuritySaltInFile($dir, $io, $newKey, 'app.php');
187
    }
188
189
    /**
190
     * Set the security.salt value in a given file
191
     *
192
     * @param string $dir The application's root directory.
193
     * @param \Composer\IO\IOInterface $io IO interface to write to console.
194
     * @param string $newKey key to set in the file
195
     * @param string $file A path to a file relative to the application's root
196
     * @return void
197
     */
198
    public static function setSecuritySaltInFile($dir, $io, $newKey, $file)
199
    {
200
        $config = $dir . '/config/' . $file;
201
        $content = file_get_contents($config);
202
203
        $content = str_replace('__SALT__', $newKey, $content, $count);
204
205
        if ($count == 0) {
206
            $io->write('No Security.salt placeholder to replace.');
207
208
            return;
209
        }
210
211
        $result = file_put_contents($config, $content);
212
        if ($result) {
213
            $io->write('Updated Security.salt value in config/' . $file);
214
215
            return;
216
        }
217
        $io->write('Unable to update Security.salt value.');
218
    }
219
220
    /**
221
     * Set the APP_NAME value in a given file
222
     *
223
     * @param string $dir The application's root directory.
224
     * @param \Composer\IO\IOInterface $io IO interface to write to console.
225
     * @param string $appName app name to set in the file
226
     * @param string $file A path to a file relative to the application's root
227
     * @return void
228
     */
229
    public static function setAppNameInFile($dir, $io, $appName, $file)
230
    {
231
        $config = $dir . '/config/' . $file;
232
        $content = file_get_contents($config);
233
        $content = str_replace('__APP_NAME__', $appName, $content, $count);
234
235
        if ($count == 0) {
236
            $io->write('No __APP_NAME__ placeholder to replace.');
237
238
            return;
239
        }
240
241
        $result = file_put_contents($config, $content);
242
        if ($result) {
243
            $io->write('Updated __APP_NAME__ value in config/' . $file);
244
245
            return;
246
        }
247
        $io->write('Unable to update __APP_NAME__ value.');
248
    }
249
}
250