*/ class Styles implements Countable, ArrayAccess, Stringable { /** @var array */ protected $styles = []; /** @var bool */ protected $sorted = true; /** * @param null|array|Traversable|null $classes */ public function __construct(null|array|Traversable $classes = null) { if (!$classes) return; foreach ($classes as $name => $value) { $this[$name] = $value; } } public function count(): int { return count($this->styles); } public function offsetExists(mixed $offset): bool { if (!$offset) return false; return isset($this->styles[$offset]); } public function offsetGet(mixed $offset): mixed { return @$this->styles[$offset]; } public function offsetSet(mixed $offset, mixed $value): void { if (!$offset) return; if ($value) $value = trim($value); if (!$value) unset($this->styles[$offset]); else { if (!static::validate($offset, $value)) return; if (!isset($this->styles[$offset])) $this->sorted = false; $this->styles[$offset] = $value; } } public function offsetUnset(mixed $offset): void { unset($this->styles[$offset]); } /** * @return array */ public function getArray(): array { if (!$this->sorted) { ksort($this->styles); $this->sorted = true; } return $this->styles; } public function __toString(): string { $styles = []; foreach ($this->getArray() as $key => $value) { $styles[] = $key . ':' . $value; } return implode(';', $styles); } protected static function validate(null|string $property, null|string $value): bool { if (!$property) return false; elseif (!preg_match('/[a-z]/', $property)) return false; if ($value) $value = trim($value); if (!$value) return false; elseif (str_contains($value, ';')) return false; elseif (str_contains($value, ':')) return false; return true; } }