1. Go to this page and download the library: Download duyler/openapi library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
use Duyler\OpenApi\Builder\OpenApiValidatorInterface;
class UserService
{
public function __construct(
private readonly OpenApiValidatorInterface $validator,
) {}
public function handleRequest(ServerRequestInterface $request): void
{
$operation = $this->validator->validateRequest($request);
// $operation->path template path, e.g. "/users/{id}"
// $operation->method matched HTTP method
// $operation->operationId operationId from the spec (nullable)
// $operation->pathParameters resolved values, e.g. ['id' => '42']
// $operation->schemaOperation Schema\Model\Operation reference (nullable)
$userId = $operation->pathParameters['id'] ?? null;
// ...
}
}
use Duyler\OpenApi\Builder\OpenApiValidatorBuilder;
// From YAML file
$validator = OpenApiValidatorBuilder::create()
->fromYamlFile('openapi.yaml')
->build();
// From JSON file
$validator = OpenApiValidatorBuilder::create()
->fromJsonFile('openapi.json')
->build();
// From YAML string
$yaml = file_get_contents('openapi.yaml');
$validator = OpenApiValidatorBuilder::create()
->fromYamlString($yaml)
->build();
// From JSON string
$json = file_get_contents('openapi.json');
$validator = OpenApiValidatorBuilder::create()
->fromJsonString($json)
->build();
use Duyler\OpenApi\Validator\Schema\RefResolver;
use Duyler\OpenApi\Validator\Schema\ExternalRefResolverInterface;
final class MyHttpExternalRefResolver implements ExternalRefResolverInterface
{
public function resolve(string $ref): \Duyler\OpenApi\Schema\Model\Schema
{
// fetch $ref over HTTP, return Schema
}
}
$refResolver = new RefResolver(new MyHttpExternalRefResolver());
use Duyler\OpenApi\Validator\Schema\FileExternalRefResolver;
$resolver = new FileExternalRefResolver(allowedRoot: '/var/specs');
use Duyler\OpenApi\Builder\OpenApiValidatorBuilder;
// /var/specs/openapi.yaml referring to /etc/passwd via $ref would now
// raise UnresolvableRefException at resolution time.
$validator = OpenApiValidatorBuilder::create()
->fromYamlFile('/var/specs/openapi.yaml')
->build();
use Duyler\OpenApi\Validator\Format\FormatValidatorInterface;
use Duyler\OpenApi\Validator\Exception\InvalidFormatException;
// Create a custom validator
class PhoneNumberValidator implements FormatValidatorInterface
{
public function validate(mixed $data): void
{
if (!is_string($data) || !preg_match('/^\+?[1-9]\d{1,14}$/', $data)) {
throw new InvalidFormatException(
'phone',
$data,
'Value must be a valid E.164 phone number'
);
}
}
}
// Register with the builder
$validator = OpenApiValidatorBuilder::create()
->fromYamlFile('openapi.yaml')
->withFormat('string', 'phone', new PhoneNumberValidator())
->build();
use Duyler\OpenApi\Event\ValidationStartedEvent;
use Duyler\OpenApi\Event\ValidationFinishedEvent;
use Duyler\OpenApi\Event\ValidationErrorEvent;
use Duyler\OpenApi\Event\ValidationWarningEvent;
use Duyler\OpenApi\Event\ArrayDispatcher;
$dispatcher = new ArrayDispatcher([
ValidationStartedEvent::class => [
function (ValidationStartedEvent $event) {
error_log(sprintf(
"Validation started: %s %s",
$event->method,
$event->path
));
},
],
ValidationFinishedEvent::class => [
function (ValidationFinishedEvent $event) {
if ($event->success) {
error_log(sprintf(
"Validation completed in %.3f seconds",
$event->duration
));
}
},
],
ValidationErrorEvent::class => [
function (ValidationErrorEvent $event) {
error_log(sprintf(
"Validation failed for %s %s: %s",
$event->method,
$event->path,
$event->exception->getMessage()
));
},
],
ValidationWarningEvent::class => [
function (ValidationWarningEvent $event) {
error_log(sprintf(
"Warning at %s (property: %s, schema: %s): %s",
$event->propertyPath,
$event->propertyName,
$event->schemaRef ?? 'unknown',
$event->message
));
},
],
]);
$validator = OpenApiValidatorBuilder::create()
->fromYamlFile('openapi.yaml')
->withEventDispatcher($dispatcher)
->build();
$dispatcher = new ArrayDispatcher([]);
$dispatcher
->listen(ValidationStartedEvent::class, $myStartedListener)
->listen(ValidationErrorEvent::class, $myErrorListener);
use Duyler\OpenApi\Builder\OpenApiValidatorBuilder;
use Duyler\OpenApi\Registry\SchemaRegistry;
// Load multiple versions
$validatorV1 = OpenApiValidatorBuilder::create()
->fromYamlFile('api-v1.yaml')
->build();
$documentV1 = $validatorV1->getDocument();
$validatorV2 = OpenApiValidatorBuilder::create()
->fromYamlFile('api-v2.yaml')
->build();
$documentV2 = $validatorV2->getDocument();
// Register schemas (throws on duplicate name+version)
$registry = new SchemaRegistry();
$registry = $registry
->register('api', '1.0.0', $documentV1)
->register('api', '2.0.0', $documentV2);
// Replace an existing entry explicitly (hot-reload, immutable replacement)
$registry = $registry->registerOrReplace('api', '1.0.0', $reloadedDocumentV1);
// Get specific version (returns null if missing)
$schema = $registry->get('api', '1.0.0');
// Get latest version (sorted by semver, returns null if no versions)
$schema = $registry->get('api');
// Get specific version with fail-fast semantics
// Throws VersionNotFoundException if the schema name or version is missing
use Duyler\OpenApi\Registry\Exception\VersionNotFoundException;
try {
$schema = $registry->getOrFail('api', '1.0.0');
$latest = $registry->getOrFail('api');
} catch (VersionNotFoundException $e) {
// $e->getMessage() describes the missing name and version
}
// List all versions
$versions = $registry->getVersions('api');
// ['1.0.0', '2.0.0']
// Check if a schema exists
$registry->has('api', '1.0.0'); // true
$registry->has('api'); // true
$registry->has('unknown'); // false
// List all registered schema names
$names = $registry->getNames();
// ['api']
// Count schemas and versions
$totalNames = $registry->countNames(); // 1 — distinct names
$totalSchemas = $registry->countSchemas(); // 2 — total name+version pairs
$apiVersions = $registry->countVersions('api'); // 2
use Duyler\OpenApi\Validator\ValidatorPool;
$pool = new ValidatorPool(); // default: 128 entries
$pool = new ValidatorPool(maxSize: 64); // custom capacity
// Swoole / threaded runtimes: pass a lock to serialize access
$pool = new ValidatorPool(maxSize: 128, lock: new \Swoole\Lock());
// Or use the named-constructor factory to make the concurrency contract
// explicit at the call site (delegates to the constructor with the same
// validation). The lock parameter is
use Duyler\OpenApi\Compiler\ValidatorCompiler;
use Duyler\OpenApi\Schema\Model\Schema;
$schema = new Schema(
type: 'object',
properties: [
'name' => new Schema(type: 'string'),
'age' => new Schema(type: 'integer'),
],
UserValidator();
$validator->validate(['name' => 'John', 'age' => 30]);
use Duyler\OpenApi\Compiler\ValidatorCompiler;
use Duyler\OpenApi\Schema\OpenApiDocument;
$compiler = new ValidatorCompiler();
// Resolve $ref pointers against the document before compiling
$code = $compiler->compileWithRefResolution($schema, 'PetValidator', $document);
use Duyler\OpenApi\Compiler\ValidatorCompiler;
use Duyler\OpenApi\Compiler\CompilationCache;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
$cachePool = new FilesystemAdapter();
$compilationCache = new CompilationCache($cachePool);
$compiler = new ValidatorCompiler();
// First call compiles and caches, subsequent calls return cached code
$code = $compiler->compileWithCache($schema, 'UserValidator', $compilationCache);
// For schemas that contain a $ref, pass the OpenApiDocument as the fourth
// argument so the cache key can resolve #/components/schemas/... pointers
// against the document and fingerprint its components.schemas map.
$code = $compiler->compileWithCache($refSchema, 'PetValidator', $compilationCache, $document);
$compilationCache = new CompilationCache($pool, ttl: 3600); // 1-hour TTL
use Duyler\OpenApi\Validator\EmptyArrayStrategy;
$validator = OpenApiValidatorBuilder::create()
->fromYamlFile('openapi.yaml')
->withEmptyArrayStrategy(EmptyArrayStrategy::PreferArray)
->build();
use Duyler\OpenApi\Builder\OpenApiValidatorBuilder;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Duyler\OpenApi\Cache\SchemaCache;
use Duyler\OpenApi\Validator\Error\Formatter\DetailedFormatter;
$cachePool = new FilesystemAdapter();
$schemaCache = new SchemaCache($cachePool, 3600);
$validator = OpenApiValidatorBuilder::create()
->fromYamlFile('openapi.yaml')
->withCache($schemaCache) // Cache parsed specs
->withErrorFormatter(new DetailedFormatter()) // Detailed errors
->enableCoercion() // Auto type conversion
->build();
use Duyler\OpenApi\Builder\OpenApiValidatorBuilder;
use Duyler\OpenApi\Builder\OpenApiValidatorInterface;
use Duyler\OpenApi\Validator\Exception\ValidationException;
use Nyholm\Psr7\Response;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Throwable;
final class ValidationMiddleware implements MiddlewareInterface
{
public function __construct(
private readonly OpenApiValidatorInterface $validator,
) {}
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
try {
$operation = $this->validator->validateRequest($request);
} catch (ValidationException $e) {
return new Response(
status: 400,
headers: ['Content-Type' => 'application/json'],
body: json_encode([
'error' => 'Validation failed',
'details' => array_map(fn ($error) => [
'path' => $error->dataPath(),
'message' => $error->message(),
], $e->getErrors()),
], JSON_PRETTY_PRINT),
);
} catch (Throwable $e) {
return new Response(
status: 400,
headers: ['Content-Type' => 'application/json'],
body: json_encode(['error' => 'Internal validation error'], JSON_PRETTY_PRINT),
);
}
return $handler->handle($request->withAttribute('operation', $operation));
}
}
$validator = OpenApiValidatorBuilder::create()
->fromYamlFile('openapi.yaml')
->build();
$middleware = new ValidationMiddleware($validator);
// Register with any PSR-15 compatible framework or dispatcher
// Example with Mezzio:
// $pipeline->pipe(new ValidationMiddleware($validator));
// Without delimiters (recommended)
new Schema(pattern: '^test$')
// With delimiters
new Schema(pattern: '/^test$/')
// This will throw InvalidPatternException:
// Invalid regex pattern "/[invalid/": preg_match(): No ending matching delimiter ']' found
new Schema(pattern: '[invalid')
use Duyler\OpenApi\Validator\Exception\ValidationException;
try {
$operation = $validator->validateRequest($request);
} catch (ValidationException $e) {
// Get array of validation errors
$errors = $e->getErrors();
foreach ($errors as $error) {
printf(
"Path: %s\nMessage: %s\nType: %s\n\n",
$error->dataPath(),
$error->message(),
$error->getType()
);
}
// Get formatted errors
$formatted = $validator->getFormattedErrors($e);
echo $formatted;
}
// Simple formatter (default)
use Duyler\OpenApi\Validator\Error\Formatter\SimpleFormatter;
// Detailed formatter with suggestions
use Duyler\OpenApi\Validator\Error\Formatter\DetailedFormatter;
// JSON formatter for API responses
use Duyler\OpenApi\Validator\Error\Formatter\JsonFormatter;
use Duyler\OpenApi\Validator\Error\Formatter\SimpleFormatter;
use Duyler\OpenApi\Validator\Exception\ValidationException;
$formatter = new SimpleFormatter();
try {
$operation = $validator->validateRequest($request);
} catch (ValidationException $e) {
echo $formatter->formatException($e);
}
$customEmailValidator = new class implements FormatValidatorInterface {
public function validate(mixed $data): void
{
// Custom email validation logic
if (!filter_var($data, FILTER_VALIDATE_EMAIL)) {
throw new InvalidFormatException('email', $data, 'Invalid email');
}
}
};
$validator = OpenApiValidatorBuilder::create()
->fromYamlFile('openapi.yaml')
->withFormat('string', 'email', $customEmailValidator)
->build();
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Duyler\OpenApi\Cache\SchemaCache;
use Duyler\OpenApi\Builder\OpenApiValidatorBuilder;
$cachePool = new FilesystemAdapter();
$schemaCache = new SchemaCache($cachePool, 3600); // TTL: 1 hour
$validator = OpenApiValidatorBuilder::create()
->fromYamlFile('openapi.yaml')
->withCache($schemaCache)
->build();
use Duyler\OpenApi\Compiler\ValidatorCompiler;
use Duyler\OpenApi\Compiler\CompilationCache;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
$compilationCache = new CompilationCache(new FilesystemAdapter());
$compiler = new ValidatorCompiler();
$code = $compiler->compileWithCache($schema, 'UserValidator', $compilationCache);