gkralik/php-uglifyjs
PHP port of Dean Edwards’ JavaScript Packer for minifying/packing JS. Install via Composer and compress a script with GK\JavascriptPacker->pack(), choosing encoding (0/10/62/95), optional fast decoder, and special chars handling.
Installation:
composer require gkralik/php-uglifyjs
Add to require-dev if only needed for local development or testing.
First Use Case: Minify a JavaScript string or file in a Laravel controller or Blade template:
use GK\JavascriptPacker;
// Minify a string
$script = 'console.log("Hello");';
$packer = new JavascriptPacker($script);
$minified = $packer->pack();
// Minify a file
$filePath = public_path('js/custom.js');
$jsContent = file_get_contents($filePath);
$minified = (new JavascriptPacker($jsContent))->pack();
file_put_contents($filePath, $minified);
Where to Look First:
$encoding (e.g., 62 for balanced compression) and $fastDecode (set false if decoder bloat is a concern).(new JavascriptPacker($script))->pack() for quick tests.95 for maximum compression but larger output).Create a custom Blade directive to minify JS snippets dynamically:
// app/Providers/BladeServiceProvider.php
use Illuminate\Support\Facades\Blade;
use GK\JavascriptPacker;
public function boot()
{
Blade::directive('packJs', function ($expression) {
return "<?php echo (new GK\\JavascriptPacker({$expression}))->pack(); ?>";
});
}
Usage in Blade:
@packJs($scriptVariable)
Register the packer as a singleton in AppServiceProvider:
public function register()
{
$this->app->singleton('jsPacker', function () {
return new JavascriptPacker('', 62, true, false); // Default config
});
}
Usage:
$minified = app('jsPacker')->pack($yourScript);
Compress JS payloads in API responses (e.g., for SPAs or dynamic JS):
// app/Http/Middleware/CompressJsMiddleware.php
public function handle($request, Closure $next)
{
$response = $next($request);
if ($response->headers->get('Content-Type') === 'application/javascript') {
$response->setContent(app('jsPacker')->pack($response->getContent()));
}
return $response;
}
Register in app/Http/Kernel.php:
protected $middleware = [
// ...
\App\Http\Middleware\CompressJsMiddleware::class,
];
Use the package as a custom Webpack loader (advanced):
// webpack.mix.js
const UglifyJS = require('gkralik/php-uglifyjs').default;
mix.webpackConfig({
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'raw-loader',
options: {
esModule: false,
minimize: (content) => {
return new UglifyJS(content, 62, true, false).pack();
}
}
}
}
]
}
});
Note: Requires Node.js and may not work directly; use as a fallback for PHP-only environments.
Offload compression for CPU-intensive tasks (e.g., admin dashboards):
// app/Jobs/CompressJsJob.php
public function handle()
{
$script = file_get_contents(storage_path('js/large-script.js'));
$minified = (new JavascriptPacker($script, 95))->pack();
file_put_contents(storage_path('js/large-script.min.js'), $minified);
}
Dispatch in a controller:
CompressJsJob::dispatch()->onQueue('compression');
Minify JS generated dynamically (e.g., for user-specific scripts):
// Controller
public function getUserScript()
{
$script = "var userId = {$this->user->id}; // ...";
return response($script)->header('Content-Type', 'application/javascript');
}
Middleware (as shown above) can auto-compress this.
ES6+ Incompatibility:
const, let, arrow functions, template literals, etc.@babel/preset-env.try-catch to fallback to original:
try {
return app('jsPacker')->pack($script);
} catch (\Exception $e) {
return $script; // Fallback
}
Encoding Level Trade-offs:
0 (None): No compression, but smallest output size (includes decoder).62 (Normal): Balanced (default). Use for most cases.95 (High ASCII): Maximum compression but largest output (includes decoder).62 first; switch to 95 only if file size is critical.Decoder Bloat:
$fastDecode=true (default) adds a decoder to the output, increasing size.$fastDecode=false if decoder isn’t needed (e.g., for static assets).Special Characters Flag:
$specialChars=true breaks if JS uses non-standard variable names (e.g., var 1var = 1;).false unless you’ve explicitly flagged private variables.Performance Overhead:
hirenwood/laravel-debugbar:
\Debugbar::info('JS Compression Time', microtime(true) - $start);
XSS Risks:
eval injection).Unmaintained Package:
declare(strict_types=1)).Validate Input: Check for malformed JS before packing:
if (!is_string($script) || empty(trim($script))) {
throw new \InvalidArgumentException('Invalid JS script');
}
Log Errors:
Wrap pack() calls to log failures:
try {
$minified = app('jsPacker')->pack($script);
} catch (\Exception $e) {
\Log::error("JS Compression Failed: {$e->getMessage()}", ['script' => substr($script, 0, 100)]);
$minified = $script; // Fallback
}
Compare Outputs:
Use diff to compare minified vs. original:
diff <(echo "$original") <(echo "$minified") | wc -l
Test Edge Cases:
Custom Encoding Strategies: Extend the packer to support additional encoding levels:
class CustomPacker extends JavascriptPacker {
public function packWithCustomEncoding($script) {
return parent::pack($script, 'CustomLevel');
}
}
Pre/Post-Processing: Chain with other tools (e.g., remove comments first):
$script = preg_replace('/\/\/.*|\/\*[\s\S]*?\*\//', '', $script);
$minified = app('jsPacker')->pack($script);
Caching Layer: Add Redis caching to avoid reprocessing:
$cacheKey = 'js:minified
How can I help you explore Laravel packages today?