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\Validator;
11
12use Zend\Stdlib\StringUtils;
13use Zend\Stdlib\StringWrapper\StringWrapperInterface as StringWrapper;
14
15class StringLength extends AbstractValidator
16{
17    const INVALID   = 'stringLengthInvalid';
18    const TOO_SHORT = 'stringLengthTooShort';
19    const TOO_LONG  = 'stringLengthTooLong';
20
21    /**
22     * @var array
23     */
24    protected $messageTemplates = array(
25        self::INVALID   => "Invalid type given. String expected",
26        self::TOO_SHORT => "The input is less than %min% characters long",
27        self::TOO_LONG  => "The input is more than %max% characters long",
28    );
29
30    /**
31     * @var array
32     */
33    protected $messageVariables = array(
34        'min' => array('options' => 'min'),
35        'max' => array('options' => 'max'),
36    );
37
38    protected $options = array(
39        'min'      => 0,       // Minimum length
40        'max'      => null,    // Maximum length, null if there is no length limitation
41        'encoding' => 'UTF-8', // Encoding to use
42    );
43
44    protected $stringWrapper;
45
46    /**
47     * Sets validator options
48     *
49     * @param  int|array|\Traversable $options
50     */
51    public function __construct($options = array())
52    {
53        if (!is_array($options)) {
54            $options     = func_get_args();
55            $temp['min'] = array_shift($options);
56            if (!empty($options)) {
57                $temp['max'] = array_shift($options);
58            }
59
60            if (!empty($options)) {
61                $temp['encoding'] = array_shift($options);
62            }
63
64            $options = $temp;
65        }
66
67        parent::__construct($options);
68    }
69
70    /**
71     * Returns the min option
72     *
73     * @return int
74     */
75    public function getMin()
76    {
77        return $this->options['min'];
78    }
79
80    /**
81     * Sets the min option
82     *
83     * @param  int $min
84     * @throws Exception\InvalidArgumentException
85     * @return StringLength Provides a fluent interface
86     */
87    public function setMin($min)
88    {
89        if (null !== $this->getMax() && $min > $this->getMax()) {
90            throw new Exception\InvalidArgumentException(
91                "The minimum must be less than or equal to the maximum length, but {$min} > {$this->getMax()}"
92            );
93        }
94
95        $this->options['min'] = max(0, (int) $min);
96        return $this;
97    }
98
99    /**
100     * Returns the max option
101     *
102     * @return int|null
103     */
104    public function getMax()
105    {
106        return $this->options['max'];
107    }
108
109    /**
110     * Sets the max option
111     *
112     * @param  int|null $max
113     * @throws Exception\InvalidArgumentException
114     * @return StringLength Provides a fluent interface
115     */
116    public function setMax($max)
117    {
118        if (null === $max) {
119            $this->options['max'] = null;
120        } elseif ($max < $this->getMin()) {
121            throw new Exception\InvalidArgumentException(
122                "The maximum must be greater than or equal to the minimum length, but {$max} < {$this->getMin()}"
123            );
124        } else {
125            $this->options['max'] = (int) $max;
126        }
127
128        return $this;
129    }
130
131    /**
132     * Get the string wrapper to detect the string length
133     *
134     * @return StringWrapper
135     */
136    public function getStringWrapper()
137    {
138        if (!$this->stringWrapper) {
139            $this->stringWrapper = StringUtils::getWrapper($this->getEncoding());
140        }
141        return $this->stringWrapper;
142    }
143
144    /**
145     * Set the string wrapper to detect the string length
146     *
147     * @param StringWrapper $stringWrapper
148     * @return StringLength
149     */
150    public function setStringWrapper(StringWrapper $stringWrapper)
151    {
152        $stringWrapper->setEncoding($this->getEncoding());
153        $this->stringWrapper = $stringWrapper;
154    }
155
156    /**
157     * Returns the actual encoding
158     *
159     * @return string
160     */
161    public function getEncoding()
162    {
163        return $this->options['encoding'];
164    }
165
166    /**
167     * Sets a new encoding to use
168     *
169     * @param string $encoding
170     * @return StringLength
171     * @throws Exception\InvalidArgumentException
172     */
173    public function setEncoding($encoding)
174    {
175        $this->stringWrapper = StringUtils::getWrapper($encoding);
176        $this->options['encoding'] = $encoding;
177        return $this;
178    }
179
180    /**
181     * Returns true if and only if the string length of $value is at least the min option and
182     * no greater than the max option (when the max option is not null).
183     *
184     * @param  string $value
185     * @return bool
186     */
187    public function isValid($value)
188    {
189        if (!is_string($value)) {
190            $this->error(self::INVALID);
191            return false;
192        }
193
194        $this->setValue($value);
195
196        $length = $this->getStringWrapper()->strlen($value);
197        if ($length < $this->getMin()) {
198            $this->error(self::TOO_SHORT);
199        }
200
201        if (null !== $this->getMax() && $this->getMax() < $length) {
202            $this->error(self::TOO_LONG);
203        }
204
205        if (count($this->getMessages())) {
206            return false;
207        }
208
209        return true;
210    }
211}
212