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);
        foreach (['fileUnderPhpSizeLimit', 'fileIsValidImage', 'fileAboveMinHeight', 'fileAboveMinWidth'] as $rule) {
            $validator->remove('file', $rule);
        }

        $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;
            },
        ]);
    }
}

configure() can run more than once while an entity is marshalled and saved. Replace your own named rules before adding them again, as above; duplicate rule names throw on current CakePHP versions. Do not remove unrelated app rules. Remove an empty associated upload from request data before validation if the form permits keeping an existing file.

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.