HTMLPurifier_Config   F
last analyzed

Complexity

Total Complexity 129

Size/Duplication

Total Lines 899
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 129
eloc 338
dl 0
loc 899
rs 2
c 0
b 0
f 0

31 Methods

Rating   Name   Duplication   Size   Complexity  
A maybeGetRawURIDefinition() 0 3 1
A maybeGetRawDefinition() 0 3 1
A finalize() 0 4 1
A getCSSDefinition() 0 3 1
A getURIDefinition() 0 3 1
A inherit() 0 3 1
A maybeGetRawCSSDefinition() 0 3 1
A getHTMLDefinition() 0 3 1
A mergeArrayFromForm() 0 4 1
A maybeGetRawHTMLDefinition() 0 3 1
A create() 0 15 5
A serialize() 0 6 1
A getSerial() 0 6 2
A loadIni() 0 7 2
A isFinalized() 0 6 3
A loadArray() 0 14 5
A getBatchSerial() 0 8 2
C getAllowedDirectivesForForm() 0 46 15
A createDefault() 0 5 1
A __construct() 0 6 2
B prepareArrayFromForm() 0 23 10
B get() 0 42 7
A getBatch() 0 15 3
A _listify() 0 7 2
A initDefinition() 0 16 4
F getDefinition() 0 143 27
A loadArrayFromForm() 0 5 1
C set() 0 85 17
A getAll() 0 11 3
A autoFinalize() 0 6 2
A triggerError() 0 18 5

How to fix   Complexity   

Complex Class

Complex classes like HTMLPurifier_Config often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use HTMLPurifier_Config, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * Configuration object that triggers customizable behavior.
5
 *
6
 * @warning This class is strongly defined: that means that the class
7
 *          will fail if an undefined directive is retrieved or set.
8
 *
