Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 and 403]

   1  <?php
   2  /**
   3   * SCSSPHP
   4   *
   5   * @copyright 2012-2019 Leaf Corcoran
   6   *
   7   * @license http://opensource.org/licenses/MIT MIT
   8   *
   9   * @link http://scssphp.github.io/scssphp
  10   */
  11  
  12  namespace ScssPhp\ScssPhp\Node;
  13  
  14  use ScssPhp\ScssPhp\Compiler;
  15  use ScssPhp\ScssPhp\Node;
  16  use ScssPhp\ScssPhp\Type;
  17  
  18  /**
  19   * Dimension + optional units
  20   *
  21   * {@internal
  22   *     This is a work-in-progress.
  23   *
  24   *     The \ArrayAccess interface is temporary until the migration is complete.
  25   * }}
  26   *
  27   * @author Anthon Pang <anthon.pang@gmail.com>
  28   */
  29  class Number extends Node implements \ArrayAccess
  30  {
  31      /**
  32       * @var integer
  33       */
  34      static public $precision = 10;
  35  
  36      /**
  37       * @see http://www.w3.org/TR/2012/WD-css3-values-20120308/
  38       *
  39       * @var array
  40       */
  41      static protected $unitTable = [
  42          'in' => [
  43              'in' => 1,
  44              'pc' => 6,
  45              'pt' => 72,
  46              'px' => 96,
  47              'cm' => 2.54,
  48              'mm' => 25.4,
  49              'q'  => 101.6,
  50          ],
  51          'turn' => [
  52              'deg'  => 360,
  53              'grad' => 400,
  54              'rad'  => 6.28318530717958647692528676, // 2 * M_PI
  55              'turn' => 1,
  56          ],
  57          's' => [
  58              's'  => 1,
  59              'ms' => 1000,
  60          ],
  61          'Hz' => [
  62              'Hz'  => 1,
  63              'kHz' => 0.001,
  64          ],
  65          'dpi' => [
  66              'dpi'  => 1,
  67              'dpcm' => 2.54,
  68              'dppx' => 96,
  69          ],
  70      ];
  71  
  72      /**
  73       * @var integer|float
  74       */
  75      public $dimension;
  76  
  77      /**
  78       * @var array
  79       */
  80      public $units;
  81  
  82      /**
  83       * Initialize number
  84       *
  85       * @param mixed $dimension
  86       * @param mixed $initialUnit
  87       */
  88      public function __construct($dimension, $initialUnit)
  89      {
  90          $this->type      = Type::T_NUMBER;
  91          $this->dimension = $dimension;
  92          $this->units     = is_array($initialUnit)
  93              ? $initialUnit
  94              : ($initialUnit ? [$initialUnit => 1]
  95                              : []);
  96      }
  97  
  98      /**
  99       * Coerce number to target units
 100       *
 101       * @param array $units
 102       *
 103       * @return \ScssPhp\ScssPhp\Node\Number
 104       */
 105      public function coerce($units)
 106      {
 107          if ($this->unitless()) {
 108              return new Number($this->dimension, $units);
 109          }
 110  
 111          $dimension = $this->dimension;
 112  
 113          foreach (static::$unitTable['in'] as $unit => $conv) {
 114              $from       = isset($this->units[$unit]) ? $this->units[$unit] : 0;
 115              $to         = isset($units[$unit]) ? $units[$unit] : 0;
 116              $factor     = pow($conv, $from - $to);
 117              $dimension /= $factor;
 118          }
 119  
 120          return new Number($dimension, $units);
 121      }
 122  
 123      /**
 124       * Normalize number
 125       *
 126       * @return \ScssPhp\ScssPhp\Node\Number
 127       */
 128      public function normalize()
 129      {
 130          $dimension = $this->dimension;
 131          $units     = [];
 132  
 133          $this->normalizeUnits($dimension, $units, 'in');
 134  
 135          return new Number($dimension, $units);
 136      }
 137  
 138      /**
 139       * {@inheritdoc}
 140       */
 141      public function offsetExists($offset)
 142      {
 143          if ($offset === -3) {
 144              return ! is_null($this->sourceColumn);
 145          }
 146  
 147          if ($offset === -2) {
 148              return ! is_null($this->sourceLine);
 149          }
 150  
 151          if ($offset === -1 ||
 152              $offset === 0 ||
 153              $offset === 1 ||
 154              $offset === 2
 155          ) {
 156              return true;
 157          }
 158  
 159          return false;
 160      }
 161  
 162      /**
 163       * {@inheritdoc}
 164       */
 165      public function offsetGet($offset)
 166      {
 167          switch ($offset) {
 168              case -3:
 169                  return $this->sourceColumn;
 170  
 171              case -2:
 172                  return $this->sourceLine;
 173  
 174              case -1:
 175                  return $this->sourceIndex;
 176  
 177              case 0:
 178                  return $this->type;
 179  
 180              case 1:
 181                  return $this->dimension;
 182  
 183              case 2:
 184                  return $this->units;
 185          }
 186      }
 187  
 188      /**
 189       * {@inheritdoc}
 190       */
 191      public function offsetSet($offset, $value)
 192      {
 193          if ($offset === 1) {
 194              $this->dimension = $value;
 195          } elseif ($offset === 2) {
 196              $this->units = $value;
 197          } elseif ($offset == -1) {
 198              $this->sourceIndex = $value;
 199          } elseif ($offset == -2) {
 200              $this->sourceLine = $value;
 201          } elseif ($offset == -3) {
 202              $this->sourceColumn = $value;
 203          }
 204      }
 205  
 206      /**
 207       * {@inheritdoc}
 208       */
 209      public function offsetUnset($offset)
 210      {
 211          if ($offset === 1) {
 212              $this->dimension = null;
 213          } elseif ($offset === 2) {
 214              $this->units = null;
 215          } elseif ($offset === -1) {
 216              $this->sourceIndex = null;
 217          } elseif ($offset === -2) {
 218              $this->sourceLine = null;
 219          } elseif ($offset === -3) {
 220              $this->sourceColumn = null;
 221          }
 222      }
 223  
 224      /**
 225       * Returns true if the number is unitless
 226       *
 227       * @return boolean
 228       */
 229      public function unitless()
 230      {
 231          return ! array_sum($this->units);
 232      }
 233  
 234      /**
 235       * Returns unit(s) as the product of numerator units divided by the product of denominator units
 236       *
 237       * @return string
 238       */
 239      public function unitStr()
 240      {
 241          $numerators   = [];
 242          $denominators = [];
 243  
 244          foreach ($this->units as $unit => $unitSize) {
 245              if ($unitSize > 0) {
 246                  $numerators = array_pad($numerators, count($numerators) + $unitSize, $unit);
 247                  continue;
 248              }
 249  
 250              if ($unitSize < 0) {
 251                  $denominators = array_pad($denominators, count($denominators) + $unitSize, $unit);
 252                  continue;
 253              }
 254          }
 255  
 256          return implode('*', $numerators) . (count($denominators) ? '/' . implode('*', $denominators) : '');
 257      }
 258  
 259      /**
 260       * Output number
 261       *
 262       * @param \ScssPhp\ScssPhp\Compiler $compiler
 263       *
 264       * @return string
 265       */
 266      public function output(Compiler $compiler = null)
 267      {
 268          $dimension = round($this->dimension, static::$precision);
 269  
 270          $units = array_filter($this->units, function ($unitSize) {
 271              return $unitSize;
 272          });
 273  
 274          if (count($units) > 1 && array_sum($units) === 0) {
 275              $dimension = $this->dimension;
 276              $units     = [];
 277  
 278              $this->normalizeUnits($dimension, $units, 'in');
 279  
 280              $dimension = round($dimension, static::$precision);
 281              $units     = array_filter($units, function ($unitSize) {
 282                  return $unitSize;
 283              });
 284          }
 285  
 286          $unitSize = array_sum($units);
 287  
 288          if ($compiler && ($unitSize > 1 || $unitSize < 0 || count($units) > 1)) {
 289              $compiler->throwError((string) $dimension . $this->unitStr() . " isn't a valid CSS value.");
 290          }
 291  
 292          reset($units);
 293          $unit = key($units);
 294          $dimension = number_format($dimension, static::$precision, '.', '');
 295  
 296          return (static::$precision ? rtrim(rtrim($dimension, '0'), '.') : $dimension) . $unit;
 297      }
 298  
 299      /**
 300       * {@inheritdoc}
 301       */
 302      public function __toString()
 303      {
 304          return $this->output();
 305      }
 306  
 307      /**
 308       * Normalize units
 309       *
 310       * @param integer|float $dimension
 311       * @param array         $units
 312       * @param string        $baseUnit
 313       */
 314      private function normalizeUnits(&$dimension, &$units, $baseUnit = 'in')
 315      {
 316          $dimension = $this->dimension;
 317          $units     = [];
 318  
 319          foreach ($this->units as $unit => $exp) {
 320              if (isset(static::$unitTable[$baseUnit][$unit])) {
 321                  $factor = pow(static::$unitTable[$baseUnit][$unit], $exp);
 322  
 323                  $unit = $baseUnit;
 324                  $dimension /= $factor;
 325              }
 326  
 327              $units[$unit] = $exp + (isset($units[$unit]) ? $units[$unit] : 0);
 328          }
 329      }
 330  }