PageRenderTime 49ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/php-css-parser/OutputFormat.php

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