1<?php 2 3 4namespace ComboStrap; 5 6 7use ComboStrap\Web\Url; 8use ComboStrap\Xml\XmlDocument; 9 10class Html 11{ 12 13 14 /** 15 * @param string $name 16 * @throws ExceptionRuntime 17 * Garbage In / Garbage Out design 18 */ 19 public static function validNameGuard(string $name) 20 { 21 /** 22 * If the name is not in lowercase, 23 * the shorthand css selector does not work 24 */ 25 $validName = strtolower($name); 26 if ($validName != $name) { 27 throw new ExceptionRuntime("The name ($name) is not a valid name"); 28 } 29 } 30 31 /** 32 * Transform a text into a valid HTML id 33 * @param $string 34 * @return string 35 * 36 * See also: https://github.com/Flet/github-slugger 37 */ 38 public static function toHtmlId($string): string 39 { 40 /** 41 * sectionId calls cleanID 42 * cleanID delete all things before a ':' 43 * we do then the replace before to not 44 * lost a minus '-' separator 45 */ 46 $string = str_replace(array(':', '.'), '', $string); 47 return sectionID($string, $check); 48 } 49 50 /** 51 * Encode special HTML characters to entity (ie escaping) 52 * 53 * This is used to transform text that may be interpreted as HTML 54 * into a text 55 * * that will not be interpreted as HTML 56 * * that may be added in html attribute 57 * 58 * For instance: 59 * * text that should go in attribute with special HTML characters (such as title) 60 * * text that we don't create (to prevent HTML injection) 61 * 62 * Example: 63 * 64 * <script>...</script> 65 * to 66 * "<script>...</hello>" 67 * 68 * 69 * @param $text 70 * @return string 71 * 72 * Note that if the `meta[charset]` matches the text encoding , it should not be encoded 73 * 74 * True ? Beware that this still allows users to insert unsafe scripting vectors, such as markdown links like [xss](javascript:alert%281%29). 75 */ 76 public static function encode($text): string 77 { 78 /** 79 * See https://stackoverflow.com/questions/46483/htmlentities-vs-htmlspecialchars/3614344 80 * 81 * Not {@link htmlentities } htmlentities($text, ENT_QUOTES); 82 * Otherwise we get `Error while loading HTMLError: Entity 'hellip' not defined` 83 * when loading HTML with {@link XmlDocument} 84 * 85 * See also {@link self::htmlDecode()} 86 * 87 * Without ENT_QUOTES 88 * <h4 class="heading-combo"> 89 * is encoded as 90 * >h4 class="heading-combo"< 91 * and cannot be added in a attribute because of the quote 92 * This is used for {@link Tooltip} 93 */ 94 return htmlspecialchars($text, ENT_XHTML | ENT_QUOTES | ENT_DISALLOWED); 95 96 } 97 98 public static function decode($int): string 99 { 100 return htmlspecialchars_decode($int, ENT_XHTML | ENT_QUOTES); 101 } 102 103 public static function getDiffBetweenValuesSeparatedByBlank(string $expected, string $actual, string $expectedName = "expected class", string $actualName = "actual class"): string 104 { 105 $leftClasses = preg_split("/\s/", $expected); 106 $rightClasses = preg_split("/\s/", $actual); 107 $error = ""; 108 foreach ($leftClasses as $leftClass) { 109 if (!in_array($leftClass, $rightClasses)) { 110 $error .= "The $expectedName has the value (" . $leftClass . ") that is not present in the $actualName)\n"; 111 } else { 112 // Delete the value 113 $key = array_search($leftClass, $rightClasses); 114 unset($rightClasses[$key]); 115 } 116 } 117 foreach ($rightClasses as $rightClass) { 118 $error .= "The $actualName has the value (" . $rightClass . ") that is not present in the $expectedName)\n"; 119 } 120 return $error; 121 } 122 123 /** 124 * @throws ExceptionBadSyntax - bad url 125 * @throws ExceptionNotEquals - not equals 126 */ 127 public static function getDiffBetweenSrcSet(string $expected, string $actual) 128 { 129 $expectedSrcSets = explode(",", $expected); 130 $actualSrcSets = explode(",", $actual); 131 $countExpected = count($expectedSrcSets); 132 $countActual = count($actualSrcSets); 133 if ($countExpected !== $countActual) { 134 throw new ExceptionNotEquals("The expected srcSet count ($countExpected) is not the same than the actual ($countActual)."); 135 } 136 for ($i = 0; $i < $countExpected; $i++) { 137 $expectedSrcSet = trim($expectedSrcSets[$i]); 138 $expectedSrcSetExploded = explode(" ", $expectedSrcSet, 2); 139 $expectedSrc = $expectedSrcSetExploded[0]; 140 if (count($expectedSrcSetExploded) == 2) { 141 $expectedWidth = $expectedSrcSetExploded[1]; 142 } else { 143 $expectedWidth = null; 144 } 145 $actualSrcSet = trim($actualSrcSets[$i]); 146 $actualSrcSetExploded = explode(" ", $actualSrcSet, 2); 147 $actualSrc = $actualSrcSetExploded[0]; 148 if (count($actualSrcSetExploded) == 2) { 149 $actualWidth = $actualSrcSetExploded[1]; 150 } else { 151 $actualWidth = null; 152 } 153 if ($expectedWidth !== $actualWidth) { 154 throw new ExceptionNotEquals("The expected width ($expectedWidth) of the srcSet ($i) is not the same than the actual ($actualWidth)."); 155 } 156 try { 157 Html::getDiffBetweenUrlStrings($expectedSrc, $actualSrc); 158 } catch (ExceptionBadSyntax $e) { 159 throw new ExceptionBadSyntax("Bad Syntax on Src Set ($i). Error: {$e->getMessage()}. Expected: $expectedSrc vs Actual: $actualSrc. "); 160 } catch (ExceptionNotEquals $e) { 161 throw ExceptionNotEquals::create("Not Equals on Src Set ($i). Error: {$e->getMessage()}.", $expectedSrc, $actualSrc); 162 } 163 } 164 } 165 166 167 /** 168 * @throws ExceptionBadSyntax 169 * @throws ExceptionNotEquals 170 */ 171 public static function getDiffBetweenUrlStrings(string $expected, string $actual) 172 { 173 try { 174 $url = Url::createFromString($expected); 175 } catch (ExceptionBadSyntax $e) { 176 throw new ExceptionBadSyntax("The expected URL string ($expected) is not valid. Error: {$e->getMessage()}"); 177 } 178 try { 179 $urlActual = Url::createFromString($actual); 180 } catch (ExceptionBadSyntax $e) { 181 throw new ExceptionBadSyntax("The $actual URL string ($actual) is not valid. Error: {$e->getMessage()}"); 182 } 183 $url->equals($urlActual); 184 185 } 186 187 /** 188 * Merge class name 189 * @param string $newNames - the name that we want to add 190 * @param ?string $actualNames - the actual names 191 * @return string - the class name list 192 * 193 * for instance: 194 * * newNames = foo blue 195 * * actual Name = foo bar 196 * return 197 * * foo bar blue 198 */ 199 public static function mergeClassNames(string $newNames, ?string $actualNames): string 200 { 201 /** 202 * It may be in the form "value1 value2" 203 */ 204 $newValues = StringUtility::explodeAndTrim($newNames, " "); 205 if (!empty($actualNames)) { 206 $actualValues = StringUtility::explodeAndTrim(trim($actualNames), " "); 207 } else { 208 $actualValues = []; 209 } 210 $newValues = array_merge($actualValues, $newValues); 211 $newValues = array_unique($newValues); 212 return implode(" ", $newValues); 213 } 214 215 /** 216 * @param array $styleProperties - an array of CSS properties with key, value 217 * @return string - the value for the style attribute (ie all rules where joined with the comma) 218 */ 219 public static function array2InlineStyle(array $styleProperties) 220 { 221 $inlineCss = ""; 222 foreach ($styleProperties as $key => $value) { 223 $inlineCss .= "$key:$value;"; 224 } 225 // Suppress the last ; 226 if ($inlineCss[strlen($inlineCss) - 1] == ";") { 227 $inlineCss = substr($inlineCss, 0, -1); 228 } 229 return $inlineCss; 230 } 231} 232