/Ip/Internal/Vendor/Zend/I18n/Validator/PhoneNumber.php

https://gitlab.com/x33n/ImpressPages · PHP · 249 lines · 126 code · 38 blank · 85 comment · 22 complexity · 58004af713061497beb04d257431ebe1 MD5 · raw file

  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-2013 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. */
  9. namespace Zend\I18n\Validator;
  10. use Traversable;
  11. use Zend\Stdlib\ArrayUtils;
  12. use Zend\Validator\AbstractValidator;
  13. class PhoneNumber extends AbstractValidator
  14. {
  15. const NO_MATCH = 'phoneNumberNoMatch';
  16. const UNSUPPORTED = 'phoneNumberUnsupported';
  17. const INVALID = 'phoneNumberInvalid';
  18. /**
  19. * Validation failure message template definitions
  20. *
  21. * @var array
  22. */
  23. protected $messageTemplates = array(
  24. self::NO_MATCH => 'The input does not match a phone number format',
  25. self::UNSUPPORTED => 'The country provided is currently unsupported',
  26. self::INVALID => 'Invalid type given. String expected',
  27. );
  28. /**
  29. * Phone Number Patterns
  30. *
  31. * @link http://libphonenumber.googlecode.com/svn/trunk/resources/PhoneNumberMetaData.xml
  32. * @var array
  33. */
  34. protected static $phone = array();
  35. /**
  36. * ISO 3611 Country Code
  37. *
  38. * @var string
  39. */
  40. protected $country;
  41. /**
  42. * Allow Possible Matches
  43. *
  44. * @var bool
  45. */
  46. protected $allowPossible = false;
  47. /**
  48. * Allowed Types
  49. *
  50. * @var array
  51. */
  52. protected $allowedTypes = array(
  53. 'general',
  54. 'fixed',
  55. 'tollfree',
  56. 'personal',
  57. 'mobile',
  58. 'voip',
  59. 'uan',
  60. );
  61. /**
  62. * Constructor for the PhoneNumber validator
  63. *
  64. * Options
  65. * - country | string | field or value
  66. * - allowed_types | array | array of allowed types
  67. * - allow_possible | boolean | allow possible matches aka non-strict
  68. *
  69. * @param array|Traversable $options
  70. */
  71. public function __construct($options = array())
  72. {
  73. if ($options instanceof Traversable) {
  74. $options = ArrayUtils::iteratorToArray($options);
  75. }
  76. if (array_key_exists('country', $options)) {
  77. $this->setCountry($options['country']);
  78. }
  79. if (array_key_exists('allowed_types', $options)) {
  80. $this->allowedTypes($options['allowed_types']);
  81. }
  82. if (array_key_exists('allow_possible', $options)) {
  83. $this->allowPossible($options['allow_possible']);
  84. }
  85. parent::__construct($options);
  86. }
  87. /**
  88. * Allowed Types
  89. *
  90. * @param array|null $types
  91. * @return self|array
  92. */
  93. public function allowedTypes(array $types = null)
  94. {
  95. if (null !== $types) {
  96. $this->allowedTypes = $types;
  97. return $this;
  98. }
  99. return $this->allowedTypes;
  100. }
  101. /**
  102. * Allow Possible
  103. *
  104. * @param bool|null $possible
  105. * @return self|bool
  106. */
  107. public function allowPossible($possible = null)
  108. {
  109. if (null !== $possible) {
  110. $this->allowPossible = (bool) $possible;
  111. return $this;
  112. }
  113. return $this->allowPossible;
  114. }
  115. /**
  116. * Get Country
  117. *
  118. * @return string
  119. */
  120. public function getCountry()
  121. {
  122. return $this->country;
  123. }
  124. /**
  125. * Set Country
  126. *
  127. * @param string $country
  128. * @return self
  129. */
  130. public function setCountry($country)
  131. {
  132. $this->country = $country;
  133. return $this;
  134. }
  135. /**
  136. * Load Pattern
  137. *
  138. * @param string $code
  139. * @return array[]|false
  140. */
  141. protected function loadPattern($code)
  142. {
  143. if (!isset(self::$phone[$code])) {
  144. if (!preg_match('/^[A-Z]{2}$/D', $code)) {
  145. return false;
  146. }
  147. $file = __DIR__ . '/PhoneNumber/' . $code . '.php';
  148. if (!file_exists($file)) {
  149. return false;
  150. }
  151. self::$phone[$code] = include $file;
  152. }
  153. return self::$phone[$code];
  154. }
  155. /**
  156. * Returns true if and only if $value matches phone number format
  157. *
  158. * @param string $value
  159. * @param array $context
  160. * @return bool
  161. */
  162. public function isValid($value = null, $context = null)
  163. {
  164. if (!is_scalar($value)) {
  165. $this->error(self::INVALID);
  166. return false;
  167. }
  168. $this->setValue($value);
  169. $country = $this->getCountry();
  170. if (!$countryPattern = $this->loadPattern($country)) {
  171. if (isset($context[$country])) {
  172. $country = $context[$country];
  173. }
  174. if (!$countryPattern = $this->loadPattern($country)) {
  175. $this->error(self::UNSUPPORTED);
  176. return false;
  177. }
  178. }
  179. if ($countryPattern['code'] == substr($value, 0, strlen($countryPattern['code']))) {
  180. $valueNoCountry = substr($value, strlen($countryPattern['code']));
  181. }
  182. // check against allowed types strict match:
  183. foreach ($countryPattern['patterns']['national'] as $type => $pattern) {
  184. if (in_array($type, $this->allowedTypes)) {
  185. // check pattern:
  186. if (preg_match($pattern, $value)) {
  187. return true;
  188. } elseif (isset($valueNoCountry) && preg_match($pattern, $valueNoCountry)) {
  189. // this handles conditions where the country code and prefix are the same
  190. return true;
  191. }
  192. }
  193. }
  194. // check for possible match:
  195. if ($this->allowPossible()) {
  196. foreach ($countryPattern['patterns']['possible'] as $type => $pattern) {
  197. if (in_array($type, $this->allowedTypes)) {
  198. // check pattern:
  199. if (preg_match($pattern, $value)) {
  200. return true;
  201. } elseif (isset($valueNoCountry) && preg_match($pattern, $valueNoCountry)) {
  202. // this handles conditions where the country code and prefix are the same
  203. return true;
  204. }
  205. }
  206. }
  207. }
  208. $this->error(self::NO_MATCH);
  209. return false;
  210. }
  211. }