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