From 075443b763b2c1135371091349d487684400d3fe Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 8 May 2016 23:04:16 -0500 Subject: [PATCH] refactor and tests --- composer.json | 1 + facades.php | 37 ++++++ src/Configuration.php | 22 +++- src/Filesystem.php | 104 +++++++++++---- src/PhpFpm.php | 3 +- src/Site.php | 95 +++++++------- tests/FilesystemTest.php | 25 ++++ tests/SiteTest.php | 84 ++++++++++++ .../project/storage/logs/.gitkeep | 0 valet.php | 120 +++++++++--------- 10 files changed, 360 insertions(+), 131 deletions(-) create mode 100644 facades.php create mode 100644 tests/FilesystemTest.php create mode 100644 tests/SiteTest.php create mode 100644 tests/test-directory-for-logs/project/storage/logs/.gitkeep diff --git a/composer.json b/composer.json index 4d27424..5400cfe 100644 --- a/composer.json +++ b/composer.json @@ -16,6 +16,7 @@ "autoload": { "files": [ "compatibility.php", + "facades.php", "helpers.php" ], "psr-4": { diff --git a/facades.php b/facades.php new file mode 100644 index 0000000..eb570c6 --- /dev/null +++ b/facades.php @@ -0,0 +1,37 @@ +write(tap($this->read(), function (&$config) use ($path) { - $config['paths'] = collect($config['paths'])->push($path)->unique()->all(); + $this->write(tap($this->read(), function (&$config) use ($path, $prepend) { + $method = $prepend ? 'prepend' : 'push'; + + $config['paths'] = collect($config['paths'])->{$method}($path)->unique()->all(); })); } + /** + * Prepend the given path to the configuration. + * + * @param string $path + * @return void + */ + function prependPath($path) + { + return $this->addPath($path, true); + } + /** * Add the given path to the configuration. * diff --git a/src/Filesystem.php b/src/Filesystem.php index 063a8c3..02051fc 100644 --- a/src/Filesystem.php +++ b/src/Filesystem.php @@ -10,7 +10,7 @@ class Filesystem * @param string $path * @return bool */ - public function isDir($path) + function isDir($path) { return is_dir($path); } @@ -23,7 +23,7 @@ public function isDir($path) * @param int $mode * @return void */ - public function mkdir($path, $owner = null, $mode = 0755) + function mkdir($path, $owner = null, $mode = 0755) { mkdir($path, $mode); @@ -40,7 +40,7 @@ public function mkdir($path, $owner = null, $mode = 0755) * @param int $mode * @return void */ - public function ensureDirExists($path, $owner = null, $mode = 0755) + function ensureDirExists($path, $owner = null, $mode = 0755) { if (! $this->isDir($path)) { $this->mkdir($path, $owner, $mode); @@ -54,7 +54,7 @@ public function ensureDirExists($path, $owner = null, $mode = 0755) * @param int $mode * @return void */ - public function mkdirAsUser($path, $mode = 0755) + function mkdirAsUser($path, $mode = 0755) { return $this->mkdir($path, user(), $mode); } @@ -64,15 +64,17 @@ public function mkdirAsUser($path, $mode = 0755) * * @param string $path * @param string|null $owner - * @return void + * @return string */ - public function touch($path, $owner = null) + function touch($path, $owner = null) { touch($path); if ($owner) { $this->chown($path, $owner); } + + return $path; } /** @@ -81,7 +83,7 @@ public function touch($path, $owner = null) * @param string $path * @return void */ - public function touchAsUser($path) + function touchAsUser($path) { return $this->touch($path, user()); } @@ -92,7 +94,7 @@ public function touchAsUser($path) * @param string $path * @return bool */ - public function exists($path) + function exists($path) { return file_exists($path); } @@ -103,7 +105,7 @@ public function exists($path) * @param string $path * @return string */ - public function get($path) + function get($path) { return file_get_contents($path); } @@ -116,7 +118,7 @@ public function get($path) * @param string|null $owner * @return string */ - public function put($path, $contents, $owner = null) + function put($path, $contents, $owner = null) { file_put_contents($path, $contents); @@ -132,7 +134,7 @@ public function put($path, $contents, $owner = null) * @param string $contents * @return string */ - public function putAsUser($path, $contents) + function putAsUser($path, $contents) { return $this->put($path, $contents, user()); } @@ -145,7 +147,7 @@ public function putAsUser($path, $contents) * @param string|null $owner * @return void */ - public function append($path, $contents, $owner = null) + function append($path, $contents, $owner = null) { file_put_contents($path, $contents, FILE_APPEND); @@ -161,7 +163,7 @@ public function append($path, $contents, $owner = null) * @param string $contents * @return void */ - public function appendAsUser($path, $contents) + function appendAsUser($path, $contents) { return $this->append($path, $contents, user()); } @@ -173,7 +175,7 @@ public function appendAsUser($path, $contents) * @param string $to * @return void */ - public function copy($from, $to) + function copy($from, $to) { copy($from, $to); } @@ -185,20 +187,36 @@ public function copy($from, $to) * @param string $to * @return void */ - public function copyAsUser($from, $to) + function copyAsUser($from, $to) { copy($from, $to); $this->chown($to, user()); } + /** + * Create a symlink to the given target. + * + * @param string $target + * @param string $link + * @return void + */ + function symlink($target, $link) + { + if ($this->exists($link)) { + $this->unlink($link); + } + + symlink($target, $link); + } + /** * Delete the file at the given path. * * @param string $path * @return void */ - public function unlink($path) + function unlink($path) { @unlink($path); } @@ -209,7 +227,7 @@ public function unlink($path) * @param string $path * @param string $user */ - public function chown($path, $user) + function chown($path, $user) { chown($path, $user); } @@ -220,7 +238,7 @@ public function chown($path, $user) * @param string $path * @param string $group */ - public function chgrp($path, $group) + function chgrp($path, $group) { chgrp($path, $group); } @@ -231,7 +249,7 @@ public function chgrp($path, $group) * @param string $path * @return string */ - public function realpath($path) + function realpath($path) { return realpath($path); } @@ -242,7 +260,7 @@ public function realpath($path) * @param string $path * @return bool */ - public function isLink($path) + function isLink($path) { return is_link($path); } @@ -253,8 +271,50 @@ public function isLink($path) * @param string $path * @return string */ - public function readLink($path) + function readLink($path) { - return read_link($path); + return readlink($path); + } + + /** + * Remove all of the broken symbolic links at the given path. + * + * @param string $path + * @return void + */ + function removeBrokenLinksAt($path) + { + collect($this->scandir($path)) + ->filter(function ($file) use ($path) { + return $this->isBrokenLink($path.'/'.$file); + }) + ->each(function ($file) use ($path) { + $this->unlink($path.'/'.$file); + }); + } + + /** + * Determine if the given path is a broken symbolic link. + * + * @param string $path + * @return bool + */ + function isBrokenLink($path) + { + return is_link($path) && ! file_exists($path); + } + + /** + * Scan the given directory path. + * + * @param string $path + * @return array + */ + function scandir($path) + { + return collect(scandir($path)) + ->reject(function ($file) { + return in_array($file, ['.', '..']); + })->values()->all(); } } diff --git a/src/PhpFpm.php b/src/PhpFpm.php index fd8b2a0..9f1391a 100644 --- a/src/PhpFpm.php +++ b/src/PhpFpm.php @@ -29,10 +29,9 @@ function __construct(Brew $brew, CommandLine $cli) /** * Install and configure DnsMasq. * - * @param OutputInterface $output * @return void */ - function install($output) + function install() { $this->brew->ensureInstalled('php70', $this->taps); diff --git a/src/Site.php b/src/Site.php index aea899a..e1b4a0b 100644 --- a/src/Site.php +++ b/src/Site.php @@ -2,31 +2,43 @@ namespace Valet; -use Exception; +use DomainException; class Site { + var $config, $files; + + /** + * Create a new Site instance. + * + * @param Configuration $config + * @param Filesystem $files + * @return void + */ + function __construct(Configuration $config, Filesystem $files) + { + $this->files = $files; + $this->config = $config; + } + /** * Link the current working directory with the given name. * + * @param string $target * @param string $name * @return string */ - public static function link($name) + function link($target, $link) { - if (! is_dir($linkPath = VALET_HOME_PATH.'/Sites')) { - mkdir($linkPath, 0755); - } + $this->files->ensureDirExists( + $linkPath = $this->sitesPath(), user() + ); - Configuration::addPath($linkPath); + $this->config->prependPath($linkPath); - if (file_exists($linkPath.'/'.$name)) { - throw new Exception("A symbolic link with this name already exists."); - } + $this->files->symlink($target, $linkPath.'/'.$link); - symlink(getcwd(), $linkPath.'/'.$name); - - return $linkPath; + return $linkPath.'/'.$link; } /** @@ -35,11 +47,11 @@ public static function link($name) * @param string $name * @return void */ - public static function unlink($name) + function unlink($name) { - quietly('rm '.VALET_HOME_PATH.'/Sites/'.$name); - - return true; + if ($this->files->exists($path = $this->sitesPath().'/'.$name)) { + $this->files->unlink($path); + } } /** @@ -47,52 +59,43 @@ public static function unlink($name) * * @return void */ - public static function pruneLinks() + function pruneLinks() { - if (! is_dir(VALET_HOME_PATH.'/Sites')) { - return; - } - - foreach (scandir(VALET_HOME_PATH.'/Sites') as $file) { - if (in_array($file, ['.', '..'])) { - continue; - } - - if (is_link($linkPath = VALET_HOME_PATH.'/Sites/'.$file) && ! file_exists($linkPath)) { - quietly('rm '.$linkPath); - } + if ($this->files->isDir($sitesPath = $this->sitesPath())) { + $this->files->removeBrokenLinksAt($sitesPath); } } /** * Get all of the log files for all sites. * + * @param array $paths * @return array */ - public static function logs() + function logs($paths) { - $paths = Configuration::read()['paths']; - - $files = []; + $files = collect(); foreach ($paths as $path) { - foreach (scandir($path) as $directory) { + $files = $files->merge(collect($this->files->scandir($path))->map(function ($directory) use ($path) { $logPath = $path.'/'.$directory.'/storage/logs/laravel.log'; - if (in_array($directory, ['.', '..'])) { - continue; + if ($this->files->isDir(dirname($logPath))) { + return $this->files->touch($logPath); } - - if (file_exists($logPath)) { - $files[] = $logPath; - } elseif (is_dir(dirname($logPath))) { - touch($logPath); - - $files[] = $logPath; - } - } + })->filter()); } - return $files; + return $files->values()->all(); + } + + /** + * Get the path to the linked Valet sites. + * + * @return string + */ + function sitesPath() + { + return VALET_HOME_PATH.'/Sites'; } } diff --git a/tests/FilesystemTest.php b/tests/FilesystemTest.php new file mode 100644 index 0000000..0bd3762 --- /dev/null +++ b/tests/FilesystemTest.php @@ -0,0 +1,25 @@ +assertTrue(file_exists(__DIR__.'/output/file.link')); + unlink(__DIR__.'/output/file.out'); + $files->removeBrokenLinksAt(__DIR__.'/output'); + $this->assertFalse(file_exists(__DIR__.'/output/file.link')); + } +} diff --git a/tests/SiteTest.php b/tests/SiteTest.php new file mode 100644 index 0000000..263368f --- /dev/null +++ b/tests/SiteTest.php @@ -0,0 +1,84 @@ +shouldReceive('ensureDirExists')->once()->with(VALET_HOME_PATH.'/Sites', user()); + $config = Mockery::mock(Configuration::class); + $config->shouldReceive('prependPath')->once()->with(VALET_HOME_PATH.'/Sites'); + $files->shouldReceive('symlink')->once()->with('target', VALET_HOME_PATH.'/Sites/link'); + + swap(Filesystem::class, $files); + swap(Configuration::class, $config); + + $linkPath = resolve(Site::class)->link('target', 'link'); + $this->assertEquals(VALET_HOME_PATH.'/Sites/link', $linkPath); + } + + + public function test_unlink_removes_existing_symlink() + { + file_put_contents(__DIR__.'/output/file.out', 'test'); + symlink(__DIR__.'/output/file.out', __DIR__.'/output/link'); + $site = resolve(StubForRemovingLinks::class); + $site->unlink('link'); + $this->assertFalse(file_exists(__DIR__.'/output/link')); + + $site = resolve(StubForRemovingLinks::class); + $site->unlink('link'); + $this->assertFalse(file_exists(__DIR__.'/output/link')); + } + + + public function test_prune_links_removes_broken_symlinks_in_sites_path() + { + file_put_contents(__DIR__.'/output/file.out', 'test'); + symlink(__DIR__.'/output/file.out', __DIR__.'/output/link'); + unlink(__DIR__.'/output/file.out'); + $site = resolve(StubForRemovingLinks::class); + $site->pruneLinks(); + $this->assertFalse(file_exists(__DIR__.'/output/link')); + } + + + public function test_logs_method_returns_array_of_log_files() + { + $logs = resolve(Site::class)->logs([__DIR__.'/test-directory-for-logs']); + $this->assertEquals(__DIR__.'/test-directory-for-logs/project/storage/logs/laravel.log', $logs[0]); + unlink(__DIR__.'/test-directory-for-logs/project/storage/logs/laravel.log'); + } +} + + +class StubForRemovingLinks extends Site +{ + function sitesPath() + { + return __DIR__.'/output'; + } +} diff --git a/tests/test-directory-for-logs/project/storage/logs/.gitkeep b/tests/test-directory-for-logs/project/storage/logs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/valet.php b/valet.php index f370dad..a247a76 100755 --- a/valet.php +++ b/valet.php @@ -11,6 +11,14 @@ } use Silly\Application; +use Valet\Facades\Brew; +use Valet\Facades\Site; +use Valet\Facades\Caddy; +use Valet\Facades\PhpFpm; +use Valet\Facades\DnsMasq; +use Valet\Facades\Filesystem; +use Valet\Facades\CommandLine; +use Valet\Facades\Configuration; use Illuminate\Container\Container; /** @@ -24,28 +32,28 @@ * Prune missing directories and symbolic links on every command. */ if (is_dir(VALET_HOME_PATH)) { - Valet\Configuration::prune(); + Configuration::prune(); - Valet\Site::pruneLinks(); + Site::pruneLinks(); } /** * Allow Valet to be run more conveniently by allowing the Node proxy to run password-less sudo. */ -$app->command('install', function ($output) { +$app->command('install', function () { should_be_sudo(); - Valet\Caddy::stop(); + Caddy::stop(); - Valet\Configuration::install(); + Configuration::install(); - Valet\Caddy::install(); + Caddy::install(); - Valet\PhpFpm::install($output); + PhpFpm::install(); - Valet\DnsMasq::install($output); + DnsMasq::install(); - Valet\Caddy::restart(); + Caddy::restart(); output(PHP_EOL.'Valet installed successfully!'); }); @@ -53,52 +61,52 @@ /** * Change the domain currently being used by Valet. */ -$app->command('domain domain', function ($domain, $output) { +$app->command('domain domain', function ($domain) { should_be_sudo(); $domain = trim($domain, '.'); - Valet\DnsMasq::updateDomain(Valet\Configuration::read()['domain'], $domain); + DnsMasq::updateDomain(Configuration::read()['domain'], $domain); - Valet\Configuration::updateKey('domain', $domain); + Configuration::updateKey('domain', $domain); - $output->writeln('Your Valet domain has been updated to ['.$domain.'].'); + output('Your Valet domain has been updated to ['.$domain.'].'); }); /** * Get the domain currently being used by Valet. */ -$app->command('current-domain', function ($output) { - $output->writeln(Valet\Configuration::read()['domain']); +$app->command('current-domain', function () { + output(Configuration::read()['domain']); }); /** * Add the current working directory to the paths configuration. */ -$app->command('park', function ($output) { - Valet\Configuration::addPath(getcwd()); +$app->command('park', function () { + Configuration::addPath(getcwd()); - $output->writeln("This directory has been added to Valet's paths."); + output("This directory has been added to Valet's paths."); }); /** * Remove the current working directory to the paths configuration. */ -$app->command('forget', function ($output) { - Valet\Configuration::removePath(getcwd()); +$app->command('forget', function () { + Configuration::removePath(getcwd()); - $output->writeln("This directory has been removed from Valet's paths."); + output("This directory has been removed from Valet's paths."); }); /** * Register a symbolic link with Valet. */ -$app->command('link [name]', function ($name, $output) { +$app->command('link [name]', function ($name) { $name = $name ?: basename(getcwd()); - $linkPath = Valet\Site::link($name); + $linkPath = Site::link(getcwd(), $name); - $output->writeln('A ['.$name.'] symbolic link has been created in ['.$linkPath.'].'); + output('A ['.$name.'] symbolic link has been created in ['.$linkPath.'].'); }); /** @@ -111,62 +119,60 @@ /** * Unlink a link from the Valet links directory. */ -$app->command('unlink [name]', function ($name, $output) { +$app->command('unlink [name]', function ($name) { $name = $name ?: basename(getcwd()); - if (Valet\Site::unlink($name)) { - $output->writeln('The ['.$name.'] symbolic link has been removed.'); - } else { - $output->writeln('A symbolic link with this name does not exist.'); - } + Site::unlink($name); + + output('The ['.$name.'] symbolic link has been removed.'); }); /** * Determine which Valet driver the current directory is using. */ -$app->command('which', function ($output) { +$app->command('which', function () { require __DIR__.'/drivers/require.php'; $driver = ValetDriver::assign(getcwd(), basename(getcwd()), '/'); if ($driver) { - $output->writeln('This site is served by ['.get_class($driver).'].'); + output('This site is served by ['.get_class($driver).'].'); } else { - $output->writeln('Valet could not determine which driver to use for this site.'); + output('Valet could not determine which driver to use for this site.'); } }); /** * Stream all of the logs for all sites. */ -$app->command('logs', function ($output) { - $files = Valet\Site::logs(); +$app->command('logs', function () { + $files = Site::logs(Configuration::read()['paths']); if (count($files) > 0) { passthru('tail -f '.implode(' ', $files)); } else { - $output->writeln('No log files were found.'); + output('No log files were found.'); } }); /** * Display all of the registered paths. */ -$app->command('paths', function ($output) { - $paths = Valet\Configuration::read()['paths']; +$app->command('paths', function () { + $paths = Configuration::read()['paths']; if (count($paths) > 0) { - $output->writeln(json_encode($paths, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); + output(json_encode($paths, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); } else { - $output->writeln('No paths have been registered.'); + output('No paths have been registered.'); } }); /** * Echo the currently tunneled URL. */ -$app->command('fetch-share-url', function ($output) { - retry(20, function () use ($output) { +$app->command('fetch-share-url', function () { + retry(20, function () { $response = Httpful\Request::get('http://127.0.0.1:4040/api/tunnels')->send(); $body = $response->body; @@ -186,48 +192,48 @@ /** * Start the daemon services. */ -$app->command('start', function ($output) { +$app->command('start', function () { should_be_sudo(); - Valet\PhpFpm::restart(); - Valet\Caddy::restart(); + PhpFpm::restart(); + Caddy::restart(); - $output->writeln('Valet services have been started.'); + output('Valet services have been started.'); }); /** * Restart the daemon services. */ -$app->command('restart', function ($output) { +$app->command('restart', function () { should_be_sudo(); - Valet\PhpFpm::restart(); - Valet\Caddy::restart(); + PhpFpm::restart(); + Caddy::restart(); - $output->writeln('Valet services have been restarted.'); + output('Valet services have been restarted.'); }); /** * Stop the daemon services. */ -$app->command('stop', function ($output) { +$app->command('stop', function () { should_be_sudo(); - Valet\PhpFpm::stop(); - Valet\Caddy::stop(); + PhpFpm::stop(); + Caddy::stop(); - $output->writeln('Valet services have been stopped.'); + output('Valet services have been stopped.'); }); /** * Uninstall Valet entirely. */ -$app->command('uninstall', function ($output) { +$app->command('uninstall', function () { should_be_sudo(); - Valet\Caddy::uninstall(); + Caddy::uninstall(); - $output->writeln('Valet has been uninstalled.'); + output('Valet has been uninstalled.'); }); /**