Jacek Kowalski
2012-08-12 6f84c60bdb704c3b038881dd8353fc9881329c87
commit | author | age
8bd4d9 1 <?php
JK 2 class BotMsgException extends Exception {}
3 interface BotMsgInterface {
4     function __construct(BotMsg $msg);
5     function __toString();
6     function sendPullResponse();
7 }
8
9 class BotMsg {
10     private $beautiful = TRUE;
11     private $parser = NULL;
12     private $html = NULL;
13     private $text = NULL;
14     private $raw = '';
15     
16     /**
17      * Włącza lub wyłącza "upiększanie" konwertowanej do czystego tekstu wiadomości, np.:
18      * <b>abc</b> zamieniane jest na *abc*
19      * <h1>efg</h1> przechodzi w = efg =
20      * Domyślnie włączone
21      * @param bool $set Ustawienie "upiększania"
22      */
23     function beautifulText($set = FALSE) {
24         $this->beautiful = (bool)$set;
25     }
26     
27     /**
28      * Konstruktor. De facto alias dla {@link BotMsg::append()}
29      */
30     function __construct($str = NULL) {
31         if($str !== NULL) {
32             $this->append($str);
33         }
34     }
35     
36     /**
37      * Serializacja klasy wymaga zapisania tylko niektórych elementów
38      */
39     function __sleep() {
40         return array('beautiful', 'raw');
41     }
42     
43     /**
44      * Alias dla {@link BotMsg::append()}
45      */
46     function a($str) {
47         $this->append($str);
48     }
49     
50     /**
51      * Dodaje kod HTML na koniec wiadomości
52      * @param string $str Treść do dodania
53      */
54     function append($str) {
55         $this->text = $this->html = $this->parser = NULL;
56         $this->raw .= (string)$str;
57     }
58     
59     /**
60      * Zwraca wiadomość jako czysty tekst
61      * @return string Wiadomość
62      */
63     function getText() {
64         if($this->text === NULL) {
65             $this->text = trim($this->parseTextDOM($this->getParser()->getElementsByTagName('body')->item(0)));
66         }
67         
68         return $this->text;
69     }
70     
71     /**
72      * Zwraca wiadomość jako kod HTML
73      * @return string Wiadomość
74      */
75     function getHTML() {
76         if($this->html === NULL) {
77             $doc = $this->getParser();
78             $this->parseHTMLDOM( $doc->getElementsByTagName('body')->item(0) );
79             $this->html = $doc->saveXML( $doc->getElementsByTagName('body')->item(0) );
80         }
81         
82         return (string)substr($this->html, 6, -7);
83     }
84     
85     /**
86      * Zwraca treść wiadomości zapisaną przy użyciu {@link BotMsg::append()} bez żadnych modyfikacji
87      * @return string Oryginalna wiadomość
88      */
89     function getRaw() {
90         return $this->raw;
91     }
92     
93     /**
94      * Zwraca wiadomość jako kod HTML
95      * @return string Wiadomość
96      */
97     function __toString() {
98         return $this->getHTML();
99     }
100     
101     /**
102      * Zwraca kopię drzewa DOM wiadomości
103      * @return DOMDocument Wiadomość
104      */
105     function getParser() {
106         if($this->parser === NULL) {
107             $this->parser = new DOMDocument('1.0', 'utf-8');
108             try {
109                 $this->parser->loadHTML('<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>'.$this->raw.'</body></html>');
110             }
111             catch(ErrorException $e) {
112                 if($e->getSeverity() != E_WARNING) {
113                     throw $e;
114                 }
115             }
116             
117             foreach($this->parser->getElementsByTagName('a') as $node) {
118                 if(!$node->hasAttribute('href')) {
119                     $node->setAttribute('href', $node->textContent);
120                 }
121             }
122         }
123         
124         return $this->parser->cloneNode(TRUE);
125     }
126     
127     private function parseTextDOM($dom) {
128         if(!($dom instanceof DOMElement)) {
129             throw new BotMsgException('Nieznany element DOM: '.get_class($dom));
130         }
131         
132         $return = '';
133         foreach($dom->childNodes as $node) {
134             if($node instanceof DOMText || $node instanceof DOMEntity) {
135                 $return .= strtr($node->nodeValue, array("\n" => '', "\r" => ''));
136             }
137             elseif($node instanceof DOMElement) {
138                 switch(strtolower($node->tagName)) {
139                     case 'b':
140                     case 'strong':
141                         $return .= ($this->beautiful ? '*' : '').$this->parseTextDOM($node).($this->beautiful ? '*' : '');
142                     break;
143                     case 'u':
144                         $return .= ($this->beautiful ? '_' : '').$this->parseTextDOM($node).($this->beautiful ? '_' : '');
145                     break;
146                     case 'i':
147                         $return .= ($this->beautiful ? '/' : '').$this->parseTextDOM($node).($this->beautiful ? '/' : '');
148                     break;
149                     case 'br':
150                         $return .= "\n";
151                     break;
152                     case 'p':
153                         if(substr($return, -1) != "\n") {
154                             $return .= "\n\n";
155                         }
156                         $return .= $this->parseTextDOM($node)."\n\n";
157                     break;
158                     case 'h1':
159                         if(substr($return, -1) != "\n") {
160                             $return .= "\n\n";
161                         }
162                         $return .= ($this->beautiful ? '= ' : '').$this->parseTextDOM($node).($this->beautiful ? ' =' : '')."\n";
163                     break;
164                     case 'h2':
165                         if(substr($return, -1) != "\n") {
166                             $return .= "\n\n";
167                         }
168                         $return .= ($this->beautiful ? '== ' : '').$this->parseTextDOM($node).($this->beautiful ? ' ==' : '')."\n";
169                     break;
170                     case 'h3':
171                         if(substr($return, -1) != "\n") {
172                             $return .= "\n\n";
173                         }
174                         $return .= ($this->beautiful ? '=== ' : '').$this->parseTextDOM($node).($this->beautiful ? ' ===' : '')."\n";
175                     break;
176                     case 'td':
177                         $return .= $this->parseTextDOM($node)."\t";
178                     break;
179                     case 'th':
180                         $return .= ($this->beautiful ? '*' : '').$this->parseTextDOM($node).($this->beautiful ? '*' : '')."\t";
181                     break;
182                     case 'tr':
183                         $return .= $this->parseTextDOM($node)."\n";
184                     break;
185                     case 'a':
186                         $return .= $this->parseTextDOM($node);
187                         
188                         if($node->getAttribute('href') != $node->textContent) {
189                             $return .= ' ('.$node->getAttribute('href').')';
190                         }
191                     break;
192                     case 'script':
193                     case 'style':
194                     case 'img':
195                     break;
196                     default:
197                         $return .= $this->parseTextDOM($node);
198                     break;
199                 }
200             }
201             else
202             {
203                 throw new BotMsgException('Nieznany element DOM: '.get_class($node));
204             }
205         }
206         
207         return $return;
208     }
209     
210     private function parseHTMLDOM($dom) {
211         if(!($dom instanceof DOMNode)) {
212             throw new BotMsgException('Nieznany element DOM: '.get_class($dom));
213         }
214         
215         foreach($dom->childNodes as $node) {
216             if($node instanceof DOMElement) {
217                 if($node->hasAttribute('color')) {
218                     $color = trim($node->getAttribute('color'));
219                     $node->removeAttribute('color');
220                     if(substr($color, 0, 1)=='#' AND (strlen($color)==4 OR strlen($color)==7) AND ctype_xdigit(substr($color, 1))) {
221                         $node->setAttribute('style', 'color:'.$color.';'.$node->getAttribute('style'));
222                     }
223                 }
224                 
225                 $this->parseHTMLDOM($node);
226             }
227         }
228     }
229 }
230 ?>