1: | <?php |
2: | |
3: | /** |
4: | * Validates the HTML attribute ID. |
5: | * @warning Even though this is the id processor, it |
6: | * will ignore the directive Attr:IDBlacklist, since it will only |
7: | * go according to the ID accumulator. Since the accumulator is |
8: | * automatically generated, it will have already absorbed the |
9: | * blacklist. If you're hacking around, make sure you use load()! |
10: | */ |
11: | |
12: | class HTMLPurifier_AttrDef_HTML_ID extends HTMLPurifier_AttrDef |
13: | { |
14: | |
15: | // selector is NOT a valid thing to use for IDREFs, because IDREFs |
16: | // *must* target IDs that exist, whereas selector #ids do not. |
17: | |
18: | /** |
19: | * Determines whether or not we're validating an ID in a CSS |
20: | * selector context. |
21: | * @type bool |
22: | */ |
23: | protected $selector; |
24: | |
25: | /** |
26: | * @param bool $selector |
27: | */ |
28: | public function __construct($selector = false) |
29: | { |
30: | $this->selector = $selector; |
31: | } |
32: | |
33: | /** |
34: | * @param string $id |
35: | * @param HTMLPurifier_Config $config |
36: | * @param HTMLPurifier_Context $context |
37: | * @return bool|string |
38: | */ |
39: | public function validate($id, $config, $context) |
40: | { |
41: | if (!$this->selector && !$config->get('Attr.EnableID')) { |
42: | return false; |
43: | } |
44: | |
45: | $id = trim($id); // trim it first |
46: | |
47: | if ($id === '') { |
48: | return false; |
49: | } |
50: | |
51: | $prefix = $config->get('Attr.IDPrefix'); |
52: | if ($prefix !== '') { |
53: | $prefix .= $config->get('Attr.IDPrefixLocal'); |
54: | // prevent re-appending the prefix |
55: | if (strpos($id, $prefix) !== 0) { |
56: | $id = $prefix . $id; |
57: | } |
58: | } elseif ($config->get('Attr.IDPrefixLocal') !== '') { |
59: | trigger_error( |
60: | '%Attr.IDPrefixLocal cannot be used unless ' . |
61: | '%Attr.IDPrefix is set', |
62: | E_USER_WARNING |
63: | ); |
64: | } |
65: | |
66: | if (!$this->selector) { |
67: | $id_accumulator =& $context->get('IDAccumulator'); |
68: | if (isset($id_accumulator->ids[$id])) { |
69: | return false; |
70: | } |
71: | } |
72: | |
73: | // we purposely avoid using regex, hopefully this is faster |
74: | |
75: | if ($config->get('Attr.ID.HTML5') === true) { |
76: | if (preg_match('/[\t\n\x0b\x0c ]/', $id)) { |
77: | return false; |
78: | } |
79: | } else { |
80: | if (ctype_alpha($id)) { |
81: | // OK |
82: | } else { |
83: | if (!ctype_alpha(@$id[0])) { |
84: | return false; |
85: | } |
86: | // primitive style of regexps, I suppose |
87: | $trim = trim( |
88: | $id, |
89: | 'A..Za..z0..9:-._' |
90: | ); |
91: | if ($trim !== '') { |
92: | return false; |
93: | } |
94: | } |
95: | } |
96: | |
97: | $regexp = $config->get('Attr.IDBlacklistRegexp'); |
98: | if ($regexp && preg_match($regexp, $id)) { |
99: | return false; |
100: | } |
101: | |
102: | if (!$this->selector) { |
103: | $id_accumulator->add($id); |
104: | } |
105: | |
106: | // if no change was made to the ID, return the result |
107: | // else, return the new id if stripping whitespace made it |
108: | // valid, or return false. |
109: | return $id; |
110: | } |
111: | } |
112: | |
113: | // vim: et sw=4 sts=4 |
114: |