*/ protected array $children; /** @var array, Route> */ protected array $routes = []; protected ?self $parent = null; final public function __construct( protected ?string $value = null, protected PathSegmentType $type = PathSegmentType::Raw, ) { if (PathSegmentType::Root === $type) { $this->value = null; } } public static function makeFromRouteString(string $value): static { $type = PathSegmentType::Parameter; $regex = $type->routeMatchRegex(); if (preg_match($regex, $value, $matches) === 1) { $value = $matches[1]; } else { $type = PathSegmentType::Raw; } return new static($value, $type); } public function getContent(): string { return match ($this->type) { PathSegmentType::Root => '', default => $this->value ?? '', }; } public function getValue(): ?string { return $this->value; } private static function createKey( ?string $value, PathSegmentType $type, ): string { return match ($type) { PathSegmentType::Raw => strtolower($value ?? ''), PathSegmentType::Root => '', PathSegmentType::Parameter => '', }; } public function getKey(): string { return self::createKey($this->value, $this->type); } public function getType(): PathSegmentType { return $this->type; } public function setParent(?self $segment): void { $this->parent = $segment; } public function getParent(): ?self { return $this->parent; } /** @return self[] */ public function getAncestors(): array { $results = []; $count = 0; $instance = $this->parent; while ( null !== $instance && $count++ < self::MAX_ANCESTORY_DEPTH ) { $results[] = $instance; $instance = $instance->parent; } return array_reverse($results); } /** @return self[] */ public function getAncestorsAndSelf(): array { $results = $this->getAncestors(); $results[] = $this; return $results; } public function addChild(self $segment): void { $segment->setParent($this); $this->children[$segment->getKey()] = $segment; } /** @return array */ public function getChildren(): array { return $this->children; } public function getChildrenCount(): int { return count($this->children); } public function findChild( ?string $value = null, ): ?PathSegment { $key = self::createKey($value, PathSegmentType::Raw); $seg = $this->children[$key] ?? null; if ($seg instanceof self) { return $seg; } $key = self::createKey($value, PathSegmentType::Parameter); $seg = $this->children[$key] ?? null; return $seg; } public function child( ?string $value = null, PathSegmentType $type = PathSegmentType::Raw, ): self { $key = self::createKey($value, $type); $seg = $this->children[$key] ?? null; if (null === $seg) { $seg = new PathSegment($value, $type); } $this->addChild($seg); return $seg; } /** @param array $parameters */ public function getPath(array $parameters = []): string { $parts = array_map( fn ($seg) => match ($seg->getType()) { PathSegmentType::Parameter => ( $parameters[$v = ($seg->getValue() ?? '')] ?? throw MissingParameterException::make($v) ), default => $seg->getContent(), }, $this->getAncestorsAndSelf() ); return implode('/', $parts); } public function addRoute(Route $route): void { $route->setSegment($this); $method = $route->getMethod()->value; $this->routes[$method] = $route; } public function getRoute(HttpMethod $method): ?Route { return $this->routes[$method->value] ?? null; } /** @return array, Route> */ public function getRoutes(): array { return $this->routes; } }