IsType

The IsType validation ensures that a request parameter matches a specific basic type. It supports the built-in BasicTypes enumeration, covering common cases like Integer, Float, Boolean, String, Array, Object, and Null.

This validation is particularly useful for APIs that need to guarantee type safety before controller logic executes.

Class Definition

namespace PHPNomad\Rest\Validations;

use PHPNomad\Rest\Interfaces\Validation;
use PHPNomad\Rest\Traits\WithProvidedErrorMessage;
use PHPNomad\Http\Interfaces\Request;
use PHPNomad\Rest\Enums\BasicTypes;

class IsType implements Validation

Usage in a Controller

Here’s an example endpoint that requires an integer userId and a boolean isActive flag. Both fields are validated before the controller runs:

<?php

use PHPNomad\Http\Enums\Method;
use PHPNomad\Http\Interfaces\Request;
use PHPNomad\Http\Interfaces\Response;

use PHPNomad\Rest\Interfaces\Controller;
use PHPNomad\Rest\Interfaces\HasValidations;
use PHPNomad\Rest\Interfaces\HasMiddleware;

use PHPNomad\Rest\Factories\ValidationSet;
use PHPNomad\Rest\Middleware\ValidationMiddleware;
use PHPNomad\Rest\Middleware\SetTypeMiddleware;
use PHPNomad\Rest\Validations\IsType;
use PHPNomad\Rest\Enums\BasicTypes;

final class UpdateUserStatus implements Controller, HasValidations, HasMiddleware
{
    public function __construct(private Response $response, private UserService $users) {}

    public function getEndpoint(): string { return '/users/{userId}/status'; }
    public function getMethod(): string   { return Method::Post; }

    public function getResponse(Request $request): Response
    {
        // At this point, inputs passed validation.
        // userId was coerced to int by middleware, isActive validated strictly and we cast now.
        $id       = (int)  $request->getParam('userId');
        $isActive = (bool) $request->getParam('isActive');

        $this->users->setActiveStatus($id, $isActive);

        return $this->response
            ->setStatus(200)
            ->setJson(['id' => $id, 'isActive' => $isActive]);
    }

    /** Declare validations for each field */
    public function getValidations(): array
    {
        return [
            'userId' => (new ValidationSet())
                ->setRequired()
                ->addValidation(fn() => new IsType(BasicTypes::Integer)),

            // IMPORTANT: Do not coerce first; let IsType(Boolean) validate the literal input.
            'isActive' => (new ValidationSet())
                ->setRequired()
                ->addValidation(fn() => new IsType(BasicTypes::Boolean)),
        ];
    }

    /** Compose middleware: coerce userId to int, then run validations */
    public function getMiddleware(Request $request): array
    {
        return [
            new SetTypeMiddleware('userId', BasicTypes::Integer),
            // Validation must come after any coercion middleware.
            new ValidationMiddleware($this),
        ];
    }
}

Error Type and Context

Example Failure Response

If the client passes "userId": "abc" and "isActive": "yes", the response might look like this:

{
  "error": {
    "message": "Validations failed.",
    "context": {
      "userId": [
        {
          "field": "userId",
          "message": "userId must be a Integer, was given abc",
          "type": "INVALID_TYPE",
          "context": {
            "requiredType": "Integer"
          }
        }
      ],
      "isActive": [
        {
          "field": "isActive",
          "message": "isActive must be a Boolean, was given yes",
          "type": "INVALID_TYPE",
          "context": {
            "requiredType": "Boolean"
          }
        }
      ]
    }
  }
}

Absolutely — great idea to show type coercion + validation together. One nuance: coercing booleans with settype() can accidentally turn any non-empty string into true. So in the example below we **coerce the integer ** (userId) with SetTypeMiddleware, but we do not coerce the boolean (isActive) before validation; we let IsType(Boolean) validate strings like "true", "false", "1", "0" strictly.

Here’s an updated section you can drop into the IsType docs.


Using IsType with Middleware (coercion + validation)

Below, the endpoint requires:

<?php

use PHPNomad\Http\Enums\Method;
use PHPNomad\Http\Interfaces\Request;
use PHPNomad\Http\Interfaces\Response;

use PHPNomad\Rest\Interfaces\Controller;
use PHPNomad\Rest\Interfaces\HasValidations;
use PHPNomad\Rest\Interfaces\HasMiddleware;

use PHPNomad\Rest\Factories\ValidationSet;
use PHPNomad\Rest\Middleware\ValidationMiddleware;
use PHPNomad\Rest\Middleware\SetTypeMiddleware;
use PHPNomad\Rest\Validations\IsType;
use PHPNomad\Rest\Enums\BasicTypes;

final class UpdateUserStatus implements Controller, HasValidations, HasMiddleware
{
    public function __construct(private Response $response, private UserService $users) {}

    public function getEndpoint(): string { return '/users/{userId}/status'; }
    public function getMethod(): string   { return Method::Post; }

    public function getResponse(Request $request): Response
    {
        // At this point, inputs passed validation.
        // userId was coerced to int by middleware, isActive validated strictly and we cast now.
        $id       = (int)  $request->getParam('userId');
        $isActive = (bool) $request->getParam('isActive');

        $this->users->setActiveStatus($id, $isActive);

        return $this->response
            ->setStatus(200)
            ->setJson(['id' => $id, 'isActive' => $isActive]);
    }

    /** Declare validations for each field */
    public function getValidations(): array
    {
        return [
            'userId' => (new ValidationSet())
                ->setRequired()
                ->addValidation(fn() => new IsType(BasicTypes::Integer)),

            // IMPORTANT: Do not coerce first; let IsType(Boolean) validate the literal input.
            'isActive' => (new ValidationSet())
                ->setRequired()
                ->addValidation(fn() => new IsType(BasicTypes::Boolean)),
        ];
    }

    /** Compose middleware: coerce userId to int, then run validations */
    public function getMiddleware(Request $request): array
    {
        return [
            new SetTypeMiddleware('userId', BasicTypes::Integer),
            new ValidationMiddleware($this),
        ];
    }
}

Example request (success)

POST /users/42/status
Content-Type: application/json

{ "isActive": "true" }
{
  "id": 42,
  "isActive": true
}

Example request (failure)

POST /users/42/status
Content-Type: application/json

{ "isActive": "perhaps" }
{
  "error": {
    "message": "Validations failed.",
    "context": {
      "isActive": [
        {
          "field": "isActive",
          "message": "isActive must be a Boolean, was given perhaps",
          "type": "INVALID_TYPE",
          "context": {
            "requiredType": "Boolean"
          }
        }
      ]
    }
  }
}