9
 * @note Many classes that could (although many times don't) use the
10
 *       configuration object make it a mandatory parameter.  This is
11
 *       because a configuration object should always be forwarded,
12
 *       otherwise, you run the risk of missing a parameter and then
13
 *       being stumped when a configuration directive doesn't work.
14
 *
15
 * @todo Reconsider some of the public member variables
16
 */
17
class HTMLPurifier_Config
18
{
19
20
    /**
21
     * HTML Purifier's version
22
     * @type string
23
     */
24
    public $version = '4.13.0';
25
26
    /**
27
     * Whether or not to automatically finalize
28
     * the object if a read operation is done.
29
     * @type bool
30
     */
31
    public $autoFinalize = true;
32
33
    // protected member variables
34
35
    /**
36
     * Namespace indexed array of serials for specific namespaces.
37
     * @see getSerial() for more info.
38
     * @type string[]
39
     */
40
    protected $serials = array();
41
42
    /**
43
     * Serial for entire configuration object.
44
     * @type string
45
     */
46
    protected $serial;
47
48
    /**
49
     * Parser for variables.
50
     * @type HTMLPurifier_VarParser_Flexible
51
     */
52
    protected $parser = null;
53
54
    /**
55
     * Reference HTMLPurifier_ConfigSchema for value checking.
56
     * @type HTMLPurifier_ConfigSchema
57
     * @note This is public for introspective purposes. Please don't
58
     *       abuse!
59
     */
60
    public $def;
61
62
    /**
63
     * Indexed array of definitions.
64
     * @type HTMLPurifier_Definition[]
65
     */
66
    protected $definitions;
67
68
    /**
69
     * Whether or not config is finalized.
70
     * @type bool
71
     */
72
    protected $finalized = false;
73
74
    /**
75
     * Property list containing configuration directives.
76
     * @type array
77
     */
78
    protected $plist;
79
80
    /**
81
     * Whether or not a set is taking place due to an alias lookup.
82
     * @type bool
83
     */
84
    private $aliasMode;
85
86
    /**
87
     * Set to false if you do not want line and file numbers in errors.
88
     * (useful when unit testing).  This will also compress some errors
89
     * and exceptions.
90
     * @type bool
91
     */
92
    public $chatty = true;
93
94
    /**
95
     * Current lock; only gets to this namespace are allowed.
96
     * @type string
97
     */
98
    private $lock;
99
100
    /**
101
     * Constructor
102
     * @param HTMLPurifier_ConfigSchema $definition ConfigSchema that defines
103
     * what directives are allowed.
104
     * @param HTMLPurifier_PropertyList $parent
105
     */
106
    public function __construct($definition, $parent = null)
107
    {
108
        $parent = $parent ? $parent : $definition->defaultPlist;
109
        $this->plist = new HTMLPurifier_PropertyList($parent);
0 ignored issues
show
Documentation Bug introduced by mambax7
It seems like new HTMLPurifier_PropertyList($parent) of type HTMLPurifier_PropertyList is incompatible with the declared type array of property $plist.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
Bug introduced by mambax7
It seems like $parent can also be of type array; however, parameter $parent of HTMLPurifier_PropertyList::__construct() does only seem to accept HTMLPurifier_PropertyList, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

109
        $this->plist = new HTMLPurifier_PropertyList(/** @scrutinizer ignore-type */ $parent);
Loading history...
110
        $this->def = $definition; // keep a copy around for checking
111
        $this->parser = new HTMLPurifier_VarParser_Flexible();
112
    }
113
114
    /**
115
     * Convenience constructor that creates a config object based on a mixed var
116
     * @param mixed $config Variable that defines the state of the config
117
     *                      object. Can be: a HTMLPurifier_Config() object,
118
     *                      an array of directives based on loadArray(),
119
     *                      or a string filename of an ini file.
120
     * @param HTMLPurifier_ConfigSchema $schema Schema object
121
     * @return HTMLPurifier_Config Configured object
122
     */
123
    public static function create($config, $schema = null)
124
    {
125
        if ($config instanceof HTMLPurifier_Config) {
126
            // pass-through
127
            return $config;
128
        }
129
        if (!$schema) {
130
            $ret = HTMLPurifier_Config::createDefault();
131
        } else {
132
            $ret = new HTMLPurifier_Config($schema);
133
        }
134
        if (is_string($config)) {
135
            $ret->loadIni($config);
136
        } elseif (is_array($config)) $ret->loadArray($config);
137
        return $ret;
138
    }
139
140
    /**
141
     * Creates a new config object that inherits from a previous one.
142
     * @param HTMLPurifier_Config $config Configuration object to inherit from.
143
     * @return HTMLPurifier_Config object with $config as its parent.
144
     */
145
    public static function inherit(HTMLPurifier_Config $config)
146
    {
147
        return new HTMLPurifier_Config($config->def, $config->plist);
0 ignored issues
show
Bug introduced by beckmi
$config->plist of type array is incompatible with the type HTMLPurifier_PropertyList expected by parameter $parent of HTMLPurifier_Config::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

147
        return new HTMLPurifier_Config($config->def, /** @scrutinizer ignore-type */ $config->plist);
Loading history...
148
    }
149
150
    /**
151
     * Convenience constructor that creates a default configuration object.
152
     * @return HTMLPurifier_Config default object.
153
     */
154
    public static function createDefault()
155
    {
156
        $definition = HTMLPurifier_ConfigSchema::instance();
157
        $config = new HTMLPurifier_Config($definition);
158
        return $config;
159
    }
160
161
    /**
162
     * Retrieves a value from the configuration.
163
     *
164
     * @param string $key String key
165
     * @param mixed $a
166
     *
167
     * @return mixed
168
     */
169
    public function get($key, $a = null)
170
    {
171
        if ($a !== null) {
172
            $this->triggerError(
173
                "Using deprecated API: use \$config->get('$key.$a') instead",
174
                E_USER_WARNING
175
            );
176
            $key = "$key.$a";
177
        }
178
        if (!$this->finalized) {
179
            $this->autoFinalize();
180
        }
181
        if (!isset($this->def->info[$key])) {
182
            // can't add % due to SimpleTest bug
183
            $this->triggerError(
184
                'Cannot retrieve value of undefined directive ' . htmlspecialchars($key),
185
                E_USER_WARNING
186
            );
187
            return;
188
        }
189
        if (isset($this->def->info[$key]->isAlias)) {
190
            $d = $this->def->info[$key];
191
            $this->triggerError(
192
                'Cannot get value from aliased directive, use real name ' . $d->key,
193
                E_USER_ERROR
194
            );
195
            return;
196
        }
197
        if ($this->lock) {
198
            list($ns) = explode('.', $key);
199
            if ($ns !== $this->lock) {
200
                $this->triggerError(
201
                    'Cannot get value of namespace ' . $ns . ' when lock for ' .
202
                    $this->lock .
203
                    ' is active, this probably indicates a Definition setup method ' .
204
                    'is accessing directives that are not within its namespace',
205
                    E_USER_ERROR
206
                );
207
                return;
208
            }
209
        }
210
        return $this->plist->get($key);
211
    }
212
213
    /**
214
     * Retrieves an array of directives to values from a given namespace
215
     *
216
     * @param string $namespace String namespace
217
     *
218
     * @return array
219
     */
220
    public function getBatch($namespace)
221
    {
222
        if (!$this->finalized) {
223
            $this->autoFinalize();
224
        }
225
        $full = $this->getAll();
226
        if (!isset($full[$namespace])) {
227
            $this->triggerError(
228
                'Cannot retrieve undefined namespace ' .
229
                htmlspecialchars($namespace),
230
                E_USER_WARNING
231
            );
232
            return;
233
        }
234
        return $full[$namespace];
235
    }
236
237
    /**
238
     * Returns a SHA-1 signature of a segment of the configuration object
239
     * that uniquely identifies that particular configuration
240
     *
241
     * @param string $namespace Namespace to get serial for
242
     *
243
     * @return string
244
     * @note Revision is handled specially and is removed from the batch
245
     *       before processing!
246
     */
247
    public function getBatchSerial($namespace)
248
    {
249
        if (empty($this->serials[$namespace])) {
250
            $batch = $this->getBatch($namespace);
251
            unset($batch['DefinitionRev']);
252
            $this->serials[$namespace] = sha1(serialize($batch));
253
        }
254
        return $this->serials[$namespace];
255
    }
256
257
    /**
258
     * Returns a SHA-1 signature for the entire configuration object
259
     * that uniquely identifies that particular configuration
260
     *
261
     * @return string
262
     */
263
    public function getSerial()
264
    {
265
        if (empty($this->serial)) {
266
            $this->serial = sha1(serialize($this->getAll()));
267
        }
268
        return $this->serial;
269
    }
270
271
    /**
272
     * Retrieves all directives, organized by namespace
273
     *
274
     * @warning This is a pretty inefficient function, avoid if you can
275
     */
276
    public function getAll()
277
    {
278
        if (!$this->finalized) {
279
            $this->autoFinalize();
280
        }
281
        $ret = array();
282
        foreach ($this->plist->squash() as $name => $value) {
283
            list($ns, $key) = explode('.', $name, 2);
284
            $ret[$ns][$key] = $value;
285
        }
286
        return $ret;
287
    }
288
289
    /**
290
     * Sets a value to configuration.
291
     *
292
     * @param string $key key
293
     * @param mixed $value value
294
     * @param mixed $a
295
     */
296
    public function set($key, $value, $a = null)
297
    {
298
        if (strpos($key, '.') === false) {
299
            $namespace = $key;
300
            $directive = $value;
301
            $value = $a;
302
            $key = "$key.$directive";
303
            $this->triggerError("Using deprecated API: use \$config->set('$key', ...) instead", E_USER_NOTICE);
304
        } else {
305
            list($namespace) = explode('.', $key);
306
        }
307
        if ($this->isFinalized('Cannot set directive after finalization')) {
308
            return;
309
        }
310
        if (!isset($this->def->info[$key])) {
311
            $this->triggerError(
312
                'Cannot set undefined directive ' . htmlspecialchars($key) . ' to value',
313
                E_USER_WARNING
314
            );
315
            return;
316
        }
317
        $def = $this->def->info[$key];
318
319
        if (isset($def->isAlias)) {
320
            if ($this->aliasMode) {
321
                $this->triggerError(
322
                    'Double-aliases not allowed, please fix '.
323
                    'ConfigSchema bug with' . $key,
324
                    E_USER_ERROR
325
                );
326
                return;
327
            }
328
            $this->aliasMode = true;
329
            $this->set($def->key, $value);
330
            $this->aliasMode = false;
331
            $this->triggerError("$key is an alias, preferred directive name is {$def->key}", E_USER_NOTICE);
332
            return;
333
        }
334
335
        // Raw type might be negative when using the fully optimized form
336
        // of stdClass, which indicates allow_null == true
337
        $rtype = is_int($def) ? $def : $def->type;
338
        if ($rtype < 0) {
339
            $type = -$rtype;
340
            $allow_null = true;
341
        } else {
342
            $type = $rtype;
343
            $allow_null = isset($def->allow_null);
344
        }
345
346
        try {
347
            $value = $this->parser->parse($value, $type, $allow_null);
348
        } catch (HTMLPurifier_VarParserException $e) {
349
            $this->triggerError(
350
                'Value for ' . $key . ' is of invalid type, should be ' .
351
                HTMLPurifier_VarParser::getTypeName($type),
352
                E_USER_WARNING
353
            );
354
            return;
355
        }
356
        if (is_string($value) && is_object($def)) {
357
            // resolve value alias if defined
358
            if (isset($def->aliases[$value])) {
359
                $value = $def->aliases[$value];
360
            }
361
            // check to see if the value is allowed
362
            if (isset($def->allowed) && !isset($def->allowed[$value])) {
363
                $this->triggerError(
364
                    'Value not supported, valid values are: ' .
365
                    $this->_listify($def->allowed),
366
                    E_USER_WARNING
367
                );
368
                return;
369
            }
370
        }
371
        $this->plist->set($key, $value);
372
373
        // reset definitions if the directives they depend on changed
374
        // this is a very costly process, so it's discouraged
375
        // with finalization
376
        if ($namespace == 'HTML' || $namespace == 'CSS' || $namespace == 'URI') {
377
            $this->definitions[$namespace] = null;
378
        }
379
380
        $this->serials[$namespace] = false;
381
    }
382
383
    /**
384
     * Convenience function for error reporting
385
     *
386
     * @param array $lookup
387
     *
388
     * @return string
389
     */
390
    private function _listify($lookup)
391
    {
392
        $list = array();
393
        foreach ($lookup as $name => $b) {
394
            $list[] = $name;
395
        }
396
        return implode(', ', $list);
397
    }
398
399
    /**
400
     * Retrieves object reference to the HTML definition.
401
     *
402
     * @param bool $raw Return a copy that has not been setup yet. Must be
403
     *             called before it's been setup, otherwise won't work.
404
     * @param bool $optimized If true, this method may return null, to
405
     *             indicate that a cached version of the modified
406
     *             definition object is available and no further edits
407
     *             are necessary.  Consider using
408
     *             maybeGetRawHTMLDefinition, which is more explicitly
409
     *             named, instead.
410
     *
411
     * @return HTMLPurifier_HTMLDefinition|null
412
     */
413
    public function getHTMLDefinition($raw = false, $optimized = false)
414
    {
415
        return $this->getDefinition('HTML', $raw, $optimized);
416
    }
417
418
    /**
419
     * Retrieves object reference to the CSS definition
420
     *
421
     * @param bool $raw Return a copy that has not been setup yet. Must be
422
     *             called before it's been setup, otherwise won't work.
423
     * @param bool $optimized If true, this method may return null, to
424
     *             indicate that a cached version of the modified
425
     *             definition object is available and no further edits
426
     *             are necessary.  Consider using
427
     *             maybeGetRawCSSDefinition, which is more explicitly
428
     *             named, instead.
429
     *
430
     * @return HTMLPurifier_CSSDefinition|null
431
     */
432
    public function getCSSDefinition($raw = false, $optimized = false)
433
    {
434
        return $this->getDefinition('CSS', $raw, $optimized);
435
    }
436
437
    /**
438
     * Retrieves object reference to the URI definition
439
     *
440
     * @param bool $raw Return a copy that has not been setup yet. Must be
441
     *             called before it's been setup, otherwise won't work.
442
     * @param bool $optimized If true, this method may return null, to
443
     *             indicate that a cached version of the modified
444
     *             definition object is available and no further edits
445
     *             are necessary.  Consider using
446
     *             maybeGetRawURIDefinition, which is more explicitly
447
     *             named, instead.
448
     *
449
     * @return HTMLPurifier_URIDefinition|null
450
     */
451
    public function getURIDefinition($raw = false, $optimized = false)
452
    {
453
        return $this->getDefinition('URI', $raw, $optimized);
454
    }
455
456
    /**
457
     * Retrieves a definition
458
     *
459
     * @param string $type Type of definition: HTML, CSS, etc
460
     * @param bool $raw Whether or not definition should be returned raw
461
     * @param bool $optimized Only has an effect when $raw is true.  Whether
462
     *        or not to return null if the result is already present in
463
     *        the cache.  This is off by default for backwards
464
     *        compatibility reasons, but you need to do things this
465
     *        way in order to ensure that caching is done properly.
466
     *        Check out enduser-customize.html for more details.
467
     *        We probably won't ever change this default, as much as the
468
     *        maybe semantics is the "right thing to do."
469
     *
470
     * @throws HTMLPurifier_Exception
471
     * @return HTMLPurifier_Definition|null
472
     */
473
    public function getDefinition($type, $raw = false, $optimized = false)
474
    {
475
        if ($optimized && !$raw) {
476
            throw new HTMLPurifier_Exception("Cannot set optimized = true when raw = false");
477
        }
478
        if (!$this->finalized) {
479
            $this->autoFinalize();
480
        }
481
        // temporarily suspend locks, so we can handle recursive definition calls
482
        $lock = $this->lock;
483
        $this->lock = null;
484
        $factory = HTMLPurifier_DefinitionCacheFactory::instance();
485
        $cache = $factory->create($type, $this);
486
        $this->lock = $lock;
487
        if (!$raw) {
488
            // full definition
489
            // ---------------
490
            // check if definition is in memory
491
            if (!empty($this->definitions[$type])) {
492
                $def = $this->definitions[$type];
493
                // check if the definition is setup
494
                if ($def->setup) {
495
                    return $def;
496
                } else {
497
                    $def->setup($this);
498
                    if ($def->optimized) {
499
                        $cache->add($def, $this);
500
                    }
501
                    return $def;
502
                }
503
            }
504
            // check if definition is in cache
505
            $def = $cache->get($this);
506
            if ($def) {
0 ignored issues
show
introduced by beckmi
The condition $def is always false.
Loading history...
507
                // definition in cache, save to memory and return it
508
                $this->definitions[$type] = $def;
509
                return $def;
510
            }
511
            // initialize it
512
            $def = $this->initDefinition($type);
513
            // set it up
514
            $this->lock = $type;
515
            $def->setup($this);
516
            $this->lock = null;
517
            // save in cache
518
            $cache->add($def, $this);
519
            // return it
520
            return $def;
521
        } else {
522
            // raw definition
523
            // --------------
524
            // check preconditions
525
            $def = null;
526
            if ($optimized) {
527
                if (is_null($this->get($type . '.DefinitionID'))) {
528
                    // fatally error out if definition ID not set
529
                    throw new HTMLPurifier_Exception(
530
                        "Cannot retrieve raw version without specifying %$type.DefinitionID"
531
                    );
532
                }
533
            }
534
            if (!empty($this->definitions[$type])) {
535
                $def = $this->definitions[$type];
536
                if ($def->setup && !$optimized) {
537
                    $extra = $this->chatty ?
538
                        " (try moving this code block earlier in your initialization)" :
539
                        "";
540
                    throw new HTMLPurifier_Exception(
541
                        "Cannot retrieve raw definition after it has already been setup" .
542
                        $extra
543
                    );
544
                }
545
                if ($def->optimized === null) {
546
                    $extra = $this->chatty ? " (try flushing your cache)" : "";
547
                    throw new HTMLPurifier_Exception(
548
                        "Optimization status of definition is unknown" . $extra
549
                    );
550
                }
551
                if ($def->optimized !== $optimized) {
552
                    $msg = $optimized ? "optimized" : "unoptimized";
553
                    $extra = $this->chatty ?
554
                        " (this backtrace is for the first inconsistent call, which was for a $msg raw definition)"
555
                        : "";
556
                    throw new HTMLPurifier_Exception(
557
                        "Inconsistent use of optimized and unoptimized raw definition retrievals" . $extra
558
                    );
559
                }
560
            }
561
            // check if definition was in memory
562
            if ($def) {
563
                if ($def->setup) {
564
                    // invariant: $optimized === true (checked above)
565
                    return null;
566
                } else {
567
                    return $def;
568
                }
569
            }
570
            // if optimized, check if definition was in cache
571
            // (because we do the memory check first, this formulation
572
            // is prone to cache slamming, but I think
573
            // guaranteeing that either /all/ of the raw
574
            // setup code or /none/ of it is run is more important.)
575
            if ($optimized) {
576
                // This code path only gets run once; once we put
577
                // something in $definitions (which is guaranteed by the
578
                // trailing code), we always short-circuit above.
579
                $def = $cache->get($this);
580
                if ($def) {
0 ignored issues
show
introduced by beckmi
The condition $def is always false.
Loading history...
581
                    // save the full definition for later, but don't
582
                    // return it yet
583
                    $this->definitions[$type] = $def;
584
                    return null;
585
                }
586
            }
587
            // check invariants for creation
588
            if (!$optimized) {
589
                if (!is_null($this->get($type . '.DefinitionID'))) {
590
                    if ($this->chatty) {
591
                        $this->triggerError(
592
                            'Due to a documentation error in previous version of HTML Purifier, your ' .
593
                            'definitions are not being cached.  If this is OK, you can remove the ' .
594
                            '%$type.DefinitionRev and %$type.DefinitionID declaration.  Otherwise, ' .
595
                            'modify your code to use maybeGetRawDefinition, and test if the returned ' .
596
                            'value is null before making any edits (if it is null, that means that a ' .
597
                            'cached version is available, and no raw operations are necessary).  See ' .
598
                            '<a href="http://htmlpurifier.org/docs/enduser-customize.html#optimized">' .
599
                            'Customize</a> for more details',
600
                            E_USER_WARNING
601
                        );
602
                    } else {
603
                        $this->triggerError(
604
                            "Useless DefinitionID declaration",
605
                            E_USER_WARNING
606
                        );
607
                    }
608
                }
609
            }
610
            // initialize it
611
            $def = $this->initDefinition($type);
612
            $def->optimized = $optimized;
613
            return $def;
614
        }
615
        throw new HTMLPurifier_Exception("The impossible happened!");
0 ignored issues
show
Unused Code introduced by beckmi
ThrowNode is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
616
    }
617
618
    /**
619
     * Initialise definition
620
     *
621
     * @param string $type What type of definition to create
622
     *
623
     * @return HTMLPurifier_CSSDefinition|HTMLPurifier_HTMLDefinition|HTMLPurifier_URIDefinition
624
     * @throws HTMLPurifier_Exception
625
     */
626
    private function initDefinition($type)
627
    {
628
        // quick checks failed, let's create the object
629
        if ($type == 'HTML') {
630
            $def = new HTMLPurifier_HTMLDefinition();
631
        } elseif ($type == 'CSS') {
632
            $def = new HTMLPurifier_CSSDefinition();
633
        } elseif ($type == 'URI') {
634
            $def = new HTMLPurifier_URIDefinition();
635
        } else {
636
            throw new HTMLPurifier_Exception(
637
                "Definition of $type type not supported"
638
            );
639
        }
640
        $this->definitions[$type] = $def;
641
        return $def;
642
    }
643
644
    public function maybeGetRawDefinition($name)
645
    {
646
        return $this->getDefinition($name, true, true);
647
    }
648
649
    /**
650
     * @return HTMLPurifier_HTMLDefinition|null
651
     */
652
    public function maybeGetRawHTMLDefinition()
653
    {
654
        return $this->getDefinition('HTML', true, true);
655
    }
656
    
657
    /**
658
     * @return HTMLPurifier_CSSDefinition|null
659
     */
660
    public function maybeGetRawCSSDefinition()
661
    {
662
        return $this->getDefinition('CSS', true, true);
663
    }
664
    
665
    /**
666
     * @return HTMLPurifier_URIDefinition|null
667
     */
668
    public function maybeGetRawURIDefinition()
669
    {
670
        return $this->getDefinition('URI', true, true);
671
    }
672
673
    /**
674
     * Loads configuration values from an array with the following structure:
675
     * Namespace.Directive => Value
676
     *
677
     * @param array $config_array Configuration associative array
678
     */
679
    public function loadArray($config_array)
680
    {
681
        if ($this->isFinalized('Cannot load directives after finalization')) {
682
            return;
683
        }
684
        foreach ($config_array as $key => $value) {
685
            $key = str_replace('_', '.', $key);
686
            if (strpos($key, '.') !== false) {
687
                $this->set($key, $value);
688
            } else {
689
                $namespace = $key;
690
                $namespace_values = $value;
691
                foreach ($namespace_values as $directive => $value2) {
692
                    $this->set($namespace .'.'. $directive, $value2);
693
                }
694
            }
695
        }
696
    }
697
698
    /**
699
     * Returns a list of array(namespace, directive) for all directives
700
     * that are allowed in a web-form context as per an allowed
701
     * namespaces/directives list.
702
     *
703
     * @param array $allowed List of allowed namespaces/directives
704
     * @param HTMLPurifier_ConfigSchema $schema Schema to use, if not global copy
705
     *
706
     * @return array
707
     */
708
    public static function getAllowedDirectivesForForm($allowed, $schema = null)
709
    {
710
        if (!$schema) {
711
            $schema = HTMLPurifier_ConfigSchema::instance();
712
        }
713
        if ($allowed !== true) {
0 ignored issues
show
introduced by beckmi
The condition $allowed !== true is always true.
Loading history...
714
            if (is_string($allowed)) {
0 ignored issues
show
introduced by beckmi
The condition is_string($allowed) is always false.
Loading history...
715
                $allowed = array($allowed);
716
            }
717
            $allowed_ns = array();
718
            $allowed_directives = array();
719
            $blacklisted_directives = array();
720
            foreach ($allowed as $ns_or_directive) {
721
                if (strpos($ns_or_directive, '.') !== false) {
722
                    // directive
723
                    if ($ns_or_directive[0] == '-') {
724
                        $blacklisted_directives[substr($ns_or_directive, 1)] = true;
725
                    } else {
726
                        $allowed_directives[$ns_or_directive] = true;
727
                    }
728
                } else {
729
                    // namespace
730
                    $allowed_ns[$ns_or_directive] = true;
731
                }
732
            }
733
        }
734
        $ret = array();
735
        foreach ($schema->info as $key => $def) {
736
            list($ns, $directive) = explode('.', $key, 2);
737
            if ($allowed !== true) {
738
                if (isset($blacklisted_directives["$ns.$directive"])) {
739
                    continue;
740
                }
741
                if (!isset($allowed_directives["$ns.$directive"]) && !isset($allowed_ns[$ns])) {
742
                    continue;
743
                }
744
            }
745
            if (isset($def->isAlias)) {
746
                continue;
747
            }
748
            if ($directive == 'DefinitionID' || $directive == 'DefinitionRev') {
749
                continue;
750
            }
751
            $ret[] = array($ns, $directive);
752
        }
753
        return $ret;
754
    }
755
756
    /**
757
     * Loads configuration values from $_GET/$_POST that were posted
758
     * via ConfigForm
759
     *
760
     * @param array $array $_GET or $_POST array to import
761
     * @param string|bool $index Index/name that the config variables are in
762
     * @param array|bool $allowed List of allowed namespaces/directives
763
     * @param bool $mq_fix Boolean whether or not to enable magic quotes fix
764
     * @param HTMLPurifier_ConfigSchema $schema Schema to use, if not global copy
765
     *
766
     * @return mixed
767
     */
768
    public static function loadArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null)
769
    {
770
        $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $schema);
771
        $config = HTMLPurifier_Config::create($ret, $schema);
772
        return $config;
773
    }
