source: branches/2.2/expressoMail1_2/spell_checker/pspell_comp.php @ 3400

Revision 3400, 7.4 KB checked in by brunocosta, 14 years ago (diff)

Ticket #891 - Implementação do correção ortográfica.

Line 
1 <?php
2/**
3 * pspell compatibility wrapper
4 *
5 * This library provides some of the functions from the pspell PHP extension
6 * by wrapping them to calls to the aspell binary
7 *
8 * It can be simply dropped into code written for the pspell extension like
9 * the following
10 *
11 * if(!function_exists('pspell_suggest')){
12 *   require_once ("pspell_comp.php");
13 * }
14 *
15 * Define the path to the aspell binary like this if needed:
16 *
17 * define('ASPELL_BIN','/path/to/aspell');
18 *
19 * @author   Andreas Gohr <andi@splitbrain.org>
20 *
21 * Copyright (c) 2005, Andreas Gohr
22 * All rights reserved.
23 *
24 * Redistribution and use in source and binary forms, with or without
25 * modification, are permitted provided that the following conditions are met:
26 *
27 *   * Redistributions of source code must retain the above copyright notice,
28 *     this list of conditions and the following disclaimer.
29 *   * Redistributions in binary form must reproduce the above copyright notice,
30 *     this list of conditions and the following disclaimer in the documentation
31 *     and/or other materials provided with the distribution.
32 *
33 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
34 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
35 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
36 * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
37 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
38 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
39 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
40 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
41 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
42 * OF SUCH DAMAGE.
43 */
44
45if(!defined('ASPELL_BIN')) define('ASPELL_BIN','aspell');
46
47define(PSPELL_FAST,1);         # Fast mode (least number of suggestions)
48define(PSPELL_NORMAL,2);       # Normal mode (more suggestions)
49define(PSPELL_BAD_SPELLERS,3); # Slow mode (a lot of suggestions) )
50
51function pspell_config_create($language, $spelling=null, $jargon=null, $encoding='iso8859-1'){
52    return new Pspell($language, $spelling, $jargon, $encoding);
53}
54
55function pspell_config_mode(&$config, $mode){
56    return $config->setMode($mode);
57}
58
59function pspell_new_config(&$config){
60    return $config;
61}
62
63function pspell_check(&$dict,$word){
64    return $dict->check($word);
65}
66
67function pspell_suggest(&$dict, $word){
68    return $dict->suggest($word);
69}
70
71/**
72 * Class to provide pspell functionality through aspell
73 *
74 * Needs PHP >= 4.3.0
75 */
76class Pspell{
77    var $language;
78    var $spelling;
79    var $jargon;
80    var $encoding;
81    var $mode=PSPELL_NORMAL;
82   
83    var $args='';
84
85    /**
86     * Constructor. Works like pspell_config_create()
87     *
88     * @author   Andreas Gohr <andi@splitbrain.org>
89     * @todo     $spelling isn't used
90     */
91    function Pspell($language, $spelling=null, $jargon=null, $encoding='iso8859-1'){
92        $this->language = $language;
93        $this->spelling = $spelling;
94        $this->jargon   = $jargon;
95        $this->encoding = $encoding;
96        $this->_prepareArgs();
97    }
98
99    /**
100     * Set the spelling mode like pspell_config_mode()
101     *
102     * Mode can be PSPELL_FAST, PSPELL_NORMAL or PSPELL_BAD_SPELLER
103     *
104     * @author   Andreas Gohr <andi@splitbrain.org>
105     * @todo     check for valid mode
106     */
107    function setMode($mode){
108        $this->mode = $mode;
109        $this->_prepareArgs();
110        return $mode;
111    }
112
113    /**
114     * Prepares the needed arguments for the call to the aspell binary
115     *
116     * No need to call this directly
117     *
118     * @author   Andreas Gohr <andi@splitbrain.org>
119     */
120    function _prepareArgs(){
121        $this->args = '';
122
123        if($this->language){
124            $this->args .= ' --lang='.escapeshellarg($this->language);
125        }else{
126            return false; // no lang no spell
127        }
128
129        #FIXME how to support spelling?
130
131        if($this->$jargon){
132            $this->args .= ' --jargon='.escapeshellarg($jargon);
133        }
134
135        if($this->$encoding){
136            $this->args .= ' --encoding='.escapeshellarg($encoding);
137        }
138
139        switch ($this->mode){
140            case PSPELL_FAST:
141                $this->args .= ' --sug-mode=fast';
142                break;
143            case PSPELL_BAD_SPELLERS:
144                $this->args .= ' --sug-mode=bad-spellers';
145                break;
146            default:
147                $this->args .= ' --sug-mode=normal';
148        }
149
150        return true;
151    }
152
153    /**
154     * Checks a word for correctness
155     *
156     * This opens a bidirectional pipe to the aspell binary, writes
157     * the given word to STDIN and reads STDOUT for the result
158     *
159     * @returns array of suggestions on wrong spelling, or true on no spellerror
160     *
161     * @author   Andreas Gohr <andi@splitbrain.org>
162     */
163    function suggest($word){
164        $out = '';
165        $err = '';
166
167        $word = trim($word);
168
169        if(empty($word)) return true;
170
171        //prepare file descriptors
172        $descspec = array(
173               0 => array('pipe', 'r'),  // stdin is a pipe that the child will read from
174               1 => array('pipe', 'w'),  // stdout is a pipe that the child will write to
175               2 => array('pipe', 'w')    // stderr is a file to write to
176        );
177
178        $process = proc_open(ASPELL_BIN.' -a'.$this->args, $descspec, $pipes);
179        if (is_resource($process)) {
180            //write to stdin
181            fwrite($pipes[0],$word);
182            fclose($pipes[0]);
183
184            //read stdout
185            while (!feof($pipes[1])) {
186                $out .= fread($pipes[1], 8192);
187            }
188            fclose($pipes[1]);
189
190            //read stderr
191            while (!feof($pipes[2])) {
192                $err .= fread($pipes[2], 8192);
193            }
194            fclose($pipes[2]);
195
196            if(proc_close($process) != 0){
197                //something went wrong
198                trigger_error("aspell returned an error: $err", E_USER_WARNING);
199                return array();
200            }
201
202            //parse output
203            $lines = split("\n",$out);
204            foreach ($lines as $line){
205                $line = trim($line);
206                if(empty($line))    continue;       // empty line
207                if($line[0] == '@') continue;       // comment
208                if($line[0] == '*') return true;    // no mistakes made
209                if($line[0] == '#') return array(); // mistake but no suggestions
210                if($line[0] == '&'){
211                    $line = preg_replace('/&.*?: /','',$line);
212                    return split(', ',$line);
213                }
214            }
215            return true; // shouldn't be reached
216        }
217        //opening failed
218        trigger_error("Could not run aspell '".ASPELL_BIN."'", E_USER_WARNING);
219        return array();
220    }
221
222    /**
223     * Check if a word is misspelled like pspell_check
224     *
225     * @author   Andreas Gohr <andi@splitbrain.org>
226     */
227    function check($word){
228        if(is_array($this->suggest($word))){
229            return false;
230        }else{
231            return true;
232        }
233    }
234}
235
236?>
Note: See TracBrowser for help on using the repository browser.