From 47c5d1be51512b2022866ed4ff708a7d95b72e53 Mon Sep 17 00:00:00 2001 From: Julien Rosset Date: Wed, 9 Aug 2023 20:24:19 +0200 Subject: [PATCH] Add validator for directory and file --- .../Validators/DirectoryValidator.php | 115 ++++++++++++++++++ .../Validation/Validators/FileValidator.php | 72 +++++++++++ .../Validators/FilesystemValidationOption.php | 12 ++ tests/Commands/ReadFile.php | 47 +++++++ tests/file.txt | 1 + 5 files changed, 247 insertions(+) create mode 100644 src/CliProgram/Validation/Validators/DirectoryValidator.php create mode 100644 src/CliProgram/Validation/Validators/FileValidator.php create mode 100644 src/CliProgram/Validation/Validators/FilesystemValidationOption.php create mode 100644 tests/Commands/ReadFile.php create mode 100644 tests/file.txt diff --git a/src/CliProgram/Validation/Validators/DirectoryValidator.php b/src/CliProgram/Validation/Validators/DirectoryValidator.php new file mode 100644 index 0000000..c4ecdf3 --- /dev/null +++ b/src/CliProgram/Validation/Validators/DirectoryValidator.php @@ -0,0 +1,115 @@ + The options + */ + private ICollection $options; + + /** + * Initialization + * + * @param ICollection|int[]|int|null $options The options + */ + public function __construct ($options = null) { + $this->setOptions($options); + } + + /** + * Get a valid default value from the initial/given default value + * + * @param string|Stringable|null $default The initial/given default value + * + * @return string|bool|int|float|array|null The valid default value + * + * @throws InvalidArgumentException If the default value is not a string or null + * @throws InvalidArgumentException If the default directory doesn't match the options + */ + public function getValidDefault ($default) { + if ($default === null) { + return null; + } + if (!is_string($default) && !$default instanceof Stringable) { + throw new InvalidArgumentException('The default value must be a string or null'); + } + + if ($this->getOptions()->contains(FilesystemValidationOption::IS_READABLE) && !is_readable($default)) { + throw new InvalidArgumentException('The default value is not readable'); + } + if ($this->getOptions()->contains(FilesystemValidationOption::IS_WRITABLE) && !is_writable($default)) { + throw new InvalidArgumentException('The default value is not writable'); + } + if ($this->getOptions()->contains(FilesystemValidationOption::MUST_EXISTS) && !file_exists($default)) { + throw new InvalidArgumentException('The default value doesn\'t exist'); + } + return $default; + } + + /** + * @inheritDoc + */ + public function validate ($value): bool { + if (!is_string($value) && !$value instanceof Stringable) { + return false; + } + + if ($this->getOptions()->contains(FilesystemValidationOption::IS_READABLE) && !is_readable($value)) { + return false; + } + if ($this->getOptions()->contains(FilesystemValidationOption::IS_WRITABLE) && !is_writable($value)) { + return false; + } + if ($this->getOptions()->contains(FilesystemValidationOption::MUST_EXISTS) && !file_exists($value)) { + return false; + } + + $this->setValue($value); + return true; + } + + /** + * The options + * + * @return ICollection The options + */ + public function getOptions (): ICollection { + return $this->options; + } + /** + * Replace the options + * + * @param ICollection|int[]|int|null $options The options + * + * @return $this + */ + public function setOptions ($options = null): self { + if ($options instanceof ICollection) { + $this->options = $options; + } + elseif (is_int($options)) { + $this->options = new Collection([$options]); + } + elseif (is_array($options)) { + $this->options = new Collection($options); + } + elseif ($options === null) { + $this->options = new Collection(); + } + else { + throw new InvalidArgumentException(); + } + return $this; + } +} \ No newline at end of file diff --git a/src/CliProgram/Validation/Validators/FileValidator.php b/src/CliProgram/Validation/Validators/FileValidator.php new file mode 100644 index 0000000..d9dfbd3 --- /dev/null +++ b/src/CliProgram/Validation/Validators/FileValidator.php @@ -0,0 +1,72 @@ +|int[]|int|null $options The options + */ + public function __construct (?string $extensionsPattern = null, $options = null) { + parent::__construct($options); + $this->setExtensionsPattern($extensionsPattern); + } + + /** + * @inheritDoc + */ + public function getValidDefault ($default) { + if (($default = parent::getValidDefault($default)) === null) { + return null; + } + if ($this->extensionsPattern !== null && preg_match($this->extensionsPattern, $default) !== 1) { + throw new InvalidArgumentException('The default value has not a valid extension'); + } + return $default; + } + /** + * @inheritDoc + */ + public function validate ($value): bool { + if (!parent::validate($value)) { + return false; + } + if ($this->extensionsPattern !== null && preg_match($this->extensionsPattern, $value) !== 1) { + return false; + } + return true; + } + + /** + * The allowed extensions (regex pattern) + * + * @return string|null The allowed extensions (regex pattern) + */ + public function getExtensionsPattern (): ?string { + return $this->extensionsPattern; + } + /** + * Replace the allowed extensions + * + * @param string|null $extensionsPattern The allowed extensions (regex pattern) + * + * @return $this + */ + public function setExtensionsPattern (?string $extensionsPattern = null): self { + $this->extensionsPattern = $extensionsPattern; + return $this; + } +} \ No newline at end of file diff --git a/src/CliProgram/Validation/Validators/FilesystemValidationOption.php b/src/CliProgram/Validation/Validators/FilesystemValidationOption.php new file mode 100644 index 0000000..f1714ee --- /dev/null +++ b/src/CliProgram/Validation/Validators/FilesystemValidationOption.php @@ -0,0 +1,12 @@ +addArgument( + 'file', + InputArgument::REQUIRED, + 'The file to read', + null, + new FileValidator( + /** @lang PhpRegExp */ '#\.txt$#i', + FilesystemValidationOption::IS_READABLE + ) + ); + } + + /** + * @inheritDoc + */ + protected function execute (InputInterface $input, OutputInterface $output): int { + if (($fileContent = file_get_contents($input->getArgument('file'))) === false) { + return Command::FAILURE; + } + $output->writeln($fileContent); + return Command::SUCCESS; + } +} \ No newline at end of file diff --git a/tests/file.txt b/tests/file.txt new file mode 100644 index 0000000..91aaf46 --- /dev/null +++ b/tests/file.txt @@ -0,0 +1 @@ +This is a file \ No newline at end of file