774
775
    /**
776
     * Merges in configuration values from $_GET/$_POST to object. NOT STATIC.
777
     *
778
     * @param array $array $_GET or $_POST array to import
779
     * @param string|bool $index Index/name that the config variables are in
780
     * @param array|bool $allowed List of allowed namespaces/directives
781
     * @param bool $mq_fix Boolean whether or not to enable magic quotes fix
782
     */
783
    public function mergeArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true)
784
    {
785
         $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $this->def);
786
         $this->loadArray($ret);
787
    }
788
789
    /**
790
     * Prepares an array from a form into something usable for the more
791
     * strict parts of HTMLPurifier_Config
792
     *
793
     * @param array $array $_GET or $_POST array to import
794
     * @param string|bool $index Index/name that the config variables are in
795
     * @param array|bool $allowed List of allowed namespaces/directives
796
     * @param bool $mq_fix Boolean whether or not to enable magic quotes fix
797
     * @param HTMLPurifier_ConfigSchema $schema Schema to use, if not global copy
798
     *
799
     * @return array
800
     */
801
    public static function prepareArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null)
802
    {
803
        if ($index !== false) {
804
            $array = (isset($array[$index]) && is_array($array[$index])) ? $array[$index] : array();
805
        }
806
        $mq = $mq_fix && function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc();
807
808
        $allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed, $schema);
