Set.php

PHP

Path: src/Table/Utility/Set.php

<?php

namespace mini\Table\Utility;

use IteratorAggregate;
use mini\Table\ColumnDef;
use mini\Table\Contracts\SetInterface;
use stdClass;
use Traversable;

/**
 * Simple in-memory set implementation
 *
 * Provides O(1) membership testing via array key lookup.
 *
 * ```php
 * $set = new Set('status', ['active', 'pending']);
 * $set->has((object)['status' => 'active']);  // true
 * $set->has((object)['status' => 'deleted']); // false
 *
 * // Iteration yields stdClass
 * foreach ($set as $member) {
 *     echo $member->status;  // 'active', 'pending'
 * }
 * ```
 */
class Set implements SetInterface, IteratorAggregate
{
    /** @var array<string, true> Values stored as keys for O(1) lookup */
    private array $items = [];

    /**
     * @param string $column Column name for the values
     * @param array<int, string|int|float> $values Values in the set
     */
    public function __construct(
        private string $column,
        array $values,
    ) {
        foreach ($values as $value) {
            // Use string key for lookup (handles int/float/string)
            $this->items[(string)$value] = true;
        }
    }

    public function getColumns(): array
    {
        return [$this->column => new ColumnDef($this->column)];
    }

    public function has(object $member): bool
    {
        $col = $this->column;

        if (!property_exists($member, $col)) {
            throw new \InvalidArgumentException("Member missing property: $col");
        }

        return isset($this->items[(string)$member->$col]);
    }

    public function getIterator(): Traversable
    {
        $col = $this->column;

        foreach ($this->items as $key => $_) {
            $obj = new stdClass();

            // Try to return original type (int if numeric)
            if (is_numeric($key) && !str_contains($key, '.')) {
                $obj->$col = (int)$key;
            } else {
                $obj->$col = $key;
            }

            yield $obj;
        }
    }
}