/modules/simpleroute-1.2/helpers/simplerouter.php

https://github.com/robertleeplummerjr/bluebox · PHP · 360 lines · 226 code · 73 blank · 61 comment · 30 complexity · 515e711cb045189214dcfb76179193fa MD5 · raw file

  1. <?php defined('SYSPATH') or die('No direct access allowed.');
  2. /**
  3. * @package Simpleroute
  4. * @author K Anderson <bitbashing@gmail.com>
  5. * @license Mozilla Public License (MPL)
  6. */
  7. class simplerouter
  8. {
  9. public static function getOutboundPattern($simple_route_id, $engine = 'freeswitch')
  10. {
  11. $simpleRoute = Doctrine::getTable('SimpleRoute')->find($simple_route_id);
  12. if (empty($simpleRoute['patterns']))
  13. {
  14. return array();
  15. }
  16. // get the outbound patterns out of the record
  17. $patterns = $simpleRoute['patterns'];
  18. switch($simpleRoute['type'])
  19. {
  20. case SimpleRoute::TYPE_REGEX:
  21. $convert = FALSE;
  22. break;
  23. case SimpleRoute::TYPE_SIMPLE:
  24. default:
  25. $convert = TRUE;
  26. }
  27. $patterns = $simpleRoute['patterns'];
  28. // based on the requesting engine generate the pattern required
  29. switch(strtolower($engine))
  30. {
  31. case 'freeswitch':
  32. // Loop each rule and build a regex
  33. $pattern = Array();
  34. foreach ($patterns as $rule)
  35. {
  36. if (!$convert)
  37. {
  38. array_push($pattern, $rule);
  39. continue;
  40. }
  41. if (preg_match('/^[0-9]*$/', $rule))
  42. {
  43. if (preg_match('/\(/',$rule))
  44. {
  45. array_push($pattern, '^' . $rule . '$');
  46. }
  47. else
  48. {
  49. array_push($pattern, '^(' . $rule . ')$');
  50. }
  51. continue;
  52. }
  53. $rule = self::npaToRegex($rule);
  54. if (empty($rule))
  55. {
  56. continue;
  57. }
  58. // Convert the short hand into regex
  59. if (preg_match('/\(/',$rule))
  60. {
  61. array_push($pattern, '^' . $rule . '$');
  62. }
  63. else
  64. {
  65. array_push($pattern, '^(' . $rule . ')$');
  66. }
  67. }
  68. // we added a pipe on the end of every rule so remove the last one
  69. //$pattern = str_replace(array('{', '}'), array('\{', '\}'), $pattern);
  70. return $pattern;
  71. case 'asterisk':
  72. // Loop each rule and build a regex
  73. $pattern = array();
  74. foreach ($patterns as $rule)
  75. {
  76. if (!$convert)
  77. {
  78. $pattern .= $rule .'|';
  79. continue;
  80. }
  81. if (preg_match('/^[0-9]*$/', $rule))
  82. {
  83. $pattern .= '^' . $rule . '$|';
  84. continue;
  85. }
  86. $rule = self::npaToAsteriskShort($rule);
  87. if (empty($rule))
  88. {
  89. continue;
  90. }
  91. // Convert the short hand into regex
  92. $pattern += $rule;
  93. }
  94. // because of the asterisk restrictions we return an array of
  95. // patterns that make up the regex groups in freeswitch
  96. return $pattern;
  97. default:
  98. return FALSE;
  99. }
  100. }
  101. public static function npaToAsteriskShort($pattern = NULL)
  102. {
  103. if (empty($pattern))
  104. {
  105. return FALSE;
  106. }
  107. $pattern = str_replace(array('A', 'P', ' '), array('X', '[0-8]', ''), $pattern);
  108. // This is nasty but asterisk does not support a conditional match....
  109. // ...TO THE PERMUTATION ENGINE!
  110. $permutations = self::conditionPermutations($pattern);
  111. // Break the permutations into an array of patterns
  112. $patterns = explode('|', $permutations);
  113. // for each pattern we need two parts in asterisk, one that matches the
  114. // dialed number, and one to get the requested parts out of the number
  115. $results = array();
  116. foreach($patterns as $pattern)
  117. {
  118. // for the pattern used by asterisk remove the parentheses
  119. $cleanPattern = str_replace(array('(', ')'), array('', ''), $pattern);
  120. // if the pattern is just numbers then let asterisk handle it as
  121. // an extension
  122. if (!preg_match('/^[0-9]*$/', $cleanPattern))
  123. {
  124. $cleanPattern = '_' .$cleanPattern;
  125. }
  126. // for the actually outbound dial we need a rule about which
  127. // numbers to use from the pattern
  128. $exten = '${EXTEN';
  129. // we dont care about parentheses on the far left or right (IE: full
  130. // number or rest of number)
  131. $pattern = trim($pattern, '()');
  132. // find the first occurance of ( and if there that is our offset
  133. if ($pos = strpos($pattern, '('))
  134. {
  135. $exten .= ':' .$pos;
  136. }
  137. // find the last occurance of ) and if there that is our length
  138. if ($pos = strrpos($pattern, ')'))
  139. {
  140. $exten .= ':' .$pos;
  141. }
  142. // close this up
  143. $exten .= '}';
  144. // this array is key => value where key is the pattern necessary to
  145. // enter the dialplan and the value is a ${EXEN} construct with the
  146. // necessary offset and length
  147. $results[$cleanPattern] = $exten;
  148. }
  149. return $results;
  150. }
  151. // This is a recursive function that will return a list of all possible
  152. // permutaions around the conditional match ? token
  153. public static function conditionPermutations($pattern)
  154. {
  155. if ($pos = strpos($pattern, '?'))
  156. {
  157. $patternWith = substr($pattern, 0, $pos) .substr($pattern, $pos+1);
  158. $patternWithOut = substr($pattern, 0, $pos-1) .substr($pattern, $pos+1);
  159. return self::conditionPermutations($patternWith) .'|' .self::conditionPermutations($patternWithOut);
  160. }
  161. else
  162. {
  163. return $pattern;
  164. }
  165. }
  166. public static function npaToRegex($pattern = NULL)
  167. {
  168. if (empty($pattern))
  169. {
  170. return FALSE;
  171. }
  172. // split the npa into an pattern
  173. $pattern = str_split($pattern);
  174. $iteration = 1;
  175. $prevRule = '';
  176. foreach ($pattern as $key => $rule)
  177. {
  178. // remove spaces
  179. if ($rule == ' ')
  180. {
  181. unset($pattern[$key]);
  182. continue;
  183. }
  184. // if this is not escaped then replace the pattern
  185. if ($prevRule != '\\')
  186. {
  187. // find our pattern stand ends and replace them
  188. switch($rule)
  189. {
  190. case 'X':
  191. case 'A':
  192. $pattern[$key] = '[0-9]';
  193. break;
  194. case 'P':
  195. $pattern[$key] = '[0-8]';
  196. break;
  197. case 'Z':
  198. $pattern[$key] = '[1-9]';
  199. break;
  200. case 'N':
  201. $pattern[$key] = '[2-9]';
  202. break;
  203. case '!':
  204. $pattern[$key] = '.*';
  205. break;
  206. case '.':
  207. $pattern[$key] = '.+';
  208. break;
  209. case '?':
  210. $pattern[$key] = '{0,1}';
  211. break;
  212. }
  213. // update the rule so we process this correctly
  214. $rule = $pattern[$key];
  215. }
  216. else
  217. {
  218. // if the previous rule was an escape remove the marker
  219. unset($pattern[$key - 1]);
  220. }
  221. if ($rule == $prevRule)
  222. {
  223. // if the rule is the same as the last inc iteration of the rule
  224. unset($pattern[$key]);
  225. $iteration++;
  226. }
  227. else if ($iteration > 1)
  228. {
  229. // if we where tracking iterations and are at a new rule
  230. // then add in the iteration count are rest iteration
  231. //
  232. // if the next element is itself an iterator handle it diff
  233. if (in_array($rule, array('.*', '.+')))
  234. {
  235. // since the next rule is an iterator we need to put
  236. // back something for it to iterate against
  237. $iteration--;
  238. // if that action reduced us to one then we dont need
  239. // an iterator after all
  240. if ($iteration > 1)
  241. {
  242. $pattern[$key] = '{' . $iteration . '}'. $prevRule . $rule;
  243. }
  244. else
  245. {
  246. $pattern[$key] = $prevRule . $rule;
  247. }
  248. }
  249. else
  250. {
  251. // if this is a iterated element followed by a non iterator
  252. // print our iterator and the new rule, no need to
  253. // backtrack for a pattern
  254. $pattern[$key] = '{' . $iteration . '}'. $rule;
  255. }
  256. $prevRule = $rule;
  257. $iteration = 1;
  258. }
  259. else
  260. {
  261. // otherwise just track the rules
  262. $prevRule = $rule;
  263. }
  264. }
  265. // if we left the loop with a final iteration then add that
  266. if ($iteration > 1)
  267. {
  268. $pattern[] = '{' . $iteration . '}';
  269. }
  270. // make our pattern from the array parts
  271. $pattern = implode('', $pattern);
  272. // if there is no opening parentheses assume it
  273. // belongs on the front
  274. if (!strstr($pattern, '('))
  275. {
  276. $pattern = '(' . $pattern;
  277. }
  278. // if there is no closing parentheses assume it
  279. // belongs on the end
  280. if (!strstr($pattern, ')'))
  281. {
  282. $pattern .= ')';
  283. }
  284. // return out regex
  285. return $pattern;
  286. }
  287. }