0 ignored issues
show
Bug introduced by beckmi
It seems like $allowed can also be of type boolean; however, parameter $allowed of HTMLPurifier_Config::getAllowedDirectivesForForm() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

808
        $allowed = HTMLPurifier_Config::getAllowedDirectivesForForm(/** @scrutinizer ignore-type */ $allowed, $schema);
Loading history...
809
        $ret = array();
810
        foreach ($allowed as $key) {
811
            list($ns, $directive) = $key;
812
            $skey = "$ns.$directive";
813
            if (!empty($array["Null_$skey"])) {
814
                $ret[$ns][$directive] = null;
815
                continue;
816
            }
817
            if (!isset($array[$skey])) {
818
                continue;
819
            }
820
            $value = $mq ? stripslashes($array[$skey]) : $array[$skey];
821
            $ret[$ns][$directive] = $value;
822
        }
823
        return $ret;
824
    }
825
826
    /**
827
     * Loads configuration values from an ini file
828
     *
829
     * @param string $filename Name of ini file
830
     */
831
    public function loadIni($filename)
832
    {
833
        if ($this->isFinalized('Cannot load directives after finalization')) {
834
            return;
835
        }
836
        $array = parse_ini_file($filename, true);
837
        $this->loadArray($array);
838
    }
839
840
    /**
841
     * Checks whether or not the configuration object is finalized.
842
     *
843
     * @param string|bool $error String error message, or false for no error
844
     *
845
     * @return bool
846
     */
