|null $server */ public static function forWordPress(?array $server = null): self { $server ??= $_SERVER; $docRoot = isset($server['DOCUMENT_ROOT']) && is_string($server['DOCUMENT_ROOT']) && $server['DOCUMENT_ROOT'] !== '' ? $server['DOCUMENT_ROOT'] : null; $configured = defined('AIWEND_KEY_DIR') && is_string(constant('AIWEND_KEY_DIR')) && constant('AIWEND_KEY_DIR') !== '' ? (string) constant('AIWEND_KEY_DIR') : null; return new self(ABSPATH, WP_CONTENT_DIR, $docRoot, $configured); } private static function clean(string $dir): string { return rtrim(str_replace('\\', '/', $dir), '/'); } public function contentFolder(): string { return self::clean($this->contentDir) . '/' . self::FOLDER; } public function outsideFolder(): string { $abs = self::clean($this->abspath); return self::clean(dirname($abs)) . '/' . self::FOLDER . '-' . substr(hash('sha256', strtolower($abs)), 0, 10); } public function configuredFolder(): ?string { return $this->configuredDir === null ? null : self::clean($this->configuredDir); } /** @return list creation candidates, most preferred first */ public function candidates(): array { $out = []; if ($this->configuredFolder() !== null) { $out[] = ['dir' => $this->configuredFolder(), 'kind' => 'configured']; } $out[] = ['dir' => $this->outsideFolder(), 'kind' => 'outside']; $out[] = ['dir' => $this->contentFolder(), 'kind' => 'content']; return $out; } /** @return list every folder where an existing key file is looked for */ public function searchDirs(): array { return array_column($this->candidates(), 'dir'); } public function kindOf(string $dir): string { $dir = self::clean($dir); foreach ($this->candidates() as $c) { if (strcasecmp($c['dir'], $dir) === 0) { return $c['kind']; } } return 'content'; } /** * True when $dir is inside the document root, false when it is outside, null when that cannot be told (no document root, or the * paths cannot be resolved). The folder may not exist yet: its nearest existing parent is resolved instead. */ public function insideDocumentRoot(string $dir): ?bool { if ($this->documentRoot === null) { return null; } $root = self::resolve($this->documentRoot); $target = self::resolve($dir); if ($root === null || $target === null) { return null; } return $target === $root || str_starts_with($target . '/', $root . '/'); } private static function resolve(string $path): ?string { $path = self::clean($path); $tail = ''; while ($path !== '' && !Fs::exists($path)) { $parent = self::clean(dirname($path)); if ($parent === $path) { return null; } $tail = '/' . basename($path) . $tail; $path = $parent; } $real = $path === '' ? false : Fs::realpath($path); if ($real === false) { return null; } $resolved = self::clean($real) . $tail; return DIRECTORY_SEPARATOR === '\\' ? strtolower($resolved) : $resolved; } }