source: trunk/lib/SpellCheck.inc.php @ 1

Last change on this file since 1 was 1, checked in by scdev, 19 years ago

Initial import.

File size: 13.0 KB
Line 
1<?php
2/**
3 * SpellCheck.inc.php
4 * code by strangecode :: www.strangecode.com :: this document contains copyrighted information
5 *
6 * Interface to PHP's pspell functions.
7 *
8 * @author  Quinn Comendant <quinn@strangecode.com>
9 * @version 1.0
10 */
11 
12/* Implementation example:
13--------------------------------------------------------------------------------
14include '_config.inc.php';
15include 'codebase/lib/SpellCheck.inc.php';
16
17// Instantiate with language and optionally the path to the custom wordlist file.
18$spell = new SpellCheck('en', '/tmp/my_custom_dict');
19
20$text_to_check = 'donky rinds taste like mealworm paste';
21
22// Add new word to persistent custom wordlist file.
23$spell->add('mealworm');
24
25if (!$spell->checkString($text_to_check)) {
26    $suggestions = $spell->getStringSuggestions($text_to_check);
27    echo 'Spelling errors:';
28    print_r($suggestions);
29} else {
30    echo 'No spelling errors';
31}
32
33// Save added words to persistent custom wordlist file.
34$spell->save();
35--------------------------------------------------------------------------------
36*/
37
38class SpellCheck {
39
40    var $_params = array(
41        'personal_wordlist' => '',
42        'skip_len' => 3,
43        'mode' => PSPELL_NORMAL, // PSPELL_FAST, PSPELL_NORMAL, or PSPELL_BAD_SPELLERS.
44        'highlight_start' => '<strong style="color:red;">',
45        'highlight_end' => '</strong>',
46    );
47   
48    var $_pspell_cfg_handle;
49    var $_pspell_handle;
50    var $_use_personal_wordlist = false;
51    var $_errors = array();
52   
53    /**
54     * Constructor.
55     */
56    function SpellCheck($lang='en', $personal_wordlist=null)
57    {
58        $this->_pspell_cfg_handle = pspell_config_create($lang);
59
60        pspell_config_ignore($this->_pspell_cfg_handle, $skip_len);
61        pspell_config_mode($this->_pspell_cfg_handle, $mode);
62
63        if (isset($personal_wordlist)) {
64            if (!is_writable(dirname($personal_wordlist)) && !is_writable($personal_wordlist)) {
65                App::logMsg(sprintf('Personal wordlist file not writable: %s', $personal_wordlist), LOG_NOTICE, __FILE__, __LINE__);
66            } else {
67                $this->setParam(array('personal_wordlist' => $personal_wordlist));
68                pspell_config_personal($this->_pspell_cfg_handle, $personal_wordlist);
69                $this->_use_personal_wordlist = true;
70                App::logMsg(sprintf('Using personal wordlist: %s', $personal_wordlist), LOG_DEBUG, __FILE__, __LINE__);
71            }
72        }
73
74        $this->_pspell_handle = pspell_new_config($this->_pspell_cfg_handle);
75    }
76
77    /**
78     * Set (or overwrite existing) parameters by passing an array of new parameters.
79     *
80     * @access public
81     * @param  array    $params     Array of parameters (key => val pairs).
82     */
83    function setParam($params)
84    {
85        if (isset($params) && is_array($params)) {
86            // Merge new parameters with old overriding only those passed.
87            $this->_params = array_merge($this->_params, $params);
88        } else {
89            App::logMsg(sprintf('Parameters are not an array: %s', $params), LOG_ERR, __FILE__, __LINE__);
90        }
91    }
92
93    /**
94     * Return the value of a parameter, if it exists.
95     *
96     * @access public
97     * @param string $param        Which parameter to return.
98     * @return mixed               Configured parameter value.
99     */
100    function getParam($param)
101    {
102        if (isset($this->_params[$param])) {
103            return $this->_params[$param];
104        } else {
105            App::logMsg(sprintf('Parameter is not set: %s', $param), LOG_DEBUG, __FILE__, __LINE__);
106            return null;
107        }
108    }
109   
110    /**
111     * Check whether any errors have been triggered.
112     *
113     * @return bool   True if any errors were found, false otherwise.
114     */
115    function anyErrors()
116    {
117        return (sizeof($this->_errors) > 0);
118    }
119
120    /**
121     * Reset the error list.
122     */
123    function resetErrorList()
124    {
125        $this->_errors = array();
126    }
127   
128    /**
129     * Check one word.
130     *
131     * @access  public
132     * @param   string  $word
133     * @return  bool    True if word is correct.
134     * @author  Quinn Comendant <quinn@strangecode.com>
135     * @version 1.0
136     * @since   09 Jun 2005 18:23:51
137     */
138    function check($word)
139    {
140        if (pspell_check($this->_pspell_handle, $word)) {
141            return true;
142        } else {
143            $this->_errors[] = $word;
144            return false;
145        }
146    }
147   
148    /**
149     * Suggest the correct spelling for one misspelled word.
150     *
151     * @access  public
152     * @param   string  $word
153     * @return  array   Word suggestions.
154     * @author  Quinn Comendant <quinn@strangecode.com>
155     * @version 1.0
156     * @since   09 Jun 2005 18:23:51
157     */
158    function suggest($word)
159    {
160        return pspell_suggest($this->_pspell_handle, $word);
161    }
162   
163    /**
164     * Add a word to a personal list.
165     *
166     * @access  public
167     * @param   string  $word
168     * @return  array   Word suggestions.
169     * @author  Quinn Comendant <quinn@strangecode.com>
170     * @version 1.0
171     * @since   09 Jun 2005 18:23:51
172     */
173    function add($word)
174    {
175        if ($this->_use_personal_wordlist) {
176            App::logMsg(sprintf('Added "%s" to personal wordlist: %s', $word, $this->getParam('personal_wordlist')), LOG_DEBUG, __FILE__, __LINE__);
177            return pspell_add_to_personal($this->_pspell_handle, $word);
178        }
179    }
180   
181    /**
182     * Save personal list to file.
183     *
184     * @access  public
185     * @param   string  $word
186     * @return  array   Word suggestions.
187     * @author  Quinn Comendant <quinn@strangecode.com>
188     * @version 1.0
189     * @since   09 Jun 2005 18:23:51
190     */
191    function save()
192    {
193        if ($this->_use_personal_wordlist) {
194            if (pspell_save_wordlist($this->_pspell_handle)) {
195                App::logMsg(sprintf('Saved personal wordlist: %s', $this->getParam('personal_wordlist')), LOG_DEBUG, __FILE__, __LINE__);
196                return true;
197            } else {
198                App::logMsg(sprintf('Failed saving personal wordlist: %s', $this->getParam('personal_wordlist')), LOG_DEBUG, __FILE__, __LINE__);
199                return false;
200            }
201        }
202    }
203   
204    /**
205     * Returns an array of suggested words for each mispelled word in the given text.
206     * The first word of the returned array is the (possibly) misspelled word.
207     *
208     * @access  public
209     * @param   string  $string String to get suggestions for.
210     * @return  mixed   Array of suggested words or false if none.
211     * @author  Quinn Comendant <quinn@strangecode.com>
212     * @version 1.0
213     * @since   09 Jun 2005 21:29:49
214     */
215    function getStringSuggestions($string)
216    {
217        $corrections = array();
218        $words = preg_split('/([\W]+?)/', $string, -1, PREG_SPLIT_DELIM_CAPTURE);
219        // Remove non-word elements.
220        $words = preg_grep('/\w+/', $words);
221
222        if (is_array($words) && !empty($words)) {
223            foreach ($words as $i => $word) {
224                if (!$this->check($word)) {
225                    $corrections[$i] = $this->suggest($word);
226                    // Keep the original spelling as one of the suggestions.
227                    array_unshift($corrections[$i], $word);
228                    array_unique($corrections[$i]);
229                }
230            }
231        }
232        if (is_array($corrections) && !empty($corrections)) {
233            return $corrections;
234        } else {
235            return false;
236        }
237    }
238   
239    /**
240     * Checks all words in a given string.
241     *
242     * @access  public
243     * @param   string  $string     String to check.
244     * @return  void
245     * @author  Quinn Comendant <quinn@strangecode.com>
246     * @version 1.0
247     * @since   09 Jun 2005 22:11:27
248     */
249    function checkString($string)
250    {
251        $errors = array();
252        $words = preg_split('/([\W]+?)/', $string, -1, PREG_SPLIT_DELIM_CAPTURE);
253        // Remove non-word elements.
254        $check_words = preg_grep('/\w+/', $words);
255
256        if (is_array($check_words) && !empty($check_words)) {
257            foreach ($check_words as $i => $word) {
258                if (!$this->check($word)) {
259                    $errors[] = $word;
260                }
261            }
262        }
263        if (empty($errors)) {
264            return true;
265        } else {
266            $this->_errors = $errors + $this->_errors;
267            return false;
268        }
269    }
270   
271    /**
272     * Returns a given string with misspelled words highlighted.
273     *
274     * @access  public
275     * @param   string  $string     Text to highlight.
276     * @return  string  Highlighted text.
277     * @author  Quinn Comendant <quinn@strangecode.com>
278     * @version 1.0
279     * @since   09 Jun 2005 21:29:49
280     */
281    function getStringHighlighted($string, $show_footnote=false)
282    {
283        $words = preg_split('/([\W]+?)/', $string, -1, PREG_SPLIT_DELIM_CAPTURE);
284        $check_words = preg_grep('/\w+/', $words);
285        $cnt = 0;
286        if (is_array($check_words) && !empty($check_words)) {
287            foreach ($check_words as $i => $word) {
288                if (!$this->check($word)) {
289                    $footnote = $show_footnote ? '<sup>' . ++$cnt . '</sup>' : '';
290                    $words[$i] = $this->getParam('highlight_start') . $word . $this->getParam('highlight_end') . $footnote;
291                }
292            }
293        }
294        return join('', $words);
295    }
296   
297    /**
298     * Prints the HTML for correcting all mispellings found in the text of one $_FORM element.
299     *
300     * @access  public
301     * @param   string  $form_name  Name of the form to check.
302     * @return  void
303     * @author  Quinn Comendant <quinn@strangecode.com>
304     * @version 1.0
305     * @since   09 Jun 2005 21:29:49
306     */
307    function printCorrectionForm($form_name)
308    {
309        ?>
310        <input name="<?php echo $form_name ?>" type="hidden" value="<?php echo getFormData($form_name) ?>" />
311        <?php
312       
313        $form_words = $this->getStringSuggestions(getFormData($form_name));
314        if (is_array($form_words) && !empty($form_words)) {
315            ?><ol><?php
316            foreach ($form_words as $i => $words) {
317                ?>
318                <li>
319                <select name="spelling_suggestions[<?php echo $form_name ?>][<?php echo $i ?>]" onChange="document.forms[0].elements['spelling_corrections[<?php echo $form_name ?>][<?php echo $i ?>]'].value = this.value;">
320                <?php $original_word = array_shift($words); ?>
321                <option value="<?php echo $original_word ?>">(<?php echo $original_word ?>)</option>
322                <?php
323               
324                foreach ($words as $suggestion) {
325                    ?>
326                    <option value="<?php echo $suggestion ?>"><?php echo $suggestion ?></option>
327                    <?php
328                }
329               
330                ?>
331                </select>
332                <input type="text" name="spelling_corrections[<?php echo $form_name ?>][<?php echo $i ?>]" value="<?php echo $original_word ?>" size="20">
333                <?php if ($this->_use_personal_wordlist) { ?>
334                <input name="save_to_personal_wordlist[]" type="checkbox" value="<?php echo $i ?>" /><?php echo _("Learn spelling") ?>
335                <?php } ?>
336                </li>
337                <?php
338            }
339            ?></ol><?php
340        }
341    }
342   
343    /**
344     * Tests if any form spelling corrections have been submitted.
345     *
346     * @access  public
347     * @return  bool    True if form spelling has been checked.
348     * @author  Quinn Comendant <quinn@strangecode.com>
349     * @version 1.0
350     * @since   09 Jun 2005 23:15:35
351     */
352    function anyFormCorrections()
353    {
354        return (false !== getFormData('spelling_suggestions', false)) || (false !== getFormData('spelling_corrections', false));
355    }
356   
357    /**
358     * Replace the misspelled words in the text of a specified form with the corrections.
359     *
360     * @access  public
361     * @param   string  $form_name      Name of form to apply corrections to.
362     * @return  string  Corrected form text.
363     * @author  Quinn Comendant <quinn@strangecode.com>
364     * @version 1.0
365     * @since   09 Jun 2005 23:18:51
366     */
367    function applyFormCorrections($form_name)
368    {
369        $form_words = preg_split('/([\W]+?)/', getFormData($form_name), -1, PREG_SPLIT_DELIM_CAPTURE);
370        $suggestions = getFormData('spelling_suggestions');
371        $corrections = getFormData('spelling_corrections');
372
373        $form_words = array_diff($corrections[$form_name], array('')) + $suggestions[$form_name] + $form_words;
374        ksort($form_words);
375
376        if ($this->_use_personal_wordlist) {
377            $save_to_personal_wordlist = getFormData('save_to_personal_wordlist');
378            if (is_array($save_to_personal_wordlist) && !empty($save_to_personal_wordlist)) {
379                foreach ($save_to_personal_wordlist as $cust) {
380                    $this->add($form_words[$cust]);
381                }
382            }
383            $this->save();
384        }
385
386        if (is_array($form_words) && !empty($form_words)) {
387            return join('', $form_words);
388        } else {
389            return getFormData($form_name);
390        }
391    }
392
393} // End.
394
395?>
Note: See TracBrowser for help on using the repository browser.