TYPO3 CMS  TYPO3_6-2
DoubleMetaPhoneUtility.php
Go to the documentation of this file.
1 <?php
3 
23 
24  // properties
28  public $original = '';
29 
33  public $primary = '';
34 
38  public $secondary = '';
39 
43  public $length = 0;
44 
48  public $last = 0;
49 
53  public $current = 0;
54 
55  // methods
56  // TYPO3 specific API to this class. BEGIN
60  public function metaphone($string, $sys_language_uid = 0) {
61  $res = $this->DoubleMetaPhone($string);
62  // debug(array($string,$res['primary']));
63  return $res['primary'];
64  }
65 
66  // TYPO3 specific API to this class. END
67  // Public method
71  public function DoubleMetaPhone($string) {
72  $this->primary = '';
73  $this->secondary = '';
74  $this->current = 0;
75  $this->current = 0;
76  $this->length = strlen($string);
77  $this->last = $this->length - 1;
78  $this->original = $string . ' ';
79  $this->original = strtoupper($this->original);
80  // skip this at beginning of word
81  if ($this->StringAt($this->original, 0, 2, array('GN', 'KN', 'PN', 'WR', 'PS'))) {
82  $this->current++;
83  }
84  // Initial 'X' is pronounced 'Z' e.g. 'Xavier'
85  if ($this->original[0] === 'X') {
86  $this->primary .= 'S';
87  // 'Z' maps to 'S'
88  $this->secondary .= 'S';
89  $this->current++;
90  }
91  // main loop
92  while (strlen($this->primary) < 4 || strlen($this->secondary < 4)) {
93  if ($this->current >= $this->length) {
94  break;
95  }
96  switch (substr($this->original, $this->current, 1)) {
97  case 'A':
98 
99  case 'E':
100 
101  case 'I':
102 
103  case 'O':
104 
105  case 'U':
106 
107  case 'Y':
108  if ($this->current == 0) {
109  // all init vowels now map to 'A'
110  $this->primary .= 'A';
111  $this->secondary .= 'A';
112  }
113  $this->current += 1;
114  break;
115  case 'B':
116  // '-mb', e.g. "dumb", already skipped over ...
117  $this->primary .= 'P';
118  $this->secondary .= 'P';
119  if (substr($this->original, $this->current + 1, 1) == 'B') {
120  $this->current += 2;
121  } else {
122  $this->current += 1;
123  }
124  break;
125  case 'Ç':
126  $this->primary .= 'S';
127  $this->secondary .= 'S';
128  $this->current += 1;
129  break;
130  case 'C':
131  // various gremanic
132  if ($this->current > 1 && !$this->IsVowel($this->original, ($this->current - 2)) && $this->StringAt($this->original, $this->current - 1, 3, array('ACH')) && (substr($this->original, $this->current + 2, 1) != 'I' && (substr($this->original, $this->current + 2, 1) != 'E' || $this->StringAt($this->original, $this->current - 2, 6, array('BACHER', 'MACHER'))))) {
133  $this->primary .= 'K';
134  $this->secondary .= 'K';
135  $this->current += 2;
136  break;
137  }
138  // special case 'caesar'
139  if ($this->current == 0 && $this->StringAt($this->original, $this->current, 6, array('CAESAR'))) {
140  $this->primary .= 'S';
141  $this->secondary .= 'S';
142  $this->current += 2;
143  break;
144  }
145  // italian 'chianti'
146  if ($this->StringAt($this->original, $this->current, 4, array('CHIA'))) {
147  $this->primary .= 'K';
148  $this->secondary .= 'K';
149  $this->current += 2;
150  break;
151  }
152  if ($this->StringAt($this->original, $this->current, 2, array('CH'))) {
153  // find 'michael'
154  if ($this->current > 0 && $this->StringAt($this->original, $this->current, 4, array('CHAE'))) {
155  $this->primary .= 'K';
156  $this->secondary .= 'X';
157  $this->current += 2;
158  break;
159  }
160  // greek roots e.g. 'chemistry', 'chorus'
161  if ($this->current == 0 && ($this->StringAt($this->original, $this->current + 1, 5, array('HARAC', 'HARIS')) || $this->StringAt($this->original, $this->current + 1, 3, array('HOR', 'HYM', 'HIA', 'HEM'))) && !$this->StringAt($this->original, 0, 5, array('CHORE'))) {
162  $this->primary .= 'K';
163  $this->secondary .= 'K';
164  $this->current += 2;
165  break;
166  }
167  // germanic, greek, or otherwise 'ch' for 'kh' sound
168  if ($this->StringAt($this->original, 0, 4, array('VAN ', 'VON ')) || $this->StringAt($this->original, 0, 3, array('SCH')) || $this->StringAt($this->original, $this->current - 2, 6, array('ORCHES', 'ARCHIT', 'ORCHID')) || $this->StringAt($this->original, $this->current + 2, 1, array('T', 'S')) || ($this->StringAt($this->original, $this->current - 1, 1, array('A', 'O', 'U', 'E')) || $this->current == 0) && $this->StringAt($this->original, $this->current + 2, 1, array('L', 'R', 'N', 'M', 'B', 'H', 'F', 'V', 'W', ' '))) {
169  $this->primary .= 'K';
170  $this->secondary .= 'K';
171  } else {
172  if ($this->current > 0) {
173  if ($this->StringAt($this->original, 0, 2, array('MC'))) {
174  // e.g. 'McHugh'
175  $this->primary .= 'K';
176  $this->secondary .= 'K';
177  } else {
178  $this->primary .= 'X';
179  $this->secondary .= 'K';
180  }
181  } else {
182  $this->primary .= 'X';
183  $this->secondary .= 'X';
184  }
185  }
186  $this->current += 2;
187  break;
188  }
189  // e.g. 'czerny'
190  if ($this->StringAt($this->original, $this->current, 2, array('CZ')) && !$this->StringAt($this->original, ($this->current - 2), 4, array('WICZ'))) {
191  $this->primary .= 'S';
192  $this->secondary .= 'X';
193  $this->current += 2;
194  break;
195  }
196  // e.g. 'focaccia'
197  if ($this->StringAt($this->original, $this->current + 1, 3, array('CIA'))) {
198  $this->primary .= 'X';
199  $this->secondary .= 'X';
200  $this->current += 3;
201  break;
202  }
203  // double 'C', but not McClellan'
204  if ($this->StringAt($this->original, $this->current, 2, array('CC')) && !($this->current == 1 && $this->original[0] === 'M')) {
205  // 'bellocchio' but not 'bacchus'
206  if ($this->StringAt($this->original, $this->current + 2, 1, array('I', 'E', 'H')) && !$this->StringAt($this->original, ($this->current + 2), 2, array('HU'))) {
207  // 'accident', 'accede', 'succeed'
208  if ($this->current == 1 && substr($this->original, $this->current - 1, 1) == 'A' || $this->StringAt($this->original, $this->current - 1, 5, array('UCCEE', 'UCCES'))) {
209  $this->primary .= 'KS';
210  $this->secondary .= 'KS';
211  } else {
212  $this->primary .= 'X';
213  $this->secondary .= 'X';
214  }
215  $this->current += 3;
216  break;
217  } else {
218  // Pierce's rule
219  $this->primary .= 'K';
220  $this->secondary .= 'K';
221  $this->current += 2;
222  break;
223  }
224  }
225  if ($this->StringAt($this->original, $this->current, 2, array('CK', 'CG', 'CQ'))) {
226  $this->primary .= 'K';
227  $this->secondary .= 'K';
228  $this->current += 2;
229  break;
230  }
231  if ($this->StringAt($this->original, $this->current, 2, array('CI', 'CE', 'CY'))) {
232  // italian vs. english
233  if ($this->StringAt($this->original, $this->current, 3, array('CIO', 'CIE', 'CIA'))) {
234  $this->primary .= 'S';
235  $this->secondary .= 'X';
236  } else {
237  $this->primary .= 'S';
238  $this->secondary .= 'S';
239  }
240  $this->current += 2;
241  break;
242  }
243  // else
244  $this->primary .= 'K';
245  $this->secondary .= 'K';
246  // name sent in 'mac caffrey', 'mac gregor'
247  if ($this->StringAt($this->original, $this->current + 1, 2, array(' C', ' Q', ' G'))) {
248  $this->current += 3;
249  } else {
250  if ($this->StringAt($this->original, $this->current + 1, 1, array('C', 'K', 'Q')) && !$this->StringAt($this->original, ($this->current + 1), 2, array('CE', 'CI'))) {
251  $this->current += 2;
252  } else {
253  $this->current += 1;
254  }
255  }
256  break;
257  case 'D':
258  if ($this->StringAt($this->original, $this->current, 2, array('DG'))) {
259  if ($this->StringAt($this->original, $this->current + 2, 1, array('I', 'E', 'Y'))) {
260  // e.g. 'edge'
261  $this->primary .= 'J';
262  $this->secondary .= 'J';
263  $this->current += 3;
264  break;
265  } else {
266  // e.g. 'edgar'
267  $this->primary .= 'TK';
268  $this->secondary .= 'TK';
269  $this->current += 2;
270  break;
271  }
272  }
273  if ($this->StringAt($this->original, $this->current, 2, array('DT', 'DD'))) {
274  $this->primary .= 'T';
275  $this->secondary .= 'T';
276  $this->current += 2;
277  break;
278  }
279  // else
280  $this->primary .= 'T';
281  $this->secondary .= 'T';
282  $this->current += 1;
283  break;
284  case 'F':
285  if (substr($this->original, $this->current + 1, 1) == 'F') {
286  $this->current += 2;
287  } else {
288  $this->current += 1;
289  }
290  $this->primary .= 'F';
291  $this->secondary .= 'F';
292  break;
293  case 'G':
294  if (substr($this->original, $this->current + 1, 1) == 'H') {
295  if ($this->current > 0 && !$this->IsVowel($this->original, ($this->current - 1))) {
296  $this->primary .= 'K';
297  $this->secondary .= 'K';
298  $this->current += 2;
299  break;
300  }
301  if ($this->current < 3) {
302  // 'ghislane', 'ghiradelli'
303  if ($this->current == 0) {
304  if (substr($this->original, $this->current + 2, 1) == 'I') {
305  $this->primary .= 'J';
306  $this->secondary .= 'J';
307  } else {
308  $this->primary .= 'K';
309  $this->secondary .= 'K';
310  }
311  $this->current += 2;
312  break;
313  }
314  }
315  // Parker's rule (with some further refinements) - e.g. 'hugh'
316  if ($this->current > 1 && $this->StringAt($this->original, $this->current - 2, 1, array('B', 'H', 'D')) || $this->current > 2 && $this->StringAt($this->original, $this->current - 3, 1, array('B', 'H', 'D')) || $this->current > 3 && $this->StringAt($this->original, $this->current - 4, 1, array('B', 'H'))) {
317  $this->current += 2;
318  break;
319  } else {
320  // e.g. 'laugh', 'McLaughlin', 'cough', 'gough', 'rough', 'tough'
321  if ($this->current > 2 && substr($this->original, $this->current - 1, 1) == 'U' && $this->StringAt($this->original, $this->current - 3, 1, array('C', 'G', 'L', 'R', 'T'))) {
322  $this->primary .= 'F';
323  $this->secondary .= 'F';
324  } elseif ($this->current > 0 && substr($this->original, $this->current - 1, 1) != 'I') {
325  $this->primary .= 'K';
326  $this->secondary .= 'K';
327  }
328  $this->current += 2;
329  break;
330  }
331  }
332  if (substr($this->original, $this->current + 1, 1) == 'N') {
333  if ($this->current == 1 && $this->IsVowel($this->original, 0) && !$this->SlavoGermanic($this->original)) {
334  $this->primary .= 'KN';
335  $this->secondary .= 'N';
336  } else {
337  // not e.g. 'cagney'
338  if (!$this->StringAt($this->original, ($this->current + 2), 2, array('EY')) && substr($this->original, $this->current + 1) != 'Y' && !$this->SlavoGermanic($this->original)) {
339  $this->primary .= 'N';
340  $this->secondary .= 'KN';
341  } else {
342  $this->primary .= 'KN';
343  $this->secondary .= 'KN';
344  }
345  }
346  $this->current += 2;
347  break;
348  }
349  // 'tagliaro'
350  if ($this->StringAt($this->original, $this->current + 1, 2, array('LI')) && !$this->SlavoGermanic($this->original)) {
351  $this->primary .= 'KL';
352  $this->secondary .= 'L';
353  $this->current += 2;
354  break;
355  }
356  // -ges-, -gep-, -gel- at beginning
357  if ($this->current == 0 && (substr($this->original, $this->current + 1, 1) == 'Y' || $this->StringAt($this->original, $this->current + 1, 2, array(
358  'ES',
359  'EP',
360  'EB',
361  'EL',
362  'EY',
363  'IB',
364  'IL',
365  'IN',
366  'IE',
367  'EI',
368  'ER'
369  )))) {
370  $this->primary .= 'K';
371  $this->secondary .= 'J';
372  $this->current += 2;
373  break;
374  }
375  // -ger-, -gy-
376  if (($this->StringAt($this->original, $this->current + 1, 2, array('ER')) || substr($this->original, $this->current + 1, 1) == 'Y') && !$this->StringAt($this->original, 0, 6, array('DANGER', 'RANGER', 'MANGER')) && !$this->StringAt($this->original, ($this->current - 1), 1, array('E', 'I')) && !$this->StringAt($this->original, ($this->current - 1), 3, array('RGY', 'OGY'))) {
377  $this->primary .= 'K';
378  $this->secondary .= 'J';
379  $this->current += 2;
380  break;
381  }
382  // italian e.g. 'biaggi'
383  if ($this->StringAt($this->original, $this->current + 1, 1, array('E', 'I', 'Y')) || $this->StringAt($this->original, $this->current - 1, 4, array('AGGI', 'OGGI'))) {
384  // obvious germanic
385  if ($this->StringAt($this->original, 0, 4, array('VAN ', 'VON ')) || $this->StringAt($this->original, 0, 3, array('SCH')) || $this->StringAt($this->original, $this->current + 1, 2, array('ET'))) {
386  $this->primary .= 'K';
387  $this->secondary .= 'K';
388  } else {
389  // always soft if french ending
390  if ($this->StringAt($this->original, $this->current + 1, 4, array('IER '))) {
391  $this->primary .= 'J';
392  $this->secondary .= 'J';
393  } else {
394  $this->primary .= 'J';
395  $this->secondary .= 'K';
396  }
397  }
398  $this->current += 2;
399  break;
400  }
401  if (substr($this->original, $this->current + 1, 1) == 'G') {
402  $this->current += 2;
403  } else {
404  $this->current += 1;
405  }
406  $this->primary .= 'K';
407  $this->secondary .= 'K';
408  break;
409  case 'H':
410  // only keep if first & before vowel or btw. 2 vowels
411  if (($this->current == 0 || $this->IsVowel($this->original, $this->current - 1)) && $this->IsVowel($this->original, $this->current + 1)) {
412  $this->primary .= 'H';
413  $this->secondary .= 'H';
414  $this->current += 2;
415  } else {
416  $this->current += 1;
417  }
418  break;
419  case 'J':
420  // obvious spanish, 'jose', 'san jacinto'
421  if ($this->StringAt($this->original, $this->current, 4, array('JOSE')) || $this->StringAt($this->original, 0, 4, array('SAN '))) {
422  if ($this->current == 0 && substr($this->original, $this->current + 4, 1) == ' ' || $this->StringAt($this->original, 0, 4, array('SAN '))) {
423  $this->primary .= 'H';
424  $this->secondary .= 'H';
425  } else {
426  $this->primary .= 'J';
427  $this->secondary .= 'H';
428  }
429  $this->current += 1;
430  break;
431  }
432  if ($this->current == 0 && !$this->StringAt($this->original, $this->current, 4, array('JOSE'))) {
433  $this->primary .= 'J';
434  // Yankelovich/Jankelowicz
435  $this->secondary .= 'A';
436  } else {
437  // spanish pron. of .e.g. 'bajador'
438  if ($this->IsVowel($this->original, $this->current - 1) && !$this->SlavoGermanic($this->original) && (substr($this->original, $this->current + 1, 1) == 'A' || substr($this->original, $this->current + 1, 1) == 'O')) {
439  $this->primary .= 'J';
440  $this->secondary .= 'H';
441  } else {
442  if ($this->current == $this->last) {
443  $this->primary .= 'J';
444  $this->secondary .= '';
445  } else {
446  if (!$this->StringAt($this->original, ($this->current + 1), 1, array('L', 'T', 'K', 'S', 'N', 'M', 'B', 'Z')) && !$this->StringAt($this->original, ($this->current - 1), 1, array('S', 'K', 'L'))) {
447  $this->primary .= 'J';
448  $this->secondary .= 'J';
449  }
450  }
451  }
452  }
453  if (substr($this->original, $this->current + 1, 1) == 'J') {
454  // it could happen
455  $this->current += 2;
456  } else {
457  $this->current += 1;
458  }
459  break;
460  case 'K':
461  if (substr($this->original, $this->current + 1, 1) == 'K') {
462  $this->current += 2;
463  } else {
464  $this->current += 1;
465  }
466  $this->primary .= 'K';
467  $this->secondary .= 'K';
468  break;
469  case 'L':
470  if (substr($this->original, $this->current + 1, 1) == 'L') {
471  // spanish e.g. 'cabrillo', 'gallegos'
472  if ($this->current == $this->length - 3 && $this->StringAt($this->original, $this->current - 1, 4, array('ILLO', 'ILLA', 'ALLE')) || ($this->StringAt($this->original, $this->last - 1, 2, array('AS', 'OS')) || $this->StringAt($this->original, $this->last, 1, array('A', 'O'))) && $this->StringAt($this->original, $this->current - 1, 4, array('ALLE'))) {
473  $this->primary .= 'L';
474  $this->secondary .= '';
475  $this->current += 2;
476  break;
477  }
478  $this->current += 2;
479  } else {
480  $this->current += 1;
481  }
482  $this->primary .= 'L';
483  $this->secondary .= 'L';
484  break;
485  case 'M':
486  if ($this->StringAt($this->original, $this->current - 1, 3, array('UMB')) && ($this->current + 1 == $this->last || $this->StringAt($this->original, $this->current + 2, 2, array('ER'))) || substr($this->original, $this->current + 1, 1) == 'M') {
487  $this->current += 2;
488  } else {
489  $this->current += 1;
490  }
491  $this->primary .= 'M';
492  $this->secondary .= 'M';
493  break;
494  case 'N':
495  if (substr($this->original, $this->current + 1, 1) == 'N') {
496  $this->current += 2;
497  } else {
498  $this->current += 1;
499  }
500  $this->primary .= 'N';
501  $this->secondary .= 'N';
502  break;
503  case 'Ñ':
504  $this->current += 1;
505  $this->primary .= 'N';
506  $this->secondary .= 'N';
507  break;
508  case 'P':
509  if (substr($this->original, $this->current + 1, 1) == 'H') {
510  $this->current += 2;
511  $this->primary .= 'F';
512  $this->secondary .= 'F';
513  break;
514  }
515  // also account for "campbell" and "raspberry"
516  if ($this->StringAt($this->original, $this->current + 1, 1, array('P', 'B'))) {
517  $this->current += 2;
518  } else {
519  $this->current += 1;
520  }
521  $this->primary .= 'P';
522  $this->secondary .= 'P';
523  break;
524  case 'Q':
525  if (substr($this->original, $this->current + 1, 1) == 'Q') {
526  $this->current += 2;
527  } else {
528  $this->current += 1;
529  }
530  $this->primary .= 'K';
531  $this->secondary .= 'K';
532  break;
533  case 'R':
534  // french e.g. 'rogier', but exclude 'hochmeier'
535  if ($this->current == $this->last && !$this->SlavoGermanic($this->original) && $this->StringAt($this->original, $this->current - 2, 2, array('IE')) && !$this->StringAt($this->original, ($this->current - 4), 2, array('ME', 'MA'))) {
536  $this->primary .= '';
537  $this->secondary .= 'R';
538  } else {
539  $this->primary .= 'R';
540  $this->secondary .= 'R';
541  }
542  if (substr($this->original, $this->current + 1, 1) == 'R') {
543  $this->current += 2;
544  } else {
545  $this->current += 1;
546  }
547  break;
548  case 'S':
549  // special cases 'island', 'isle', 'carlisle', 'carlysle'
550  if ($this->StringAt($this->original, $this->current - 1, 3, array('ISL', 'YSL'))) {
551  $this->current += 1;
552  break;
553  }
554  // special case 'sugar-'
555  if ($this->current == 0 && $this->StringAt($this->original, $this->current, 5, array('SUGAR'))) {
556  $this->primary .= 'X';
557  $this->secondary .= 'S';
558  $this->current += 1;
559  break;
560  }
561  if ($this->StringAt($this->original, $this->current, 2, array('SH'))) {
562  // germanic
563  if ($this->StringAt($this->original, $this->current + 1, 4, array('HEIM', 'HOEK', 'HOLM', 'HOLZ'))) {
564  $this->primary .= 'S';
565  $this->secondary .= 'S';
566  } else {
567  $this->primary .= 'X';
568  $this->secondary .= 'X';
569  }
570  $this->current += 2;
571  break;
572  }
573  // italian & armenian
574  if ($this->StringAt($this->original, $this->current, 3, array('SIO', 'SIA')) || $this->StringAt($this->original, $this->current, 4, array('SIAN'))) {
575  if (!$this->SlavoGermanic($this->original)) {
576  $this->primary .= 'S';
577  $this->secondary .= 'X';
578  } else {
579  $this->primary .= 'S';
580  $this->secondary .= 'S';
581  }
582  $this->current += 3;
583  break;
584  }
585  // german & anglicisations, e.g. 'smith' match 'schmidt', 'snider' match 'schneider'
586  // also, -sz- in slavic language altho in hungarian it is pronounced 's'
587  if ($this->current == 0 && $this->StringAt($this->original, $this->current + 1, 1, array('M', 'N', 'L', 'W')) || $this->StringAt($this->original, $this->current + 1, 1, array('Z'))) {
588  $this->primary .= 'S';
589  $this->secondary .= 'X';
590  if ($this->StringAt($this->original, $this->current + 1, 1, array('Z'))) {
591  $this->current += 2;
592  } else {
593  $this->current += 1;
594  }
595  break;
596  }
597  if ($this->StringAt($this->original, $this->current, 2, array('SC'))) {
598  // Schlesinger's rule
599  if (substr($this->original, $this->current + 2, 1) == 'H') {
600  // dutch origin, e.g. 'school', 'schooner'
601  if ($this->StringAt($this->original, $this->current + 3, 2, array('OO', 'ER', 'EN', 'UY', 'ED', 'EM'))) {
602  // 'schermerhorn', 'schenker'
603  if ($this->StringAt($this->original, $this->current + 3, 2, array('ER', 'EN'))) {
604  $this->primary .= 'X';
605  $this->secondary .= 'SK';
606  } else {
607  $this->primary .= 'SK';
608  $this->secondary .= 'SK';
609  }
610  $this->current += 3;
611  break;
612  } else {
613  if ($this->current == 0 && !$this->IsVowel($this->original, 3) && substr($this->original, $this->current + 3, 1) != 'W') {
614  $this->primary .= 'X';
615  $this->secondary .= 'S';
616  } else {
617  $this->primary .= 'X';
618  $this->secondary .= 'X';
619  }
620  $this->current += 3;
621  break;
622  }
623  }
624  if ($this->StringAt($this->original, $this->current + 2, 1, array('I', 'E', 'Y'))) {
625  $this->primary .= 'S';
626  $this->secondary .= 'S';
627  $this->current += 3;
628  break;
629  }
630  // else
631  $this->primary .= 'SK';
632  $this->secondary .= 'SK';
633  $this->current += 3;
634  break;
635  }
636  // french e.g. 'resnais', 'artois'
637  if ($this->current == $this->last && $this->StringAt($this->original, $this->current - 2, 2, array('AI', 'OI'))) {
638  $this->primary .= '';
639  $this->secondary .= 'S';
640  } else {
641  $this->primary .= 'S';
642  $this->secondary .= 'S';
643  }
644  if ($this->StringAt($this->original, $this->current + 1, 1, array('S', 'Z'))) {
645  $this->current += 2;
646  } else {
647  $this->current += 1;
648  }
649  break;
650  case 'T':
651  if ($this->StringAt($this->original, $this->current, 4, array('TION'))) {
652  $this->primary .= 'X';
653  $this->secondary .= 'X';
654  $this->current += 3;
655  break;
656  }
657  if ($this->StringAt($this->original, $this->current, 3, array('TIA', 'TCH'))) {
658  $this->primary .= 'X';
659  $this->secondary .= 'X';
660  $this->current += 3;
661  break;
662  }
663  if ($this->StringAt($this->original, $this->current, 2, array('TH')) || $this->StringAt($this->original, $this->current, 3, array('TTH'))) {
664  // special case 'thomas', 'thames' or germanic
665  if ($this->StringAt($this->original, $this->current + 2, 2, array('OM', 'AM')) || $this->StringAt($this->original, 0, 4, array('VAN ', 'VON ')) || $this->StringAt($this->original, 0, 3, array('SCH'))) {
666  $this->primary .= 'T';
667  $this->secondary .= 'T';
668  } else {
669  $this->primary .= '0';
670  $this->secondary .= 'T';
671  }
672  $this->current += 2;
673  break;
674  }
675  if ($this->StringAt($this->original, $this->current + 1, 1, array('T', 'D'))) {
676  $this->current += 2;
677  } else {
678  $this->current += 1;
679  }
680  $this->primary .= 'T';
681  $this->secondary .= 'T';
682  break;
683  case 'V':
684  if (substr($this->original, $this->current + 1, 1) == 'V') {
685  $this->current += 2;
686  } else {
687  $this->current += 1;
688  }
689  $this->primary .= 'F';
690  $this->secondary .= 'F';
691  break;
692  case 'W':
693  // can also be in middle of word
694  if ($this->StringAt($this->original, $this->current, 2, array('WR'))) {
695  $this->primary .= 'R';
696  $this->secondary .= 'R';
697  $this->current += 2;
698  break;
699  }
700  if ($this->current == 0 && ($this->IsVowel($this->original, $this->current + 1) || $this->StringAt($this->original, $this->current, 2, array('WH')))) {
701  // Wasserman should match Vasserman
702  if ($this->IsVowel($this->original, $this->current + 1)) {
703  $this->primary .= 'A';
704  $this->secondary .= 'F';
705  } else {
706  // need Uomo to match Womo
707  $this->primary .= 'A';
708  $this->secondary .= 'A';
709  }
710  }
711  // Arnow should match Arnoff
712  if ($this->current == $this->last && $this->IsVowel($this->original, $this->current - 1) || $this->StringAt($this->original, $this->current - 1, 5, array('EWSKI', 'EWSKY', 'OWSKI', 'OWSKY')) || $this->StringAt($this->original, 0, 3, array('SCH'))) {
713  $this->primary .= '';
714  $this->secondary .= 'F';
715  $this->current += 1;
716  break;
717  }
718  // polish e.g. 'filipowicz'
719  if ($this->StringAt($this->original, $this->current, 4, array('WICZ', 'WITZ'))) {
720  $this->primary .= 'TS';
721  $this->secondary .= 'FX';
722  $this->current += 4;
723  break;
724  }
725  // else skip it
726  $this->current += 1;
727  break;
728  case 'X':
729  // french e.g. breaux
730  if (!($this->current == $this->last && ($this->StringAt($this->original, $this->current - 3, 3, array('IAU', 'EAU')) || $this->StringAt($this->original, $this->current - 2, 2, array('AU', 'OU'))))) {
731  $this->primary .= 'KS';
732  $this->secondary .= 'KS';
733  }
734  if ($this->StringAt($this->original, $this->current + 1, 1, array('C', 'X'))) {
735  $this->current += 2;
736  } else {
737  $this->current += 1;
738  }
739  break;
740  case 'Z':
741  // chinese pinyin e.g. 'zhao'
742  if (substr($this->original, $this->current + 1, 1) == 'H') {
743  $this->primary .= 'J';
744  $this->secondary .= 'J';
745  $this->current += 2;
746  break;
747  } elseif ($this->StringAt($this->original, $this->current + 1, 2, array('ZO', 'ZI', 'ZA')) || $this->SlavoGermanic($this->original) && ($this->current > 0 && substr($this->original, $this->current - 1, 1) != 'T')) {
748  $this->primary .= 'S';
749  $this->secondary .= 'TS';
750  } else {
751  $this->primary .= 'S';
752  $this->secondary .= 'S';
753  }
754  if (substr($this->original, $this->current + 1, 1) == 'Z') {
755  $this->current += 2;
756  } else {
757  $this->current += 1;
758  }
759  break;
760  default:
761  $this->current += 1;
762  }
763  }
764  // end while
765  $this->primary = substr($this->primary, 0, 4);
766  $this->secondary = substr($this->secondary, 0, 4);
767  $result['primary'] = $this->primary;
768  $result['secondary'] = $this->secondary;
769  return $result;
770  }
771 
772  // end of function MetaPhone
773  // Private methods
777  public function StringAt($string, $start, $length, $list) {
778  if ($start < 0 || $start >= strlen($string)) {
779  return 0;
780  }
781  $listCount = count($list);
782  for ($i = 0; $i < $listCount; $i++) {
783  if ($list[$i] == substr($string, $start, $length)) {
784  return 1;
785  }
786  }
787  return 0;
788  }
789 
798  public function IsVowel($string, $pos) {
799  return preg_match('/[AEIOUY]/', substr($string, $pos, 1));
800  }
801 
809  public function SlavoGermanic($string) {
810  return preg_match('/W|K|CZ|WITZ/', $string);
811  }
812 
813 }
if($list_of_literals) if(!empty($literals)) if(!empty($literals)) $result
Analyse literals to prepend the N char to them if their contents aren&#39;t numeric.