diff --git a/composer.lock b/composer.lock index 3a657cc01ac49d16cd8b3cc0c5ca7d5d6c1a82c9..c5c699c60fddffa8bd7e2bc88bbe9044b4b30933 100644 --- a/composer.lock +++ b/composer.lock @@ -8,7 +8,7 @@ "packages": [ { "name": "symfony/polyfill-mbstring", - "version": "v1.24.0", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", @@ -71,7 +71,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.24.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.25.0" }, "funding": [ { @@ -91,16 +91,16 @@ }, { "name": "symfony/polyfill-php80", - "version": "v1.24.0", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "57b712b08eddb97c762a8caa32c84e037892d2e9" + "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/57b712b08eddb97c762a8caa32c84e037892d2e9", - "reference": "57b712b08eddb97c762a8caa32c84e037892d2e9", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/4407588e0d3f1f52efb65fbe92babe41f37fe50c", + "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c", "shasum": "" }, "require": { @@ -154,7 +154,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.24.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.25.0" }, "funding": [ { @@ -170,11 +170,11 @@ "type": "tidelift" } ], - "time": "2021-09-13T13:58:33+00:00" + "time": "2022-03-04T08:16:47+00:00" }, { "name": "symfony/polyfill-php81", - "version": "v1.24.0", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php81.git", @@ -233,7 +233,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.24.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.25.0" }, "funding": [ { @@ -392,16 +392,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.4.6", + "version": "1.4.8", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "8a7761f1c520e0dad6e04d862fdc697445457cfe" + "reference": "2a6d6704b17c4db6190cc3104056c0aad740cb15" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/8a7761f1c520e0dad6e04d862fdc697445457cfe", - "reference": "8a7761f1c520e0dad6e04d862fdc697445457cfe", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/2a6d6704b17c4db6190cc3104056c0aad740cb15", + "reference": "2a6d6704b17c4db6190cc3104056c0aad740cb15", "shasum": "" }, "require": { @@ -432,7 +432,7 @@ "description": "PHPStan - PHP Static Analysis Tool", "support": { "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/1.4.6" + "source": "https://github.com/phpstan/phpstan/tree/1.4.8" }, "funding": [ { @@ -452,7 +452,7 @@ "type": "tidelift" } ], - "time": "2022-02-06T12:56:13+00:00" + "time": "2022-03-04T13:03:56+00:00" }, { "name": "spip/coding-standards", diff --git a/ecrire/inc/bandeau.php b/ecrire/inc/bandeau.php index d20f727d1ea6962d3ccff71f26b571447b4f4fc5..d1f0680d17a37c9d17160393adfe5fe889c1ad30 100644 --- a/ecrire/inc/bandeau.php +++ b/ecrire/inc/bandeau.php @@ -10,6 +10,8 @@ * Pour plus de détails voir le fichier COPYING.txt ou l'aide en ligne. * \***************************************************************************/ +use Spip\Admin\Bouton; + /** * Ce fichier gère le bandeau supérieur de l'espace privé * diff --git a/ecrire/inc/boutons.php b/ecrire/inc/boutons.php index 6a3254f897c0d56dff9a97bac2eb225e5f7b6534..c2e18559cdeb4d4a1b70149996a094aaaa38dcec 100644 --- a/ecrire/inc/boutons.php +++ b/ecrire/inc/boutons.php @@ -10,6 +10,8 @@ * Pour plus de détails voir le fichier COPYING.txt ou l'aide en ligne. * \***************************************************************************/ +use Spip\Admin\Bouton; + /** * Gestion des boutons de l'interface privée * @@ -20,65 +22,6 @@ if (!defined('_ECRIRE_INC_VERSION')) { return; } -/** - * Classe définissant un bouton dans la barre du haut de l'interface - * privée ou dans un de ses sous menus - */ -class Bouton { - /** L'icone à mettre dans le bouton */ - public string $icone; - - /** Le nom de l'entrée d'i18n associé */ - public string $libelle; - - /** @var null|string L'URL de la page (null => ?exec=nom) */ - public $url = null; - - /** @var null|string|array Arguments supplementaires de l'URL */ - public $urlArg = null; - - /** @var null|string URL du javascript */ - public $url2 = null; - - /** @var null|string Pour ouvrir dans une fenetre a part */ - public $target = null; - - /** Sous-barre de boutons / onglets */ - public array $sousmenu = []; - - /** Position dans le menu */ - public int $position = 0; - - /** Entrée favorite (sa position dans les favoris) ? */ - public int $favori = 0; - - - /** - * Définit un bouton - * - * @param string $icone - * L'icone à mettre dans le bouton - * @param string $libelle - * Le nom de l'entrée i18n associé - * @param null|string $url - * L'URL de la page - * @param null|string|array $urlArg - * Arguments supplémentaires de l'URL - * @param null|string $url2 - * URL du javascript - * @param null|mixed $target - * Pour ouvrir une fenêtre à part - */ - public function __construct($icone, $libelle, $url = null, $urlArg = null, $url2 = null, $target = null) { - $this->icone = $icone; - $this->libelle = $libelle; - $this->url = $url; - $this->urlArg = $urlArg; - $this->url2 = $url2; - $this->target = $target; - } -} - /** * Définir la liste des onglets dans une page de l'interface privée. * diff --git a/ecrire/inc/traduire.php b/ecrire/inc/traduire.php index 83ce1110a5331c87d9534826059dc53dc2153dde..279fba27d960300611ac2edabfb42ad6aec4fde1 100644 --- a/ecrire/inc/traduire.php +++ b/ecrire/inc/traduire.php @@ -10,6 +10,8 @@ * Pour plus de détails voir le fichier COPYING.txt ou l'aide en ligne. * \***************************************************************************/ +use Spip\I18n\Description; + /** * Outils pour la traduction et recherche de traductions * @@ -202,24 +204,6 @@ function surcharger_langue($fichiers) { } } - - -class SPIP_Traductions_Description { - /** @var string code de langue (hors module) */ - public $code; - /** @var string nom du module de langue */ - public $module; - /** @var string langue de la traduction */ - public $langue; - /** @var string traduction */ - public $texte; - /** @var string var mode particulier appliqué ? */ - public $mode; - /** @var bool Corrections des textes appliqué ? */ - public $corrections = false; -} - - /** * Traduire une chaine internationalisée * @@ -253,9 +237,9 @@ class SPIP_Traductions_Description { * @param bool $raw * - false : retourne le texte (par défaut) * - true : retourne une description de la chaine de langue (module, texte, langue) - * @return string|SPIP_Traductions_Description + * @return string|Description * - string : Traduction demandée. Chaîne vide si aucune traduction trouvée. - * - SPIP_Traductions_Description : traduction et description (texte, module, langue) + * - Description : traduction et description (texte, module, langue) **/ function inc_traduire_dist($ori, $lang, $raw = false) { static $deja_vu = []; @@ -276,7 +260,7 @@ function inc_traduire_dist($ori, $lang, $raw = false) { $ori_complet = implode('|', $modules) . ':' . $ori; } - $desc = new SPIP_Traductions_Description(); + $desc = new Description(); // parcourir tous les modules jusqu'a ce qu'on trouve foreach ($modules as $module) { @@ -357,9 +341,9 @@ function inc_traduire_dist($ori, $lang, $raw = false) { * Modifie le texte de traduction pour indiquer des éléments * servant au debug de celles-ci. (pour var_mode=traduction) * - * @param SPIP_Traductions_Description $desc + * @param Description $desc * @param string $modules Les modules qui étaient demandés - * @return SPIP_Traductions_Description + * @return Description */ function definir_details_traduction($desc, $modules) { if (!$desc->mode and $desc->texte) { diff --git a/ecrire/iterateur/condition.php b/ecrire/iterateur/condition.php index 86fc2dfe91ef0ba811d9722ce2c638faa8407ee3..f3e388e1c2eb6903bde12d9403282d9c9433c35b 100644 --- a/ecrire/iterateur/condition.php +++ b/ecrire/iterateur/condition.php @@ -44,19 +44,3 @@ function iterateur_CONDITION_dist($b) { return $b; } - -/** - * Iterateur CONDITION pour itérer sur des données - * - * La boucle condition n'a toujours qu'un seul élément. - */ -class IterateurCONDITION extends IterateurData { - /** - * Obtenir les données de la boucle CONDITION - * - * @param array $command - **/ - protected function select($command) { - $this->tableau = [0 => 1]; - } -} diff --git a/ecrire/iterateur/data.php b/ecrire/iterateur/data.php index 55455cc8945872d466351417b9fa5608fb0e4388..59543b459cf032ae28877ae3ca9e2dbc30992f59 100644 --- a/ecrire/iterateur/data.php +++ b/ecrire/iterateur/data.php @@ -58,526 +58,6 @@ function iterateur_DATA_dist($b) { } -/** - * Itérateur DATA - * - * Pour itérer sur des données quelconques (transformables en tableau) - */ -class IterateurDATA implements Iterator { - /** Tableau de données */ - protected array $tableau = []; - - /** - * Conditions de filtrage - * ie criteres de selection - */ - protected array $filtre = []; - - - /** - * Cle courante - * - * @var scalar - */ - protected $cle = null; - - /** - * Valeur courante - * - * @var mixed - */ - protected $valeur = null; - - protected string $type = 'DATA'; - - protected array $command = []; - - protected array $info = []; - - /** Erreur presente ? */ - public bool $err = false; - - /** - * Calcul du total des elements - * - * @var int|null - **/ - public $total = null; - - /** - * Constructeur - * - * @param $command - * @param array $info - */ - public function __construct($command, $info = []) { - $this->type = 'DATA'; - $this->command = $command; - $this->info = $info; - - $this->select($command); - } - - /** - * Revenir au depart - * - * @return void - */ - public function rewind(): void { - reset($this->tableau); - $this->cle = array_key_first($this->tableau); - $this->valeur = current($this->tableau); - next($this->tableau); - } - - /** - * Déclarer les critères exceptions - * - * @return array - */ - public function exception_des_criteres() { - return ['tableau']; - } - - /** - * Récupérer depuis le cache si possible - * - * @param string $cle - * @return mixed - */ - protected function cache_get($cle) { - if (!$cle) { - return; - } - # utiliser memoization si dispo - if (!function_exists('cache_get')) { - return; - } - - return cache_get($cle); - } - - /** - * Stocker en cache si possible - * - * @param string $cle - * @param int $ttl - * @param null|mixed $valeur - * @return bool - */ - protected function cache_set($cle, $ttl, $valeur = null) { - if (!$cle) { - return; - } - if (is_null($valeur)) { - $valeur = $this->tableau; - } - # utiliser memoization si dispo - if (!function_exists('cache_set')) { - return; - } - - return cache_set( - $cle, - [ - 'data' => $valeur, - 'time' => time(), - 'ttl' => $ttl - ], - 3600 + $ttl - ); - # conserver le cache 1h de plus que la validite demandee, - # pour le cas ou le serveur distant ne reponde plus - } - - /** - * Aller chercher les données de la boucle DATA - * - * @throws Exception - * @param array $command - * @return void - */ - protected function select($command) { - - // l'iterateur DATA peut etre appele en passant (data:type) - // le type se retrouve dans la commande 'from' - // dans ce cas la le critere {source}, si present, n'a pas besoin du 1er argument - if (isset($this->command['from'][0])) { - if (isset($this->command['source']) and is_array($this->command['source'])) { - array_unshift($this->command['source'], $this->command['sourcemode']); - } - $this->command['sourcemode'] = $this->command['from'][0]; - } - - // cherchons differents moyens de creer le tableau de donnees - // les commandes connues pour l'iterateur DATA - // sont : {tableau #ARRAY} ; {cle=...} ; {valeur=...} - - // {source format, [URL], [arg2]...} - if ( - isset($this->command['source']) - and isset($this->command['sourcemode']) - ) { - $this->select_source(); - } - - // Critere {liste X1, X2, X3} - if (isset($this->command['liste'])) { - $this->select_liste(); - } - if (isset($this->command['enum'])) { - $this->select_enum(); - } - - // Si a ce stade on n'a pas de table, il y a un bug - if (!is_array($this->tableau)) { - $this->err = true; - spip_log('erreur datasource ' . var_export($command, true)); - } - - // {datapath query.results} - // extraire le chemin "query.results" du tableau de donnees - if ( - !$this->err - and isset($this->command['datapath']) - and is_array($this->command['datapath']) - ) { - $this->select_datapath(); - } - - // tri {par x} - if ($this->command['orderby']) { - $this->select_orderby(); - } - - // grouper les resultats {fusion /x/y/z} ; - if ($this->command['groupby']) { - $this->select_groupby(); - } - - $this->rewind(); - #var_dump($this->tableau); - } - - - /** - * Aller chercher les donnees de la boucle DATA - * depuis une source - * {source format, [URL], [arg2]...} - */ - protected function select_source() { - # un peu crado : avant de charger le cache il faut charger - # les class indispensables, sinon PHP ne saura pas gerer - # l'objet en cache ; cf plugins/icalendar - # perf : pas de fonction table_to_array ! (table est deja un array) - if ( - isset($this->command['sourcemode']) - and !in_array($this->command['sourcemode'], ['table', 'array', 'tableau']) - ) { - charger_fonction($this->command['sourcemode'] . '_to_array', 'inc', true); - } - - # le premier argument peut etre un array, une URL etc. - $src = $this->command['source'][0]; - - # avons-nous un cache dispo ? - $cle = null; - if (is_string($src)) { - $cle = 'datasource_' . md5($this->command['sourcemode'] . ':' . var_export($this->command['source'], true)); - } - - $cache = $this->cache_get($cle); - if (isset($this->command['datacache'])) { - $ttl = intval($this->command['datacache']); - } - if ( - $cache - and ($cache['time'] + ($ttl ?? $cache['ttl']) - > time()) - and !(_request('var_mode') === 'recalcul' - and include_spip('inc/autoriser') - and autoriser('recalcul') - ) - ) { - $this->tableau = $cache['data']; - } else { - try { - if ( - isset($this->command['sourcemode']) - and in_array( - $this->command['sourcemode'], - ['table', 'array', 'tableau'] - ) - ) { - if ( - is_array($a = $src) - or (is_string($a) - and $a = str_replace('"', '"', $a) # fragile! - and is_array($a = @unserialize($a))) - ) { - $this->tableau = $a; - } - } else { - $data = $src; - if (is_string($src)) { - if (tester_url_absolue($src)) { - include_spip('inc/distant'); - $data = recuperer_url($src, ['taille_max' => _DATA_SOURCE_MAX_SIZE]); - $data = $data['page'] ?? ''; - if (!$data) { - throw new Exception('404'); - } - if (!isset($ttl)) { - $ttl = 24 * 3600; - } - } elseif (@is_dir($src)) { - $data = $src; - } elseif (@is_readable($src) && @is_file($src)) { - $data = spip_file_get_contents($src); - } - if (!isset($ttl)) { - $ttl = 10; - } - } - - if ( - !$this->err - and $data_to_array = charger_fonction($this->command['sourcemode'] . '_to_array', 'inc', true) - ) { - $args = $this->command['source']; - $args[0] = $data; - if (is_array($a = $data_to_array(...$args))) { - $this->tableau = $a; - } - } - } - - if (!is_array($this->tableau)) { - $this->err = true; - } - - if (!$this->err and isset($ttl) and $ttl > 0) { - $this->cache_set($cle, $ttl); - } - } catch (Exception $e) { - $e = $e->getMessage(); - $err = sprintf( - "[%s, %s] $e", - $src, - $this->command['sourcemode'] - ); - erreur_squelette([$err, []]); - $this->err = true; - } - } - - # en cas d'erreur, utiliser le cache si encore dispo - if ( - $this->err - and $cache - ) { - $this->tableau = $cache['data']; - $this->err = false; - } - } - - - /** - * Retourne un tableau donne depuis un critère liste - * - * Critère `{liste X1, X2, X3}` - * - * @see critere_DATA_liste_dist() - * - **/ - protected function select_liste() { - # s'il n'y a qu'une valeur dans la liste, sans doute une #BALISE - if (!isset($this->command['liste'][1])) { - if (!is_array($this->command['liste'][0])) { - $this->command['liste'] = explode(',', $this->command['liste'][0]); - } else { - $this->command['liste'] = $this->command['liste'][0]; - } - } - $this->tableau = $this->command['liste']; - } - - /** - * Retourne un tableau donne depuis un critere liste - * Critere {enum Xmin, Xmax} - * - **/ - protected function select_enum() { - # s'il n'y a qu'une valeur dans la liste, sans doute une #BALISE - if (!isset($this->command['enum'][1])) { - if (!is_array($this->command['enum'][0])) { - $this->command['enum'] = explode(',', $this->command['enum'][0]); - } else { - $this->command['enum'] = $this->command['enum'][0]; - } - } - if ((is_countable($this->command['enum']) ? count($this->command['enum']) : 0) >= 3) { - $enum = range( - array_shift($this->command['enum']), - array_shift($this->command['enum']), - array_shift($this->command['enum']) - ); - } else { - $enum = range(array_shift($this->command['enum']), array_shift($this->command['enum'])); - } - $this->tableau = $enum; - } - - - /** - * extraire le chemin "query.results" du tableau de donnees - * {datapath query.results} - * - **/ - protected function select_datapath() { - $base = reset($this->command['datapath']); - if (strlen($base = ltrim(trim($base), '/'))) { - $this->tableau = table_valeur($this->tableau, $base); - if (!is_array($this->tableau)) { - $this->tableau = []; - $this->err = true; - spip_log("datapath '$base' absent"); - } - } - } - - /** - * Ordonner les resultats - * {par x} - * - **/ - protected function select_orderby() { - $sortfunc = ''; - $aleas = 0; - foreach ($this->command['orderby'] as $tri) { - // virer le / initial pour les criteres de la forme {par /xx} - if (preg_match(',^\.?([/\w:_-]+)( DESC)?$,iS', ltrim($tri, '/'), $r)) { - $r = array_pad($r, 3, null); - - // tri par cle - if ($r[1] == 'cle') { - if (isset($r[2]) and $r[2]) { - krsort($this->tableau); - } else { - ksort($this->tableau); - } - } # {par hasard} - else { - if ($r[1] == 'hasard') { - $k = array_keys($this->tableau); - shuffle($k); - $v = []; - foreach ($k as $cle) { - $v[$cle] = $this->tableau[$cle]; - } - $this->tableau = $v; - } else { - # {par valeur} - if ($r[1] == 'valeur') { - $tv = '%s'; - } # {par valeur/xx/yy} ?? - else { - $tv = 'table_valeur(%s, ' . var_export($r[1], true) . ')'; - } - $sortfunc .= ' - $a = ' . sprintf($tv, '$aa') . '; - $b = ' . sprintf($tv, '$bb') . '; - if ($a <> $b) - return ($a ' . (!empty($r[2]) ? '>' : '<') . ' $b) ? -1 : 1;'; - } - } - } - } - - if ($sortfunc) { - $sortfunc .= "\n return 0;"; - uasort($this->tableau, fn($aa, $bb) => eval($sortfunc)); - } - } - - - /** - * Grouper les resultats - * {fusion /x/y/z} - * - **/ - protected function select_groupby() { - // virer le / initial pour les criteres de la forme {fusion /xx} - if (strlen($fusion = ltrim($this->command['groupby'][0], '/'))) { - $vu = []; - foreach ($this->tableau as $k => $v) { - $val = table_valeur($v, $fusion); - if (isset($vu[$val])) { - unset($this->tableau[$k]); - } else { - $vu[$val] = true; - } - } - } - } - - - /** - * L'iterateur est-il encore valide ? - * - * @return bool - */ - public function valid(): bool { - return !is_null($this->cle); - } - - /** - * Retourner la valeur - * - * @return mixed - */ - #[\ReturnTypeWillChange] - public function current() { - return $this->valeur; - } - - /** - * Retourner la cle - * - * @return mixed - */ - #[\ReturnTypeWillChange] - public function key() { - return $this->cle; - } - - /** - * Passer a la valeur suivante - * - * @return void - */ - public function next(): void { - if ($this->valid()) { - $this->cle = key($this->tableau); - $this->valeur = current($this->tableau); - next($this->tableau); - } - } - - /** - * Compter le nombre total de resultats - * - * @return int - */ - public function count() { - if (is_null($this->total)) { - $this->total = count($this->tableau); - } - - return $this->total; - } -} - /* * Fonctions de transformation donnee => tableau */ diff --git a/ecrire/iterateur/php.php b/ecrire/iterateur/php.php index a86b65f9919a3b9c9ce96d52ccab2892914a50af..2a2b211390e438de88593e86ea2e267977ccba7d 100644 --- a/ecrire/iterateur/php.php +++ b/ecrire/iterateur/php.php @@ -45,15 +45,10 @@ function iterateur_php_dist($b, $iteratorName) { 'valeur' => 'STRING', ] ]; + foreach (get_class_methods($iteratorName) as $method) { $b->show['field'][strtolower($method)] = 'METHOD'; } - /* - foreach (get_class_vars($iteratorName) as $property) { - $b->show['field'][ strtolower($property) ] = 'PROPERTY'; - } - */ - return $b; } diff --git a/ecrire/iterateur/pour.php b/ecrire/iterateur/pour.php index 36cb2a349b935e64ed1dc53fcd9efd19bb5775e3..1e2805e77b8ba69108e9330bbf462188a995cccc 100644 --- a/ecrire/iterateur/pour.php +++ b/ecrire/iterateur/pour.php @@ -22,9 +22,6 @@ if (!defined('_ECRIRE_INC_VERSION')) { return; } -include_spip('iterateur/data'); - - /** * Créer une boucle sur un itérateur POUR * diff --git a/ecrire/public/compiler.php b/ecrire/public/compiler.php index 1bb6e50a19a304b412871f7307ace328c76aab85..24ea196dfed8850596c7dd6c7eefe3137de96cb0 100644 --- a/ecrire/public/compiler.php +++ b/ecrire/public/compiler.php @@ -451,7 +451,7 @@ define('CODE_CORPS_BOUCLE', '%s if (defined("_BOUCLE_PROFILER")) $timer = time()+(float)microtime(); $t0 = ""; // REQUETE - $iter = IterFactory::create( + $iter = Spip\\Core\\Iterateur\\Factory::create( "%s", %s, array(%s) @@ -465,8 +465,7 @@ define('CODE_CORPS_BOUCLE', '%s if (defined("_BOUCLE_PROFILER") AND 1000*($timer = (time()+(float)microtime())-$timer) > _BOUCLE_PROFILER) spip_log(intval(1000*$timer)."ms %s","profiler"._LOG_AVERTISSEMENT); - return $t0;' -); + return $t0;'); /** * Compilation d'une boucle (non recursive). diff --git a/ecrire/public/composer.php b/ecrire/public/composer.php index a3ada38a8297d2dbfd14d2c2887a91092b1df239..5561154276e77dcc7eb0371281cbc58da5d6af71 100644 --- a/ecrire/public/composer.php +++ b/ecrire/public/composer.php @@ -28,7 +28,6 @@ include_spip('inc/rubriques'); # pour calcul_branche (cf critere branche) include_spip('inc/acces'); // Gestion des acces pour ical include_spip('inc/actions'); include_spip('public/fonctions'); -include_spip('public/iterateur'); include_spip('public/interfaces'); include_spip('public/quete'); diff --git a/ecrire/public/normaliser.php b/ecrire/public/normaliser.php index 39a6f3e219907316cae5748af85ac36544462119..b98148f4b5d3a8e870eb097e8336b972434f1683 100644 --- a/ecrire/public/normaliser.php +++ b/ecrire/public/normaliser.php @@ -1,5 +1,6 @@ <?php +use Spip\Core\Champ; use Spip\Core\Texte; /***************************************************************************\ diff --git a/ecrire/src/Admin/Bouton.php b/ecrire/src/Admin/Bouton.php new file mode 100644 index 0000000000000000000000000000000000000000..90e85be1dc6744912020ff5b74393b0e8af4f749 --- /dev/null +++ b/ecrire/src/Admin/Bouton.php @@ -0,0 +1,63 @@ +<?php + +namespace Spip\Admin; + +/** + * Classe définissant un bouton dans la barre du haut de l'interface + * privée ou dans un de ses sous menus + */ +class Bouton { + /** L'icone à mettre dans le bouton */ + public string $icone; + + /** Le nom de l'entrée d'i18n associé */ + public string $libelle; + + /** @var null|string L'URL de la page (null => ?exec=nom) */ + public $url = null; + + /** @var null|string|array Arguments supplementaires de l'URL */ + public $urlArg = null; + + /** @var null|string URL du javascript */ + public $url2 = null; + + /** @var null|string Pour ouvrir dans une fenetre a part */ + public $target = null; + + /** Sous-barre de boutons / onglets */ + public array $sousmenu = []; + + /** Position dans le menu */ + public int $position = 0; + + /** Entrée favorite (sa position dans les favoris) ? */ + public int $favori = 0; + + + /** + * Définit un bouton + * + * @param string $icone + * L'icone à mettre dans le bouton + * @param string $libelle + * Le nom de l'entrée i18n associé + * @param null|string $url + * L'URL de la page + * @param null|string|array $urlArg + * Arguments supplémentaires de l'URL + * @param null|string $url2 + * URL du javascript + * @param null|mixed $target + * Pour ouvrir une fenêtre à part + */ + public function __construct($icone, $libelle, $url = null, $urlArg = null, $url2 = null, $target = null) { + $this->icone = $icone; + $this->libelle = $libelle; + $this->url = $url; + $this->urlArg = $urlArg; + $this->url2 = $url2; + $this->target = $target; + } +} + diff --git a/ecrire/src/Core/Iterateur/AbstractIterateur.php b/ecrire/src/Core/Iterateur/AbstractIterateur.php new file mode 100644 index 0000000000000000000000000000000000000000..ba5b6bc640c27d37d3d0257cb301c447ea7a47d1 --- /dev/null +++ b/ecrire/src/Core/Iterateur/AbstractIterateur.php @@ -0,0 +1,22 @@ +<?php + +namespace Spip\Core\Iterateur; + +abstract class AbstractIterateur +{ + /** + * Erreur presente ? + * + * @var bool + */ + public $err = false; + + public $command; + + public $info; + + public function __construct($command, $info = []) { + $this->command = $command; + $this->info = $info; + } +} diff --git a/ecrire/src/Core/Iterateur/Condition.php b/ecrire/src/Core/Iterateur/Condition.php new file mode 100644 index 0000000000000000000000000000000000000000..160b86c45db69d21b61bfabc9213273b07b67e1c --- /dev/null +++ b/ecrire/src/Core/Iterateur/Condition.php @@ -0,0 +1,20 @@ +<?php + +namespace Spip\Core\Iterateur; + +/** + * Iterateur CONDITION pour itérer sur des données. + * + * La boucle condition n'a toujours qu'un seul élément. + */ +class Condition extends Data +{ + /** + * Obtenir les données de la boucle CONDITION. + * + * @param array $command + */ + protected function select($command) { + $this->tableau = [0 => 1]; + } +} diff --git a/ecrire/src/Core/Iterateur/Data.php b/ecrire/src/Core/Iterateur/Data.php new file mode 100644 index 0000000000000000000000000000000000000000..cb28220efb292a642905f50267d09c94bdd4b559 --- /dev/null +++ b/ecrire/src/Core/Iterateur/Data.php @@ -0,0 +1,527 @@ +<?php + +namespace Spip\Core\Iterateur; + +use Exception; +use Iterator; + +/** + * Itérateur DATA. + * + * Pour itérer sur des données quelconques (transformables en tableau) + */ +class Data extends AbstractIterateur implements Iterator +{ + /** Tableau de données */ + protected array $tableau = []; + + /** + * Conditions de filtrage + * ie criteres de selection + */ + protected array $filtre = []; + + + /** + * Cle courante + * + * @var scalar + */ + protected $cle = null; + + /** + * Valeur courante + * + * @var mixed + */ + protected $valeur = null; + + protected string $type = 'DATA'; + + protected array $command = []; + + protected array $info = []; + + /** Erreur presente ? */ + public bool $err = false; + + /** + * Calcul du total des elements + * + * @var int|null + **/ + public $total = null; + + /** + * Constructeur + * + * @param $command + * @param array $info + */ + public function __construct($command, $info = []) { + $this->type = 'DATA'; + $this->command = $command; + $this->info = $info; + + $this->select($command); + } + + /** + * Revenir au depart + * + * @return void + */ + public function rewind(): void { + reset($this->tableau); + $this->cle = array_key_first($this->tableau); + $this->valeur = current($this->tableau); + next($this->tableau); + } + + /** + * Déclarer les critères exceptions + * + * @return array + */ + public function exception_des_criteres() { + return ['tableau']; + } + + /** + * Récupérer depuis le cache si possible + * + * @param string $cle + * @return mixed + */ + protected function cache_get($cle) { + if (!$cle) { + return; + } + # utiliser memoization si dispo + if (!function_exists('cache_get')) { + return; + } + + return cache_get($cle); + } + + /** + * Stocker en cache si possible + * + * @param string $cle + * @param int $ttl + * @param null|mixed $valeur + * @return bool + */ + protected function cache_set($cle, $ttl, $valeur = null) { + if (!$cle) { + return; + } + if (is_null($valeur)) { + $valeur = $this->tableau; + } + # utiliser memoization si dispo + if (!function_exists('cache_set')) { + return; + } + + return cache_set( + $cle, + [ + 'data' => $valeur, + 'time' => time(), + 'ttl' => $ttl + ], + 3600 + $ttl + ); + # conserver le cache 1h de plus que la validite demandee, + # pour le cas ou le serveur distant ne reponde plus + } + + /** + * Aller chercher les données de la boucle DATA + * + * @throws Exception + * @param array $command + * @return void + */ + protected function select($command) { + + // l'iterateur DATA peut etre appele en passant (data:type) + // le type se retrouve dans la commande 'from' + // dans ce cas la le critere {source}, si present, n'a pas besoin du 1er argument + if (isset($this->command['from'][0])) { + if (isset($this->command['source']) and is_array($this->command['source'])) { + array_unshift($this->command['source'], $this->command['sourcemode']); + } + $this->command['sourcemode'] = $this->command['from'][0]; + } + + // cherchons differents moyens de creer le tableau de donnees + // les commandes connues pour l'iterateur DATA + // sont : {tableau #ARRAY} ; {cle=...} ; {valeur=...} + + // {source format, [URL], [arg2]...} + if ( + isset($this->command['source']) + and isset($this->command['sourcemode']) + ) { + $this->select_source(); + } + + // Critere {liste X1, X2, X3} + if (isset($this->command['liste'])) { + $this->select_liste(); + } + if (isset($this->command['enum'])) { + $this->select_enum(); + } + + // Si a ce stade on n'a pas de table, il y a un bug + if (!is_array($this->tableau)) { + $this->err = true; + spip_log('erreur datasource ' . var_export($command, true)); + } + + // {datapath query.results} + // extraire le chemin "query.results" du tableau de donnees + if ( + !$this->err + and isset($this->command['datapath']) + and is_array($this->command['datapath']) + ) { + $this->select_datapath(); + } + + // tri {par x} + if ($this->command['orderby']) { + $this->select_orderby(); + } + + // grouper les resultats {fusion /x/y/z} ; + if ($this->command['groupby']) { + $this->select_groupby(); + } + + $this->rewind(); + #var_dump($this->tableau); + } + + + /** + * Aller chercher les donnees de la boucle DATA + * depuis une source + * {source format, [URL], [arg2]...} + */ + protected function select_source() { + # un peu crado : avant de charger le cache il faut charger + # les class indispensables, sinon PHP ne saura pas gerer + # l'objet en cache ; cf plugins/icalendar + # perf : pas de fonction table_to_array ! (table est deja un array) + if ( + isset($this->command['sourcemode']) + and !in_array($this->command['sourcemode'], ['table', 'array', 'tableau']) + ) { + charger_fonction($this->command['sourcemode'] . '_to_array', 'inc', true); + } + + # le premier argument peut etre un array, une URL etc. + $src = $this->command['source'][0]; + + # avons-nous un cache dispo ? + $cle = null; + if (is_string($src)) { + $cle = 'datasource_' . md5($this->command['sourcemode'] . ':' . var_export($this->command['source'], true)); + } + + $cache = $this->cache_get($cle); + if (isset($this->command['datacache'])) { + $ttl = intval($this->command['datacache']); + } + if ( + $cache + and ($cache['time'] + ($ttl ?? $cache['ttl']) + > time()) + and !(_request('var_mode') === 'recalcul' + and include_spip('inc/autoriser') + and autoriser('recalcul') + ) + ) { + $this->tableau = $cache['data']; + } else { + try { + if ( + isset($this->command['sourcemode']) + and in_array( + $this->command['sourcemode'], + ['table', 'array', 'tableau'] + ) + ) { + if ( + is_array($a = $src) + or (is_string($a) + and $a = str_replace('"', '"', $a) # fragile! + and is_array($a = @unserialize($a))) + ) { + $this->tableau = $a; + } + } else { + $data = $src; + if (is_string($src)) { + if (tester_url_absolue($src)) { + include_spip('inc/distant'); + $data = recuperer_url($src, ['taille_max' => _DATA_SOURCE_MAX_SIZE]); + $data = $data['page'] ?? ''; + if (!$data) { + throw new Exception('404'); + } + if (!isset($ttl)) { + $ttl = 24 * 3600; + } + } elseif (@is_dir($src)) { + $data = $src; + } elseif (@is_readable($src) && @is_file($src)) { + $data = spip_file_get_contents($src); + } + if (!isset($ttl)) { + $ttl = 10; + } + } + + if ( + !$this->err + and $data_to_array = charger_fonction($this->command['sourcemode'] . '_to_array', 'inc', true) + ) { + $args = $this->command['source']; + $args[0] = $data; + if (is_array($a = $data_to_array(...$args))) { + $this->tableau = $a; + } + } + } + + if (!is_array($this->tableau)) { + $this->err = true; + } + + if (!$this->err and isset($ttl) and $ttl > 0) { + $this->cache_set($cle, $ttl); + } + } catch (Exception $e) { + $e = $e->getMessage(); + $err = sprintf( + "[%s, %s] $e", + $src, + $this->command['sourcemode'] + ); + erreur_squelette([$err, []]); + $this->err = true; + } + } + + # en cas d'erreur, utiliser le cache si encore dispo + if ( + $this->err + and $cache + ) { + $this->tableau = $cache['data']; + $this->err = false; + } + } + + + /** + * Retourne un tableau donne depuis un critère liste + * + * Critère `{liste X1, X2, X3}` + * + * @see critere_DATA_liste_dist() + * + **/ + protected function select_liste() { + # s'il n'y a qu'une valeur dans la liste, sans doute une #BALISE + if (!isset($this->command['liste'][1])) { + if (!is_array($this->command['liste'][0])) { + $this->command['liste'] = explode(',', $this->command['liste'][0]); + } else { + $this->command['liste'] = $this->command['liste'][0]; + } + } + $this->tableau = $this->command['liste']; + } + + /** + * Retourne un tableau donne depuis un critere liste + * Critere {enum Xmin, Xmax} + * + **/ + protected function select_enum() { + # s'il n'y a qu'une valeur dans la liste, sans doute une #BALISE + if (!isset($this->command['enum'][1])) { + if (!is_array($this->command['enum'][0])) { + $this->command['enum'] = explode(',', $this->command['enum'][0]); + } else { + $this->command['enum'] = $this->command['enum'][0]; + } + } + if ((is_countable($this->command['enum']) ? count($this->command['enum']) : 0) >= 3) { + $enum = range( + array_shift($this->command['enum']), + array_shift($this->command['enum']), + array_shift($this->command['enum']) + ); + } else { + $enum = range(array_shift($this->command['enum']), array_shift($this->command['enum'])); + } + $this->tableau = $enum; + } + + + /** + * extraire le chemin "query.results" du tableau de donnees + * {datapath query.results} + * + **/ + protected function select_datapath() { + $base = reset($this->command['datapath']); + if (strlen($base = ltrim(trim($base), '/'))) { + $this->tableau = table_valeur($this->tableau, $base); + if (!is_array($this->tableau)) { + $this->tableau = []; + $this->err = true; + spip_log("datapath '$base' absent"); + } + } + } + + /** + * Ordonner les resultats + * {par x} + * + **/ + protected function select_orderby() { + $sortfunc = ''; + $aleas = 0; + foreach ($this->command['orderby'] as $tri) { + // virer le / initial pour les criteres de la forme {par /xx} + if (preg_match(',^\.?([/\w:_-]+)( DESC)?$,iS', ltrim($tri, '/'), $r)) { + $r = array_pad($r, 3, null); + + // tri par cle + if ($r[1] == 'cle') { + if (isset($r[2]) and $r[2]) { + krsort($this->tableau); + } else { + ksort($this->tableau); + } + } # {par hasard} + else { + if ($r[1] == 'hasard') { + $k = array_keys($this->tableau); + shuffle($k); + $v = []; + foreach ($k as $cle) { + $v[$cle] = $this->tableau[$cle]; + } + $this->tableau = $v; + } else { + # {par valeur} + if ($r[1] == 'valeur') { + $tv = '%s'; + } # {par valeur/xx/yy} ?? + else { + $tv = 'table_valeur(%s, ' . var_export($r[1], true) . ')'; + } + $sortfunc .= ' + $a = ' . sprintf($tv, '$aa') . '; + $b = ' . sprintf($tv, '$bb') . '; + if ($a <> $b) + return ($a ' . (!empty($r[2]) ? '>' : '<') . ' $b) ? -1 : 1;'; + } + } + } + } + + if ($sortfunc) { + $sortfunc .= "\n return 0;"; + uasort($this->tableau, fn($aa, $bb) => eval($sortfunc)); + } + } + + + /** + * Grouper les resultats + * {fusion /x/y/z} + * + **/ + protected function select_groupby() { + // virer le / initial pour les criteres de la forme {fusion /xx} + if (strlen($fusion = ltrim($this->command['groupby'][0], '/'))) { + $vu = []; + foreach ($this->tableau as $k => $v) { + $val = table_valeur($v, $fusion); + if (isset($vu[$val])) { + unset($this->tableau[$k]); + } else { + $vu[$val] = true; + } + } + } + } + + + /** + * L'iterateur est-il encore valide ? + * + * @return bool + */ + public function valid(): bool { + return !is_null($this->cle); + } + + /** + * Retourner la valeur + * + * @return mixed + */ + #[\ReturnTypeWillChange] + public function current() { + return $this->valeur; + } + + /** + * Retourner la cle + * + * @return mixed + */ + #[\ReturnTypeWillChange] + public function key() { + return $this->cle; + } + + /** + * Passer a la valeur suivante + * + * @return void + */ + public function next(): void { + if ($this->valid()) { + $this->cle = key($this->tableau); + $this->valeur = current($this->tableau); + next($this->tableau); + } + } + + /** + * Compter le nombre total de resultats + * + * @return int + */ + public function count() { + if (is_null($this->total)) { + $this->total = count($this->tableau); + } + + return $this->total; + } +} diff --git a/ecrire/public/iterateur.php b/ecrire/src/Core/Iterateur/Decorator.php similarity index 61% rename from ecrire/public/iterateur.php rename to ecrire/src/Core/Iterateur/Decorator.php index 77cd7885e23985fe03e62b50281fe7994a8aee2e..4e3c2154f0dd7268f94a1eeb7e487dbea8f006dd 100644 --- a/ecrire/public/iterateur.php +++ b/ecrire/src/Core/Iterateur/Decorator.php @@ -1,133 +1,40 @@ <?php -/***************************************************************************\ - * SPIP, Système de publication pour l'internet * - * * - * Copyright © avec tendresse depuis 2001 * - * Arnaud Martin, Antoine Pitrou, Philippe Rivière, Emmanuel Saint-James * - * * - * Ce programme est un logiciel libre distribué sous licence GNU/GPL. * - * Pour plus de détails voir le fichier COPYING.txt ou l'aide en ligne. * -\***************************************************************************/ - -if (!defined('_ECRIRE_INC_VERSION')) { - return; -} - -/** - * Fabrique d'iterateur - * permet de charger n'importe quel iterateur IterateurXXX - * fourni dans le fichier iterateurs/xxx.php - * - */ -class IterFactory { - public static function create($iterateur, $command, $info = null) { - - $iter = null; - // cas des SI {si expression} analises tres tot - // pour eviter le chargement de tout iterateur - if (isset($command['si'])) { - foreach ($command['si'] as $si) { - if (!$si) { - // $command pour boucle SQL peut generer des erreurs de compilation - // s'il est transmis alors qu'on est dans un iterateur vide - return new IterDecorator(new EmptyIterator(), [], $info); - } - } - } - - // chercher un iterateur PHP existant (par exemple dans SPL) - // (il faudrait passer l'argument ->sql_serveur - // pour etre certain qu'on est sur un "php:") - if (class_exists($iterateur)) { - $a = $command['args'] ?? []; - - // permettre de passer un Iterateur directement {args #ITERATEUR} : - // si on recoit deja un iterateur en argument, on l'utilise - if ((is_countable($a) ? count($a) : 0) == 1 and is_object($a[0]) and is_subclass_of($a[0], \Iterator::class)) { - $iter = $a[0]; - - // sinon, on cree un iterateur du type donne - } else { - // arguments de creation de l'iterateur... - // (pas glop) - try { - switch (is_countable($a) ? count($a) : 0) { - case 0: - $iter = new $iterateur(); - break; - case 1: - $iter = new $iterateur($a[0]); - break; - case 2: - $iter = new $iterateur($a[0], $a[1]); - break; - case 3: - $iter = new $iterateur($a[0], $a[1], $a[2]); - break; - case 4: - $iter = new $iterateur($a[0], $a[1], $a[2], $a[3]); - break; - } - } catch (Exception $e) { - spip_log("Erreur de chargement de l'iterateur $iterateur"); - spip_log($e->getMessage()); - $iter = new EmptyIterator(); - } - } - } else { - // chercher la classe d'iterateur - // IterateurXXX - // definie dans le fichier iterateurs/xxx.php - $class = 'Iterateur' . $iterateur; - if (!class_exists($class)) { - if ( - !include_spip('iterateur/' . strtolower($iterateur)) - or !class_exists($class) - ) { - die("Iterateur $iterateur non trouvé"); - // si l'iterateur n'existe pas, on se rabat sur le generique - # $iter = new EmptyIterator(); - } - } - $iter = new $class($command, $info); - } - - return new IterDecorator($iter, $command, $info); - } -} +namespace Spip\Core\Iterateur; +use Exception; +use FilterIterator; +use Iterator; -class IterDecorator extends FilterIterator { - private $iter; - +class Decorator extends FilterIterator +{ /** * Conditions de filtrage - * ie criteres de selection + * ie criteres de selection. * * @var array */ protected $filtre = []; /** - * Fonction de filtrage compilee a partir des criteres de filtre + * Fonction de filtrage compilee a partir des criteres de filtre. * * @var string */ - protected $func_filtre = null; + protected $func_filtre; /** - * Critere {offset, limit} + * Critere {offset, limit}. * * @var int * @var int */ - protected $offset = null; - protected $limit = null; + protected $offset; + protected $limit; /** * nombre d'elements recuperes depuis la position 0, - * en tenant compte des filtres + * en tenant compte des filtres. * * @var int */ @@ -137,23 +44,57 @@ class IterDecorator extends FilterIterator { * Y a t'il une erreur ? * * @var bool - **/ + */ protected $err = false; + // Extension SPIP des iterateurs PHP /** - * Drapeau a activer en cas d'echec - * (select SQL errone, non chargement des DATA, etc) + * type de l'iterateur. + * + * @var string */ - public function err() { - if (method_exists($this->iter, 'err')) { - return $this->iter->err(); - } - if (property_exists($this->iter, 'err')) { - return $this->iter->err; - } + protected $type; - return false; - } + /** + * parametres de l'iterateur. + * + * @var array + */ + protected $command; + + /** + * infos de compilateur. + * + * @var array + */ + protected $info; + + /** + * position courante de l'iterateur. + * + * @var int + */ + protected $pos; + + /** + * nombre total resultats dans l'iterateur. + * + * @var int + */ + protected $total; + + /** + * nombre maximal de recherche pour $total + * si l'iterateur n'implemente pas de fonction specifique. + */ + protected $max = 100000; + + /** + * Liste des champs a inserer dans les $row + * retournes par ->fetch(). + */ + protected $select = []; + private $iter; public function __construct(Iterator $iter, $command, $info) { parent::__construct($iter); @@ -182,20 +123,19 @@ class IterDecorator extends FilterIterator { //$this->total = $this->count(); } - - // calcule les elements a retournes par fetch() - // enleve les elements inutiles du select() - // - private function calculer_select() { - if ($select = &$this->command['select']) { - foreach ($select as $s) { - // /!\ $s = '.nom' - if ($s[0] == '.') { - $s = substr($s, 1); - } - $this->select[] = $s; - } + /** + * Drapeau a activer en cas d'echec + * (select SQL errone, non chargement des DATA, etc). + */ + public function err() { + if (method_exists($this->iter, 'err')) { + return $this->iter->err(); } + if (property_exists($this->iter, 'err')) { + return $this->iter->err; + } + + return false; } // recuperer la valeur d'une balise #X @@ -207,10 +147,10 @@ class IterDecorator extends FilterIterator { and method_exists($this->iter, $nom) ) { try { - return $this->iter->$nom(); + return $this->iter->{$nom}(); } catch (Exception $e) { // #GETCHILDREN sur un fichier de DirectoryIterator ... - spip_log("Methode $nom en echec sur " . get_class($this->iter)); + spip_log("Methode {$nom} en echec sur " . get_class($this->iter)); spip_log("Cela peut être normal : retour d'une ligne de resultat ne pouvant pas calculer cette methode"); return ''; @@ -226,57 +166,197 @@ class IterDecorator extends FilterIterator { in_array($nom, ['cle', 'valeur']) and method_exists($this, $nom) ) { - return $this->$nom(); + return $this->{$nom}(); } // Par defaut chercher en xpath dans la valeur() return table_valeur($this->valeur(), $nom, null); } + public function next(): void { + ++$this->pos; + parent::next(); + } - private function calculer_filtres() { + /** + * revient au depart. + */ + public function rewind(): void { + $this->pos = 0; + $this->fetched = 0; + parent::rewind(); + } - // Issu de calculer_select() de public/composer L.519 - // TODO: externaliser... - // - // retirer les criteres vides: - // {X ?} avec X absent de l'URL - // {par #ENV{X}} avec X absent de l'URL - // IN sur collection vide (ce dernier devrait pouvoir etre fait a la compil) - if ($where = &$this->command['where']) { - foreach ($where as $k => $v) { - $this->filtre[] = $this->traduire_condition_sql_en_filtre($v); - } + /** + * aller a la position absolue n, + * comptee depuis le debut. + * + * @param int $n + * absolute pos + * @param string $continue + * param for sql_ api + * + * @return bool + * success or fail if not implemented + */ + public function seek($n = 0, $continue = null) { + if ($this->func_filtre or !method_exists($this->iter, 'seek') or !$this->iter->seek($n)) { + $this->seek_loop($n); } + $this->pos = $n; + $this->fetched = $n; - // critere {2,7} - if (isset($this->command['limit']) and $this->command['limit']) { - $limit = explode(',', $this->command['limit']); - $this->offset = $limit[0]; - $this->limit = $limit[1]; + return true; + } + + /** + * Avancer de $saut pas. + * + * @param $saut + * @param $max + * + * @return int + */ + public function skip($saut, $max = null) { + // pas de saut en arriere autorise pour cette fonction + if (($saut = intval($saut)) <= 0) { + return $this->pos; + } + $seek = $this->pos + $saut; + // si le saut fait depasser le maxi, on libere la resource + // et on sort + if (is_null($max)) { + $max = $this->count(); } - // Creer la fonction de filtrage sur $this - if ($this->filtre) { - if ($filtres = $this->assembler_filtres($this->filtre)) { - $filtres = 'return ' . $filtres . ';'; - $this->func_filtre = fn() => eval($filtres); + if ($seek >= $max or $seek >= $this->count()) { + // sortie plus rapide que de faire next() jusqu'a la fin ! + $this->free(); + + return $max; + } + + $this->seek($seek); + + return $this->pos; + } + + /** + * Renvoyer un tableau des donnees correspondantes + * a la position courante de l'iterateur + * en controlant si on respecte le filtre + * Appliquer aussi le critere {offset,limit}. + * + * @return array|bool + */ + public function fetch() { + if (method_exists($this->iter, 'fetch')) { + return $this->iter->fetch(); + } + while ( + $this->valid() + and ( + !$this->accept() + or (isset($this->offset) and $this->fetched++ < $this->offset) + ) + ) { + $this->next(); + } + + if (!$this->valid()) { + return false; + } + + if ( + isset($this->limit) + and $this->fetched > $this->offset + $this->limit + ) { + return false; + } + + $r = []; + foreach ($this->select as $nom) { + $r[$nom] = $this->get_select($nom); + } + $this->next(); + + return $r; + } + + // retourner la cle pour #CLE + public function cle() { + return $this->key(); + } + + // retourner la valeur pour #VALEUR + public function valeur() { + return $this->current(); + } + + /** + * Accepte-t-on l'entree courante lue ? + * On execute les filtres pour le savoir. + */ + public function accept(): bool { + if ($f = $this->func_filtre) { + return $f(); + } + + return true; + } + + /** + * liberer la ressource. + * + * @return bool + */ + public function free() { + if (method_exists($this->iter, 'free')) { + $this->iter->free(); + } + $this->pos = $this->total = 0; + + return true; + } + + /** + * Compter le nombre total de resultats + * pour #TOTAL_BOUCLE. + * + * @return int + */ + public function count() { + if (is_null($this->total)) { + if ( + method_exists($this->iter, 'count') + and !$this->func_filtre + ) { + return $this->total = $this->iter->count(); } - else { - $this->func_filtre = null; + // compter les lignes et rembobiner + $total = 0; + $pos = $this->pos; // sauver la position + $this->rewind(); + while ($this->fetch() and $total < $this->max) { + ++$total; } + $this->seek($pos); + $this->total = $total; } + + return $this->total; } /** * Assembler le tableau de filtres traduits depuis les conditions SQL - * les filtres vides ou null sont ignores + * les filtres vides ou null sont ignores. + * * @param $filtres * @param string $operateur - * @return string|null + * + * @return null|string */ protected function assembler_filtres($filtres, $operateur = 'AND') { - $filtres_string = []; foreach ($filtres as $k => $v) { // si c'est un tableau de OR/AND + 2 sous-filtres, on recurse pour transformer en chaine @@ -294,30 +374,32 @@ class IterDecorator extends FilterIterator { return null; } - return '(' . implode(") $operateur (", $filtres_string) . ')'; + return '(' . implode(") {$operateur} (", $filtres_string) . ')'; } /** - * Traduire un element du tableau where SQL en un filtre + * Traduire un element du tableau where SQL en un filtre. + * * @param $v - * @return string|array|null + * + * @return null|array|string */ protected function traduire_condition_sql_en_filtre($v) { if (is_array($v)) { - if ((count($v) >= 2) && ($v[0] == 'REGEXP') && ($v[2] == "'.*'")) { + if ((count($v) >= 2) && ('REGEXP' == $v[0]) && ("'.*'" == $v[2])) { return 'true'; - } elseif ((count($v) >= 2) && ($v[0] == 'LIKE') && ($v[2] == "'%'")) { + } + if ((count($v) >= 2) && ('LIKE' == $v[0]) && ("'%'" == $v[2])) { return 'true'; - } else { - $op = $v[0] ?: $v; } + $op = $v[0] ?: $v; } else { $op = $v; } - if ((!$op) or ($op == 1) or ($op == '0=0')) { + if ((!$op) or (1 == $op) or ('0=0' == $op)) { return 'true'; } - if ($op === '0=1') { + if ('0=1' === $op) { return 'false'; } // traiter {cle IN a,b} ou {valeur !IN a,b} @@ -331,34 +413,32 @@ class IterDecorator extends FilterIterator { // * 3 : {x op y} ; on recoit $v[0] = 'op', $v[1] = x, $v[2] = y // 1 : forcement traite par un critere, on passe - if (!$v or !is_array($v) or count($v) == 1) { + if (!$v or !is_array($v) or 1 == count($v)) { return null; // sera ignore } - if (count($v) == 2 and is_array($v[1])) { + if (2 == count($v) and is_array($v[1])) { return $this->composer_filtre($v[1][1], $v[1][0], $v[1][2], 'NOT'); } - if (count($v) == 3) { + if (3 == count($v)) { // traiter le OR/AND suivi de 2 valeurs if (in_array($op, ['OR', 'AND'])) { array_shift($v); foreach (array_keys($v) as $k) { $v[$k] = $this->traduire_condition_sql_en_filtre($v[$k]); - if ($v[$k] === null) { + if (null === $v[$k]) { unset($v[$k]); - } - elseif ($v[$k] === 'true') { - if ($op === 'OR') { + } elseif ('true' === $v[$k]) { + if ('OR' === $op) { return 'true'; } - if ($op === 'AND') { + if ('AND' === $op) { unset($v[$k]); } - } - elseif ($v[$k] === 'false') { - if ($op === 'OR') { + } elseif ('false' === $v[$k]) { + if ('OR' === $op) { unset($v[$k]); } - if ($op === 'AND') { + if ('AND' === $op) { return 'false'; } } @@ -366,12 +446,14 @@ class IterDecorator extends FilterIterator { if (!count($v)) { return null; } - if (count($v) === 1) { + if (1 === count($v)) { return reset($v); } array_unshift($v, $op); + return $v; } + return $this->composer_filtre($v[1], $v[0], $v[2]); } @@ -379,12 +461,14 @@ class IterDecorator extends FilterIterator { } /** - * Calculer un filtre sur un champ du tableau + * Calculer un filtre sur un champ du tableau. + * * @param $cle * @param $op * @param $valeur * @param false $not - * @return string|null + * + * @return null|string */ protected function composer_filtre($cle, $op, $valeur, $not = false) { if (method_exists($this->iter, 'exception_des_criteres')) { @@ -393,31 +477,31 @@ class IterDecorator extends FilterIterator { } } // TODO: analyser le filtre pour refuser ce qu'on ne sait pas traiter ? - # mais c'est normalement deja opere par calculer_critere_infixe() - # qui regarde la description 'desc' (en casse reelle d'ailleurs : {isDir=1} - # ne sera pas vu si l'on a defini desc['field']['isdir'] pour que #ISDIR soit present. - # il faudrait peut etre definir les 2 champs isDir et isdir... a reflechir... + // mais c'est normalement deja opere par calculer_critere_infixe() + // qui regarde la description 'desc' (en casse reelle d'ailleurs : {isDir=1} + // ne sera pas vu si l'on a defini desc['field']['isdir'] pour que #ISDIR soit present. + // il faudrait peut etre definir les 2 champs isDir et isdir... a reflechir... - # if (!in_array($cle, array('cle', 'valeur'))) - # return; + // if (!in_array($cle, array('cle', 'valeur'))) + // return; $a = '$this->get_select(\'' . $cle . '\')'; $filtre = ''; - if ($op == 'REGEXP') { + if ('REGEXP' == $op) { $filtre = 'filtrer("match", ' . $a . ', ' . str_replace('\"', '"', $valeur) . ')'; $op = ''; } else { - if ($op == 'LIKE') { + if ('LIKE' == $op) { $valeur = str_replace(['\"', '_', '%'], ['"', '.', '.*'], preg_quote($valeur)); $filtre = 'filtrer("match", ' . $a . ', ' . $valeur . ')'; $op = ''; } else { - if ($op == '=') { + if ('=' == $op) { $op = '=='; } else { - if ($op == 'IN') { + if ('IN' == $op) { $filtre = 'in_array(' . $a . ', array' . $valeur . ')'; $op = ''; } else { @@ -435,99 +519,57 @@ class IterDecorator extends FilterIterator { } if ($not) { - $filtre = "!($filtre)"; + $filtre = "!({$filtre})"; } return $filtre; } - - public function next(): void { - $this->pos++; - parent::next(); - } - - /** - * revient au depart - * - * @return void - */ - public function rewind(): void { - $this->pos = 0; - $this->fetched = 0; - parent::rewind(); + // calcule les elements a retournes par fetch() + // enleve les elements inutiles du select() + // + private function calculer_select() { + if ($select = &$this->command['select']) { + foreach ($select as $s) { + // /!\ $s = '.nom' + if ('.' == $s[0]) { + $s = substr($s, 1); + } + $this->select[] = $s; + } + } } + private function calculer_filtres() { + // Issu de calculer_select() de public/composer L.519 + // TODO: externaliser... + // + // retirer les criteres vides: + // {X ?} avec X absent de l'URL + // {par #ENV{X}} avec X absent de l'URL + // IN sur collection vide (ce dernier devrait pouvoir etre fait a la compil) + if ($where = &$this->command['where']) { + foreach ($where as $k => $v) { + $this->filtre[] = $this->traduire_condition_sql_en_filtre($v); + } + } - # Extension SPIP des iterateurs PHP - /** - * type de l'iterateur - * - * @var string - */ - protected $type; - - /** - * parametres de l'iterateur - * - * @var array - */ - protected $command; - - /** - * infos de compilateur - * - * @var array - */ - protected $info; - - /** - * position courante de l'iterateur - * - * @var int - */ - protected $pos = null; - - /** - * nombre total resultats dans l'iterateur - * - * @var int - */ - protected $total = null; - - /** - * nombre maximal de recherche pour $total - * si l'iterateur n'implemente pas de fonction specifique - */ - protected $max = 100000; - - - /** - * Liste des champs a inserer dans les $row - * retournes par ->fetch() - */ - protected $select = []; - - - /** - * aller a la position absolue n, - * comptee depuis le debut - * - * @param int $n - * absolute pos - * @param string $continue - * param for sql_ api - * @return bool - * success or fail if not implemented - */ - public function seek($n = 0, $continue = null) { - if ($this->func_filtre or !method_exists($this->iter, 'seek') or !$this->iter->seek($n)) { - $this->seek_loop($n); + // critere {2,7} + if (isset($this->command['limit']) and $this->command['limit']) { + $limit = explode(',', $this->command['limit']); + $this->offset = $limit[0]; + $this->limit = $limit[1]; } - $this->pos = $n; - $this->fetched = $n; - return true; + // Creer la fonction de filtrage sur $this + if ($this->filtre) { + if ($filtres = $this->assembler_filtres($this->filtre)) { + $filtres = 'return ' . $filtres . ';'; + $this->func_filtre = fn () => eval($filtres); + } else { + $this->func_filtre = null; + } + } } /* @@ -545,143 +587,4 @@ class IterDecorator extends FilterIterator { return true; } - - /** - * Avancer de $saut pas - * - * @param $saut - * @param $max - * @return int - */ - public function skip($saut, $max = null) { - // pas de saut en arriere autorise pour cette fonction - if (($saut = intval($saut)) <= 0) { - return $this->pos; - } - $seek = $this->pos + $saut; - // si le saut fait depasser le maxi, on libere la resource - // et on sort - if (is_null($max)) { - $max = $this->count(); - } - - if ($seek >= $max or $seek >= $this->count()) { - // sortie plus rapide que de faire next() jusqu'a la fin ! - $this->free(); - - return $max; - } - - $this->seek($seek); - - return $this->pos; - } - - /** - * Renvoyer un tableau des donnees correspondantes - * a la position courante de l'iterateur - * en controlant si on respecte le filtre - * Appliquer aussi le critere {offset,limit} - * - * @return array|bool - */ - public function fetch() { - if (method_exists($this->iter, 'fetch')) { - return $this->iter->fetch(); - } else { - while ( - $this->valid() - and ( - !$this->accept() - or (isset($this->offset) and $this->fetched++ < $this->offset) - ) - ) { - $this->next(); - } - - if (!$this->valid()) { - return false; - } - - if ( - isset($this->limit) - and $this->fetched > $this->offset + $this->limit - ) { - return false; - } - - $r = []; - foreach ($this->select as $nom) { - $r[$nom] = $this->get_select($nom); - } - $this->next(); - - return $r; - } - } - - // retourner la cle pour #CLE - public function cle() { - return $this->key(); - } - - // retourner la valeur pour #VALEUR - public function valeur() { - return $this->current(); - } - - /** - * Accepte-t-on l'entree courante lue ? - * On execute les filtres pour le savoir. - **/ - public function accept(): bool { - if ($f = $this->func_filtre) { - return $f(); - } - - return true; - } - - /** - * liberer la ressource - * - * @return bool - */ - public function free() { - if (method_exists($this->iter, 'free')) { - $this->iter->free(); - } - $this->pos = $this->total = 0; - - return true; - } - - /** - * Compter le nombre total de resultats - * pour #TOTAL_BOUCLE - * - * @return int - */ - public function count() { - if (is_null($this->total)) { - if ( - method_exists($this->iter, 'count') - and !$this->func_filtre - ) { - return $this->total = $this->iter->count(); - } else { - // compter les lignes et rembobiner - $total = 0; - $pos = $this->pos; // sauver la position - $this->rewind(); - while ($this->fetch() and $total < $this->max) { - $total++; - } - $this->seek($pos); - $this->total = $total; - } - } - - return $this->total; - } } diff --git a/ecrire/src/Core/Iterateur/Factory.php b/ecrire/src/Core/Iterateur/Factory.php new file mode 100644 index 0000000000000000000000000000000000000000..d04a5fdb1bf1fe5041d2f33ce0ede065e9640f9f --- /dev/null +++ b/ecrire/src/Core/Iterateur/Factory.php @@ -0,0 +1,92 @@ +<?php + +namespace Spip\Core\Iterateur; + +use EmptyIterator; +use Exception; + +/** + * Fabrique d'iterateur + * permet de charger n'importe quel iterateur IterateurXXX + * fourni dans le fichier iterateurs/xxx.php. + */ +class Factory +{ + public static function create($iterateur, $command, $info = null) { + $iter = null; + // cas des SI {si expression} analises tres tot + // pour eviter le chargement de tout iterateur + if (isset($command['si'])) { + foreach ($command['si'] as $si) { + if (!$si) { + // $command pour boucle SQL peut generer des erreurs de compilation + // s'il est transmis alors qu'on est dans un iterateur vide + return new Decorator(new EmptyIterator(), [], $info); + } + } + } + + // chercher un iterateur PHP existant (par exemple dans SPL) + // (il faudrait passer l'argument ->sql_serveur + // pour etre certain qu'on est sur un "php:") + if (class_exists($iterateur)) { + $a = $command['args'] ?? []; + + // permettre de passer un Iterateur directement {args #ITERATEUR} : + // si on recoit deja un iterateur en argument, on l'utilise + if ((is_countable($a) ? count($a) : 0) == 1 and is_object($a[0]) and is_subclass_of($a[0], \Iterator::class)) { + $iter = $a[0]; + + // sinon, on cree un iterateur du type donne + } else { + // arguments de creation de l'iterateur... + // (pas glop) + try { + switch (is_countable($a) ? count($a) : 0) { + case 0: + $iter = new $iterateur(); + + break; + + case 1: + $iter = new $iterateur($a[0]); + + break; + + case 2: + $iter = new $iterateur($a[0], $a[1]); + + break; + + case 3: + $iter = new $iterateur($a[0], $a[1], $a[2]); + + break; + + case 4: + $iter = new $iterateur($a[0], $a[1], $a[2], $a[3]); + + break; + } + } catch (Exception $e) { + spip_log("Erreur de chargement de l'iterateur {$iterateur}"); + spip_log($e->getMessage()); + $iter = new EmptyIterator(); + } + } + } else { + // chercher la classe d'iterateur + // IterateurXXX + // definie dans le fichier src/Core/Iterateur/xxx.php + $class = 'Spip\\Core\\Iterateur\\' . ucfirst($iterateur); + if (!class_exists($class)) { + exit("Iterateur {$iterateur} non trouvé"); + // si l'iterateur n'existe pas, on se rabat sur le generique + // $iter = new EmptyIterator(); + } + $iter = new $class($command, $info); + } + + return new Decorator($iter, $command, $info); + } +} diff --git a/ecrire/iterateur/sql.php b/ecrire/src/Core/Iterateur/Sql.php similarity index 66% rename from ecrire/iterateur/sql.php rename to ecrire/src/Core/Iterateur/Sql.php index d85d549f6d260397b4827d04188a54a4a9b1ce38..95bc624dbdd964a2d69bbd9806ec6e75e733cb91 100644 --- a/ecrire/iterateur/sql.php +++ b/ecrire/src/Core/Iterateur/Sql.php @@ -1,45 +1,36 @@ <?php -/***************************************************************************\ - * SPIP, Système de publication pour l'internet * - * * - * Copyright © avec tendresse depuis 2001 * - * Arnaud Martin, Antoine Pitrou, Philippe Rivière, Emmanuel Saint-James * - * * - * Ce programme est un logiciel libre distribué sous licence GNU/GPL. * - * Pour plus de détails voir le fichier COPYING.txt ou l'aide en ligne. * -\***************************************************************************/ - -/** - * Gestion de l'itérateur SQL - * - * @package SPIP\Core\Iterateur\SQL - **/ - -if (!defined('_ECRIRE_INC_VERSION')) { - return; -} +namespace Spip\Core\Iterateur; +use Iterator; /** - * Itérateur SQL + * Itérateur SQL. * * Permet d'itérer sur des données en base de données */ -class IterateurSQL implements Iterator { +class Sql extends AbstractIterateur implements Iterator +{ /** - * Ressource sql + * Calcul du total des elements. * - * @var Object|bool + * @var null|int + */ + public $total; + + /** + * Ressource sql. + * + * @var bool|object */ protected $sqlresult = false; /** - * row sql courante + * row sql courante. * - * @var array|null + * @var null|array */ - protected $row = null; + protected $row; protected bool $firstseek = false; @@ -55,57 +46,18 @@ class IterateurSQL implements Iterator { /** Erreur presente ? **/ public bool $err = false; - /** - * Calcul du total des elements - * - * @var int|null - **/ - public $total = null; - - /** - * selectionner les donnees, ie faire la requete SQL - * - * @return void - */ - protected function select() { - $this->row = null; - $v = &$this->command; - $this->sqlresult = calculer_select( - $v['select'], - $v['from'], - $v['type'], - $v['where'], - $v['join'], - $v['groupby'], - $v['orderby'], - $v['limit'], - $v['having'], - $v['table'], - $v['id'], - $v['connect'], - $this->info - ); - $this->err = !$this->sqlresult; - $this->firstseek = false; - $this->pos = -1; - - // pas d'init a priori, le calcul ne sera fait qu'en cas de besoin (provoque une double requete souvent inutile en sqlite) - //$this->total = $this->count(); - } - /* * array command: les commandes d'initialisation * array info: les infos sur le squelette */ public function __construct($command, $info = []) { - $this->type = 'SQL'; - $this->command = $command; - $this->info = $info; + parent::__construct($command, $info); + $this->select(); } /** - * Rembobiner + * Rembobiner. * * @return bool */ @@ -116,9 +68,7 @@ class IterateurSQL implements Iterator { } /** - * Verifier l'etat de l'iterateur - * - * @return bool + * Verifier l'etat de l'iterateur. */ public function valid(): bool { if ($this->err) { @@ -132,7 +82,7 @@ class IterateurSQL implements Iterator { } /** - * Valeurs sur la position courante + * Valeurs sur la position courante. * * @return array */ @@ -147,10 +97,11 @@ class IterateurSQL implements Iterator { } /** - * Sauter a une position absolue + * Sauter a une position absolue. * - * @param int $n + * @param int $n * @param null|string $continue + * * @return bool */ public function seek($n = 0, $continue = null) { @@ -174,20 +125,18 @@ class IterateurSQL implements Iterator { } /** - * Avancer d'un cran - * - * @return void + * Avancer d'un cran. */ public function next(): void { $this->row = sql_fetch($this->sqlresult, $this->command['connect']); - $this->pos++; + ++$this->pos; $this->firstseek |= true; } /** - * Avancer et retourner les donnees pour le nouvel element + * Avancer et retourner les donnees pour le nouvel element. * - * @return array|bool|null + * @return null|array|bool */ public function fetch() { if ($this->valid()) { @@ -201,7 +150,7 @@ class IterateurSQL implements Iterator { } /** - * liberer les ressources + * liberer les ressources. * * @return bool */ @@ -216,7 +165,7 @@ class IterateurSQL implements Iterator { } /** - * Compter le nombre de resultats + * Compter le nombre de resultats. * * @return int */ @@ -225,7 +174,7 @@ class IterateurSQL implements Iterator { if (!$this->sqlresult) { $this->total = 0; } else { - # cas count(*) + // cas count(*) if (in_array('count(*)', $this->command['select'])) { $this->valid(); $s = $this->current(); @@ -238,4 +187,33 @@ class IterateurSQL implements Iterator { return $this->total; } + + /** + * selectionner les donnees, ie faire la requete SQL. + */ + protected function select() { + $this->row = null; + $v = &$this->command; + $this->sqlresult = calculer_select( + $v['select'], + $v['from'], + $v['type'], + $v['where'], + $v['join'], + $v['groupby'], + $v['orderby'], + $v['limit'], + $v['having'], + $v['table'], + $v['id'], + $v['connect'], + $this->info + ); + $this->err = !$this->sqlresult; + $this->firstseek = false; + $this->pos = -1; + + // pas d'init a priori, le calcul ne sera fait qu'en cas de besoin (provoque une double requete souvent inutile en sqlite) + //$this->total = $this->count(); + } } diff --git a/ecrire/src/Css/Vars/Collection.php b/ecrire/src/Css/Vars/Collection.php new file mode 100644 index 0000000000000000000000000000000000000000..94f94a5515d9bab43dc6d624bf13972ffb6c1e59 --- /dev/null +++ b/ecrire/src/Css/Vars/Collection.php @@ -0,0 +1,27 @@ +<?php + +namespace Spip\Css\Vars; + +/** + * Collection de variables CSS + * @internal + */ +class Collection { + private array $vars = []; + + public function add(string $var, string $value) { + $this->vars[$var] = $value; + } + + public function getString(): string { + $string = ''; + foreach ($this->vars as $key => $value) { + $string .= "$key: $value;\n"; + } + return $string; + } + + public function __toString(): string { + return $this->getString(); + } +} diff --git a/ecrire/src/I18n/Description.php b/ecrire/src/I18n/Description.php new file mode 100644 index 0000000000000000000000000000000000000000..ab7b5a356fca0584c76b198191e97a7ab0417c69 --- /dev/null +++ b/ecrire/src/I18n/Description.php @@ -0,0 +1,18 @@ +<?php + +namespace Spip\I18n; + +class Description { + /** @var string code de langue (hors module) */ + public $code; + /** @var string nom du module de langue */ + public $module; + /** @var string langue de la traduction */ + public $langue; + /** @var string traduction */ + public $texte; + /** @var string var mode particulier appliqué ? */ + public $mode; + /** @var bool Corrections des textes appliqué ? */ + public $corrections = false; +} diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 2fa8a7ecb3914c31493a99976405cd6071e0187a..bf745cbd88ed946795cf200a1b4adf15d2a8d9ff 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -45,63 +45,23 @@ parameters: count: 1 path: ecrire/inc/utils.php - - - message: "#^Class IterateurDATA referenced with incorrect case\\: IterateurData\\.$#" - count: 1 - path: ecrire/iterateur/condition.php - - - - message: "#^Access to an undefined property IterateurDATA\\:\\:\\$command\\.$#" - count: 32 - path: ecrire/iterateur/data.php - - - - message: "#^Access to an undefined property IterateurDATA\\:\\:\\$info\\.$#" - count: 1 - path: ecrire/iterateur/data.php - - - - message: "#^Access to an undefined property IterateurDATA\\:\\:\\$type\\.$#" - count: 1 - path: ecrire/iterateur/data.php - - message: "#^Function analyser_backend not found\\.$#" count: 1 path: ecrire/iterateur/data.php - - - message: "#^Access to an undefined property IterateurSQL\\:\\:\\$command\\.$#" - count: 7 - path: ecrire/iterateur/sql.php - - - - message: "#^Access to an undefined property IterateurSQL\\:\\:\\$info\\.$#" - count: 2 - path: ecrire/iterateur/sql.php - - - - message: "#^Access to an undefined property IterateurSQL\\:\\:\\$pos\\.$#" - count: 7 - path: ecrire/iterateur/sql.php - - - - message: "#^Access to an undefined property IterateurSQL\\:\\:\\$type\\.$#" - count: 1 - path: ecrire/iterateur/sql.php - - message: "#^Function inclure_balise_dynamique\\(\\) should return string but return statement is missing\\.$#" count: 1 path: ecrire/public/assembler.php - - message: "#^Function balise_COMPTEUR_BOUCLE_dist\\(\\) should return Champ but return statement is missing\\.$#" + message: "#^Function balise_COMPTEUR_BOUCLE_dist\\(\\) should return Spip\\\\Core\\\\Champ but return statement is missing\\.$#" count: 1 path: ecrire/public/balises.php - - message: "#^Function balise_FILTRE_dist\\(\\) should return Champ but return statement is missing\\.$#" + message: "#^Function balise_FILTRE_dist\\(\\) should return Spip\\\\Core\\\\Champ but return statement is missing\\.$#" count: 1 path: ecrire/public/balises.php diff --git a/prive/themes/spip/vars.css_fonctions.php b/prive/themes/spip/vars.css_fonctions.php index 58f181c90ff76a6c01546343c5628617fac14ba5..a0884a3c94281aea1e6584b7620ed2de1963d51f 100644 --- a/prive/themes/spip/vars.css_fonctions.php +++ b/prive/themes/spip/vars.css_fonctions.php @@ -1,36 +1,14 @@ <?php -/** - * Collection de variables CSS - * @internal - */ -class Spip_Css_Vars_Collection { - private array $vars = []; - - public function add(string $var, string $value) { - $this->vars[$var] = $value; - } - - public function getString(): string { - $string = ''; - foreach ($this->vars as $key => $value) { - $string .= "$key: $value;\n"; - } - return $string; - } - - public function __toString(): string { - return $this->getString(); - } -} +use Spip\Css\Vars\Collection; /** * Génère les variables CSS relatif à la typo et langue pour l'espace privé * * @param Pile $pile Pile */ -function spip_generer_variables_css_typo(array $Pile): \Spip_Css_Vars_Collection { - $vars = new \Spip_Css_Vars_Collection(); +function spip_generer_variables_css_typo(array $Pile): Collection { + $vars = new Collection(); // Direction $vars->add('--spip-dir', $Pile[0]['dir']); @@ -90,8 +68,8 @@ function spip_generer_variables_css_typo(array $Pile): \Spip_Css_Vars_Collection * * @param string $couleur Couleur hex */ -function spip_generer_variables_css_couleurs_theme(string $couleur): \Spip_Css_Vars_Collection { - $vars = new \Spip_Css_Vars_Collection(); +function spip_generer_variables_css_couleurs_theme(string $couleur): Collection { + $vars = new Collection(); #$vars->add('--spip-color-theme--hsl', couleur_hex_to_hsl($couleur, 'h, s, l')); // redéfini ensuite $vars->add('--spip-color-theme--h', couleur_hex_to_hsl($couleur, 'h')); @@ -119,8 +97,8 @@ function spip_generer_variables_css_couleurs_theme(string $couleur): \Spip_Css_V /** * Génère les variables CSS de couleurs, dont celles dépendantes des couleurs du thème actif. */ -function spip_generer_variables_css_couleurs(): \Spip_Css_Vars_Collection { - $vars = new \Spip_Css_Vars_Collection(); +function spip_generer_variables_css_couleurs(): Collection { + $vars = new Collection(); // nos déclinaisons de couleur (basées sur le dégradé précedent, où 60 est là couleur du thème) $vars->add('--spip-color-theme-white--hsl', 'var(--spip-color-theme--100)');