Skip to content

Instantly share code, notes, and snippets.

@RhysLees
Forked from emsifa/AssetsScannerCommand.php
Created December 5, 2023 23:57
Show Gist options
  • Save RhysLees/3d7ecb994c7ef44a9976d2024d20010c to your computer and use it in GitHub Desktop.
Save RhysLees/3d7ecb994c7ef44a9976d2024d20010c to your computer and use it in GitHub Desktop.
Laravel Command for Scan Used/Unused Asset Files
<?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