PageRenderTime 40ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

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

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