Navigation

Php

How to Use PHP's DNF (Disjunctive Normal Form) Types

Master PHP 8.2's advanced type system that combines union and intersection types for precise parameter constraints.

Table Of Contents

Here's the Answer

The issue is expressing complex type requirements like "A and B, or C and D". You can solve this by using DNF types with parentheses:

<?php

interface Loggable {
    public function log(string $message): void;
}

interface Cacheable {
    public function getFromCache(string $key): mixed;
    public function putInCache(string $key, mixed $value): void;
}

interface Searchable {
    public function search(string $query): array;
}

interface Indexable {
    public function index(): void;
    public function reindex(): void;
}

// DNF Type: (Loggable&Cacheable)|(Searchable&Indexable)
function processContent((Loggable&Cacheable)|(Searchable&Indexable) $processor): void {
    // This accepts objects that are:
    // - EITHER: Loggable AND Cacheable
    // - OR: Searchable AND Indexable
    
    if ($processor instanceof Loggable) {
        $processor->log("Processing content with cache");
        $cached = $processor->getFromCache("content");
        if (!$cached) {
            $processor->putInCache("content", "processed data");
        }
    } else {
        $processor->index();
        $results = $processor->search("example");
    }
}

// Classes implementing different combinations
class CachedLogger implements Loggable, Cacheable {
    public function log(string $message): void {
        echo "Log: $message\n";
    }
    
    public function getFromCache(string $key): mixed {
        return $_SESSION[$key] ?? null;
    }
    
    public function putInCache(string $key, mixed $value): void {
        $_SESSION[$key] = $value;
    }
}

class SearchEngine implements Searchable, Indexable {
    private array $index = [];
    
    public function search(string $query): array {
        return array_filter($this->index, fn($item) => str_contains($item, $query));
    }
    
    public function index(): void {
        $this->index = ['content1', 'content2', 'example data'];
    }
    
    public function reindex(): void {
        $this->index = [];
        $this->index();
    }
}

// More complex DNF with classes and interfaces
class DatabaseService {
    public function connect(): void {}
}

class FileService {
    public function readFile(string $path): string { return ''; }
}

// Complex DNF: (DatabaseService&Loggable)|(FileService&Cacheable)|Searchable
function handleData((DatabaseService&Loggable)|(FileService&Cacheable)|Searchable $handler): void {
    match (true) {
        $handler instanceof DatabaseService => $handler->connect(),
        $handler instanceof FileService => $handler->readFile('/path'),
        $handler instanceof Searchable => $handler->search('query'),
        default => throw new InvalidArgumentException('Invalid handler type')
    };
}

// Usage
$logger = new CachedLogger();
$searcher = new SearchEngine();

processContent($logger);    // ✅ Implements Loggable&Cacheable
processContent($searcher);  // ✅ Implements Searchable&Indexable

// This would fail:
// processContent(new SomeOtherClass()); // ❌ Doesn't match either combination

Explanation

DNF types allow you to create sophisticated type constraints:

  1. Flexible Requirements: Accept multiple valid type combinations
  2. Precise Contracts: Each branch specifies exact interface requirements
  3. Better API Design: Express "this OR that" with different capabilities
  4. Type Safety: Compiler ensures only valid combinations are accepted

Syntax Rules:

  • Use & for intersection (AND)
  • Use | for union (OR)
  • Use () to group intersections within unions
  • Format: (A&B)|(C&D)|(E&F)

DNF types are perfect for APIs that can work with different service types, each requiring specific capabilities.

Share this article

Add Comment

No comments yet. Be the first to comment!

More from Php