ZwiiCMS, le gestionnaire de site Web sans base de données. #zwii #cms #nosql #json https://www.zwiicms.fr
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

584 lines
17 KiB

5 months ago
5 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
1 year ago
7 months ago
1 year ago
1 year ago
1 year ago
  1. <?php
  2. class helper {
  3. /** Statut de la réécriture d'URL (pour éviter de lire le contenu du fichier .htaccess à chaque self::baseUrl()) */
  4. public static $rewriteStatus = null;
  5. /** Filtres personnalisés */
  6. const FILTER_BOOLEAN = 1;
  7. const FILTER_DATETIME = 2;
  8. const FILTER_FLOAT = 3;
  9. const FILTER_ID = 4;
  10. const FILTER_INT = 5;
  11. const FILTER_MAIL = 6;
  12. const FILTER_PASSWORD = 7;
  13. const FILTER_STRING_LONG = 8;
  14. const FILTER_STRING_SHORT = 9;
  15. const FILTER_TIMESTAMP = 10;
  16. const FILTER_URL = 11;
  17. /**
  18. * Récupérer l'adresse IP sans tenit compte du proxy
  19. * @return string IP adress
  20. * Cette focntion est utilisé par user
  21. */
  22. public static function getIp() {
  23. if(!empty($_SERVER['HTTP_CLIENT_IP'])){
  24. $ip=$_SERVER['HTTP_CLIENT_IP'];
  25. }
  26. elseif(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])){
  27. $ip=$_SERVER['HTTP_X_FORWARDED_FOR'];
  28. }
  29. else{
  30. $ip=$_SERVER['REMOTE_ADDR'];
  31. }
  32. return $ip;
  33. }
  34. /**
  35. * Fonction pour récupérer le numéro de version en ligne et le catalogue des modules
  36. * @param string $url à récupérer
  37. * @return mixed données récupérées
  38. */
  39. public static function urlGetContents ($url) {
  40. // Ejecter free.fr
  41. if (strpos(self::baseUrl(),'free.fr') > 0 ){
  42. return false;
  43. }
  44. if(function_exists('file_get_contents') &&
  45. ini_get('allow_url_fopen') ){
  46. $url_get_contents_data = @file_get_contents($url); // Masque un warning éventuel
  47. }elseif(function_exists('curl_version')){
  48. $ch = curl_init();
  49. curl_setopt($ch, CURLOPT_HEADER, 0);
  50. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  51. curl_setopt($ch, CURLOPT_URL, $url);
  52. $url_get_contents_data = curl_exec($ch);
  53. curl_close($ch);
  54. }elseif(function_exists('fopen') &&
  55. function_exists('stream_get_contents') &&
  56. ini_get('allow_url_fopen')){
  57. $handle = fopen ($url, "r");
  58. $url_get_contents_data = stream_get_contents($handle);
  59. }else{
  60. $url_get_contents_data = false;
  61. }
  62. return $url_get_contents_data;
  63. }
  64. /**
  65. * Retourne les valeurs d'une colonne du tableau de données
  66. * @param array $array Tableau cible
  67. * @param string $column Colonne à extraire
  68. * @param string $sort Type de tri à appliquer au tableau (SORT_ASC, SORT_DESC, ou null)
  69. * @return array
  70. */
  71. public static function arrayCollumn($array, $column, $sort = null) {
  72. $newArray = [];
  73. if(empty($array) === false) {
  74. $newArray = array_map(function($element) use($column) {
  75. return $element[$column];
  76. }, $array);
  77. switch($sort) {
  78. case 'SORT_ASC':
  79. asort($newArray);
  80. break;
  81. case 'SORT_DESC':
  82. arsort($newArray);
  83. break;
  84. }
  85. }
  86. return $newArray;
  87. }
  88. /**
  89. * Génère un backup des données de site
  90. * @param string $folder dossier de sauvegarde
  91. * @param array $exclude dossier exclus
  92. * @return string nom du fichier de sauvegarde
  93. */
  94. public static function autoBackup($folder, $filter = ['backup','tmp'] ) {
  95. // Creation du ZIP
  96. $baseName = str_replace('/','',helper::baseUrl(false,false));
  97. $baseName = empty($baseName) ? 'ZwiiCMS' : $baseName;
  98. $fileName = $baseName . '-backup-' . date('Y-m-d-h-i-s', time()) . '.zip';
  99. $zip = new ZipArchive();
  100. $zip->open($folder . $fileName, ZipArchive::CREATE | ZipArchive::OVERWRITE);
  101. $directory = 'site/';
  102. //$filter = array('backup','tmp','file');
  103. $files = new RecursiveIteratorIterator(
  104. new RecursiveCallbackFilterIterator(
  105. new RecursiveDirectoryIterator(
  106. $directory,
  107. RecursiveDirectoryIterator::SKIP_DOTS
  108. ),
  109. function ($fileInfo, $key, $iterator) use ($filter) {
  110. return $fileInfo->isFile() || !in_array($fileInfo->getBaseName(), $filter);
  111. }
  112. )
  113. );
  114. foreach ($files as $name => $file) {
  115. if (!$file->isDir()) {
  116. $filePath = $file->getRealPath();
  117. $relativePath = substr($filePath, strlen(realpath($directory)) + 1);
  118. $zip->addFile($filePath, $relativePath);
  119. }
  120. }
  121. $zip->close();
  122. return ($fileName);
  123. }
  124. /**
  125. * Retourne la liste des modules installés dans un tableau composé
  126. * du nom réel
  127. * du numéro de version
  128. */
  129. public static function getModules() {
  130. $modules = array();
  131. $dirs = array_diff(scandir('module'), array('..', '.'));
  132. foreach ($dirs as $key => $value) {
  133. // Dossier non vide
  134. if (file_exists('module/' . $value . '/' . $value . '.php')) {
  135. // Lire les constantes en gérant les erreurs de nom de classe
  136. try {
  137. $class_reflex = new \ReflectionClass($value);
  138. $class_constants = $class_reflex->getConstants();
  139. // Constante REALNAME
  140. if (array_key_exists('REALNAME', $class_constants)) {
  141. $realName = $value::REALNAME;
  142. } else {
  143. $realName = ucfirst($value);
  144. }
  145. // Constante VERSION
  146. if (array_key_exists('VERSION', $class_constants)) {
  147. $version = $value::VERSION;
  148. } else {
  149. $version = '0.0';
  150. }
  151. // Constante UPDATE
  152. if (array_key_exists('UPDATE', $class_constants)) {
  153. $update = $value::UPDATE;
  154. } else {
  155. $update = '0.0';
  156. }
  157. // Constante DELETE
  158. if (array_key_exists('DELETE', $class_constants)) {
  159. $delete = $value::DELETE;
  160. } else {
  161. $delete = true;
  162. }
  163. // Constante DATADIRECTORY
  164. if ( array_key_exists('DATADIRECTORY', $class_constants)
  165. && $class_constants['DATADIRECTORY'] !== []
  166. && is_array($class_constants['DATADIRECTORY'])
  167. ) {
  168. $dataDirectory = $value::DATADIRECTORY;
  169. } else {
  170. $dataDirectory = [];
  171. }
  172. // Affection
  173. $modules [$value] = [
  174. 'realName' => $realName,
  175. 'version' => $version,
  176. 'update' => $update,
  177. 'delete' => $delete,
  178. 'dataDirectory' => $dataDirectory
  179. ];
  180. } catch (Exception $e){
  181. // on ne fait rien
  182. }
  183. }
  184. }
  185. return($modules);
  186. }
  187. /**
  188. * Retourne true si le protocole est en TLS
  189. * @return bool
  190. */
  191. public static function isHttps() {
  192. if(
  193. (empty($_SERVER['HTTPS']) === false AND $_SERVER['HTTPS'] !== 'off')
  194. OR $_SERVER['SERVER_PORT'] === 443
  195. ) {
  196. return true;
  197. } else {
  198. return false;
  199. }
  200. }
  201. /**
  202. * Retourne l'URL de base du site
  203. * @param bool $queryString Affiche ou non le point d'interrogation
  204. * @param bool $host Affiche ou non l'host
  205. * @return string
  206. */
  207. public static function baseUrl($queryString = true, $host = true) {
  208. // Protocole
  209. $protocol = helper::isHttps() === true ? 'https://' : 'http://';
  210. // Host
  211. if($host) {
  212. $host = $protocol . $_SERVER['HTTP_HOST'];
  213. }
  214. // Pathinfo
  215. $pathInfo = pathinfo($_SERVER['PHP_SELF']);
  216. // Querystring
  217. if($queryString AND helper::checkRewrite() === false) {
  218. $queryString = '?';
  219. }
  220. else {
  221. $queryString = '';
  222. }
  223. return $host . rtrim($pathInfo['dirname'], ' ' . DIRECTORY_SEPARATOR) . '/' . $queryString;
  224. }
  225. /**
  226. * Check le statut de l'URL rewriting
  227. * @return bool
  228. */
  229. public static function checkRewrite() {
  230. if(self::$rewriteStatus === null) {
  231. // Ouvre et scinde le fichier .htaccess
  232. $htaccess = explode('# URL rewriting', file_get_contents('.htaccess'));
  233. // Retourne un boolean en fonction du contenu de la partie réservée à l'URL rewriting
  234. self::$rewriteStatus = (empty($htaccess[1]) === false);
  235. }
  236. return self::$rewriteStatus;
  237. }
  238. /**
  239. * Renvoie le numéro de version de Zwii est en ligne
  240. * @return string
  241. */
  242. public static function getOnlineVersion() {
  243. return (helper::urlGetContents('http://zwiicms.fr/update/'. common::ZWII_UPDATE_CHANNEL . '/version'));
  244. }
  245. /**
  246. * Check si une nouvelle version de Zwii est disponible
  247. * @return bool
  248. */
  249. public static function checkNewVersion() {
  250. if($version = helper::getOnlineVersion()) {
  251. //return (trim($version) !== common::ZWII_VERSION);
  252. return ((version_compare(common::ZWII_VERSION,$version)) === -1);
  253. }
  254. else {
  255. return false;
  256. }
  257. }
  258. /**
  259. * Génère des variations d'une couleur
  260. * @param string $rgba Code rgba de la couleur
  261. * @return array
  262. */
  263. public static function colorVariants($rgba) {
  264. preg_match('#\(+(.*)\)+#', $rgba, $matches);
  265. $rgba = explode(', ', $matches[1]);
  266. return [
  267. 'normal' => 'rgba(' . $rgba[0] . ',' . $rgba[1] . ',' . $rgba[2] . ',' . $rgba[3] . ')',
  268. 'darken' => 'rgba(' . max(0, $rgba[0] - 15) . ',' . max(0, $rgba[1] - 15) . ',' . max(0, $rgba[2] - 15) . ',' . $rgba[3] . ')',
  269. 'veryDarken' => 'rgba(' . max(0, $rgba[0] - 20) . ',' . max(0, $rgba[1] - 20) . ',' . max(0, $rgba[2] - 20) . ',' . $rgba[3] . ')',
  270. 'text' => self::relativeLuminanceW3C($rgba) > .22 ? "#222" : "#DDD",
  271. 'rgb' => 'rgb(' . $rgba[0] . ',' . $rgba[1] . ',' . $rgba[2] . ')'
  272. ];
  273. }
  274. /**
  275. * Supprime un cookie
  276. * @param string $cookieKey Clé du cookie à supprimer
  277. */
  278. public static function deleteCookie($cookieKey) {
  279. unset($_COOKIE[$cookieKey]);
  280. setcookie($cookieKey, '', time() - 3600, helper::baseUrl(false, false), '', false, true);
  281. }
  282. /**
  283. * Filtre une chaîne en fonction d'un tableau de données
  284. * @param string $text Chaîne à filtrer
  285. * @param int $filter Type de filtre à appliquer
  286. * @return string
  287. */
  288. public static function filter($text, $filter) {
  289. $text = trim($text);
  290. switch($filter) {
  291. case self::FILTER_BOOLEAN:
  292. $text = (bool) $text;
  293. break;
  294. case self::FILTER_DATETIME:
  295. $timezone = new DateTimeZone(core::$timezone);
  296. $date = new DateTime($text);
  297. $date->setTimezone($timezone);
  298. $text = (int) $date->format('U');
  299. break;
  300. case self::FILTER_FLOAT:
  301. $text = filter_var($text, FILTER_SANITIZE_NUMBER_FLOAT);
  302. $text = (float) $text;
  303. break;
  304. case self::FILTER_ID:
  305. $text = mb_strtolower($text, 'UTF-8');
  306. $text = strip_tags(str_replace(
  307. explode(',', 'á,à,â,ä,ã,å,ç,é,è,ê,ë,í,ì,î,ï,ñ,ó,ò,ô,ö,õ,ú,ù,û,ü,ý,ÿ,\',", '),
  308. explode(',', 'a,a,a,a,a,a,c,e,e,e,e,i,i,i,i,n,o,o,o,o,o,u,u,u,u,y,y,-,-,-'),
  309. $text
  310. ));
  311. $text = preg_replace('/([^a-z0-9-])/', '', $text);
  312. // Supprime les emoji
  313. $text = preg_replace('/[[:^print:]]/', '', $text);
  314. // Supprime les tirets en fin de chaine (emoji en fin de nom)
  315. $text = rtrim($text,'-');
  316. // Cas où un identifiant est vide
  317. if (empty($text)) {
  318. $text = uniqid('');
  319. }
  320. // Un ID ne peut pas être un entier, pour éviter les conflits avec le système de pagination
  321. if(intval($text) !== 0) {
  322. $text = '_' . $text;
  323. }
  324. break;
  325. case self::FILTER_INT:
  326. $text = (int) filter_var($text, FILTER_SANITIZE_NUMBER_INT);
  327. break;
  328. case self::FILTER_MAIL:
  329. $text = filter_var($text, FILTER_SANITIZE_EMAIL);
  330. break;
  331. case self::FILTER_PASSWORD:
  332. $text = password_hash($text, PASSWORD_BCRYPT);
  333. break;
  334. case self::FILTER_STRING_LONG:
  335. $text = mb_substr(filter_var($text, FILTER_SANITIZE_STRING), 0, 500000);
  336. break;
  337. case self::FILTER_STRING_SHORT:
  338. $text = mb_substr(filter_var($text, FILTER_SANITIZE_STRING), 0, 500);
  339. break;
  340. case self::FILTER_TIMESTAMP:
  341. $text = date('Y-m-d H:i:s', $text);
  342. break;
  343. case self::FILTER_URL:
  344. $text = filter_var($text, FILTER_SANITIZE_URL);
  345. break;
  346. }
  347. return $text;
  348. }
  349. /**
  350. * Incrémente une clé en fonction des clés ou des valeurs d'un tableau
  351. * @param mixed $key Clé à incrémenter
  352. * @param array $array Tableau à vérifier
  353. * @return string
  354. */
  355. public static function increment($key, $array = []) {
  356. // Pas besoin d'incrémenter si la clef n'existe pas
  357. if($array === []) {
  358. return $key;
  359. }
  360. // Incrémente la clef
  361. else {
  362. // Si la clef est numérique elle est incrémentée
  363. if(is_numeric($key)) {
  364. $newKey = $key;
  365. while(array_key_exists($newKey, $array) OR in_array($newKey, $array)) {
  366. $newKey++;
  367. }
  368. }
  369. // Sinon l'incrémentation est ajoutée après la clef
  370. else {
  371. $i = 2;
  372. $newKey = $key;
  373. while(array_key_exists($newKey, $array) OR in_array($newKey, $array)) {
  374. $newKey = $key . '-' . $i;
  375. $i++;
  376. }
  377. }
  378. return $newKey;
  379. }
  380. }
  381. /**
  382. * Minimise du css
  383. * @param string $css Css à minimiser
  384. * @return string
  385. */
  386. public static function minifyCss($css) {
  387. // Supprime les commentaires
  388. $css = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $css);
  389. // Supprime les tabulations, espaces, nouvelles lignes, etc...
  390. $css = str_replace(["\r\n", "\r", "\n" ,"\t", ' ', ' ', ' '], '', $css);
  391. $css = preg_replace(['(( )+{)', '({( )+)'], '{', $css);
  392. $css = preg_replace(['(( )+})', '(}( )+)', '(;( )*})'], '}', $css);
  393. $css = preg_replace(['(;( )+)', '(( )+;)'], ';', $css);
  394. // Retourne le css minifié
  395. return $css;
  396. }
  397. /**
  398. * Minimise du js
  399. * @param string $js Js à minimiser
  400. * @return string
  401. */
  402. public static function minifyJs($js) {
  403. // Supprime les commentaires
  404. $js = preg_replace('/\\/\\*[^*]*\\*+([^\\/][^*]*\\*+)*\\/|\s*(?<![\:\=])\/\/.*/', '', $js);
  405. // Supprime les tabulations, espaces, nouvelles lignes, etc...
  406. $js = str_replace(["\r\n", "\r", "\t", "\n", ' ', ' ', ' '], '', $js);
  407. $js = preg_replace(['(( )+\))', '(\)( )+)'], ')', $js);
  408. // Retourne le js minifié
  409. return $js;
  410. }
  411. /**
  412. * Crée un système de pagination (retourne un tableau contenant les informations sur la pagination (first, last, pages))
  413. * @param array $array Tableau de donnée à utiliser
  414. * @param string $url URL à utiliser, la dernière partie doit correspondre au numéro de page, par défaut utiliser $this->getUrl()
  415. * @param string $item pagination nombre d'éléments par page
  416. * @param null|int $sufix Suffixe de l'url
  417. * @return array
  418. */
  419. public static function pagination($array, $url, $item, $sufix = null) {
  420. // Scinde l'url
  421. $url = explode('/', $url);
  422. // Url de pagination
  423. $urlPagination = is_numeric($url[count($url) - 1]) ? array_pop($url) : 1;
  424. // Url de la page courante
  425. $urlCurrent = implode('/', $url);
  426. // Nombre d'éléments à afficher
  427. $nbElements = count($array);
  428. // Nombre de page
  429. $nbPage = ceil($nbElements / $item);
  430. // Page courante
  431. $currentPage = is_numeric($urlPagination) ? self::filter($urlPagination, self::FILTER_INT) : 1;
  432. // Premier élément de la page
  433. $firstElement = ($currentPage - 1) * $item;
  434. // Dernier élément de la page
  435. $lastElement = $firstElement + $item;
  436. $lastElement = ($lastElement > $nbElements) ? $nbElements : $lastElement;
  437. // Mise en forme de la liste des pages
  438. $pages = '';
  439. if($nbPage > 1) {
  440. for($i = 1; $i <= $nbPage; $i++) {
  441. $disabled = ($i === $currentPage) ? ' class="disabled"' : false;
  442. $pages .= '<a href="' . helper::baseUrl() . $urlCurrent . '/' . $i . $sufix . '"' . $disabled . '>' . $i . '</a>';
  443. }
  444. $pages = '<div class="pagination">' . $pages . '</div>';
  445. }
  446. // Retourne un tableau contenant les informations sur la pagination
  447. return [
  448. 'first' => $firstElement,
  449. 'last' => $lastElement,
  450. 'pages' => $pages
  451. ];
  452. }
  453. /**
  454. * Calcul de la luminance relative d'une couleur
  455. */
  456. public static function relativeLuminanceW3C($rgba) {
  457. // Conversion en sRGB
  458. $RsRGB = $rgba[0] / 255;
  459. $GsRGB = $rgba[1] / 255;
  460. $BsRGB = $rgba[2] / 255;
  461. // Ajout de la transparence
  462. $RsRGBA = $rgba[3] * $RsRGB + (1 - $rgba[3]);
  463. $GsRGBA = $rgba[3] * $GsRGB + (1 - $rgba[3]);
  464. $BsRGBA = $rgba[3] * $BsRGB + (1 - $rgba[3]);
  465. // Calcul de la luminance
  466. $R = ($RsRGBA <= .03928) ? $RsRGBA / 12.92 : pow(($RsRGBA + .055) / 1.055, 2.4);
  467. $G = ($GsRGBA <= .03928) ? $GsRGBA / 12.92 : pow(($GsRGBA + .055) / 1.055, 2.4);
  468. $B = ($BsRGBA <= .03928) ? $BsRGBA / 12.92 : pow(($BsRGBA + .055) / 1.055, 2.4);
  469. return .2126 * $R + .7152 * $G + .0722 * $B;
  470. }
  471. /**
  472. * Retourne les attributs d'une balise au bon format
  473. * @param array $array Liste des attributs ($key => $value)
  474. * @param array $exclude Clés à ignorer ($key)
  475. * @return string
  476. */
  477. public static function sprintAttributes(array $array = [], array $exclude = []) {
  478. $exclude = array_merge(
  479. [
  480. 'before',
  481. 'classWrapper',
  482. 'help',
  483. 'label'
  484. ],
  485. $exclude
  486. );
  487. $attributes = [];
  488. foreach($array as $key => $value) {
  489. if(($value OR $value === 0) AND in_array($key, $exclude) === false) {
  490. // Désactive le message de modifications non enregistrées pour le champ
  491. if($key === 'noDirty') {
  492. $attributes[] = 'data-no-dirty';
  493. }
  494. // Disabled
  495. // Readonly
  496. elseif(in_array($key, ['disabled', 'readonly'])) {
  497. $attributes[] = sprintf('%s', $key);
  498. }
  499. // Autres
  500. else {
  501. $attributes[] = sprintf('%s="%s"', $key, $value);
  502. }
  503. }
  504. }
  505. return implode(' ', $attributes);
  506. }
  507. /**
  508. * Retourne un segment de chaîne sans couper de mot
  509. * @param string $text Texte à scinder
  510. * @param int $start (voir substr de PHP pour fonctionnement)
  511. * @param int $length (voir substr de PHP pour fonctionnement)
  512. * @return string
  513. */
  514. public static function subword($text, $start, $length) {
  515. $text = trim($text);
  516. if(strlen($text) > $length) {
  517. $text = mb_substr($text, $start, $length);
  518. $text = mb_substr($text, 0, min(mb_strlen($text), mb_strrpos($text, ' ')));
  519. }
  520. return $text;
  521. }
  522. /**
  523. * Cryptage
  524. * @param string $key la clé d'encryptage
  525. * @param string $payload la chaine à coder
  526. * @return string
  527. */
  528. public static function encrypt($key, $payload) {
  529. $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
  530. $encrypted = openssl_encrypt($payload, 'aes-256-cbc', $key, 0, $iv);
  531. return base64_encode($encrypted . '::' . $iv);
  532. }
  533. /**
  534. * Décryptage
  535. * @param string $key la clé d'encryptage
  536. * @param string $garble la chaine à décoder
  537. * @return string
  538. */
  539. public static function decrypt($key, $garble) {
  540. list($encrypted_data, $iv) = explode('::', base64_decode($garble), 2);
  541. return openssl_decrypt($encrypted_data, 'aes-256-cbc', $key, 0, $iv);
  542. }
  543. }