Skip to content

Instantly share code, notes, and snippets.

@sasin91
Created September 25, 2018 15:53
Show Gist options
  • Save sasin91/0c3485bae60d6d5ec90dcb169934cdf5 to your computer and use it in GitHub Desktop.
Save sasin91/0c3485bae60d6d5ec90dcb169934cdf5 to your computer and use it in GitHub Desktop.
Create archives with a pretty fluent syntax
<?php
namespace App;
use Illuminate\Contracts\Filesystem\Filesystem;
use Illuminate\Support\Facades\Storage;
use Phar;
use PharData;
use function is_dir;
use function is_file;
use function is_null;
use function starts_with;
use function str_contains;
use function str_random;
use function strstr;
use function substr;
use function unlink;
/**
* Class Archive
*
* @package \App
* @api (new Archive)->containing($arrayOfFiles)->storeOn($laravelDiskName)->storeIn($directory)->storeAs($filename)->create() or forceCreate()
*/
class Archive
{
/**
* The files to stuff in the archive
*
* @var array
*/
protected $files = [];
/**
* The archive format
*
* @var string
*/
protected $format;
/**
* The target file name
*
* @var string
*/
protected $targetFilename = null;
/**
* Store the archive in target directory
*
* @var string
*/
protected $targetDirectory = null;
/**
* Filesystem disk name
*
* @var string
*/
protected $disk = null;
/**
* The filesystem adapter for the disk
*
* @var Filesystem
*/
private $storage;
/**
* Whether to force create the archive
*
* @var bool
*/
private $forceCreating = false;
/**
* Archive constructor.
*
* @param array $files
* @param string $format
*/
public function __construct(array $files = [], string $format = 'tar.gz')
{
$this->files = $files;
$this->format = $format;
}
/**
* Set the files to compress
*
* @param array $files
* @return Archive
*/
public function containing(array $files): Archive
{
$this->files = $files;
return $this;
}
/**
* Set the target file name
*
* @param string $targetFilename
* @return Archive
*/
public function storeAs(string $targetFilename): Archive
{
// When the target file name contains an extension,
// strip that format and use it as Archive.format
if (str_contains($targetFilename, '.')) {
$this->format(strstr($targetFilename, '.'));
$this->targetFilename = strstr($targetFilename, '.', true);
return $this;
}
$this->targetFilename = $targetFilename;
return $this;
}
/**
* Set the archive format
*
* @param string $format
* @return Archive
*/
public function format(string $format): Archive
{
if (starts_with($format, '.')) {
$format = substr($format, 1);
}
$this->format = $format;
return $this;
}
/**
* Set the target directory
*
* @param string $targetDirectory
* @return Archive
*/
public function storeIn(string $targetDirectory): Archive
{
$this->targetDirectory = $targetDirectory;
return $this;
}
/**
* Set the filesystem disk name
*
* @param string $disk
* @return Archive
*/
public function storeOn(string $disk): Archive
{
$this->disk = $disk;
return $this;
}
/**
* Force creating the archive
*
* @return string
*/
public function forceCreate(): string
{
return $this->useForce(true)->create();
}
/**
* Create the archive then return the path to it
*
* @return string
*/
public function create(): string
{
if (is_null($this->targetFilename)) {
$this->targetFilename = $this->makeFileName();
}
switch ($this->format) {
case 'tar.gz':
$archivePath = $this->fullTargetFilePath();
if (is_file("{$archivePath}.tar.gz") && $this->isForceCreating()) {
unlink("{$archivePath}.tar.gz");
}
$archive = $this->compressIntoTarArchive($archivePath);
$archive->compress(Phar::GZ);
$this->storage()->delete("{$this->targetFilename}.tar");
return "{$this->targetFilename}.tar.gz";
break;
default:
$this->compressIntoTarArchive();
return "{$this->targetFilename}.tar";
break;
}
}
private function makeFileName(): string
{
return str_random();
}
private function fullTargetFilePath(): string
{
$targetDirectory = $this->fullTargetDirectory();
return "{$targetDirectory}/{$this->targetFilename}";
}
private function fullTargetDirectory(): string
{
$storageRoot = config("filesystems.disks.{$this->disk}.root");
if (is_null($this->targetDirectory)) {
return $storageRoot;
}
if (is_dir($this->targetDirectory)) {
return $this->targetDirectory;
}
return "{$storageRoot}/{$this->targetDirectory}";
}
/**
* Whether we're forcing archive creation
*
* @return bool
*/
public function isForceCreating(): bool
{
return $this->forceCreating;
}
protected function compressIntoTarArchive(string $archivePath = null): PharData
{
$archivePath = $archivePath ?? $this->fullTargetFilePath();
$archive = new PharData("{$archivePath}.tar");
$this->addFilesToArchive($archive);
return $archive;
}
private function addFilesToArchive($archive): void
{
if ($archive instanceof PharData) {
$this->addFilesToPharArchive($archive);
}
}
private function addFilesToPharArchive(PharData $archive): void
{
foreach ($this->files as $file) {
$archive->addFromString($file, $this->storage()->get($file));
}
}
protected function storage(): Filesystem
{
if ($this->storage) {
return $this->storage;
}
return $this->storage = Storage::disk($this->disk);
}
/**
* Determine whether to force creating the archive
*
* @param bool $force
* @return Archive
*/
public function useForce(bool $force = true): Archive
{
$this->forceCreating = $force;
return $this;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment