Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/**
3
 * HTML_Common2: port of HTML_Common package to PHP5
4
 *
5
 * PHP version 5
6
 *
7
 * LICENSE:
8
 *
9
 * Copyright (c) 2004-2010, Alexey Borzov <avb@php.net>
10
 *
11
 * All rights reserved.
12
 *
13
 * Redistribution and use in source and binary forms, with or without
14
 * modification, are permitted provided that the following conditions
15
 * are met:
16
 *
17
 *    * Redistributions of source code must retain the above copyright
18
 *      notice, this list of conditions and the following disclaimer.
19
 *    * Redistributions in binary form must reproduce the above copyright
20
 *      notice, this list of conditions and the following disclaimer in the
21
 *      documentation and/or other materials provided with the distribution.
22
 *    * The names of the authors may not be used to endorse or promote products
23
 *      derived from this software without specific prior written permission.
24
 *
25
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
26
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
27
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
29
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
33
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36
 *
37
 * @category   HTML
38
 * @package    HTML_Common2
39
 * @author     Alexey Borzov <avb@php.net>
40
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
41
 * @version    CVS: $Id: Common2.php 304516 2010-10-19 18:43:58Z avb $
42
 * @link       http://pear.php.net/package/HTML_Common2
43
 */
44
 
45
/**
46
 * Base class for HTML classes
47
 *
48
 * Implements methods for working with HTML attributes, parsing and generating
49
 * attribute strings. Port of HTML_Common class for PHP4 originally written by
50
 * Adam Daniel with contributions from numerous other developers.
51
 *
52
 * @category   HTML
53
 * @package    HTML_Common2
54
 * @author     Alexey Borzov <avb@php.net>
55
 * @version    Release: 2.0.0
56
 */
57
abstract class HTML_Common2
58
{
59
   /**
60
    * Associative array of attributes
61
    * @var array
62
    */
63
    protected $attributes = array();
64
 
65
   /**
66
    * List of attribites changes to which will be announced via onAttributeChange()
67
    * method rather than performed by HTML_Common2 class itself
68
    * @var array
69
    * @see onAttributeChange()
70
    */
71
    protected $watchedAttributes = array();
72
 
73
   /**
74
    * Indentation level of the element
75
    * @var int
76
    */
77
    private $_indentLevel = 0;
78
 
79
   /**
80
    * Comment associated with the element
81
    * @var string
82
    */
83
    private $_comment = null;
84
 
85
   /**
86
    * Global options for all elements generated by subclasses of HTML_Common2
87
    *
88
    * Preset options are
89
    * - 'charset': charset parameter used in htmlspecialchars() calls,
90
    *   defaults to 'ISO-8859-1'
91
    * - 'indent': string used to indent HTML elements, defaults to "\11"
92
    * - 'linebreak': string used to indicate linebreak, defaults to "\12"
93
    *
94
    * @var array
95
    */
96
    private static $_options = array(
97
        'charset'   => 'ISO-8859-1',
98
        'indent'    => "\11",
99
        'linebreak' => "\12"
100
    );
101
 
102
   /**
103
    * Sets global option(s)
104
    *
105
    * @param    string|array    Option name or array ('option name' => 'option value')
106
    * @param    mixed           Option value, if first argument is not an array
107
    */
108
    public static function setOption($nameOrOptions, $value = null)
109
    {
110
        if (is_array($nameOrOptions)) {
111
            foreach ($nameOrOptions as $k => $v) {
112
                self::setOption($k, $v);
113
            }
114
        } else {
115
            $linebreaks = array('win' => "\15\12", 'unix' => "\12", 'mac' => "\15");
116
            if ('linebreak' == $nameOrOptions && isset($linebreaks[$value])) {
117
                $value = $linebreaks[$value];
118
            }
119
            self::$_options[$nameOrOptions] = $value;
120
        }
121
    }
122
 
123
   /**
124
    * Returns global option(s)
125
    *
126
    * @param    string  Option name
127
    * @return   mixed   Option value, null if option does not exist,
128
    *                   array of all options if $name is not given
129
    */
130
    public static function getOption($name = null)
131
    {
132
        if (null === $name) {
133
            return self::$_options;
134
        } else {
135
            return isset(self::$_options[$name])? self::$_options[$name]: null;
136
        }
137
    }
138
 
139
   /**
140
    * Parses the HTML attributes given as string
141
    *
142
    * @param    string  HTML attribute string
143
    * @return   array   An associative aray of attributes
144
    */
145
    protected static function parseAttributes($attrString)
146
    {
147
        $attributes = array();
148
        if (preg_match_all(
149
                "/(([A-Za-z_:]|[^\\x00-\\x7F])([A-Za-z0-9_:.-]|[^\\x00-\\x7F])*)" .
150
                "([ \\n\\t\\r]+)?(=([ \\n\\t\\r]+)?(\"[^\"]*\"|'[^']*'|[^ \\n\\t\\r]*))?/",
151
                $attrString,
152
                $regs
153
           )) {
154
            for ($i = 0; $i < count($regs[1]); $i++) {
155
                $name  = trim($regs[1][$i]);
156
                $check = trim($regs[0][$i]);
157
                $value = trim($regs[7][$i]);
158
                if ($name == $check) {
159
                    $attributes[strtolower($name)] = strtolower($name);
160
                } else {
161
                    if (!empty($value) && ($value[0] == '\'' || $value[0] == '"')) {
162
                        $value = substr($value, 1, -1);
163
                    }
164
                    $attributes[strtolower($name)] = $value;
165
                }
166
            }
167
        }
168
        return $attributes;
169
    }
170
 
171
   /**
172
    * Creates a valid attribute array from either a string or an array
173
    *
174
    * @param    mixed   Array of attributes or HTML attribute string
175
    * @return   array   An associative aray of attributes
176
    */
177
    protected static function prepareAttributes($attributes)
178
    {
179
        $prepared = array();
180
        if (is_string($attributes)) {
181
            return self::parseAttributes($attributes);
182
 
183
        } elseif (is_array($attributes)) {
184
            foreach ($attributes as $key => $value) {
185
                if (is_int($key)) {
186
                    $key = strtolower($value);
187
                    $prepared[$key] = $key;
188
                } else {
189
                    $prepared[strtolower($key)] = (string)$value;
190
                }
191
            }
192
        }
193
        return $prepared;
194
    }
195
 
196
   /**
197
    * Removes an attribute from an attribute array
198
    *
199
    * @param    array   Attribute array
200
    * @param    string  Name of attribute to remove
201
    */
202
    protected static function removeAttributeArray(array &$attributes, $name)
203
    {
204
        unset($attributes[strtolower($name)]);
205
    }
206
 
207
   /**
208
    * Creates HTML attribute string from array
209
    *
210
    * @param    array   Attribute array
211
    * @return   string  Attribute string
212
    */
213
    protected static function getAttributesString(array $attributes)
214
    {
215
        $str     = '';
216
        $charset = self::getOption('charset');
217
        foreach ($attributes as $key => $value) {
218
            $str .= ' ' . $key . '="' . htmlspecialchars($value, ENT_QUOTES, $charset) . '"';
219
        }
220
        return $str;
221
    }
222
 
223
   /**
224
    * Class constructor, sets default attributes
225
    *
226
    * @param    mixed   Array of attribute 'name' => 'value' pairs or HTML attribute string
227
    */
228
    public function __construct($attributes = null)
229
    {
230
        $this->mergeAttributes($attributes);
231
    }
232
 
233
   /**
234
    * Sets the value of the attribute
235
    *
236
    * @param    string  Attribute name
237
    * @param    string  Attribute value (will be set to $name if omitted)
238
    * @return   HTML_Common2
239
    */
240
    public function setAttribute($name, $value = null)
241
    {
242
        $name = strtolower($name);
243
        if (is_null($value)) {
244
            $value = $name;
245
        }
246
        if (in_array($name, $this->watchedAttributes)) {
247
            $this->onAttributeChange($name, $value);
248
        } else {
249
            $this->attributes[$name] = (string)$value;
250
        }
251
        return $this;
252
    }
253
 
254
   /**
255
    * Returns the value of an attribute
256
    *
257
    * @param    string  Attribute name
258
    * @return   string  Attribute value, null if attribute does not exist
259
    */
260
    public function getAttribute($name)
261
    {
262
        $name = strtolower($name);
263
        return isset($this->attributes[$name])? $this->attributes[$name]: null;
264
    }
265
 
266
   /**
267
    * Sets the attributes
268
    *
269
    * @param    mixed   Array of attribute 'name' => 'value' pairs or HTML attribute string
270
    * @return   HTML_Common2
271
    */
272
    public function setAttributes($attributes)
273
    {
274
        $attributes = self::prepareAttributes($attributes);
275
        $watched    = array();
276
        foreach ($this->watchedAttributes as $watchedKey) {
277
            if (isset($attributes[$watchedKey])) {
278
                $this->setAttribute($watchedKey, $attributes[$watchedKey]);
279
                unset($attributes[$watchedKey]);
280
            } else {
281
                $this->removeAttribute($watchedKey);
282
            }
283
            if (isset($this->attributes[$watchedKey])) {
284
                $watched[$watchedKey] = $this->attributes[$watchedKey];
285
            }
286
        }
287
        $this->attributes = array_merge($watched, $attributes);
288
        return $this;
289
    }
290
 
291
   /**
292
    * Returns the attribute array or string
293
    *
294
    * @param    bool    Whether to return attributes as string
295
    * @return   mixed   Either an array or string of attributes
296
    */
297
    public function getAttributes($asString = false)
298
    {
299
        if ($asString) {
300
            return self::getAttributesString($this->attributes);
301
        } else {
302
            return $this->attributes;
303
        }
304
    }
305
 
306
   /**
307
    * Merges the existing attributes with the new ones
308
    *
309
    * @param    mixed   Array of attribute 'name' => 'value' pairs or HTML attribute string
310
    * @return   HTML_Common2
311
    */
312
    public function mergeAttributes($attributes)
313
    {
314
        $attributes = self::prepareAttributes($attributes);
315
        foreach ($this->watchedAttributes as $watchedKey) {
316
            if (isset($attributes[$watchedKey])) {
317
                $this->onAttributeChange($watchedKey, $attributes[$watchedKey]);
318
                unset($attributes[$watchedKey]);
319
            }
320
        }
321
        $this->attributes = array_merge($this->attributes, $attributes);
322
        return $this;
323
    }
324
 
325
   /**
326
    * Removes an attribute
327
    *
328
    * @param    string  Name of attribute to remove
329
    * @return   HTML_Common2
330
    */
331
    public function removeAttribute($attribute)
332
    {
333
        if (in_array(strtolower($attribute), $this->watchedAttributes)) {
334
            $this->onAttributeChange(strtolower($attribute), null);
335
        } else {
336
            self::removeAttributeArray($this->attributes, $attribute);
337
        }
338
        return $this;
339
    }
340
 
341
   /**
342
    * Sets the indentation level
343
    *
344
    * @param    int
345
    * @return   HTML_Common2
346
    */
347
    public function setIndentLevel($level)
348
    {
349
        $level = intval($level);
350
        if (0 <= $level) {
351
            $this->_indentLevel = $level;
352
        }
353
        return $this;
354
    }
355
 
356
   /**
357
    * Gets the indentation level
358
    *
359
    * @return   int
360
    */
361
    public function getIndentLevel()
362
    {
363
        return $this->_indentLevel;
364
    }
365
 
366
   /**
367
    * Returns the string to indent the element
368
    *
369
    * @return   string
370
    */
371
    protected function getIndent()
372
    {
373
        return str_repeat(self::getOption('indent'), $this->getIndentLevel());
374
    }
375
 
376
   /**
377
    * Sets the comment for the element
378
    *
379
    * @param    string
380
    * @return   HTML_Common2
381
    */
382
    public function setComment($comment)
383
    {
384
        $this->_comment = $comment;
385
        return $this;
386
    }
387
 
388
   /**
389
    * Returns the comment associated with the element
390
    *
391
    * @return   string
392
    */
393
    public function getComment()
394
    {
395
        return $this->_comment;
396
    }
397
 
398
   /**
399
    * Checks whether the element has given CSS class
400
    *
401
    * @param    string  Class name
402
    * @return   bool
403
    */
404
    public function hasClass($class)
405
    {
406
        $regex = '/(^|\s)' . preg_quote($class, '/') . '(\s|$)/';
407
        return (bool)preg_match($regex, $this->getAttribute('class'));
408
    }
409
 
410
   /**
411
    * Adds the given CSS class(es) to the element
412
    *
413
    * @param    string|array    Class name, multiple class names separated by
414
    *                           whitespace, array of class names
415
    * @return   HTML_Common2
416
    */
417
    public function addClass($class)
418
    {
419
        if (!is_array($class)) {
420
            $class = preg_split('/\s+/', $class, null, PREG_SPLIT_NO_EMPTY);
421
        }
422
        $curClass = preg_split('/\s+/', $this->getAttribute('class'),
423
                               null, PREG_SPLIT_NO_EMPTY);
424
        foreach ($class as $c) {
425
            if (!in_array($c, $curClass)) {
426
                $curClass[] = $c;
427
            }
428
        }
429
        $this->setAttribute('class', implode(' ', $curClass));
430
 
431
        return $this;
432
    }
433
 
434
   /**
435
    * Removes the given CSS class(es) from the element
436
    *
437
    * @param    string|array    Class name, multiple class names separated by
438
    *                           whitespace, array of class names
439
    * @return   HTML_Common2
440
    */
441
    public function removeClass($class)
442
    {
443
        if (!is_array($class)) {
444
            $class = preg_split('/\s+/', $class, null, PREG_SPLIT_NO_EMPTY);
445
        }
446
        $curClass = array_diff(
447
            preg_split('/\s+/', $this->getAttribute('class'),
448
                       null, PREG_SPLIT_NO_EMPTY),
449
            $class
450
        );
451
        if (0 == count($curClass)) {
452
            $this->removeAttribute('class');
453
        } else {
454
            $this->setAttribute('class', implode(' ', $curClass));
455
        }
456
        return $this;
457
    }
458
 
459
   /**
460
    * Returns the HTML representation of the element
461
    *
462
    * This magic method allows using the instances of HTML_Common2 in string
463
    * contexts
464
    *
465
    * @return string
466
    */
467
    abstract public function __toString();
468
 
469
   /**
470
    * Called if trying to change an attribute with name in $watchedAttributes
471
    *
472
    * This method is called for each attribute whose name is in the
473
    * $watchedAttributes array and which is being changed by setAttribute(),
474
    * setAttributes() or mergeAttributes() or removed via removeAttribute().
475
    * Note that the operation for the attribute is not carried on after calling
476
    * this method, it is the responsibility of this method to change or remove
477
    * (or not) the attribute.
478
    *
479
    * @param    string  Attribute name
480
    * @param    string  Attribute value, null if attribute is being removed
481
    */
482
    protected function onAttributeChange($name, $value = null)
483
    {
484
    }
485
}
486
?>