configuration = $configuration; $this->errorManager = new ErrorManager($this->configuration); $this->urlGenerator = new UrlGenerator( $this->configuration ); $this->metas = new Metas(); $this->reset(); } public function reset(): void { $this->titleLetters = []; $this->currentTitleLevel = 0; $this->levels = []; $this->counters = []; for ($level = 0; $level < 16; $level++) { $this->levels[$level] = 1; $this->counters[$level] = 0; } } public function getConfiguration(): Configuration { return $this->configuration; } public function getErrorManager(): ErrorManager { return $this->errorManager; } public function setErrorManager(ErrorManager $errorManager): void { $this->errorManager = $errorManager; } public function setMetas(Metas $metas): void { $this->metas = $metas; } public function getNodeFactory(): NodeFactory { return $this->configuration->getNodeFactory($this); } public function getTemplateRenderer(): TemplateRenderer { return $this->configuration->getTemplateRenderer(); } public function registerReference(Reference $reference): void { $this->references[$reference->getName()] = $reference; } public function resolve(string $section, string $data): ?ResolvedReference { if (! isset($this->references[$section])) { $this->addMissingReferenceSectionError($section); return null; } $reference = $this->references[$section]; $resolvedReference = $reference->resolve($this, $data); if ($resolvedReference === null) { $this->addInvalidLink(new InvalidLink($data)); if ($this->getMetaEntry() !== null) { $this->getMetaEntry()->removeDependency( // use the original name $this->originalDependencyNames[$data] ?? $data ); } return null; } if (isset($this->unresolvedDependencies[$data]) && $this->getMetaEntry() !== null) { $this->getMetaEntry()->resolveDependency( // use the unique, unresolved name $this->unresolvedDependencies[$data], $resolvedReference->getFile() ); } return $resolvedReference; } public function addInvalidLink(InvalidLink $invalidLink): void { $this->invalidLinks[] = $invalidLink; } /** @return InvalidLink[] */ public function getInvalidLinks(): array { return $this->invalidLinks; } /** @return string[]|null */ public function found(string $section, string $data): ?array { if (isset($this->references[$section])) { $reference = $this->references[$section]; $reference->found($this, $data); return null; } $this->addMissingReferenceSectionError($section); return null; } /** @param mixed $value */ public function setVariable(string $variable, $value): void { $this->variables[$variable] = $value; } public function createTitle(int $level): string { for ($currentLevel = 0; $currentLevel < 16; $currentLevel++) { if ($currentLevel <= $level) { continue; } $this->levels[$currentLevel] = 1; $this->counters[$currentLevel] = 0; } $this->levels[$level] = 1; $this->counters[$level]++; $token = ['title']; for ($i = 1; $i <= $level; $i++) { $token[] = $this->counters[$i]; } return implode('.', $token); } public function getNumber(int $level): int { return $this->levels[$level]++; } /** * @param mixed|null $default * * @return mixed */ public function getVariable(string $variable, $default = null) { if (isset($this->variables[$variable])) { return $this->variables[$variable]; } return $default; } public function setLink(string $name, string $url): void { $name = trim(strtolower($name)); if ($name === '_') { $name = array_shift($this->anonymous); } $this->links[$name] = trim($url); } public function resetAnonymousStack(): void { $this->anonymous = []; } public function pushAnonymous(string $name): void { $this->anonymous[] = trim(strtolower($name)); } /** @return string[] */ public function getLinks(): array { return $this->links; } public function getLink(string $name, bool $relative = true): string { $name = trim(strtolower($name)); if (isset($this->links[$name])) { $link = $this->links[$name]; if ($relative) { return (string) $this->relativeUrl($link); } return $link; } return ''; } public function addDependency(string $dependency, bool $requiresResolving = false): void { if ($requiresResolving) { // a hack to avoid collisions between resolved and unresolved dependencies $dependencyName = 'UNRESOLVED__' . $dependency; $this->unresolvedDependencies[$dependency] = $dependencyName; // map the original dependency name to the one that will be stored $this->originalDependencyNames[$dependency] = $dependencyName; } else { // the dependency is already a filename, probably a :doc: // or from a toc-tree - change it to the canonical URL $canonicalDependency = $this->canonicalUrl($dependency); if ($canonicalDependency === null) { throw new InvalidArgumentException(sprintf( 'Could not get canonical url for dependency %s', $dependency )); } $dependencyName = $canonicalDependency; // map the original dependency name to the one that will be stored $this->originalDependencyNames[$dependency] = $canonicalDependency; } if (in_array($dependencyName, $this->dependencies, true)) { return; } $this->dependencies[] = $dependencyName; } /** @return string[] */ public function getDependencies(): array { return $this->dependencies; } public function relativeUrl(?string $url): ?string { return $this->urlGenerator->relativeUrl($url, $this->currentFileName); } public function absoluteUrl(string $url): string { return $this->urlGenerator->absoluteUrl($this->getDirName(), $url); } public function canonicalUrl(string $url): ?string { return $this->urlGenerator->canonicalUrl($this->getDirName(), $url); } public function generateUrl(string $path): string { return $this->urlGenerator->generateUrl( $path, $this->currentFileName, $this->getDirName() ); } public function getDirName(): string { $dirname = dirname($this->currentFileName); if ($dirname === '.') { return ''; } return $dirname; } public function setCurrentFileName(string $filename): void { $this->currentFileName = $filename; } /** * Returns the currently-parsed filename. * * This is relative to the root source directory and without * the extension (e.g. "index" or "subdir/file") */ public function getCurrentFileName(): string { return $this->currentFileName; } public function setCurrentDirectory(string $directory): void { $this->currentDirectory = $directory; } public function getCurrentDirectory(): string { return $this->currentDirectory; } public function absoluteRelativePath(string $url): string { return $this->currentDirectory . '/' . $this->getDirName() . '/' . $this->relativeUrl($url); } public function setTargetDirectory(string $directory): void { $this->targetDirectory = $directory; } public function getTargetDirectory(): string { return $this->targetDirectory; } public function getUrl(): string { if ($this->url !== null) { return $this->url; } return $this->currentFileName; } public function setUrl(string $url): void { if ($this->getDirName() !== '') { $url = $this->getDirName() . '/' . $url; } $this->url = $url; } public function getMetas(): Metas { return $this->metas; } public function getMetaEntry(): ?MetaEntry { return $this->metas->get($this->currentFileName); } public function getLevel(string $letter): int { foreach ($this->titleLetters as $level => $titleLetter) { if ($letter === $titleLetter) { return $level; } } $this->currentTitleLevel++; $this->titleLetters[$this->currentTitleLevel] = $letter; return $this->currentTitleLevel; } /** @return string[] */ public function getTitleLetters(): array { return $this->titleLetters; } public static function slugify(string $text): string { return (new AsciiSlugger('en', []))->slug($text)->lower()->toString(); } private function addMissingReferenceSectionError(string $section): void { $this->errorManager->error( sprintf('Unknown reference section "%s"', $section), $this->getCurrentFileName() ); } }