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

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

Tons of little updates and bugfixes. CSS updates to templates and core css files. File upload ability to module_maker. Remade Upload interface to use setParam/getParam.

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