diff --git a/bin/pdf-finder.php b/bin/pdf-finder.php index 34efa0b..9269716 100755 --- a/bin/pdf-finder.php +++ b/bin/pdf-finder.php @@ -3,7 +3,7 @@ use IO\ExceptionHandler; use IO\Input\FinderArguments; -use IO\Output\DocumentListingOutput; +use IO\Output\DocumentListing; use PDF\Document; require_once __DIR__ . '/../vendor/autoload.php'; @@ -23,4 +23,4 @@ foreach ($filters as $filter) { $documents = $documents->filter(fn(Document $document) => $filter->allows($document)); } -DocumentListingOutput::forDocuments($documents)->render(); +DocumentListing::of($documents)->render(); diff --git a/bin/pdf-show-info.php b/bin/pdf-show-info.php index 695cf09..325fd00 100755 --- a/bin/pdf-show-info.php +++ b/bin/pdf-show-info.php @@ -3,17 +3,12 @@ use IO\ExceptionHandler; use IO\Input\ShowInfoArguments; -use IO\Output\DocumentOutput; +use IO\Output\DocumentDetails; require_once __DIR__ . '/../vendor/autoload.php'; ExceptionHandler::registerCallback(); -$arguments = ShowInfoArguments::createFromGlobals(); -$file = $arguments->getFile(); - -$documentFactory = DocumentFactory::create(); -$document = $documentFactory->createDocument($file); - -$output = DocumentOutput::forDocument($document); -$output->render(); +$file = ShowInfoArguments::createFromGlobals()->getFile(); +$document = DocumentFactory::create()->fromFile($file); +DocumentDetails::of($document)->render(); diff --git a/src/DocumentFactory.php b/src/DocumentFactory.php index 3c14172..3b1a582 100644 --- a/src/DocumentFactory.php +++ b/src/DocumentFactory.php @@ -1,5 +1,6 @@ pdfinfo->getMetadata($file); return new Document($file, $metadata); diff --git a/src/Filter/FilterFactory.php b/src/Filter/FilterParser.php similarity index 59% rename from src/Filter/FilterFactory.php rename to src/Filter/FilterParser.php index bdcb634..ba6781c 100644 --- a/src/Filter/FilterFactory.php +++ b/src/Filter/FilterParser.php @@ -2,12 +2,12 @@ namespace Filter; -class FilterFactory +class FilterParser { - public function createFromString(string $string): DocumentFilter + public function parse(string $string): DocumentFilter { if (preg_match('/^.+=.*$/', $string)) { - [$prop, $term] = explode('=', $string); + [$prop, $term] = explode('=', $string, 2); return new SpecificFilter(trim($prop), trim($term)); } return new GenericFilter($string); diff --git a/src/IO/Filesystem/Directory.php b/src/IO/Filesystem/Directory.php new file mode 100644 index 0000000..c03ec31 --- /dev/null +++ b/src/IO/Filesystem/Directory.php @@ -0,0 +1,42 @@ +directory = rtrim($directory, DIRECTORY_SEPARATOR); + $this->guardUnusableDirectory($directory); + $this->directory = realpath($directory); + } + + public static function fromString(?string $directory): self + { + return new self($directory ?? getcwd()); + } + + public function __toString(): string + { + return $this->directory; + } + + private function guardUnusableDirectory(string $directory): void + { + if (!file_exists($directory)) { + throw new DirectoryNotFoundException($directory); + } + if (!is_dir($directory)) { + throw new NotADirectoryException($directory); + } + if (!is_readable($directory)) { + throw new DirectoryNotReadableException($directory); + } + } +} diff --git a/src/IO/Filesystem/File.php b/src/IO/Filesystem/File.php new file mode 100644 index 0000000..77c6cd1 --- /dev/null +++ b/src/IO/Filesystem/File.php @@ -0,0 +1,44 @@ +guardUnusableFile($filepath); + $filepath = realpath($filepath); + $this->info = new SplFileInfo($filepath); + } + + public static function fromString(string $filepath): self + { + return new self($filepath); + } + + public function getInfo(): SplFileInfo + { + return $this->info; + } + + public function __toString(): string + { + return (string)$this->getInfo(); + } + + private function guardUnusableFile(string $file): void + { + if (!file_exists($file)) { + throw new FileNotFoundException($file); + } + if (!is_readable($file)) { + throw new FileNotReadableException($file); + } + } +} diff --git a/src/IO/Input/ArgvAccess.php b/src/IO/Input/ArgvAccess.php index 439297e..d422946 100644 --- a/src/IO/Input/ArgvAccess.php +++ b/src/IO/Input/ArgvAccess.php @@ -4,13 +4,13 @@ namespace IO\Input; trait ArgvAccess { - protected static function getArguments(): array + protected static function getScriptArgs(): array { - // Get local copy of $argv + // local copy of $argv global $argv; $arguments = $argv; - // Lose the script name + // shift off the script name array_shift($arguments); return $arguments; diff --git a/src/IO/Input/FinderArguments.php b/src/IO/Input/FinderArguments.php index f1c568c..f62e777 100644 --- a/src/IO/Input/FinderArguments.php +++ b/src/IO/Input/FinderArguments.php @@ -3,38 +3,32 @@ namespace IO\Input; use Filter\DocumentFilter; -use Filter\FilterFactory; -use IO\Exception\DirectoryNotFoundException; -use IO\Exception\DirectoryNotReadableException; -use IO\Exception\NotADirectoryException; +use Filter\FilterParser; +use IO\Filesystem\Directory; class FinderArguments { use ArgvAccess; - private ?string $directory; + private Directory $directory; private array $filters; public static function createFromGlobals(): self { - $arguments = self::getArguments(); + $arguments = self::getScriptArgs(); + $directory = array_shift($arguments); - $dir = array_shift($arguments) ?? getcwd(); - $dir = rtrim($dir, DIRECTORY_SEPARATOR); - - return new self($dir, $arguments); + return new self($directory, $arguments); } public function __construct(?string $directory, array $filters) { - $this->guardUnusableDirectory($directory); - $this->directory = realpath($directory); - - $factory = new FilterFactory(); - $this->filters = array_map([$factory, 'createFromString'], $filters); + $factory = new FilterParser(); + $this->directory = Directory::fromString($directory); + $this->filters = array_map([$factory, 'parse'], $filters); } - public function getDirectory(): string + public function getDirectory(): Directory { return $this->directory; } @@ -46,17 +40,4 @@ class FinderArguments { return $this->filters; } - - private function guardUnusableDirectory(string $directory): void - { - if (!file_exists($directory)) { - throw new DirectoryNotFoundException($directory); - } - if (!is_dir($directory)) { - throw new NotADirectoryException($directory); - } - if (!is_readable($directory)) { - throw new DirectoryNotReadableException($directory); - } - } } diff --git a/src/IO/Input/ShowInfoArguments.php b/src/IO/Input/ShowInfoArguments.php index f591d34..8dbcb27 100644 --- a/src/IO/Input/ShowInfoArguments.php +++ b/src/IO/Input/ShowInfoArguments.php @@ -2,44 +2,31 @@ namespace IO\Input; -use IO\Exception\FileNotFoundException; -use IO\Exception\FileNotReadableException; use IO\Exception\MissingFileArgumentException; -use SplFileInfo; +use IO\Filesystem\File; class ShowInfoArguments { use ArgvAccess; - private SplFileInfo $file; + private File $file; public static function createFromGlobals(): self { - $arguments = self::getArguments(); + $arguments = self::getScriptArgs(); return new self(array_shift($arguments)); } - public function __construct(?string $file) + public function __construct(?string $filepath) { - $this->guardUnusableFile($file); - $this->file = new SplFileInfo($file); + if (is_null($filepath)) { + throw new MissingFileArgumentException(); + } + $this->file = File::fromString($filepath); } - public function getFile(): SplFileInfo + public function getFile(): File { return $this->file; } - - private function guardUnusableFile(string $file): void - { - if (is_null($file)) { - throw new MissingFileArgumentException(); - } - if (!file_exists($file)) { - throw new FileNotFoundException($file); - } - if (!is_readable($file)) { - throw new FileNotReadableException($file); - } - } } diff --git a/src/IO/Output/DocumentOutput.php b/src/IO/Output/DocumentDetails.php similarity index 84% rename from src/IO/Output/DocumentOutput.php rename to src/IO/Output/DocumentDetails.php index 1747d6c..2a9733e 100644 --- a/src/IO/Output/DocumentOutput.php +++ b/src/IO/Output/DocumentDetails.php @@ -5,16 +5,16 @@ namespace IO\Output; use PDF\Document; use Symfony\Component\Console\Output\OutputInterface; -class DocumentOutput implements Output +class DocumentDetails implements Output { private Document $document; - public function __construct(Document $document) + private function __construct(Document $document) { $this->document = $document; } - public static function forDocument(Document $document): self + public static function of(Document $document): self { return new self($document); } diff --git a/src/IO/Output/DocumentListingOutput.php b/src/IO/Output/DocumentListing.php similarity index 85% rename from src/IO/Output/DocumentListingOutput.php rename to src/IO/Output/DocumentListing.php index b773a71..c5e6076 100644 --- a/src/IO/Output/DocumentListingOutput.php +++ b/src/IO/Output/DocumentListing.php @@ -5,17 +5,17 @@ namespace IO\Output; use PDF\Document; use Symfony\Component\Console\Output\OutputInterface; -class DocumentListingOutput implements Output +class DocumentListing implements Output { /** @var Document[] */ private iterable $documents; - public function __construct(iterable $documents) + private function __construct(iterable $documents) { $this->documents = $documents; } - public static function forDocuments(iterable $documents): self + public static function of(iterable $documents): self { return new self($documents); } @@ -55,10 +55,10 @@ class DocumentListingOutput implements Output foreach ($this->documents as $document) { $template->addRow([ - $document->file->getBasename(), + $document->file->getInfo()->getBasename(), $document->metadata->title, $document->metadata->author, - $document->file->getPath(), + $document->file->getInfo()->getPath(), ]); } diff --git a/src/IO/Shell/Pdfinfo.php b/src/IO/Shell/Pdfinfo.php index ee0f3a0..57bf1d1 100644 --- a/src/IO/Shell/Pdfinfo.php +++ b/src/IO/Shell/Pdfinfo.php @@ -13,13 +13,11 @@ class Pdfinfo $lines = $this->shellExec('pdfinfo', '-isodates', $filepath); $data = []; - foreach ($lines as $line) { - $parts = explode(':', $line, 2); - if (count($parts) === 2) { - $data[trim($parts[0])] = trim($parts[1]); - } - } + collect($lines) + ->map(fn(string $line) => explode(':', $line, 2)) + ->filter(fn(array $parts) => count($parts) === 2) + ->each(fn(array $parts) => $data[trim($parts[0])] = trim($parts[1])); - return (new Metadata)->fillWith($data); + return Metadata::fill($data); } } diff --git a/src/PDF/Document.php b/src/PDF/Document.php index 41a2edf..0ab4fbb 100644 --- a/src/PDF/Document.php +++ b/src/PDF/Document.php @@ -2,28 +2,28 @@ namespace PDF; +use IO\Filesystem\File; use RuntimeException; -use SplFileInfo; class Document { - public SplFileInfo $file; + public File $file; public Metadata $metadata; - public function __construct(SplFileInfo $file, ?Metadata $metadata = null) + public function __construct(File $file, Metadata $metadata) { $this->file = $file; - $this->metadata = $metadata ?? new Metadata(); + $this->metadata = $metadata; } public function getProperty(string $prop): ?string { if (in_array($prop, ['path', 'filepath'])) { - return $this->file->getPath(); + return (string)$this->file; } if (in_array($prop, ['file', 'name', 'filename'])) { - return $this->file->getBasename(); + return $this->file->getInfo()->getBasename(); } if (property_exists($this->metadata, $prop)) { @@ -35,9 +35,11 @@ class Document public function getProperties(): array { + $info = $this->file->getInfo(); + return [ - 'filepath' => $this->file->getPath(), - 'filename' => $this->file->getBasename(), + 'filepath' => $info->getPath(), + 'filename' => $info->getBasename(), ] + $this->metadata->toArray(); } } diff --git a/src/PDF/Metadata.php b/src/PDF/Metadata.php index b946f61..ba1acfa 100644 --- a/src/PDF/Metadata.php +++ b/src/PDF/Metadata.php @@ -31,19 +31,28 @@ class Metadata public ?string $title = null; public ?string $userproperties = null; - public function fillWith(array $array): Metadata + private function __construct(array $array) { $slugify = new Slugify(['separator' => '_']); + $notEmpty = static fn(string $v) => trim($v) !== ''; - $array = array_filter($array, static fn(string $v) => trim($v) !== ''); + $array = array_filter($array, $notEmpty); foreach ($array as $key => $value) { $key = $slugify->slugify($key); if (property_exists(__CLASS__, $key)) { $this->{$key} = trim($value); } } + } - return $this; + public static function fill(array $data): Metadata + { + return new self($data); + } + + public static function empty(): Metadata + { + return new self([]); } public function toArray(): array diff --git a/src/RecursiveDocumentLocator.php b/src/RecursiveDocumentLocator.php index a7ca008..85fe22d 100644 --- a/src/RecursiveDocumentLocator.php +++ b/src/RecursiveDocumentLocator.php @@ -25,7 +25,7 @@ class RecursiveDocumentLocator $documents = []; foreach ($iterator as $file) { if ($this->validate($file)) { - $documents[] = $this->documentFactory->createDocument($file); + $documents[] = $this->documentFactory->fromFile($file); } }