Last active
December 5, 2023 23:57
-
-
Save emsifa/8785185036db11f1fa2a to your computer and use it in GitHub Desktop.
Laravel Command for Scan Used/Unused Asset Files
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
use Illuminate\Console\Command; | |
use Symfony\Component\Console\Input\InputOption; | |
use Symfony\Component\Console\Input\InputArgument; | |
class AssetsScannerCommand extends Command { | |
/** | |
* The console command name. | |
* | |
* @var string | |
*/ | |
protected $name = 'asset:scan'; | |
/** | |
* The console command description. | |
* | |
* @var string | |
*/ | |
protected $description = 'Find used/unused asset files in application'; | |
/** | |
* Create a new command instance. | |
* | |
* @return void | |
*/ | |
public function __construct() | |
{ | |
parent::__construct(); | |
} | |
/** | |
* Execute the console command. | |
* | |
* @return mixed | |
*/ | |
public function fire() | |
{ | |
$list_extensions = [ | |
'html', 'css', 'js', | |
'jpg', 'bmp', 'png', 'bmp', 'gif', | |
'mp3', 'mp4', 'mkv' | |
]; | |
$used_or_unused = $this->argument("used_or_unused"); | |
$scan_dir = app_path($this->option("dir")); | |
$extensions = $this->option("extensions"); | |
switch($this->option("remove")) { | |
case "all": $action = "actionRemove"; | |
break; | |
case "ask": $action = "actionAskRemove"; | |
break; | |
default: $action = "actionShow"; | |
} | |
if(!$extensions) { | |
$extensions = $list_extensions; | |
} else { | |
$extensions = explode('|', $extensions); | |
} | |
if(!in_array($used_or_unused, ['used', 'unused'])) { | |
throw new \InvalidArgumentException("First argument only can be 'used' or 'unused'"); | |
} | |
// scan php files in application directory | |
$php_files = $this->findRecursive($scan_dir, function($file) { | |
$ext = pathinfo($file, PATHINFO_EXTENSION); | |
if("php" == $ext) { | |
return $file; | |
} | |
}); | |
$assets = array(); | |
// scan use of asset() function in each php files | |
foreach($php_files as $php_file) { | |
$files = $this->scanUsedAssets($php_file); | |
$assets = array_merge($assets, $files); | |
} | |
// if unused, scan public dir, and then select inverse using array_diff | |
if("unused" === $used_or_unused) { | |
$all_assets = $this->scanPublicDir(); | |
$assets = array_diff($all_assets, $assets); | |
} | |
$assets = array_unique($assets, SORT_STRING); | |
// run actions | |
$n = 0; | |
foreach($assets as $i => $asset) { | |
$ext = pathinfo($asset, PATHINFO_EXTENSION); | |
if(in_array($ext, $extensions)) { | |
$this->{$action}($n+1, $asset); | |
$n++; | |
} | |
} | |
echo "\n"; | |
} | |
protected function actionAskRemove($n, $file) | |
{ | |
$sure = $this->ask(str_pad($n, 5, ' ', STR_PAD_LEFT)." > Want remove '{$file}'? [N|y]"); | |
if("y" === strtolower($sure)) { | |
unlink(public_path($file)); | |
} | |
} | |
protected function actionRemove($n, $file) | |
{ | |
$this->info(str_pad($n, 5, ' ', STR_PAD_LEFT)." > Removing '{$file}'"); | |
unlink(public_path($file)); | |
} | |
protected function actionShow($n, $file) | |
{ | |
$this->info(str_pad($n, 5, ' ', STR_PAD_LEFT)." > {$file}"); | |
} | |
protected function scanUsedAssets($file) | |
{ | |
$file_content = file_get_contents($file); | |
// http://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words | |
preg_match_all("/asset\([\'\"](?<filename>[a-zA-Z0-9.#@&()\[\]{}_ \/-]+)[\'\"]\)/", $file_content, $match); | |
$filenames = $match['filename']; | |
return $filenames; | |
} | |
protected function scanPublicDir() | |
{ | |
return $this->findRecursive(public_path(), function($file) { | |
return str_replace(public_path().'/', '', $file); | |
}); | |
} | |
protected function findRecursive($path, Closure $matcher) | |
{ | |
$files = array_diff(scandir($path), ['.','..']); | |
$matchs = array(); | |
foreach($files as $file) { | |
$filepath = $path.'/'.$file; | |
$matchname = $matcher($filepath); | |
if(!empty($matchname) AND !is_dir($filepath)) { | |
$matchs[] = $matchname; | |
} | |
if(is_dir($filepath)) { | |
$matchs = array_merge($matchs, $this->findRecursive($filepath, $matcher)); | |
} | |
} | |
return $matchs; | |
} | |
/** | |
* Get the console command arguments. | |
* | |
* @return array | |
*/ | |
protected function getArguments() | |
{ | |
return array( | |
array('used_or_unused', InputArgument::REQUIRED, 'Used or unused assets'), | |
); | |
} | |
/** | |
* Get the console command options. | |
* | |
* @return array | |
*/ | |
protected function getOptions() | |
{ | |
return array( | |
array('dir', 'd', InputOption::VALUE_OPTIONAL, 'Scanned directory from app (default is "views")', 'views'), | |
array('extensions', 'e', InputOption::VALUE_OPTIONAL, 'Showing only specified file extension, example "js|css|png"', null), | |
array('remove', 'r', InputOption::VALUE_OPTIONAL, 'Remove list assets, can be "all" or "ask"', null), | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment