1<?php
2/**
3 * Zend Framework (http://framework.zend.com/)
4 *
5 * @link      http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license   http://framework.zend.com/license/new-bsd New BSD License
8 */
9
10namespace Zend\Filter;
11
12use Traversable;
13
14class PregReplace extends AbstractFilter
15{
16    protected $options = array(
17        'pattern'     => null,
18        'replacement' => '',
19    );
20
21    /**
22     * Constructor
23     * Supported options are
24     *     'pattern'     => matching pattern
25     *     'replacement' => replace with this
26     *
27     * @param  array|Traversable|string|null $options
28     */
29    public function __construct($options = null)
30    {
31        if ($options instanceof Traversable) {
32            $options = iterator_to_array($options);
33        }
34
35        if (!is_array($options) || (!isset($options['pattern']) && !isset($options['replacement']))) {
36            $args = func_get_args();
37            if (isset($args[0])) {
38                $this->setPattern($args[0]);
39            }
40            if (isset($args[1])) {
41                $this->setReplacement($args[1]);
42            }
43        } else {
44            $this->setOptions($options);
45        }
46    }
47
48    /**
49     * Set the regex pattern to search for
50     * @see preg_replace()
51     *
52     * @param  string|array $pattern - same as the first argument of preg_replace
53     * @return self
54     * @throws Exception\InvalidArgumentException
55     */
56    public function setPattern($pattern)
57    {
58        if (!is_array($pattern) && !is_string($pattern)) {
59            throw new Exception\InvalidArgumentException(sprintf(
60                '%s expects pattern to be array or string; received "%s"',
61                __METHOD__,
62                (is_object($pattern) ? get_class($pattern) : gettype($pattern))
63            ));
64        }
65
66        if (is_array($pattern)) {
67            foreach ($pattern as $p) {
68                $this->validatePattern($p);
69            }
70        }
71
72        if (is_string($pattern)) {
73            $this->validatePattern($pattern);
74        }
75
76        $this->options['pattern'] = $pattern;
77        return $this;
78    }
79
80    /**
81     * Get currently set match pattern
82     *
83     * @return string|array
84     */
85    public function getPattern()
86    {
87        return $this->options['pattern'];
88    }
89
90    /**
91     * Set the replacement array/string
92     * @see preg_replace()
93     *
94     * @param  array|string $replacement - same as the second argument of preg_replace
95     * @return self
96     * @throws Exception\InvalidArgumentException
97     */
98    public function setReplacement($replacement)
99    {
100        if (!is_array($replacement) && !is_string($replacement)) {
101            throw new Exception\InvalidArgumentException(sprintf(
102                '%s expects replacement to be array or string; received "%s"',
103                __METHOD__,
104                (is_object($replacement) ? get_class($replacement) : gettype($replacement))
105            ));
106        }
107        $this->options['replacement'] = $replacement;
108        return $this;
109    }
110
111    /**
112     * Get currently set replacement value
113     *
114     * @return string|array
115     */
116    public function getReplacement()
117    {
118        return $this->options['replacement'];
119    }
120
121    /**
122     * Perform regexp replacement as filter
123     *
124     * @param  mixed $value
125     * @return mixed
126     * @throws Exception\RuntimeException
127     */
128    public function filter($value)
129    {
130        if (!is_scalar($value) && !is_array($value)) {
131            return $value;
132        }
133
134        if ($this->options['pattern'] === null) {
135            throw new Exception\RuntimeException(sprintf(
136                'Filter %s does not have a valid pattern set',
137                get_class($this)
138            ));
139        }
140
141        return preg_replace($this->options['pattern'], $this->options['replacement'], $value);
142    }
143
144    /**
145     * Validate a pattern and ensure it does not contain the "e" modifier
146     *
147     * @param  string $pattern
148     * @return bool
149     * @throws Exception\InvalidArgumentException
150     */
151    protected function validatePattern($pattern)
152    {
153        if (!preg_match('/(?<modifier>[imsxeADSUXJu]+)$/', $pattern, $matches)) {
154            return true;
155        }
156
157        if (false !== strstr($matches['modifier'], 'e')) {
158            throw new Exception\InvalidArgumentException(sprintf(
159                'Pattern for a PregReplace filter may not contain the "e" pattern modifier; received "%s"',
160                $pattern
161            ));
162        }
163    }
164}
165