namespace = ''; return; } // Remove leading/trailing backslashes $namespace = trim($namespace, '\\'); if (! $this->isValidNamespace($namespace)) { throw new InvalidArgumentException("Invalid namespace: {$namespace}"); } $this->namespace = $namespace; } /** * Create from namespace string */ public static function fromString(string $namespace): self { return new self($namespace); } /** * Create from class name */ public static function fromClass(string $className): self { $className = ltrim($className, '\\'); $lastBackslash = strrpos($className, '\\'); if ($lastBackslash === false) { return new self(''); // Global namespace } return new self(substr($className, 0, $lastBackslash)); } /** * Create from namespace parts * @param array $parts */ public static function fromParts(array $parts): self { return new self(implode('\\', $parts)); } /** * Create global namespace */ public static function global(): self { return new self(''); } /** * Get namespace as string */ public function toString(): string { return $this->namespace; } /** * Get namespace parts * @return array */ public function parts(): array { if ($this->namespace === '') { return []; } return explode('\\', $this->namespace); } /** * Get namespace depth (number of levels) */ public function depth(): int { if ($this->namespace === '') { return 0; } return count($this->parts()); } /** * Check if this is the global namespace */ public function isGlobal(): bool { return $this->namespace === ''; } /** * Get parent namespace */ public function parent(): ?self { $parts = $this->parts(); if (count($parts) <= 1) { return null; // No parent or already at global namespace } array_pop($parts); return self::fromParts($parts); } /** * Append namespace segment */ public function append(string $segment): self { if ($this->namespace === '') { return new self($segment); } return new self($this->namespace . '\\' . ltrim($segment, '\\')); } /** * Check if namespace starts with given prefix */ public function startsWith(string|self $prefix): bool { $prefixStr = $prefix instanceof self ? $prefix->toString() : $prefix; $prefixStr = trim($prefixStr, '\\'); if ($prefixStr === '') { return true; // All namespaces start with global namespace } return str_starts_with($this->namespace, $prefixStr); } /** * Check if namespace ends with given suffix */ public function endsWith(string|self $suffix): bool { $suffixStr = $suffix instanceof self ? $suffix->toString() : $suffix; $suffixStr = trim($suffixStr, '\\'); if ($suffixStr === '') { return true; } return str_ends_with($this->namespace, $suffixStr); } /** * Compare for equality */ public function equals(self $other): bool { return $this->namespace === $other->namespace; } /** * Convert to a fully qualified class name (with leading backslash) */ public function toFqcn(string $className): string { if ($this->namespace === '') { return '\\' . $className; } return '\\' . $this->namespace . '\\' . $className; } /** * Convert to file path (for PSR-4 autoloading) */ public function toPath(): string { if ($this->namespace === '') { return ''; } return str_replace('\\', '/', $this->namespace); } /** * String representation */ public function __toString(): string { return $this->namespace; } /** * Validate namespace format */ private function isValidNamespace(string $namespace): bool { // Empty namespace is valid (global namespace) if ($namespace === '') { return true; } // Each part must be a valid PHP identifier $parts = explode('\\', $namespace); foreach ($parts as $part) { // Must start with letter or underscore, followed by letters, numbers, or underscores if (! preg_match('/^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$/', $part)) { return false; } } return true; } }