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

Last change on this file since 42 was 42, checked in by scdev, 18 years ago

detabbed all files ;P

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_WARNING, __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_ERR, __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.