1: <?php
2: namespace TIC\CoreBundle\Util;
3:
4: /**
5: * Méthodes statiques utilitaires pour convertir différents types de données.
6: */
7: class ConvertHelper
8: {
9:
10: /**
11: * This class should not be instantiated.
12: */
13: private function __construct()
14: {
15: }
16:
17: /**
18: * Retourne une quantité à partir d'une variable de type quelconque (intval, floatval, count...).
19: *
20: * @param mixed $value Valeur dont une quantité est à déterminer
21: * @return int|float|null Valeur numérique (ou null si indéterminé)
22: */
23: public static function counter(mixed $value): int|float|null
24: {
25: if (null === $value) return 0;
26: if (\is_bool($value)) return ($value) ? 1 : 0;
27:
28: # if (\is_int($value)) return $value;
29: # if (\is_float($value)) return $value;
30: if (\is_numeric($value)) return $value + 0; // chaine numérique => int|float
31:
32: # if (\is_array($value)) return count($value);
33: if (\is_countable($value)) return \count($value); // PHP 7.3+
34:
35: if (\is_string($value)) return \mb_strlen(\trim($value));
36:
37: return null;
38: }
39:
40: /**
41: * Retourne l'équivalent booléen (ou null) d'une variable quelconque.
42: * @see https://www.php.net/manual/fr/function.boolval.php
43: *
44: * @param mixed $value Valeur à interpréter comme booléen
45: * @return bool|null Booléen correspondant (ou null si indéterminé)
46: */
47: public static function boolean(mixed $value): ?bool
48: {
49: if (null === $value) return null;
50: if (\is_bool($value)) return ($value) ? true : false;
51: if (\is_numeric($value)) return (\floatval($value)>0) ? true : false;
52: if (\is_array($value)) return (\count($value)>0) ? true : false;
53:
54: if (\is_object($value)) {
55: if (\is_countable($value)) return (\count($value)>0) ? true : false; // PHP 7.3+
56: if (\method_exists($value, '__toString')) $value = (string)$value;
57: }
58:
59: if (\is_string($value)) {
60: $v = \strtoupper(\trim($value));
61:
62: if ('' === $v) return false;
63: if ('OFF' === $v) return false;
64: if ('NULL' === $v) return null;
65:
66: $v = \substr($v, 0, 1);
67: if (\strpos('1TVYO', $v) !== FALSE) return true;
68: if (\strpos('0FXN', $v) !== FALSE) return false;
69: if (\strpos('?- ', $v) !== FALSE) return null;
70: }
71:
72: throw new \UnexpectedValueException("Impossible de déterminer un booléen à partir de la variable donnée !");
73: }
74:
75: /**
76: * Retourne une liste (ou null) à partir d'une variable quelconque.
77: *
78: * @param mixed $value Valeur à transformer en tableau
79: * @param string $stropt Traitement des chaînes : auto (défaut), json, serialize (sinon séparateur pour explode)
80: * @return array|null Tableau correspondant à la valeur en entrée (ou null si indéterminé)
81: */
82: public static function toArray(mixed $value, string $stropt = 'auto'): ?array
83: {
84: if (null === $value) return [];
85:
86: if (\is_string($value)) {
87: if ($value === '') return [];
88: switch ($stropt) {
89: case 'auto' :
90: // json_decode ?
91: try { $value = \json_decode($value, true, 512, JSON_THROW_ON_ERROR); break; }
92: catch (\Exception $e) {}
93: catch (\TypeError $t) {}
94: // unserialize ?
95: try { $value = \unserialize($value); break; }
96: catch (\Exception $e) {}
97: catch (\TypeError $t) {}
98: // explode ?
99: foreach (array("\n", "\t", "|", ",", ";", "-") as $delim) if (\strpos($value, $delim)) {
100: $value = \explode($delim, $value); break;
101: }
102: break;
103: case 'json' :
104: $value = @\json_decode($value, true);
105: break;
106: case 'serialize' :
107: $value = @\unserialize($value);
108: break;
109: default :
110: return \explode($stropt, $value);
111: }
112: }
113:
114: if (\is_array($value)) return $value;
115: if (\is_bool($value)) return ($value) ? [] : null;
116: if (\is_object($value)) {
117: if (\method_exists($value, 'toArray')) return $value->toArray();
118: return \get_object_vars($values);
119: }
120: return array($value);
121: }
122:
123: /**
124: * Retourne une chaine de caractères à partir d'une variable de type quelconque.
125: * @TODO: proposer un 'join' pour certains 'array' (liste indexée à un seul niveau de scalar).
126: *
127: * @param mixed $value Valeur à convertir en caractères
128: * @return string|null Chaine de caractères (ou null si indéterminé)
129: */
130: public static function stringify(mixed $value): ?string
131: {
132: if (null === $value) return 'null';
133: if (\is_bool($value)) return ($value) ? 'true' : 'false';
134:
135: if (\is_string($value)) return $value;
136: if (\is_numeric($value)) return \number_format($value);
137: if (\is_array($value)) return \json_encode($value);
138:
139: if (!\is_object($value)) return null;
140:
141: if (\is_a($value, 'DateTimeInterface')) return $value->format('Y-m-d H:i:s');
142: if (\method_exists($value, '__toString')) return (string)$value;
143: return \json_encode($value);
144: }
145:
146: /**
147: * Retourne un timestamp Unix à partir d'une variable quelconque.
148: *
149: * @param mixed $value Valeur à analyser pour déterminer une date
150: * @return integer|null Nombre de secondes depuis Epoch (ou null si indéterminé)
151: */
152: public static function timestamp(mixed $value): ?int
153: {
154: if (null === $value) return null;
155: if (\is_bool($value)) return null;
156:
157: if (\is_object($value)) {
158: if (\is_a($value, 'DateTimeInterface')) return $value->getTimestamp();
159: elseif (\method_exists($value, '__toString')) $value = (string)$value;
160: else return null;
161: }
162: if (\is_array($value)) return null; // @TODO ?
163: if ($value === '') return null;
164:
165: // prise en compte de formats du type "YYYYMMDD"...
166: if (\preg_match('/^\s*(\d\d\d\d)[-_]?(\d\d)[-_]?(\d\d)([-_ ]?(\d\d)[-:]?(\d\d)[-:]?(\d\d))?([^\d].*)?$/', $value, $m))
167: return \mktime(isset($m[5])?$m[5]:12, isset($m[6])?$m[6]:0, isset($m[7])?$m[7]:0, $m[2], $m[3], $m[1]);
168:
169: if (\is_int($value)) return $value;
170: if (\is_numeric($value)) return \intval($value);
171: return null;
172: }
173:
174: /**
175: * Retourne une quantité avec des unités "pour humain" (avec l'unité la plus proche).
176: *
177: * @param mixed $bytes Valeur en octets (variable convertie par self::counter)
178: * @param mixed $units Liste des unités (si vide, liste par défaut en octets)
179: * @param integer $prec Précision (nombre de décimales)
180: * @param integer $base Calculs en base 2 ou en base 10
181: * @return string Valeur numérique arrondie avec unité en suffixe
182: */
183: public static function hsize(mixed $bytes, mixed $units = null, int $prec = 1, int $base = 2): string
184: {
185: if (\is_string($units)) $units = \explode(",", $units);
186: if (! \is_array($units) || ! \count($units)) $units = [ ' octets', ' Ko', ' Mo', ' Go', ' To' ];
187:
188: # if (! \is_numeric($bytes)) return $bytes;
189: # $bytes = \intval($bytes);
190: $bytes = self::counter($bytes);
191: if ($bytes === null) return $bytes;
192:
193: if ($bytes <= 1) return $bytes . \substr($units[0], 0, -1); // ' octet'
194:
195: $step = ($base == 2) ? 10 : 3;
196: for ($p = 0; $p <= \count($units); $p++) {
197: $v = $bytes / \pow($base, ($p * $step));
198: if ($v > 999) continue;
199: if ($p == 0) $prec = 0;
200: return \sprintf('%0.' . $prec . 'f%s', $v, $units[$p]);
201: }
202: return $bytes;
203: }
204:
205: }
206: