/php/autocomplete.php

https://github.com/jpatokal/openflights · PHP · 199 lines · 160 code · 19 blank · 20 comment · 49 complexity · 13af1e4cb4a67a8af16a0d3a38316334 MD5 · raw file

  1. <?php
  2. include 'helper.php';
  3. include 'db_pdo.php';
  4. // Trim anything after a hyphen, period or left paren
  5. function trim_query($query) {
  6. $chunks = preg_split("/[-.(]/", $query);
  7. return trim($chunks[0]);
  8. }
  9. // If quick, then return only one row, with no UL tags
  10. if($_POST['quick']) {
  11. $limit = 1;
  12. } else {
  13. $limit = 6;
  14. }
  15. // If multi, then search airports and airlines
  16. $multi = $_POST["qs"];
  17. $results = false;
  18. // Autocompletion for airports
  19. // 3 chars: match on IATA or name (major airports only)
  20. // 4 chars: match on ICAO or name (major airports only)
  21. // >4 chars: match on name or city
  22. $airports = array("qs", "src_ap", "dst_ap", "src_ap1", "dst_ap1", "src_ap2", "dst_ap2", "src_ap3", "dst_ap3", "src_ap4", "dst_ap4");
  23. foreach($airports as $ap) {
  24. if($_POST[$ap]) {
  25. $query = trim_query($_POST[$ap]);
  26. // Limit the number of rows returned in multiinput, where space is at a premium
  27. if($limit > 1) {
  28. $idx = substr($ap, -1);
  29. switch($idx) {
  30. case "4":
  31. case "3":
  32. case "2":
  33. case "1":
  34. $limit = 7 - $idx;
  35. }
  36. }
  37. break;
  38. }
  39. }
  40. // Skip this block for two-letter strings in limited multi mode (generic search box covering airlines & airports),
  41. // since they're assumed to be airlines
  42. if($query && ! ($multi && $limit == 1 && strlen($query) < 3)) {
  43. $ext = "";
  44. $sort_order = "iata IS NULL,icao IS NULL,city,name";
  45. if(strlen($query) <= 3) {
  46. $ext = "iata != '' AND iata != :code AND";
  47. } else {
  48. // Exclude private airports from multisearch
  49. if ($multi) {
  50. $ext = "icao != '' AND";
  51. }
  52. }
  53. $sql = "SELECT 2 as sort_col,apid,name,city,country,iata,icao,x,y,timezone,dst FROM airports WHERE $ext (city LIKE :name";
  54. switch(strlen($query)) {
  55. case 3: // IATA
  56. $sql = "SELECT 1 as sort_col,apid,name,city,country,iata,icao,x,y,timezone,dst FROM airports WHERE iata=:code UNION ($sql)) ORDER BY sort_col,$sort_order LIMIT $limit";
  57. break;
  58. case 4: // ICAO
  59. $sql = "SELECT 1 as sort_col,apid,name,city,country,iata,icao,x,y,timezone,dst FROM airports WHERE icao=:code UNION ($sql)) ORDER BY sort_col,$sort_order LIMIT $limit";
  60. break;
  61. default:
  62. if(strlen($query) > 4) {
  63. $sql .= " OR name LIKE :name) ORDER BY $sort_order LIMIT $limit";
  64. } else {
  65. $sql .= ") ORDER BY $sort_order LIMIT $limit";
  66. }
  67. break;
  68. }
  69. if($limit > 1) print ("<ul class='autocomplete'>");
  70. $sth = $dbh->prepare($sql);
  71. // This is intentionally one-sided (foo%s) to avoid excessive substring matches.
  72. $sth->execute(['name' => "$query%", 'code' => $query]);
  73. if($sth->rowCount() > 0) {
  74. $results = true;
  75. while($row = $sth->fetch()) {
  76. if($limit > 1) {
  77. printf ("<li class='autocomplete' origin='%s' id='%s'>%s</li>\n", $ap, format_apdata($row), format_airport($row));
  78. } else {
  79. printf ("%s;%s", format_apdata($row), format_airport($row));
  80. exit; // match found, do not fall thru to airlines
  81. }
  82. }
  83. }
  84. }
  85. if(! $query || $multi) {
  86. // Autocompletion for airlines
  87. // 2 chars: match on IATA or name (major airlines only)
  88. // 3 chars: match on ICAO or name (major airlines only)
  89. // >3 chars: match on name (any airline)
  90. $airlines = array("qs", "airline", "airline1", "airline2", "airline3", "airline4");
  91. foreach($airlines as $al) {
  92. if($_POST[$al]) {
  93. $query = trim_query($_POST[$al]);
  94. // Limit(/expand) the number of rows returned in multiinput, where space is at a premium
  95. if($limit != 1) {
  96. $idx = substr($al, -1);
  97. switch($idx) {
  98. case "4":
  99. case "3":
  100. case "2":
  101. case "1":
  102. $limit = 7 - $idx;
  103. break;
  104. default:
  105. $limit = 3;
  106. }
  107. }
  108. break;
  109. }
  110. }
  111. if($query) {
  112. $mode = $_POST["mode"];
  113. if(! $mode) $mode = "F";
  114. if(strlen($query) <= 3 && $mode == 'F') {
  115. $ext = "iata != '' AND icao != :code AND";
  116. } else {
  117. $ext = ""; // anything goes!
  118. }
  119. if($multi) {
  120. $ext = "iata!='' AND active='Y' AND"; // quick search only for active, IATA-coded airlines
  121. }
  122. $sql = "SELECT 2 as sort_col,alid,name,iata,icao,mode FROM airlines WHERE mode=:mode AND $ext (name LIKE :name OR alias LIKE :name)";
  123. // IATA/ICAO only apply to flights
  124. if($mode == 'F') {
  125. switch(strlen($query)) {
  126. case 2: // IATA
  127. $sql = "SELECT 1 as sort_col,alid,name,iata,icao,mode FROM airlines WHERE iata=:code AND active='Y' UNION ($sql) ORDER BY sort_col, name LIMIT $limit";
  128. break;
  129. case 3: // ICAO
  130. if(! $multi) {
  131. $sql = "SELECT 1 as sort_col,alid,name,iata,icao,mode FROM airlines WHERE icao=:code UNION ($sql) ORDER BY sort_col, name LIMIT $limit";
  132. break;
  133. } // else fallthru
  134. default: // sort non-IATA airlines last
  135. $sql .= " ORDER BY LENGTH(iata) DESC, name LIMIT $limit";
  136. break;
  137. }
  138. } else {
  139. $sql .= " ORDER BY name LIMIT $limit";
  140. }
  141. if($limit > 1 && ! $multi) print ("<ul class='autocomplete'>");
  142. $sth = $dbh->prepare($sql);
  143. $sth->execute(['mode' => $mode, 'code' => $query, 'name' => "$query%"]) or die('Autocomplete failed.');
  144. if($sth->rowCount() > 0) {
  145. $results = true;
  146. while($row = $sth->fetch()) {
  147. if($limit > 1) {
  148. printf ("<li class='autocomplete' id='%s'>%s</li>", $row["alid"], format_airline($row));
  149. } else {
  150. printf ("%s;%s", $row["alid"], format_airline($row));
  151. }
  152. }
  153. }
  154. } else if($_POST['plane']) {
  155. // Autocompletion for plane types
  156. // First match against major types with IATA codes, then pad to max 6 by matching against frequency of use
  157. $query = $_POST['plane'];
  158. $name = "%$query%";
  159. $query = "(name LIKE :name OR iata LIKE :name) ";
  160. $sql = "(SELECT name,plid FROM planes WHERE " . $query . " AND iata IS NOT NULL ORDER BY name LIMIT 6) UNION " .
  161. "(SELECT name,plid FROM planes WHERE " . $query . " AND iata IS NULL ORDER BY frequency DESC LIMIT 6) LIMIT 6";
  162. $sth = $dbh->prepare($sql);
  163. $sth->execute(compact('name'));
  164. print ("<ul class='autocomplete2'>");
  165. while($data = $sth->fetch()) {
  166. $results = true;
  167. $item = stripslashes($data['name']);
  168. $MAX_LEN = 35;
  169. if(strlen($item) > $MAX_LEN) {
  170. $item = substr($item, 0, $MAX_LEN-13) . "..." . substr($item, -10, 10);
  171. }
  172. echo "<li class='autocomplete' id='" . $data['plid'] . "'>" . $item . "</li>";
  173. }
  174. }
  175. }
  176. if(!$results) {
  177. http_response_code(204); // No Data
  178. }
  179. if($limit > 1) printf("</ul>");
  180. ?>