Skip to content

Validation

In your config you can set a fileValidator to be used:

php
'FileStorage' => [
    'behaviorConfig' => [
        'fileValidator' => \App\FileStorage\Validator\ImageValidator::class,
    ],
],

Such a class implements FileStorage\Model\Validation\UploadValidatorInterface:

php
namespace App\FileStorage\Validator;

use Cake\Validation\Validator;
use FileStorage\Model\Validation\ImageValidationTrait;
use FileStorage\Model\Validation\UploadValidationTrait;
use FileStorage\Model\Validation\UploadValidatorInterface;

class ImageValidator implements UploadValidatorInterface
{
    use UploadValidationTrait;
    use ImageValidationTrait;

    public function configure(Validator $validator): void
    {
        $validator->setProvider('upload', static::class);

        $validator->add('file', 'fileUnderPhpSizeLimit', [
            'rule' => 'isUnderPhpSizeLimit',
            'message' => 'This file is too large',
            'provider' => 'upload',
        ]);

        // Validate that the file is actually an image before checking dimensions
        $validator->add('file', 'fileIsValidImage', [
            'rule' => 'isValidImage',
            'message' => 'File must be a valid image (JPEG, PNG, GIF, or WebP)',
            'provider' => 'upload',
            'on' => function ($context) {
                $file = $context['data']['file'] ?? null;

                return $file && $file->getError() === UPLOAD_ERR_OK;
            },
        ]);

        $validator->add('file', 'fileAboveMinHeight', [
            'rule' => ['isAboveMinHeight', 50],
            'message' => 'This image should at least be 50px high',
            'provider' => 'upload',
            'on' => function ($context) {
                $file = $context['data']['file'] ?? null;

                return $file && $file->getError() === UPLOAD_ERR_OK;
            },
        ]);
        $validator->add('file', 'fileAboveMinWidth', [
            'rule' => ['isAboveMinWidth', 50],
            'message' => 'This image should at least be 50px wide',
            'provider' => 'upload',
            'on' => function ($context) {
                $file = $context['data']['file'] ?? null;

                return $file && $file->getError() === UPLOAD_ERR_OK;
            },
        ]);
    }
}

Available upload validation rules

The UploadValidationTrait provides the following validation methods. They all accept either a PSR-7 UploadedFileInterface or the legacy $_FILES-style array (['tmp_name' => …, 'name' => …, 'type' => …, 'size' => …, 'error' => …]).

File-error checks

RulePurpose
isUnderPhpSizeLimitFalse when the upload hit PHP's upload_max_filesize (UPLOAD_ERR_INI_SIZE).
isUnderFormSizeLimitFalse when the upload hit the HTML form's MAX_FILE_SIZE (UPLOAD_ERR_FORM_SIZE).
isCompletedUploadFalse when the upload was truncated (UPLOAD_ERR_PARTIAL).
isFileUploadFalse when no file was supplied (UPLOAD_ERR_NO_FILE).
isSuccessfulWriteFalse when the temp file could not be written (UPLOAD_ERR_CANT_WRITE).

Size checks

RulePurpose
isAboveMinSize($check, $size)True when uploaded size ≥ $size bytes.
isBelowMaxSize($check, $size)True when uploaded size ≤ $size bytes.

Allow-list checks

Recommended for any upload that is not strictly an image.

Don't trust client headers

The client-supplied name / type (Content-Type) headers are not trustworthy — a request can claim image/jpeg while delivering a .php shell. The MIME helper sniffs server-side via finfo against the actual file contents by default, so callers get the value PHP detects, not what the user claimed.

php
// Restrict by extension (case-insensitive, leading dot tolerant).
$validator->add('file', 'fileExtensionAllowed', [
    'rule' => ['hasAllowedExtension', ['jpg', 'jpeg', 'png', 'webp']],
    'message' => 'Only JPEG, PNG, or WebP images are allowed.',
    'provider' => 'upload',
]);

// Restrict by sniffed MIME type (DEFAULT). Sniffs the actual file contents.
$validator->add('file', 'fileMimeAllowed', [
    'rule' => ['hasAllowedMimeType', ['image/jpeg', 'image/png', 'image/webp']],
    'message' => 'File contents must be a JPEG, PNG, or WebP image.',
    'provider' => 'upload',
]);

// Same, but trust the client header instead of sniffing. Only safe inside
// trusted code paths (tests, internal-only forms). Disable the sniff with a
// false third argument:
$validator->add('file', 'fileMimeAllowedClient', [
    'rule' => ['hasAllowedMimeType', ['image/jpeg'], false],
    'message' => '…',
    'provider' => 'upload',
]);

For images, ImageValidationTrait::isValidImage() (below) is a stricter check — it actually parses the image header — and should be preferred over hasAllowedMimeType when you only accept images.

Available image validation rules

The ImageValidationTrait provides the following validation methods.

isValidImage

Validates that the uploaded file is a valid image. Use this before dimension checks to produce clear error messages when non-image files are uploaded.

php
// Default: allows JPEG, PNG, GIF, WebP
$validator->add('file', 'fileIsValidImage', [
    'rule' => 'isValidImage',
    'message' => 'File must be a valid image',
    'provider' => 'upload',
]);

// Custom allowed types
$validator->add('file', 'fileIsValidImage', [
    'rule' => ['isValidImage', [IMAGETYPE_JPEG, IMAGETYPE_PNG]],
    'message' => 'File must be a JPEG or PNG image',
    'provider' => 'upload',
]);

Dimension validators

  • isAboveMinWidth($check, int $width) — check minimum width.
  • isBelowMaxWidth($check, int $width) — check maximum width.
  • isAboveMinHeight($check, int $height) — check minimum height.
  • isBelowMaxHeight($check, int $height) — check maximum height.

All dimension validators safely handle non-image files by returning false instead of throwing an error.

Released under the MIT License.