nur-sery/nur_src/v/prefix.php

163 lines
5.2 KiB
PHP

<?php
namespace nur\v;
use nur\A;
use nur\str;
use nur\v\model\IPage;
/**
* Class prefix: gestion des préfixes pour les URLs
*/
class prefix {
static function get_base_url(): string {
$base_url = getenv("BASE_URL");
if ($base_url !== false) {
return str::without_suffix("/", parse_url($base_url, PHP_URL_PATH));
} else {
return "";
}
}
/**
* obtenir le chemin par défaut du script courant.
* - si la variable d'environnement BASE_URL est définie, alors la valeur est
* exacte.
* - sinon, on part du principe que l'application ne sert des pages qu'à sa
* racine, ce qui n'est pas forcément vrai
*
* la valeur retournée est
* - soit le chemin avec le préfixe '/' e.g '/index.php'
* - soir la chaine vide ''
*/
static function get_default_self(): string {
$script_name = $_SERVER["SCRIPT_NAME"];
$base_url = getenv("BASE_URL");
if ($base_url !== false) {
# d'abord, isoler le chemin de $base_url
$base_url = str::without_suffix("/", parse_url($base_url, PHP_URL_PATH));
# puis enlever le préfixe
return str::without_prefix($base_url, $script_name);
}
$pos = strrpos($script_name, "/");
if ($pos === false) return "";
return substr($script_name, $pos);
}
static function compute(?string $self): string {
if ($self !== null) {
if (substr($self, 0, 1) != "/") $self = "/$self";
} else {
$self = self::get_default_self();
}
$php_self = $_SERVER["PHP_SELF"];
$pos = strpos($php_self, $self);
if ($pos === false) return "";
$prefix = "";
$offset = $pos + 1;
while (($pos = strpos($php_self, "/", $offset)) !== false) {
$prefix .= "../";
$offset = $pos + 1;
}
return $prefix;
}
static function add(string $url, ?string $prefix): string {
if ($prefix === null) return $url;
if (preg_match('/^(\/)|(https?:\/\/)/', $url)) {
# l'url est de la forme /ANY ou http[s]://ANY
# url absolue, laisser en l'état
} elseif (str::starts_with("./", $url)) {
# l'url est de la forme ./ANY
# url relative
$url = $prefix.substr($url, 2);
} elseif (str::starts_with("__PREFIX__/", $url)) {
# l'url est de la forme __PREFIX__/ANY
# compatibilité: url exprimée par rapport au préfixe
$url = $prefix.substr($url, 11);
} else {
# par défaut, l'url est exprimée par rapport au préfixe
$url = $prefix.$url;
}
return $url;
}
#############################################################################
# exemple d'implémentation pour utiliser la valeur calculée de prefix
/**
* @var string chemin du script correspondant à la page courante, depuis la
* racine de l'application e.g. index.php, ou null s'il faut utiliser la
* valeur par défaut
*/
protected static $self;
/** @var string */
protected static $prefix;
static final function set_self(?string $self) {
if ($self === null) $self = self::get_default_self();
self::$self = $self;
self::$prefix = self::compute($self);
}
/**
* construire un url avec les paramètres spécifiés. le cas échéant, corriger
* l'url en fonction de $self
*
* cette méthode sera utilisée notamment pour des liens vers des resources
* statiques
*/
static final function resu(string $url, ...$params): string {
return html::bu(self::add($url, self::$prefix), ...$params);
}
/**
* construire un url vers le composant destination avec les paramètres
* spécifiés. le cas échéant, corriger l'url en fonction de $self
*
* @param string|IPage $dest composant destination
*/
static final function dynu($dest, ...$params): string {
$url = $dest; #XXX $url = route::get_path($dest);
return html::bu(self::add($url, self::$prefix), ...$params);
}
const BU_STATIC_PARTS = ["scheme", "host", "port", "query", "fragment"];
/**
* méthode générique pour construire une url. utiliser des heuristiques pour
* déterminer si la destination est une url statique ou si c'est un composant
*/
static final function bu($dest, ...$params): string {
$dyn = true;
if (is_string($dest)) {
# heuristiques pour déterminer si c'est une url statique
$parts = parse_url($dest);
foreach (self::BU_STATIC_PARTS as $part) {
if (A::has($parts, $part)) {
# y a-t-il au moins une partie d'une url statique?
$dyn = false;
break;
}
}
if ($dyn) {
# s'il n'y a qu'un chemin, tester si ça ressemble à une url
$path = A::get($parts, "path", "");
if ($path === "" || $path === ".") $dyn = false;
elseif (strpos($path, "/")) $dyn = false;
elseif (preg_match('/\.(php|html)\b/', $path)) $dyn = false;
}
}
if ($dyn) {
# tenir compte du préfixe, puisque les chemins des composants sont
# toujours exprimés depuis la racine
$url = $dest; #XXX $url = route::get_path($dest);
return html::bu(self::add($url, self::$prefix), ...$params);
} else {
# ne pas corriger une url statique: on fait confiance à l'utilisateur pour
# utiliser des chemins relatifs
return html::bu($dest, ...$params);
}
}
}