├── app
├── stubs
│ ├── php
│ │ ├── ext-imagick.ini
│ │ ├── ext-redis.ini
│ │ ├── ext-memcached.ini
│ │ ├── ext-ioncube.ini
│ │ ├── z-session-memcached.ini
│ │ ├── ext-xdebug.ini
│ │ ├── ext-apcu.ini
│ │ ├── ext-xdebug-5.6.ini
│ │ ├── ext-xdebug-7.0.ini
│ │ ├── ext-xdebug-7.1.ini
│ │ ├── ext-opcache.ini
│ │ ├── z-performance.ini
│ │ └── smtp_catcher.php
│ ├── localhost
│ │ ├── no-entry.jpg
│ │ └── index.php
│ ├── httpd-vhost.conf
│ ├── httpd-vhost-ssl.conf
│ ├── config
│ │ ├── filesystems.php
│ │ └── env.php
│ ├── httpd-proxy-vhost.conf
│ ├── httpd-proxy-vhost-ssl.conf
│ ├── openssl.conf
│ ├── my.cnf
│ ├── httpd.conf
│ └── completion
│ │ └── bash
├── Facades
│ ├── Stub.php
│ ├── Cli.php
│ ├── MemcachedSession.php
│ ├── PhpHelper.php
│ ├── IonCubeHelper.php
│ ├── BrewService.php
│ ├── File.php
│ ├── Secure.php
│ ├── ApacheHelper.php
│ ├── Brew.php
│ └── PeclHelper.php
├── Commands
│ ├── Memcached
│ │ ├── FlushCommand.php
│ │ ├── RestartCommand.php
│ │ ├── InstallCommand.php
│ │ ├── StopCommand.php
│ │ ├── StartCommand.php
│ │ ├── UninstallCommand.php
│ │ └── SessionCommand.php
│ ├── Apache
│ │ ├── RestartCommand.php
│ │ ├── StopCommand.php
│ │ ├── StartCommand.php
│ │ ├── HostRevokeCommand.php
│ │ ├── HostPortCreateCommand.php
│ │ ├── InstallCommand.php
│ │ ├── HostCreateCommand.php
│ │ └── UninstallCommand.php
│ ├── MySql
│ │ ├── RestartCommand.php
│ │ ├── StopCommand.php
│ │ ├── StartCommand.php
│ │ ├── UninstallCommand.php
│ │ └── InstallCommand.php
│ ├── Redis
│ │ ├── RestartCommand.php
│ │ ├── StopCommand.php
│ │ ├── UninstallCommand.php
│ │ ├── StartCommand.php
│ │ ├── InstallCommand.php
│ │ └── FlushCommand.php
│ ├── DnsMasq
│ │ ├── RestartCommand.php
│ │ ├── StopCommand.php
│ │ ├── StartCommand.php
│ │ ├── UninstallCommand.php
│ │ └── InstallCommand.php
│ ├── MailHog
│ │ ├── RestartCommand.php
│ │ ├── StopCommand.php
│ │ ├── StartCommand.php
│ │ ├── InstallCommand.php
│ │ └── UninstallCommand.php
│ ├── RabbitMq
│ │ ├── RestartCommand.php
│ │ ├── StopCommand.php
│ │ ├── StartCommand.php
│ │ ├── VhostListCommand.php
│ │ ├── InstallCommand.php
│ │ ├── VhostDeleteCommand.php
│ │ ├── QueueListCommand.php
│ │ ├── VhostCreateCommand.php
│ │ └── UninstallCommand.php
│ ├── Services
│ │ ├── StopCommand.php
│ │ ├── StartCommand.php
│ │ └── StatusCommand.php
│ ├── Database
│ │ ├── ListCommand.php
│ │ ├── DropCommand.php
│ │ ├── CreateCommand.php
│ │ ├── InstallCommand.php
│ │ ├── ExportCommand.php
│ │ └── ImportCommand.php
│ ├── Site
│ │ ├── UnlinkCommand.php
│ │ ├── LinkProxyCommand.php
│ │ └── LinkCommand.php
│ ├── Secure
│ │ ├── InstallCommand.php
│ │ ├── RevokeCommand.php
│ │ └── GenerateCommand.php
│ ├── CompletionCommand.php
│ ├── Php
│ │ ├── SwitchCommand.php
│ │ ├── IoncubeCommand.php
│ │ ├── UninstallCommand.php
│ │ ├── XdebugCommand.php
│ │ └── InstallCommand.php
│ ├── InstallCommand.php
│ ├── ConfigDumpCommand.php
│ └── UninstallCommand.php
├── Helper
│ └── ConfigMerge.php
├── Services
│ ├── Stubs.php
│ ├── CommandLine.php
│ ├── Php.php
│ ├── MemcachedSession.php
│ ├── Files.php
│ ├── Brew.php
│ ├── IonCube.php
│ ├── BrewService.php
│ ├── Pecl.php
│ ├── Secure.php
│ └── Apache.php
├── Command.php
└── Providers
│ └── AppServiceProvider.php
├── .gitignore
├── box.json
├── config
├── env.php
├── app.php
└── commands.php
├── .github
└── ISSUE_TEMPLATE
│ └── bug_report.md
├── patches
└── command-revert.patch
├── composer.json
├── bootstrap
└── app.php
├── sage
└── README.md
/app/stubs/php/ext-imagick.ini:
--------------------------------------------------------------------------------
1 | [imagick]
2 | extension="imagick.so"
--------------------------------------------------------------------------------
/app/stubs/php/ext-redis.ini:
--------------------------------------------------------------------------------
1 | [redis]
2 | extension="redis.so"
3 |
--------------------------------------------------------------------------------
/app/stubs/php/ext-memcached.ini:
--------------------------------------------------------------------------------
1 | [memcached]
2 | extension="memcached.so"
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor
2 | /.idea
3 | /.vscode
4 | /.vagrant
5 | /builds/*
6 |
--------------------------------------------------------------------------------
/app/stubs/php/ext-ioncube.ini:
--------------------------------------------------------------------------------
1 | [ioncube]
2 | zend_extension="ioncube.so"
3 |
--------------------------------------------------------------------------------
/app/stubs/localhost/no-entry.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ytorbyk/sage/HEAD/app/stubs/localhost/no-entry.jpg
--------------------------------------------------------------------------------
/app/stubs/php/z-session-memcached.ini:
--------------------------------------------------------------------------------
1 | session.save_handler=memcached
2 | session.save_path=127.0.0.1:11211
3 |
--------------------------------------------------------------------------------
/app/stubs/php/ext-xdebug.ini:
--------------------------------------------------------------------------------
1 | [xdebug]
2 | zend_extension="xdebug.so"
3 | xdebug.mode=debug
4 | #xdebug.start_with_request=yes
5 |
--------------------------------------------------------------------------------
/app/stubs/php/ext-apcu.ini:
--------------------------------------------------------------------------------
1 | [apcu]
2 | extension="apcu.so"
3 | apc.enabled=1
4 | apc.shm_size=64M
5 | apc.ttl=7200
6 | apc.enable_cli=1
7 |
--------------------------------------------------------------------------------
/app/stubs/php/ext-xdebug-5.6.ini:
--------------------------------------------------------------------------------
1 | [xdebug]
2 | zend_extension="xdebug.so"
3 | xdebug.remote_enable=1
4 | xdebug.remote_host=localhost
5 | xdebug.remote_handler=dbgp
6 | xdebug.remote_port=9000
7 | xdebug.remote_autostart=0
8 |
--------------------------------------------------------------------------------
/app/stubs/php/ext-xdebug-7.0.ini:
--------------------------------------------------------------------------------
1 | [xdebug]
2 | zend_extension="xdebug.so"
3 | xdebug.remote_enable=1
4 | xdebug.remote_host=localhost
5 | xdebug.remote_handler=dbgp
6 | xdebug.remote_port=9000
7 | xdebug.remote_autostart=0
8 |
--------------------------------------------------------------------------------
/app/stubs/php/ext-xdebug-7.1.ini:
--------------------------------------------------------------------------------
1 | [xdebug]
2 | zend_extension="xdebug.so"
3 | xdebug.remote_enable=1
4 | xdebug.remote_host=localhost
5 | xdebug.remote_handler=dbgp
6 | xdebug.remote_port=9000
7 | xdebug.remote_autostart=0
8 |
--------------------------------------------------------------------------------
/app/stubs/httpd-vhost.conf:
--------------------------------------------------------------------------------
1 |
2 | DocumentRoot "DOCUMENT_ROOT"
3 |
4 | ServerName DOMAIN
5 | SERVER_ALIAS
6 |
7 | ErrorLog "LOGS_PATH/DOMAIN-error.log"
8 | CustomLog "LOGS_PATH/DOMAIN-access.log" common
9 |
10 | VIRTUAL_HOST_SSL
11 |
--------------------------------------------------------------------------------
/app/stubs/httpd-vhost-ssl.conf:
--------------------------------------------------------------------------------
1 |
2 |
3 | DocumentRoot "DOCUMENT_ROOT"
4 |
5 | ServerName DOMAIN
6 | SERVER_ALIAS
7 |
8 | ErrorLog "LOGS_PATH/DOMAIN-ssl-error.log"
9 | CustomLog "LOGS_PATH/DOMAIN-ssl-access.log" common
10 |
11 | SSLEngine on
12 | SSLCertificateFile "CERTIFICATE_CRT"
13 | SSLCertificateKeyFile "CERTIFICATE_KEY"
14 |
15 |
--------------------------------------------------------------------------------
/app/stubs/localhost/index.php:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |

14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/stubs/config/filesystems.php:
--------------------------------------------------------------------------------
1 | 'dump',
5 | 'disks' => [
6 | 'dump' => [
7 | 'driver' => 'local',
8 | 'root' => config('env.db.dump_path'),
9 | 'disable_asserts' => true
10 | ],
11 | 'm2_configs' => [
12 | 'driver' => 'local',
13 | 'root' => config('env.m2.configs_path')
14 | ]
15 | ]
16 | ];
17 |
--------------------------------------------------------------------------------
/app/stubs/httpd-proxy-vhost.conf:
--------------------------------------------------------------------------------
1 |
2 | ServerName DOMAIN
3 | SERVER_ALIAS
4 |
5 |
6 | ProxyPreserveHost On
7 | ProxyPass http://localhost:PORT/
8 | ProxyPassReverse http://localhost:PORT/
9 |
10 |
11 | ErrorLog "LOGS_PATH/DOMAIN-error.log"
12 | CustomLog "LOGS_PATH/DOMAIN-access.log" common
13 |
14 | VIRTUAL_HOST_SSL
15 |
--------------------------------------------------------------------------------
/box.json:
--------------------------------------------------------------------------------
1 | {
2 | "chmod": "0755",
3 | "directories": [
4 | "app",
5 | "bootstrap",
6 | "config",
7 | "patches",
8 | "vendor"
9 | ],
10 | "files": [
11 | "composer.json"
12 | ],
13 | "exclude-composer-files": false,
14 | "compression": "GZ",
15 | "shebang": "#!/usr/bin/php",
16 | "compactors": [
17 | "Herrera\\Box\\Compactor\\Php",
18 | "Herrera\\Box\\Compactor\\Json"
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/app/stubs/httpd-proxy-vhost-ssl.conf:
--------------------------------------------------------------------------------
1 |
2 |
3 | ServerName DOMAIN
4 | SERVER_ALIAS
5 |
6 |
7 | ProxyPreserveHost On
8 | ProxyPass http://localhost:PORT/
9 | ProxyPassReverse http://localhost:PORT/
10 |
11 |
12 | ErrorLog "LOGS_PATH/DOMAIN-ssl-error.log"
13 | CustomLog "LOGS_PATH/DOMAIN-ssl-access.log" common
14 |
15 | SSLEngine on
16 | SSLCertificateFile "CERTIFICATE_CRT"
17 | SSLCertificateKeyFile "CERTIFICATE_KEY"
18 |
19 |
--------------------------------------------------------------------------------
/app/Facades/Stub.php:
--------------------------------------------------------------------------------
1 | env('HOME') . DIRECTORY_SEPARATOR . $homeFolder,
9 | 'home_public' => env('HOME') . DIRECTORY_SEPARATOR .'x' . $app['name'],
10 | 'config_path' => env('HOME') . DIRECTORY_SEPARATOR . $homeFolder . DIRECTORY_SEPARATOR . 'config.php',
11 | ];
12 |
13 | if (file_exists($env['config_path'])) {
14 | $localConfig = include $env['config_path'];
15 | if (!empty($localConfig) && is_array($localConfig)) {
16 | return $env + $localConfig;
17 | }
18 | }
19 | return $env;
20 |
--------------------------------------------------------------------------------
/app/Facades/MemcachedSession.php:
--------------------------------------------------------------------------------
1 | mergeRecursiveConfigFrom(require $path, $key);
17 | }
18 |
19 | /**
20 | * @param array $config
21 | * @param string $key
22 | * @return void
23 | */
24 | protected function mergeRecursiveConfigFrom(array $config, $key)
25 | {
26 | config([$key => array_replace_recursive($config, (array)config($key))]);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/Commands/Apache/RestartCommand.php:
--------------------------------------------------------------------------------
1 | info('Apache Restart:');
29 | $this->call(StopCommand::COMMAND);
30 | $this->call(StartCommand::COMMAND);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/patches/command-revert.patch:
--------------------------------------------------------------------------------
1 |
2 | @package laravel-zero/framework
3 |
4 | Index: src/Commands/Command.php
5 | IDEA additional info:
6 | Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
7 | <+>UTF-8
8 | ===================================================================
9 | --- src/Commands/Command.php (date 1546196279000)
10 | +++ src/Commands/Command.php (date 1546196279000)
11 | @@ -51,7 +51,7 @@
12 | * Performs the given task, outputs and
13 | * returns the result.
14 | */
15 | - public function task(string $title = '', $task = null): bool
16 | + public function task(string $title = '', $task = null)
17 | {
18 | return $this->__call('task', func_get_args());
19 | }
20 |
--------------------------------------------------------------------------------
/app/Commands/MySql/RestartCommand.php:
--------------------------------------------------------------------------------
1 | info('MySQL Restart:');
29 |
30 | $this->call(StopCommand::COMMAND);
31 | $this->call(StartCommand::COMMAND);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/Commands/Redis/RestartCommand.php:
--------------------------------------------------------------------------------
1 | info('Redis restart:');
29 |
30 | $this->call(StopCommand::COMMAND);
31 | $this->call(StartCommand::COMMAND);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/Facades/IonCubeHelper.php:
--------------------------------------------------------------------------------
1 | info('DnsMasq restart:');
29 |
30 | $this->call(StopCommand::COMMAND);
31 | $this->call(StartCommand::COMMAND);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/Facades/BrewService.php:
--------------------------------------------------------------------------------
1 | info('MailHog restart:');
29 |
30 | $this->call(StopCommand::COMMAND);
31 | $this->call(StartCommand::COMMAND);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/Commands/RabbitMq/RestartCommand.php:
--------------------------------------------------------------------------------
1 | info('RabbitMq restart:');
29 |
30 | $this->call(StopCommand::COMMAND);
31 | $this->call(StartCommand::COMMAND);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/Commands/Memcached/RestartCommand.php:
--------------------------------------------------------------------------------
1 | info('Memcached restart:');
29 |
30 | $this->call(StopCommand::COMMAND);
31 | $this->call(StartCommand::COMMAND);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/Facades/File.php:
--------------------------------------------------------------------------------
1 | info('Stop services:');
31 | foreach ($services as $service) {
32 | $this->call($service . ':stop');
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/Commands/Services/StartCommand.php:
--------------------------------------------------------------------------------
1 | info('Start services:');
31 | foreach ($services as $service) {
32 | $this->call($service . ':start');
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/Commands/Database/ListCommand.php:
--------------------------------------------------------------------------------
1 | argument('query');
31 | $query = $query ? '%' . $query . '%' : '';
32 | Cli::passthru('mysqlshow ' . $query);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/Commands/Database/DropCommand.php:
--------------------------------------------------------------------------------
1 | argument('name');
30 |
31 | $this->task(sprintf('Drop DB %s if exists', $name), function () use ($name) {
32 | Cli::run(sprintf("mysql -e 'DROP DATABASE IF EXISTS `%s`'", $name));
33 | });
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/Commands/MySql/StopCommand.php:
--------------------------------------------------------------------------------
1 | task('MySQL Stop', function () {
30 | try {
31 | BrewService::stop((string)config('env.mysql.formula'));
32 | } catch (\Exception $e) {
33 | return $e->getMessage();
34 | }
35 | });
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/Commands/Redis/StopCommand.php:
--------------------------------------------------------------------------------
1 | task('Redis Stop', function () {
30 | try {
31 | BrewService::stop((string)config('env.redis.formula'));
32 | } catch (\Exception $e) {
33 | return $e->getMessage();
34 | }
35 | });
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/Commands/Redis/UninstallCommand.php:
--------------------------------------------------------------------------------
1 | info('Uninstall Redis:');
30 |
31 | if (Brew::isInstalled((string)config('env.redis.formula'))) {
32 | $this->call(StopCommand::COMMAND);
33 | $this->uninstallFormula((string)config('env.redis.formula'));
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/Commands/Site/UnlinkCommand.php:
--------------------------------------------------------------------------------
1 | info('Unlink website:');
32 |
33 | $this->call(HostRevokeCommand::COMMAND, ['domain' => $this->argument('domain')]);
34 | $this->call(RestartCommand::COMMAND);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/Commands/Apache/StopCommand.php:
--------------------------------------------------------------------------------
1 | task('Apache Stop', function () {
30 | try {
31 | BrewService::stop((string)config('env.apache.formula'));
32 | } catch (\Exception $e) {
33 | return $e->getMessage();
34 | }
35 | });
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/Commands/DnsMasq/StopCommand.php:
--------------------------------------------------------------------------------
1 | task('DnsMasq Stop', function () {
30 | try {
31 | BrewService::stop((string)config('env.dns.formula'));
32 | } catch (\Exception $e) {
33 | return $e->getMessage();
34 | }
35 | });
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/Commands/MySql/StartCommand.php:
--------------------------------------------------------------------------------
1 | task('MySQL Start', function () {
30 | try {
31 | BrewService::start((string)config('env.mysql.formula'));
32 | } catch (\Exception $e) {
33 | return $e->getMessage();
34 | }
35 | });
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/Commands/Redis/StartCommand.php:
--------------------------------------------------------------------------------
1 | task('Redis Start', function () {
30 | try {
31 | BrewService::start((string)config('env.redis.formula'));
32 | } catch (\Exception $e) {
33 | return $e->getMessage();
34 | }
35 | });
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/Commands/Memcached/InstallCommand.php:
--------------------------------------------------------------------------------
1 | info('Install Memcached:');
30 |
31 | $this->installFormula((string)config('env.memcached.formula'));
32 |
33 | foreach (config('env.memcached.dependencies') as $formula) {
34 | Brew::ensureInstalled($formula);
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/Commands/DnsMasq/StartCommand.php:
--------------------------------------------------------------------------------
1 | task('DnsMasq Start', function () {
30 | try {
31 | BrewService::start((string)config('env.dns.formula'), true);
32 | } catch (\Exception $e) {
33 | return $e->getMessage();
34 | }
35 | });
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/Commands/MailHog/StopCommand.php:
--------------------------------------------------------------------------------
1 | task('MailHog Stop', function () {
30 | try {
31 | BrewService::stop((string)config('env.mailhog.formula'));
32 | } catch (\Exception $e) {
33 | return $e->getMessage();
34 | }
35 | });
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/Commands/Redis/InstallCommand.php:
--------------------------------------------------------------------------------
1 | info('Install Redis:');
29 |
30 | $needInstall = $this->installFormula((string)config('env.redis.formula'));
31 |
32 | if ($needInstall) {
33 | $this->call(StartCommand::COMMAND);
34 | } else {
35 | $this->call(RestartCommand::COMMAND);
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/Commands/Apache/StartCommand.php:
--------------------------------------------------------------------------------
1 | task('Apache Start', function () {
30 | try {
31 | BrewService::start((string)config('env.apache.formula'), true);
32 | } catch (\Exception $e) {
33 | return $e->getMessage();
34 | }
35 | });
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/Commands/MailHog/StartCommand.php:
--------------------------------------------------------------------------------
1 | task('MailHog Start', function () {
30 | try {
31 | BrewService::start((string)config('env.mailhog.formula'));
32 | } catch (\Exception $e) {
33 | return $e->getMessage();
34 | }
35 | });
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/Commands/RabbitMq/StopCommand.php:
--------------------------------------------------------------------------------
1 | task('RabbitMq Stop', function () {
30 | try {
31 | BrewService::stop((string)config('env.rabbitmq.formula'));
32 | } catch (\Exception $e) {
33 | return $e->getMessage();
34 | }
35 | });
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/Commands/Memcached/StopCommand.php:
--------------------------------------------------------------------------------
1 | task('Memcached Stop', function () {
30 | try {
31 | BrewService::stop((string)config('env.memcached.formula'));
32 | } catch (\Exception $e) {
33 | return $e->getMessage();
34 | }
35 | });
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/Commands/RabbitMq/StartCommand.php:
--------------------------------------------------------------------------------
1 | task('RabbitMq Start', function () {
30 | try {
31 | BrewService::start((string)config('env.rabbitmq.formula'));
32 | } catch (\Exception $e) {
33 | return $e->getMessage();
34 | }
35 | });
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/Commands/Redis/FlushCommand.php:
--------------------------------------------------------------------------------
1 | argument('db');
31 |
32 | if (empty($db) && $db !== '0') {
33 | Cli::passthru('redis-cli FLUSHALL');
34 | } else {
35 | Cli::passthru(sprintf('redis-cli -n %d FLUSHDB', (int)$db));
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/Commands/Memcached/StartCommand.php:
--------------------------------------------------------------------------------
1 | task('Memcached Start', function () {
30 | try {
31 | BrewService::start((string)config('env.memcached.formula'));
32 | } catch (\Exception $e) {
33 | return $e->getMessage();
34 | }
35 | });
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/Facades/ApacheHelper.php:
--------------------------------------------------------------------------------
1 | info('Install secure stuff:');
30 |
31 | $this->installFormula((string)config('env.secure.formula'));
32 |
33 | $this->task('Ensure certificate directory created', function () {
34 | File::ensureDirExists((string)config('env.secure.certificates_path'));
35 | });
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/stubs/my.cnf:
--------------------------------------------------------------------------------
1 | [client]
2 | user=root
3 | password=MYSQL_PASSWORD
4 |
5 | [mysqld]
6 | sql_mode="NO_AUTO_VALUE_ON_ZERO"
7 | bind-address = 127.0.0.1
8 | log-error=LOGS_PATH/mysql.log
9 |
10 | collation_server=utf8_general_ci
11 | character_set_server=utf8
12 | init-connect='SET NAMES utf8'
13 |
14 | max_allowed_packet = 1073741824
15 | innodb_strict_mode = off
16 | query_cache_limit = 64M
17 | query_cache_size = 512M
18 | query_cache_type = 1
19 | table_open_cache = 100
20 | wait_timeout = 28800
21 | tmp_table_size = 1G
22 | max_heap_table_size = 1G
23 | innodb_buffer_pool_size = 1500M
24 | innodb_log_buffer_size = 128M
25 | innodb_lock_wait_timeout= 200
26 | log-queries-not-using-indexes
27 | long_query_time = 2
28 | skip-external-locking
29 |
30 | #innodb_force_recovery = 1
31 |
32 | [mysqldump]
33 | quick
34 | quote-names
35 | max_allowed_packet = 1073741824
36 |
--------------------------------------------------------------------------------
/app/stubs/php/ext-opcache.ini:
--------------------------------------------------------------------------------
1 | opcache.enable = 1
2 | opcache.enable_cli = 1
3 | opcache.memory_consumption = 2048
4 | opcache.interned_strings_buffer = 20
5 | opcache.file_cache=1
6 | opcache.max_accelerated_files = 80000
7 | opcache.max_wasted_percentage = 5
8 | opcache.use_cwd = 1
9 | opcache.validate_timestamps = 1
10 | opcache.revalidate_freq = 0
11 | opcache.file_update_protection = 2
12 | opcache.revalidate_path = 0
13 | opcache.save_comments = 1
14 | opcache.load_comments = 1
15 | opcache.fast_shutdown = 1
16 | opcache.enable_file_override = 0
17 | opcache.optimization_level = 0xffffffff
18 | opcache.inherited_hack = 1
19 | opcache.blacklist_filename = ""
20 | opcache.max_file_size = 0
21 | opcache.consistency_checks = 0
22 | opcache.force_restart_timeout = 180
23 | opcache.error_log = ""
24 | opcache.log_verbosity_level = 1
25 | opcache.preferred_memory_model = ""
26 | opcache.protect_memory = 0
27 | apc.cache_by_default = false
28 |
--------------------------------------------------------------------------------
/app/Commands/Apache/HostRevokeCommand.php:
--------------------------------------------------------------------------------
1 | argument('domain');
32 |
33 | $this->call(RevokeCommand::COMMAND, ['domain' => $domain]);
34 |
35 | $this->task('Delete Apache Virtual Host', function () use ($domain) {
36 | ApacheHelper::deleteVHost($domain);
37 | });
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/Commands/RabbitMq/VhostListCommand.php:
--------------------------------------------------------------------------------
1 | getOutput()->write(Cli::run('rabbitmqadmin list vhosts name'));
31 | } catch (\Symfony\Component\Process\Exception\ProcessFailedException $e) {
32 | $this->error(trim($e->getProcess()->getErrorOutput()));
33 | } catch (\Exception $e) {
34 | $this->error($e->getMessage());
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/Commands/Database/CreateCommand.php:
--------------------------------------------------------------------------------
1 | argument('name');
30 |
31 | if ($this->option('force')) {
32 | $this->call(DropCommand::COMMAND, ['name' => $name]);
33 | }
34 |
35 | $this->task(sprintf('Create DB %s if not exists', $name), function () use ($name) {
36 | Cli::run(sprintf("mysql -e 'CREATE DATABASE IF NOT EXISTS `%s`'", $name));
37 | });
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/Commands/Memcached/UninstallCommand.php:
--------------------------------------------------------------------------------
1 | info('Uninstall Memcached:');
30 |
31 | foreach (config('env.memcached.dependencies') as $formula) {
32 | Brew::ensureUninstalled($formula);
33 | }
34 |
35 | if (Brew::isInstalled((string)config('env.memcached.formula'))) {
36 | $this->call(StopCommand::COMMAND);
37 | $this->uninstallFormula((string)config('env.memcached.formula'));
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/Services/Stubs.php:
--------------------------------------------------------------------------------
1 | getPath($name));
29 | }
30 |
31 | /**
32 | * @param string $name
33 | * @param array $vars
34 | * @return string
35 | */
36 | public function get(string $name, array $vars = []): string
37 | {
38 | $content = File::get($this->getPath($name));
39 |
40 | foreach ($vars as $name => $value) {
41 | $content = str_replace($name, $value, $content);
42 | }
43 |
44 | return $content;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/app/stubs/php/z-performance.ini:
--------------------------------------------------------------------------------
1 | ; Add TIMEZONE string for replacing with machine timezone
2 | date.timezone = "TIMEZONE"
3 |
4 | ; Max execution time per request
5 | max_execution_time = 18000
6 |
7 | ; Max memory per instance
8 | memory_limit = 4G
9 |
10 | ; The maximum size of an uploaded file.
11 | upload_max_filesize = 128M
12 |
13 | ; Sets max size of post data allowed. This setting also affects file upload. To upload large files, this value must be larger than upload_max_filesize
14 | post_max_size = 128M
15 |
16 | ; Sets max input vars. Sometimes Magento sends too many variables, for instance saving big bundle product
17 | max_input_vars = 6000
18 |
19 | session.auto_start = off
20 | session.gc_probability = 0
21 | suhosin.session.cryptua = off
22 |
23 | ; Disable garbage collector
24 | zend.enable_gc = off
25 |
26 | ; Transparent output compression using the zlib library
27 | zlib.output_compression = On
28 |
29 | ; Use Smtp for sending emails
30 | sendmail_path = SMTP_CATCHER_PATH
31 |
32 | phar.readonly = Off
33 |
34 | error_log = ERROR_LOG_FILE
35 |
36 | sys_temp_dir = '/private/var/tmp/'
37 |
--------------------------------------------------------------------------------
/app/Commands/Secure/RevokeCommand.php:
--------------------------------------------------------------------------------
1 | argument('domain');
31 |
32 | $this->task('Delete SSL certificate', function () use ($domain) {
33 | if (Secure::hasPredefined($domain)) {
34 | return 'SSL certificate is predefined. Skipping...';
35 | }
36 |
37 | if (!Secure::canGenerate($domain)) {
38 | return 'The domain cannot be secured. Skipping...';
39 | }
40 |
41 | Secure::delete($domain);
42 | });
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/Facades/Brew.php:
--------------------------------------------------------------------------------
1 | info('Install MailHog:');
31 |
32 | $needInstall = $this->installFormula((string)config('env.mailhog.formula'));
33 |
34 | Secure::generate((string)config('env.mailhog.domain'));
35 | ApacheHelper::configureProxyVHost((string)config('env.mailhog.domain'), '8025');
36 |
37 | if ($needInstall) {
38 | $this->call(StartCommand::COMMAND);
39 | } else {
40 | $this->call(RestartCommand::COMMAND);
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/Commands/RabbitMq/InstallCommand.php:
--------------------------------------------------------------------------------
1 | info('Install RabbitMq:');
31 |
32 | $needInstall = $this->installFormula((string)config('env.rabbitmq.formula'));
33 |
34 | Secure::generate((string)config('env.rabbitmq.domain'));
35 | ApacheHelper::configureProxyVHost((string)config('env.rabbitmq.domain'), '15672');
36 |
37 | if ($needInstall) {
38 | $this->call(StartCommand::COMMAND);
39 | } else {
40 | $this->call(RestartCommand::COMMAND);
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/Commands/MySql/UninstallCommand.php:
--------------------------------------------------------------------------------
1 | info('Uninstall MySQL:');
31 |
32 | $this->uninstallFormula((string)config('env.mysql.formula'));
33 |
34 | $this->task('Delete configuration', function () {
35 | File::delete(config('env.mysql.brew_config_path'));
36 | });
37 |
38 | if ($this->option('force')) {
39 | $this->task('Delete Data', function () {
40 | File::deleteDirectory((string)config('env.mysql.data_dir_path'));
41 | });
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "yurii/sage",
3 | "description": "CLI helper for local development",
4 | "keywords": ["brew", "console", "cli", "mamp", "sage"],
5 | "license": "MIT",
6 | "authors": [
7 | {
8 | "name": "Yurii Torbyk",
9 | "email": "mail@yurii.me"
10 | }
11 | ],
12 | "require": {
13 | "php": "^7.2",
14 | "laravel-zero/framework": "^6.4",
15 | "nunomaduro/laravel-console-menu": "^2.3",
16 | "symfony/yaml": "^5.3",
17 | "vaimo/composer-patches": "^4.22"
18 | },
19 | "autoload": {
20 | "psr-4": {
21 | "App\\": "app/"
22 | }
23 | },
24 | "config": {
25 | "preferred-install": "dist",
26 | "sort-packages": true,
27 | "optimize-autoloader": true,
28 | "platform": {
29 | "ext-posix": "0"
30 | },
31 | "allow-plugins": {
32 | "vaimo/composer-patches": true
33 | }
34 | },
35 | "minimum-stability": "dev",
36 | "prefer-stable": true,
37 | "bin": ["sage"],
38 | "extra": {
39 | "patches-search": "patches"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/Commands/Site/LinkProxyCommand.php:
--------------------------------------------------------------------------------
1 | info('Link proxy website:');
33 |
34 | $this->call(HostPortCreateCommand::COMMAND, [
35 | 'port' => $this->argument('port'),
36 | 'domain' => $this->argument('domain'),
37 | 'aliases' => $this->argument('aliases'),
38 | '--not-secure' => $this->option('not-secure')
39 | ]);
40 |
41 | $this->call(RestartCommand::COMMAND);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/Commands/RabbitMq/VhostDeleteCommand.php:
--------------------------------------------------------------------------------
1 | argument('name');
31 |
32 | $this->task(sprintf('Delete RabbitMQ VHost %s', $name), static function () use ($name) {
33 | try {
34 | Cli::run(sprintf('rabbitmqadmin delete vhost name=%s', $name));
35 | } catch (\Symfony\Component\Process\Exception\ProcessFailedException $e) {
36 | return trim($e->getProcess()->getErrorOutput());
37 | } catch (\Exception $e) {
38 | return $e->getMessage();
39 | }
40 | });
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/Facades/PeclHelper.php:
--------------------------------------------------------------------------------
1 | info('Link website:');
34 |
35 | $this->call(HostCreateCommand::COMMAND, [
36 | 'domain' => $this->argument('domain'),
37 | 'aliases' => $this->argument('aliases'),
38 | '--path' => $this->option('path'),
39 | '--not-secure' => $this->option('not-secure')
40 | ]);
41 |
42 | $this->call(RestartCommand::COMMAND);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/Commands/Services/StatusCommand.php:
--------------------------------------------------------------------------------
1 | info('Services:');
35 | foreach ($services as $service) {
36 | $isRunning = $servicesStatus[(string)config(sprintf('env.%s.formula', $service))] ?? false;
37 | $status = $isRunning ? $this->successText('running') : $this->errorText('stopped');
38 |
39 | $this->comment(sprintf("%-{$maxLength}s %s", $service . ':', $status));
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/Commands/RabbitMq/QueueListCommand.php:
--------------------------------------------------------------------------------
1 | argument('vhost');
31 | $columns = $vhostName ? 'name' : 'vhost name';
32 | $vhostFilter = $vhostName ? '-V ' . $vhostName : '';
33 |
34 | try {
35 | $this->getOutput()->write(Cli::run(sprintf('rabbitmqadmin list queues %s %s', $columns, $vhostFilter)));
36 | } catch (\Symfony\Component\Process\Exception\ProcessFailedException $e) {
37 | $this->error(trim($e->getProcess()->getErrorOutput()));
38 | } catch (\Exception $e) {
39 | $this->error($e->getMessage());
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/Commands/Secure/GenerateCommand.php:
--------------------------------------------------------------------------------
1 | argument('domain');
31 | $aliases = $this->argument('aliases');
32 |
33 | $this->task('Generate SSL certificate', function () use ($domain, $aliases) {
34 | if (!Secure::canSecure($domain)) {
35 | return $this->errorText('The domain cannot be secured');
36 | }
37 |
38 | if (Secure::canGenerate($domain)) {
39 | Secure::delete($domain);
40 | Secure::generate($domain, $aliases);
41 | return true;
42 | }
43 |
44 | return 'SSL certificate is predefined. Skipping...';
45 | });
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/Commands/MailHog/UninstallCommand.php:
--------------------------------------------------------------------------------
1 | info('Uninstall MailHog:');
32 |
33 | if (Brew::isInstalled((string)config('env.mailhog.formula'))) {
34 | $this->call(StopCommand::COMMAND);
35 | $this->uninstallFormula((string)config('env.mailhog.formula'));
36 | }
37 |
38 | ApacheHelper::deleteVHost((string)config('env.mailhog.domain'));
39 |
40 | $this->task('Delete MailHog Data', function () {
41 | $this->deleteData();
42 | });
43 | }
44 |
45 | /**
46 | * @return void
47 | */
48 | private function deleteData()
49 | {
50 | File::deleteDirectory((string)config('env.mailhog.log_path'));
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/Commands/RabbitMq/VhostCreateCommand.php:
--------------------------------------------------------------------------------
1 | argument('name');
32 |
33 | if ($this->option('force')) {
34 | $this->call(VhostDeleteCommand::COMMAND, ['name' => $name]);
35 | }
36 |
37 | $this->task(sprintf('Create RabbitMQ VHost %s', $name), static function () use ($name) {
38 | try {
39 | Cli::run(sprintf('rabbitmqadmin declare vhost name=%s', $name));
40 | } catch (\Symfony\Component\Process\Exception\ProcessFailedException $e) {
41 | return trim($e->getProcess()->getErrorOutput());
42 | } catch (\Exception $e) {
43 | return $e->getMessage();
44 | }
45 | });
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/Commands/DnsMasq/UninstallCommand.php:
--------------------------------------------------------------------------------
1 | info('Uninstall DnsMasq:');
32 |
33 | if (Brew::isInstalled((string)config('env.dns.formula'))) {
34 | $this->call(StopCommand::COMMAND);
35 | $this->uninstallFormula((string)config('env.dns.formula'));
36 | }
37 |
38 | $this->task('Delete DnsMasq config', function () {
39 | $this->deleteConfig();
40 | });
41 | }
42 |
43 | /**
44 | * @return void
45 | */
46 | private function deleteConfig()
47 | {
48 | Cli::run(sprintf('sudo rm -rf %s', config('env.dns.resolver_path')));
49 | File::delete(config('env.dns.brew_config_path'));
50 | File::deleteDirectory((string)config('env.dns.brew_config_dir_path'));
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/Commands/Apache/HostPortCreateCommand.php:
--------------------------------------------------------------------------------
1 | argument('domain');
34 | $aliases = $this->argument('aliases');
35 | $secure = !$this->option('not-secure');
36 |
37 | if (Secure::canSecure($domain) && $secure) {
38 | $this->call(GenerateCommand::COMMAND, ['domain' => $domain, 'aliases' => $aliases]);
39 | }
40 |
41 | $this->task('Create Apache Virtual Host-Proxy', function () use ($secure) {
42 | ApacheHelper::configureProxyVHost(
43 | $this->argument('domain'),
44 | $this->argument('port'),
45 | $this->argument('aliases'),
46 | $secure
47 | );
48 | });
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/Commands/RabbitMq/UninstallCommand.php:
--------------------------------------------------------------------------------
1 | info('Uninstall RabbitMq:');
32 |
33 | if (Brew::isInstalled((string)config('env.rabbitmq.formula'))) {
34 | $this->call(StopCommand::COMMAND);
35 | $this->uninstallFormula((string)config('env.rabbitmq.formula'));
36 | }
37 |
38 | ApacheHelper::deleteVHost((string)config('env.rabbitmq.domain'));
39 |
40 | $this->task('Delete RabbitMq Data', function () {
41 | $this->deleteData();
42 | });
43 | }
44 |
45 | /**
46 | * @return void
47 | */
48 | private function deleteData()
49 | {
50 | File::deleteDirectory((string)config('env.rabbitmq.brew_config_dir_path'));
51 | File::deleteDirectory((string)config('env.rabbitmq.brew_lib_dir_path'));
52 | File::deleteDirectory((string)config('env.rabbitmq.log_dir_path'));
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/Services/CommandLine.php:
--------------------------------------------------------------------------------
1 | runCommand($command);
21 |
22 | if (!$process->isSuccessful()) {
23 | throw new ProcessFailedException($process);
24 | }
25 |
26 | return $process->getOutput();
27 | }
28 |
29 | /**
30 | * @param string $command
31 | * @return string
32 | */
33 | public function runQuietly(string $command): string
34 | {
35 | $process = $this->runCommand($command);
36 | return $process->isSuccessful() ? $process->getOutput() : '';
37 | }
38 |
39 | /**
40 | * @param string $command
41 | * @return Process
42 | */
43 | private function runCommand(string $command): Process
44 | {
45 | $process = new Process($command, null, null, null, 5400.);
46 | $process->run();
47 | return $process;
48 | }
49 |
50 | /**
51 | * Pass the command to the command line and display the output.
52 | *
53 | * @param string $command
54 | * @return void
55 | */
56 | public function passthru(string $command): void
57 | {
58 | passthru($command);
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/app/Commands/Database/InstallCommand.php:
--------------------------------------------------------------------------------
1 | info('Install DB dump stuff:');
30 |
31 | $this->installFormula((string)config('env.progress.formula'));
32 |
33 | $this->task('Ensure DB dumps directory created', function () {
34 | File::ensureDirExists((string)config('env.db.dump_path'));
35 | });
36 |
37 | $this->task('Setup Locale setting for Bash', function () {
38 |
39 | $bashrcPath = (string)config('env.completion.bashrc_path');
40 | $bashProfilePath = (string)config('env.completion.bash_profile_path');
41 |
42 | if ((!File::exists($bashrcPath) || strpos(File::get($bashrcPath), 'LC_ALL') === false)
43 | && (!File::exists($bashProfilePath) || strpos(File::get($bashProfilePath), 'LC_ALL') === false)
44 | ) {
45 | File::append(
46 | $bashProfilePath,
47 | PHP_EOL . 'export LC_ALL=en_US.UTF-8' . PHP_EOL . 'export LANG=en_US.UTF-8' . PHP_EOL
48 | );
49 | }
50 | });
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/stubs/httpd.conf:
--------------------------------------------------------------------------------
1 | Listen 80
2 | ServerName localhost
3 |
4 | User CURRENT_USER
5 | Group staff
6 |
7 | LoadModule rewrite_module lib/httpd/modules/mod_rewrite.so
8 | LoadModule vhost_alias_module lib/httpd/modules/mod_vhost_alias.so
9 | LoadModule socache_shmcb_module lib/httpd/modules/mod_socache_shmcb.so
10 | LoadModule ssl_module lib/httpd/modules/mod_ssl.so
11 |
12 | LoadModule proxy_module lib/httpd/modules/mod_proxy.so
13 | LoadModule proxy_http_module lib/httpd/modules/mod_proxy_http.so
14 |
15 | PHP_MODULE_LOADER
16 |
17 |
18 |
19 | DirectoryIndex index.php index.html
20 |
21 |
22 |
23 | SetHandler application/x-httpd-php
24 |
25 |
26 |
27 | AllowOverride All
28 | Require all granted
29 |
30 |
31 | ErrorLog "LOGS_PATH/apache-error.log"
32 |
33 | LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
34 | LogFormat "%h %l %u %t \"%r\" %>s %b" common
35 |
36 |
37 | LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
38 |
39 |
40 | CustomLog "LOGS_PATH/apache-access.log" common
41 |
42 |
43 | Listen 443
44 | SSLCipherSuite HIGH:MEDIUM:!MD5:!RC4
45 | SSLProxyCipherSuite HIGH:MEDIUM:!MD5:!RC4
46 | SSLHonorCipherOrder on
47 | SSLProtocol all -SSLv3
48 | SSLProxyProtocol all -SSLv3
49 | SSLPassPhraseDialog builtin
50 | SSLSessionCache "shmcb:/usr/local/var/run/apache2/ssl_scache(512000)"
51 | SSLSessionCacheTimeout 300
52 |
53 | Include VHOSTS_PATH/00-default.conf
54 | Include VHOSTS_PATH/*.conf
55 |
--------------------------------------------------------------------------------
/app/Commands/Apache/InstallCommand.php:
--------------------------------------------------------------------------------
1 | info('Install Apache:');
34 |
35 | $this->task('Ensure Apache is not running', function () {
36 | Cli::runQuietly('sudo apachectl stop');
37 | Cli::runQuietly('sudo launchctl unload -w /System/Library/LaunchDaemons/org.apache.httpd.plist');
38 |
39 | if (Brew::isInstalled((string)config('env.apache.formula'))) {
40 | BrewService::stop((string)config('env.apache.formula'));
41 | }
42 | });
43 |
44 | $this->installFormula((string)config('env.apache.formula'));
45 |
46 | $this->task('Configure Apache', function () {
47 | ApacheHelper::configure();
48 | File::ensureDirExists('/usr/local/var/log/httpd');
49 | });
50 |
51 | $this->task('Create default Virtual Host (localhost)', function () {
52 | ApacheHelper::initDefaultLocalhostVHost();
53 | });
54 |
55 | $this->call(StartCommand::COMMAND);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/bootstrap/app.php:
--------------------------------------------------------------------------------
1 | singleton(
30 | Illuminate\Contracts\Console\Kernel::class,
31 | LaravelZero\Framework\Kernel::class
32 | );
33 |
34 | $app->singleton(
35 | Illuminate\Contracts\Debug\ExceptionHandler::class,
36 | Illuminate\Foundation\Exceptions\Handler::class
37 | );
38 |
39 | /*
40 | |--------------------------------------------------------------------------
41 | | Return The Application
42 | |--------------------------------------------------------------------------
43 | |
44 | | This script returns the application instance. The instance is given to
45 | | the calling script so we can separate the building of the instances
46 | | from the actual running of the application and sending responses.
47 | |
48 | */
49 |
50 | return $app;
51 |
--------------------------------------------------------------------------------
/app/Commands/Apache/HostCreateCommand.php:
--------------------------------------------------------------------------------
1 | argument('domain');
35 | $aliases = $this->argument('aliases');
36 | $secure = !$this->option('not-secure');
37 | $path = $this->option('path');
38 |
39 | $hostPath = $this->getCurrentPath($path);
40 | if (!$this->verifyPath($hostPath, false)) {
41 | $this->error('Passed path does not exist or not a folder: ' . $hostPath);
42 | return;
43 | }
44 |
45 | if (Secure::canSecure($domain) && $secure) {
46 | $this->call(GenerateCommand::COMMAND, ['domain' => $domain, 'aliases' => $aliases]);
47 | }
48 |
49 | $this->task('Create Apache Virtual Host', function () use ($hostPath, $secure) {
50 | ApacheHelper::configureVHost(
51 | $this->argument('domain'),
52 | $hostPath,
53 | $this->argument('aliases'),
54 | $secure
55 | );
56 | });
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/app/Services/Php.php:
--------------------------------------------------------------------------------
1 | getLinkedPhp();
19 | if ($currentVersion) {
20 | Brew::unlink($this->getFormula($currentVersion));
21 | }
22 |
23 | Brew::link($this->getFormula($version));
24 | }
25 |
26 | /**
27 | * @return null|string
28 | */
29 | public function getLinkedPhp(): ?string
30 | {
31 | if (!File::isLink((string)config('env.php.brew_path'))) {
32 | return null;
33 | }
34 |
35 | $resolvedPath = File::readLink((string)config('env.php.brew_path'));
36 |
37 | foreach (config('env.php.versions') as $phpVersion) {
38 | if (strpos($resolvedPath, $phpVersion) !== false) {
39 | return $phpVersion;
40 | }
41 | }
42 |
43 | throw new \DomainException('Unable to determine linked PHP.');
44 | }
45 |
46 | /**
47 | * @param string $version
48 | * @return void
49 | */
50 | public function link(string $version): void
51 | {
52 | Brew::link($this->getFormula($version));
53 | }
54 |
55 | /**
56 | * @param string $version
57 | * @return void
58 | */
59 | public function unlink(string $version): void
60 | {
61 | Brew::unlink($this->getFormula($version));
62 | }
63 |
64 | /**
65 | * @param string $version
66 | * @return string
67 | */
68 | public function getFormula(string $version): string
69 | {
70 | return config('env.php.main_version') === $version ? 'php' : 'php@' . $version;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/app/Services/MemcachedSession.php:
--------------------------------------------------------------------------------
1 | getConfigIniFilePath());
19 | }
20 |
21 | /**
22 | * @return bool
23 | */
24 | public function isInstalled(): bool
25 | {
26 | return PeclHelper::isInstalled(Pecl::MEMCACHED_EXTENSION);
27 | }
28 |
29 | /**
30 | * @return void
31 | */
32 | public function enable(): void
33 | {
34 | if (!File::exists($this->getConfigIniFilePath(true))) {
35 | throw new \RuntimeException('Memcached session config file is not found.');
36 | }
37 | File::move($this->getConfigIniFilePath(true), $this->getConfigIniFilePath());
38 | }
39 |
40 | /**
41 | * @return void
42 | */
43 | public function disable(): void
44 | {
45 | if (!File::exists($this->getConfigIniFilePath())) {
46 | throw new \RuntimeException('Memcached session config file is not found.');
47 | }
48 | File::move($this->getConfigIniFilePath(), $this->getConfigIniFilePath(true));
49 | }
50 |
51 | /**
52 | * @return void
53 | */
54 | public function configure(): void
55 | {
56 | File::put(PeclHelper::getConfDPath() . 'z-session-memcached.ini', Stub::get('php/z-session-memcached.ini'));
57 | }
58 |
59 | /**
60 | * @param bool $disabled
61 | * @return string
62 | */
63 | private function getConfigIniFilePath(bool $disabled = false): string
64 | {
65 | return PeclHelper::getConfDPath() . 'z-session-memcached.ini' . ($disabled ? '.disabled' : '');
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/app/stubs/php/smtp_catcher.php:
--------------------------------------------------------------------------------
1 | #!/usr/bin/php
2 |
60 |
--------------------------------------------------------------------------------
/app/Commands/Apache/UninstallCommand.php:
--------------------------------------------------------------------------------
1 | info('Uninstall Apache:');
34 |
35 | $this->task('Ensure Apache is not running', function () {
36 | Cli::runQuietly('sudo apachectl stop');
37 | Cli::runQuietly('sudo launchctl unload -w /System/Library/LaunchDaemons/org.apache.httpd.plist');
38 |
39 | if (Brew::isInstalled((string)config('env.apache.formula'))) {
40 | try {
41 | BrewService::stop((string)config('env.apache.formula'));
42 | } catch (\Exception $e) {
43 | return $e->getMessage();
44 | }
45 | }
46 | });
47 |
48 | $this->uninstallFormula((string)config('env.apache.formula'));
49 |
50 | $this->task('Delete Apache configuration', function () {
51 | File::delete((string)config('env.apache.config'));
52 | File::deleteDirectory((string)config('env.apache.localhost_path'));
53 | File::deleteDirectory((string)config('env.apache.brew_config_dir_path'));
54 | });
55 |
56 | if ($this->option('force')) {
57 | File::deleteDirectory((string)config('env.apache.vhosts'));
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/sage:
--------------------------------------------------------------------------------
1 | #!/usr/bin/php
2 | make(Illuminate\Contracts\Console\Kernel::class);
36 |
37 | $status = $kernel->handle(
38 | $input = new Symfony\Component\Console\Input\ArgvInput,
39 | new Symfony\Component\Console\Output\ConsoleOutput
40 | );
41 |
42 | /*
43 | |--------------------------------------------------------------------------
44 | | Shutdown The Application
45 | |--------------------------------------------------------------------------
46 | |
47 | | Once Artisan has finished running, we will fire off the shutdown events
48 | | so that any final work may be done by the application before we shut
49 | | down the process. This is the last thing to happen to the request.
50 | |
51 | */
52 |
53 | $kernel->terminate($input, $status);
54 |
55 | exit($status);
56 |
--------------------------------------------------------------------------------
/config/app.php:
--------------------------------------------------------------------------------
1 | 'Sage',
16 |
17 | /*
18 | |--------------------------------------------------------------------------
19 | | Application Version
20 | |--------------------------------------------------------------------------
21 | |
22 | | This value determines the "version" your application is currently running
23 | | in. You may want to follow the "Semantic Versioning" - Given a version
24 | | number MAJOR.MINOR.PATCH when an update happens: https://semver.org.
25 | |
26 | */
27 | 'version' => '0.15.0',
28 |
29 | /*
30 | |--------------------------------------------------------------------------
31 | | Application Environment
32 | |--------------------------------------------------------------------------
33 | |
34 | | This value determines the "environment" your application is currently
35 | | running in. This may determine how you prefer to configure various
36 | | services your application utilizes. Should be true in production.
37 | |
38 | */
39 | 'production' => true,
40 |
41 | /*
42 | |--------------------------------------------------------------------------
43 | | Autoloaded Service Providers
44 | |--------------------------------------------------------------------------
45 | |
46 | | The service providers listed here will be automatically loaded on the
47 | | request to your application. Feel free to add your own services to
48 | | this array to grant expanded functionality to your applications.
49 | |
50 | */
51 | 'providers' => [
52 | App\Providers\AppServiceProvider::class,
53 | ],
54 |
55 | ];
56 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | ## Sage
3 |
4 | Sage is helper for development environment on macOS (High Sierra, Mojave, Catalina and Big Sur on intel).
5 |
6 |
7 | ## Installation
8 |
9 | 1. Since Sage depends on Brew. Install or update [Homebrew](https://brew.sh/) to the latest version using brew update.
10 | ```bash
11 | # if not installed
12 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
13 |
14 | # add the next line into your ~/.bash_profile file (create if not exists)
15 |
16 | export PATH="$PATH:/usr/local/sbin:$HOME/bin:$HOME/.composer/vendor/bin"
17 |
18 |
19 | # if installed
20 |
21 | brew update
22 | brew upgrade
23 | ```
24 | 2. Since there is no php by default in macOS it hsould be istalled manually via brew.
25 | ```bash
26 | brew install shivammathur/php/php@7.3
27 | ```
28 |
29 | 3. Download phar package from [the latest release](https://github.com/ytorbyk/sage/releases/latest) and put it in `$HOME/bin` folder.
30 | ```bash
31 | curl -L https://github.com/ytorbyk/sage/releases/latest/download/sage.phar > $HOME/bin/bin-sage
32 | chmod +x $HOME/bin/bin-sage
33 | ```
34 | 4. Create $HOME/bin/sage txt file with the next contend
35 | ```bash
36 | #!/usr/bin/env bash
37 | /usr/local/opt/php@7.3/bin/php "$HOME/bin/bin-sage" "$@"
38 | ```
39 | 5. Make the txt file executable
40 | ```bash
41 | chown +x $HOME/bin/sage
42 | ```
43 |
44 | 6. [Optional step] Customize configuration
45 | ```bash
46 | # It creates configuration dump ~/xSage/config.php.
47 | # You can customize and move it to ~/.sage/config.php before next step if you want.
48 |
49 | sage env:config-dump
50 | ```
51 |
52 | 7. Install and configure required environments
53 | ```bash
54 | # It's automatic, you will prompt to enter your password once and two times MySQL root password.
55 | # If you don't have installed MySQL before, just press enter (there is no password by default).
56 | # After installation MySQL root password is 1 (until you changed it in ~/.sage/config.php config in node mysql.password)
57 |
58 | sage env:install
59 | ```
60 |
61 | 8. [Optional step] Install Bash completion for the application
62 | ```bash
63 | sage env:completion
64 | ```
65 |
66 | 9. Ready to use
67 | ```bash
68 | # Displays a list of supported commands with short descriptions
69 |
70 | sage list
71 | ```
72 |
--------------------------------------------------------------------------------
/app/Services/Files.php:
--------------------------------------------------------------------------------
1 | isDirectory($path)) {
19 | $this->makeDirectory($path, $mode, $recursive, $force);
20 | }
21 | }
22 |
23 | /**
24 | * Determine if the given path is a symbolic link.
25 | *
26 | * @param string $path
27 | * @return bool
28 | */
29 | public function isLink(string $path): bool
30 | {
31 | return is_link($path);
32 | }
33 |
34 | /**
35 | * Resolve the given symbolic link.
36 | *
37 | * @param string $path
38 | * @return string|false
39 | */
40 | public function readLink(string $path)
41 | {
42 | return readlink($path);
43 | }
44 |
45 | /**
46 | * @param float $size
47 | * @return string
48 | */
49 | public function getFormatedFileSize(float $size): string
50 | {
51 | $arBytes = [
52 | [
53 | 'tag' => 'GB',
54 | 'value' => pow(1024, 3)
55 | ],
56 | [
57 | 'tag' => 'MB',
58 | 'value' => pow(1024, 2)
59 | ],
60 | [
61 | 'tag' => 'KB',
62 | 'value' => 1024
63 | ],
64 | [
65 | 'tag' => 'B',
66 | 'value' => 1
67 | ]
68 | ];
69 |
70 | $result = '0';
71 | foreach ($arBytes as $arItem) {
72 | if ($size >= $arItem['value']) {
73 | $result = $size / $arItem['value'];
74 | $result = str_replace('.', ',' , strval(round($result, 2))) . ' ' . $arItem['tag'];
75 | break;
76 | }
77 | }
78 | return $result;
79 | }
80 |
81 | /**
82 | * Read a file as a stream.
83 | *
84 | * @param string $filePath
85 | * @return resource|false
86 | */
87 | public function readStream(string $filePath)
88 | {
89 | return fopen($filePath, 'rb');
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/app/Commands/CompletionCommand.php:
--------------------------------------------------------------------------------
1 | argument('shell');
33 | switch ($shell) {
34 | case 'bash':
35 | $this->setupForBash();
36 | break;
37 | default:
38 | $this->error(sprintf('Wrong shell, currently supported: bash'));
39 | return;
40 | }
41 |
42 | $this->output->success('Restart all open terminal windows and completion is ready to use.');
43 | }
44 |
45 | /**
46 | * @return void
47 | */
48 | private function setupForBash(): void
49 | {
50 | $this->task(sprintf('Ensure [%s] installed', config('env.completion.formula')), function () {
51 | Brew::ensureInstalled((string)config('env.completion.formula'));
52 | });
53 |
54 | $this->task('Copy completion script', function () {
55 | File::copy(
56 | Stub::getPath('completion/bash'),
57 | (string)config('env.completion.brew_config_completion_path')
58 | );
59 | });
60 |
61 | $this->task('Include Brew completion to Bash', function () {
62 |
63 | $sourceText = (string)config('env.completion.brew_completion');
64 | $bashrcPath = (string)config('env.completion.bashrc_path');
65 | $bashProfilePath = (string)config('env.completion.bash_profile_path');
66 |
67 | if ((!File::exists($bashrcPath) || strpos(File::get($bashrcPath), $sourceText) === false)
68 | && (!File::exists($bashProfilePath) || strpos(File::get($bashProfilePath), $sourceText) === false)
69 | ) {
70 | File::append($bashProfilePath, PHP_EOL . $sourceText . PHP_EOL);
71 | }
72 | });
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/app/Commands/Php/SwitchCommand.php:
--------------------------------------------------------------------------------
1 | argument('version') ?: $this->getVersion();
37 |
38 | if (!$phpVersion) {
39 | return;
40 | }
41 |
42 | if (!Brew::isInstalled(PhpHelper::getFormula($phpVersion))) {
43 | $this->warn("PHP {$phpVersion} is not installed.");
44 | return;
45 | }
46 |
47 | $currentVersion = PhpHelper::getLinkedPhp();
48 | $supportedVersions = config('env.php.versions');
49 |
50 | if (!in_array($phpVersion, $supportedVersions, true)) {
51 | $this->warn("PHP {$phpVersion} is not available. The following versions are supported: " . implode(' ', $supportedVersions));
52 | }
53 |
54 | if ($phpVersion === $currentVersion) {
55 | $this->info("{$phpVersion} version is current. Skipping...");
56 | return;
57 | }
58 |
59 | $this->info('Enable PHP v' . $phpVersion . ':');
60 |
61 | $this->task('Relink php', function () use ($phpVersion) {
62 | PhpHelper::switchTo($phpVersion);
63 | });
64 |
65 |
66 | if (File::isFile((string)config('env.apache.config'))) {
67 | $this->task('Update apache config', function () use ($phpVersion) {
68 | ApacheHelper::linkPhp($phpVersion);
69 | });
70 | }
71 |
72 | if (!$this->option('skip')
73 | &&
74 | (!config('env.php.skip_apache_restart') || $this->option('restart'))
75 | ) {
76 | $this->call(RestartCommand::COMMAND);
77 | }
78 | }
79 |
80 | /**
81 | * @return string|false
82 | */
83 | private function getVersion()
84 | {
85 | return $this->menu('Switch PHP', array_combine(config('env.php.versions'), config('env.php.versions')));
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/app/Commands/InstallCommand.php:
--------------------------------------------------------------------------------
1 | info('Installing and configuring local environment:');
41 |
42 | $brewInstalled = $this->task('Check if Brew installed', function () {
43 | return Brew::isBrewAvailable();
44 | });
45 | if (!$brewInstalled) {
46 | $this->info('Brew is not installed, it is required. Run the next command:');
47 | $this->comment('/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"');
48 | return;
49 | }
50 |
51 | File::ensureDirExists((string)config('env.home_public'));
52 | File::ensureDirExists((string)config('env.logs_path'));
53 |
54 | foreach (config('env.software') as $formula) {
55 | $this->installFormula($formula);
56 | }
57 |
58 | $this->call(DnsMasqInstall::COMMAND);
59 | $this->call(MySqlInstall::COMMAND);
60 | $this->call(DatabaseInstall::COMMAND);
61 | $this->call(ApacheInstall::COMMAND);
62 | $this->call(SecureInstall::COMMAND);
63 | $this->call(PhpInstall::COMMAND);
64 |
65 | $this->call(SwitchCommand::COMMAND, ['version' => '8.1']);
66 |
67 | $this->call(RedisInstall::COMMAND);
68 | $this->call(MailHogInstall::COMMAND);
69 | $this->call(RabbitMqInstall::COMMAND);
70 |
71 | $this->call(CompletionInstall::COMMAND);
72 |
73 | $this->output->success(sprintf('%s is successfully installed and ready to use!', config('app.name')));
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/app/Commands/MySql/InstallCommand.php:
--------------------------------------------------------------------------------
1 | info('Install MySQL:');
33 |
34 | $needInstall = $this->installFormula((string)config('env.mysql.formula'));
35 |
36 | $this->task(sprintf('Link [%s] formula', config('env.mysql.formula')), function () {
37 | Brew::link((string)config('env.mysql.formula'));
38 | });
39 |
40 | $this->task('Configure MySQL', function () {
41 | $this->configureMySQL();
42 | });
43 |
44 | if ($needInstall === true) {
45 | $this->call(RestartCommand::COMMAND);
46 | $this->updateSecureSettings();
47 | }
48 |
49 | $this->call(RestartCommand::COMMAND);
50 | }
51 |
52 | /**
53 | * @return void
54 | */
55 | private function configureMySQL(): void
56 | {
57 | File::chmod((string)config('env.mysql.data_dir_path'), 0777);
58 | $mysqlConfig = Stub::get(
59 | 'my.cnf',
60 | [
61 | 'MYSQL_PASSWORD' => (string)config('env.mysql.password'),
62 | 'LOGS_PATH' => (string)config('env.logs_path')
63 | ]
64 | );
65 | File::put((string)config('env.mysql.brew_config_path'), $mysqlConfig);
66 | }
67 |
68 | /**
69 | * @return void
70 | */
71 | private function updateSecureSettings(): void
72 | {
73 | $mysqlPasswordMessage = 'Enter previously installed MySQL root password. If was not installed any, just press enter (empty password)!';
74 | $this->error($mysqlPasswordMessage);
75 | $this->task(sprintf('Update MySQL Password to "%s"', config('env.mysql.password')), function () {
76 | Cli::passthru(sprintf('mysql -u root -p -e "UPDATE mysql.user SET authentication_string=PASSWORD(\'%s\') WHERE User=\'root\'"', config('env.mysql.password')));
77 | });
78 |
79 | $this->error($mysqlPasswordMessage);
80 | $this->task('Flush MySQL privileges', function () {
81 | Cli::passthru('mysql -u root -p -e "FLUSH PRIVILEGES;"');
82 | });
83 |
84 | $this->task('Delete anonymous users', function () {
85 | Cli::passthru('mysql -e "DELETE FROM mysql.user WHERE User=\'\';"');
86 | });
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/app/Commands/Php/IoncubeCommand.php:
--------------------------------------------------------------------------------
1 | argument('action');
38 |
39 | if (!$action) {
40 | $action = $this->getAction();
41 | } elseif (!in_array($action, $this->allowedActions, true)) {
42 | $this->warn('Wrong action. Allowed actions: ' . implode('|', $this->allowedActions));
43 | return;
44 | }
45 |
46 | $shouldApacheRestart = false;
47 |
48 | if ($action === 'on') {
49 | $shouldApacheRestart = $this->enable();
50 | }
51 |
52 | if ($action === 'off') {
53 | $shouldApacheRestart = $this->disable();
54 | }
55 |
56 |
57 | if (!$this->option('skip') && $shouldApacheRestart) {
58 | $this->call(RestartCommand::COMMAND);
59 | }
60 | }
61 |
62 | /**
63 | * @return bool
64 | */
65 | private function enable(): bool
66 | {
67 | if (!IonCubeHelper::isInstalled()) {
68 | $this->warn('IonCube is not installed');
69 | return false;
70 | }
71 |
72 | if (IonCubeHelper::isEnabled()) {
73 | $this->warn('IonCube is already enabled');
74 | return false;
75 | }
76 |
77 | $this->task('IonCube enable', function () {
78 | IonCubeHelper::enable();
79 | });
80 | return true;
81 | }
82 |
83 | /**
84 | * @return bool
85 | */
86 | private function disable(): bool
87 | {
88 | if (!IonCubeHelper::isEnabled()) {
89 | $this->warn('IonCube is already disabled');
90 | return false;
91 | }
92 |
93 | $this->task('IonCube disable', function () {
94 | IonCubeHelper::disable();
95 | });
96 | return true;
97 | }
98 |
99 | /**
100 | * @return null|string
101 | */
102 | private function getAction(): ?string
103 | {
104 | if (IonCubeHelper::isEnabled()) {
105 | $action = 'off';
106 | $confirm = 'IonCube is enabled, do you want to disable?';
107 | } else {
108 | $action = 'on';
109 | $confirm = 'IonCube is disabled, do you want to enable?';
110 | }
111 |
112 | return $this->confirm($confirm, true) ? $action : null;
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/app/Commands/ConfigDumpCommand.php:
--------------------------------------------------------------------------------
1 | task('Ensure public home folder exists', function () {
37 | File::ensureDirExists((string)config('env.home_public'));
38 | });
39 |
40 | $this->writeConfig((bool)$this->option('skip-custom'));
41 |
42 | $this->comment('Config dump: ' . $this->getConfigDumpPath());
43 | $this->info('The config will not be used.');
44 | $this->info('It could be customized and moved to: ' . config('env.config_path'));
45 | $this->info('There should be changed values only, they will be merged with default values.');
46 | }
47 |
48 | /**
49 | * @return string
50 | */
51 | private function getConfigDumpPath(): string
52 | {
53 | return config('env.home_public') . DIRECTORY_SEPARATOR . 'config.php';
54 | }
55 |
56 | /**
57 | * @param bool $skipCustom
58 | * @return void
59 | */
60 | private function writeConfig(bool $skipCustom = false): void
61 | {
62 | $config = $skipCustom ? $this->loadDefaultConfig() : config('env');
63 |
64 | $this->task('Generate config', function () use ($config) {
65 | $content = $this->varExportShort($config, 1);
66 | File::put(
67 | $this->getConfigDumpPath(), ' $value) {
94 | $expanded[] = str_repeat(self::INDENT, $depth)
95 | . ($indexed ? '' : $this->varExportShort($key) . ' => ')
96 | . $this->varExportShort($value, $depth + 1);
97 | }
98 |
99 | return sprintf("[\n%s\n%s]", implode(",\n", $expanded), str_repeat(self::INDENT, $depth - 1));
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/app/Commands/DnsMasq/InstallCommand.php:
--------------------------------------------------------------------------------
1 | info('Install DnsMasq:');
31 |
32 | $needInstall = $this->installFormula((string)config('env.dns.formula'));
33 |
34 | $this->task('Configure DnsMasq', function () {
35 | $this->configureDnsMasq();
36 | });
37 |
38 | if ($needInstall) {
39 | $this->call(StartCommand::COMMAND);
40 | } else {
41 | $this->call(RestartCommand::COMMAND);
42 | }
43 | }
44 |
45 | /**
46 | * @return void
47 | */
48 | private function configureDnsMasq(): void
49 | {
50 | File::ensureDirExists((string)config('env.home'));
51 | $this->createCustomConfigFile();
52 | $domains = (array)config('env.dns.domains');
53 | $this->setConfig($domains);
54 | $this->createDomainResolvers($domains);
55 | }
56 |
57 | /**
58 | * @return void
59 | */
60 | private function createCustomConfigFile()
61 | {
62 | $customConfigPath = config('env.dns.config_path');
63 | if (!$this->customConfigIsImported($customConfigPath)) {
64 | File::put(
65 | (string)config('env.dns.brew_config_path'),
66 | 'conf-file=' . $customConfigPath . PHP_EOL
67 | );
68 | }
69 | }
70 |
71 | /**
72 | * @param string $customConfigPath
73 | * @return bool
74 | */
75 | private function customConfigIsImported($customConfigPath)
76 | {
77 | return strpos(File::get((string)config('env.dns.brew_config_path')), $customConfigPath) !== false;
78 | }
79 |
80 | /**
81 | * @param string[] $domains
82 | * @return void
83 | */
84 | private function setConfig($domains)
85 | {
86 | $content = '';
87 | foreach ($domains as $domain) {
88 | $content .= 'address=/.' . $domain . '/127.0.0.1' . PHP_EOL;
89 | }
90 |
91 | File::put((string)config('env.dns.config_path'), $content);
92 | }
93 |
94 | /**
95 | * @param string[] $domains
96 | * @return void
97 | */
98 | private function createDomainResolvers($domains)
99 | {
100 | Cli::run(sprintf('sudo rm -rf %s', config('env.dns.resolver_path')));
101 | Cli::run(sprintf('sudo mkdir %s', config('env.dns.resolver_path')));
102 |
103 | foreach ($domains as $domain) {
104 | Cli::run(sprintf('sudo bash -c "echo \'nameserver 127.0.0.1\' > %s/%s"', config('env.dns.resolver_path'), $domain));
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/app/Commands/Memcached/SessionCommand.php:
--------------------------------------------------------------------------------
1 | argument('action');
38 |
39 | if (!$action) {
40 | $action = $this->getAction();
41 | } elseif (!in_array($action, $this->allowedActions, true)) {
42 | $this->warn('Wrong action. Allowed actions: ' . implode('|', $this->allowedActions));
43 | return;
44 | }
45 |
46 | $shouldApacheRestart = false;
47 |
48 | if ($action === 'on') {
49 | $shouldApacheRestart = $this->enable();
50 | }
51 |
52 | if ($action === 'off') {
53 | $shouldApacheRestart = $this->disable();
54 | }
55 |
56 |
57 | if (!$this->option('skip') && $shouldApacheRestart) {
58 | $this->call(RestartCommand::COMMAND);
59 | }
60 | }
61 |
62 | /**
63 | * @return bool
64 | */
65 | private function enable(): bool
66 | {
67 | if (!MemcachedSession::isInstalled()) {
68 | $this->warn('Memcached is not installed');
69 | return false;
70 | }
71 |
72 | if (MemcachedSession::isEnabled()) {
73 | $this->warn('Memcached as session storage is already enabled');
74 | return false;
75 | }
76 |
77 | $this->task('Memcached as session storage enable', function () {
78 | MemcachedSession::enable();
79 | });
80 | return true;
81 | }
82 |
83 | /**
84 | * @return bool
85 | */
86 | private function disable(): bool
87 | {
88 | if (!MemcachedSession::isEnabled()) {
89 | $this->warn('Memcached as session storage is already disabled');
90 | return false;
91 | }
92 |
93 | $this->task('Memcached as session storage disable', function () {
94 | MemcachedSession::disable();
95 | });
96 | return true;
97 | }
98 |
99 | /**
100 | * @return null|string
101 | */
102 | private function getAction(): ?string
103 | {
104 | if (MemcachedSession::isEnabled()) {
105 | $action = 'off';
106 | $confirm = 'Memcached as session storage is enabled, do you want to disable?';
107 | } else {
108 | $action = 'on';
109 | $confirm = 'Memcached as session storage is disabled, do you want to enable?';
110 | }
111 |
112 | return $this->confirm($confirm, true) ? $action : null;
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/config/commands.php:
--------------------------------------------------------------------------------
1 | NunoMaduro\LaravelConsoleSummary\SummaryCommand::class,
16 |
17 | /*
18 | |--------------------------------------------------------------------------
19 | | Commands Paths
20 | |--------------------------------------------------------------------------
21 | |
22 | | This value determines the "paths" that should be loaded by the console's
23 | | kernel. Foreach "path" present on the array provided below the kernel
24 | | will extract all "Illuminate\Console\Command" based class commands.
25 | |
26 | */
27 | 'paths' => [app_path('Commands')],
28 |
29 | /*
30 | |--------------------------------------------------------------------------
31 | | Added Commands
32 | |--------------------------------------------------------------------------
33 | |
34 | | You may want to include a single command class without having to load an
35 | | entire folder. Here you can specify which commands should be added to
36 | | your list of commands. The console's kernel will try to load them.
37 | |
38 | */
39 | 'add' => [
40 | // ..
41 | ],
42 |
43 | /*
44 | |--------------------------------------------------------------------------
45 | | Hidden Commands
46 | |--------------------------------------------------------------------------
47 | |
48 | | Your application commands will always be visible on the application list
49 | | of commands. But you can still make them "hidden" specifying an array
50 | | of commands below. All "hidden" commands can still be run/executed.
51 | |
52 | */
53 | 'hidden' => [
54 | NunoMaduro\LaravelConsoleSummary\SummaryCommand::class,
55 | Symfony\Component\Console\Command\HelpCommand::class,
56 | App\Commands\Apache\InstallCommand::class,
57 | App\Commands\Apache\UninstallCommand::class,
58 | App\Commands\Database\InstallCommand::class,
59 | App\Commands\DnsMasq\InstallCommand::class,
60 | App\Commands\DnsMasq\UninstallCommand::class,
61 | App\Commands\MySql\InstallCommand::class,
62 | App\Commands\MySql\UninstallCommand::class,
63 | App\Commands\Php\InstallCommand::class,
64 | App\Commands\Php\UninstallCommand::class,
65 | App\Commands\Secure\InstallCommand::class,
66 | ],
67 |
68 | /*
69 | |--------------------------------------------------------------------------
70 | | Removed Commands
71 | |--------------------------------------------------------------------------
72 | |
73 | | Do you have a service provider that loads a list of commands that
74 | | you don't need? No problem. Laravel Zero allows you to specify
75 | | below a list of commands that you don't to see in your app.
76 | |
77 | */
78 | 'remove' => [
79 | Illuminate\Console\Scheduling\ScheduleRunCommand::class,
80 | Illuminate\Console\Scheduling\ScheduleFinishCommand::class,
81 | Illuminate\Foundation\Console\VendorPublishCommand::class,
82 | ],
83 |
84 | ];
85 |
--------------------------------------------------------------------------------
/app/Commands/UninstallCommand.php:
--------------------------------------------------------------------------------
1 | info('Uninstalling local environment:');
37 |
38 | $brewInstalled = $this->task('Check if Brew installed', function () {
39 | return Brew::isBrewAvailable();
40 | });
41 | if (!$brewInstalled) {
42 | $this->info('Brew is not installed. Nothing to uninstall!');
43 | }
44 |
45 | foreach (config('env.software') as $formula) {
46 | $this->uninstallFormula($formula);
47 | }
48 |
49 | $this->call(DnsMasqUninstall::COMMAND);
50 | $this->call(MySqlUninstall::COMMAND, ['--force' => $this->option('force')]);
51 | $this->call(ApacheUninstall::COMMAND, ['--force' => $this->option('force')]);
52 | $this->call(PhpUninstall::COMMAND);
53 | $this->call(MemcachedUninstall::COMMAND);
54 | $this->call(RedisUninstall::COMMAND);
55 | $this->call(MailHogUninstall::COMMAND);
56 | $this->call(RabbitMqUninstall::COMMAND);
57 |
58 | if ($this->option('force')) {
59 | File::deleteDirectory((string)config('env.home'));
60 | $this->uninstallCompletion();
61 | }
62 |
63 | $this->output->success(sprintf('%s is successfully uninstalled :(', config('app.name')));
64 | }
65 |
66 | /**
67 | * @return void
68 | */
69 | private function uninstallCompletion(): void
70 | {
71 | $this->info('Uninstall completion:');
72 |
73 | $this->task(sprintf('Uninstall [%s]', config('env.completion.formula')), function () {
74 | Brew::ensureUninstalled((string)config('env.completion.formula'));
75 | });
76 |
77 | $this->task('Delete completion script', function () {
78 | File::delete(config('env.completion.brew_config_completion_path'));
79 | });
80 |
81 | $this->task('Remove include Brew completion from Bash', function () {
82 | $sourceText = (string)config('env.completion.brew_completion');
83 |
84 | $bashrcPath = (string)config('env.completion.bashrc_path');
85 | if (File::exists($bashrcPath)) {
86 | File::put($bashrcPath, str_replace($sourceText, '', File::get($bashrcPath)));
87 | }
88 |
89 | $bashProfilePath = (string)config('env.completion.bash_profile_path');
90 | if (File::exists($bashProfilePath)) {
91 | File::put($bashProfilePath, str_replace($sourceText, '', File::get($bashProfilePath)));
92 | }
93 | });
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/app/Commands/Database/ExportCommand.php:
--------------------------------------------------------------------------------
1 | argument('name') ?: $this->getDbName();
34 | if (!$dbName) {
35 | return;
36 | }
37 |
38 | $file = $this->argument('file') ?: $this->getDumpName($dbName);
39 |
40 | $dumpPath = $this->getDumpPath($this->updateDumpExtension($file));
41 | $packCommand = $this->getPackCommand($dumpPath);
42 |
43 | $filter = "sed -e 's/DEFINER[ ]*=[ ]*[^*]*\*/\*/' "
44 | . " | sed -e 's/DEFINER[ ]*=[ ]*[^*]*PROCEDURE/PROCEDURE/'"
45 | . " | sed -e 's/DEFINER[ ]*=[ ]*[^*]*FUNCTION/FUNCTION/'"
46 | . " | sed -e 's/ROW_FORMAT=FIXED//g'";
47 |
48 | Cli::passthru("mysqldump {$dbName} --routines=true"
49 | . ' | pv -b -t -w 80 -N Export '
50 | . (!$this->option('skip-filter') ? " | {$filter}" : '')
51 | . " {$packCommand} > {$dumpPath}");
52 |
53 | $this->task(sprintf('DB %s exported', $dbName));
54 | $this->comment(sprintf('Dump path: %s', $dumpPath));
55 | }
56 |
57 | /**
58 | * @return string|null
59 | */
60 | private function getDbName(): ?string
61 | {
62 | $dbs = Cli::run('mysql -N -e "SHOW DATABASES"');
63 | $dbs = array_filter(explode(PHP_EOL, $dbs));
64 | $dbs = array_diff($dbs, ['sys', 'mysql', 'performance_schema', 'information_schema']);
65 | return $this->askWithCompletion('Enter DB name', $dbs);
66 | }
67 |
68 | /**
69 | * @param string $dbName
70 | * @return string
71 | */
72 | private function getDumpName(string $dbName): string
73 | {
74 | $defaultName = $dbName . '.' . date('d-m-Y') . '.sql.gz';
75 | return $this->ask('Enter Dump file name (location)', $defaultName);
76 | }
77 |
78 | /**
79 | * @param string $file
80 | * @return string
81 | */
82 | private function updateDumpExtension(string $file): string
83 | {
84 | return in_array(File::extension($file), ['sql', 'gz'], true) ? $file : $file . '.sql.gz';
85 | }
86 |
87 | /**
88 | * @param string $dumpPath
89 | * @return string
90 | */
91 | private function getPackCommand(string $dumpPath): string
92 | {
93 | return File::extension($dumpPath) === 'gz' ? '| gzip' : '';
94 | }
95 |
96 | /**
97 | * @param string $file
98 | * @return string
99 | */
100 | private function getDumpPath(string $file): string
101 | {
102 | if (strpos($file, '/') === 0) {
103 | $dbPath = $file;
104 | } elseif (strpos($file, './') === 0) {
105 | $dbPath = getcwd() . DIRECTORY_SEPARATOR . substr($file, 2);
106 | } else {
107 | $dbPath = config('env.db.dump_path') . DIRECTORY_SEPARATOR . $file;
108 | }
109 |
110 | return $dbPath;
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/app/Commands/Php/UninstallCommand.php:
--------------------------------------------------------------------------------
1 | info(sprintf('Uninstall PHP v%s', $phpVersion));
42 | $this->uninstallVersion($phpVersion);
43 | }
44 |
45 | // foreach (config('env.php.dependencies') as $formula) {
46 | // Brew::ensureUninstalled($formula);
47 | // }
48 |
49 | File::deleteDirectory((string)config('env.php.brew_etc_path'));
50 | File::deleteDirectory((string)config('env.php.brew_lib_path'));
51 | }
52 |
53 | /**
54 | * @param string $phpVersion
55 | * @return void
56 | */
57 | private function uninstallVersion(string $phpVersion): void
58 | {
59 | $this->task('Ensure no PHP is linked', function () {
60 | $currentVersion = PhpHelper::getLinkedPhp();
61 | if ($currentVersion !== null) {
62 | PhpHelper::unlink($currentVersion);
63 | }
64 | });
65 |
66 | $isPhpInstalled = $this->task(sprintf('Need to be uninstalled PHP v%s?', $phpVersion), function () use ($phpVersion) {
67 | return Brew::isInstalled(PhpHelper::getFormula($phpVersion)) ?: 'Uninstalled. Skip';
68 | });
69 | if ($isPhpInstalled !== true) {
70 | return;
71 | }
72 |
73 | $this->task(sprintf('Link PHP v%s', $phpVersion), function () use ($phpVersion) {
74 | PhpHelper::link($phpVersion);
75 | });
76 |
77 | $this->uninstallPeclExtension($phpVersion, Pecl::XDEBUG_EXTENSION);
78 |
79 | if ($phpVersion !== '5.6') {
80 | $this->uninstallPeclExtension($phpVersion, Pecl::IMAGICK_EXTENSION);
81 | $this->uninstallPeclExtension($phpVersion, Pecl::MEMCACHED_EXTENSION);
82 | }
83 |
84 | $this->task('[ioncube] uninstall', function () {
85 | try {
86 | IonCubeHelper::uninstall();
87 | } catch (\Exception $e) {
88 | return $e->getMessage();
89 | }
90 | });
91 |
92 | $this->task(sprintf('Uninstall %s Brew formula', PhpHelper::getFormula($phpVersion)), function () use ($phpVersion) {
93 | BrewService::stop(PhpHelper::getFormula($phpVersion));
94 | Brew::uninstall(PhpHelper::getFormula($phpVersion), ['--force']);
95 | });
96 |
97 | $this->task('PECL delete config', function () use ($phpVersion) {
98 | PeclHelper::deleteConfigs($phpVersion);
99 | });
100 | }
101 |
102 | /**
103 | * @param string $phpVersion
104 | * @param string $extension
105 | * @return void
106 | */
107 | private function uninstallPeclExtension(string $phpVersion, string $extension): void
108 | {
109 | $apcuInstalled = $this->task(sprintf('[%s] need to be uninstalled?', $extension), function () use ($extension) {
110 | return PeclHelper::isInstalled($extension) ?: 'Uninstalled. Skip';
111 | });
112 | if ($apcuInstalled === true) {
113 | $this->task(sprintf('[%s] uninstall', $extension), function () use ($phpVersion, $extension) {
114 | try {
115 | PeclHelper::uninstall($extension, $phpVersion);
116 | } catch (\Exception $e) {
117 | return $e->getMessage();
118 | }
119 | });
120 | }
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/app/Services/Brew.php:
--------------------------------------------------------------------------------
1 | isInstalled($formula)) {
58 | return false;
59 | }
60 |
61 | $this->install($formula, $options, $taps);
62 | return true;
63 | }
64 |
65 | /**
66 | * @param string $formula
67 | * @param string[] $options
68 | * @param array $tap
69 | * @return string
70 | *
71 | * @throws \DomainException
72 | */
73 | public function install(string $formula, array $options = [], array $tap = null): string
74 | {
75 | $vendor = '';
76 | if (!empty($tap)) {
77 | $this->tap(... $tap);
78 | $vendor = array_shift($tap) . '/';
79 | }
80 |
81 | try {
82 | return Cli::run('brew install ' . $vendor . $formula . ' ' . implode(' ', $options));
83 | } catch (ProcessFailedException $e) {
84 | throw new \DomainException('Brew was unable to install [' . $formula . '].', 0, $e);
85 | }
86 | }
87 |
88 | /**
89 | * @param string $formula
90 | * @param string[] $options
91 | * @return bool
92 | */
93 | public function ensureUninstalled(string $formula, array $options = []): bool
94 | {
95 | if (!$this->isInstalled($formula)) {
96 | return false;
97 | }
98 |
99 | $this->uninstall($formula, $options);
100 | return true;
101 | }
102 |
103 | /**
104 | * @param string $formula
105 | * @param string[] $options
106 | * @return string
107 | */
108 | public function uninstall(string $formula, array $options = []): string
109 | {
110 | try {
111 | return Cli::run('brew uninstall ' . $formula . ' ' . implode(' ', $options));
112 | } catch (ProcessFailedException $e) {
113 | throw new \DomainException('Brew was unable to uninstall [' . $formula . '].', 0, $e);
114 | }
115 | }
116 |
117 | /**
118 | * @param string[] $formulas
119 | * @return void
120 | */
121 | public function tap(string ... $formulas): void
122 | {
123 | $formulas = is_array($formulas) ? $formulas : [$formulas];
124 |
125 | foreach ($formulas as $formula) {
126 | Cli::run('brew tap ' . $formula);
127 | }
128 | }
129 |
130 | /**
131 | * @param string[] $formulas
132 | * @return void
133 | */
134 | public function unTap(string ... $formulas): void
135 | {
136 | $formulas = is_array($formulas) ? $formulas : [$formulas];
137 |
138 | foreach ($formulas as $formula) {
139 | Cli::run('brew untap ' . $formula);
140 | }
141 | }
142 |
143 | /**
144 | * @param string $formula
145 | * @return bool
146 | */
147 | public function hasTap(string $formula): bool
148 | {
149 | return strpos(Cli::run('brew tap | grep ' . $formula), $formula) !== false;
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/app/Services/IonCube.php:
--------------------------------------------------------------------------------
1 | 'https://downloads.ioncube.com/loader_downloads/ioncube_loaders_dar_x86-64.tar.gz',
23 | '7.2' => 'https://downloads.ioncube.com/loader_downloads/ioncube_loaders_dar_x86-64.tar.gz',
24 | '7.1' => 'https://downloads.ioncube.com/loader_downloads/ioncube_loaders_dar_x86-64.tar.gz',
25 | '7.0' => 'https://downloads.ioncube.com/loader_downloads/ioncube_loaders_dar_x86-64.tar.gz',
26 | '5.6' => 'https://downloads.ioncube.com/loader_downloads/ioncube_loaders_dar_x86-64.tar.gz',
27 | 'extension_type' => Pecl::ZEND_EXTENSION_TYPE,
28 | 'extension_php_name' => 'the ionCube PHP Loader'
29 | ];
30 |
31 | /**
32 | * @return bool
33 | */
34 | public function isInstalled(): bool
35 | {
36 | return File::exists($this->extensionPath());
37 | }
38 |
39 | /**
40 | * @return bool
41 | */
42 | public function isEnabled(): bool
43 | {
44 | $extensions = explode("\n", Cli::runQuietly("php -m | grep '" . self::IONCUBE_PHP_NAME . "'"));
45 | return in_array(self::IONCUBE_PHP_NAME, $extensions);
46 | }
47 |
48 | /**
49 | * @return void
50 | * @throws \RuntimeException
51 | */
52 | public function enable(): void
53 | {
54 | if (!File::exists($this->iniPath(true))) {
55 | throw new \RuntimeException('IounCube config file is not found.');
56 | }
57 |
58 | File::move($this->iniPath(true), $this->iniPath());
59 | }
60 |
61 | /**
62 | * @return void
63 | */
64 | public function disable(): void
65 | {
66 | if (!File::exists($this->iniPath())) {
67 | throw new \RuntimeException('IounCube config file is not found.');
68 | }
69 |
70 | File::move($this->iniPath(), $this->iniPath(true));
71 | }
72 |
73 | /**
74 | * @param bool $disabled
75 | * @return string
76 | */
77 | private function iniPath(bool $disabled = false): string
78 | {
79 | return PeclHelper::getConfdPath() . 'ext-ioncube.ini' . ($disabled ? '.disabled' : '');
80 | }
81 |
82 | /**
83 | * @return string
84 | */
85 | private function extensionPath(): string
86 | {
87 | return PeclHelper::getExtensionDirectory() . DIRECTORY_SEPARATOR . self::IONCUBE_EXTENSION . '.so';
88 | }
89 |
90 | /**
91 | * @return void
92 | */
93 | public function configure(): void
94 | {
95 | File::ensureDirExists(PeclHelper::getConfdPath());
96 |
97 | File::put($this->iniPath(), Stub::get('php/ext-ioncube.ini'));
98 | File::delete($this->iniPath(true));
99 | }
100 |
101 | /**
102 | * @param string $phpVersion
103 | * @return void
104 | */
105 | public function install(string $phpVersion): void
106 | {
107 | if (empty($this->setting[$phpVersion])) {
108 | throw new \RuntimeException(sprintf('PHP v%s is not supported', $phpVersion));
109 | }
110 |
111 | File::ensureDirExists((string)config('env.tmp_path'));
112 |
113 |
114 | $url = $this->setting[$phpVersion];
115 |
116 | $urlSplit = explode('/', $url);
117 | $archiveName = $urlSplit[count($urlSplit) - 1];
118 |
119 | $extensionPath = sprintf('%s/ioncube/ioncube_loader_dar_%s.so', config('env.tmp_path'), $phpVersion);
120 |
121 | if (!File::exists($extensionPath)) {
122 | Cli::run(sprintf("cd %s && curl -O %s", config('env.tmp_path'), $url));
123 | Cli::run(sprintf("cd %s && tar -xvzf %s", config('env.tmp_path'), $archiveName));
124 | }
125 |
126 | if (!File::exists($extensionPath)) {
127 | throw new \RuntimeException('Something went wrong while IonCube installation');
128 | }
129 | File::copy($extensionPath, $this->extensionPath());
130 | }
131 |
132 | /**
133 | * @return void
134 | */
135 | public function uninstall(): void
136 | {
137 | File::delete($this->iniPath());
138 | File::delete($this->iniPath(true));
139 | File::delete($this->extensionPath());
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/app/Services/BrewService.php:
--------------------------------------------------------------------------------
1 | getServiceData($service)[self::SERVICE_STARTED];
24 | }
25 |
26 | /**
27 | * @return bool[]
28 | */
29 | public function getServicesStatus(): array
30 | {
31 | return array_map(static function ($serviceData) {
32 | return $serviceData[self::SERVICE_STARTED] ?? false;
33 | }, $this->getServices());
34 | }
35 |
36 | /**
37 | * @param string $service
38 | * @return array
39 | *
40 | * @throws \DomainException
41 | */
42 | private function getServiceData(string $service): array
43 | {
44 | if (!Brew::isInstalled($service)) {
45 | throw new \DomainException("[{$service}] formula is not installed");
46 | }
47 |
48 | $services = $this->getServices();
49 | if (!isset($services[$service])) {
50 | throw new \DomainException("[{$service}] formula is not a service");
51 | }
52 |
53 | return $services[$service];
54 | }
55 |
56 | /**
57 | * $result['serviceName'] = ['started' => bool, 'root' => bool]
58 | *
59 | * @return array
60 | */
61 | private function getServices(): array
62 | {
63 | return array_merge(
64 | $this->parseServicesData(explode(PHP_EOL, Cli::run('brew services list'))),
65 | $this->parseServicesData(explode(PHP_EOL, Cli::run('sudo brew services list')), true)
66 | );
67 | }
68 |
69 | /**
70 | * @param array $serviceLines
71 | * @param bool $skipStopped
72 | * @return array
73 | */
74 | private function parseServicesData(array $serviceLines, bool $skipStopped = false): array
75 | {
76 | $services = [];
77 |
78 | array_shift($serviceLines);
79 | foreach ($serviceLines as $serviceLine) {
80 | $serviceLine = array_values(array_filter(explode(' ', $serviceLine)));
81 | if (empty($serviceLine)) {
82 | continue;
83 | }
84 |
85 | $isStarted = ($serviceLine[1] === 'started' || $serviceLine[1] === 'unknown');
86 | if ($isStarted === false && $skipStopped === true) {
87 | continue;
88 | }
89 |
90 | $services[$serviceLine[0]] = [
91 | self::SERVICE_STARTED => $isStarted,
92 | self::SERVICE_ROOT => null
93 | ];
94 |
95 | if ($services[$serviceLine[0]][self::SERVICE_STARTED] === true) {
96 | $services[$serviceLine[0]][self::SERVICE_ROOT] = ($serviceLine[2] === 'root');
97 | }
98 | }
99 | return $services;
100 | }
101 |
102 | /**
103 | * @param string $service
104 | * @param bool $root
105 | * @return bool
106 | *
107 | * @throws \DomainException
108 | */
109 | public function start(string $service, bool $root = false): bool
110 | {
111 | $serviceData = $this->getServiceData($service);
112 |
113 | if ($serviceData[self::SERVICE_STARTED] === true && $serviceData[self::SERVICE_ROOT] === $root) {
114 | return false;
115 | }
116 |
117 | if ($serviceData[self::SERVICE_STARTED] === true) {
118 | $this->stop($service);
119 | }
120 |
121 | $commandPrefix = $root ? 'sudo ' : '';
122 | Cli::run($commandPrefix . 'brew services start ' . $service);
123 | return true;
124 | }
125 |
126 | /**
127 | * @param string $service
128 | * @return void
129 | *
130 | * @throws \DomainException
131 | */
132 | public function stop(string $service): void
133 | {
134 | $serviceData = $this->getServiceData($service);
135 |
136 | if ($serviceData[self::SERVICE_STARTED] === true) {
137 | $commandPrefix = $serviceData[self::SERVICE_ROOT] ? 'sudo ' : '';
138 | Cli::run($commandPrefix . 'brew services stop ' . $service);
139 | }
140 | }
141 |
142 | /**
143 | * @param string $service
144 | * @param bool $root
145 | * @return void
146 | *
147 | * @throws \DomainException
148 | */
149 | public function restart(string $service, bool $root = false): void
150 | {
151 | $this->stop($service);
152 | $this->start($service, $root);
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/app/Commands/Database/ImportCommand.php:
--------------------------------------------------------------------------------
1 | 'gunzip -cf',
33 | 'zip' => 'unzip -p',
34 | 'sql' => '',
35 | ];
36 |
37 | /**
38 | * @return void
39 | */
40 | public function handle(): void
41 | {
42 | $name = $this->argument('name') ?: $this->ask('Enter Db name');
43 | if (!$name) {
44 | return;
45 | }
46 |
47 | $file = $this->argument('file') ?: $this->getDumpName();
48 | if ($file === null) {
49 | return;
50 | }
51 |
52 | $dbPath = $this->getDumpPath($file);
53 | if (!$this->verifyPath($dbPath)) {
54 | $this->error(sprintf('Passed path does not exist or not a file: %s', $dbPath));
55 | return;
56 | }
57 |
58 | $fileType = File::extension($file);
59 | if (!isset($this->fileType[File::extension($file)])) {
60 | $this->error(sprintf('The file type is not supported: %s', $fileType));
61 | return;
62 | }
63 |
64 | $this->call(CreateCommand::COMMAND, ['name' => $name, '--force' => true]);
65 |
66 | $dumpPath = $dbPath;
67 |
68 | $tmpFilePath = config('env.tmp_path') . DIRECTORY_SEPARATOR . 'dump.sql';
69 | if (!empty($this->fileType[$fileType])) {
70 | File::ensureDirExists((string)config('env.tmp_path'));
71 | File::delete($tmpFilePath);
72 |
73 | Cli::passthru("{$this->fileType[$fileType]} {$dbPath} | pv -b -t -w 80 -N Unpack > {$tmpFilePath}");
74 | $dumpPath = $tmpFilePath;
75 | }
76 |
77 | $filter = "sed -e 's/DEFINER[ ]*=[ ]*[^*]*\*/\*/' "
78 | . " | sed -e 's/DEFINER[ ]*=[ ]*[^*]*PROCEDURE/PROCEDURE/'"
79 | . " | sed -e 's/DEFINER[ ]*=[ ]*[^*]*FUNCTION/FUNCTION/'"
80 | . " | sed -e 's/ROW_FORMAT=FIXED//g'";
81 |
82 | Cli::passthru("pv {$dumpPath} -w 80 -N Import "
83 | . (!$this->option('skip-filter') ? " | {$filter}" : '')
84 | . " | mysql --force {$name}");
85 |
86 | File::delete($tmpFilePath);
87 | $this->task('Imported!');
88 | }
89 |
90 | /**
91 | * @return string|null
92 | */
93 | private function getDumpName(): ?string
94 | {
95 | $dumps = $this->getDumpList();
96 |
97 | $options = array_map(function ($dump) {
98 | return sprintf('%-50s %-15s %s', $dump['name'], $dump['size'], $dump['date']);
99 | }, $dumps);
100 |
101 | return $this->menu('Import DB', $options);
102 | }
103 |
104 | /**
105 | * @return string[]
106 | */
107 | private function getDumpList(): array
108 | {
109 | /** @var \Symfony\Component\Finder\SplFileInfo[] $files */
110 | $files = File::files((string)config('env.db.dump_path'));
111 |
112 | $dumps = [];
113 | foreach ($files as $file) {
114 | if (!$file->isFile() || !isset($this->fileType[$file->getExtension()])) {
115 | continue;
116 | }
117 |
118 | $dumps[$file->getFilename()] = [
119 | 'name' => $file->getFilename(),
120 | 'size' => File::getFormatedFileSize($file->getSize()),
121 | 'date' => date('d M Y', $file->getCTime()),
122 | ];
123 | }
124 | return $dumps;
125 | }
126 |
127 | /**
128 | * @param string $file
129 | * @return string
130 | */
131 | private function getDumpPath(string $file): string
132 | {
133 | if (strpos($file, '/') === 0) {
134 | $dbPath = $file;
135 | } elseif (strpos($file, './') === 0) {
136 | $dbPath = getcwd() . DIRECTORY_SEPARATOR . substr($file, 2);
137 | } else {
138 | $dbPath = config('env.db.dump_path') . DIRECTORY_SEPARATOR . $file;
139 | }
140 |
141 | return $dbPath;
142 | }
143 |
144 |
145 | /**
146 | * @param string $dbPath
147 | * @return bool
148 | */
149 | private function verifyPath(string $dbPath): bool
150 | {
151 | return File::exists($dbPath) && File::isFile($dbPath);
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/app/Commands/Php/XdebugCommand.php:
--------------------------------------------------------------------------------
1 | argument('action');
41 |
42 | if (!$action) {
43 | $action = $this->getAction();
44 | } elseif (!in_array($action, $this->allowedActions, true)) {
45 | $this->warn('Wrong action. Allowed actions: ' . implode('|', $this->allowedActions));
46 | return;
47 | }
48 |
49 | $shouldApacheRestart = false;
50 |
51 | if ($action === 'on') {
52 | $shouldApacheRestart = $this->enable((bool)$this->option('remote-autostart'));
53 | }
54 |
55 | if ($action === 'off') {
56 | $shouldApacheRestart = $this->disable();
57 | }
58 |
59 | if (!$this->option('skip') && $shouldApacheRestart) {
60 | $this->call(RestartCommand::COMMAND);
61 | }
62 | }
63 |
64 | /**
65 | * @param bool $remoteAutostart
66 | * @return bool
67 | */
68 | private function enable(bool $remoteAutostart = false): bool
69 | {
70 | if (!PeclHelper::isInstalled(Pecl::XDEBUG_EXTENSION)) {
71 | $this->warn('xDebug is not installed');
72 | return false;
73 | }
74 |
75 | if (PeclHelper::isEnabled(Pecl::XDEBUG_EXTENSION)) {
76 | $this->warn('xDebug is already enabled');
77 | return false;
78 | }
79 |
80 | $this->task('xDebug enable', function () {
81 | PeclHelper::enable(Pecl::XDEBUG_EXTENSION);
82 | });
83 |
84 | if ($remoteAutostart) {
85 | $this->task('xDebug enable autostart', function () {
86 | $this->enableAutostart();
87 | });
88 | }
89 | return true;
90 | }
91 |
92 | /**
93 | * @return void
94 | */
95 | private function enableAutostart(): void
96 | {
97 | $configContent = File::get(PeclHelper::iniPath(Pecl::XDEBUG_EXTENSION));
98 | $configContent = str_replace('#xdebug.start_with_request=yes', 'xdebug.start_with_request=yes', $configContent);
99 | $configContent = str_replace('xdebug.remote_autostart=0', 'xdebug.remote_autostart=1', $configContent);
100 | File::put(PeclHelper::iniPath(Pecl::XDEBUG_EXTENSION), $configContent);
101 | }
102 |
103 | /**
104 | * @return bool
105 | */
106 | private function disable(): bool
107 | {
108 | if (!PeclHelper::isEnabled(Pecl::XDEBUG_EXTENSION)) {
109 | $this->warn('xDebug is already disabled');
110 | return false;
111 | }
112 |
113 | $this->task('xDebug disable', function () {
114 | $this->disableAutostart();
115 | PeclHelper::disable(Pecl::XDEBUG_EXTENSION);
116 | });
117 | return true;
118 | }
119 |
120 | /**
121 | * @return void
122 | */
123 | private function disableAutostart(): void
124 | {
125 | $configContent = File::get(PeclHelper::iniPath(Pecl::XDEBUG_EXTENSION));
126 | if (strpos('#xdebug.start_with_request=yes', $configContent) === false) {
127 | $configContent = str_replace('xdebug.start_with_request=yes', '#xdebug.start_with_request=yes', $configContent);
128 | }
129 | $configContent = str_replace('xdebug.remote_autostart=1', 'xdebug.remote_autostart=0', $configContent);
130 | File::put(PeclHelper::iniPath(Pecl::XDEBUG_EXTENSION), $configContent);
131 | }
132 |
133 | /**
134 | * @return null|string
135 | */
136 | private function getAction(): ?string
137 | {
138 | if (PeclHelper::isEnabled(Pecl::XDEBUG_EXTENSION)) {
139 | $action = 'off';
140 | $confirm = 'xDebug is enabled, do you want to disable?';
141 | } else {
142 | $action = 'on';
143 | $confirm = 'xDebug is disabled, do you want to enable?';
144 | }
145 |
146 | return $this->confirm($confirm, true) ? $action : null;
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/app/stubs/config/env.php:
--------------------------------------------------------------------------------
1 | [
5 | 'dns',
6 | 'apache',
7 | 'mysql',
8 | 'memcached',
9 | 'redis',
10 | 'mailhog',
11 | 'rabbitmq'
12 | ],
13 |
14 | 'dns' => [
15 | 'formula' => 'dnsmasq',
16 | 'domains' => ['test'],
17 | 'config_path' => config('env.home') . DIRECTORY_SEPARATOR . 'dnsmasq.conf',
18 | 'brew_config_path' => '/usr/local/etc/dnsmasq.conf',
19 | 'brew_config_dir_path' => '/usr/local/etc/dnsmasq.d',
20 | 'resolver_path' => '/etc/resolver',
21 | ],
22 |
23 | 'mysql' => [
24 | 'formula' => 'mysql@5.7',
25 | 'password' => '1',
26 | 'brew_config_path' => '/usr/local/etc/my.cnf',
27 | 'data_dir_path' => '/usr/local/var/mysql'
28 | ],
29 |
30 | 'progress' => [
31 | 'formula' => 'pv'
32 | ],
33 |
34 | 'db' => [
35 | 'dump_path' => config('env.home_public') . DIRECTORY_SEPARATOR . 'databases',
36 | ],
37 |
38 | 'apache' => [
39 | 'formula' => 'httpd',
40 | 'vhosts' => config('env.home') . DIRECTORY_SEPARATOR . 'apache-vhosts',
41 | 'config' => config('env.home') . DIRECTORY_SEPARATOR . 'httpd.conf',
42 | 'brew_config_path' => '/usr/local/etc/httpd/httpd.conf',
43 | 'brew_config_dir_path' => '/usr/local/etc/httpd',
44 | 'localhost_path' => config('env.home_public') . DIRECTORY_SEPARATOR . 'localhost',
45 | 'php_module' => 'LoadModule php{high_version}_module /usr/local/opt/php@{version}/lib/httpd/modules/libphp{high_version}.so',
46 | 'php_module_header' => '#Load PHP Module',
47 | ],
48 |
49 | 'php' => [
50 | 'main_version' => '8.2',
51 | 'brew_path' => '/usr/local/bin/php',
52 | 'brew_etc_path' => '/usr/local/etc/php',
53 | 'brew_lib_path' => '/usr/local/lib/php',
54 | 'brew_pear_path' => '/usr/local/share/pear',
55 | 'dependencies' => [
56 | 'autoconf',
57 | 'pkg-config',
58 | 'imagemagick',
59 | 'zlib',
60 | ],
61 | 'taps' => [
62 | '5.6' => 'shivammathur/php',
63 | '7.0' => 'shivammathur/php',
64 | '7.1' => 'shivammathur/php',
65 | '7.2' => 'shivammathur/php',
66 | '7.3' => 'shivammathur/php',
67 | '7.4' => 'shivammathur/php',
68 | '8.0' => 'shivammathur/php',
69 | '8.1' => 'shivammathur/php',
70 | '8.2' => 'shivammathur/php',
71 | ],
72 | 'versions' => [
73 | '5.6',
74 | '7.0',
75 | '7.1',
76 | '7.2',
77 | '7.3',
78 | '7.4',
79 | '8.0',
80 | '8.1',
81 | '8.2',
82 | ],
83 | 'skip_apache_restart' => false,
84 | 'smtp_catcher' => 'files',
85 | 'smtp_catcher_mailhog' => '/usr/local/bin/MailHog sendmail no@email',
86 | 'mail_path' => config('env.home_public') . DIRECTORY_SEPARATOR . 'mail',
87 | 'smtp_catcher_files' => config('env.home') . DIRECTORY_SEPARATOR . 'smtp_catcher.php',
88 | ],
89 |
90 | 'memcached' => [
91 | 'formula' => 'memcached',
92 | 'dependencies' => [
93 | 'libmemcached',
94 | ]
95 | ],
96 |
97 | 'mailhog' => [
98 | 'formula' => 'mailhog',
99 | 'domain' => 'mailhog.test',
100 | 'log_path' => '/usr/local/var/log/mailhog.log'
101 | ],
102 |
103 | 'rabbitmq' => [
104 | 'formula' => 'rabbitmq',
105 | 'domain' => 'rabbitmq.test',
106 | 'brew_config_dir_path' => '/usr/local/etc/rabbitmq',
107 | 'brew_lib_dir_path' => '/usr/local/var/lib/rabbitmq',
108 | 'log_dir_path' => '/usr/local/var/log/rabbitmq'
109 | ],
110 |
111 | 'redis' => [
112 | 'formula' => 'redis'
113 | ],
114 |
115 | 'secure' => [
116 | 'formula' => 'openssl',
117 | 'certificates_path' => config('env.home') . DIRECTORY_SEPARATOR . 'certificates',
118 | 'securable_domain' => '.test',
119 | 'secured_domains' => [],
120 | ],
121 |
122 | 'completion' => [
123 | 'formula' => 'bash-completion',
124 | 'brew_config_completion_path' => '/usr/local/etc/bash_completion.d' . DIRECTORY_SEPARATOR . strtolower(config('app.name')),
125 | 'brew_completion' => 'source $(brew --prefix)/etc/bash_completion',
126 | 'bashrc_path' => env('HOME') . DIRECTORY_SEPARATOR . '.bashrc',
127 | 'bash_profile_path' => env('HOME') . DIRECTORY_SEPARATOR . '.bash_profile'
128 | ],
129 |
130 | 'software' => [
131 | 'git',
132 | 'composer',
133 | 'bash'
134 | ],
135 |
136 | 'm2' => [
137 | 'configs_path' => config('env.home_public') . DIRECTORY_SEPARATOR . 'm2' . DIRECTORY_SEPARATOR . 'configs',
138 | ],
139 |
140 | 'backup_path' => config('env.home_public') . DIRECTORY_SEPARATOR . 'backups',
141 | 'logs_path' => config('env.home_public') . DIRECTORY_SEPARATOR . 'logs',
142 | 'tmp_path' => config('env.home') . DIRECTORY_SEPARATOR . 'tmp',
143 | ];
144 |
--------------------------------------------------------------------------------
/app/Command.php:
--------------------------------------------------------------------------------
1 | output->writeln($this->successText($string), $this->parseVerbosity($verbosity));
31 | }
32 |
33 | /**
34 | * @param string $string
35 | * @return string
36 | */
37 | protected function successText(string $string): string
38 | {
39 | return "$string>";
40 | }
41 |
42 | /**
43 | * Write a string as error output.
44 | *
45 | * @param string $string
46 | * @param null|int|string $verbosity
47 | * @return void
48 | */
49 | public function error($string, $verbosity = null)
50 | {
51 | $this->output->writeln($this->errorText($string), $this->parseVerbosity($verbosity));
52 | }
53 |
54 | /**
55 | * @param string $string
56 | * @return string
57 | */
58 | protected function errorText(string $string): string
59 | {
60 | return "$string>";
61 | }
62 |
63 | /**
64 | * @param string $title
65 | * @param null $task
66 | * @return null
67 | */
68 | public function task(string $title = '', $task = null)
69 | {
70 | $this->output->write("$title: processing...");
71 |
72 | $result = is_callable($task) ? $task() : $task;
73 |
74 | if (is_string($result) && !empty($result)) {
75 | $resultText = "$result";
76 | } elseif ($result === true || $result === null) {
77 | $resultText = $this->successText('✔');
78 | } else {
79 | $resultText = $this->errorText('𐄂');
80 | }
81 |
82 | if ($this->output->isDecorated()) { // Determines if we can use escape sequences
83 | // Move the cursor to the beginning of the line
84 | $this->output->write("\x0D");
85 |
86 | // Erase the line
87 | $this->output->write("\x1B[2K");
88 | } else {
89 | $this->output->writeln(''); // Make sure we first close the previous line
90 | }
91 |
92 | $this->output->writeln("$title: " . $resultText);
93 |
94 | return $result;
95 | }
96 |
97 | /**
98 | * @param string $title
99 | * @param array $options
100 | * @return string|null
101 | */
102 | public function menu(string $title, array $options = [])
103 | {
104 | $addMenuOption = function (CliMenuBuilder $menuBuilder, array $options, &$optionSelected) use (&$addMenuOption) : void
105 | {
106 | foreach ($options as $value => $label) {
107 | if (is_array($label)) {
108 | $menuBuilder->addSubMenu($value, function (CliMenuBuilder $subMenu) use ($value, $label, &$optionSelected, &$addMenuOption) {
109 | $subMenu->setTitle($value);
110 | $subMenu->disableDefaultItems();
111 |
112 | $addMenuOption($subMenu, $label, $optionSelected);
113 |
114 | $subMenu->addLineBreak('-');
115 | $subMenu->addItem('Go Back', new GoBackAction);
116 | });
117 | } else {
118 | $menuBuilder->addMenuItem(
119 | new MenuOption(
120 | $value, $label, function (CliMenu $menu) use (&$optionSelected) {
121 | $optionSelected = $menu->getSelectedItem();
122 | $menu->close();
123 | })
124 | );
125 | }
126 | }
127 | };
128 |
129 | $menuBuilder = new CliMenuBuilder;
130 | $menuBuilder->setTitle($title);
131 | $menuBuilder->setWidth(110);
132 | $menuBuilder->setTitleSeparator('=');
133 | $menuBuilder->setForegroundColour('green');
134 | $menuBuilder->setBackgroundColour('black');
135 |
136 | $optionSelected = null;
137 | $addMenuOption($menuBuilder, $options, $optionSelected);
138 |
139 | $menuBuilder->addLineBreak('-');
140 | $menuBuilder->setExitButtonText('Cancel');
141 | $menuBuilder->build()->open();
142 |
143 | return $optionSelected instanceof MenuOption ? $optionSelected->getValue() : null;
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/app/Providers/AppServiceProvider.php:
--------------------------------------------------------------------------------
1 | task(sprintf('Does [%s] need to be installed?', $formula), function () use ($formula) {
41 | return !BrewFacade::isInstalled($formula) ?: 'Installed. Skip';
42 | });
43 |
44 | if ($needInstall === true) {
45 | /** @var Command $this */
46 | $this->task(sprintf('Install [%s] Brew formula', $formula), function () use ($formula, $options, $tap) {
47 | BrewFacade::install($formula, $options, (array)$tap);
48 | });
49 | }
50 | return $needInstall === true;
51 | }
52 | );
53 |
54 | Command::macro(
55 | 'uninstallFormula',
56 | function (string $formula) {
57 | /** @var Command $this */
58 | $isInstalled = $this->task(sprintf('Does [%s] need to be uninstalled?', $formula), function () use ($formula) {
59 | return BrewFacade::isInstalled($formula) ?: 'Uninstalled. Skip';
60 | });
61 |
62 | if ($isInstalled === true) {
63 | /** @var Command $this */
64 | $this->task(sprintf('Uninstall [%s] Brew formula', $formula), function () use ($formula) {
65 | BrewFacade::uninstall($formula, ['--force']);
66 | });
67 | }
68 |
69 | return $isInstalled === true;
70 | }
71 | );
72 |
73 | Command::macro(
74 | 'getCurrentPath',
75 | function (?string $path): string {
76 | if (null === $path) {
77 | $hostPath = getcwd();
78 | } elseif (strpos($path, '/') === 0) {
79 | $hostPath = $path;
80 | } else {
81 | $hostPath = getcwd() . DIRECTORY_SEPARATOR . ltrim($path, DIRECTORY_SEPARATOR);
82 | }
83 |
84 | return $hostPath;
85 | }
86 | );
87 |
88 | Command::macro(
89 | 'getFilePath',
90 | function (string $file, string $defaultRoot): string {
91 | if (strpos($file, '/') === 0) {
92 | $path = $file;
93 | } elseif (strpos($file, './') === 0) {
94 | $path = getcwd() . DIRECTORY_SEPARATOR . substr($file, 2);
95 | } else {
96 | $path = $defaultRoot . DIRECTORY_SEPARATOR . $file;
97 | }
98 |
99 | return $path;
100 | }
101 | );
102 |
103 | Command::macro(
104 | 'verifyPath',
105 | function (string $path, bool $isFile = true): bool {
106 | return FileStubs::exists($path)
107 | && ($isFile && FileStubs::isFile($path)) || (!$isFile && FileStubs::isDirectory($path));
108 | }
109 | );
110 | }
111 |
112 | /**
113 | * @return void
114 | */
115 | public function register(): void
116 | {
117 | $this->app->extend('files', function () {
118 | return new Files;
119 | });
120 |
121 | $this->app->bind('stubs', function () {
122 | return new Stubs;
123 | });
124 |
125 | $this->app->bind('command-line', function () {
126 | return new CommandLine;
127 | });
128 |
129 | $this->app->bind('brew', function () {
130 | return new Brew;
131 | });
132 |
133 | $this->app->bind('brew.service', function () {
134 | return new BrewService;
135 | });
136 |
137 | $this->app->bind('secure', function () {
138 | return new Secure;
139 | });
140 |
141 | $this->app->bind('apache.helper', function () {
142 | return new Apache;
143 | });
144 |
145 | $this->app->bind('php.helper', function () {
146 | return new Php;
147 | });
148 |
149 | $this->app->bind('memcached.session', function () {
150 | return new MemcachedSession;
151 | });
152 |
153 | $this->app->bind('pecl.helper', function () {
154 | return new Pecl;
155 | });
156 |
157 | $this->app->bind('ioncube.helper', function () {
158 | return new IonCube;
159 | });
160 |
161 | $this->mergeRecursiveConfigFromPath(BrewStubs::getPath('config/env.php'), 'env');
162 | $this->mergeRecursiveConfigFromPath(BrewStubs::getPath('config/filesystems.php'), 'env.filesystems');
163 | $this->mergeRecursiveConfigFrom(config('env.filesystems'), 'filesystems');
164 |
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/app/Services/Pecl.php:
--------------------------------------------------------------------------------
1 | [
26 | '5.6' => '2.5.5',
27 | '7.0' => '2.7.2',
28 | '7.1' => '2.9.8',
29 | '7.2' => '3.1.5',
30 | '7.3' => '3.1.5',
31 | '7.4' => '3.1.5',
32 | 'extension_type' => self::ZEND_EXTENSION_TYPE
33 | ]
34 | ];
35 |
36 | /**
37 | * @param string $extension
38 | * @return bool
39 | */
40 | public function isInstalled(string $extension): bool
41 | {
42 | return strpos(Cli::runQuietly('pecl list | grep ' . $extension), $extension) !== false;
43 | }
44 |
45 | /**
46 | * @param string $extension
47 | * @return bool
48 | */
49 | public function isEnabled(string $extension): bool
50 | {
51 | $extensions = explode("\n", Cli::runQuietly("php -m | grep '$extension'"));
52 | return in_array($extension, $extensions);
53 | }
54 |
55 | /**
56 | * @param string $extension
57 | * @return void
58 | */
59 | public function enable(string $extension): void
60 | {
61 | if (!File::exists($this->iniPath($extension, true))) {
62 | throw new \RuntimeException($extension . ' config file is not found.');
63 | }
64 | File::move($this->iniPath($extension, true), $this->iniPath($extension));
65 | }
66 |
67 | /**
68 | * @param string $extension
69 | * @return void
70 | */
71 | public function disable(string $extension): void
72 | {
73 | if (!File::exists($this->iniPath($extension))) {
74 | throw new \RuntimeException($extension . ' config file is not found.');
75 | }
76 | File::move($this->iniPath($extension), $this->iniPath($extension, true));
77 | }
78 |
79 | /**
80 | * @param string $extension
81 | * @param bool $disabled
82 | * @return string
83 | */
84 | public function iniPath(string $extension, bool $disabled = false): string
85 | {
86 | return $this->getConfDPath() . "ext-{$extension}.ini" . ($disabled ? '.disabled' : '');
87 | }
88 |
89 | /**
90 | * @return string
91 | */
92 | public function getConfDPath(): string
93 | {
94 | return dirname($this->getPhpIniPath()) . '/conf.d/';
95 | }
96 |
97 | /**
98 | * @return string
99 | */
100 | public function getPhpIniPath(): string
101 | {
102 | return str_replace("\n", '', Cli::run('pecl config-get php_ini'));
103 | }
104 |
105 | /**
106 | * @return string
107 | */
108 | public function getExtensionDirectory(): string
109 | {
110 | return str_replace("\n", '', Cli::run('pecl config-get ext_dir'));
111 | }
112 |
113 | /**
114 | * @return void
115 | */
116 | public function updatePeclChannel(): void
117 | {
118 | Cli::run('pecl channel-update pecl.php.net');
119 | }
120 |
121 | /**`
122 | * @param string $extension
123 | * @param string $phpVersion
124 | * @return void
125 | */
126 | public function install(string $extension, string $phpVersion): void
127 | {
128 | $extensionVersion = isset($this->extensions[$extension][$phpVersion]) ? $this->extensions[$extension][$phpVersion] : null;
129 | $extensionVersion = $extensionVersion === null ? $extension : $extension . '-' . $extensionVersion;
130 |
131 | Cli::run("pecl uninstall -r $extension");
132 | $result = Cli::run(sprintf('printf "\n" | pecl install %s', $extensionVersion));
133 |
134 | if (!preg_match("/Installing '(.*{$extension}.so)'/", $result)) {
135 | throw new \DomainException("Could not find installation path for: $extension\n\n$result");
136 | }
137 |
138 | if (strpos($result, "Error:")) {
139 | throw new \DomainException("Installation path found, but installation failed:\n\n$result");
140 | }
141 | }
142 |
143 | /**
144 | * @param string $extension
145 | * @param string $phpVersion
146 | * @return void
147 | */
148 | public function configure(string $extension, string $phpVersion): void
149 | {
150 | $stubName = "php/ext-{$extension}.ini";
151 | $stubNameVersion = "php/ext-{$extension}-{$phpVersion}.ini";
152 |
153 | if (Stub::isExist($stubNameVersion)) {
154 | $this->removeIniDefinition($extension);
155 | File::put($this->iniPath($extension), Stub::get($stubNameVersion));
156 | } elseif (Stub::isExist($stubName)) {
157 | $this->removeIniDefinition($extension);
158 | File::put($this->iniPath($extension), Stub::get($stubName));
159 | }
160 | }
161 |
162 | /**
163 | * Replace and remove all directives of the .so file for the given extension within the php.ini file.
164 | *
165 | * @param $extension
166 | * The extension key name.
167 | */
168 | private function removeIniDefinition($extension)
169 | {
170 | $phpIniPath = $this->getPhpIniPath();
171 | $phpIniFile = File::get($phpIniPath);
172 | $phpIniFile = preg_replace('/;?(zend_extension|extension)\=".*' . $extension . '.so"/', '', $phpIniFile);
173 | File::put($phpIniPath, $phpIniFile);
174 | }
175 |
176 | /**
177 | * @param string $extension
178 | * @param string $phpVersion
179 | * @return void
180 | */
181 | public function uninstall(string $extension, string $phpVersion): void
182 | {
183 | $extensionVersion = isset($this->extensions[$extension][$phpVersion]) ? $this->extensions[$extension][$phpVersion] : null;
184 | $extensionVersion = $extensionVersion === null ? $extension : $extension . '-' . $extensionVersion;
185 |
186 | Cli::run("pecl uninstall $extensionVersion");
187 | }
188 |
189 | /**
190 | * @param string $phpVersion
191 | * @return void
192 | */
193 | public function deleteConfigs(string $phpVersion): void
194 | {
195 | $pearDirSuffix = config('env.php.main_version') === $phpVersion ? '' : '@' . $phpVersion;
196 | File::deleteDirectory(config('env.php.brew_pear_path') . $pearDirSuffix);
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/app/Services/Secure.php:
--------------------------------------------------------------------------------
1 | canGenerate($domain) || $this->hasPredefined($domain);
20 | }
21 |
22 | /**
23 | * @param string $domain
24 | * @return bool
25 | */
26 | public function canGenerate(string $domain): bool
27 | {
28 | $securableDomain = config('env.secure.securable_domain');
29 | return substr($domain, -strlen($securableDomain)) === $securableDomain;
30 | }
31 |
32 | /**
33 | * @param string $domain
34 | * @return bool
35 | */
36 | public function hasPredefined(string $domain): bool
37 | {
38 | $securedDomainsData = config('env.secure.secured_domains');
39 | $domain = $this->getPredefinedDomain($domain);
40 | return !empty($securedDomainsData[$domain]['crt']) && !empty($securedDomainsData[$domain]['key']);
41 | }
42 |
43 | /**
44 | * @param string $domain
45 | * @return null|string
46 | */
47 | private function getPredefinedDomain(string $domain): ?string
48 | {
49 | $securedDomainsData = config('env.secure.secured_domains');
50 | return isset($securedDomainsData[$domain]) ? $domain : $this->getWildcardPredefinedDomain($domain);
51 | }
52 |
53 | /**
54 | * @param string $domain
55 | * @return null|string
56 | */
57 | private function getWildcardPredefinedDomain(string $domain): ?string
58 | {
59 | $securedDomainsData = config('env.secure.secured_domains');
60 | $securedDomains = array_filter(array_keys($securedDomainsData), function($value) use ($domain) {
61 | if (strpos($value, '*.') !== 0) {
62 | return false;
63 | }
64 |
65 | $value = str_replace('*.', '.', $value);
66 | return substr($domain, -strlen($value)) === $value;
67 | });
68 | return !empty($securedDomains) ? array_shift($securedDomains) : null;
69 | }
70 |
71 | /**
72 | * @param string $domain
73 | * @param string[] $aliases
74 | * @return void
75 | */
76 | public function generate(string $domain, array $aliases = []): void
77 | {
78 | if (!$this->canGenerate($domain)) {
79 | throw new \RuntimeException("The $domain cannot be secured");
80 | }
81 |
82 | $this->delete($domain);
83 |
84 | File::ensureDirExists((string)config('env.secure.certificates_path'));
85 |
86 | $this->buildCertificateConf($domain, $aliases);
87 | $this->createPrivateKey($domain);
88 | $this->createSigningRequest($domain);
89 | $this->generateCertificates($domain);
90 | $this->trustCertificate($domain);
91 | }
92 |
93 | /**
94 | * @param string $domain
95 | * @return void
96 | */
97 | public function delete(string $domain): void
98 | {
99 | if (!$this->canGenerate($domain)) {
100 | return;
101 | }
102 |
103 | File::delete(
104 | [
105 | $this->getFilePath($domain, 'crt'),
106 | $this->getFilePath($domain, 'key'),
107 | $this->getFilePath($domain, 'conf'),
108 | $this->getFilePath($domain, 'csr')
109 | ]
110 | );
111 |
112 | Cli::runQuietly(sprintf('sudo security delete-certificate -c "%s" -t', $domain));
113 | Cli::runQuietly(sprintf('sudo security delete-certificate -c "%s"', $domain));
114 | }
115 |
116 | /**
117 | * @param string $domain
118 | * @param string[] $aliases
119 | * @return void
120 | */
121 | private function buildCertificateConf(string $domain, array $aliases = []): void
122 | {
123 | $domains = empty($aliases) ? [$domain] : array_merge([$domain], $aliases);
124 |
125 | $dnsNames = '';
126 | $count = 0;
127 | foreach ($domains as $name) {
128 | $count++;
129 | $dnsNames .= 'DNS.' . $count . ' = ' . $name . PHP_EOL;
130 | }
131 |
132 | $config = Stub::get('openssl.conf', ['DNS_NAMES' => $dnsNames]);
133 | File::put($this->getFilePath($domain, 'conf'), $config);
134 | }
135 |
136 | /**
137 | * @param string $domain
138 | * @return void
139 | */
140 | private function createPrivateKey(string $domain): void
141 | {
142 | $command = sprintf('openssl genrsa -out %s 2048', $this->getFilePath($domain, 'key'));
143 | Cli::run($command);
144 | }
145 |
146 | /**
147 | * @param string $domain
148 | * @return void
149 | */
150 | private function createSigningRequest(string $domain): void
151 | {
152 | $command = sprintf(
153 | 'openssl req -new -key %s -out %s -subj "/C=/ST=/O=/localityName=/commonName=*.%s/organizationalUnitName=/emailAddress=/" -config %s -passin pass:',
154 | $this->getFilePath($domain, 'key'),
155 | $this->getFilePath($domain, 'csr'),
156 | $domain,
157 | $this->getFilePath($domain, 'conf')
158 | );
159 | Cli::run($command);
160 | }
161 |
162 | /**
163 | * @param string $domain
164 | * @return void
165 | */
166 | private function generateCertificates(string $domain): void
167 | {
168 | $command = sprintf(
169 | 'openssl x509 -req -days 365 -in %s -signkey %s -out %s -extensions v3_req -extfile %s',
170 | $this->getFilePath($domain, 'csr'),
171 | $this->getFilePath($domain, 'key'),
172 | $this->getFilePath($domain, 'crt'),
173 | $this->getFilePath($domain, 'conf')
174 | );
175 | Cli::run($command);
176 | }
177 |
178 | /**
179 | * @param string $domain
180 | * @return void
181 | */
182 | private function trustCertificate(string $domain): void
183 | {
184 | $command = sprintf(
185 | 'sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain %s',
186 | $this->getFilePath($domain, 'crt')
187 | );
188 | Cli::run($command);
189 | }
190 |
191 | /**
192 | * @param string $domain
193 | * @param string $fileType
194 | * @return string
195 | */
196 | public function getFilePath(string $domain, string $fileType): string
197 | {
198 | if ($this->canGenerate($domain)) {
199 | return config('env.secure.certificates_path') . DIRECTORY_SEPARATOR . $domain . '.' . $fileType;
200 | }
201 |
202 | if ($this->hasPredefined($domain) && in_array($fileType, ['crt', 'key'])) {
203 | return config('env.secure.secured_domains')[$this->getPredefinedDomain($domain)][$fileType];
204 | }
205 |
206 | throw new \RuntimeException('Path cannot be built.');
207 | }
208 | }
209 |
--------------------------------------------------------------------------------
/app/Services/Apache.php:
--------------------------------------------------------------------------------
1 | getConfPath($domain));
20 | }
21 |
22 | /**
23 | * @param string $domain
24 | * @param string $documentRoot
25 | * @param string[] $aliases
26 | * @param bool $secure
27 | * @return void
28 | */
29 | public function configureVHost(string $domain, string $documentRoot, array $aliases = [], bool $secure = true): void
30 | {
31 | $this->configureHost($domain, $documentRoot, '', $aliases, $secure);
32 | }
33 |
34 | /**
35 | * @param string $domain
36 | * @param string $port
37 | * @param string[] $aliases
38 | * @param bool $secure
39 | * @return void
40 | */
41 | public function configureProxyVHost(string $domain, string $port, array $aliases = [], bool $secure = true): void
42 | {
43 | $this->configureHost($domain, '', $port, $aliases, $secure);
44 | }
45 |
46 | /**
47 | * @param string $domain
48 | * @param string $documentRoot
49 | * @param string $port
50 | * @param array $aliases
51 | * @param bool $secure
52 | * @return void
53 | */
54 | private function configureHost(string $domain, string $documentRoot = '', string $port = '', array $aliases = [], bool $secure = true)
55 | {
56 | $vhostTemplate = empty($documentRoot) && !empty($port) ? 'httpd-proxy-vhost.conf' : 'httpd-vhost.conf';
57 | $sslVhostTemplate = empty($documentRoot) && !empty($port) ? 'httpd-proxy-vhost-ssl.conf' : 'httpd-vhost-ssl.conf';
58 |
59 | File::ensureDirExists((string)config('env.apache.vhosts'));
60 | $this->deleteVHost($domain);
61 |
62 | $serverAliases = !empty($aliases)
63 | ? 'ServerAlias ' . implode(' ', $aliases)
64 | : '';
65 |
66 | $virtualHostSsl = '';
67 | if ($secure && Secure::canSecure($domain)) {
68 | $virtualHostSsl = Stub::get(
69 | $sslVhostTemplate,
70 | [
71 | 'DOMAIN' => $domain,
72 | 'SERVER_ALIAS' => $serverAliases,
73 | 'DOCUMENT_ROOT' => $documentRoot,
74 | 'PORT' => $port,
75 | 'LOGS_PATH' => config('env.logs_path'),
76 | 'CERTIFICATE_CRT' => Secure::getFilePath($domain, 'crt'),
77 | 'CERTIFICATE_KEY' => Secure::getFilePath($domain, 'key')
78 | ]
79 | );
80 | }
81 |
82 | $vhostConfig = Stub::get(
83 | $vhostTemplate,
84 | [
85 | 'DOMAIN' => $domain,
86 | 'SERVER_ALIAS' => $serverAliases,
87 | 'DOCUMENT_ROOT' => $documentRoot,
88 | 'PORT' => $port,
89 | 'LOGS_PATH' => config('env.logs_path'),
90 | 'VIRTUAL_HOST_SSL' => $virtualHostSsl,
91 | ]
92 | );
93 | File::put($this->getConfPath($domain), $vhostConfig);
94 | }
95 |
96 | /**
97 | * @return void
98 | */
99 | public function unlinkPhp(): void
100 | {
101 | $config = File::get((string)config('env.apache.config'));
102 | foreach (config('env.php.versions') as $phpVersion) {
103 | $config = $this->removePhpVersion($config, $phpVersion);
104 | }
105 | File::put((string)config('env.apache.config'), $config);
106 | }
107 |
108 | /**
109 | * @param string $version
110 | * @return void
111 | */
112 | public function linkPhp(string $version): void
113 | {
114 | $this->unlinkPhp();
115 |
116 | $config = File::get((string)config('env.apache.config'));
117 | $phpModuleHeader = config('env.apache.php_module_header') . PHP_EOL;
118 | if (strpos($config, $phpModuleHeader) === false) {
119 | throw new \RuntimeException('Apache config is broken');
120 | }
121 |
122 | $phpModuleLoad = $this->getPhpModuleLoad($version);
123 | $config = str_replace($phpModuleHeader, $phpModuleHeader . $phpModuleLoad . PHP_EOL, $config);
124 | File::put((string)config('env.apache.config'), $config);
125 | }
126 |
127 | /**
128 | * @param string $config
129 | * @param string $phpVersion
130 | * @return string
131 | */
132 | private function removePhpVersion(string $config, string $phpVersion): string
133 | {
134 | $phpModuleLoad = $this->getPhpModuleLoad($phpVersion);
135 |
136 | $config = str_replace('#' . $phpModuleLoad . PHP_EOL, '', $config);
137 | $config = str_replace('#' . $phpModuleLoad, '', $config);
138 | $config = str_replace($phpModuleLoad . PHP_EOL, '', $config);
139 | $config = str_replace($phpModuleLoad, '', $config);
140 |
141 | return $config;
142 | }
143 |
144 | /**
145 | * @param string $phpVersion
146 | * @return string
147 | */
148 | private function getPhpModuleLoad($phpVersion): string
149 | {
150 | $highVersion = explode('.', $phpVersion);
151 | $highVersion = array_shift($highVersion);
152 | $highVersion = $highVersion < 8 ? $highVersion : '';
153 |
154 | $phpModuleLoad = config('env.apache.php_module');
155 | $phpModuleLoad = str_replace('{version}', $phpVersion, $phpModuleLoad);
156 | $phpModuleLoad = str_replace('{high_version}', $highVersion, $phpModuleLoad);
157 |
158 | return $phpModuleLoad;
159 | }
160 |
161 | /**
162 | * @param string $domain
163 | * @return string
164 | */
165 | private function getConfPath(string $domain): string
166 | {
167 | $configFilename = $domain === 'localhost' ? '00-default' : $domain;
168 | return config('env.apache.vhosts') . DIRECTORY_SEPARATOR . $configFilename . '.conf';
169 | }
170 |
171 | /**
172 | * @return void
173 | */
174 | public function configure(): void
175 | {
176 | File::ensureDirExists((string)config('env.apache.vhosts'));
177 | File::ensureDirExists((string)config('env.logs_path'));
178 |
179 | $apacheConfig = Stub::get(
180 | 'httpd.conf',
181 | [
182 | 'CURRENT_USER' => $_SERVER['USER'],
183 | 'PHP_MODULE_LOADER' => config('env.apache.php_module_header'),
184 | 'VHOSTS_PATH' => config('env.apache.vhosts'),
185 | 'LOGS_PATH' => config('env.logs_path'),
186 | ]
187 | );
188 | File::put((string)config('env.apache.config'), $apacheConfig);
189 | $this->includeToHttpdConfig();
190 | }
191 |
192 | /**
193 | * @return void
194 | */
195 | private function includeToHttpdConfig(): void
196 | {
197 | $includeConfig = sprintf('Include %s', config('env.apache.config'));
198 | if (strpos(File::get((string)config('env.apache.brew_config_path')), $includeConfig) === false) {
199 | File::append((string)config('env.apache.brew_config_path'), PHP_EOL . $includeConfig . PHP_EOL);
200 | }
201 | }
202 |
203 | /**
204 | * @return void
205 | */
206 | public function initDefaultLocalhostVHost()
207 | {
208 | $valetDir = (string)config('env.apache.localhost_path');
209 | File::ensureDirExists($valetDir);
210 | File::put($valetDir . '/index.php', Stub::get('localhost/index.php'));
211 | File::put($valetDir . '/no-entry.jpg', Stub::get('localhost/no-entry.jpg'));
212 |
213 | $this->configureVHost('localhost', $valetDir, [], false);
214 | }
215 | }
216 |
--------------------------------------------------------------------------------
/app/stubs/completion/bash:
--------------------------------------------------------------------------------
1 | _sage()
2 | {
3 | local cur script coms opts com
4 | COMPREPLY=()
5 | _get_comp_words_by_ref -n : cur words
6 |
7 | # for an alias, get the real script behind it
8 | if [[ $(type -t ${words[0]}) == "alias" ]]; then
9 | script=$(alias ${words[0]} | sed -E "s/alias ${words[0]}='(.*)'/\1/")
10 | else
11 | script=${words[0]}
12 | fi
13 |
14 | # lookup for command
15 | for word in ${words[@]:1}; do
16 | if [[ $word != -* ]]; then
17 | com=$word
18 | break
19 | fi
20 | done
21 |
22 | # completing for an option
23 | if [[ ${cur} == --* ]] ; then
24 | opts="--help --quiet --verbose --version --ansi --no-ansi --no-interaction --env"
25 |
26 | case "$com" in
27 |
28 | help)
29 | opts="${opts} --format --raw"
30 | ;;
31 |
32 | list)
33 | opts="${opts} --raw --format"
34 | ;;
35 |
36 | apache:host)
37 | opts="${opts} --path --not-secure"
38 | ;;
39 |
40 | apache:host-revoke)
41 | opts="${opts} "
42 | ;;
43 |
44 | apache:install)
45 | opts="${opts} "
46 | ;;
47 |
48 | apache:proxy:host)
49 | opts="${opts} --not-secure"
50 | ;;
51 |
52 | apache:restart)
53 | opts="${opts} "
54 | ;;
55 |
56 | apache:start)
57 | opts="${opts} "
58 | ;;
59 |
60 | apache:stop)
61 | opts="${opts} "
62 | ;;
63 |
64 | apache:uninstall)
65 | opts="${opts} --force"
66 | ;;
67 |
68 | db:create)
69 | opts="${opts} --force"
70 | ;;
71 |
72 | db:drop)
73 | opts="${opts} "
74 | ;;
75 |
76 | db:export)
77 | opts="${opts} --skip-filter"
78 | ;;
79 |
80 | db:import)
81 | opts="${opts} --skip-filter"
82 | ;;
83 |
84 | db:install)
85 | opts="${opts} "
86 | ;;
87 |
88 | db:list)
89 | opts="${opts} "
90 | ;;
91 |
92 | dns:install)
93 | opts="${opts} "
94 | ;;
95 |
96 | dns:restart)
97 | opts="${opts} "
98 | ;;
99 |
100 | dns:start)
101 | opts="${opts} "
102 | ;;
103 |
104 | dns:stop)
105 | opts="${opts} "
106 | ;;
107 |
108 | dns:uninstall)
109 | opts="${opts} "
110 | ;;
111 |
112 | env:completion)
113 | opts="${opts} "
114 | ;;
115 |
116 | env:config-dump)
117 | opts="${opts} --skip-custom"
118 | ;;
119 |
120 | env:install)
121 | opts="${opts} "
122 | ;;
123 |
124 | env:uninstall)
125 | opts="${opts} --force"
126 | ;;
127 |
128 | m2:configure)
129 | opts="${opts} --magento-path --scope --scope-code"
130 | ;;
131 |
132 | mailhog:install)
133 | opts="${opts} "
134 | ;;
135 |
136 | mailhog:restart)
137 | opts="${opts} "
138 | ;;
139 |
140 | mailhog:start)
141 | opts="${opts} "
142 | ;;
143 |
144 | mailhog:stop)
145 | opts="${opts} "
146 | ;;
147 |
148 | mailhog:uninstall)
149 | opts="${opts} "
150 | ;;
151 |
152 | memcached:flush)
153 | opts="${opts} "
154 | ;;
155 |
156 | memcached:install)
157 | opts="${opts} "
158 | ;;
159 |
160 | memcached:restart)
161 | opts="${opts} "
162 | ;;
163 |
164 | memcached:session)
165 | opts="${opts} --skip"
166 | ;;
167 |
168 | memcached:start)
169 | opts="${opts} "
170 | ;;
171 |
172 | memcached:stop)
173 | opts="${opts} "
174 | ;;
175 |
176 | memcached:uninstall)
177 | opts="${opts} "
178 | ;;
179 |
180 | mysql:install)
181 | opts="${opts} "
182 | ;;
183 |
184 | mysql:restart)
185 | opts="${opts} "
186 | ;;
187 |
188 | mysql:start)
189 | opts="${opts} "
190 | ;;
191 |
192 | mysql:stop)
193 | opts="${opts} "
194 | ;;
195 |
196 | mysql:uninstall)
197 | opts="${opts} --force"
198 | ;;
199 |
200 | php:install)
201 | opts="${opts} "
202 | ;;
203 |
204 | php:ioncube)
205 | opts="${opts} --skip"
206 | ;;
207 |
208 | php:switch)
209 | opts="${opts} --skip"
210 | ;;
211 |
212 | php:uninstall)
213 | opts="${opts} "
214 | ;;
215 |
216 | php:xdebug)
217 | opts="${opts} --skip --remote-autostart"
218 | ;;
219 |
220 | rabbitmq:install)
221 | opts="${opts} "
222 | ;;
223 |
224 | rabbitmq:queue:list)
225 | opts="${opts} "
226 | ;;
227 |
228 | rabbitmq:restart)
229 | opts="${opts} "
230 | ;;
231 |
232 | rabbitmq:start)
233 | opts="${opts} "
234 | ;;
235 |
236 | rabbitmq:stop)
237 | opts="${opts} "
238 | ;;
239 |
240 | rabbitmq:uninstall)
241 | opts="${opts} "
242 | ;;
243 |
244 | rabbitmq:vhost:create)
245 | opts="${opts} --force"
246 | ;;
247 |
248 | rabbitmq:vhost:delete)
249 | opts="${opts} "
250 | ;;
251 |
252 | rabbitmq:vhost:list)
253 | opts="${opts} "
254 | ;;
255 |
256 | redis:flush)
257 | opts="${opts} "
258 | ;;
259 |
260 | redis:install)
261 | opts="${opts} "
262 | ;;
263 |
264 | redis:restart)
265 | opts="${opts} "
266 | ;;
267 |
268 | redis:start)
269 | opts="${opts} "
270 | ;;
271 |
272 | redis:stop)
273 | opts="${opts} "
274 | ;;
275 |
276 | redis:uninstall)
277 | opts="${opts} "
278 | ;;
279 |
280 | secure:generate)
281 | opts="${opts} "
282 | ;;
283 |
284 | secure:install)
285 | opts="${opts} "
286 | ;;
287 |
288 | secure:revoke)
289 | opts="${opts} "
290 | ;;
291 |
292 | services:start)
293 | opts="${opts} "
294 | ;;
295 |
296 | services:status)
297 | opts="${opts} "
298 | ;;
299 |
300 | services:stop)
301 | opts="${opts} "
302 | ;;
303 |
304 | site:link)
305 | opts="${opts} --path --not-secure"
306 | ;;
307 |
308 | site:proxy:link)
309 | opts="${opts} --not-secure"
310 | ;;
311 |
312 | site:unlink)
313 | opts="${opts} "
314 | ;;
315 |
316 | esac
317 |
318 | COMPREPLY=($(compgen -W "${opts}" -- ${cur}))
319 | __ltrim_colon_completions "$cur"
320 |
321 | return 0;
322 | fi
323 |
324 | # completing for a command
325 | if [[ $cur == $com ]]; then
326 | coms="help list apache:host apache:host-revoke apache:install apache:proxy:host apache:restart apache:start apache:stop apache:uninstall db:create db:drop db:export db:import db:install db:list dns:install dns:restart dns:start dns:stop dns:uninstall env:completion env:config-dump env:install env:uninstall m2:configure mailhog:install mailhog:restart mailhog:start mailhog:stop mailhog:uninstall memcached:flush memcached:install memcached:restart memcached:session memcached:start memcached:stop memcached:uninstall mysql:install mysql:restart mysql:start mysql:stop mysql:uninstall php:install php:ioncube php:switch php:uninstall php:xdebug rabbitmq:install rabbitmq:queue:list rabbitmq:restart rabbitmq:start rabbitmq:stop rabbitmq:uninstall rabbitmq:vhost:create rabbitmq:vhost:delete rabbitmq:vhost:list redis:flush redis:install redis:restart redis:start redis:stop redis:uninstall secure:generate secure:install secure:revoke services:start services:status services:stop site:link site:proxy:link site:unlink"
327 |
328 | COMPREPLY=($(compgen -W "${coms}" -- ${cur}))
329 | __ltrim_colon_completions "$cur"
330 |
331 | return 0
332 | fi
333 | }
334 |
335 | complete -o default -F _sage sage
336 |
--------------------------------------------------------------------------------
/app/Commands/Php/InstallCommand.php:
--------------------------------------------------------------------------------
1 | [Pecl::IMAGICK_EXTENSION, Pecl::REDIS_EXTENSION, Pecl::MEMCACHED_EXTENSION],
45 | ];
46 |
47 | /**
48 | * @return void
49 | */
50 | public function handle(): void
51 | {
52 | $this->call(MemcachedInstallCommand::COMMAND);
53 |
54 | $phpVersions = config('env.php.versions');
55 |
56 | foreach (config('env.php.dependencies') as $formula) {
57 | Brew::ensureInstalled($formula);
58 | }
59 |
60 | $this->setupSmtpCatcher();
61 |
62 | foreach ($phpVersions as $phpVersion) {
63 | if ($phpVersion === '7.3') {
64 | continue;
65 | }
66 |
67 | $this->info(sprintf('Install PHP v%s', $phpVersion));
68 | $this->installVersion($phpVersion);
69 | }
70 |
71 | $this->call(MemcachedStartCommand::COMMAND);
72 |
73 | File::deleteDirectory((string)config('env.tmp_path'));
74 | }
75 |
76 | /**
77 | * @param string $phpVersion
78 | * @return void
79 | */
80 | private function installVersion(string $phpVersion): void
81 | {
82 | $this->task('Ensure no PHP is linked', function () {
83 | $currentVersion = PhpHelper::getLinkedPhp();
84 | if ($currentVersion !== null) {
85 | PhpHelper::unlink($currentVersion);
86 | }
87 | });
88 |
89 | $phpTaps = config('env.php.taps');
90 | $taps = !empty($phpTaps[$phpVersion]) ? $phpTaps[$phpVersion] : null;
91 |
92 | $installOptions = config('env.php.install_options');
93 | $options = !empty($installOptions[$phpVersion]) ? $installOptions[$phpVersion] : [];
94 |
95 | if ($this->installFormula(PhpHelper::getFormula($phpVersion), $options, $taps)) {
96 | BrewService::stop(PhpHelper::getFormula($phpVersion));
97 | }
98 |
99 | $this->task(sprintf('PHP v%s link', $phpVersion), function () use ($phpVersion) {
100 | PhpHelper::link($phpVersion);
101 | });
102 |
103 | $this->task(sprintf('PHP v%s update ini files', $phpVersion), function () use ($phpVersion) {
104 | $this->tunePhpIni($phpVersion);
105 | $this->tuneOpCache();
106 | });
107 |
108 | $this->task('PECL updating channel', function () {
109 | PeclHelper::updatePeclChannel();
110 | });
111 |
112 | $this->installPeclExtension($phpVersion, Pecl::XDEBUG_EXTENSION);
113 |
114 | if (empty($this->skipExtension[$phpVersion])
115 | || !in_array(Pecl::IMAGICK_EXTENSION, $this->skipExtension[$phpVersion], true)
116 | ) {
117 | $this->installPeclExtension($phpVersion, Pecl::IMAGICK_EXTENSION);
118 | }
119 |
120 | if (empty($this->skipExtension[$phpVersion])
121 | || !in_array(Pecl::REDIS_EXTENSION, $this->skipExtension[$phpVersion], true)
122 | ) {
123 | $this->installPeclExtension($phpVersion, Pecl::REDIS_EXTENSION);
124 | }
125 |
126 | if (empty($this->skipExtension[$phpVersion])
127 | || !in_array(Pecl::MEMCACHED_EXTENSION, $this->skipExtension[$phpVersion], true)
128 | ) {
129 | $this->installPeclExtension($phpVersion, Pecl::MEMCACHED_EXTENSION);
130 | MemcachedSession::configure();
131 | }
132 |
133 | $this->installIonCube($phpVersion);
134 |
135 | $this->call(MemcachedSessionCommand::COMMAND, ['action' => 'off', '--skip' => 1]);
136 | $this->call(IoncubeCommand::COMMAND, ['action' => 'off', '--skip' => 1]);
137 | $this->call(XdebugCommand::COMMAND, ['action' => 'off', '--skip' => 1]);
138 | }
139 |
140 | /**
141 | * @param string $phpVersion
142 | * @param string $extension
143 | * @return void
144 | */
145 | private function installPeclExtension(string $phpVersion, string $extension): void
146 | {
147 | $needInstall = $this->task(sprintf('[%s] need to be installed?', $extension), function () use ($phpVersion, $extension) {
148 | return !PeclHelper::isInstalled($extension) ?: 'Installed. Skip';
149 | });
150 | if ($needInstall === true) {
151 | $this->task(sprintf('[%s] install', $extension), function () use ($phpVersion, $extension) {
152 | PeclHelper::install($extension, $phpVersion);
153 | });
154 | }
155 |
156 | $this->task(sprintf('[%s] configure', $extension), function () use ($phpVersion, $extension) {
157 | PeclHelper::configure($extension, $phpVersion);
158 | });
159 | }
160 |
161 | /**
162 | * @param string $phpVersion
163 | * @return void
164 | */
165 | private function installIonCube(string $phpVersion): void
166 | {
167 | $ioncubeNeedInstall = $this->task('[ioncube] need to be installed?', function () use ($phpVersion) {
168 | return !IonCubeHelper::isInstalled() ?: 'Installed. Skip';
169 | });
170 |
171 | $ioncubeNeedConfigure = true;
172 | if ($ioncubeNeedInstall === true) {
173 | $ioncubeNeedConfigure = $this->task('[ioncube] install', function () use ($phpVersion) {
174 | try {
175 | IonCubeHelper::install($phpVersion);
176 | } catch (\Exception $e) {
177 | return $e->getMessage();
178 | }
179 | return true;
180 | });
181 | }
182 |
183 | if ($ioncubeNeedConfigure === true) {
184 | $this->task('[ioncube] configure', function () {
185 | IonCubeHelper::configure();
186 | });
187 | }
188 | }
189 |
190 | /**
191 | * @return void
192 | */
193 | private function tuneOpCache()
194 | {
195 | if (!File::exists(PeclHelper::getConfdPath() . 'ext-opcache.ini.origin')) {
196 | File::move(
197 | PeclHelper::getConfdPath() . 'ext-opcache.ini',
198 | PeclHelper::getConfdPath() . 'ext-opcache.ini.origin'
199 | );
200 | }
201 | $originOpCacheConfig = File::get(PeclHelper::getConfdPath() . 'ext-opcache.ini.origin') . PHP_EOL;
202 | File::put(
203 | PeclHelper::getConfdPath() . 'ext-opcache.ini',
204 | $originOpCacheConfig . Stub::get('php/ext-opcache.ini')
205 | );
206 | }
207 |
208 | /**
209 | * @return void
210 | */
211 | private function tunePhpIni(string $phpVersion)
212 | {
213 | File::ensureDirExists(PeclHelper::getConfdPath());
214 |
215 | $smtpCatcher = config('env.php.smtp_catcher');
216 | $smtpCatcher = in_array($smtpCatcher, $this->supportedSmtpCatchers, true) ? $smtpCatcher : 'files';
217 |
218 | $phpZIni = Stub::get('php/z-performance.ini', [
219 | 'TIMEZONE' => $this->getSystemTimeZone(),
220 | 'SMTP_CATCHER_PATH' => config('env.php.smtp_catcher_' . $smtpCatcher),
221 | 'ERROR_LOG_FILE' => config('env.home_public') . DIRECTORY_SEPARATOR . 'logs' . DIRECTORY_SEPARATOR . 'php' . $phpVersion . '-error.log'
222 | ]);
223 | File::put(PeclHelper::getConfdPath() . 'z-performance.ini', $phpZIni);
224 | }
225 |
226 | /**
227 | * @return void
228 | */
229 | private function setupSmtpCatcher()
230 | {
231 | $mailDir = (string)config('env.php.mail_path');
232 | $smtpCatcherPath = (string)config('env.php.smtp_catcher_files');
233 | File::ensureDirExists((string)config('env.home'));
234 | File::ensureDirExists($mailDir);
235 |
236 | $smtpCatcher = Stub::get('php/smtp_catcher.php', [
237 | 'TIMEZONE' => $this->getSystemTimeZone(),
238 | 'MAIL_FOLDER' => $mailDir
239 | ]);
240 | File::put($smtpCatcherPath, $smtpCatcher);
241 | File::chmod($smtpCatcherPath, 0755);
242 | }
243 |
244 | /**
245 | * @return string
246 | */
247 | private function getSystemTimeZone()
248 | {
249 | $systemZoneName = readlink('/etc/localtime');
250 | // All versions below High Sierra
251 | $systemZoneName = str_replace('/usr/share/zoneinfo/', '', $systemZoneName);
252 | // macOS High Sierra has a new location for the timezone info
253 | $systemZoneName = str_replace('/var/db/timezone/zoneinfo/', '', $systemZoneName);
254 |
255 | return $systemZoneName;
256 | }
257 | }
258 |
--------------------------------------------------------------------------------