1<?php
2
3/**
4 * Represents a measurable length, with a string numeric magnitude
5 * and a unit. This object is immutable.
6 */
7class HTMLPurifier_Length
8{
9
10    /**
11     * String numeric magnitude.
12     * @type string
13     */
14    protected $n;
15
16    /**
17     * String unit. False is permitted if $n = 0.
18     * @type string|bool
19     */
20    protected $unit;
21
22    /**
23     * Whether or not this length is valid. Null if not calculated yet.
24     * @type bool
25     */
26    protected $isValid;
27
28    /**
29     * Array Lookup array of units recognized by CSS 2.1
30     * @type array
31     */
32    protected static $allowedUnits = array(
33        'em' => true, 'ex' => true, 'px' => true, 'in' => true,
34        'cm' => true, 'mm' => true, 'pt' => true, 'pc' => true
35    );
36
37    /**
38     * @param string $n Magnitude
39     * @param bool|string $u Unit
40     */
41    public function __construct($n = '0', $u = false)
42    {
43        $this->n = (string) $n;
44        $this->unit = $u !== false ? (string) $u : false;
45    }
46
47    /**
48     * @param string $s Unit string, like '2em' or '3.4in'
49     * @return HTMLPurifier_Length
50     * @warning Does not perform validation.
51     */
52    public static function make($s)
53    {
54        if ($s instanceof HTMLPurifier_Length) {
55            return $s;
56        }
57        $n_length = strspn($s, '1234567890.+-');
58        $n = substr($s, 0, $n_length);
59        $unit = substr($s, $n_length);
60        if ($unit === '') {
61            $unit = false;
62        }
63        return new HTMLPurifier_Length($n, $unit);
64    }
65
66    /**
67     * Validates the number and unit.
68     * @return bool
69     */
70    protected function validate()
71    {
72        // Special case:
73        if ($this->n === '+0' || $this->n === '-0') {
74            $this->n = '0';
75        }
76        if ($this->n === '0' && $this->unit === false) {
77            return true;
78        }
79        if (!ctype_lower($this->unit)) {
80            $this->unit = strtolower($this->unit);
81        }
82        if (!isset(HTMLPurifier_Length::$allowedUnits[$this->unit])) {
83            return false;
84        }
85        // Hack:
86        $def = new HTMLPurifier_AttrDef_CSS_Number();
87        $result = $def->validate($this->n, false, false);
88        if ($result === false) {
89            return false;
90        }
91        $this->n = $result;
92        return true;
93    }
94
95    /**
96     * Returns string representation of number.
97     * @return string
98     */
99    public function toString()
100    {
101        if (!$this->isValid()) {
102            return false;
103        }
104        return $this->n . $this->unit;
105    }
106
107    /**
108     * Retrieves string numeric magnitude.
109     * @return string
110     */
111    public function getN()
112    {
113        return $this->n;
114    }
115
116    /**
117     * Retrieves string unit.
118     * @return string
119     */
120    public function getUnit()
121    {
122        return $this->unit;
123    }
124
125    /**
126     * Returns true if this length unit is valid.
127     * @return bool
128     */
129    public function isValid()
130    {
131        if ($this->isValid === null) {
132            $this->isValid = $this->validate();
133        }
134        return $this->isValid;
135    }
136
137    /**
138     * Compares two lengths, and returns 1 if greater, -1 if less and 0 if equal.
139     * @param HTMLPurifier_Length $l
140     * @return int
141     * @warning If both values are too large or small, this calculation will
142     *          not work properly
143     */
144    public function compareTo($l)
145    {
146        if ($l === false) {
147            return false;
148        }
149        if ($l->unit !== $this->unit) {
150            $converter = new HTMLPurifier_UnitConverter();
151            $l = $converter->convert($l, $this->unit);
152            if ($l === false) {
153                return false;
154            }
155        }
156        return $this->n - $l->n;
157    }
158}
159
160// vim: et sw=4 sts=4
161