orkhanahmadov/laravel-zip-validator

View on GitHub
src/Validator.php

Summary

Maintainability
A
55 mins
Test Coverage
<?php

namespace Orkhanahmadov\ZipValidator;

use Illuminate\Support\Collection;
use Orkhanahmadov\ZipValidator\Exceptions\ZipException;
use ZipArchive;

class Validator
{
    /**
     * @var Collection
     */
    private $files;
    /**
     * @var bool
     */
    private $allowEmpty;

    /**
     * ZipValidator constructor.
     *
     * @param array|string $files
     * @param bool $allowEmpty
     */
    public function __construct($files, bool $allowEmpty = true)
    {
        $this->files = Collection::make($files);
        $this->allowEmpty = $allowEmpty;
    }

    /**
     * Static function instantiate Validator class with given rules
     *
     * @param array|string $files
     *
     * @param bool $allowEmpty
     *
     * @return Validator
     */
    public static function rules($files, bool $allowEmpty = true): Validator
    {
        return new static($files, $allowEmpty);
    }

    /**
     * Validates ZIP content with given file path.
     *
     * @param string $filePath
     *
     * @return Collection
     */
    public function validate(string $filePath): Collection
    {
        $zipContent = $this->readContent($filePath);

        return $this->files
            ->reject(function ($value, $key) use ($zipContent) {
                return $this->checkFile($zipContent, $value, $key);
            })->map(function ($value, $key) {
                return is_int($key) ? $value : $key;
            });
    }

    /**
     * @param Collection $zipContent
     * @param string|int $value
     * @param string|int $key
     *
     * @return bool
     */
    public function checkFile(Collection $zipContent, $value, $key): bool
    {
        if (! is_int($value)) {
            $entityName = $this->contains($zipContent->pluck('name'), $value);

            if ($this->allowEmpty) {
                return (bool) $entityName;
            }

            return $zipContent->firstWhere('name', $entityName)['size'] > 0;
        }

        $entityName = $this->contains($zipContent->pluck('name'), $key);
        if (! $entityName) {
            return false;
        }

        $entitySize = $zipContent->firstWhere('name', $entityName)['size'];

        if ($this->allowEmpty) {
            return $entitySize <= $value;
        }

        return $entitySize > 0 && $entitySize <= $value;
    }

    /**
     * Checks if file name exists in ZIP file. Returns matching file name, null otherwise.
     *
     * @param Collection $names
     * @param string $search
     *
     * @return string|null
     */
    public function contains(Collection $names, string $search): ?string
    {
        $options = explode('|', $search);

        return $names->first(function ($name) use ($options) {
            foreach ($options as $option) {
                if (fnmatch($option, $name)) {
                    return true;
                }
            }
            return false;
        });
    }

    /**
     * Reads ZIP file content and returns collection with result.
     *
     * @param string $filePath
     *
     * @return Collection
     */
    private function readContent(string $filePath): Collection
    {
        $zip = new ZipArchive();
        $zipOpen = $zip->open($filePath);
        throw_unless($zipOpen === true, new ZipException($zipOpen));

        $content = collect();
        for ($i = 0; $i < $zip->count(); $i++) {
            $content->add($zip->statIndex($i));
        }

        $zip->close();

        return $content;
    }
}