diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 242206692..77026a1e9 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -10,20 +10,18 @@ permissions: contents: read # to fetch code (actions/checkout) jobs: - unit-tests: - - runs-on: ${{ matrix.os }} - strategy: matrix: php: [8.4, 8.3, 8.2] os: [ubuntu-latest] - steps: - - uses: actions/checkout@v2 + runs-on: ${{ matrix.os }} - - name: Setup PHP + steps: + - uses: actions/checkout@v4 + + - name: Setup PHP ${{ matrix.php }} uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} @@ -31,17 +29,11 @@ jobs: tools: composer:v2 coverage: none env: - COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }} - -# - name: Update composer -# run: composer update -# -# - name: Validate composer.json and composer.lock -# run: composer validate + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Get composer cache directory id: composer-cache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies uses: actions/cache@v4 diff --git a/system/src/Grav/Common/Data/BlueprintSchema.php b/system/src/Grav/Common/Data/BlueprintSchema.php index afb1fd7f0..553243c0b 100644 --- a/system/src/Grav/Common/Data/BlueprintSchema.php +++ b/system/src/Grav/Common/Data/BlueprintSchema.php @@ -196,6 +196,38 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface $messages += Validation::validate($child, $rule); + if (isset($rule['validate']['match']) || isset($rule['validate']['match_exact']) || isset($rule['validate']['match_any'])) { + $ruleKey = current(array_intersect(['match', 'match_exact', 'match_any'], array_keys($rule['validate']))); + $otherKey = $rule['validate'][$ruleKey] ?? null; + $otherVal = $data[$otherKey] ?? null; + $otherLabel = $this->items[$otherKey]['label'] ?? $otherKey; + $currentVal = $data[$key] ?? null; + $currentLabel = $this->items[$key]['label'] ?? $key; + + // Determine comparison type (loose, strict, substring) + // Perform comparison: + $isValid = false; + if ($ruleKey === 'match') { + $isValid = ($currentVal == $otherVal); + } elseif ($ruleKey === 'match_exact') { + $isValid = ($currentVal === $otherVal); + } elseif ($ruleKey === 'match_any') { + // If strings: + if (is_string($currentVal) && is_string($otherVal)) { + $isValid = (strlen($currentVal) && strlen($otherVal) && (str_contains($currentVal, + $otherVal) || strpos($otherVal, $currentVal) !== false)); + } + // If arrays: + if (is_array($currentVal) && is_array($otherVal)) { + $common = array_intersect($currentVal, $otherVal); + $isValid = !empty($common); + } + } + if (!$isValid) { + $messages[$rule['name']][] = sprintf(Grav::instance()['language']->translate('PLUGIN_FORM.VALIDATION_MATCH'), $currentLabel, $otherLabel); + } + } + } elseif (is_array($child) && is_array($val)) { // Array has been defined in blueprints. $messages += $this->validateArray($child, $val, $strict); diff --git a/system/src/Grav/Common/Data/Validation.php b/system/src/Grav/Common/Data/Validation.php index d6887d001..37b6e09b6 100644 --- a/system/src/Grav/Common/Data/Validation.php +++ b/system/src/Grav/Common/Data/Validation.php @@ -47,12 +47,14 @@ class Validation } $validate = (array)($field['validate'] ?? null); - $type = $validate['type'] ?? $field['type']; + $validate_type = $validate['type'] ?? null; $required = $validate['required'] ?? false; + $type = $validate_type ?? $field['type']; + + $required = $required && ($validate_type !== 'ignore'); // If value isn't required, we will stop validation if empty value is given. - if ($required !== true && ($value === null || $value === '' || (($field['type'] === 'checkbox' || $field['type'] === 'switch') && $value == false)) - ) { + if ($required !== true && ($value === null || $value === '' || empty($value) || (($field['type'] === 'checkbox' || $field['type'] === 'switch') && $value == false))) { return []; } diff --git a/system/src/Grav/Framework/Collection/AbstractLazyCollection.php b/system/src/Grav/Framework/Collection/AbstractLazyCollection.php index f2572780a..045685eb2 100644 --- a/system/src/Grav/Framework/Collection/AbstractLazyCollection.php +++ b/system/src/Grav/Framework/Collection/AbstractLazyCollection.php @@ -26,7 +26,7 @@ abstract class AbstractLazyCollection extends BaseAbstractLazyCollection impleme * @par ArrayCollection * @phpstan-var ArrayCollection */ - protected ?\Doctrine\Common\Collections\Collection $collection = null; + protected $collection; /** * {@inheritDoc} diff --git a/system/src/Grav/Framework/Form/FormFlashFile.php b/system/src/Grav/Framework/Form/FormFlashFile.php index 00a9fd948..306401ade 100644 --- a/system/src/Grav/Framework/Form/FormFlashFile.php +++ b/system/src/Grav/Framework/Form/FormFlashFile.php @@ -16,11 +16,14 @@ use InvalidArgumentException; use JsonSerializable; use Psr\Http\Message\StreamInterface; use Psr\Http\Message\UploadedFileInterface; +use ReturnTypeWillChange; use RuntimeException; use function copy; use function fopen; use function is_string; use function sprintf; +use const UPLOAD_ERR_NO_FILE; +use const UPLOAD_ERR_OK; /** * Class FormFlashFile @@ -29,11 +32,15 @@ use function sprintf; class FormFlashFile implements UploadedFileInterface, JsonSerializable { /** @var string */ - private $id; + private string $id; + /** @var string */ + private string $field; /** @var bool */ - private $moved = false; + private bool $moved = false; + /** @var array */ + private array $upload; /** @var FormFlash */ - private $flash; + private FormFlash $flash; /** * FormFlashFile constructor. @@ -41,14 +48,16 @@ class FormFlashFile implements UploadedFileInterface, JsonSerializable * @param array $upload * @param FormFlash $flash */ - public function __construct(private readonly string $field, private array $upload, FormFlash $flash) + public function __construct(string $field, array $upload, FormFlash $flash) { $this->id = $flash->getId() ?: $flash->getUniqueId(); + $this->field = $field; + $this->upload = $upload; $this->flash = $flash; $tmpFile = $this->getTmpFile(); if (!$tmpFile && $this->isOk()) { - $this->upload['error'] = \UPLOAD_ERR_NO_FILE; + $this->upload['error'] = UPLOAD_ERR_NO_FILE; } if (!isset($this->upload['size'])) { @@ -59,7 +68,7 @@ class FormFlashFile implements UploadedFileInterface, JsonSerializable /** * @return StreamInterface */ - public function getStream() + public function getStream(): StreamInterface { $this->validateActive(); @@ -80,7 +89,7 @@ class FormFlashFile implements UploadedFileInterface, JsonSerializable * @param string $targetPath * @return void */ - public function moveTo($targetPath) + public function moveTo($targetPath): void { $this->validateActive(); @@ -120,7 +129,7 @@ class FormFlashFile implements UploadedFileInterface, JsonSerializable /** * @return int */ - public function getSize() + public function getSize(): int { return $this->upload['size']; } @@ -128,15 +137,15 @@ class FormFlashFile implements UploadedFileInterface, JsonSerializable /** * @return int */ - public function getError() + public function getError(): int { - return $this->upload['error'] ?? \UPLOAD_ERR_OK; + return $this->upload['error'] ?? UPLOAD_ERR_OK; } /** * @return string */ - public function getClientFilename() + public function getClientFilename(): string { return $this->upload['name'] ?? 'unknown'; } @@ -144,7 +153,7 @@ class FormFlashFile implements UploadedFileInterface, JsonSerializable /** * @return string */ - public function getClientMediaType() + public function getClientMediaType(): string { return $this->upload['type'] ?? 'application/octet-stream'; } @@ -172,7 +181,7 @@ class FormFlashFile implements UploadedFileInterface, JsonSerializable /** * @return string */ - public function getDestination() + public function getDestination(): string { return $this->upload['path'] ?? ''; } @@ -180,8 +189,8 @@ class FormFlashFile implements UploadedFileInterface, JsonSerializable /** * @return array */ - #[\ReturnTypeWillChange] - public function jsonSerialize() + #[ReturnTypeWillChange] + public function jsonSerialize(): array { return $this->upload; } @@ -220,7 +229,7 @@ class FormFlashFile implements UploadedFileInterface, JsonSerializable /** * @return array */ - #[\ReturnTypeWillChange] + #[ReturnTypeWillChange] public function __debugInfo() { return [ @@ -255,6 +264,6 @@ class FormFlashFile implements UploadedFileInterface, JsonSerializable */ private function isOk(): bool { - return \UPLOAD_ERR_OK === $this->getError(); + return UPLOAD_ERR_OK === $this->getError(); } }