Installer::createWritableDirectories()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 3
nop 2
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4
 * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5
 *
6
 * Licensed under The MIT License
7
 * For full copyright and license information, please see the LICENSE.txt
8
 * Redistributions of files must retain the above copyright notice.
9
 *
10
 * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11
 * @link      https://cakephp.org CakePHP(tm) Project
12
 * @since     3.0.0
13
 * @license   https://opensource.org/licenses/mit-license.php MIT License
14
 */
15
namespace App\Console;
16
17
if (! defined('STDIN')) {
18
    define('STDIN', fopen('php://stdin', 'r'));
19
}
20
21
use Cake\Utility\Security;
22
use Composer\Script\Event;
23
use Exception;
24
25
/**
26
 * Provides installation hooks for when this application is installed via
27
 * composer. Customize this class to suit your needs.
28
 */
29
class Installer
30
{
31
32
    /**
33
     * An array of directories to be made writable
34
     */
35
    const WRITABLE_DIRS = [
36
        'logs',
37
        'tmp',
38
        'tmp/cache',
39
        'tmp/cache/models',
40
        'tmp/cache/persistent',
41
        'tmp/cache/views',
42
        'tmp/sessions',
43
        'tmp/tests',
44
    ];
45
46
    /**
47
     * Does some routine installation tasks so people don't have to.
48
     *
49
     * @param \Composer\Script\Event $event The composer event object.
50
     * @throws \Exception Exception raised by validator.
51
     * @return void
52
     */
53
    public static function postInstall(Event $event)
54
    {
55
        $io = $event->getIO();
56
57
        $rootDir = dirname(dirname(__DIR__));
58
59
        static::createAppConfig($rootDir, $io);
60
        static::createWritableDirectories($rootDir, $io);
61
62
        // ask if the permissions should be changed
63
        if ($io->isInteractive()) {
64
            $validator = function ($arg) {
65
                if (in_array($arg, ['Y', 'y', 'N', 'n'])) {
66
                    return $arg;
67
                }
68
                throw new Exception('This is not a valid answer. Please choose Y or n.');
69
            };
70
            $setFolderPermissions = $io->askAndValidate(
71
                '<info>Set Folder Permissions ? (Default to Y)</info> [<comment>Y,n</comment>]? ',
72
                $validator,
73
                10,
74
                'Y'
75
            );
76
77
            if (in_array($setFolderPermissions, ['Y', 'y'])) {
78
                static::setFolderPermissions($rootDir, $io);
79
            }
80
        } else {
81
            static::setFolderPermissions($rootDir, $io);
82
        }
83
84
        static::setSecuritySalt($rootDir, $io);
85
86
        $class = 'Cake\Codeception\Console\Installer';
87
        if (class_exists($class)) {
88
            $class::customizeCodeceptionBinary($event);
89
        }
90
    }
91
92
    /**
93
     * Create the config/app.php file if it does not exist.
94
     *
95
     * @param string                   $dir The application's root directory.
96
     * @param \Composer\IO\IOInterface $io  IO interface to write to console.
97
     * @return void
98
     */
99
    public static function createAppConfig($dir, $io)
100
    {
101
        $appConfig = $dir . '/config/app.php';
102
        $defaultConfig = $dir . '/config/app.default.php';
103
        if (! file_exists($appConfig)) {
104
            copy($defaultConfig, $appConfig);
105
            $io->write('Created `config/app.php` file');
106
        }
107
    }
108
109
    /**
110
     * Create the `logs` and `tmp` directories.
111
     *
112
     * @param string                   $dir The application's root directory.
113
     * @param \Composer\IO\IOInterface $io  IO interface to write to console.
114
     * @return void
115
     */
116
    public static function createWritableDirectories($dir, $io)
117
    {
118
        foreach (static::WRITABLE_DIRS as $path) {
119
            $path = $dir . '/' . $path;
120
            if (! file_exists($path)) {
121
                mkdir($path);
122
                $io->write('Created `' . $path . '` directory');
123
            }
124
        }
125
    }
126
127
    /**
128
     * Set globally writable permissions on the "tmp" and "logs" directory.
129
     *
130
     * This is not the most secure default, but it gets people up and running quickly.
131
     *
132
     * @param string                   $dir The application's root directory.
133
     * @param \Composer\IO\IOInterface $io  IO interface to write to console.
134
     * @return void
135
     */
136
    public static function setFolderPermissions($dir, $io)
137
    {
138
        // Change the permissions on a path and output the results.
139
        $changePerms = function ($path) use ($io) {
140
            $currentPerms = fileperms($path) & 0777;
141
            $worldWritable = $currentPerms | 0007;
142
            if ($worldWritable == $currentPerms) {
143
                return;
144
            }
145
146
            $res = chmod($path, $worldWritable);
147
            if ($res) {
148
                $io->write('Permissions set on ' . $path);
149
            } else {
150
                $io->write('Failed to set permissions on ' . $path);
151
            }
152
        };
153
154
        $walker = function ($dir) use (&$walker, $changePerms) {
155
            $files = array_diff(scandir($dir), ['.', '..']);
156
            foreach ($files as $file) {
157
                $path = $dir . '/' . $file;
158
159
                if (! is_dir($path)) {
160
                    continue;
161
                }
162
163
                $changePerms($path);
164
                $walker($path);
165
            }
166
        };
167
168
        $walker($dir . '/tmp');
169
        $changePerms($dir . '/tmp');
170
        $changePerms($dir . '/logs');
171
    }
172
173
    /**
174
     * Set the security.salt value in the application's config file.
175
     *
176
     * @param string                   $dir The application's root directory.
177
     * @param \Composer\IO\IOInterface $io  IO interface to write to console.
178
     * @return void
179
     */
180
    public static function setSecuritySalt($dir, $io)
181
    {
182
        $newKey = hash('sha256', Security::randomBytes(64));
183
        static::setSecuritySaltInFile($dir, $io, $newKey, 'app.php');
184
    }
185
186
    /**
187
     * Set the security.salt value in a given file
188
     *
189
     * @param string                   $dir    The application's root directory.
190
     * @param \Composer\IO\IOInterface $io     IO interface to write to console.
191
     * @param string                   $newKey key to set in the file
192
     * @param string                   $file   A path to a file relative to the application's root
193
     * @return void
194
     */
195
    public static function setSecuritySaltInFile($dir, $io, $newKey, $file)
196
    {
197
        $config = $dir . '/config/' . $file;
198
        $content = file_get_contents($config);
199
200
        $content = str_replace('__SALT__', $newKey, $content, $count);
201
202
        if ($count == 0) {
203
            $io->write('No Security.salt placeholder to replace.');
204
205
            return;
206
        }
207
208
        $result = file_put_contents($config, $content);
209
        if ($result) {
210
            $io->write('Updated Security.salt value in config/' . $file);
211
212
            return;
213
        }
214
        $io->write('Unable to update Security.salt value.');
215
    }
216
217
    /**
218
     * Set the APP_NAME value in a given file
219
     *
220
     * @param string                   $dir     The application's root directory.
221
     * @param \Composer\IO\IOInterface $io      IO interface to write to console.
222
     * @param string                   $appName app name to set in the file
223
     * @param string                   $file    A path to a file relative to the application's root
224
     * @return void
225
     */
226
    public static function setAppNameInFile($dir, $io, $appName, $file)
227
    {
228
        $config = $dir . '/config/' . $file;
229
        $content = file_get_contents($config);
230
        $content = str_replace('__APP_NAME__', $appName, $content, $count);
231
232
        if ($count == 0) {
233
            $io->write('No __APP_NAME__ placeholder to replace.');
234
235
            return;
236
        }
237
238
        $result = file_put_contents($config, $content);
239
        if ($result) {
240
            $io->write('Updated __APP_NAME__ value in config/' . $file);
241
242
            return;
243
        }
244
        $io->write('Unable to update __APP_NAME__ value.');
245
    }
246
}
247