composer package updates
[openemr.git] / vendor / zendframework / zend-i18n / src / Validator / PhoneNumber.php
blob5fde4d8c156e702c8b92b1e93c2ac6797f7f1151
1 <?php
2 /**
3 * Zend Framework (http://framework.zend.com/)
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 */
10 namespace Zend\I18n\Validator;
12 use Locale;
13 use Traversable;
14 use Zend\Stdlib\ArrayUtils;
15 use Zend\Validator\AbstractValidator;
17 class PhoneNumber extends AbstractValidator
19 const NO_MATCH = 'phoneNumberNoMatch';
20 const UNSUPPORTED = 'phoneNumberUnsupported';
21 const INVALID = 'phoneNumberInvalid';
23 /**
24 * Validation failure message template definitions
26 * @var array
28 protected $messageTemplates = [
29 self::NO_MATCH => 'The input does not match a phone number format',
30 self::UNSUPPORTED => 'The country provided is currently unsupported',
31 self::INVALID => 'Invalid type given. String expected',
34 /**
35 * Phone Number Patterns
37 * @link http://code.google.com/p/libphonenumber/source/browse/trunk/resources/PhoneNumberMetadata.xml
38 * @var array
40 protected static $phone = [];
42 /**
43 * ISO 3611 Country Code
45 * @var string
47 protected $country;
49 /**
50 * Allow Possible Matches
52 * @var bool
54 protected $allowPossible = false;
56 /**
57 * Allowed Types
59 * @var array
61 protected $allowedTypes = [
62 'general',
63 'fixed',
64 'tollfree',
65 'personal',
66 'mobile',
67 'voip',
68 'uan',
71 /**
72 * Constructor for the PhoneNumber validator
74 * Options
75 * - country | string | field or value
76 * - allowed_types | array | array of allowed types
77 * - allow_possible | boolean | allow possible matches aka non-strict
79 * @param array|Traversable $options
81 public function __construct($options = [])
83 if ($options instanceof Traversable) {
84 $options = ArrayUtils::iteratorToArray($options);
87 if (array_key_exists('country', $options)) {
88 $this->setCountry($options['country']);
89 } else {
90 $country = Locale::getRegion(Locale::getDefault());
91 $this->setCountry($country);
94 if (array_key_exists('allowed_types', $options)) {
95 $this->allowedTypes($options['allowed_types']);
98 if (array_key_exists('allow_possible', $options)) {
99 $this->allowPossible($options['allow_possible']);
102 parent::__construct($options);
106 * Allowed Types
108 * @param array|null $types
109 * @return self|array
111 public function allowedTypes(array $types = null)
113 if (null !== $types) {
114 $this->allowedTypes = $types;
116 return $this;
119 return $this->allowedTypes;
123 * Allow Possible
125 * @param bool|null $possible
126 * @return self|bool
128 public function allowPossible($possible = null)
130 if (null !== $possible) {
131 $this->allowPossible = (bool) $possible;
133 return $this;
136 return $this->allowPossible;
140 * Get Country
142 * @return string
144 public function getCountry()
146 return $this->country;
150 * Set Country
152 * @param string $country
153 * @return self
155 public function setCountry($country)
157 $this->country = $country;
159 return $this;
163 * Load Pattern
165 * @param string $code
166 * @return array[]|false
168 protected function loadPattern($code)
170 if (! isset(static::$phone[$code])) {
171 if (! preg_match('/^[A-Z]{2}$/D', $code)) {
172 return false;
175 $file = __DIR__ . '/PhoneNumber/' . $code . '.php';
176 if (! file_exists($file)) {
177 return false;
180 static::$phone[$code] = include $file;
183 return static::$phone[$code];
187 * Returns true if and only if $value matches phone number format
189 * @param string $value
190 * @param array $context
191 * @return bool
193 public function isValid($value = null, $context = null)
195 if (! is_scalar($value)) {
196 $this->error(self::INVALID);
198 return false;
200 $this->setValue($value);
202 $country = $this->getCountry();
204 if (! $countryPattern = $this->loadPattern(strtoupper($country))) {
205 if (isset($context[$country])) {
206 $country = $context[$country];
209 if (! $countryPattern = $this->loadPattern(strtoupper($country))) {
210 $this->error(self::UNSUPPORTED);
212 return false;
216 $codeLength = strlen($countryPattern['code']);
219 * Check for existence of either:
220 * 1) E.123/E.164 international prefix
221 * 2) International double-O prefix
222 * 3) Bare country prefix
224 if (0 === strpos($value, '+' . $countryPattern['code'])) {
225 $valueNoCountry = substr($value, $codeLength + 1);
226 } elseif (0 === strpos($value, '00' . $countryPattern['code'])) {
227 $valueNoCountry = substr($value, $codeLength + 2);
228 } elseif (0 === strpos($value, $countryPattern['code'])) {
229 $valueNoCountry = substr($value, $codeLength);
232 // check against allowed types strict match:
233 foreach ($countryPattern['patterns']['national'] as $type => $pattern) {
234 if (in_array($type, $this->allowedTypes)) {
235 // check pattern:
236 if (preg_match($pattern, $value)) {
237 return true;
238 } elseif (isset($valueNoCountry) && preg_match($pattern, $valueNoCountry)) {
239 // this handles conditions where the country code and prefix are the same
240 return true;
245 // check for possible match:
246 if ($this->allowPossible()) {
247 foreach ($countryPattern['patterns']['possible'] as $type => $pattern) {
248 if (in_array($type, $this->allowedTypes)) {
249 // check pattern:
250 if (preg_match($pattern, $value)) {
251 return true;
252 } elseif (isset($valueNoCountry) && preg_match($pattern, $valueNoCountry)) {
253 // this handles conditions where the country code and prefix are the same
254 return true;
260 $this->error(self::NO_MATCH);
262 return false;