@@ -15,98 +15,98 @@ |
||
| 15 | 15 | * Collecte les raccourcis liens [titre->url] de SPIP |
| 16 | 16 | */ |
| 17 | 17 | class Liens extends AbstractCollecteur { |
| 18 | - protected static string $markPrefix = 'LIEN'; |
|
| 19 | - |
|
| 20 | - /** |
|
| 21 | - * La preg pour découper et collecter les modèles |
|
| 22 | - * @var string |
|
| 23 | - */ |
|
| 24 | - protected string $preg_lien; |
|
| 25 | - |
|
| 26 | - public function __construct(?string $preg = null) { |
|
| 27 | - |
|
| 28 | - // Regexp des raccourcis, aussi utilisee pour la fusion de sauvegarde Spip |
|
| 29 | - // Laisser passer des paires de crochets pour la balise multi |
|
| 30 | - // mais refuser plus d'imbrications ou de mauvaises imbrications |
|
| 31 | - // sinon les crochets ne peuvent plus servir qu'a ce type de raccourci |
|
| 32 | - $this->preg_lien = ($preg ?: '/\[([^][]*?([[][^]>-]*[]][^][]*)*)->(>?)([^]]*)\]/msS'); |
|
| 33 | - } |
|
| 34 | - |
|
| 35 | - |
|
| 36 | - /** |
|
| 37 | - * Sanitizer une collection d'occurences de liens : il faut sanitizer le href et le texte uniquement |
|
| 38 | - * |
|
| 39 | - * @param array $collection |
|
| 40 | - * @param string $sanitize_callback |
|
| 41 | - * @return array |
|
| 42 | - */ |
|
| 43 | - protected function sanitizer_collection(array $collection, string $sanitize_callback): array { |
|
| 44 | - foreach ($collection as &$lien) { |
|
| 45 | - $t = $sanitize_callback($lien['texte']); |
|
| 46 | - if ($t !== $lien['texte']) { |
|
| 47 | - $lien['raw'] = str_replace($lien['texte'], $t, $lien['raw']); |
|
| 48 | - $lien['texte'] = $t; |
|
| 49 | - } |
|
| 50 | - $href = $sanitize_callback($lien['href']); |
|
| 51 | - if ($href !== $lien['href']) { |
|
| 52 | - $lien['raw'] = str_replace($lien['href'], $href, $lien['raw']); |
|
| 53 | - $lien['href'] = $href; |
|
| 54 | - } |
|
| 55 | - } |
|
| 56 | - |
|
| 57 | - return $collection; |
|
| 58 | - } |
|
| 59 | - |
|
| 60 | - /** |
|
| 61 | - * @param string $texte |
|
| 62 | - * @param array $options |
|
| 63 | - * bool $collecter_liens |
|
| 64 | - * @return array |
|
| 65 | - */ |
|
| 66 | - public function collecter(string $texte, array $options = []): array { |
|
| 67 | - if (!$texte) { |
|
| 68 | - return []; |
|
| 69 | - } |
|
| 70 | - |
|
| 71 | - $liens = []; |
|
| 72 | - if (str_contains($texte, '->')) { |
|
| 73 | - $desechappe_crochets = false; |
|
| 74 | - // si il y a un crochet ouvrant échappé ou un crochet fermant échappé, les substituer pour les ignorer |
|
| 75 | - if (str_contains($texte, '\[') || str_contains($texte, '\]')) { |
|
| 76 | - $texte = str_replace(['\[', '\]'], ["\x1\x5", "\x1\x6"], $texte); |
|
| 77 | - $desechappe_crochets = true; |
|
| 78 | - } |
|
| 79 | - |
|
| 80 | - // collecter les matchs de la preg |
|
| 81 | - $liens = static::collecteur($texte, '->', '[', $this->preg_lien, empty($options['detecter_presence']) ? 0 : 1); |
|
| 82 | - |
|
| 83 | - // si on veut seulement detecter la présence, on peut retourner tel quel |
|
| 84 | - if (empty($options['detecter_presence'])) { |
|
| 85 | - foreach ($liens as $k => &$lien) { |
|
| 86 | - $lien['href'] = end($lien['match']); |
|
| 87 | - $lien['texte'] = $lien['match'][1]; |
|
| 88 | - $lien['ouvrant'] = $lien['match'][3] ?? ''; |
|
| 89 | - |
|
| 90 | - // la mise en lien automatique est passee par la a tort ! |
|
| 91 | - // corrigeons pour eviter d'avoir un <a...> dans un href... |
|
| 92 | - if (str_starts_with($lien['href'], '<a')) { |
|
| 93 | - $href = extraire_attribut($lien['href'], 'href'); |
|
| 94 | - // remplacons dans la source qui peut etre reinjectee dans les arguments |
|
| 95 | - // d'un modele |
|
| 96 | - $lien['raw'] = str_replace($lien['href'], $href, $lien['raw']); |
|
| 97 | - // et prenons le href comme la vraie url a linker |
|
| 98 | - $lien['href'] = $href; |
|
| 99 | - } |
|
| 100 | - |
|
| 101 | - if ($desechappe_crochets && str_contains($lien['raw'], "\x1")) { |
|
| 102 | - $lien['raw'] = str_replace(["\x1\x5", "\x1\x6"], ['[', ']'], $lien['raw']); |
|
| 103 | - $lien['texte'] = str_replace(["\x1\x5", "\x1\x6"], ['[', ']'], $lien['texte']); |
|
| 104 | - $lien['href'] = str_replace(["\x1\x5", "\x1\x6"], ['[', ']'], $lien['href']); |
|
| 105 | - } |
|
| 106 | - } |
|
| 107 | - } |
|
| 108 | - } |
|
| 109 | - |
|
| 110 | - return $liens; |
|
| 111 | - } |
|
| 18 | + protected static string $markPrefix = 'LIEN'; |
|
| 19 | + |
|
| 20 | + /** |
|
| 21 | + * La preg pour découper et collecter les modèles |
|
| 22 | + * @var string |
|
| 23 | + */ |
|
| 24 | + protected string $preg_lien; |
|
| 25 | + |
|
| 26 | + public function __construct(?string $preg = null) { |
|
| 27 | + |
|
| 28 | + // Regexp des raccourcis, aussi utilisee pour la fusion de sauvegarde Spip |
|
| 29 | + // Laisser passer des paires de crochets pour la balise multi |
|
| 30 | + // mais refuser plus d'imbrications ou de mauvaises imbrications |
|
| 31 | + // sinon les crochets ne peuvent plus servir qu'a ce type de raccourci |
|
| 32 | + $this->preg_lien = ($preg ?: '/\[([^][]*?([[][^]>-]*[]][^][]*)*)->(>?)([^]]*)\]/msS'); |
|
| 33 | + } |
|
| 34 | + |
|
| 35 | + |
|
| 36 | + /** |
|
| 37 | + * Sanitizer une collection d'occurences de liens : il faut sanitizer le href et le texte uniquement |
|
| 38 | + * |
|
| 39 | + * @param array $collection |
|
| 40 | + * @param string $sanitize_callback |
|
| 41 | + * @return array |
|
| 42 | + */ |
|
| 43 | + protected function sanitizer_collection(array $collection, string $sanitize_callback): array { |
|
| 44 | + foreach ($collection as &$lien) { |
|
| 45 | + $t = $sanitize_callback($lien['texte']); |
|
| 46 | + if ($t !== $lien['texte']) { |
|
| 47 | + $lien['raw'] = str_replace($lien['texte'], $t, $lien['raw']); |
|
| 48 | + $lien['texte'] = $t; |
|
| 49 | + } |
|
| 50 | + $href = $sanitize_callback($lien['href']); |
|
| 51 | + if ($href !== $lien['href']) { |
|
| 52 | + $lien['raw'] = str_replace($lien['href'], $href, $lien['raw']); |
|
| 53 | + $lien['href'] = $href; |
|
| 54 | + } |
|
| 55 | + } |
|
| 56 | + |
|
| 57 | + return $collection; |
|
| 58 | + } |
|
| 59 | + |
|
| 60 | + /** |
|
| 61 | + * @param string $texte |
|
| 62 | + * @param array $options |
|
| 63 | + * bool $collecter_liens |
|
| 64 | + * @return array |
|
| 65 | + */ |
|
| 66 | + public function collecter(string $texte, array $options = []): array { |
|
| 67 | + if (!$texte) { |
|
| 68 | + return []; |
|
| 69 | + } |
|
| 70 | + |
|
| 71 | + $liens = []; |
|
| 72 | + if (str_contains($texte, '->')) { |
|
| 73 | + $desechappe_crochets = false; |
|
| 74 | + // si il y a un crochet ouvrant échappé ou un crochet fermant échappé, les substituer pour les ignorer |
|
| 75 | + if (str_contains($texte, '\[') || str_contains($texte, '\]')) { |
|
| 76 | + $texte = str_replace(['\[', '\]'], ["\x1\x5", "\x1\x6"], $texte); |
|
| 77 | + $desechappe_crochets = true; |
|
| 78 | + } |
|
| 79 | + |
|
| 80 | + // collecter les matchs de la preg |
|
| 81 | + $liens = static::collecteur($texte, '->', '[', $this->preg_lien, empty($options['detecter_presence']) ? 0 : 1); |
|
| 82 | + |
|
| 83 | + // si on veut seulement detecter la présence, on peut retourner tel quel |
|
| 84 | + if (empty($options['detecter_presence'])) { |
|
| 85 | + foreach ($liens as $k => &$lien) { |
|
| 86 | + $lien['href'] = end($lien['match']); |
|
| 87 | + $lien['texte'] = $lien['match'][1]; |
|
| 88 | + $lien['ouvrant'] = $lien['match'][3] ?? ''; |
|
| 89 | + |
|
| 90 | + // la mise en lien automatique est passee par la a tort ! |
|
| 91 | + // corrigeons pour eviter d'avoir un <a...> dans un href... |
|
| 92 | + if (str_starts_with($lien['href'], '<a')) { |
|
| 93 | + $href = extraire_attribut($lien['href'], 'href'); |
|
| 94 | + // remplacons dans la source qui peut etre reinjectee dans les arguments |
|
| 95 | + // d'un modele |
|
| 96 | + $lien['raw'] = str_replace($lien['href'], $href, $lien['raw']); |
|
| 97 | + // et prenons le href comme la vraie url a linker |
|
| 98 | + $lien['href'] = $href; |
|
| 99 | + } |
|
| 100 | + |
|
| 101 | + if ($desechappe_crochets && str_contains($lien['raw'], "\x1")) { |
|
| 102 | + $lien['raw'] = str_replace(["\x1\x5", "\x1\x6"], ['[', ']'], $lien['raw']); |
|
| 103 | + $lien['texte'] = str_replace(["\x1\x5", "\x1\x6"], ['[', ']'], $lien['texte']); |
|
| 104 | + $lien['href'] = str_replace(["\x1\x5", "\x1\x6"], ['[', ']'], $lien['href']); |
|
| 105 | + } |
|
| 106 | + } |
|
| 107 | + } |
|
| 108 | + } |
|
| 109 | + |
|
| 110 | + return $liens; |
|
| 111 | + } |
|
| 112 | 112 | } |
@@ -12,144 +12,144 @@ |
||
| 12 | 12 | namespace Spip\Texte\Collecteur; |
| 13 | 13 | |
| 14 | 14 | abstract class AbstractCollecteur { |
| 15 | - protected static string $markPrefix = 'COLLECT'; |
|
| 16 | - protected string $markId; |
|
| 17 | - |
|
| 18 | - /** |
|
| 19 | - * Collecteur générique des occurences d'une preg dans un texte avec leurs positions et longueur |
|
| 20 | - * @param string $texte |
|
| 21 | - * texte à analyser pour la collecte |
|
| 22 | - * @param string $if_chars |
|
| 23 | - * caractere(s) à tester avant de tenter la preg |
|
| 24 | - * @param string $start_with |
|
| 25 | - * caractere(s) par lesquels commencent l'expression recherchée (permet de démarrer la preg à la prochaine occurence de cette chaine) |
|
| 26 | - * @param string $preg |
|
| 27 | - * preg utilisée pour la collecte |
|
| 28 | - * @param int $max_items |
|
| 29 | - * pour limiter le nombre de preg collectée (pour la detection simple de présence par exemple) |
|
| 30 | - * @return array |
|
| 31 | - */ |
|
| 32 | - protected static function collecteur(string $texte, string $if_chars, string $start_with, string $preg, int $max_items = 0): array { |
|
| 33 | - |
|
| 34 | - $collection = []; |
|
| 35 | - $pos = 0; |
|
| 36 | - while ( |
|
| 37 | - (!$if_chars || str_contains($texte, $if_chars)) |
|
| 38 | - && ($next = ($start_with ? strpos($texte, $start_with, $pos) : $pos)) !== false |
|
| 39 | - && preg_match($preg, $texte, $r, PREG_OFFSET_CAPTURE, $next) |
|
| 40 | - ) { |
|
| 41 | - $found_pos = $r[0][1]; |
|
| 42 | - $found_length = strlen($r[0][0]); |
|
| 43 | - $match = [ |
|
| 44 | - 'raw' => $r[0][0], |
|
| 45 | - 'match' => array_column($r, 0), |
|
| 46 | - 'pos' => $found_pos, |
|
| 47 | - 'length' => $found_length |
|
| 48 | - ]; |
|
| 49 | - |
|
| 50 | - $collection[] = $match; |
|
| 51 | - |
|
| 52 | - if ($max_items && count($collection) === $max_items) { |
|
| 53 | - break; |
|
| 54 | - } |
|
| 55 | - |
|
| 56 | - $pos = $match['pos'] + $match['length']; |
|
| 57 | - } |
|
| 58 | - |
|
| 59 | - return $collection; |
|
| 60 | - } |
|
| 61 | - |
|
| 62 | - /** |
|
| 63 | - * Sanitizer une collection d'occurences |
|
| 64 | - */ |
|
| 65 | - protected function sanitizer_collection(array $collection, string $sanitize_callback): array { |
|
| 66 | - foreach ($collection as &$c) { |
|
| 67 | - $c['raw'] = $sanitize_callback($c['raw']); |
|
| 68 | - } |
|
| 69 | - |
|
| 70 | - return $collection; |
|
| 71 | - } |
|
| 72 | - |
|
| 73 | - /** |
|
| 74 | - * @return array |
|
| 75 | - */ |
|
| 76 | - public function collecter(string $texte, array $options = []): array { |
|
| 77 | - return []; |
|
| 78 | - } |
|
| 79 | - |
|
| 80 | - public function detecter($texte): bool { |
|
| 81 | - if (!empty($this->markId) && str_contains($texte, $this->markId)) { |
|
| 82 | - return true; |
|
| 83 | - } |
|
| 84 | - return !empty($this->collecter($texte, ['detecter_presence' => true])); |
|
| 85 | - } |
|
| 86 | - |
|
| 87 | - /** |
|
| 88 | - * Echapper les occurences de la collecte par un texte neutre du point de vue HTML |
|
| 89 | - * |
|
| 90 | - * @see retablir() |
|
| 91 | - * @param string $texte |
|
| 92 | - * @param array $options |
|
| 93 | - * string $sanitize_callback |
|
| 94 | - * @return array |
|
| 95 | - * texte, marqueur utilise pour echapper les modeles |
|
| 96 | - */ |
|
| 97 | - public function echapper(string $texte, array $options = []): string { |
|
| 98 | - if (!function_exists('creer_uniqid')) { |
|
| 99 | - include_spip('inc/acces'); |
|
| 100 | - } |
|
| 101 | - |
|
| 102 | - $collection = $this->collecter($texte, $options); |
|
| 103 | - if (!empty($options['sanitize_callback']) && is_callable($options['sanitize_callback'])) { |
|
| 104 | - $collection = $this->sanitizer_collection($collection, $options['sanitize_callback']); |
|
| 105 | - } |
|
| 106 | - |
|
| 107 | - if ($collection !== []) { |
|
| 108 | - if (empty($this->markId)) { |
|
| 109 | - // generer un marqueur qui n'existe pas dans le texte |
|
| 110 | - do { |
|
| 111 | - $this->markId = substr(md5(uniqid(static::class, 1)), 0, 7); |
|
| 112 | - $this->markId = '@|' . static::$markPrefix . $this->markId . '|'; |
|
| 113 | - } while (str_contains($texte, $this->markId)); |
|
| 114 | - } |
|
| 115 | - |
|
| 116 | - $offset_pos = 0; |
|
| 117 | - foreach ($collection as $c) { |
|
| 118 | - $rempl = $this->markId . base64_encode($c['raw']) . '|@'; |
|
| 119 | - $texte = substr_replace($texte, $rempl, $c['pos'] + $offset_pos, $c['length']); |
|
| 120 | - $offset_pos += strlen($rempl) - $c['length']; |
|
| 121 | - } |
|
| 122 | - } |
|
| 123 | - |
|
| 124 | - return $texte; |
|
| 125 | - } |
|
| 126 | - |
|
| 127 | - |
|
| 128 | - /** |
|
| 129 | - * Retablir les occurences échappées précédemment |
|
| 130 | - * |
|
| 131 | - * @see echapper() |
|
| 132 | - */ |
|
| 133 | - function retablir(string $texte): string { |
|
| 134 | - |
|
| 135 | - if (!empty($this->markId)) { |
|
| 136 | - $lm = strlen($this->markId); |
|
| 137 | - $pos = 0; |
|
| 138 | - while ( |
|
| 139 | - ($p = strpos($texte, $this->markId, $pos)) !== false |
|
| 140 | - && ($end = strpos($texte, '|@', $p + $lm)) |
|
| 141 | - ) { |
|
| 142 | - $base64 = substr($texte, $p + $lm, $end - ($p + $lm)); |
|
| 143 | - if ($c = base64_decode($base64, true)) { |
|
| 144 | - $texte = substr_replace($texte, $c, $p, $end + 2 - $p); |
|
| 145 | - $pos = $p + strlen($c); |
|
| 146 | - } |
|
| 147 | - else { |
|
| 148 | - $pos = $end; |
|
| 149 | - } |
|
| 150 | - } |
|
| 151 | - } |
|
| 152 | - |
|
| 153 | - return $texte; |
|
| 154 | - } |
|
| 15 | + protected static string $markPrefix = 'COLLECT'; |
|
| 16 | + protected string $markId; |
|
| 17 | + |
|
| 18 | + /** |
|
| 19 | + * Collecteur générique des occurences d'une preg dans un texte avec leurs positions et longueur |
|
| 20 | + * @param string $texte |
|
| 21 | + * texte à analyser pour la collecte |
|
| 22 | + * @param string $if_chars |
|
| 23 | + * caractere(s) à tester avant de tenter la preg |
|
| 24 | + * @param string $start_with |
|
| 25 | + * caractere(s) par lesquels commencent l'expression recherchée (permet de démarrer la preg à la prochaine occurence de cette chaine) |
|
| 26 | + * @param string $preg |
|
| 27 | + * preg utilisée pour la collecte |
|
| 28 | + * @param int $max_items |
|
| 29 | + * pour limiter le nombre de preg collectée (pour la detection simple de présence par exemple) |
|
| 30 | + * @return array |
|
| 31 | + */ |
|
| 32 | + protected static function collecteur(string $texte, string $if_chars, string $start_with, string $preg, int $max_items = 0): array { |
|
| 33 | + |
|
| 34 | + $collection = []; |
|
| 35 | + $pos = 0; |
|
| 36 | + while ( |
|
| 37 | + (!$if_chars || str_contains($texte, $if_chars)) |
|
| 38 | + && ($next = ($start_with ? strpos($texte, $start_with, $pos) : $pos)) !== false |
|
| 39 | + && preg_match($preg, $texte, $r, PREG_OFFSET_CAPTURE, $next) |
|
| 40 | + ) { |
|
| 41 | + $found_pos = $r[0][1]; |
|
| 42 | + $found_length = strlen($r[0][0]); |
|
| 43 | + $match = [ |
|
| 44 | + 'raw' => $r[0][0], |
|
| 45 | + 'match' => array_column($r, 0), |
|
| 46 | + 'pos' => $found_pos, |
|
| 47 | + 'length' => $found_length |
|
| 48 | + ]; |
|
| 49 | + |
|
| 50 | + $collection[] = $match; |
|
| 51 | + |
|
| 52 | + if ($max_items && count($collection) === $max_items) { |
|
| 53 | + break; |
|
| 54 | + } |
|
| 55 | + |
|
| 56 | + $pos = $match['pos'] + $match['length']; |
|
| 57 | + } |
|
| 58 | + |
|
| 59 | + return $collection; |
|
| 60 | + } |
|
| 61 | + |
|
| 62 | + /** |
|
| 63 | + * Sanitizer une collection d'occurences |
|
| 64 | + */ |
|
| 65 | + protected function sanitizer_collection(array $collection, string $sanitize_callback): array { |
|
| 66 | + foreach ($collection as &$c) { |
|
| 67 | + $c['raw'] = $sanitize_callback($c['raw']); |
|
| 68 | + } |
|
| 69 | + |
|
| 70 | + return $collection; |
|
| 71 | + } |
|
| 72 | + |
|
| 73 | + /** |
|
| 74 | + * @return array |
|
| 75 | + */ |
|
| 76 | + public function collecter(string $texte, array $options = []): array { |
|
| 77 | + return []; |
|
| 78 | + } |
|
| 79 | + |
|
| 80 | + public function detecter($texte): bool { |
|
| 81 | + if (!empty($this->markId) && str_contains($texte, $this->markId)) { |
|
| 82 | + return true; |
|
| 83 | + } |
|
| 84 | + return !empty($this->collecter($texte, ['detecter_presence' => true])); |
|
| 85 | + } |
|
| 86 | + |
|
| 87 | + /** |
|
| 88 | + * Echapper les occurences de la collecte par un texte neutre du point de vue HTML |
|
| 89 | + * |
|
| 90 | + * @see retablir() |
|
| 91 | + * @param string $texte |
|
| 92 | + * @param array $options |
|
| 93 | + * string $sanitize_callback |
|
| 94 | + * @return array |
|
| 95 | + * texte, marqueur utilise pour echapper les modeles |
|
| 96 | + */ |
|
| 97 | + public function echapper(string $texte, array $options = []): string { |
|
| 98 | + if (!function_exists('creer_uniqid')) { |
|
| 99 | + include_spip('inc/acces'); |
|
| 100 | + } |
|
| 101 | + |
|
| 102 | + $collection = $this->collecter($texte, $options); |
|
| 103 | + if (!empty($options['sanitize_callback']) && is_callable($options['sanitize_callback'])) { |
|
| 104 | + $collection = $this->sanitizer_collection($collection, $options['sanitize_callback']); |
|
| 105 | + } |
|
| 106 | + |
|
| 107 | + if ($collection !== []) { |
|
| 108 | + if (empty($this->markId)) { |
|
| 109 | + // generer un marqueur qui n'existe pas dans le texte |
|
| 110 | + do { |
|
| 111 | + $this->markId = substr(md5(uniqid(static::class, 1)), 0, 7); |
|
| 112 | + $this->markId = '@|' . static::$markPrefix . $this->markId . '|'; |
|
| 113 | + } while (str_contains($texte, $this->markId)); |
|
| 114 | + } |
|
| 115 | + |
|
| 116 | + $offset_pos = 0; |
|
| 117 | + foreach ($collection as $c) { |
|
| 118 | + $rempl = $this->markId . base64_encode($c['raw']) . '|@'; |
|
| 119 | + $texte = substr_replace($texte, $rempl, $c['pos'] + $offset_pos, $c['length']); |
|
| 120 | + $offset_pos += strlen($rempl) - $c['length']; |
|
| 121 | + } |
|
| 122 | + } |
|
| 123 | + |
|
| 124 | + return $texte; |
|
| 125 | + } |
|
| 126 | + |
|
| 127 | + |
|
| 128 | + /** |
|
| 129 | + * Retablir les occurences échappées précédemment |
|
| 130 | + * |
|
| 131 | + * @see echapper() |
|
| 132 | + */ |
|
| 133 | + function retablir(string $texte): string { |
|
| 134 | + |
|
| 135 | + if (!empty($this->markId)) { |
|
| 136 | + $lm = strlen($this->markId); |
|
| 137 | + $pos = 0; |
|
| 138 | + while ( |
|
| 139 | + ($p = strpos($texte, $this->markId, $pos)) !== false |
|
| 140 | + && ($end = strpos($texte, '|@', $p + $lm)) |
|
| 141 | + ) { |
|
| 142 | + $base64 = substr($texte, $p + $lm, $end - ($p + $lm)); |
|
| 143 | + if ($c = base64_decode($base64, true)) { |
|
| 144 | + $texte = substr_replace($texte, $c, $p, $end + 2 - $p); |
|
| 145 | + $pos = $p + strlen($c); |
|
| 146 | + } |
|
| 147 | + else { |
|
| 148 | + $pos = $end; |
|
| 149 | + } |
|
| 150 | + } |
|
| 151 | + } |
|
| 152 | + |
|
| 153 | + return $texte; |
|
| 154 | + } |
|
| 155 | 155 | } |
@@ -109,13 +109,13 @@ |
||
| 109 | 109 | // generer un marqueur qui n'existe pas dans le texte |
| 110 | 110 | do { |
| 111 | 111 | $this->markId = substr(md5(uniqid(static::class, 1)), 0, 7); |
| 112 | - $this->markId = '@|' . static::$markPrefix . $this->markId . '|'; |
|
| 112 | + $this->markId = '@|'.static::$markPrefix.$this->markId.'|'; |
|
| 113 | 113 | } while (str_contains($texte, $this->markId)); |
| 114 | 114 | } |
| 115 | 115 | |
| 116 | 116 | $offset_pos = 0; |
| 117 | 117 | foreach ($collection as $c) { |
| 118 | - $rempl = $this->markId . base64_encode($c['raw']) . '|@'; |
|
| 118 | + $rempl = $this->markId.base64_encode($c['raw']).'|@'; |
|
| 119 | 119 | $texte = substr_replace($texte, $rempl, $c['pos'] + $offset_pos, $c['length']); |
| 120 | 120 | $offset_pos += strlen($rempl) - $c['length']; |
| 121 | 121 | } |
@@ -8,112 +8,112 @@ |
||
| 8 | 8 | **/ |
| 9 | 9 | class Sqlite |
| 10 | 10 | { |
| 11 | - /** @var Requeteur[] Liste des instances de requêteurs créés */ |
|
| 12 | - public static $requeteurs = []; |
|
| 13 | - /** @var bool[] Pour chaque connexion, flag pour savoir si une transaction est en cours */ |
|
| 14 | - public static $transaction_en_cours = []; |
|
| 11 | + /** @var Requeteur[] Liste des instances de requêteurs créés */ |
|
| 12 | + public static $requeteurs = []; |
|
| 13 | + /** @var bool[] Pour chaque connexion, flag pour savoir si une transaction est en cours */ |
|
| 14 | + public static $transaction_en_cours = []; |
|
| 15 | 15 | |
| 16 | 16 | |
| 17 | - /** |
|
| 18 | - * Retourne une unique instance du requêteur |
|
| 19 | - * |
|
| 20 | - * Retourne une instance unique du requêteur pour une connexion SQLite |
|
| 21 | - * donnée |
|
| 22 | - * |
|
| 23 | - * @param string $serveur |
|
| 24 | - * Nom du connecteur |
|
| 25 | - * @return Requeteur |
|
| 26 | - * Instance unique du requêteur |
|
| 27 | - **/ |
|
| 28 | - public static function requeteur($serveur) { |
|
| 29 | - if (!isset(static::$requeteurs[$serveur])) { |
|
| 30 | - static::$requeteurs[$serveur] = new Requeteur($serveur); |
|
| 31 | - } |
|
| 17 | + /** |
|
| 18 | + * Retourne une unique instance du requêteur |
|
| 19 | + * |
|
| 20 | + * Retourne une instance unique du requêteur pour une connexion SQLite |
|
| 21 | + * donnée |
|
| 22 | + * |
|
| 23 | + * @param string $serveur |
|
| 24 | + * Nom du connecteur |
|
| 25 | + * @return Requeteur |
|
| 26 | + * Instance unique du requêteur |
|
| 27 | + **/ |
|
| 28 | + public static function requeteur($serveur) { |
|
| 29 | + if (!isset(static::$requeteurs[$serveur])) { |
|
| 30 | + static::$requeteurs[$serveur] = new Requeteur($serveur); |
|
| 31 | + } |
|
| 32 | 32 | |
| 33 | - return static::$requeteurs[$serveur]; |
|
| 34 | - } |
|
| 33 | + return static::$requeteurs[$serveur]; |
|
| 34 | + } |
|
| 35 | 35 | |
| 36 | - /** |
|
| 37 | - * Prépare le texte d'une requête avant son exécution |
|
| 38 | - * |
|
| 39 | - * Adapte la requête au format plus ou moins MySQL par un format |
|
| 40 | - * compris de SQLite. |
|
| 41 | - * |
|
| 42 | - * Change les préfixes de tables SPIP par ceux véritables |
|
| 43 | - * |
|
| 44 | - * @param string $query Requête à préparer |
|
| 45 | - * @param string $serveur Nom de la connexion |
|
| 46 | - * @return string Requête préparée |
|
| 47 | - */ |
|
| 48 | - public static function traduire_requete($query, $serveur) { |
|
| 49 | - $requeteur = static::requeteur($serveur); |
|
| 50 | - $traducteur = new Traducteur($query, $requeteur->prefixe, $requeteur->sqlite_version); |
|
| 36 | + /** |
|
| 37 | + * Prépare le texte d'une requête avant son exécution |
|
| 38 | + * |
|
| 39 | + * Adapte la requête au format plus ou moins MySQL par un format |
|
| 40 | + * compris de SQLite. |
|
| 41 | + * |
|
| 42 | + * Change les préfixes de tables SPIP par ceux véritables |
|
| 43 | + * |
|
| 44 | + * @param string $query Requête à préparer |
|
| 45 | + * @param string $serveur Nom de la connexion |
|
| 46 | + * @return string Requête préparée |
|
| 47 | + */ |
|
| 48 | + public static function traduire_requete($query, $serveur) { |
|
| 49 | + $requeteur = static::requeteur($serveur); |
|
| 50 | + $traducteur = new Traducteur($query, $requeteur->prefixe, $requeteur->sqlite_version); |
|
| 51 | 51 | |
| 52 | - return $traducteur->traduire_requete(); |
|
| 53 | - } |
|
| 52 | + return $traducteur->traduire_requete(); |
|
| 53 | + } |
|
| 54 | 54 | |
| 55 | - /** |
|
| 56 | - * Démarre une transaction |
|
| 57 | - * |
|
| 58 | - * @param string $serveur Nom de la connexion |
|
| 59 | - **/ |
|
| 60 | - public static function demarrer_transaction($serveur) { |
|
| 61 | - Sqlite::executer_requete('BEGIN TRANSACTION', $serveur); |
|
| 62 | - Sqlite::$transaction_en_cours[$serveur] = true; |
|
| 63 | - } |
|
| 55 | + /** |
|
| 56 | + * Démarre une transaction |
|
| 57 | + * |
|
| 58 | + * @param string $serveur Nom de la connexion |
|
| 59 | + **/ |
|
| 60 | + public static function demarrer_transaction($serveur) { |
|
| 61 | + Sqlite::executer_requete('BEGIN TRANSACTION', $serveur); |
|
| 62 | + Sqlite::$transaction_en_cours[$serveur] = true; |
|
| 63 | + } |
|
| 64 | 64 | |
| 65 | - /** |
|
| 66 | - * Exécute la requête donnée |
|
| 67 | - * |
|
| 68 | - * @param string $query Requête |
|
| 69 | - * @param string $serveur Nom de la connexion |
|
| 70 | - * @param null|bool $tracer Demander des statistiques (temps) ? |
|
| 71 | - **/ |
|
| 72 | - public static function executer_requete($query, $serveur, $tracer = null) { |
|
| 73 | - $requeteur = Sqlite::requeteur($serveur); |
|
| 65 | + /** |
|
| 66 | + * Exécute la requête donnée |
|
| 67 | + * |
|
| 68 | + * @param string $query Requête |
|
| 69 | + * @param string $serveur Nom de la connexion |
|
| 70 | + * @param null|bool $tracer Demander des statistiques (temps) ? |
|
| 71 | + **/ |
|
| 72 | + public static function executer_requete($query, $serveur, $tracer = null) { |
|
| 73 | + $requeteur = Sqlite::requeteur($serveur); |
|
| 74 | 74 | |
| 75 | - return $requeteur->executer_requete($query, $tracer); |
|
| 76 | - } |
|
| 75 | + return $requeteur->executer_requete($query, $tracer); |
|
| 76 | + } |
|
| 77 | 77 | |
| 78 | - /** |
|
| 79 | - * Obtient l'identifiant de la dernière ligne insérée ou modifiée |
|
| 80 | - * |
|
| 81 | - * @param string $serveur Nom de la connexion |
|
| 82 | - * return int Identifiant |
|
| 83 | - **/ |
|
| 84 | - public static function last_insert_id($serveur) { |
|
| 85 | - $requeteur = Sqlite::requeteur($serveur); |
|
| 78 | + /** |
|
| 79 | + * Obtient l'identifiant de la dernière ligne insérée ou modifiée |
|
| 80 | + * |
|
| 81 | + * @param string $serveur Nom de la connexion |
|
| 82 | + * return int Identifiant |
|
| 83 | + **/ |
|
| 84 | + public static function last_insert_id($serveur) { |
|
| 85 | + $requeteur = Sqlite::requeteur($serveur); |
|
| 86 | 86 | |
| 87 | - return $requeteur->last_insert_id(); |
|
| 88 | - } |
|
| 87 | + return $requeteur->last_insert_id(); |
|
| 88 | + } |
|
| 89 | 89 | |
| 90 | - /** |
|
| 91 | - * Annule une transaction |
|
| 92 | - * |
|
| 93 | - * @param string $serveur Nom de la connexion |
|
| 94 | - **/ |
|
| 95 | - public static function annuler_transaction($serveur) { |
|
| 96 | - Sqlite::executer_requete('ROLLBACK', $serveur); |
|
| 97 | - Sqlite::$transaction_en_cours[$serveur] = false; |
|
| 98 | - } |
|
| 90 | + /** |
|
| 91 | + * Annule une transaction |
|
| 92 | + * |
|
| 93 | + * @param string $serveur Nom de la connexion |
|
| 94 | + **/ |
|
| 95 | + public static function annuler_transaction($serveur) { |
|
| 96 | + Sqlite::executer_requete('ROLLBACK', $serveur); |
|
| 97 | + Sqlite::$transaction_en_cours[$serveur] = false; |
|
| 98 | + } |
|
| 99 | 99 | |
| 100 | - /** |
|
| 101 | - * Termine une transaction |
|
| 102 | - * |
|
| 103 | - * @param string $serveur Nom de la connexion |
|
| 104 | - **/ |
|
| 105 | - public static function finir_transaction($serveur) { |
|
| 106 | - // si pas de transaction en cours, ne rien faire et le dire |
|
| 107 | - if ( |
|
| 108 | - !isset(Sqlite::$transaction_en_cours[$serveur]) |
|
| 109 | - || Sqlite::$transaction_en_cours[$serveur] == false |
|
| 110 | - ) { |
|
| 111 | - return false; |
|
| 112 | - } |
|
| 113 | - // sinon fermer la transaction et retourner true |
|
| 114 | - Sqlite::executer_requete('COMMIT', $serveur); |
|
| 115 | - Sqlite::$transaction_en_cours[$serveur] = false; |
|
| 100 | + /** |
|
| 101 | + * Termine une transaction |
|
| 102 | + * |
|
| 103 | + * @param string $serveur Nom de la connexion |
|
| 104 | + **/ |
|
| 105 | + public static function finir_transaction($serveur) { |
|
| 106 | + // si pas de transaction en cours, ne rien faire et le dire |
|
| 107 | + if ( |
|
| 108 | + !isset(Sqlite::$transaction_en_cours[$serveur]) |
|
| 109 | + || Sqlite::$transaction_en_cours[$serveur] == false |
|
| 110 | + ) { |
|
| 111 | + return false; |
|
| 112 | + } |
|
| 113 | + // sinon fermer la transaction et retourner true |
|
| 114 | + Sqlite::executer_requete('COMMIT', $serveur); |
|
| 115 | + Sqlite::$transaction_en_cours[$serveur] = false; |
|
| 116 | 116 | |
| 117 | - return true; |
|
| 118 | - } |
|
| 117 | + return true; |
|
| 118 | + } |
|
| 119 | 119 | } |
@@ -6,10 +6,10 @@ |
||
| 6 | 6 | * Pouvoir retrouver le PDO utilisé pour générer un résultat de requête. |
| 7 | 7 | */ |
| 8 | 8 | final class PDOStatement extends \PDOStatement { |
| 9 | - private function __construct(private \PDO &$PDO) { |
|
| 10 | - } |
|
| 9 | + private function __construct(private \PDO &$PDO) { |
|
| 10 | + } |
|
| 11 | 11 | |
| 12 | - public function getPDO(): \PDO { |
|
| 13 | - return $this->PDO; |
|
| 14 | - } |
|
| 12 | + public function getPDO(): \PDO { |
|
| 13 | + return $this->PDO; |
|
| 14 | + } |
|
| 15 | 15 | } |
@@ -13,109 +13,109 @@ |
||
| 13 | 13 | |
| 14 | 14 | class Requeteur |
| 15 | 15 | { |
| 16 | - /** @var string texte de la requête */ |
|
| 17 | - public $query = ''; // la requete |
|
| 18 | - /** @var string Nom de la connexion */ |
|
| 19 | - public $serveur = ''; |
|
| 20 | - /** @var \PDO|null Identifiant de la connexion SQLite */ |
|
| 21 | - public $link = null; |
|
| 22 | - /** @var string Prefixe des tables SPIP */ |
|
| 23 | - public $prefixe = ''; |
|
| 24 | - /** @var string Nom de la base de donnée */ |
|
| 25 | - public $db = ''; |
|
| 26 | - /** @var bool Doit-on tracer les requetes (var_profile) ? */ |
|
| 27 | - public $tracer = false; // doit-on tracer les requetes (var_profile) |
|
| 28 | - |
|
| 29 | - /** @var string Version de SQLite (2 ou 3) */ |
|
| 30 | - public $sqlite_version = ''; |
|
| 31 | - |
|
| 32 | - /** |
|
| 33 | - * Constructeur |
|
| 34 | - * |
|
| 35 | - * @param string $serveur |
|
| 36 | - */ |
|
| 37 | - public function __construct($serveur = '') { |
|
| 38 | - _sqlite_init(); |
|
| 39 | - $this->serveur = strtolower($serveur); |
|
| 40 | - |
|
| 41 | - if (!($this->link = _sqlite_link($this->serveur)) && (!defined('_ECRIRE_INSTALL') || !_ECRIRE_INSTALL)) { |
|
| 42 | - spip_log('Aucune connexion sqlite (link)', 'sqlite.' . _LOG_ERREUR); |
|
| 43 | - |
|
| 44 | - return; |
|
| 45 | - } |
|
| 46 | - |
|
| 47 | - $this->sqlite_version = _sqlite_is_version('', $this->link); |
|
| 48 | - |
|
| 49 | - $this->prefixe = $GLOBALS['connexions'][$this->serveur ?: 0]['prefixe']; |
|
| 50 | - $this->db = $GLOBALS['connexions'][$this->serveur ?: 0]['db']; |
|
| 51 | - |
|
| 52 | - // tracage des requetes ? |
|
| 53 | - $this->tracer = (isset($_GET['var_profile']) && $_GET['var_profile']); |
|
| 54 | - } |
|
| 55 | - |
|
| 56 | - /** |
|
| 57 | - * Lancer la requête transmise et faire le tracage si demandé |
|
| 58 | - * |
|
| 59 | - * @param string $query |
|
| 60 | - * Requête à exécuter |
|
| 61 | - * @param bool|null $tracer |
|
| 62 | - * true pour tracer la requête |
|
| 63 | - * @return bool|\PDOStatement|array |
|
| 64 | - */ |
|
| 65 | - public function executer_requete($query, $tracer = null) { |
|
| 66 | - if (is_null($tracer)) { |
|
| 67 | - $tracer = $this->tracer; |
|
| 68 | - } |
|
| 69 | - $err = ''; |
|
| 70 | - $t = 0; |
|
| 71 | - if ($tracer || defined('_DEBUG_TRACE_QUERIES') && _DEBUG_TRACE_QUERIES) { |
|
| 72 | - include_spip('public/tracer'); |
|
| 73 | - $t = trace_query_start(); |
|
| 74 | - } |
|
| 75 | - |
|
| 76 | - # spip_log("requete: $this->serveur >> $query",'sqlite.'._LOG_DEBUG); // boum ? pourquoi ? |
|
| 77 | - if ($this->link) { |
|
| 78 | - // memoriser la derniere erreur PHP vue |
|
| 79 | - $last_error = (function_exists('error_get_last') ? error_get_last() : ''); |
|
| 80 | - $e = null; |
|
| 81 | - // sauver la derniere requete |
|
| 82 | - $GLOBALS['connexions'][$this->serveur ?: 0]['last'] = $query; |
|
| 83 | - $GLOBALS['connexions'][$this->serveur ?: 0]['total_requetes']++; |
|
| 84 | - |
|
| 85 | - try { |
|
| 86 | - $r = $this->link->query($query); |
|
| 87 | - } catch (\PDOException $e) { |
|
| 88 | - spip_log('PDOException: ' . $e->getMessage(), 'sqlite.' . _LOG_DEBUG); |
|
| 89 | - $r = false; |
|
| 90 | - } |
|
| 91 | - |
|
| 92 | - // loger les warnings/erreurs eventuels de sqlite remontant dans PHP |
|
| 93 | - if ($e && $e instanceof \PDOException) { |
|
| 94 | - $err = strip_tags($e->getMessage()) . ' in ' . $e->getFile() . ' line ' . $e->getLine(); |
|
| 95 | - spip_log("$err - " . $query, 'sqlite.' . _LOG_ERREUR); |
|
| 96 | - } elseif (($err = (function_exists('error_get_last') ? error_get_last() : '')) && $err != $last_error) { |
|
| 97 | - $err = strip_tags($err['message']) . ' in ' . $err['file'] . ' line ' . $err['line']; |
|
| 98 | - spip_log("$err - " . $query, 'sqlite.' . _LOG_ERREUR); |
|
| 99 | - } else { |
|
| 100 | - $err = ''; |
|
| 101 | - } |
|
| 102 | - } else { |
|
| 103 | - $r = false; |
|
| 104 | - } |
|
| 105 | - |
|
| 106 | - if (spip_sqlite_errno($this->serveur)) { |
|
| 107 | - $err .= spip_sqlite_error($query, $this->serveur); |
|
| 108 | - } |
|
| 109 | - |
|
| 110 | - return $t ? trace_query_end($query, $t, $r, $err, $this->serveur) : $r; |
|
| 111 | - } |
|
| 112 | - |
|
| 113 | - /** |
|
| 114 | - * Obtient l'identifiant de la dernière ligne insérée ou modifiée |
|
| 115 | - * |
|
| 116 | - * @return string|false |
|
| 117 | - **/ |
|
| 118 | - public function last_insert_id() { |
|
| 119 | - return $this->link->lastInsertId(); |
|
| 120 | - } |
|
| 16 | + /** @var string texte de la requête */ |
|
| 17 | + public $query = ''; // la requete |
|
| 18 | + /** @var string Nom de la connexion */ |
|
| 19 | + public $serveur = ''; |
|
| 20 | + /** @var \PDO|null Identifiant de la connexion SQLite */ |
|
| 21 | + public $link = null; |
|
| 22 | + /** @var string Prefixe des tables SPIP */ |
|
| 23 | + public $prefixe = ''; |
|
| 24 | + /** @var string Nom de la base de donnée */ |
|
| 25 | + public $db = ''; |
|
| 26 | + /** @var bool Doit-on tracer les requetes (var_profile) ? */ |
|
| 27 | + public $tracer = false; // doit-on tracer les requetes (var_profile) |
|
| 28 | + |
|
| 29 | + /** @var string Version de SQLite (2 ou 3) */ |
|
| 30 | + public $sqlite_version = ''; |
|
| 31 | + |
|
| 32 | + /** |
|
| 33 | + * Constructeur |
|
| 34 | + * |
|
| 35 | + * @param string $serveur |
|
| 36 | + */ |
|
| 37 | + public function __construct($serveur = '') { |
|
| 38 | + _sqlite_init(); |
|
| 39 | + $this->serveur = strtolower($serveur); |
|
| 40 | + |
|
| 41 | + if (!($this->link = _sqlite_link($this->serveur)) && (!defined('_ECRIRE_INSTALL') || !_ECRIRE_INSTALL)) { |
|
| 42 | + spip_log('Aucune connexion sqlite (link)', 'sqlite.' . _LOG_ERREUR); |
|
| 43 | + |
|
| 44 | + return; |
|
| 45 | + } |
|
| 46 | + |
|
| 47 | + $this->sqlite_version = _sqlite_is_version('', $this->link); |
|
| 48 | + |
|
| 49 | + $this->prefixe = $GLOBALS['connexions'][$this->serveur ?: 0]['prefixe']; |
|
| 50 | + $this->db = $GLOBALS['connexions'][$this->serveur ?: 0]['db']; |
|
| 51 | + |
|
| 52 | + // tracage des requetes ? |
|
| 53 | + $this->tracer = (isset($_GET['var_profile']) && $_GET['var_profile']); |
|
| 54 | + } |
|
| 55 | + |
|
| 56 | + /** |
|
| 57 | + * Lancer la requête transmise et faire le tracage si demandé |
|
| 58 | + * |
|
| 59 | + * @param string $query |
|
| 60 | + * Requête à exécuter |
|
| 61 | + * @param bool|null $tracer |
|
| 62 | + * true pour tracer la requête |
|
| 63 | + * @return bool|\PDOStatement|array |
|
| 64 | + */ |
|
| 65 | + public function executer_requete($query, $tracer = null) { |
|
| 66 | + if (is_null($tracer)) { |
|
| 67 | + $tracer = $this->tracer; |
|
| 68 | + } |
|
| 69 | + $err = ''; |
|
| 70 | + $t = 0; |
|
| 71 | + if ($tracer || defined('_DEBUG_TRACE_QUERIES') && _DEBUG_TRACE_QUERIES) { |
|
| 72 | + include_spip('public/tracer'); |
|
| 73 | + $t = trace_query_start(); |
|
| 74 | + } |
|
| 75 | + |
|
| 76 | + # spip_log("requete: $this->serveur >> $query",'sqlite.'._LOG_DEBUG); // boum ? pourquoi ? |
|
| 77 | + if ($this->link) { |
|
| 78 | + // memoriser la derniere erreur PHP vue |
|
| 79 | + $last_error = (function_exists('error_get_last') ? error_get_last() : ''); |
|
| 80 | + $e = null; |
|
| 81 | + // sauver la derniere requete |
|
| 82 | + $GLOBALS['connexions'][$this->serveur ?: 0]['last'] = $query; |
|
| 83 | + $GLOBALS['connexions'][$this->serveur ?: 0]['total_requetes']++; |
|
| 84 | + |
|
| 85 | + try { |
|
| 86 | + $r = $this->link->query($query); |
|
| 87 | + } catch (\PDOException $e) { |
|
| 88 | + spip_log('PDOException: ' . $e->getMessage(), 'sqlite.' . _LOG_DEBUG); |
|
| 89 | + $r = false; |
|
| 90 | + } |
|
| 91 | + |
|
| 92 | + // loger les warnings/erreurs eventuels de sqlite remontant dans PHP |
|
| 93 | + if ($e && $e instanceof \PDOException) { |
|
| 94 | + $err = strip_tags($e->getMessage()) . ' in ' . $e->getFile() . ' line ' . $e->getLine(); |
|
| 95 | + spip_log("$err - " . $query, 'sqlite.' . _LOG_ERREUR); |
|
| 96 | + } elseif (($err = (function_exists('error_get_last') ? error_get_last() : '')) && $err != $last_error) { |
|
| 97 | + $err = strip_tags($err['message']) . ' in ' . $err['file'] . ' line ' . $err['line']; |
|
| 98 | + spip_log("$err - " . $query, 'sqlite.' . _LOG_ERREUR); |
|
| 99 | + } else { |
|
| 100 | + $err = ''; |
|
| 101 | + } |
|
| 102 | + } else { |
|
| 103 | + $r = false; |
|
| 104 | + } |
|
| 105 | + |
|
| 106 | + if (spip_sqlite_errno($this->serveur)) { |
|
| 107 | + $err .= spip_sqlite_error($query, $this->serveur); |
|
| 108 | + } |
|
| 109 | + |
|
| 110 | + return $t ? trace_query_end($query, $t, $r, $err, $this->serveur) : $r; |
|
| 111 | + } |
|
| 112 | + |
|
| 113 | + /** |
|
| 114 | + * Obtient l'identifiant de la dernière ligne insérée ou modifiée |
|
| 115 | + * |
|
| 116 | + * @return string|false |
|
| 117 | + **/ |
|
| 118 | + public function last_insert_id() { |
|
| 119 | + return $this->link->lastInsertId(); |
|
| 120 | + } |
|
| 121 | 121 | } |
@@ -39,7 +39,7 @@ discard block |
||
| 39 | 39 | $this->serveur = strtolower($serveur); |
| 40 | 40 | |
| 41 | 41 | if (!($this->link = _sqlite_link($this->serveur)) && (!defined('_ECRIRE_INSTALL') || !_ECRIRE_INSTALL)) { |
| 42 | - spip_log('Aucune connexion sqlite (link)', 'sqlite.' . _LOG_ERREUR); |
|
| 42 | + spip_log('Aucune connexion sqlite (link)', 'sqlite.'._LOG_ERREUR); |
|
| 43 | 43 | |
| 44 | 44 | return; |
| 45 | 45 | } |
@@ -85,17 +85,17 @@ discard block |
||
| 85 | 85 | try { |
| 86 | 86 | $r = $this->link->query($query); |
| 87 | 87 | } catch (\PDOException $e) { |
| 88 | - spip_log('PDOException: ' . $e->getMessage(), 'sqlite.' . _LOG_DEBUG); |
|
| 88 | + spip_log('PDOException: '.$e->getMessage(), 'sqlite.'._LOG_DEBUG); |
|
| 89 | 89 | $r = false; |
| 90 | 90 | } |
| 91 | 91 | |
| 92 | 92 | // loger les warnings/erreurs eventuels de sqlite remontant dans PHP |
| 93 | 93 | if ($e && $e instanceof \PDOException) { |
| 94 | - $err = strip_tags($e->getMessage()) . ' in ' . $e->getFile() . ' line ' . $e->getLine(); |
|
| 95 | - spip_log("$err - " . $query, 'sqlite.' . _LOG_ERREUR); |
|
| 94 | + $err = strip_tags($e->getMessage()).' in '.$e->getFile().' line '.$e->getLine(); |
|
| 95 | + spip_log("$err - ".$query, 'sqlite.'._LOG_ERREUR); |
|
| 96 | 96 | } elseif (($err = (function_exists('error_get_last') ? error_get_last() : '')) && $err != $last_error) { |
| 97 | - $err = strip_tags($err['message']) . ' in ' . $err['file'] . ' line ' . $err['line']; |
|
| 98 | - spip_log("$err - " . $query, 'sqlite.' . _LOG_ERREUR); |
|
| 97 | + $err = strip_tags($err['message']).' in '.$err['file'].' line '.$err['line']; |
|
| 98 | + spip_log("$err - ".$query, 'sqlite.'._LOG_ERREUR); |
|
| 99 | 99 | } else { |
| 100 | 100 | $err = ''; |
| 101 | 101 | } |
@@ -9,195 +9,195 @@ |
||
| 9 | 9 | */ |
| 10 | 10 | class Traducteur |
| 11 | 11 | { |
| 12 | - /** Pour les corrections à effectuer sur les requêtes : array(code=>'texte') trouvé |
|
| 13 | - * |
|
| 14 | - * @var array |
|
| 15 | - */ |
|
| 16 | - public $textes = []; |
|
| 17 | - |
|
| 18 | - /** |
|
| 19 | - * Constructeur |
|
| 20 | - */ |
|
| 21 | - public function __construct( |
|
| 22 | - /** Requête à préparer */ |
|
| 23 | - public string $query, |
|
| 24 | - /** Prefixe des tables à utiliser */ |
|
| 25 | - public string $prefixe, |
|
| 26 | - /** Version SQLite (2 ou 3) */ |
|
| 27 | - public string $sqlite_version |
|
| 28 | - ) { |
|
| 29 | - } |
|
| 30 | - |
|
| 31 | - /** |
|
| 32 | - * Transformer la requete pour SQLite |
|
| 33 | - * |
|
| 34 | - * Enlève les textes, transforme la requête pour quelle soit |
|
| 35 | - * bien interprétée par SQLite, puis remet les textes |
|
| 36 | - * la fonction affecte `$this->query` |
|
| 37 | - */ |
|
| 38 | - public function traduire_requete() { |
|
| 39 | - // |
|
| 40 | - // 1) Protection des textes en les remplacant par des codes |
|
| 41 | - // |
|
| 42 | - // enlever les 'textes' et initialiser avec |
|
| 43 | - [$this->query, $textes] = query_echappe_textes($this->query); |
|
| 44 | - |
|
| 45 | - // |
|
| 46 | - // 2) Corrections de la requete |
|
| 47 | - // |
|
| 48 | - // Correction Create Database |
|
| 49 | - // Create Database -> requete ignoree |
|
| 50 | - if (str_starts_with($this->query, 'CREATE DATABASE')) { |
|
| 51 | - spip_log("Sqlite : requete non executee -> $this->query", 'sqlite.' . _LOG_AVERTISSEMENT); |
|
| 52 | - $this->query = 'SELECT 1'; |
|
| 53 | - } |
|
| 54 | - |
|
| 55 | - // Correction Insert Ignore |
|
| 56 | - // INSERT IGNORE -> insert (tout court et pas 'insert or replace') |
|
| 57 | - if (str_starts_with($this->query, 'INSERT IGNORE')) { |
|
| 58 | - spip_log("Sqlite : requete transformee -> $this->query", 'sqlite.' . _LOG_DEBUG); |
|
| 59 | - $this->query = 'INSERT ' . substr($this->query, '13'); |
|
| 60 | - } |
|
| 61 | - |
|
| 62 | - // Correction des dates avec INTERVAL |
|
| 63 | - // utiliser sql_date_proche() de preference |
|
| 64 | - if (str_contains($this->query, 'INTERVAL')) { |
|
| 65 | - $this->query = preg_replace_callback( |
|
| 66 | - '/DATE_(ADD|SUB)(.*)INTERVAL\s+(\d+)\s+([a-zA-Z]+)\)/U', |
|
| 67 | - fn(array $matches): string => $this->_remplacerDateParTime($matches), |
|
| 68 | - $this->query |
|
| 69 | - ); |
|
| 70 | - } |
|
| 71 | - |
|
| 72 | - if (str_contains($this->query, 'LEFT(')) { |
|
| 73 | - $this->query = str_replace('LEFT(', '_LEFT(', $this->query); |
|
| 74 | - } |
|
| 75 | - |
|
| 76 | - if (str_contains($this->query, 'TIMESTAMPDIFF(')) { |
|
| 77 | - $this->query = preg_replace('/TIMESTAMPDIFF\(\s*([^,]*)\s*,/Uims', "TIMESTAMPDIFF('\\1',", $this->query); |
|
| 78 | - } |
|
| 79 | - |
|
| 80 | - |
|
| 81 | - // Correction Using |
|
| 82 | - // USING (non reconnu en sqlite2) |
|
| 83 | - // problematique car la jointure ne se fait pas du coup. |
|
| 84 | - if (($this->sqlite_version == 2) && (str_contains($this->query, 'USING'))) { |
|
| 85 | - spip_log( |
|
| 86 | - "'USING (champ)' n'est pas reconnu en SQLite 2. Utilisez 'ON table1.champ = table2.champ'", |
|
| 87 | - 'sqlite.' . _LOG_ERREUR |
|
| 88 | - ); |
|
| 89 | - $this->query = preg_replace('/USING\s*\([^\)]*\)/', '', $this->query); |
|
| 90 | - } |
|
| 91 | - |
|
| 92 | - // Correction Field |
|
| 93 | - // remplace FIELD(table,i,j,k...) par CASE WHEN table=i THEN n ... ELSE 0 END |
|
| 94 | - if (str_contains($this->query, 'FIELD')) { |
|
| 95 | - $this->query = preg_replace_callback( |
|
| 96 | - '/FIELD\s*\(([^\)]*)\)/', |
|
| 97 | - fn(array $matches): string => $this->_remplacerFieldParCase($matches), |
|
| 98 | - $this->query |
|
| 99 | - ); |
|
| 100 | - } |
|
| 101 | - |
|
| 102 | - // Correction des noms de tables FROM |
|
| 103 | - // mettre les bons noms de table dans from, update, insert, replace... |
|
| 104 | - if (preg_match('/\s(SET|VALUES|WHERE|DATABASE)\s/iS', $this->query, $regs)) { |
|
| 105 | - $suite = strstr($this->query, $regs[0]); |
|
| 106 | - $this->query = substr($this->query, 0, -strlen($suite)); |
|
| 107 | - } else { |
|
| 108 | - $suite = ''; |
|
| 109 | - } |
|
| 110 | - $pref = ($this->prefixe) ? $this->prefixe . '_' : ''; |
|
| 111 | - $this->query = preg_replace('/([,\s])spip_/S', '\1' . $pref, $this->query) . $suite; |
|
| 112 | - |
|
| 113 | - // Correction zero AS x |
|
| 114 | - // pg n'aime pas 0+x AS alias, sqlite, dans le meme style, |
|
| 115 | - // n'apprecie pas du tout SELECT 0 as x ... ORDER BY x |
|
| 116 | - // il dit que x ne doit pas être un integer dans le order by ! |
|
| 117 | - // on remplace du coup x par vide() dans ce cas uniquement |
|
| 118 | - // |
|
| 119 | - // apparait dans public/vertebrer.php et dans le plugin menu aussi qui genere aussi ce genre de requete via un {par num #GET{tri_num}} |
|
| 120 | - // mais est-ce encore un soucis pour sqlite en 2021 ? (ie commenter le preg_replace marche très bien en sqlite 3.28) |
|
| 121 | - // on ne remplace que dans ORDER BY ou GROUP BY |
|
| 122 | - if (str_contains($this->query, '0 AS') && preg_match('/\s(ORDER|GROUP) BY\s/i', $this->query, $regs)) { |
|
| 123 | - $suite = strstr($this->query, $regs[0]); |
|
| 124 | - $this->query = substr($this->query, 0, -strlen($suite)); |
|
| 125 | - // on cherche les noms des x dans 0 AS x |
|
| 126 | - // on remplace dans $suite le nom par vide() |
|
| 127 | - preg_match_all('/\b0 AS\s*([^\s,]+)/', $this->query, $matches, PREG_PATTERN_ORDER); |
|
| 128 | - foreach ($matches[1] as $m) { |
|
| 129 | - if (str_contains($suite, $m)) { |
|
| 130 | - $suite = preg_replace(",\b$m\b,", 'VIDE()', $suite); |
|
| 131 | - } |
|
| 132 | - } |
|
| 133 | - $this->query .= $suite; |
|
| 134 | - } |
|
| 135 | - |
|
| 136 | - // Correction possible des divisions entieres |
|
| 137 | - // Le standard SQL (lequel? ou?) semble indiquer que |
|
| 138 | - // a/b=c doit donner c entier si a et b sont entiers 4/3=1. |
|
| 139 | - // C'est ce que retournent effectivement SQL Server et SQLite |
|
| 140 | - // Ce n'est pas ce qu'applique MySQL qui retourne un reel : 4/3=1.333... |
|
| 141 | - // |
|
| 142 | - // On peut forcer la conversion en multipliant par 1.0 avant la division |
|
| 143 | - // /!\ SQLite 3.5.9 Debian/Ubuntu est victime d'un bug en plus ! |
|
| 144 | - // cf. https://bugs.launchpad.net/ubuntu/+source/sqlite3/+bug/254228 |
|
| 145 | - // http://www.sqlite.org/cvstrac/tktview?tn=3202 |
|
| 146 | - // (4*1.0/3) n'est pas rendu dans ce cas ! |
|
| 147 | - # $this->query = str_replace('/','* 1.00 / ',$this->query); |
|
| 148 | - |
|
| 149 | - |
|
| 150 | - // Correction critere REGEXP, non reconnu en sqlite2 |
|
| 151 | - if (($this->sqlite_version == 2) && (str_contains($this->query, 'REGEXP'))) { |
|
| 152 | - $this->query = preg_replace('/([^\s\(]*)(\s*)REGEXP(\s*)([^\s\)]*)/', 'REGEXP($4, $1)', $this->query); |
|
| 153 | - } |
|
| 154 | - |
|
| 155 | - // |
|
| 156 | - // 3) Remise en place des textes d'origine |
|
| 157 | - // |
|
| 158 | - // Correction Antiquotes et echappements |
|
| 159 | - // ` => rien |
|
| 160 | - if (str_contains($this->query, '`')) { |
|
| 161 | - $this->query = str_replace('`', '', $this->query); |
|
| 162 | - } |
|
| 163 | - |
|
| 164 | - $this->query = query_reinjecte_textes($this->query, $textes); |
|
| 165 | - |
|
| 166 | - return $this->query; |
|
| 167 | - } |
|
| 168 | - |
|
| 169 | - /** |
|
| 170 | - * Callback pour remplacer `DATE_` / `INTERVAL` |
|
| 171 | - * par `DATE ... strtotime` |
|
| 172 | - * |
|
| 173 | - * @param array $matches Captures |
|
| 174 | - * @return string texte de date compris par SQLite |
|
| 175 | - */ |
|
| 176 | - public function _remplacerDateParTime($matches) { |
|
| 177 | - $op = strtoupper($matches[1] == 'ADD') ? '+' : '-'; |
|
| 178 | - |
|
| 179 | - return "datetime$matches[2] '$op$matches[3] $matches[4]')"; |
|
| 180 | - } |
|
| 181 | - |
|
| 182 | - /** |
|
| 183 | - * Callback pour remplacer `FIELD(table,i,j,k...)` |
|
| 184 | - * par `CASE WHEN table=i THEN n ... ELSE 0 END` |
|
| 185 | - * |
|
| 186 | - * @param array $matches Captures |
|
| 187 | - * @return string texte de liste ordonnée compris par SQLite |
|
| 188 | - */ |
|
| 189 | - public function _remplacerFieldParCase($matches) { |
|
| 190 | - $fields = substr($matches[0], 6, -1); // ne recuperer que l'interieur X de field(X) |
|
| 191 | - $t = explode(',', $fields); |
|
| 192 | - $index = array_shift($t); |
|
| 193 | - |
|
| 194 | - $res = ''; |
|
| 195 | - $n = 0; |
|
| 196 | - foreach ($t as $v) { |
|
| 197 | - $n++; |
|
| 198 | - $res .= "\nWHEN $index=$v THEN $n"; |
|
| 199 | - } |
|
| 200 | - |
|
| 201 | - return "CASE $res ELSE 0 END "; |
|
| 202 | - } |
|
| 12 | + /** Pour les corrections à effectuer sur les requêtes : array(code=>'texte') trouvé |
|
| 13 | + * |
|
| 14 | + * @var array |
|
| 15 | + */ |
|
| 16 | + public $textes = []; |
|
| 17 | + |
|
| 18 | + /** |
|
| 19 | + * Constructeur |
|
| 20 | + */ |
|
| 21 | + public function __construct( |
|
| 22 | + /** Requête à préparer */ |
|
| 23 | + public string $query, |
|
| 24 | + /** Prefixe des tables à utiliser */ |
|
| 25 | + public string $prefixe, |
|
| 26 | + /** Version SQLite (2 ou 3) */ |
|
| 27 | + public string $sqlite_version |
|
| 28 | + ) { |
|
| 29 | + } |
|
| 30 | + |
|
| 31 | + /** |
|
| 32 | + * Transformer la requete pour SQLite |
|
| 33 | + * |
|
| 34 | + * Enlève les textes, transforme la requête pour quelle soit |
|
| 35 | + * bien interprétée par SQLite, puis remet les textes |
|
| 36 | + * la fonction affecte `$this->query` |
|
| 37 | + */ |
|
| 38 | + public function traduire_requete() { |
|
| 39 | + // |
|
| 40 | + // 1) Protection des textes en les remplacant par des codes |
|
| 41 | + // |
|
| 42 | + // enlever les 'textes' et initialiser avec |
|
| 43 | + [$this->query, $textes] = query_echappe_textes($this->query); |
|
| 44 | + |
|
| 45 | + // |
|
| 46 | + // 2) Corrections de la requete |
|
| 47 | + // |
|
| 48 | + // Correction Create Database |
|
| 49 | + // Create Database -> requete ignoree |
|
| 50 | + if (str_starts_with($this->query, 'CREATE DATABASE')) { |
|
| 51 | + spip_log("Sqlite : requete non executee -> $this->query", 'sqlite.' . _LOG_AVERTISSEMENT); |
|
| 52 | + $this->query = 'SELECT 1'; |
|
| 53 | + } |
|
| 54 | + |
|
| 55 | + // Correction Insert Ignore |
|
| 56 | + // INSERT IGNORE -> insert (tout court et pas 'insert or replace') |
|
| 57 | + if (str_starts_with($this->query, 'INSERT IGNORE')) { |
|
| 58 | + spip_log("Sqlite : requete transformee -> $this->query", 'sqlite.' . _LOG_DEBUG); |
|
| 59 | + $this->query = 'INSERT ' . substr($this->query, '13'); |
|
| 60 | + } |
|
| 61 | + |
|
| 62 | + // Correction des dates avec INTERVAL |
|
| 63 | + // utiliser sql_date_proche() de preference |
|
| 64 | + if (str_contains($this->query, 'INTERVAL')) { |
|
| 65 | + $this->query = preg_replace_callback( |
|
| 66 | + '/DATE_(ADD|SUB)(.*)INTERVAL\s+(\d+)\s+([a-zA-Z]+)\)/U', |
|
| 67 | + fn(array $matches): string => $this->_remplacerDateParTime($matches), |
|
| 68 | + $this->query |
|
| 69 | + ); |
|
| 70 | + } |
|
| 71 | + |
|
| 72 | + if (str_contains($this->query, 'LEFT(')) { |
|
| 73 | + $this->query = str_replace('LEFT(', '_LEFT(', $this->query); |
|
| 74 | + } |
|
| 75 | + |
|
| 76 | + if (str_contains($this->query, 'TIMESTAMPDIFF(')) { |
|
| 77 | + $this->query = preg_replace('/TIMESTAMPDIFF\(\s*([^,]*)\s*,/Uims', "TIMESTAMPDIFF('\\1',", $this->query); |
|
| 78 | + } |
|
| 79 | + |
|
| 80 | + |
|
| 81 | + // Correction Using |
|
| 82 | + // USING (non reconnu en sqlite2) |
|
| 83 | + // problematique car la jointure ne se fait pas du coup. |
|
| 84 | + if (($this->sqlite_version == 2) && (str_contains($this->query, 'USING'))) { |
|
| 85 | + spip_log( |
|
| 86 | + "'USING (champ)' n'est pas reconnu en SQLite 2. Utilisez 'ON table1.champ = table2.champ'", |
|
| 87 | + 'sqlite.' . _LOG_ERREUR |
|
| 88 | + ); |
|
| 89 | + $this->query = preg_replace('/USING\s*\([^\)]*\)/', '', $this->query); |
|
| 90 | + } |
|
| 91 | + |
|
| 92 | + // Correction Field |
|
| 93 | + // remplace FIELD(table,i,j,k...) par CASE WHEN table=i THEN n ... ELSE 0 END |
|
| 94 | + if (str_contains($this->query, 'FIELD')) { |
|
| 95 | + $this->query = preg_replace_callback( |
|
| 96 | + '/FIELD\s*\(([^\)]*)\)/', |
|
| 97 | + fn(array $matches): string => $this->_remplacerFieldParCase($matches), |
|
| 98 | + $this->query |
|
| 99 | + ); |
|
| 100 | + } |
|
| 101 | + |
|
| 102 | + // Correction des noms de tables FROM |
|
| 103 | + // mettre les bons noms de table dans from, update, insert, replace... |
|
| 104 | + if (preg_match('/\s(SET|VALUES|WHERE|DATABASE)\s/iS', $this->query, $regs)) { |
|
| 105 | + $suite = strstr($this->query, $regs[0]); |
|
| 106 | + $this->query = substr($this->query, 0, -strlen($suite)); |
|
| 107 | + } else { |
|
| 108 | + $suite = ''; |
|
| 109 | + } |
|
| 110 | + $pref = ($this->prefixe) ? $this->prefixe . '_' : ''; |
|
| 111 | + $this->query = preg_replace('/([,\s])spip_/S', '\1' . $pref, $this->query) . $suite; |
|
| 112 | + |
|
| 113 | + // Correction zero AS x |
|
| 114 | + // pg n'aime pas 0+x AS alias, sqlite, dans le meme style, |
|
| 115 | + // n'apprecie pas du tout SELECT 0 as x ... ORDER BY x |
|
| 116 | + // il dit que x ne doit pas être un integer dans le order by ! |
|
| 117 | + // on remplace du coup x par vide() dans ce cas uniquement |
|
| 118 | + // |
|
| 119 | + // apparait dans public/vertebrer.php et dans le plugin menu aussi qui genere aussi ce genre de requete via un {par num #GET{tri_num}} |
|
| 120 | + // mais est-ce encore un soucis pour sqlite en 2021 ? (ie commenter le preg_replace marche très bien en sqlite 3.28) |
|
| 121 | + // on ne remplace que dans ORDER BY ou GROUP BY |
|
| 122 | + if (str_contains($this->query, '0 AS') && preg_match('/\s(ORDER|GROUP) BY\s/i', $this->query, $regs)) { |
|
| 123 | + $suite = strstr($this->query, $regs[0]); |
|
| 124 | + $this->query = substr($this->query, 0, -strlen($suite)); |
|
| 125 | + // on cherche les noms des x dans 0 AS x |
|
| 126 | + // on remplace dans $suite le nom par vide() |
|
| 127 | + preg_match_all('/\b0 AS\s*([^\s,]+)/', $this->query, $matches, PREG_PATTERN_ORDER); |
|
| 128 | + foreach ($matches[1] as $m) { |
|
| 129 | + if (str_contains($suite, $m)) { |
|
| 130 | + $suite = preg_replace(",\b$m\b,", 'VIDE()', $suite); |
|
| 131 | + } |
|
| 132 | + } |
|
| 133 | + $this->query .= $suite; |
|
| 134 | + } |
|
| 135 | + |
|
| 136 | + // Correction possible des divisions entieres |
|
| 137 | + // Le standard SQL (lequel? ou?) semble indiquer que |
|
| 138 | + // a/b=c doit donner c entier si a et b sont entiers 4/3=1. |
|
| 139 | + // C'est ce que retournent effectivement SQL Server et SQLite |
|
| 140 | + // Ce n'est pas ce qu'applique MySQL qui retourne un reel : 4/3=1.333... |
|
| 141 | + // |
|
| 142 | + // On peut forcer la conversion en multipliant par 1.0 avant la division |
|
| 143 | + // /!\ SQLite 3.5.9 Debian/Ubuntu est victime d'un bug en plus ! |
|
| 144 | + // cf. https://bugs.launchpad.net/ubuntu/+source/sqlite3/+bug/254228 |
|
| 145 | + // http://www.sqlite.org/cvstrac/tktview?tn=3202 |
|
| 146 | + // (4*1.0/3) n'est pas rendu dans ce cas ! |
|
| 147 | + # $this->query = str_replace('/','* 1.00 / ',$this->query); |
|
| 148 | + |
|
| 149 | + |
|
| 150 | + // Correction critere REGEXP, non reconnu en sqlite2 |
|
| 151 | + if (($this->sqlite_version == 2) && (str_contains($this->query, 'REGEXP'))) { |
|
| 152 | + $this->query = preg_replace('/([^\s\(]*)(\s*)REGEXP(\s*)([^\s\)]*)/', 'REGEXP($4, $1)', $this->query); |
|
| 153 | + } |
|
| 154 | + |
|
| 155 | + // |
|
| 156 | + // 3) Remise en place des textes d'origine |
|
| 157 | + // |
|
| 158 | + // Correction Antiquotes et echappements |
|
| 159 | + // ` => rien |
|
| 160 | + if (str_contains($this->query, '`')) { |
|
| 161 | + $this->query = str_replace('`', '', $this->query); |
|
| 162 | + } |
|
| 163 | + |
|
| 164 | + $this->query = query_reinjecte_textes($this->query, $textes); |
|
| 165 | + |
|
| 166 | + return $this->query; |
|
| 167 | + } |
|
| 168 | + |
|
| 169 | + /** |
|
| 170 | + * Callback pour remplacer `DATE_` / `INTERVAL` |
|
| 171 | + * par `DATE ... strtotime` |
|
| 172 | + * |
|
| 173 | + * @param array $matches Captures |
|
| 174 | + * @return string texte de date compris par SQLite |
|
| 175 | + */ |
|
| 176 | + public function _remplacerDateParTime($matches) { |
|
| 177 | + $op = strtoupper($matches[1] == 'ADD') ? '+' : '-'; |
|
| 178 | + |
|
| 179 | + return "datetime$matches[2] '$op$matches[3] $matches[4]')"; |
|
| 180 | + } |
|
| 181 | + |
|
| 182 | + /** |
|
| 183 | + * Callback pour remplacer `FIELD(table,i,j,k...)` |
|
| 184 | + * par `CASE WHEN table=i THEN n ... ELSE 0 END` |
|
| 185 | + * |
|
| 186 | + * @param array $matches Captures |
|
| 187 | + * @return string texte de liste ordonnée compris par SQLite |
|
| 188 | + */ |
|
| 189 | + public function _remplacerFieldParCase($matches) { |
|
| 190 | + $fields = substr($matches[0], 6, -1); // ne recuperer que l'interieur X de field(X) |
|
| 191 | + $t = explode(',', $fields); |
|
| 192 | + $index = array_shift($t); |
|
| 193 | + |
|
| 194 | + $res = ''; |
|
| 195 | + $n = 0; |
|
| 196 | + foreach ($t as $v) { |
|
| 197 | + $n++; |
|
| 198 | + $res .= "\nWHEN $index=$v THEN $n"; |
|
| 199 | + } |
|
| 200 | + |
|
| 201 | + return "CASE $res ELSE 0 END "; |
|
| 202 | + } |
|
| 203 | 203 | } |
@@ -48,15 +48,15 @@ discard block |
||
| 48 | 48 | // Correction Create Database |
| 49 | 49 | // Create Database -> requete ignoree |
| 50 | 50 | if (str_starts_with($this->query, 'CREATE DATABASE')) { |
| 51 | - spip_log("Sqlite : requete non executee -> $this->query", 'sqlite.' . _LOG_AVERTISSEMENT); |
|
| 51 | + spip_log("Sqlite : requete non executee -> $this->query", 'sqlite.'._LOG_AVERTISSEMENT); |
|
| 52 | 52 | $this->query = 'SELECT 1'; |
| 53 | 53 | } |
| 54 | 54 | |
| 55 | 55 | // Correction Insert Ignore |
| 56 | 56 | // INSERT IGNORE -> insert (tout court et pas 'insert or replace') |
| 57 | 57 | if (str_starts_with($this->query, 'INSERT IGNORE')) { |
| 58 | - spip_log("Sqlite : requete transformee -> $this->query", 'sqlite.' . _LOG_DEBUG); |
|
| 59 | - $this->query = 'INSERT ' . substr($this->query, '13'); |
|
| 58 | + spip_log("Sqlite : requete transformee -> $this->query", 'sqlite.'._LOG_DEBUG); |
|
| 59 | + $this->query = 'INSERT '.substr($this->query, '13'); |
|
| 60 | 60 | } |
| 61 | 61 | |
| 62 | 62 | // Correction des dates avec INTERVAL |
@@ -84,7 +84,7 @@ discard block |
||
| 84 | 84 | if (($this->sqlite_version == 2) && (str_contains($this->query, 'USING'))) { |
| 85 | 85 | spip_log( |
| 86 | 86 | "'USING (champ)' n'est pas reconnu en SQLite 2. Utilisez 'ON table1.champ = table2.champ'", |
| 87 | - 'sqlite.' . _LOG_ERREUR |
|
| 87 | + 'sqlite.'._LOG_ERREUR |
|
| 88 | 88 | ); |
| 89 | 89 | $this->query = preg_replace('/USING\s*\([^\)]*\)/', '', $this->query); |
| 90 | 90 | } |
@@ -107,8 +107,8 @@ discard block |
||
| 107 | 107 | } else { |
| 108 | 108 | $suite = ''; |
| 109 | 109 | } |
| 110 | - $pref = ($this->prefixe) ? $this->prefixe . '_' : ''; |
|
| 111 | - $this->query = preg_replace('/([,\s])spip_/S', '\1' . $pref, $this->query) . $suite; |
|
| 110 | + $pref = ($this->prefixe) ? $this->prefixe.'_' : ''; |
|
| 111 | + $this->query = preg_replace('/([,\s])spip_/S', '\1'.$pref, $this->query).$suite; |
|
| 112 | 112 | |
| 113 | 113 | // Correction zero AS x |
| 114 | 114 | // pg n'aime pas 0+x AS alias, sqlite, dans le meme style, |
@@ -7,85 +7,85 @@ |
||
| 7 | 7 | **/ |
| 8 | 8 | class Idiome |
| 9 | 9 | { |
| 10 | - /** Type de noeud */ |
|
| 11 | - public string $type = 'idiome'; |
|
| 12 | - |
|
| 13 | - /** Clé de traduction demandée. Exemple 'item_oui' */ |
|
| 14 | - public string $nom_champ = ''; |
|
| 15 | - |
|
| 16 | - /** Module de langue où chercher la clé de traduction. Exemple 'medias' */ |
|
| 17 | - public string $module = ''; |
|
| 18 | - |
|
| 19 | - /** Arguments à passer à la chaîne */ |
|
| 20 | - public array $arg = []; |
|
| 21 | - |
|
| 22 | - /** |
|
| 23 | - * Filtres à appliquer au résultat |
|
| 24 | - * |
|
| 25 | - * |
|
| 26 | - * * FIXME: type unique. |
|
| 27 | - * @var false|array |
|
| 28 | - * - false: erreur de syntaxe |
|
| 29 | - */ |
|
| 30 | - public $param = []; |
|
| 31 | - |
|
| 32 | - /** Source des filtres (compatibilité) (?) */ |
|
| 33 | - public array $fonctions = []; |
|
| 34 | - |
|
| 35 | - /** |
|
| 36 | - * Inutilisé, propriété générique de l'AST |
|
| 37 | - * |
|
| 38 | - * @var string|array |
|
| 39 | - */ |
|
| 40 | - public $avant = ''; |
|
| 41 | - |
|
| 42 | - /** |
|
| 43 | - * Inutilisé, propriété générique de l'AST |
|
| 44 | - * |
|
| 45 | - * @var string|array |
|
| 46 | - */ |
|
| 47 | - public $apres = ''; |
|
| 48 | - |
|
| 49 | - /** Identifiant de la boucle */ |
|
| 50 | - public string $id_boucle = ''; |
|
| 51 | - |
|
| 52 | - /** |
|
| 53 | - * AST du squelette, liste de toutes les boucles |
|
| 54 | - * |
|
| 55 | - * @var Boucle[] |
|
| 56 | - */ |
|
| 57 | - public array $boucles; |
|
| 58 | - |
|
| 59 | - /** Alias de table d'application de la requête ou nom complet de la table SQL */ |
|
| 60 | - public ?string $type_requete = null; |
|
| 61 | - |
|
| 62 | - /** Résultat de la compilation: toujours une expression PHP */ |
|
| 63 | - public string $code = ''; |
|
| 64 | - |
|
| 65 | - /** |
|
| 66 | - * Interdire les scripts |
|
| 67 | - * |
|
| 68 | - * @see interdire_scripts() |
|
| 69 | - */ |
|
| 70 | - public bool $interdire_scripts = false; |
|
| 71 | - |
|
| 72 | - /** |
|
| 73 | - * Description du squelette |
|
| 74 | - * |
|
| 75 | - * Sert pour la gestion d'erreur et la production de code dependant du contexte |
|
| 76 | - * |
|
| 77 | - * Peut contenir les index : |
|
| 78 | - * - nom : Nom du fichier de cache |
|
| 79 | - * - gram : Nom de la grammaire du squelette (détermine le phraseur à utiliser) |
|
| 80 | - * - sourcefile : Chemin du squelette |
|
| 81 | - * - squelette : Code du squelette |
|
| 82 | - * - id_mere : Identifiant de la boucle parente |
|
| 83 | - * - documents : Pour embed et img dans les textes |
|
| 84 | - * - session : Pour un cache sessionné par auteur |
|
| 85 | - * - niv : Niveau de tabulation |
|
| 86 | - */ |
|
| 87 | - public array $descr = []; |
|
| 88 | - |
|
| 89 | - /** Numéro de ligne dans le code source du squelette */ |
|
| 90 | - public int $ligne = 0; |
|
| 10 | + /** Type de noeud */ |
|
| 11 | + public string $type = 'idiome'; |
|
| 12 | + |
|
| 13 | + /** Clé de traduction demandée. Exemple 'item_oui' */ |
|
| 14 | + public string $nom_champ = ''; |
|
| 15 | + |
|
| 16 | + /** Module de langue où chercher la clé de traduction. Exemple 'medias' */ |
|
| 17 | + public string $module = ''; |
|
| 18 | + |
|
| 19 | + /** Arguments à passer à la chaîne */ |
|
| 20 | + public array $arg = []; |
|
| 21 | + |
|
| 22 | + /** |
|
| 23 | + * Filtres à appliquer au résultat |
|
| 24 | + * |
|
| 25 | + * |
|
| 26 | + * * FIXME: type unique. |
|
| 27 | + * @var false|array |
|
| 28 | + * - false: erreur de syntaxe |
|
| 29 | + */ |
|
| 30 | + public $param = []; |
|
| 31 | + |
|
| 32 | + /** Source des filtres (compatibilité) (?) */ |
|
| 33 | + public array $fonctions = []; |
|
| 34 | + |
|
| 35 | + /** |
|
| 36 | + * Inutilisé, propriété générique de l'AST |
|
| 37 | + * |
|
| 38 | + * @var string|array |
|
| 39 | + */ |
|
| 40 | + public $avant = ''; |
|
| 41 | + |
|
| 42 | + /** |
|
| 43 | + * Inutilisé, propriété générique de l'AST |
|
| 44 | + * |
|
| 45 | + * @var string|array |
|
| 46 | + */ |
|
| 47 | + public $apres = ''; |
|
| 48 | + |
|
| 49 | + /** Identifiant de la boucle */ |
|
| 50 | + public string $id_boucle = ''; |
|
| 51 | + |
|
| 52 | + /** |
|
| 53 | + * AST du squelette, liste de toutes les boucles |
|
| 54 | + * |
|
| 55 | + * @var Boucle[] |
|
| 56 | + */ |
|
| 57 | + public array $boucles; |
|
| 58 | + |
|
| 59 | + /** Alias de table d'application de la requête ou nom complet de la table SQL */ |
|
| 60 | + public ?string $type_requete = null; |
|
| 61 | + |
|
| 62 | + /** Résultat de la compilation: toujours une expression PHP */ |
|
| 63 | + public string $code = ''; |
|
| 64 | + |
|
| 65 | + /** |
|
| 66 | + * Interdire les scripts |
|
| 67 | + * |
|
| 68 | + * @see interdire_scripts() |
|
| 69 | + */ |
|
| 70 | + public bool $interdire_scripts = false; |
|
| 71 | + |
|
| 72 | + /** |
|
| 73 | + * Description du squelette |
|
| 74 | + * |
|
| 75 | + * Sert pour la gestion d'erreur et la production de code dependant du contexte |
|
| 76 | + * |
|
| 77 | + * Peut contenir les index : |
|
| 78 | + * - nom : Nom du fichier de cache |
|
| 79 | + * - gram : Nom de la grammaire du squelette (détermine le phraseur à utiliser) |
|
| 80 | + * - sourcefile : Chemin du squelette |
|
| 81 | + * - squelette : Code du squelette |
|
| 82 | + * - id_mere : Identifiant de la boucle parente |
|
| 83 | + * - documents : Pour embed et img dans les textes |
|
| 84 | + * - session : Pour un cache sessionné par auteur |
|
| 85 | + * - niv : Niveau de tabulation |
|
| 86 | + */ |
|
| 87 | + public array $descr = []; |
|
| 88 | + |
|
| 89 | + /** Numéro de ligne dans le code source du squelette */ |
|
| 90 | + public int $ligne = 0; |
|
| 91 | 91 | } |
@@ -7,104 +7,104 @@ |
||
| 7 | 7 | */ |
| 8 | 8 | class Champ |
| 9 | 9 | { |
| 10 | - /** Type de noeud */ |
|
| 11 | - public string $type = 'champ'; |
|
| 12 | - |
|
| 13 | - /** Nom du champ demandé. Exemple 'ID_ARTICLE' */ |
|
| 14 | - public ?string $nom_champ = null; |
|
| 15 | - |
|
| 16 | - /** Identifiant de la boucle parente si explicité */ |
|
| 17 | - public ?string $nom_boucle = ''; |
|
| 18 | - |
|
| 19 | - /** |
|
| 20 | - * Partie optionnelle avant |
|
| 21 | - * |
|
| 22 | - * @var null|string|array |
|
| 23 | - */ |
|
| 24 | - public $avant; |
|
| 25 | - |
|
| 26 | - /** |
|
| 27 | - * Partie optionnelle après |
|
| 28 | - * |
|
| 29 | - * @var null|string|array |
|
| 30 | - */ |
|
| 31 | - public $apres; |
|
| 32 | - |
|
| 33 | - /** |
|
| 34 | - * Étoiles : annuler des automatismes |
|
| 35 | - * |
|
| 36 | - * - '*' annule les filtres automatiques |
|
| 37 | - * - '**' annule en plus les protections de scripts |
|
| 38 | - * |
|
| 39 | - * FIXME: type unique. |
|
| 40 | - */ |
|
| 41 | - public ?string $etoile = ''; |
|
| 42 | - |
|
| 43 | - /** |
|
| 44 | - * Arguments et filtres explicites sur la balise |
|
| 45 | - * |
|
| 46 | - * - $param[0] contient les arguments de la balise |
|
| 47 | - * - $param[1..n] contient les filtres à appliquer à la balise |
|
| 48 | - * |
|
| 49 | - * FIXME: type unique. |
|
| 50 | - * @var false|array |
|
| 51 | - * - false: erreur de syntaxe |
|
| 52 | - */ |
|
| 53 | - public $param = []; |
|
| 54 | - |
|
| 55 | - /** Source des filtres (compatibilité) (?) */ |
|
| 56 | - public array $fonctions = []; |
|
| 57 | - |
|
| 58 | - /** |
|
| 59 | - * Identifiant de la boucle |
|
| 60 | - * |
|
| 61 | - * @var string |
|
| 62 | - */ |
|
| 63 | - public $id_boucle = ''; |
|
| 64 | - |
|
| 65 | - /** |
|
| 66 | - * AST du squelette, liste de toutes les boucles |
|
| 67 | - * |
|
| 68 | - * @var Boucle[] |
|
| 69 | - */ |
|
| 70 | - public array $boucles; |
|
| 71 | - |
|
| 72 | - /** Alias de table d'application de la requête ou nom complet de la table SQL */ |
|
| 73 | - public ?string $type_requete = null; |
|
| 74 | - |
|
| 75 | - /** Résultat de la compilation: toujours une expression PHP */ |
|
| 76 | - public string $code = ''; |
|
| 77 | - |
|
| 78 | - /** |
|
| 79 | - * Interdire les scripts |
|
| 80 | - * |
|
| 81 | - * false si on est sûr de cette balise |
|
| 82 | - * |
|
| 83 | - * @see interdire_scripts() |
|
| 84 | - */ |
|
| 85 | - public bool $interdire_scripts = true; |
|
| 86 | - |
|
| 87 | - /** |
|
| 88 | - * Description du squelette |
|
| 89 | - * |
|
| 90 | - * Sert pour la gestion d'erreur et la production de code dependant du contexte |
|
| 91 | - * |
|
| 92 | - * Peut contenir les index : |
|
| 93 | - * |
|
| 94 | - * - nom : Nom du fichier de cache |
|
| 95 | - * - gram : Nom de la grammaire du squelette (détermine le phraseur à utiliser) |
|
| 96 | - * - sourcefile : Chemin du squelette |
|
| 97 | - * - squelette : Code du squelette |
|
| 98 | - * - id_mere : Identifiant de la boucle parente |
|
| 99 | - * - documents : Pour embed et img dans les textes |
|
| 100 | - * - session : Pour un cache sessionné par auteur |
|
| 101 | - * - niv : Niveau de tabulation |
|
| 102 | - */ |
|
| 103 | - public array $descr = []; |
|
| 104 | - |
|
| 105 | - /** Numéro de ligne dans le code source du squelette*/ |
|
| 106 | - public int $ligne = 0; |
|
| 107 | - |
|
| 108 | - /** Drapeau pour reperer les balises calculées par une fonction explicite */ |
|
| 109 | - public bool $balise_calculee = false; |
|
| 10 | + /** Type de noeud */ |
|
| 11 | + public string $type = 'champ'; |
|
| 12 | + |
|
| 13 | + /** Nom du champ demandé. Exemple 'ID_ARTICLE' */ |
|
| 14 | + public ?string $nom_champ = null; |
|
| 15 | + |
|
| 16 | + /** Identifiant de la boucle parente si explicité */ |
|
| 17 | + public ?string $nom_boucle = ''; |
|
| 18 | + |
|
| 19 | + /** |
|
| 20 | + * Partie optionnelle avant |
|
| 21 | + * |
|
| 22 | + * @var null|string|array |
|
| 23 | + */ |
|
| 24 | + public $avant; |
|
| 25 | + |
|
| 26 | + /** |
|
| 27 | + * Partie optionnelle après |
|
| 28 | + * |
|
| 29 | + * @var null|string|array |
|
| 30 | + */ |
|
| 31 | + public $apres; |
|
| 32 | + |
|
| 33 | + /** |
|
| 34 | + * Étoiles : annuler des automatismes |
|
| 35 | + * |
|
| 36 | + * - '*' annule les filtres automatiques |
|
| 37 | + * - '**' annule en plus les protections de scripts |
|
| 38 | + * |
|
| 39 | + * FIXME: type unique. |
|
| 40 | + */ |
|
| 41 | + public ?string $etoile = ''; |
|
| 42 | + |
|
| 43 | + /** |
|
| 44 | + * Arguments et filtres explicites sur la balise |
|
| 45 | + * |
|
| 46 | + * - $param[0] contient les arguments de la balise |
|
| 47 | + * - $param[1..n] contient les filtres à appliquer à la balise |
|
| 48 | + * |
|
| 49 | + * FIXME: type unique. |
|
| 50 | + * @var false|array |
|
| 51 | + * - false: erreur de syntaxe |
|
| 52 | + */ |
|
| 53 | + public $param = []; |
|
| 54 | + |
|
| 55 | + /** Source des filtres (compatibilité) (?) */ |
|
| 56 | + public array $fonctions = []; |
|
| 57 | + |
|
| 58 | + /** |
|
| 59 | + * Identifiant de la boucle |
|
| 60 | + * |
|
| 61 | + * @var string |
|
| 62 | + */ |
|
| 63 | + public $id_boucle = ''; |
|
| 64 | + |
|
| 65 | + /** |
|
| 66 | + * AST du squelette, liste de toutes les boucles |
|
| 67 | + * |
|
| 68 | + * @var Boucle[] |
|
| 69 | + */ |
|
| 70 | + public array $boucles; |
|
| 71 | + |
|
| 72 | + /** Alias de table d'application de la requête ou nom complet de la table SQL */ |
|
| 73 | + public ?string $type_requete = null; |
|
| 74 | + |
|
| 75 | + /** Résultat de la compilation: toujours une expression PHP */ |
|
| 76 | + public string $code = ''; |
|
| 77 | + |
|
| 78 | + /** |
|
| 79 | + * Interdire les scripts |
|
| 80 | + * |
|
| 81 | + * false si on est sûr de cette balise |
|
| 82 | + * |
|
| 83 | + * @see interdire_scripts() |
|
| 84 | + */ |
|
| 85 | + public bool $interdire_scripts = true; |
|
| 86 | + |
|
| 87 | + /** |
|
| 88 | + * Description du squelette |
|
| 89 | + * |
|
| 90 | + * Sert pour la gestion d'erreur et la production de code dependant du contexte |
|
| 91 | + * |
|
| 92 | + * Peut contenir les index : |
|
| 93 | + * |
|
| 94 | + * - nom : Nom du fichier de cache |
|
| 95 | + * - gram : Nom de la grammaire du squelette (détermine le phraseur à utiliser) |
|
| 96 | + * - sourcefile : Chemin du squelette |
|
| 97 | + * - squelette : Code du squelette |
|
| 98 | + * - id_mere : Identifiant de la boucle parente |
|
| 99 | + * - documents : Pour embed et img dans les textes |
|
| 100 | + * - session : Pour un cache sessionné par auteur |
|
| 101 | + * - niv : Niveau de tabulation |
|
| 102 | + */ |
|
| 103 | + public array $descr = []; |
|
| 104 | + |
|
| 105 | + /** Numéro de ligne dans le code source du squelette*/ |
|
| 106 | + public int $ligne = 0; |
|
| 107 | + |
|
| 108 | + /** Drapeau pour reperer les balises calculées par une fonction explicite */ |
|
| 109 | + public bool $balise_calculee = false; |
|
| 110 | 110 | } |
@@ -8,32 +8,32 @@ |
||
| 8 | 8 | * Sous-noeud de Boucle |
| 9 | 9 | **/ |
| 10 | 10 | class Critere { |
| 11 | - /** Type de noeud */ |
|
| 12 | - public string $type = 'critere'; |
|
| 13 | - |
|
| 14 | - /** Opérateur (>, <, >=, IN, ...) */ |
|
| 15 | - public ?string $op = null; |
|
| 16 | - |
|
| 17 | - /** Présence d'une négation (truc !op valeur) */ |
|
| 18 | - public bool $not = false; |
|
| 19 | - |
|
| 20 | - /** Présence d'une exclusion (!truc op valeur) */ |
|
| 21 | - public string $exclus = ''; |
|
| 22 | - |
|
| 23 | - /** Présence d'une condition dans le critère (truc ?) */ |
|
| 24 | - public bool $cond = false; |
|
| 25 | - |
|
| 26 | - /** |
|
| 27 | - * Paramètres du critère |
|
| 28 | - * - $param[0] : élément avant l'opérateur |
|
| 29 | - * - $param[1..n] : éléments après l'opérateur |
|
| 30 | - * |
|
| 31 | - * FIXME: type unique. |
|
| 32 | - * @var false|array |
|
| 33 | - * - false: erreur de syntaxe |
|
| 34 | - */ |
|
| 35 | - public $param = []; |
|
| 36 | - |
|
| 37 | - /** Numéro de ligne dans le code source du squelette */ |
|
| 38 | - public int $ligne = 0; |
|
| 11 | + /** Type de noeud */ |
|
| 12 | + public string $type = 'critere'; |
|
| 13 | + |
|
| 14 | + /** Opérateur (>, <, >=, IN, ...) */ |
|
| 15 | + public ?string $op = null; |
|
| 16 | + |
|
| 17 | + /** Présence d'une négation (truc !op valeur) */ |
|
| 18 | + public bool $not = false; |
|
| 19 | + |
|
| 20 | + /** Présence d'une exclusion (!truc op valeur) */ |
|
| 21 | + public string $exclus = ''; |
|
| 22 | + |
|
| 23 | + /** Présence d'une condition dans le critère (truc ?) */ |
|
| 24 | + public bool $cond = false; |
|
| 25 | + |
|
| 26 | + /** |
|
| 27 | + * Paramètres du critère |
|
| 28 | + * - $param[0] : élément avant l'opérateur |
|
| 29 | + * - $param[1..n] : éléments après l'opérateur |
|
| 30 | + * |
|
| 31 | + * FIXME: type unique. |
|
| 32 | + * @var false|array |
|
| 33 | + * - false: erreur de syntaxe |
|
| 34 | + */ |
|
| 35 | + public $param = []; |
|
| 36 | + |
|
| 37 | + /** Numéro de ligne dans le code source du squelette */ |
|
| 38 | + public int $ligne = 0; |
|
| 39 | 39 | } |