alchemy/zippy
Zippy is a PHP library to read, create, list, and extract archives using CLI tools or PHP extensions. Supports ZIP plus GNU/BSD tar formats (.tar, .tar.gz, .tar.bz2) via a simple API for opening, iterating members, extracting, and creating archives.
Installation:
composer require alchemy/zippy
Add to composer.json if using Laravel's autoloader:
"autoload": {
"psr-4": {
"App\\": "app/",
"Alchemy\\Zippy\\": "vendor/alchemy/zippy/src/"
}
}
Basic Usage:
use Alchemy\Zippy\Zippy;
$zippy = Zippy::load();
$archive = $zippy->open('path/to/file.zip');
$archive->extract(storage_path('app/extracted'));
$archive = $zippy->create('backup.zip', [
'folder' => storage_path('app'),
'file.txt' => fopen('path/to/file.txt', 'r'),
]);
Leverage Zippy’s adapter system to handle .zip, .tar, .tar.gz, and .tar.bz2 without switching libraries:
$zippy = Zippy::load(['adapter' => 'zip']); // Force ZIP adapter
// or
$zippy = Zippy::load(['adapter' => 'tar']); // Force TAR adapter
Use teleporters (e.g., Guzzle) to fetch and extract archives directly from URLs:
$archive = $zippy->open('https://example.com/archive.zip');
$archive->extract(storage_path('app/remote'));
Map local paths to custom names inside the archive:
$archive = $zippy->create('custom.zip', [
'public/images' => 'assets/images', // Rename directory
'README.md' => 'documentation/README.md', // Custom path
]);
$archive = $zippy->open('archive.zip');
foreach ($archive as $member) {
if (str_contains($member, 'config/')) {
$member->extract(storage_path('app/config'));
}
}
Use Zippy in Laravel controllers/services:
// In a Laravel service
public function backupDatabase()
{
$zippy = Zippy::load();
$archive = $zippy->create(storage_path('app/backup/backup.zip'), [
'database' => database_path(),
]);
return $archive->close();
}
Create a custom Artisan command for archive operations:
// app/Console/Commands/MakeArchive.php
use Alchemy\Zippy\Zippy;
class MakeArchive extends Command
{
protected $signature = 'archive:create {name} {--path=}';
public function handle()
{
$zippy = Zippy::load();
$archive = $zippy->create(
storage_path("app/{$this->argument('name')}.zip"),
[$this->option('path') ?: 'storage/app' => '.']
);
$this->info("Archive created: {$archive->getPathname()}");
}
}
Listen for file uploads and auto-archive them:
// In a Laravel event listener
use Alchemy\Zippy\Zippy;
public function handle(FileUploaded $event)
{
$zippy = Zippy::load();
$archive = $zippy->create(
storage_path('app/uploads/archive.zip'),
[$event->file->path() => 'uploads/' . $event->file->getClientOriginalName()]
);
}
Offload archive creation to a queue job:
// app/Jobs/CreateArchiveJob.php
use Alchemy\Zippy\Zippy;
class CreateArchiveJob implements ShouldQueue
{
public function handle()
{
$zippy = Zippy::load();
$archive = $zippy->create('large_archive.zip', [
'huge_directory' => storage_path('app/huge_directory'),
]);
}
}
Mock Zippy in PHPUnit tests:
$mockArchive = $this->createMock(Archive::class);
$mockArchive->method('extract')->willReturn(true);
$zippy = $this->createMock(Zippy::class);
$zippy->method('open')->willReturn($mockArchive);
$archive->extract(realpath('/tmp/extracted'));
Filesystem to set permissions post-extraction:
$files = File::allFiles(storage_path('app/extracted'));
foreach ($files as $file) {
File::chmod($file, 0644);
}
mbstring extension is enabled (Zippy requires it).zip or tar CLI tools) or chunked processing.zip extension may not be available.
Fix: Fall back to CLI tools (e.g., zip or tar commands):
$zippy = Zippy::load(['adapter' => 'zip-cli']); // Use system zip tool
$archive = $zippy->open('archive.zip');
foreach ($archive as $member) {
Log::debug("Member: {$member->getPathname()}");
}
$zippy = Zippy::load();
Log::info('Supported adapters:', $zippy->getSupportedAdapters());
Wrap Zippy operations in try-catch blocks:
try {
$archive = $zippy->open('nonexistent.zip');
} catch (ArchiveException $e) {
Log::error("Archive error: " . $e->getMessage());
}
Extend Zippy to support additional formats (e.g., .7z):
// app/Adapters/CustomAdapter.php
use Alchemy\Zippy\Adapter\AdapterInterface;
class CustomAdapter implements AdapterInterface
{
public function open($pathname) { /* ... */ }
public function create($pathname, array $resources) { /* ... */ }
}
Register the adapter in Zippy::load():
$zippy = Zippy::load(['adapters' => ['custom' => new CustomAdapter()]]);
Hook into archive events (e.g., before extraction):
$archive->on('extract', function ($member, $target) {
Log::info("Extracting: {$member->getPathname()} to {$target}");
});
Customize Zippy’s behavior globally:
$zippy = Zippy::load([
'adapter' => 'zip',
'options' => [
'compression_level' => 9, // Max compression
'exclude' => ['*.log', 'node_modules/'],
],
]);
Bind Zippy to the container for dependency injection:
// app/Providers/AppServiceProvider.php
public function register()
{
$this->app->singleton(Zippy::class, function () {
return Zippy::load();
});
}
If system tools (zip, tar) are not in PATH, specify their paths:
$zippy = Zippy::load([
'adapter' => 'zip-cli',
'options' => ['zip_path' => '/usr/local/bin/zip'],
]);
For large archives, increase PHP’s memory limit:
ini_set('memory_limit', '512M');
$zippy = Zippy::load();
Ensure consistent timestamps in archives:
date_default_timezone_set('UTC');
$archive = $zippy->create('archive.zip', [...]);
How can I help you explore Laravel packages today?