Source for file fr.php

Documentation is available at fr.php

  1. <?php
  2. /**
  3.  * @package     Joomla.Administrator
  4.  * @subpackage  com_finder
  5.  *
  6.  * @copyright   Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
  7.  * @license     GNU General Public License version 2 or later; see LICENSE
  8.  */
  9.  
  10. defined('_JEXEC') or die;
  11.  
  12. JLoader::register('FinderIndexerStemmer', dirname(__DIR__) . '/stemmer.php');
  13.  
  14. /**
  15.  * French stemmer class for Smart Search indexer.
  16.  *
  17.  * First contributed by Eric Sanou (bobotche@hotmail.fr)
  18.  * This class is inspired in  Alexis Ulrich's French stemmer code (http://alx2002.free.fr)
  19.  *
  20.  * @package     Joomla.Administrator
  21.  * @subpackage  com_finder
  22.  * @since       3.0
  23.  */
  24. {
  25.     /**
  26.      * Stemming rules.
  27.      *
  28.      * @var    array 
  29.      * @since  3.0
  30.      */
  31.     private static $_stemRules = null;
  32.  
  33.     /**
  34.      * Method to stem a token and return the root.
  35.      *
  36.      * @param   string  $token  The token to stem.
  37.      * @param   string  $lang   The language of the token.
  38.      *
  39.      * @return  string  The root token.
  40.      *
  41.      * @since   3.0
  42.      */
  43.     public function stem($token, $lang)
  44.     {
  45.         // Check if the token is long enough to merit stemming.
  46.         if (strlen($token) <= 2)
  47.         {
  48.             return $token;
  49.         }
  50.  
  51.         // Check if the language is French or All.
  52.         if ($lang !== 'fr' && $lang != '*')
  53.         {
  54.             return $token;
  55.         }
  56.  
  57.         // Stem the token if it is not in the cache.
  58.         if (!isset($this->cache[$lang][$token]))
  59.         {
  60.             // Stem the token.
  61.             $result = static::_getStem($token);
  62.  
  63.             // Add the token to the cache.
  64.             $this->cache[$lang][$token] = $result;
  65.         }
  66.  
  67.         return $this->cache[$lang][$token];
  68.     }
  69.  
  70.     /**
  71.      * French stemmer rules variables.
  72.      *
  73.      * @return  array  The rules
  74.      *
  75.      * @since   3.0
  76.      */
  77.     protected static function getStemRules()
  78.     {
  79.         if (static::$_stemRules)
  80.         {
  81.             return static::$_stemRules;
  82.         }
  83.  
  84.         $vars = array();
  85.  
  86.         // French accented letters in ISO-8859-1 encoding
  87.         $vars['accents'] = chr(224) . chr(226) . chr(232) . chr(233) . chr(234) . chr(235) . chr(238) . chr(239) . chr(244) . chr(251) . chr(249) . chr(231);
  88.  
  89.         // The rule patterns include all accented words for french language
  90.         $vars['rule_pattern'] = "/^([a-z" . $vars['accents'] . "]*)(\*){0,1}(\d)([a-z" . $vars['accents'] . "]*)([.|>])/";
  91.  
  92.         // French vowels (including y) in ISO-8859-1 encoding
  93.         $vars['vowels'] = chr(97) . chr(224) . chr(226) . chr(101) . chr(232) . chr(233) . chr(234) . chr(235) . chr(105) . chr(238) . chr(239) . chr(111) . chr(244) . chr(117) . chr(251) . chr(249) . chr(121);
  94.  
  95.         // The French rules in ISO-8859-1 encoding
  96.         $vars['rules'] = array(
  97.             'esre1>', 'esio1>', 'siol1.', 'siof0.', 'sioe0.', 'sio3>', 'st1>', 'sf1>', 'sle1>', 'slo1>', 's' . chr(233) . '1>', chr(233) . 'tuae5.',
  98.             chr(233) . 'tuae2.', 'tnia0.', 'tniv1.', 'tni3>', 'suor1.', 'suo0.', 'sdrail5.', 'sdrai4.', 'er' . chr(232) . 'i1>', 'sesue3x>',
  99.             'esuey5i.', 'esue2x>', 'se1>', 'er' . chr(232) . 'g3.', 'eca1>', 'esiah0.', 'esi1>', 'siss2.', 'sir2>', 'sit2>', 'egan' . chr(233) . '1.',
  100.             'egalli6>', 'egass1.', 'egas0.', 'egat3.', 'ega3>', 'ette4>', 'ett2>', 'etio1.', 'tio' . chr(231) . '4c.', 'tio0.', 'et1>', 'eb1>',
  101.             'snia1>', 'eniatnau8>', 'eniatn4.', 'enia1>', 'niatnio3.', 'niatg3.', 'e' . chr(233) . '1>', chr(233) . 'hcat1.', chr(233) . 'hca4.',
  102.             chr(233) . 'tila5>', chr(233) . 'tici5.', chr(233) . 'tir1.', chr(233) . 'ti3>', chr(233) . 'gan1.', chr(233) . 'ga3>',
  103.             chr(233) . 'tehc1.', chr(233) . 'te3>', chr(233) . 'it0.', chr(233) . '1>', 'eire4.', 'eirue5.', 'eio1.', 'eia1.', 'ei1>', 'eng1.',
  104.             'xuaessi7.', 'xuae1>', 'uaes0.', 'uae3.', 'xuave2l.', 'xuav2li>', 'xua3la>', 'ela1>', 'lart2.', 'lani2>', 'la' . chr(233) . '2>',
  105.             'siay4i.', 'siassia7.', 'siarv1*.', 'sia1>', 'tneiayo6i.', 'tneiay6i.', 'tneiassia9.', 'tneiareio7.', 'tneia5>', 'tneia4>', 'tiario4.',
  106.             'tiarim3.', 'tiaria3.', 'tiaris3.', 'tiari5.', 'tiarve6>', 'tiare5>', 'iare4>', 'are3>', 'tiay4i.', 'tia3>', 'tnay4i.',
  107.             'em' . chr(232) . 'iu5>', 'em' . chr(232) . 'i4>', 'tnaun3.', 'tnauqo3.', 'tnau4>', 'tnaf0.', 'tnat' . chr(233) . '2>', 'tna3>', 'tno3>',
  108.             'zeiy4i.', 'zey3i.', 'zeire5>', 'zeird4.', 'zeirio4.', 'ze2>', 'ssiab0.', 'ssia4.', 'ssi3.', 'tnemma6>', 'tnemesuey9i.', 'tnemesue8>',
  109.             'tnemevi7.', 'tnemessia5.', 'tnemessi8.', 'tneme5>', 'tnemia4.', 'tnem' . chr(233) . '5>', 'el2l>', 'lle3le>', 'let' . chr(244) . '0.',
  110.             'lepp0.', 'le2>', 'srei1>', 'reit3.', 'reila2.', 'rei3>', 'ert' . chr(226) . 'e5.', 'ert' . chr(226) . chr(233) . '1.',
  111.             'ert' . chr(226) . '4.', 'drai4.', 'erdro0.', 'erute5.', 'ruta0.', 'eruta1.', 'erutiov1.', 'erub3.', 'eruh3.', 'erul3.', 'er2r>', 'nn1>',
  112.             'r' . chr(232) . 'i3.', 'srev0.', 'sr1>', 'rid2>', 're2>', 'xuei4.', 'esuei5.', 'lbati3.', 'lba3>', 'rueis0.', 'ruehcn4.', 'ecirta6.',
  113.             'ruetai6.', 'rueta5.', 'rueir0.', 'rue3>', 'esseti6.', 'essere6>', 'esserd1.', 'esse4>', 'essiab1.', 'essia5.', 'essio1.', 'essi4.',
  114.             'essal4.', 'essa1>', 'ssab1.', 'essurp1.', 'essu4.', 'essi1.', 'ssor1.', 'essor2.', 'esso1>', 'ess2>', 'tio3.', 'r' . chr(232) . 's2re.',
  115.             'r' . chr(232) . '0e.', 'esn1.', 'eu1>', 'sua0.', 'su1>', 'utt1>', 'tu' . chr(231) . '3c.', 'u' . chr(231) . '2c.', 'ur1.', 'ehcn2>',
  116.             'ehcu1>', 'snorr3.', 'snoru3.', 'snorua3.', 'snorv3.', 'snorio4.', 'snori5.', 'snore5>', 'snortt4>', 'snort' . chr(238) . 'a7.', 'snort3.',
  117.             'snor4.', 'snossi6.', 'snoire6.', 'snoird5.', 'snoitai7.', 'snoita6.', 'snoits1>', 'noits0.', 'snoi4>', 'noitaci7>', 'noitai6.', 'noita5.',
  118.             'noitu4.', 'noi3>', 'snoya0.', 'snoy4i.', 'sno' . chr(231) . 'a1.', 'sno' . chr(231) . 'r1.', 'snoe4.', 'snosiar1>', 'snola1.', 'sno3>',
  119.             'sno1>', 'noll2.', 'tnennei4.', 'ennei2>', 'snei1>', 'sne' . chr(233) . '1>', 'enne' . chr(233) . '5e.', 'ne' . chr(233) . '3e.', 'neic0.',
  120.             'neiv0.', 'nei3.', 'sc1.', 'sd1.', 'sg1.', 'sni1.', 'tiu0.', 'ti2.', 'sp1>', 'sna1>', 'sue1.', 'enn2>', 'nong2.', 'noss2.', 'rioe4.',
  121.             'riot0.', 'riorc1.', 'riovec5.', 'rio3.', 'ric2.', 'ril2.', 'tnerim3.', 'tneris3>', 'tneri5.', 't' . chr(238) . 'a3.', 'riss2.',
  122.             't' . chr(238) . '2.', 't' . chr(226) . '2>', 'ario2.', 'arim1.', 'ara1.', 'aris1.', 'ari3.', 'art1>', 'ardn2.', 'arr1.', 'arua1.',
  123.             'aro1.', 'arv1.', 'aru1.', 'ar2.', 'rd1.', 'ud1.', 'ul1.', 'ini1.', 'rin2.', 'tnessiab3.', 'tnessia7.', 'tnessi6.', 'tnessni4.', 'sini2.',
  124.             'sl1.', 'iard3.', 'iario3.', 'ia2>', 'io0.', 'iule2.', 'i1>', 'sid2.', 'sic2.', 'esoi4.', 'ed1.', 'ai2>', 'a1>', 'adr1.',
  125.             'tner' . chr(232) . '5>', 'evir1.', 'evio4>', 'evi3.', 'fita4.', 'fi2>', 'enie1.', 'sare4>', 'sari4>', 'sard3.', 'sart2>', 'sa2.',
  126.             'tnessa6>', 'tnessu6>', 'tnegna3.', 'tnegi3.', 'tneg0.', 'tneru5>', 'tnemg0.', 'tnerni4.', 'tneiv1.', 'tne3>', 'une1.', 'en1>', 'nitn2.',
  127.             'ecnay5i.', 'ecnal1.', 'ecna4.', 'ec1>', 'nn1.', 'rit2>', 'rut2>', 'rud2.', 'ugn1>', 'eg1>', 'tuo0.', 'tul2>', 't' . chr(251) . '2>',
  128.             'ev1>', 'v' . chr(232) . '2ve>', 'rtt1>', 'emissi6.', 'em1.', 'ehc1.', 'c' . chr(233) . 'i2c' . chr(232) . '.', 'libi2l.', 'llie1.',
  129.             'liei4i.', 'xuev1.', 'xuey4i.', 'xueni5>', 'xuell4.', 'xuere5.', 'xue3>', 'rb' . chr(233) . '3rb' . chr(232) . '.', 'tur2.',
  130.             'rir' . chr(233) . '4re.', 'rir2.', 'c' . chr(226) . '2ca.', 'snu1.', 'rt' . chr(238) . 'a4.', 'long2.', 'vec2.', chr(231) . '1c>',
  131.             'ssilp3.', 'silp2.', 't' . chr(232) . 'hc2te.', 'n' . chr(232) . 'm2ne.', 'llepp1.', 'tan2.', 'rv' . chr(232) . '3rve.',
  132.             'rv' . chr(233) . '3rve.', 'r' . chr(232) . '2re.', 'r' . chr(233) . '2re.', 't' . chr(232) . '2te.', 't' . chr(233) . '2te.', 'epp1.',
  133.             'eya2i.', 'ya1i.', 'yo1i.', 'esu1.', 'ugi1.', 'tt1.', 'end0.'
  134.         );
  135.  
  136.         static::$_stemRules = $vars;
  137.  
  138.         return static::$_stemRules;
  139.     }
  140.  
  141.     /**
  142.      * Returns the number of the first rule from the rule number
  143.      * that can be applied to the given reversed input.
  144.      * returns -1 if no rule can be applied, ie the stem has been found
  145.      *
  146.      * @param   string   $reversed_input  The input to check in reversed order
  147.      * @param   integer  $rule_number     The rule number to check
  148.      *
  149.      * @return  integer  Number of the first rule
  150.      *
  151.      * @since   3.0
  152.      */
  153.     private static function _getFirstRule($reversed_input, $rule_number)
  154.     {
  155.         $vars = static::getStemRules();
  156.  
  157.         $nb_rules = count($vars['rules']);
  158.  
  159.         for ($i = $rule_number; $i < $nb_rules; $i++)
  160.         {
  161.             // Gets the letters from the current rule
  162.             $rule = $vars['rules'][$i];
  163.             $rule = preg_replace($vars['rule_pattern'], "\\1", $rule);
  164.  
  165.             if (strncasecmp(utf8_decode($rule), $reversed_input, strlen(utf8_decode($rule))) == 0)
  166.             {
  167.                 return $i;
  168.             }
  169.         }
  170.  
  171.         return -1;
  172.     }
  173.  
  174.     /**
  175.      * Check the acceptability of a stem for French language
  176.      *
  177.      * @param   string  $reversed_stem  The stem to check in reverse form
  178.      *
  179.      * @return  boolean  True if stem is acceptable
  180.      *
  181.      * @since   3.0
  182.      */
  183.     private static function _check($reversed_stem)
  184.     {
  185.         $vars = static::getStemRules();
  186.  
  187.         if (preg_match('/[' . $vars['vowels'] . ']$/', utf8_encode($reversed_stem)))
  188.         {
  189.             // If the form starts with a vowel then at least two letters must remain after stemming (e.g.: "etaient" --> "et")
  190.             return (strlen($reversed_stem) > 2);
  191.         }
  192.         else
  193.         {
  194.             // If the reversed stem starts with a consonant then at least two letters must remain after stemming
  195.             if (strlen($reversed_stem) <= 2)
  196.             {
  197.                 return false;
  198.             }
  199.  
  200.             // And at least one of these must be a vowel or "y"
  201.             return (preg_match('/[' . $vars['vowels'] . ']/', utf8_encode($reversed_stem)));
  202.         }
  203.     }
  204.  
  205.     /**
  206.      * Paice/Husk stemmer which returns a stem for the given $input
  207.      *
  208.      * @param   string  $input  The word for which we want the stem in UTF-8
  209.      *
  210.      * @return  string  The stem
  211.      *
  212.      * @since   3.0
  213.      */
  214.     private static function _getStem($input)
  215.     {
  216.         $vars = static::getStemRules();
  217.  
  218.         $intact = true;
  219.         $reversed_input = strrev(utf8_decode($input));
  220.         $rule_number = 0;
  221.  
  222.         // This loop goes through the rules' array until it finds an ending one (ending by '.') or the last one ('end0.')
  223.         while (true)
  224.         {
  225.             $rule_number = static::_getFirstRule($reversed_input, $rule_number);
  226.  
  227.             if ($rule_number == -1)
  228.             {
  229.                 // No other rule can be applied => the stem has been found
  230.                 break;
  231.             }
  232.             $rule = $vars['rules'][$rule_number];
  233.             preg_match($vars['rule_pattern'], $rule, $matches);
  234.  
  235.             if (($matches[2] != '*') || ($intact))
  236.             {
  237.                 $reversed_stem = utf8_decode($matches[4]) . substr($reversed_input, $matches[3], strlen($reversed_input) - $matches[3]);
  238.  
  239.                 if (self::_check($reversed_stem))
  240.                 {
  241.                     $reversed_input = $reversed_stem;
  242.  
  243.                     if ($matches[5] == '.')
  244.                     {
  245.                         break;
  246.                     }
  247.                 }
  248.                 else
  249.                 {
  250.                     // Go to another rule
  251.                     $rule_number++;
  252.                 }
  253.             }
  254.             else
  255.             {
  256.                 // Go to another rule
  257.                 $rule_number++;
  258.             }
  259.         }
  260.  
  261.         return utf8_encode(strrev($reversed_input));
  262.     }
  263. }

Documentation generated on Tue, 19 Nov 2013 15:03:46 +0100 by phpDocumentor 1.4.3