Differences Between: [Versions 400 and 401] [Versions 400 and 402] [Versions 400 and 403]
1 <?php 2 3 namespace Sabberworm\CSS; 4 5 use Sabberworm\CSS\Parsing\OutputException; 6 7 /** 8 * Class OutputFormat 9 * 10 * @method OutputFormat setSemicolonAfterLastRule( bool $bSemicolonAfterLastRule ) Set whether semicolons are added after last rule. 11 */ 12 class OutputFormat { 13 /** 14 * Value format 15 */ 16 // " means double-quote, ' means single-quote 17 public $sStringQuotingType = '"'; 18 // Output RGB colors in hash notation if possible 19 public $bRGBHashNotation = true; 20 21 /** 22 * Declaration format 23 */ 24 // Semicolon after the last rule of a declaration block can be omitted. To do that, set this false. 25 public $bSemicolonAfterLastRule = true; 26 27 /** 28 * Spacing 29 * Note that these strings are not sanity-checked: the value should only consist of whitespace 30 * Any newline character will be indented according to the current level. 31 * The triples (After, Before, Between) can be set using a wildcard (e.g. `$oFormat->set('Space*Rules', "\n");`) 32 */ 33 public $sSpaceAfterRuleName = ' '; 34 35 public $sSpaceBeforeRules = ''; 36 public $sSpaceAfterRules = ''; 37 public $sSpaceBetweenRules = ''; 38 39 public $sSpaceBeforeBlocks = ''; 40 public $sSpaceAfterBlocks = ''; 41 public $sSpaceBetweenBlocks = "\n"; 42 43 // Content injected in and around @-rule blocks. 44 public $sBeforeAtRuleBlock = ''; 45 public $sAfterAtRuleBlock = ''; 46 47 // This is what’s printed before and after the comma if a declaration block contains multiple selectors. 48 public $sSpaceBeforeSelectorSeparator = ''; 49 public $sSpaceAfterSelectorSeparator = ' '; 50 // This is what’s printed after the comma of value lists 51 public $sSpaceBeforeListArgumentSeparator = ''; 52 public $sSpaceAfterListArgumentSeparator = ''; 53 54 public $sSpaceBeforeOpeningBrace = ' '; 55 56 // Content injected in and around declaration blocks. 57 public $sBeforeDeclarationBlock = ''; 58 public $sAfterDeclarationBlockSelectors = ''; 59 public $sAfterDeclarationBlock = ''; 60 61 /** 62 * Indentation 63 */ 64 // Indentation character(s) per level. Only applicable if newlines are used in any of the spacing settings. 65 public $sIndentation = "\t"; 66 67 /** 68 * Output exceptions. 69 */ 70 public $bIgnoreExceptions = false; 71 72 73 private $oFormatter = null; 74 private $oNextLevelFormat = null; 75 private $iIndentationLevel = 0; 76 77 public function __construct() { 78 } 79 80 public function get($sName) { 81 $aVarPrefixes = array('a', 's', 'm', 'b', 'f', 'o', 'c', 'i'); 82 foreach($aVarPrefixes as $sPrefix) { 83 $sFieldName = $sPrefix.ucfirst($sName); 84 if(isset($this->$sFieldName)) { 85 return $this->$sFieldName; 86 } 87 } 88 return null; 89 } 90 91 public function set($aNames, $mValue) { 92 $aVarPrefixes = array('a', 's', 'm', 'b', 'f', 'o', 'c', 'i'); 93 if(is_string($aNames) && strpos($aNames, '*') !== false) { 94 $aNames = array(str_replace('*', 'Before', $aNames), str_replace('*', 'Between', $aNames), str_replace('*', 'After', $aNames)); 95 } else if(!is_array($aNames)) { 96 $aNames = array($aNames); 97 } 98 foreach($aVarPrefixes as $sPrefix) { 99 $bDidReplace = false; 100 foreach($aNames as $sName) { 101 $sFieldName = $sPrefix.ucfirst($sName); 102 if(isset($this->$sFieldName)) { 103 $this->$sFieldName = $mValue; 104 $bDidReplace = true; 105 } 106 } 107 if($bDidReplace) { 108 return $this; 109 } 110 } 111 // Break the chain so the user knows this option is invalid 112 return false; 113 } 114 115 public function __call($sMethodName, $aArguments) { 116 if(strpos($sMethodName, 'set') === 0) { 117 return $this->set(substr($sMethodName, 3), $aArguments[0]); 118 } else if(strpos($sMethodName, 'get') === 0) { 119 return $this->get(substr($sMethodName, 3)); 120 } else if(method_exists('\\Sabberworm\\CSS\\OutputFormatter', $sMethodName)) { 121 return call_user_func_array(array($this->getFormatter(), $sMethodName), $aArguments); 122 } else { 123 throw new \Exception('Unknown OutputFormat method called: '.$sMethodName); 124 } 125 } 126 127 public function indentWithTabs($iNumber = 1) { 128 return $this->setIndentation(str_repeat("\t", $iNumber)); 129 } 130 131 public function indentWithSpaces($iNumber = 2) { 132 return $this->setIndentation(str_repeat(" ", $iNumber)); 133 } 134 135 public function nextLevel() { 136 if($this->oNextLevelFormat === null) { 137 $this->oNextLevelFormat = clone $this; 138 $this->oNextLevelFormat->iIndentationLevel++; 139 $this->oNextLevelFormat->oFormatter = null; 140 } 141 return $this->oNextLevelFormat; 142 } 143 144 public function beLenient() { 145 $this->bIgnoreExceptions = true; 146 } 147 148 public function getFormatter() { 149 if($this->oFormatter === null) { 150 $this->oFormatter = new OutputFormatter($this); 151 } 152 return $this->oFormatter; 153 } 154 155 public function level() { 156 return $this->iIndentationLevel; 157 } 158 159 /** 160 * Create format. 161 * 162 * @return OutputFormat Format. 163 */ 164 public static function create() { 165 return new OutputFormat(); 166 } 167 168 /** 169 * Create compact format. 170 * 171 * @return OutputFormat Format. 172 */ 173 public static function createCompact() { 174 $format = self::create(); 175 $format->set('Space*Rules', "")->set('Space*Blocks', "")->setSpaceAfterRuleName('')->setSpaceBeforeOpeningBrace('')->setSpaceAfterSelectorSeparator(''); 176 return $format; 177 } 178 179 /** 180 * Create pretty format. 181 * 182 * @return OutputFormat Format. 183 */ 184 public static function createPretty() { 185 $format = self::create(); 186 $format->set('Space*Rules', "\n")->set('Space*Blocks', "\n")->setSpaceBetweenBlocks("\n\n")->set('SpaceAfterListArgumentSeparator', array('default' => '', ',' => ' ')); 187 return $format; 188 } 189 } 190 191 class OutputFormatter { 192 private $oFormat; 193 194 public function __construct(OutputFormat $oFormat) { 195 $this->oFormat = $oFormat; 196 } 197 198 public function space($sName, $sType = null) { 199 $sSpaceString = $this->oFormat->get("Space$sName"); 200 // If $sSpaceString is an array, we have multple values configured depending on the type of object the space applies to 201 if(is_array($sSpaceString)) { 202 if($sType !== null && isset($sSpaceString[$sType])) { 203 $sSpaceString = $sSpaceString[$sType]; 204 } else { 205 $sSpaceString = reset($sSpaceString); 206 } 207 } 208 return $this->prepareSpace($sSpaceString); 209 } 210 211 public function spaceAfterRuleName() { 212 return $this->space('AfterRuleName'); 213 } 214 215 public function spaceBeforeRules() { 216 return $this->space('BeforeRules'); 217 } 218 219 public function spaceAfterRules() { 220 return $this->space('AfterRules'); 221 } 222 223 public function spaceBetweenRules() { 224 return $this->space('BetweenRules'); 225 } 226 227 public function spaceBeforeBlocks() { 228 return $this->space('BeforeBlocks'); 229 } 230 231 public function spaceAfterBlocks() { 232 return $this->space('AfterBlocks'); 233 } 234 235 public function spaceBetweenBlocks() { 236 return $this->space('BetweenBlocks'); 237 } 238 239 public function spaceBeforeSelectorSeparator() { 240 return $this->space('BeforeSelectorSeparator'); 241 } 242 243 public function spaceAfterSelectorSeparator() { 244 return $this->space('AfterSelectorSeparator'); 245 } 246 247 public function spaceBeforeListArgumentSeparator($sSeparator) { 248 return $this->space('BeforeListArgumentSeparator', $sSeparator); 249 } 250 251 public function spaceAfterListArgumentSeparator($sSeparator) { 252 return $this->space('AfterListArgumentSeparator', $sSeparator); 253 } 254 255 public function spaceBeforeOpeningBrace() { 256 return $this->space('BeforeOpeningBrace'); 257 } 258 259 /** 260 * Runs the given code, either swallowing or passing exceptions, depending on the bIgnoreExceptions setting. 261 */ 262 public function safely($cCode) { 263 if($this->oFormat->get('IgnoreExceptions')) { 264 // If output exceptions are ignored, run the code with exception guards 265 try { 266 return $cCode(); 267 } catch (OutputException $e) { 268 return null; 269 } //Do nothing 270 } else { 271 // Run the code as-is 272 return $cCode(); 273 } 274 } 275 276 /** 277 * Clone of the implode function but calls ->render with the current output format instead of __toString() 278 */ 279 public function implode($sSeparator, $aValues, $bIncreaseLevel = false) { 280 $sResult = ''; 281 $oFormat = $this->oFormat; 282 if($bIncreaseLevel) { 283 $oFormat = $oFormat->nextLevel(); 284 } 285 $bIsFirst = true; 286 foreach($aValues as $mValue) { 287 if($bIsFirst) { 288 $bIsFirst = false; 289 } else { 290 $sResult .= $sSeparator; 291 } 292 if($mValue instanceof \Sabberworm\CSS\Renderable) { 293 $sResult .= $mValue->render($oFormat); 294 } else { 295 $sResult .= $mValue; 296 } 297 } 298 return $sResult; 299 } 300 301 public function removeLastSemicolon($sString) { 302 if($this->oFormat->get('SemicolonAfterLastRule')) { 303 return $sString; 304 } 305 $sString = explode(';', $sString); 306 if(count($sString) < 2) { 307 return $sString[0]; 308 } 309 $sLast = array_pop($sString); 310 $sNextToLast = array_pop($sString); 311 array_push($sString, $sNextToLast.$sLast); 312 return implode(';', $sString); 313 } 314 315 private function prepareSpace($sSpaceString) { 316 return str_replace("\n", "\n".$this->indent(), $sSpaceString); 317 } 318 319 private function indent() { 320 return str_repeat($this->oFormat->sIndentation, $this->oFormat->level()); 321 } 322 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body