847
    public function isFinalized($error = false)
848
    {
849
        if ($this->finalized && $error) {
850
            $this->triggerError($error, E_USER_ERROR);
0 ignored issues
show
Bug introduced by beckmi
It seems like $error can also be of type true; however, parameter $msg of HTMLPurifier_Config::triggerError() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

850
            $this->triggerError(/** @scrutinizer ignore-type */ $error, E_USER_ERROR);
Loading history...
851
        }
852
        return $this->finalized;
853
    }
854
855
    /**
856
     * Finalizes configuration only if auto finalize is on and not
857
     * already finalized
858
     */
859
    public function autoFinalize()
860
    {
861
        if ($this->autoFinalize) {
862
            $this->finalize();
863
        } else {
864
            $this->plist->squash(true);
865
        }
866
    }
867
868
    /**
869
     * Finalizes a configuration object, prohibiting further change
870
     */
871
    public function finalize()
872
    {
873
        $this->finalized = true;
874
        $this->parser = null;
875
    }
876
877
    /**
878
     * Produces a nicely formatted error message by supplying the
879
     * stack frame information OUTSIDE of HTMLPurifier_Config.
880
     *
881
     * @param string $msg An error message
882
     * @param int $no An error number
883
     */
884
    protected function triggerError($msg, $no)
885
    {
886
        // determine previous stack frame
887
        $extra = '';
888
        if ($this->chatty) {
889
            $trace = debug_backtrace();
890
            // zip(tail(trace), trace) -- but PHP is not Haskell har har
891
            for ($i = 0, $c = count($trace); $i < $c - 1; $i++) {
892
                // XXX this is not correct on some versions of HTML Purifier
893
                if (isset($trace[$i + 1]['class']) && $trace[$i + 1]['class'] === 'HTMLPurifier_Config') {
894
                    continue;
895
                }
896
                $frame = $trace[$i];
897
                $extra = " invoked on line {$frame['line']} in file {$frame['file']}";
898
                break;
899
            }
900
        }
901
        trigger_error($msg . $extra, $no);
902
    }
903
904
    /**
905
     * Returns a serialized form of the configuration object that can
906
     * be reconstituted.
907
     *
908
     * @return string
909
     */
910
    public function serialize()
911
    {
912
        $this->getDefinition('HTML');
913
        $this->getDefinition('CSS');
914
        $this->getDefinition('URI');
915
        return serialize($this);
916
    }
917
918
}
919
920
// vim: et sw=4 sts=4
921