Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401]

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