├── .gitignore ├── Makefile ├── README.md ├── bin ├── doxphp ├── doxphp2docco └── doxphp2sphinx ├── composer.json ├── examples ├── DrawerInterface.json ├── DrawerInterface.rst ├── resources │ └── doxphp.css ├── test.html ├── test.json ├── test.php └── test.rst └── lib ├── DoxPHP ├── Exception │ ├── Exception.php │ └── OutOfBoundsException.php └── Parser │ ├── Parser.php │ └── Tokens.php └── vendor └── php-markdown ├── LICENSE ├── README ├── README.markdown └── markdown.php /.gitignore: -------------------------------------------------------------------------------- 1 | doxphp-*.tgz -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VERSION="" 2 | 3 | define package_1 4 | 5 | 9 | doxphp 10 | pear.avalanche123.com 11 | 12 | Dox for PHP, inspired by Dox for JavaScript. 13 | 14 | 15 | Dox for PHP is a documentation engine for PHP inspired by the Dox for JavaScript. 16 | 17 | 18 | Bulat Shakirzyanov 19 | avalanche123 20 | mallluhuct at gmail.com 21 | yes 22 | 23 | $(shell date +%Y-%m-%d) 24 | 25 | 26 | $(VERSION) 27 | $(VERSION) 28 | 29 | 30 | beta 31 | beta 32 | 33 | MIT 34 | Early release. 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | endef 59 | 60 | define package_2 61 | 62 | 63 | 64 | 65 | 66 | 67 | 5.3.2 68 | 69 | 70 | 1.4.0 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | endef 80 | 81 | define package_3 82 | 83 | 84 | 85 | endef 86 | 87 | define composer 88 | { 89 | "name": "doxphp/doxphp", 90 | "description": "Dox for PHP, inspired by Dox for JavaScript.", 91 | "keywords": ["documentation","phpdoc", "api docs"], 92 | "homepage": "http://github.com/avalanche123/doxphp/", 93 | "version": "$(VERSION)-dev", 94 | "license": "MIT", 95 | "authors": [ 96 | { 97 | "name": "Bulat Shakirzyanov", 98 | "email": "mallluhuct@gmail.com", 99 | "homepage": "http://avalanche123.com" 100 | } 101 | ], 102 | "require": { 103 | "php": ">=5.3.2" 104 | } 105 | } 106 | endef 107 | 108 | export package_1 package_2 package_3 composer 109 | 110 | .PHONY: package release clean 111 | 112 | clean: 113 | git clean -df 114 | 115 | package: 116 | echo "$$package_1" > package.xml 117 | git ls-files lib | while read line; do echo " " >> package.xml; done; 118 | echo "$$package_2" >> package.xml 119 | git ls-files lib | while read line; do echo " " >> package.xml; done; 120 | echo "$$package_3" >> package.xml 121 | pear package 122 | rm -f package.xml 123 | 124 | release: 125 | echo "$$composer" > composer.json 126 | git add composer.json; git commit -m "updated composer.json for $(VERSION) release" 127 | @echo "composer.json updated" 128 | make package 129 | @echo "a new package docco-$(VERSION).tgz has been created" 130 | git tag v$(VERSION) -m "release v$(VERSION)" 131 | @echo "tag v$(VERSION) created" 132 | git push; git push --tags 133 | @echo "code pushed" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dox PHP 2 | 3 | Dox for PHP is a documentation engine for PHP inspired by the [Dox](https://github.com/visionmedia/dox) for JavaScript. 4 | 5 | # Installation 6 | 7 | ## Pear 8 | 9 | ```console 10 | pear channel-discover pear.avalanche123.com 11 | pear install avalanche123/doxphp-beta 12 | ``` 13 | 14 | ## Github 15 | 16 | Clone this repository and put the `doxphp` under bin directory in your executable path. 17 | 18 | # Usage 19 | 20 | Dox PHP operates over stdio: 21 | 22 | ```shell 23 | $ doxphp < test.php 24 | ...JSON... 25 | ``` 26 | 27 | test.php 28 | 29 | ```php 30 | (http://avalanche123.com) 36 | * 37 | * @param string $world - the world to greet 38 | * 39 | * @return void 40 | */ 41 | function hello($world) { 42 | echo "Hello ${world}"; 43 | } 44 | ``` 45 | 46 | output 47 | 48 | ```js 49 | [ 50 | { 51 | "tags": [ 52 | { 53 | "type" : "author" 54 | , "email" : "mallluhuct@gmail.com" 55 | , "website": "http:\/\/avalanche123.com" 56 | , "name" : "Bulat Shakirzyanov" 57 | } 58 | , { 59 | "type" : "param" 60 | , "types" : [ "string" ] 61 | , "name" : "world" 62 | , "description": "- the world to greet" 63 | } 64 | , { 65 | "type" : "return" 66 | , "types": [ "void" ] 67 | } 68 | ] 69 | , "description": "Greets the world" 70 | , "isPrivate" : false 71 | , "isProtected": false 72 | , "isPublic" : true 73 | , "isAbstract" : false 74 | , "isFinal" : false 75 | , "isStatic" : false 76 | , "code" : "function hello($world)" 77 | , "type" : "function" 78 | , "name" : "hello()" 79 | , "line" : 12 80 | } 81 | ] 82 | ``` 83 | 84 | # Supports 85 | 86 | * classes and interfaces 87 | * functions and methods (produces slightly different results) 88 | * namespaces (who phpdocs them really?) 89 | * class variables and constants (sweet!) 90 | 91 | # Installation 92 | 93 | Use pear to install 94 | 95 | ```console 96 | pear channel-discover pear.avalanche123.com 97 | pear install avalanche123/doxphp-alpha 98 | ``` 99 | 100 | # Renderers 101 | 102 | * doxphp2sphinx 103 | 104 | ```console 105 | doxphp < test.php | doxphp2sphinx > test.rst 106 | ``` 107 | 108 | test.rst: 109 | 110 | ```rst 111 | .. php:function:: hello 112 | 113 | Greets the world 114 | 115 | :param string $world: - the world to greet 116 | 117 | :returns void: 118 | ``` 119 | 120 | * doxphp2docco 121 | 122 | ```console 123 | doxphp2docco *.php 124 | ``` 125 | 126 | this creates `docs` directory in the current directory and populates it with html files. 127 | 128 | test.html: 129 | 130 | ```html 131 | 132 | 133 | 134 | 135 | test.php 136 | 137 | 138 | 139 | 140 |
141 |
142 | 143 | 144 | 145 | 148 | 150 | 151 | 152 | 153 | 154 | 160 | 172 | 173 | 174 |
146 |

test.php

147 |
149 |
155 |
156 | 157 |
158 |

Greets the world

159 |
161 |
162 |
163 |                 <?php
164 |                 function
165 |                 hello
166 |                 (
167 |                 $world
168 |                 )
169 |               
170 |
171 |
175 |
176 | 177 | 178 | ``` 179 | 180 | # TODO 181 | 182 | implement more renderers 183 | -------------------------------------------------------------------------------- /bin/doxphp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | parse($tokens)); 23 | } catch (Exception $e) { 24 | echo $e->getMessage(); 25 | exit(1); 26 | } 27 | -------------------------------------------------------------------------------- /bin/doxphp2docco: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | array("pipe", "r"), 27 | 1 => array("pipe", "w") 28 | ); 29 | if (is_resource($process = proc_open('pygmentize -l php -f html', $descriptors, $pipes))) { 30 | fwrite($pipes[0], " 56 | /*--------------------- Layout and Typography ----------------------------*/ 57 | body { 58 | font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif; 59 | font-size: 15px; 60 | line-height: 22px; 61 | color: #252519; 62 | margin: 0; padding: 0; 63 | } 64 | a { 65 | color: #261a3b; 66 | } 67 | a:visited { 68 | color: #261a3b; 69 | } 70 | p { 71 | margin: 0 0 15px 0; 72 | } 73 | h1, h2, h3, h4, h5, h6 { 74 | margin: 0px 0 15px 0; 75 | } 76 | h1 { 77 | margin-top: 40px; 78 | } 79 | #container { 80 | position: relative; 81 | } 82 | #background { 83 | position: fixed; 84 | top: 0; left: 525px; right: 0; bottom: 0; 85 | background: #f5f5ff; 86 | border-left: 1px solid #e5e5ee; 87 | z-index: -1; 88 | } 89 | #jump_to, #jump_page { 90 | background: white; 91 | -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777; 92 | -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px; 93 | font: 10px Arial; 94 | text-transform: uppercase; 95 | cursor: pointer; 96 | text-align: right; 97 | } 98 | #jump_to, #jump_wrapper { 99 | position: fixed; 100 | right: 0; top: 0; 101 | padding: 5px 10px; 102 | } 103 | #jump_wrapper { 104 | padding: 0; 105 | display: none; 106 | } 107 | #jump_to:hover #jump_wrapper { 108 | display: block; 109 | } 110 | #jump_page { 111 | padding: 5px 0 3px; 112 | margin: 0 0 25px 25px; 113 | } 114 | #jump_page .source { 115 | display: block; 116 | padding: 5px 10px; 117 | text-decoration: none; 118 | border-top: 1px solid #eee; 119 | } 120 | #jump_page .source:hover { 121 | background: #f5f5ff; 122 | } 123 | #jump_page .source:first-child { 124 | } 125 | table td { 126 | border: 0; 127 | outline: 0; 128 | } 129 | td.docs, th.docs { 130 | max-width: 450px; 131 | min-width: 450px; 132 | min-height: 5px; 133 | padding: 10px 25px 1px 50px; 134 | overflow-x: hidden; 135 | vertical-align: top; 136 | text-align: left; 137 | } 138 | .docs pre { 139 | margin: 15px 0 15px; 140 | padding-left: 15px; 141 | } 142 | .docs p tt, .docs p code { 143 | background: #f8f8ff; 144 | border: 1px solid #dedede; 145 | font-size: 12px; 146 | padding: 0 0.2em; 147 | } 148 | .pilwrap { 149 | position: relative; 150 | } 151 | .pilcrow { 152 | font: 12px Arial; 153 | text-decoration: none; 154 | color: #454545; 155 | position: absolute; 156 | top: 3px; left: -20px; 157 | padding: 1px 2px; 158 | opacity: 0; 159 | -webkit-transition: opacity 0.2s linear; 160 | } 161 | td.docs:hover .pilcrow { 162 | opacity: 1; 163 | } 164 | td.code, th.code { 165 | padding: 14px 15px 16px 25px; 166 | width: 100%; 167 | vertical-align: top; 168 | background: #f5f5ff; 169 | border-left: 1px solid #e5e5ee; 170 | } 171 | pre, tt, code { 172 | font-size: 12px; line-height: 18px; 173 | font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace; 174 | margin: 0; padding: 0; 175 | } 176 | 177 | 178 | /*---------------------- Syntax Highlighting -----------------------------*/ 179 | td.linenos { background-color: #f0f0f0; padding-right: 10px; } 180 | span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; } 181 | body .hll { background-color: #ffffcc } 182 | body .c { color: #408080; font-style: italic } /* Comment */ 183 | body .err { border: 1px solid #FF0000 } /* Error */ 184 | body .k { color: #954121 } /* Keyword */ 185 | body .o { color: #666666 } /* Operator */ 186 | body .cm { color: #408080; font-style: italic } /* Comment.Multiline */ 187 | body .cp { color: #BC7A00; display:none; } /* Comment.Preproc */ 188 | body .c1 { color: #408080; font-style: italic } /* Comment.Single */ 189 | body .cs { color: #408080; font-style: italic } /* Comment.Special */ 190 | body .gd { color: #A00000 } /* Generic.Deleted */ 191 | body .ge { font-style: italic } /* Generic.Emph */ 192 | body .gr { color: #FF0000 } /* Generic.Error */ 193 | body .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 194 | body .gi { color: #00A000 } /* Generic.Inserted */ 195 | body .go { color: #808080 } /* Generic.Output */ 196 | body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ 197 | body .gs { font-weight: bold } /* Generic.Strong */ 198 | body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 199 | body .gt { color: #0040D0 } /* Generic.Traceback */ 200 | body .kc { color: #954121 } /* Keyword.Constant */ 201 | body .kd { color: #954121; font-weight: bold } /* Keyword.Declaration */ 202 | body .kn { color: #954121; font-weight: bold } /* Keyword.Namespace */ 203 | body .kp { color: #954121 } /* Keyword.Pseudo */ 204 | body .kr { color: #954121; font-weight: bold } /* Keyword.Reserved */ 205 | body .kt { color: #B00040 } /* Keyword.Type */ 206 | body .m { color: #666666 } /* Literal.Number */ 207 | body .s { color: #219161 } /* Literal.String */ 208 | body .na { color: #7D9029 } /* Name.Attribute */ 209 | body .nb { color: #954121 } /* Name.Builtin */ 210 | body .nc { color: #0000FF; font-weight: bold } /* Name.Class */ 211 | body .no { color: #880000 } /* Name.Constant */ 212 | body .nd { color: #AA22FF } /* Name.Decorator */ 213 | body .ni { color: #999999; font-weight: bold } /* Name.Entity */ 214 | body .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ 215 | body .nf { color: #0000FF } /* Name.Function */ 216 | body .nl { color: #A0A000 } /* Name.Label */ 217 | body .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ 218 | body .nt { color: #954121; font-weight: bold } /* Name.Tag */ 219 | body .nv { color: #19469D } /* Name.Variable */ 220 | body .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ 221 | body .w { color: #bbbbbb } /* Text.Whitespace */ 222 | body .mf { color: #666666 } /* Literal.Number.Float */ 223 | body .mh { color: #666666 } /* Literal.Number.Hex */ 224 | body .mi { color: #666666 } /* Literal.Number.Integer */ 225 | body .mo { color: #666666 } /* Literal.Number.Oct */ 226 | body .sb { color: #219161 } /* Literal.String.Backtick */ 227 | body .sc { color: #219161 } /* Literal.String.Char */ 228 | body .sd { color: #219161; font-style: italic } /* Literal.String.Doc */ 229 | body .s2 { color: #219161 } /* Literal.String.Double */ 230 | body .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ 231 | body .sh { color: #219161 } /* Literal.String.Heredoc */ 232 | body .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ 233 | body .sx { color: #954121 } /* Literal.String.Other */ 234 | body .sr { color: #BB6688 } /* Literal.String.Regex */ 235 | body .s1 { color: #219161 } /* Literal.String.Single */ 236 | body .ss { color: #19469D } /* Literal.String.Symbol */ 237 | body .bp { color: #954121 } /* Name.Builtin.Pseudo */ 238 | body .vc { color: #19469D } /* Name.Variable.Class */ 239 | body .vg { color: #19469D } /* Name.Variable.Global */ 240 | body .vi { color: #19469D } /* Name.Variable.Instance */ 241 | body .il { color: #666666 } /* Literal.Number.Integer.Long */ 242 | basename($file), 253 | 'dir' => $dir, 254 | 'name' => $dir."/".pathinfo($file, PATHINFO_FILENAME).".html", 255 | 'level' => substr_count($file, '/'), 256 | 'blocks' => array_filter($parser->parse($tokens), function($block) { 257 | return $block->isPublic; 258 | }) 259 | ); 260 | }, array_slice($argv, 1)); 261 | 262 | ob_start(); 263 | ?> 264 | 265 | "> 266 | 267 | 274 | 275 | 276 | 277 | 278 | <?php echo $info['title']; ?> 279 | 280 | resources/doxphp.css" /> 281 | 282 | 283 |
284 |
285 | 1): ?> 286 |
287 | Jump To … 288 |
289 |
290 | 291 |
292 |
293 |
294 | 295 | 296 | 297 | 298 | 303 | 305 | 306 | 307 | 308 | $block): ?> 309 | 310 | 316 | 317 | 318 | 319 | 320 |
299 |

300 | 301 |

302 |
304 |
311 |
312 | 313 |
314 | description); ?> 315 |
code); ?>
321 |
322 | 323 | 324 | tags as $tag) { 17 | if ($tag->type == "ignore") { 18 | // Ignore this block because an @ignore tag was found 19 | $ignore = true; 20 | break; 21 | } 22 | } 23 | 24 | if ($block->isPrivate || $ignore) { 25 | continue; 26 | } 27 | 28 | if (in_array($block->type, array('function', 'namespace'))) { 29 | $level = 0; 30 | } else { 31 | $level = 1; 32 | } 33 | 34 | $prefix = str_repeat($indent, $level); 35 | 36 | if (!in_array($block->type, array('function', 'namespace', 'class', 'interface'))) { 37 | $out .= $prefix; 38 | } 39 | 40 | if ("variable" === $block->type) { 41 | $out .= ".. php:attr:: ".$block->name."\n\n"; 42 | } elseif ("constant" === $block->type) { 43 | $out .= ".. php:const:: ".$block->name."\n\n"; 44 | } else { 45 | $out .= ".. php:".$block->type.":: ".$block->name."\n\n"; 46 | } 47 | 48 | // no description for namespace 49 | if ("namespace" === $block->type) { 50 | continue; 51 | } 52 | 53 | if (!empty($block->description)) { 54 | // escape slashes 55 | $description = str_replace("\\", "\\\\", $prefix.$indent.$block->description); 56 | // indent new lines 57 | $description = preg_replace('/([\\n\\r]+)/', '$1'.$prefix.$indent, $description); 58 | 59 | $out .= $description."\n\n"; 60 | } 61 | 62 | foreach ($block->tags as $tag) { 63 | if ("param" !== $tag->type) { 64 | $out .= "\n"; 65 | } 66 | 67 | $out .= $prefix.$indent.":".$tag->type; 68 | 69 | if ("return" === $tag->type) { 70 | $out .= "s"; 71 | } 72 | 73 | if ("param" !== $tag->type) { 74 | $out .= ":"; 75 | } 76 | 77 | if (isset($tag->types)) { 78 | $out .= " ".str_replace("\\", "\\\\", implode("|", $tag->types)); 79 | } 80 | 81 | if (isset($tag->name)) { 82 | $out .= " $".$tag->name; 83 | } 84 | 85 | if ("param" === $tag->type) { 86 | $out .= ":"; 87 | } 88 | 89 | if (isset($tag->description)) { 90 | $lines = str_replace("\\", "\\\\", explode("\n", $tag->description)); 91 | $out .= " ".$lines[0]; 92 | } 93 | 94 | $out .= "\n"; 95 | } 96 | $out .= "\n"; 97 | } 98 | 99 | echo trim(str_replace("\n\n\n", "\n\n", $out)); -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "doxphp/doxphp", 3 | "description": "Dox for PHP, inspired by Dox for JavaScript.", 4 | "keywords": ["documentation","phpdoc", "api docs"], 5 | "bin": ["bin/doxphp", "bin/doxphp2docco", "bin/doxphp2sphinx"], 6 | "homepage": "http://github.com/avalanche123/doxphp/", 7 | "version": "0.2.0-dev", 8 | "license": "MIT", 9 | "authors": [ 10 | { 11 | "name": "Bulat Shakirzyanov", 12 | "email": "mallluhuct@gmail.com", 13 | "homepage": "http://avalanche123.com" 14 | } 15 | ], 16 | "require": { 17 | "php": ">=5.3.2" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/DrawerInterface.json: -------------------------------------------------------------------------------- 1 | [{"tags":[],"description":"","isPrivate":false,"isProtected":false,"isPublic":true,"isAbstract":false,"isFinal":false,"isStatic":false,"code":"namespace Imagine\\Draw","type":"namespace","name":"Imagine\\Draw"},{"tags":[],"description":"","isPrivate":false,"isProtected":false,"isPublic":true,"isAbstract":false,"isFinal":false,"isStatic":false,"code":"interface DrawerInterface","type":"interface","name":"DrawerInterface"},{"tags":[{"type":"param","types":["Imagine\\Image\\PointInterface"],"name":"center"},{"type":"param","types":["Imagine\\Image\\BoxInterface"],"name":"size"},{"type":"param","types":["integer"],"name":"start"},{"type":"param","types":["integer"],"name":"end"},{"type":"param","types":["Imagine\\Image\\Color"],"name":"color"},{"type":"throws","types":["Imagine\\Exception\\RuntimeException"]},{"type":"return","types":["Imagine\\Draw\\DrawerInterface"]}],"description":"Draws an arc on a starting at a given x, y coordinates under a given\nstart and end angles","isPrivate":false,"isProtected":false,"isPublic":true,"isAbstract":false,"isFinal":false,"isStatic":false,"code":"function arc(PointInterface $center, BoxInterface $size, $start, $end, Color $color)","type":"method","name":"arc"},{"tags":[{"type":"param","types":["Imagine\\Image\\PointInterface"],"name":"center"},{"type":"param","types":["Imagine\\Image\\BoxInterface"],"name":"size"},{"type":"param","types":["integer"],"name":"start"},{"type":"param","types":["integer"],"name":"end"},{"type":"param","types":["Imagine\\Image\\Color"],"name":"color"},{"type":"param","types":["Boolean"],"name":"fill"},{"type":"throws","types":["Imagine\\Exception\\RuntimeException"]},{"type":"return","types":["Imagine\\Draw\\DrawerInterface"]}],"description":"Same as arc, but also connects end points with a straight line","isPrivate":false,"isProtected":false,"isPublic":true,"isAbstract":false,"isFinal":false,"isStatic":false,"code":"function chord(PointInterface $center, BoxInterface $size, $start, $end, Color $color, $fill = false)","type":"method","name":"chord"},{"tags":[{"type":"param","types":["Imagine\\Image\\PointInterface"],"name":"center"},{"type":"param","types":["Imagine\\Image\\BoxInterface"],"name":"size"},{"type":"param","types":["Imagine\\Image\\Color"],"name":"color"},{"type":"param","types":["Boolean"],"name":"fill"},{"type":"throws","types":["Imagine\\Exception\\RuntimeException"]},{"type":"return","types":["Imagine\\Draw\\DrawerInterface"]}],"description":"Draws and ellipse with center at the given x, y coordinates, and given\nwidth and height","isPrivate":false,"isProtected":false,"isPublic":true,"isAbstract":false,"isFinal":false,"isStatic":false,"code":"function ellipse(PointInterface $center, BoxInterface $size, Color $color, $fill = false)","type":"method","name":"ellipse"},{"tags":[{"type":"param","types":["Imagine\\Image\\PointInterface"],"name":"start"},{"type":"param","types":["Imagine\\Image\\PointInterface"],"name":"end"},{"type":"param","types":["Imagine\\Image\\Color"],"name":"outline"},{"type":"return","types":["Imagine\\Draw\\DrawerInterface"]}],"description":"Draws a line from start(x, y) to end(x, y) coordinates","isPrivate":false,"isProtected":false,"isPublic":true,"isAbstract":false,"isFinal":false,"isStatic":false,"code":"function line(PointInterface $start, PointInterface $end, Color $outline)","type":"method","name":"line"},{"tags":[{"type":"param","types":["Imagine\\Image\\PointInterface"],"name":"center"},{"type":"param","types":["Imagine\\Image\\BoxInterface"],"name":"size"},{"type":"param","types":["integer"],"name":"start"},{"type":"param","types":["integer"],"name":"end"},{"type":"param","types":["Imagine\\Image\\Color"],"name":"color"},{"type":"param","types":["Boolean"],"name":"fill"},{"type":"throws","types":["Imagine\\Exception\\RuntimeException"]},{"type":"return","types":["Imagine\\Draw\\DrawerInterface"]}],"description":"Same as arc, but connects end points and the center","isPrivate":false,"isProtected":false,"isPublic":true,"isAbstract":false,"isFinal":false,"isStatic":false,"code":"function pieSlice(PointInterface $center, BoxInterface $size, $start, $end, Color $color, $fill = false)","type":"method","name":"pieSlice"},{"tags":[{"type":"param","types":["Imagine\\Image\\PointInterface"],"name":"position"},{"type":"param","types":["Imagine\\Image\\Color"],"name":"color"},{"type":"throws","types":["Imagine\\Exception\\RuntimeException"]},{"type":"return","types":["Imagine\\Draw\\DrawerInterface"]}],"description":"Places a one pixel point at specific coordinates and fills it with\nspecified color","isPrivate":false,"isProtected":false,"isPublic":true,"isAbstract":false,"isFinal":false,"isStatic":false,"code":"function dot(PointInterface $position, Color $color)","type":"method","name":"dot"},{"tags":[{"type":"param","types":["array"],"name":"coordinates"},{"type":"param","types":["Imagine\\Image\\Color"],"name":"color"},{"type":"param","types":["Boolean"],"name":"fill"},{"type":"throws","types":["Imagine\\Exception\\RuntimeException"]},{"type":"return","types":["Imagine\\Draw\\DrawerInterface"]}],"description":"Draws a polygon using array of x, y coordinates. Must contain at least\nthree coordinates","isPrivate":false,"isProtected":false,"isPublic":true,"isAbstract":false,"isFinal":false,"isStatic":false,"code":"function polygon(array $coordinates, Color $color, $fill = false)","type":"method","name":"polygon"},{"tags":[{"type":"param","types":["string"],"name":"string"},{"type":"param","types":["Imagine\\Image\\AbstractFont"],"name":"font"},{"type":"param","types":["Imagine\\Image\\PointInterface"],"name":"position"},{"type":"param","types":["integer"],"name":"angle"},{"type":"throws","types":["Imagine\\Exception\\RuntimeException"]},{"type":"return","types":["Imagine\\Draw\\DrawerInterface"]}],"description":"Annotates image with specified text at a given position starting on the\ntop left of the final text box\n\nThe rotation is done CW","isPrivate":false,"isProtected":false,"isPublic":true,"isAbstract":false,"isFinal":false,"isStatic":false,"code":"function text($string, AbstractFont $font, PointInterface $position, $angle = 0)","type":"method","name":"text"}] -------------------------------------------------------------------------------- /examples/DrawerInterface.rst: -------------------------------------------------------------------------------- 1 | .. php:namespace:: Imagine\Draw 2 | 3 | .. php:interface:: DrawerInterface 4 | 5 | 6 | .. php:method:: arc 7 | 8 | Draws an arc on a starting at a given x, y coordinates under a given 9 | start and end angles 10 | 11 | :param Imagine\Image\PointInterface $center: 12 | :param Imagine\Image\BoxInterface $size: 13 | :param integer $start: 14 | :param integer $end: 15 | :param Imagine\Image\Color $color: 16 | 17 | :throws Imagine\Exception\RuntimeException: 18 | 19 | :returns Imagine\Draw\DrawerInterface: 20 | 21 | .. php:method:: chord 22 | 23 | Same as arc, but also connects end points with a straight line 24 | 25 | :param Imagine\Image\PointInterface $center: 26 | :param Imagine\Image\BoxInterface $size: 27 | :param integer $start: 28 | :param integer $end: 29 | :param Imagine\Image\Color $color: 30 | :param Boolean $fill: 31 | 32 | :throws Imagine\Exception\RuntimeException: 33 | 34 | :returns Imagine\Draw\DrawerInterface: 35 | 36 | .. php:method:: ellipse 37 | 38 | Draws and ellipse with center at the given x, y coordinates, and given 39 | width and height 40 | 41 | :param Imagine\Image\PointInterface $center: 42 | :param Imagine\Image\BoxInterface $size: 43 | :param Imagine\Image\Color $color: 44 | :param Boolean $fill: 45 | 46 | :throws Imagine\Exception\RuntimeException: 47 | 48 | :returns Imagine\Draw\DrawerInterface: 49 | 50 | .. php:method:: line 51 | 52 | Draws a line from start(x, y) to end(x, y) coordinates 53 | 54 | :param Imagine\Image\PointInterface $start: 55 | :param Imagine\Image\PointInterface $end: 56 | :param Imagine\Image\Color $outline: 57 | 58 | :returns Imagine\Draw\DrawerInterface: 59 | 60 | .. php:method:: pieSlice 61 | 62 | Same as arc, but connects end points and the center 63 | 64 | :param Imagine\Image\PointInterface $center: 65 | :param Imagine\Image\BoxInterface $size: 66 | :param integer $start: 67 | :param integer $end: 68 | :param Imagine\Image\Color $color: 69 | :param Boolean $fill: 70 | 71 | :throws Imagine\Exception\RuntimeException: 72 | 73 | :returns Imagine\Draw\DrawerInterface: 74 | 75 | .. php:method:: dot 76 | 77 | Places a one pixel point at specific coordinates and fills it with 78 | specified color 79 | 80 | :param Imagine\Image\PointInterface $position: 81 | :param Imagine\Image\Color $color: 82 | 83 | :throws Imagine\Exception\RuntimeException: 84 | 85 | :returns Imagine\Draw\DrawerInterface: 86 | 87 | .. php:method:: polygon 88 | 89 | Draws a polygon using array of x, y coordinates. Must contain at least 90 | three coordinates 91 | 92 | :param array $coordinates: 93 | :param Imagine\Image\Color $color: 94 | :param Boolean $fill: 95 | 96 | :throws Imagine\Exception\RuntimeException: 97 | 98 | :returns Imagine\Draw\DrawerInterface: 99 | 100 | .. php:method:: text 101 | 102 | Annotates image with specified text at a given position starting on the 103 | top left of the final text box 104 | 105 | The rotation is done CW 106 | 107 | :param string $string: 108 | :param Imagine\Image\AbstractFont $font: 109 | :param Imagine\Image\PointInterface $position: 110 | :param integer $angle: 111 | 112 | :throws Imagine\Exception\RuntimeException: 113 | 114 | :returns Imagine\Draw\DrawerInterface: -------------------------------------------------------------------------------- /examples/resources/doxphp.css: -------------------------------------------------------------------------------- 1 | /*--------------------- Layout and Typography ----------------------------*/ 2 | body { 3 | font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif; 4 | font-size: 15px; 5 | line-height: 22px; 6 | color: #252519; 7 | margin: 0; padding: 0; 8 | } 9 | a { 10 | color: #261a3b; 11 | } 12 | a:visited { 13 | color: #261a3b; 14 | } 15 | p { 16 | margin: 0 0 15px 0; 17 | } 18 | h1, h2, h3, h4, h5, h6 { 19 | margin: 0px 0 15px 0; 20 | } 21 | h1 { 22 | margin-top: 40px; 23 | } 24 | #container { 25 | position: relative; 26 | } 27 | #background { 28 | position: fixed; 29 | top: 0; left: 525px; right: 0; bottom: 0; 30 | background: #f5f5ff; 31 | border-left: 1px solid #e5e5ee; 32 | z-index: -1; 33 | } 34 | #jump_to, #jump_page { 35 | background: white; 36 | -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777; 37 | -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px; 38 | font: 10px Arial; 39 | text-transform: uppercase; 40 | cursor: pointer; 41 | text-align: right; 42 | } 43 | #jump_to, #jump_wrapper { 44 | position: fixed; 45 | right: 0; top: 0; 46 | padding: 5px 10px; 47 | } 48 | #jump_wrapper { 49 | padding: 0; 50 | display: none; 51 | } 52 | #jump_to:hover #jump_wrapper { 53 | display: block; 54 | } 55 | #jump_page { 56 | padding: 5px 0 3px; 57 | margin: 0 0 25px 25px; 58 | } 59 | #jump_page .source { 60 | display: block; 61 | padding: 5px 10px; 62 | text-decoration: none; 63 | border-top: 1px solid #eee; 64 | } 65 | #jump_page .source:hover { 66 | background: #f5f5ff; 67 | } 68 | #jump_page .source:first-child { 69 | } 70 | table td { 71 | border: 0; 72 | outline: 0; 73 | } 74 | td.docs, th.docs { 75 | max-width: 450px; 76 | min-width: 450px; 77 | min-height: 5px; 78 | padding: 10px 25px 1px 50px; 79 | overflow-x: hidden; 80 | vertical-align: top; 81 | text-align: left; 82 | } 83 | .docs pre { 84 | margin: 15px 0 15px; 85 | padding-left: 15px; 86 | } 87 | .docs p tt, .docs p code { 88 | background: #f8f8ff; 89 | border: 1px solid #dedede; 90 | font-size: 12px; 91 | padding: 0 0.2em; 92 | } 93 | .pilwrap { 94 | position: relative; 95 | } 96 | .pilcrow { 97 | font: 12px Arial; 98 | text-decoration: none; 99 | color: #454545; 100 | position: absolute; 101 | top: 3px; left: -20px; 102 | padding: 1px 2px; 103 | opacity: 0; 104 | -webkit-transition: opacity 0.2s linear; 105 | } 106 | td.docs:hover .pilcrow { 107 | opacity: 1; 108 | } 109 | td.code, th.code { 110 | padding: 14px 15px 16px 25px; 111 | width: 100%; 112 | vertical-align: top; 113 | background: #f5f5ff; 114 | border-left: 1px solid #e5e5ee; 115 | } 116 | pre, tt, code { 117 | font-size: 12px; line-height: 18px; 118 | font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace; 119 | margin: 0; padding: 0; 120 | } 121 | 122 | 123 | /*---------------------- Syntax Highlighting -----------------------------*/ 124 | td.linenos { background-color: #f0f0f0; padding-right: 10px; } 125 | span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; } 126 | body .hll { background-color: #ffffcc } 127 | body .c { color: #408080; font-style: italic } /* Comment */ 128 | body .err { border: 1px solid #FF0000 } /* Error */ 129 | body .k { color: #954121 } /* Keyword */ 130 | body .o { color: #666666 } /* Operator */ 131 | body .cm { color: #408080; font-style: italic } /* Comment.Multiline */ 132 | body .cp { color: #BC7A00; display:none; } /* Comment.Preproc */ 133 | body .c1 { color: #408080; font-style: italic } /* Comment.Single */ 134 | body .cs { color: #408080; font-style: italic } /* Comment.Special */ 135 | body .gd { color: #A00000 } /* Generic.Deleted */ 136 | body .ge { font-style: italic } /* Generic.Emph */ 137 | body .gr { color: #FF0000 } /* Generic.Error */ 138 | body .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 139 | body .gi { color: #00A000 } /* Generic.Inserted */ 140 | body .go { color: #808080 } /* Generic.Output */ 141 | body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ 142 | body .gs { font-weight: bold } /* Generic.Strong */ 143 | body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 144 | body .gt { color: #0040D0 } /* Generic.Traceback */ 145 | body .kc { color: #954121 } /* Keyword.Constant */ 146 | body .kd { color: #954121; font-weight: bold } /* Keyword.Declaration */ 147 | body .kn { color: #954121; font-weight: bold } /* Keyword.Namespace */ 148 | body .kp { color: #954121 } /* Keyword.Pseudo */ 149 | body .kr { color: #954121; font-weight: bold } /* Keyword.Reserved */ 150 | body .kt { color: #B00040 } /* Keyword.Type */ 151 | body .m { color: #666666 } /* Literal.Number */ 152 | body .s { color: #219161 } /* Literal.String */ 153 | body .na { color: #7D9029 } /* Name.Attribute */ 154 | body .nb { color: #954121 } /* Name.Builtin */ 155 | body .nc { color: #0000FF; font-weight: bold } /* Name.Class */ 156 | body .no { color: #880000 } /* Name.Constant */ 157 | body .nd { color: #AA22FF } /* Name.Decorator */ 158 | body .ni { color: #999999; font-weight: bold } /* Name.Entity */ 159 | body .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ 160 | body .nf { color: #0000FF } /* Name.Function */ 161 | body .nl { color: #A0A000 } /* Name.Label */ 162 | body .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ 163 | body .nt { color: #954121; font-weight: bold } /* Name.Tag */ 164 | body .nv { color: #19469D } /* Name.Variable */ 165 | body .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ 166 | body .w { color: #bbbbbb } /* Text.Whitespace */ 167 | body .mf { color: #666666 } /* Literal.Number.Float */ 168 | body .mh { color: #666666 } /* Literal.Number.Hex */ 169 | body .mi { color: #666666 } /* Literal.Number.Integer */ 170 | body .mo { color: #666666 } /* Literal.Number.Oct */ 171 | body .sb { color: #219161 } /* Literal.String.Backtick */ 172 | body .sc { color: #219161 } /* Literal.String.Char */ 173 | body .sd { color: #219161; font-style: italic } /* Literal.String.Doc */ 174 | body .s2 { color: #219161 } /* Literal.String.Double */ 175 | body .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ 176 | body .sh { color: #219161 } /* Literal.String.Heredoc */ 177 | body .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ 178 | body .sx { color: #954121 } /* Literal.String.Other */ 179 | body .sr { color: #BB6688 } /* Literal.String.Regex */ 180 | body .s1 { color: #219161 } /* Literal.String.Single */ 181 | body .ss { color: #19469D } /* Literal.String.Symbol */ 182 | body .bp { color: #954121 } /* Name.Builtin.Pseudo */ 183 | body .vc { color: #19469D } /* Name.Variable.Class */ 184 | body .vg { color: #19469D } /* Name.Variable.Global */ 185 | body .vi { color: #19469D } /* Name.Variable.Instance */ 186 | body .il { color: #666666 } /* Literal.Number.Integer.Long */ 187 | -------------------------------------------------------------------------------- /examples/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | test.php 6 | 7 | 8 | 9 | 10 |
11 |
12 | 13 | 14 | 15 | 19 | 21 | 22 | 23 | 24 | 25 | 31 | 35 | 36 | 37 |
16 |

17 | test.php

18 |
20 |
26 |
27 | 28 |
29 |

Greets the world

30 |
<?php
32 | function hello($world)
33 | 
34 |
38 |
39 | 40 | 41 | -------------------------------------------------------------------------------- /examples/test.json: -------------------------------------------------------------------------------- 1 | [{"tags":[{"type":"author","email":"mallluhuct@gmail.com","website":"http:\/\/avalanche123.com","name":"Bulat Shakirzyanov"},{"type":"param","types":["string"],"name":"world","description":"- the world to greet"},{"type":"return","types":["void"]}],"description":"Greets the world","isPrivate":false,"isProtected":false,"isPublic":true,"isAbstract":false,"isFinal":false,"isStatic":false,"code":"function hello($world)","type":"function","name":"hello()"}] -------------------------------------------------------------------------------- /examples/test.php: -------------------------------------------------------------------------------- 1 | (http://avalanche123.com) 7 | * 8 | * @param string $world - the world to greet 9 | * 10 | * @return void 11 | */ 12 | function hello($world) { 13 | echo "Hello ${world}"; 14 | } 15 | -------------------------------------------------------------------------------- /examples/test.rst: -------------------------------------------------------------------------------- 1 | .. php:function:: hello 2 | 3 | Greets the world 4 | 5 | :param string $world: - the world to greet 6 | 7 | :returns void: -------------------------------------------------------------------------------- /lib/DoxPHP/Exception/Exception.php: -------------------------------------------------------------------------------- 1 | (http://avalanche123.com) 11 | * 12 | * asd 13 | */ 14 | class Parser 15 | { 16 | /** 17 | * Parses given Tokens object and returns array of parsed blocks 18 | * 19 | * @param DoxPHP\Parser\Tokens $tokens - tokens to parse 20 | * 21 | * @return array 22 | */ 23 | public function parse(Tokens $tokens) 24 | { 25 | $this->blocks = array(); 26 | $tokens->rewind(); 27 | 28 | while($tokens->valid()) { 29 | $block = (object) array( 30 | "tags" => array(), 31 | "description" => '', 32 | "isPrivate" => false, 33 | "isProtected" => false, 34 | "isPublic" => false, 35 | "isAbstract" => false, 36 | "isFinal" => false, 37 | "isStatic" => false, 38 | "code" => '', 39 | "type" => '', 40 | "name" => '', 41 | ); 42 | $this->blocks[] = $block; 43 | 44 | $this->skipWhitespace($tokens); 45 | $this->parseBlock($tokens, $block); 46 | } 47 | 48 | return $this->blocks; 49 | } 50 | 51 | private function skipWhitespace(Tokens $tokens) 52 | { 53 | do { 54 | $tokens->next(); 55 | $token = $tokens->current(); 56 | } while ($tokens->valid() && in_array($token->name, array("T_WHITESPACE", "T_OPEN_TAG"))); 57 | } 58 | 59 | private function parseBlock(Tokens $tokens, \stdClass $block) 60 | { 61 | $token = $tokens->current(); 62 | 63 | if ("T_DOC_COMMENT" === $token->name) { 64 | // parse all comments, last one before code wins 65 | $this->parseComment($token, $block); 66 | $this->skipWhitespace($tokens); 67 | $token = $tokens->current(); 68 | } 69 | if ("T_ABSTRACT" === $token->name) { 70 | $block->isAbstract = true; 71 | $block->code .= $token->value." "; 72 | $this->skipWhitespace($tokens); 73 | $token = $tokens->current(); 74 | } else { 75 | $block->isAbstract = false; 76 | } 77 | if ("T_FINAL" === $token->name) { 78 | $block->isFinal = true; 79 | $block->code .= $token->value." "; 80 | $this->skipWhitespace($tokens); 81 | $token = $tokens->current(); 82 | } else { 83 | $block->isFinal = false; 84 | } 85 | if ("T_PRIVATE" === $token->name) { 86 | $block->isPrivate = true; 87 | $block->code .= $token->value." "; 88 | $this->skipWhitespace($tokens); 89 | $token = $tokens->current(); 90 | } else { 91 | $block->isPrivate = false; 92 | } 93 | if ("T_PROTECTED" === $token->name) { 94 | $block->isProtected = true; 95 | $block->code .= $token->value." "; 96 | $this->skipWhitespace($tokens); 97 | $token = $tokens->current(); 98 | } else { 99 | $block->isProtected = false; 100 | } 101 | if ("T_PUBLIC" === $token->name) { 102 | $block->code .= $token->value." "; 103 | $this->skipWhitespace($tokens); 104 | $token = $tokens->current(); 105 | } 106 | if ("T_STATIC" === $token->name) { 107 | $block->isStatic = true; 108 | $block->code .= $token->value." "; 109 | $this->skipWhitespace($tokens); 110 | $token = $tokens->current(); 111 | } else { 112 | $block->isStatic = false; 113 | } 114 | 115 | // implicit public 116 | $block->isPublic = !($block->isPrivate || $block->isProtected); 117 | 118 | if ("T_CLASS" === $token->name) { 119 | $block->type = 'class'; 120 | 121 | $this->parseClassOrInterface($tokens, $block); 122 | } elseif ("T_INTERFACE" === $token->name) { 123 | $block->type = 'interface'; 124 | 125 | $this->parseClassOrInterface($tokens, $block); 126 | } elseif ("T_FUNCTION" === $token->name) { 127 | $block->type = 'function'; 128 | 129 | $this->parseFunctionOrMethod($tokens, $block); 130 | } elseif ("T_NAMESPACE" === $token->name) { 131 | $block->type = 'namespace'; 132 | 133 | $this->parseNamespace($tokens, $block); 134 | } elseif ("T_VARIABLE" === $token->name) { 135 | $block->type = 'variable'; 136 | 137 | $this->parseVariable($tokens, $block); 138 | } elseif ("T_CONST" === $token->name) { 139 | $block->type = 'constant'; 140 | 141 | $this->parseConstant($tokens, $block); 142 | } else { 143 | array_pop($this->blocks); 144 | } 145 | } 146 | 147 | private function parseComment($token, \stdClass $block) 148 | { 149 | $lines = array_map(function($line) { 150 | return substr(trim($line), 2); 151 | }, array_slice(explode("\n", $token->value), 1, -1)); 152 | 153 | $tagLines = array(); 154 | 155 | foreach ($lines as $i => $line) { 156 | if (strpos($line, "@") === 0) { 157 | $tagLines[] = $line; 158 | unset($lines[$i]); 159 | } 160 | } 161 | 162 | $block->tags = array_values(array_map(function($line) { 163 | $line = substr($line, 1); 164 | $words = explode(" ", preg_replace('!\s+!', ' ', $line), 4); 165 | $count = count($words); 166 | 167 | $tag = array('type' => $words[0]); 168 | 169 | if ($tag['type'] == 'author') { 170 | array_shift($words); 171 | $words = explode(" ", implode(" ", array_filter($words))); 172 | foreach ($words as $i => $word) { 173 | if (0 === strpos($word, "(")) { 174 | $tag['website'] = substr($word, 1, -1); 175 | unset($words[$i]); 176 | continue; 177 | } 178 | if (0 === strpos($word, "<")) { 179 | $tag['email'] = substr($word, 1, -1); 180 | unset($words[$i]); 181 | } 182 | } 183 | $tag['name'] = implode(' ', $words); 184 | } elseif ($count > 3) { 185 | $tag['types'] = explode("|", $words[1]); 186 | $tag['name'] = substr($words[2], 1); 187 | $tag['description'] = trim($words[3]); 188 | } elseif ($count > 2) { 189 | $tag['types'] = explode("|", $words[1]); 190 | $tag['name'] = substr($words[2], 1); 191 | } elseif ($count > 1) { 192 | $tag['types'] = explode("|", $words[1]); 193 | } 194 | 195 | return $tag; 196 | }, $tagLines)); 197 | 198 | $block->description = trim(str_replace("\n\n\n", "\n\n", implode(PHP_EOL, $lines)), "\n"); 199 | } 200 | 201 | private function parseClassOrInterface(Tokens $tokens, \stdClass $block) 202 | { 203 | $token = $tokens->current(); 204 | $gotName = false; 205 | 206 | while ("{" !== $token->value) { 207 | $block->code .= $token->value; 208 | if (!$gotName && in_array($token->name, array("T_NS_SEPARATOR", "T_STRING"))) { 209 | $block->name .= $token->value; 210 | $block->line = $token->line; 211 | } 212 | if (!empty($block->name) && !$gotName && !in_array($token->name, array("T_NS_SEPARATOR", "T_STRING", "T_WHITESPACE"))) { 213 | $gotName = true; 214 | } 215 | $tokens->next(); 216 | $token = $tokens->current(); 217 | } 218 | 219 | $block->code = trim($block->code); 220 | 221 | while ("}" !== $token->value) { 222 | $subBlock = (object) array( 223 | "tags" => array(), 224 | "description" => '', 225 | "isPrivate" => false, 226 | "isProtected" => false, 227 | "isPublic" => false, 228 | "isAbstract" => false, 229 | "isFinal" => false, 230 | "isStatic" => false, 231 | "code" => '', 232 | "type" => '', 233 | "name" => $block->name.'::', 234 | ); 235 | 236 | $this->blocks[] = $subBlock; 237 | 238 | $this->skipWhitespace($tokens); 239 | 240 | if ("}" === $token->value) { 241 | break; 242 | } 243 | 244 | $this->parseBlock($tokens, $subBlock); 245 | 246 | if ($subBlock->type === 'function') { 247 | $subBlock->type = 'method'; 248 | } 249 | $token = $tokens->current(); 250 | } 251 | } 252 | 253 | private function parseFunctionOrMethod(Tokens $tokens, \stdClass $block) 254 | { 255 | $gotName = false; 256 | $openCurlys = 0; 257 | $token = $tokens->current(); 258 | 259 | while (!in_array($token->value, array("{", ";"))) { 260 | $block->code .= $token->value; 261 | 262 | // first string after keyword is the function name 263 | if (!$gotName && "T_STRING" === $token->name) { 264 | $gotName = true; 265 | $block->name .= $token->value."()"; 266 | $block->line = $token->line; 267 | } 268 | 269 | $tokens->next(); 270 | $token = $tokens->current(); 271 | } 272 | 273 | $block->code = trim($block->code); 274 | $block->name = trim($block->name); 275 | 276 | if ("{" === $token->value) { 277 | $openCurlys = 1; 278 | $tokens->next(); 279 | $token = $tokens->current(); 280 | 281 | while ($openCurlys !== 0) { 282 | if ("}" === $token->value) { 283 | $openCurlys--; 284 | } elseif ("{" === $token->value) { 285 | $openCurlys++; 286 | } 287 | $tokens->next(); 288 | $token = $tokens->current(); 289 | } 290 | } 291 | } 292 | 293 | private function parseNamespace(Tokens $tokens, \stdClass $block) 294 | { 295 | $openCurlys = 0; 296 | $token = $tokens->current(); 297 | 298 | while (!in_array($token->value, array("{", ";"))) { 299 | $block->code .= $token->value; 300 | if (in_array($token->name, array("T_NS_SEPARATOR", "T_STRING"))) { 301 | $block->name .= $token->value; 302 | } 303 | $tokens->next(); 304 | $token = $tokens->current(); 305 | } 306 | 307 | $block->code = trim($block->code); 308 | $block->name = trim($block->name); 309 | 310 | if ("{" === $token->value) { 311 | $openCurlys = 1; 312 | $tokens->next(); 313 | $token = $tokens->current(); 314 | 315 | // skip to the end of namespace declaration 316 | while ($openCurlys !== 0) { 317 | if ("}" === $token->value) { 318 | $openCurlys--; 319 | } elseif ("{" === $token->value) { 320 | $openCurlys++; 321 | } 322 | $tokens->next(); 323 | $token = $tokens->current(); 324 | } 325 | } 326 | } 327 | 328 | private function parseVariable(Tokens $tokens, \stdClass $block) 329 | { 330 | $token = $tokens->current(); 331 | 332 | while (";" !== $token->value) { 333 | $block->code .= $token->value; 334 | if ("T_VARIABLE" === $token->name) { 335 | $block->name = $token->value; 336 | } 337 | $tokens->next(); 338 | $token = $tokens->current(); 339 | } 340 | 341 | $block->code = trim($block->code); 342 | $block->name = trim($block->name); 343 | } 344 | 345 | private function parseConstant(Tokens $tokens, \stdClass $block) 346 | { 347 | $token = $tokens->current(); 348 | 349 | while (";" !== $token->value) { 350 | $block->code .= $token->value; 351 | 352 | $tokens->next(); 353 | $token = $tokens->current(); 354 | 355 | $block->name .= $token->value; 356 | } 357 | 358 | $block->code = trim($block->code); 359 | $block->name = trim($block->name); 360 | } 361 | } 362 | -------------------------------------------------------------------------------- /lib/DoxPHP/Parser/Tokens.php: -------------------------------------------------------------------------------- 1 | token_name($token[0]), 20 | "value" => $token[1], 21 | "line" => $token[2], 22 | ); 23 | } else { 24 | $tok = array( 25 | "name" => null, 26 | "value" => $token 27 | ); 28 | } 29 | 30 | return (object) $tok; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/vendor/php-markdown/LICENSE: -------------------------------------------------------------------------------- 1 | PHP Markdown 2 | Copyright (c) 2004-2008 Michel Fortin 3 | 4 | All rights reserved. 5 | 6 | Additional features and fixes by Henrik Paul 7 | 8 | 9 | Based on Markdown 10 | Copyright (c) 2003-2006 John Gruber 11 | 12 | All rights reserved. 13 | 14 | Redistribution and use in source and binary forms, with or without 15 | modification, are permitted provided that the following conditions are 16 | met: 17 | 18 | * Redistributions of source code must retain the above copyright notice, 19 | this list of conditions and the following disclaimer. 20 | 21 | * Redistributions in binary form must reproduce the above copyright 22 | notice, this list of conditions and the following disclaimer in the 23 | documentation and/or other materials provided with the distribution. 24 | 25 | * Neither the name "Markdown" nor the names of its contributors may 26 | be used to endorse or promote products derived from this software 27 | without specific prior written permission. 28 | 29 | This software is provided by the copyright holders and contributors "as 30 | is" and any express or implied warranties, including, but not limited 31 | to, the implied warranties of merchantability and fitness for a 32 | particular purpose are disclaimed. In no event shall the copyright owner 33 | or contributors be liable for any direct, indirect, incidental, special, 34 | exemplary, or consequential damages (including, but not limited to, 35 | procurement of substitute goods or services; loss of use, data, or 36 | profits; or business interruption) however caused and on any theory of 37 | liability, whether in contract, strict liability, or tort (including 38 | negligence or otherwise) arising in any way out of the use of this 39 | software, even if advised of the possibility of such damage. 40 | -------------------------------------------------------------------------------- /lib/vendor/php-markdown/README: -------------------------------------------------------------------------------- 1 | README.markdown -------------------------------------------------------------------------------- /lib/vendor/php-markdown/README.markdown: -------------------------------------------------------------------------------- 1 | PHP Markdown 2 | ============ 3 | 4 | Version 1.0.1m - Sat 21 Jun 2008 5 | 6 | by Michel Fortin 7 | 8 | 9 | Additional features and fixes by Henrik Paul 10 | 11 | 12 | based on Markdown by John Gruber 13 | 14 | 15 | 16 | Introduction 17 | ------------ 18 | 19 | Markdown is a text-to-HTML conversion tool for web writers. Markdown 20 | allows you to write using an easy-to-read, easy-to-write plain text 21 | format, then convert it to structurally valid XHTML (or HTML). 22 | 23 | "Markdown" is two things: a plain text markup syntax, and a software 24 | tool, written in Perl, that converts the plain text markup to HTML. 25 | PHP Markdown is a port to PHP of the original Markdown program by 26 | John Gruber. 27 | 28 | PHP Markdown can work as a plug-in for WordPress and bBlog, as a 29 | modifier for the Smarty templating engine, or as a remplacement for 30 | textile formatting in any software that support textile. 31 | 32 | Full documentation of Markdown's syntax is available on John's 33 | Markdown page: 34 | 35 | 36 | Installation and Requirement 37 | ---------------------------- 38 | 39 | PHP Markdown requires PHP version 4.0.5 or later. 40 | 41 | 42 | ### WordPress ### 43 | 44 | PHP Markdown works with [WordPress][wp], version 1.2 or later. 45 | 46 | [wp]: http://wordpress.org/ 47 | 48 | 1. To use PHP Markdown with WordPress, place the "makrdown.php" file 49 | in the "plugins" folder. This folder is located inside 50 | "wp-content" at the root of your site: 51 | 52 | (site home)/wp-content/plugins/ 53 | 54 | 2. Activate the plugin with the administrative interface of 55 | WordPress. In the "Plugins" section you will now find Markdown. 56 | To activate the plugin, click on the "Activate" button on the 57 | same line than Markdown. Your entries will now be formatted by 58 | PHP Markdown. 59 | 60 | 3. To post Markdown content, you'll first have to disable the 61 | "visual" editor in the User section of WordPress. 62 | 63 | You can configure PHP Markdown to not apply to the comments on your 64 | WordPress weblog. See the "Configuration" section below. 65 | 66 | It is not possible at this time to apply a different set of 67 | filters to different entries. All your entries will be formated by 68 | PHP Markdown. This is a limitation of WordPress. If your old entries 69 | are written in HTML (as opposed to another formatting syntax, like 70 | Textile), they'll probably stay fine after installing Markdown. 71 | 72 | 73 | ### bBlog ### 74 | 75 | PHP Markdown also works with [bBlog][bb]. 76 | 77 | [bb]: http://www.bblog.com/ 78 | 79 | To use PHP Markdown with bBlog, rename "markdown.php" to 80 | "modifier.markdown.php" and place the file in the "bBlog_plugins" 81 | folder. This folder is located inside the "bblog" directory of 82 | your site, like this: 83 | 84 | (site home)/bblog/bBlog_plugins/modifier.markdown.php 85 | 86 | Select "Markdown" as the "Entry Modifier" when you post a new 87 | entry. This setting will only apply to the entry you are editing. 88 | 89 | 90 | ### Replacing Textile in TextPattern ### 91 | 92 | [TextPattern][tp] use [Textile][tx] to format your text. You can 93 | replace Textile by Markdown in TextPattern without having to change 94 | any code by using the *Texitle Compatibility Mode*. This may work 95 | with other software that expect Textile too. 96 | 97 | [tx]: http://www.textism.com/tools/textile/ 98 | [tp]: http://www.textpattern.com/ 99 | 100 | 1. Rename the "markdown.php" file to "classTextile.php". This will 101 | make PHP Markdown behave as if it was the actual Textile parser. 102 | 103 | 2. Replace the "classTextile.php" file TextPattern installed in your 104 | web directory. It can be found in the "lib" directory: 105 | 106 | (site home)/textpattern/lib/ 107 | 108 | Contrary to Textile, Markdown does not convert quotes to curly ones 109 | and does not convert multiple hyphens (`--` and `---`) into en- and 110 | em-dashes. If you use PHP Markdown in Textile Compatibility Mode, you 111 | can solve this problem by installing the "smartypants.php" file from 112 | [PHP SmartyPants][psp] beside the "classTextile.php" file. The Textile 113 | Compatibility Mode function will use SmartyPants automatically without 114 | further modification. 115 | 116 | [psp]: http://www.michelf.com/projects/php-smartypants/ 117 | 118 | 119 | ### Updating Markdown in Other Programs ### 120 | 121 | Many web applications now ship with PHP Markdown, or have plugins to 122 | perform the conversion to HTML. You can update PHP Markdown in many of 123 | these programs by swapping the old "markdown.php" file for the new one. 124 | 125 | Here is a short non-exhaustive list of some programs and where they 126 | hide the "markdown.php" file. 127 | 128 | | Program | Path to Markdown 129 | | ------- | ---------------- 130 | | [Pivot][] | `(site home)/pivot/includes/markdown/markdown.php` 131 | 132 | If you're unsure if you can do this with your application, ask the 133 | developer, or wait for the developer to update his application or 134 | plugin with the new version of PHP Markdown. 135 | 136 | [Pivot]: http://pivotlog.net/ 137 | 138 | 139 | ### In Your Own Programs ### 140 | 141 | You can use PHP Markdown easily in your current PHP program. Simply 142 | include the file and then call the Markdown function on the text you 143 | want to convert: 144 | 145 | include_once "markdown.php"; 146 | $my_html = Markdown($my_text); 147 | 148 | If you wish to use PHP Markdown with another text filter function 149 | built to parse HTML, you should filter the text *after* the Markdown 150 | function call. This is an example with [PHP SmartyPants][psp]: 151 | 152 | $my_html = SmartyPants(Markdown($my_text)); 153 | 154 | 155 | ### With Smarty ### 156 | 157 | If your program use the [Smarty][sm] template engine, PHP Markdown 158 | can now be used as a modifier for your templates. Rename "markdown.php" 159 | to "modifier.markdown.php" and put it in your smarty plugins folder. 160 | 161 | [sm]: http://smarty.php.net/ 162 | 163 | If you are using MovableType 3.1 or later, the Smarty plugin folder is 164 | located at `(MT CGI root)/php/extlib/smarty/plugins`. This will allow 165 | Markdown to work on dynamic pages. 166 | 167 | 168 | Configuration 169 | ------------- 170 | 171 | By default, PHP Markdown produces XHTML output for tags with empty 172 | elements. E.g.: 173 | 174 |
175 | 176 | Markdown can be configured to produce HTML-style tags; e.g.: 177 | 178 |
179 | 180 | To do this, you must edit the "MARKDOWN_EMPTY_ELEMENT_SUFFIX" 181 | definition below the "Global default settings" header at the start of 182 | the "markdown.php" file. 183 | 184 | 185 | ### WordPress-Specific Settings ### 186 | 187 | By default, the Markdown plugin applies to both posts and comments on 188 | your WordPress weblog. To deactivate one or the other, edit the 189 | `MARKDOWN_WP_POSTS` or `MARKDOWN_WP_COMMENTS` definitions under the 190 | "WordPress settings" header at the start of the "markdown.php" file. 191 | 192 | 193 | Bugs 194 | ---- 195 | 196 | To file bug reports please send email to: 197 | 198 | 199 | Please include with your report: (1) the example input; (2) the output you 200 | expected; (3) the output PHP Markdown actually produced. 201 | 202 | 203 | Version History 204 | --------------- 205 | 206 | 1.0.1m (21 Jun 2008): 207 | 208 | * Lists can now have empty items. 209 | 210 | * Rewrote the emphasis and strong emphasis parser to fix some issues 211 | with odly placed and overlong markers. 212 | 213 | 214 | 1.0.1l (11 May 2008): 215 | 216 | * Now removing the UTF-8 BOM at the start of a document, if present. 217 | 218 | * Now accepting capitalized URI schemes (such as HTTP:) in automatic 219 | links, such as ``. 220 | 221 | * Fixed a problem where `` was seen as a horizontal 222 | rule instead of an automatic link. 223 | 224 | * Fixed an issue where some characters in Markdown-generated HTML 225 | attributes weren't properly escaped with entities. 226 | 227 | * Fix for code blocks as first element of a list item. Previously, 228 | this didn't create any code block for item 2: 229 | 230 | * Item 1 (regular paragraph) 231 | 232 | * Item 2 (code block) 233 | 234 | * A code block starting on the second line of a document wasn't seen 235 | as a code block. This has been fixed. 236 | 237 | * Added programatically-settable parser properties `predef_urls` and 238 | `predef_titles` for predefined URLs and titles for reference-style 239 | links. To use this, your PHP code must call the parser this way: 240 | 241 | $parser = new Markdwon_Parser; 242 | $parser->predef_urls = array('linkref' => 'http://example.com'); 243 | $html = $parser->transform($text); 244 | 245 | You can then use the URL as a normal link reference: 246 | 247 | [my link][linkref] 248 | [my link][linkRef] 249 | 250 | Reference names in the parser properties *must* be lowercase. 251 | Reference names in the Markdown source may have any case. 252 | 253 | * Added `setup` and `teardown` methods which can be used by subclassers 254 | as hook points to arrange the state of some parser variables before and 255 | after parsing. 256 | 257 | 258 | 1.0.1k (26 Sep 2007): 259 | 260 | * Fixed a problem introduced in 1.0.1i where three or more identical 261 | uppercase letters, as well as a few other symbols, would trigger 262 | a horizontal line. 263 | 264 | 265 | 1.0.1j (4 Sep 2007): 266 | 267 | * Fixed a problem introduced in 1.0.1i where the closing `code` and 268 | `pre` tags at the end of a code block were appearing in the wrong 269 | order. 270 | 271 | * Overriding configuration settings by defining constants from an 272 | external before markdown.php is included is now possible without 273 | producing a PHP warning. 274 | 275 | 276 | 1.0.1i (31 Aug 2007): 277 | 278 | * Fixed a problem where an escaped backslash before a code span 279 | would prevent the code span from being created. This should now 280 | work as expected: 281 | 282 | Litteral backslash: \\`code span` 283 | 284 | * Overall speed improvements, especially with long documents. 285 | 286 | 287 | 1.0.1h (3 Aug 2007): 288 | 289 | * Added two properties (`no_markup` and `no_entities`) to the parser 290 | allowing HTML tags and entities to be disabled. 291 | 292 | * Fix for a problem introduced in 1.0.1g where posting comments in 293 | WordPress would trigger PHP warnings and cause some markup to be 294 | incorrectly filtered by the kses filter in WordPress. 295 | 296 | 297 | 1.0.1g (3 Jul 2007): 298 | 299 | * Fix for PHP 5 compiled without the mbstring module. Previous fix to 300 | calculate the length of UTF-8 strings in `detab` when `mb_strlen` is 301 | not available was only working with PHP 4. 302 | 303 | * Fixed a problem with WordPress 2.x where full-content posts in RSS feeds 304 | were not processed correctly by Markdown. 305 | 306 | * Now supports URLs containing literal parentheses for inline links 307 | and images, such as: 308 | 309 | [WIMP](http://en.wikipedia.org/wiki/WIMP_(computing)) 310 | 311 | Such parentheses may be arbitrarily nested, but must be 312 | balanced. Unbalenced parentheses are allowed however when the URL 313 | when escaped or when the URL is enclosed in angle brakets `<>`. 314 | 315 | * Fixed a performance problem where the regular expression for strong 316 | emphasis introduced in version 1.0.1d could sometime be long to process, 317 | give slightly wrong results, and in some circumstances could remove 318 | entirely the content for a whole paragraph. 319 | 320 | * Some change in version 1.0.1d made possible the incorrect nesting of 321 | anchors within each other. This is now fixed. 322 | 323 | * Fixed a rare issue where certain MD5 hashes in the content could 324 | be changed to their corresponding text. For instance, this: 325 | 326 | The MD5 value for "+" is "26b17225b626fb9238849fd60eabdf60". 327 | 328 | was incorrectly changed to this in previous versions of PHP Markdown: 329 | 330 |

The MD5 value for "+" is "+".

331 | 332 | * Now convert escaped characters to their numeric character 333 | references equivalent. 334 | 335 | This fix an integration issue with SmartyPants and backslash escapes. 336 | Since Markdown and SmartyPants have some escapable characters in common, 337 | it was sometime necessary to escape them twice. Previously, two 338 | backslashes were sometime required to prevent Markdown from "eating" the 339 | backslash before SmartyPants sees it: 340 | 341 | Here are two hyphens: \\-- 342 | 343 | Now, only one backslash will do: 344 | 345 | Here are two hyphens: \-- 346 | 347 | 348 | 1.0.1f (7 Feb 2007): 349 | 350 | * Fixed an issue with WordPress where manually-entered excerpts, but 351 | not the auto-generated ones, would contain nested paragraphs. 352 | 353 | * Fixed an issue introduced in 1.0.1d where headers and blockquotes 354 | preceded too closely by a paragraph (not separated by a blank line) 355 | where incorrectly put inside the paragraph. 356 | 357 | * Fixed an issue introduced in 1.0.1d in the tokenizeHTML method where 358 | two consecutive code spans would be merged into one when together they 359 | form a valid tag in a multiline paragraph. 360 | 361 | * Fixed an long-prevailing issue where blank lines in code blocks would 362 | be doubled when the code block is in a list item. 363 | 364 | This was due to the list processing functions relying on artificially 365 | doubled blank lines to correctly determine when list items should 366 | contain block-level content. The list item processing model was thus 367 | changed to avoid the need for double blank lines. 368 | 369 | * Fixed an issue with `<% asp-style %>` instructions used as inline 370 | content where the opening `<` was encoded as `<`. 371 | 372 | * Fixed a parse error occuring when PHP is configured to accept 373 | ASP-style delimiters as boundaries for PHP scripts. 374 | 375 | * Fixed a bug introduced in 1.0.1d where underscores in automatic links 376 | got swapped with emphasis tags. 377 | 378 | 379 | 1.0.1e (28 Dec 2006) 380 | 381 | * Added support for internationalized domain names for email addresses in 382 | automatic link. Improved the speed at which email addresses are converted 383 | to entities. Thanks to Milian Wolff for his optimisations. 384 | 385 | * Made deterministic the conversion to entities of email addresses in 386 | automatic links. This means that a given email address will always be 387 | encoded the same way. 388 | 389 | * PHP Markdown will now use its own function to calculate the length of an 390 | UTF-8 string in `detab` when `mb_strlen` is not available instead of 391 | giving a fatal error. 392 | 393 | 394 | 1.0.1d (1 Dec 2006) 395 | 396 | * Fixed a bug where inline images always had an empty title attribute. The 397 | title attribute is now present only when explicitly defined. 398 | 399 | * Link references definitions can now have an empty title, previously if the 400 | title was defined but left empty the link definition was ignored. This can 401 | be useful if you want an empty title attribute in images to hide the 402 | tooltip in Internet Explorer. 403 | 404 | * Made `detab` aware of UTF-8 characters. UTF-8 multi-byte sequences are now 405 | correctly mapped to one character instead of the number of bytes. 406 | 407 | * Fixed a small bug with WordPress where WordPress' default filter `wpautop` 408 | was not properly deactivated on comment text, resulting in hard line breaks 409 | where Markdown do not prescribes them. 410 | 411 | * Added a `TextileRestrited` method to the textile compatibility mode. There 412 | is no restriction however, as Markdown does not have a restricted mode at 413 | this point. This should make PHP Markdown work again in the latest 414 | versions of TextPattern. 415 | 416 | * Converted PHP Markdown to a object-oriented design. 417 | 418 | * Changed span and block gamut methods so that they loop over a 419 | customizable list of methods. This makes subclassing the parser a more 420 | interesting option for creating syntax extensions. 421 | 422 | * Also added a "document" gamut loop which can be used to hook document-level 423 | methods (like for striping link definitions). 424 | 425 | * Changed all methods which were inserting HTML code so that they now return 426 | a hashed representation of the code. New methods `hashSpan` and `hashBlock` 427 | are used to hash respectivly span- and block-level generated content. This 428 | has a couple of significant effects: 429 | 430 | 1. It prevents invalid nesting of Markdown-generated elements which 431 | could occur occuring with constructs like `*something [link*][1]`. 432 | 2. It prevents problems occuring with deeply nested lists on which 433 | paragraphs were ill-formed. 434 | 3. It removes the need to call `hashHTMLBlocks` twice during the the 435 | block gamut. 436 | 437 | Hashes are turned back to HTML prior output. 438 | 439 | * Made the block-level HTML parser smarter using a specially-crafted regular 440 | expression capable of handling nested tags. 441 | 442 | * Solved backtick issues in tag attributes by rewriting the HTML tokenizer to 443 | be aware of code spans. All these lines should work correctly now: 444 | 445 | bar 446 | bar 447 | `` 448 | 449 | * Changed the parsing of HTML comments to match simply from `` 450 | instead using of the more complicated SGML-style rule with paired `--`. 451 | This is how most browsers parse comments and how XML defines them too. 452 | 453 | * `
` has been added to the list of block-level elements and is now 454 | treated as an HTML block instead of being wrapped within paragraph tags. 455 | 456 | * Now only trim trailing newlines from code blocks, instead of trimming 457 | all trailing whitespace characters. 458 | 459 | * Fixed bug where this: 460 | 461 | [text](http://m.com "title" ) 462 | 463 | wasn't working as expected, because the parser wasn't allowing for spaces 464 | before the closing paren. 465 | 466 | * Filthy hack to support markdown='1' in div tags. 467 | 468 | * _DoAutoLinks() now supports the 'dict://' URL scheme. 469 | 470 | * PHP- and ASP-style processor instructions are now protected as 471 | raw HTML blocks. 472 | 473 | 474 | <% ... %> 475 | 476 | * Fix for escaped backticks still triggering code spans: 477 | 478 | There are two raw backticks here: \` and here: \`, not a code span 479 | 480 | 481 | 1.0.1c (9 Dec 2005) 482 | 483 | * Fixed a problem occurring with PHP 5.1.1 due to a small 484 | change to strings variable replacement behaviour in 485 | this version. 486 | 487 | 488 | 1.0.1b (6 Jun 2005) 489 | 490 | * Fixed a bug where an inline image followed by a reference link would 491 | give a completely wrong result. 492 | 493 | * Fix for escaped backticks still triggering code spans: 494 | 495 | There are two raw backticks here: \` and here: \`, not a code span 496 | 497 | * Fix for an ordered list following an unordered list, and the 498 | reverse. There is now a loop in _DoList that does the two 499 | separately. 500 | 501 | * Fix for nested sub-lists in list-paragraph mode. Previously we got 502 | a spurious extra level of `

` tags for something like this: 503 | 504 | * this 505 | 506 | * sub 507 | 508 | that 509 | 510 | * Fixed some incorrect behaviour with emphasis. This will now work 511 | as it should: 512 | 513 | *test **thing*** 514 | **test *thing*** 515 | ***thing* test** 516 | ***thing** test* 517 | 518 | Name: __________ 519 | Address: _______ 520 | 521 | * Correct a small bug in `_TokenizeHTML` where a Doctype declaration 522 | was not seen as HTML. 523 | 524 | * Major rewrite of the WordPress integration code that should 525 | correct many problems by preventing default WordPress filters from 526 | tampering with Markdown-formatted text. More details here: 527 | 528 | 529 | 530 | 1.0.1a (15 Apr 2005) 531 | 532 | * Fixed an issue where PHP warnings were trigged when converting 533 | text with list items running on PHP 4.0.6. This was comming from 534 | the `rtrim` function which did not support the second argument 535 | prior version 4.1. Replaced by a regular expression. 536 | 537 | * Markdown now filter correctly post excerpts and comment 538 | excerpts in WordPress. 539 | 540 | * Automatic links and some code sample were "corrected" by 541 | the balenceTag filter in WordPress meant to ensure HTML 542 | is well formed. This new version of PHP Markdown postpone this 543 | filter so that it runs after Markdown. 544 | 545 | * Blockquote syntax and some code sample were stripped by 546 | a new WordPress 1.5 filter meant to remove unwanted HTML 547 | in comments. This new version of PHP Markdown postpone this 548 | filter so that it runs after Markdown. 549 | 550 | 551 | 1.0.1 (16 Dec 2004): 552 | 553 | * Changed the syntax rules for code blocks and spans. Previously, 554 | backslash escapes for special Markdown characters were processed 555 | everywhere other than within inline HTML tags. Now, the contents of 556 | code blocks and spans are no longer processed for backslash escapes. 557 | This means that code blocks and spans are now treated literally, 558 | with no special rules to worry about regarding backslashes. 559 | 560 | **IMPORTANT**: This breaks the syntax from all previous versions of 561 | Markdown. Code blocks and spans involving backslash characters will 562 | now generate different output than before. 563 | 564 | Implementation-wise, this change was made by moving the call to 565 | `_EscapeSpecialChars()` from the top-level `Markdown()` function to 566 | within `_RunSpanGamut()`. 567 | 568 | * Significants performance improvement in `_DoHeader`, `_Detab` 569 | and `_TokenizeHTML`. 570 | 571 | * Added `>`, `+`, and `-` to the list of backslash-escapable 572 | characters. These should have been done when these characters 573 | were added as unordered list item markers. 574 | 575 | * Inline links using `<` and `>` URL delimiters weren't working: 576 | 577 | like [this]() 578 | 579 | Fixed by moving `_DoAutoLinks()` after `_DoAnchors()` in 580 | `_RunSpanGamut()`. 581 | 582 | * Fixed bug where auto-links were being processed within code spans: 583 | 584 | like this: `` 585 | 586 | Fixed by moving `_DoAutoLinks()` from `_RunBlockGamut()` to 587 | `_RunSpanGamut()`. 588 | 589 | * Sort-of fixed a bug where lines in the middle of hard-wrapped 590 | paragraphs, which lines look like the start of a list item, 591 | would accidentally trigger the creation of a list. E.g. a 592 | paragraph that looked like this: 593 | 594 | I recommend upgrading to version 595 | 8. Oops, now this line is treated 596 | as a sub-list. 597 | 598 | This is fixed for top-level lists, but it can still happen for 599 | sub-lists. E.g., the following list item will not be parsed 600 | properly: 601 | 602 | * I recommend upgrading to version 603 | 8. Oops, now this line is treated 604 | as a sub-list. 605 | 606 | Given Markdown's list-creation rules, I'm not sure this can 607 | be fixed. 608 | 609 | * Fix for horizontal rules preceded by 2 or 3 spaces or followed by 610 | trailing spaces and tabs. 611 | 612 | * Standalone HTML comments are now handled; previously, they'd get 613 | wrapped in a spurious `

` tag. 614 | 615 | * `_HashHTMLBlocks()` now tolerates trailing spaces and tabs following 616 | HTML comments and `


` tags. 617 | 618 | * Changed special case pattern for hashing `
` tags in 619 | `_HashHTMLBlocks()` so that they must occur within three spaces 620 | of left margin. (With 4 spaces or a tab, they should be 621 | code blocks, but weren't before this fix.) 622 | 623 | * Auto-linked email address can now optionally contain 624 | a 'mailto:' protocol. I.e. these are equivalent: 625 | 626 | 627 | 628 | 629 | * Fixed annoying bug where nested lists would wind up with 630 | spurious (and invalid) `

` tags. 631 | 632 | * Changed `_StripLinkDefinitions()` so that link definitions must 633 | occur within three spaces of the left margin. Thus if you indent 634 | a link definition by four spaces or a tab, it will now be a code 635 | block. 636 | 637 | * You can now write empty links: 638 | 639 | [like this]() 640 | 641 | and they'll be turned into anchor tags with empty href attributes. 642 | This should have worked before, but didn't. 643 | 644 | * `***this***` and `___this___` are now turned into 645 | 646 | this 647 | 648 | Instead of 649 | 650 | this 651 | 652 | which isn't valid. 653 | 654 | * Fixed problem for links defined with urls that include parens, e.g.: 655 | 656 | [1]: http://sources.wikipedia.org/wiki/Middle_East_Policy_(Chomsky) 657 | 658 | "Chomsky" was being erroneously treated as the URL's title. 659 | 660 | * Double quotes in the title of an inline link used to give strange 661 | results (incorrectly made entities). Fixed. 662 | 663 | * Tabs are now correctly changed into spaces. Previously, only 664 | the first tab was converted. In code blocks, the second one was too, 665 | but was not always correctly aligned. 666 | 667 | * Fixed a bug where a tab character inserted after a quote on the same 668 | line could add a slash before the quotes. 669 | 670 | This is "before" [tab] and "after" a tab. 671 | 672 | Previously gave this result: 673 | 674 |

This is \"before\" [tab] and "after" a tab.

675 | 676 | * Removed a call to `htmlentities`. This fixes a bug where multibyte 677 | characters present in the title of a link reference could lead to 678 | invalid utf-8 characters. 679 | 680 | * Changed a regular expression in `_TokenizeHTML` that could lead to 681 | a segmentation fault with PHP 4.3.8 on Linux. 682 | 683 | * Fixed some notices that could show up if PHP error reporting 684 | E_NOTICE flag was set. 685 | 686 | 687 | Copyright and License 688 | --------------------- 689 | 690 | PHP Markdown 691 | Copyright (c) 2004-2007 Michel Fortin 692 | 693 | All rights reserved. 694 | 695 | Additional features and fixes by Henrik Paul 696 | 697 | 698 | Based on Markdown 699 | Copyright (c) 2003-2006 John Gruber 700 | 701 | All rights reserved. 702 | 703 | Redistribution and use in source and binary forms, with or without 704 | modification, are permitted provided that the following conditions are 705 | met: 706 | 707 | * Redistributions of source code must retain the above copyright notice, 708 | this list of conditions and the following disclaimer. 709 | 710 | * Redistributions in binary form must reproduce the above copyright 711 | notice, this list of conditions and the following disclaimer in the 712 | documentation and/or other materials provided with the distribution. 713 | 714 | * Neither the name "Markdown" nor the names of its contributors may 715 | be used to endorse or promote products derived from this software 716 | without specific prior written permission. 717 | 718 | This software is provided by the copyright holders and contributors "as 719 | is" and any express or implied warranties, including, but not limited 720 | to, the implied warranties of merchantability and fitness for a 721 | particular purpose are disclaimed. In no event shall the copyright owner 722 | or contributors be liable for any direct, indirect, incidental, special, 723 | exemplary, or consequential damages (including, but not limited to, 724 | procurement of substitute goods or services; loss of use, data, or 725 | profits; or business interruption) however caused and on any theory of 726 | liability, whether in contract, strict liability, or tort (including 727 | negligence or otherwise) arising in any way out of the use of this 728 | software, even if advised of the possibility of such damage. 729 | -------------------------------------------------------------------------------- /lib/vendor/php-markdown/markdown.php: -------------------------------------------------------------------------------- 1 | 8 | # 9 | # Original Markdown 10 | # Copyright (c) 2004-2006 John Gruber 11 | # 12 | # 13 | 14 | 15 | define( 'MARKDOWN_VERSION', "1.0.1m" ); # Sat 21 Jun 2008 16 | 17 | 18 | # 19 | # Global default settings: 20 | # 21 | 22 | # Change to ">" for HTML output 23 | @define( 'MARKDOWN_EMPTY_ELEMENT_SUFFIX', " />"); 24 | 25 | # Define the width of a tab for code blocks. 26 | @define( 'MARKDOWN_TAB_WIDTH', 4 ); 27 | 28 | 29 | # 30 | # WordPress settings: 31 | # 32 | 33 | # Change to false to remove Markdown from posts and/or comments. 34 | @define( 'MARKDOWN_WP_POSTS', true ); 35 | @define( 'MARKDOWN_WP_COMMENTS', true ); 36 | 37 | 38 | 39 | ### Standard Function Interface ### 40 | 41 | @define( 'MARKDOWN_PARSER_CLASS', 'Markdown_Parser' ); 42 | 43 | function Markdown($text) { 44 | # 45 | # Initialize the parser and return the result of its transform method. 46 | # 47 | # Setup static parser variable. 48 | static $parser; 49 | if (!isset($parser)) { 50 | $parser_class = MARKDOWN_PARSER_CLASS; 51 | $parser = new $parser_class; 52 | } 53 | 54 | # Transform text using parser. 55 | return $parser->transform($text); 56 | } 57 | 58 | 59 | ### WordPress Plugin Interface ### 60 | 61 | /* 62 | Plugin Name: Markdown 63 | Plugin URI: http://www.michelf.com/projects/php-markdown/ 64 | Description: Markdown syntax allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by John Gruber. More... 65 | Version: 1.0.1m 66 | Author: Michel Fortin 67 | Author URI: http://www.michelf.com/ 68 | */ 69 | 70 | if (isset($wp_version)) { 71 | # More details about how it works here: 72 | # 73 | 74 | # Post content and excerpts 75 | # - Remove WordPress paragraph generator. 76 | # - Run Markdown on excerpt, then remove all tags. 77 | # - Add paragraph tag around the excerpt, but remove it for the excerpt rss. 78 | if (MARKDOWN_WP_POSTS) { 79 | remove_filter('the_content', 'wpautop'); 80 | remove_filter('the_content_rss', 'wpautop'); 81 | remove_filter('the_excerpt', 'wpautop'); 82 | add_filter('the_content', 'Markdown', 6); 83 | add_filter('the_content_rss', 'Markdown', 6); 84 | add_filter('get_the_excerpt', 'Markdown', 6); 85 | add_filter('get_the_excerpt', 'trim', 7); 86 | add_filter('the_excerpt', 'mdwp_add_p'); 87 | add_filter('the_excerpt_rss', 'mdwp_strip_p'); 88 | 89 | remove_filter('content_save_pre', 'balanceTags', 50); 90 | remove_filter('excerpt_save_pre', 'balanceTags', 50); 91 | add_filter('the_content', 'balanceTags', 50); 92 | add_filter('get_the_excerpt', 'balanceTags', 9); 93 | } 94 | 95 | # Comments 96 | # - Remove WordPress paragraph generator. 97 | # - Remove WordPress auto-link generator. 98 | # - Scramble important tags before passing them to the kses filter. 99 | # - Run Markdown on excerpt then remove paragraph tags. 100 | if (MARKDOWN_WP_COMMENTS) { 101 | remove_filter('comment_text', 'wpautop', 30); 102 | remove_filter('comment_text', 'make_clickable'); 103 | add_filter('pre_comment_content', 'Markdown', 6); 104 | add_filter('pre_comment_content', 'mdwp_hide_tags', 8); 105 | add_filter('pre_comment_content', 'mdwp_show_tags', 12); 106 | add_filter('get_comment_text', 'Markdown', 6); 107 | add_filter('get_comment_excerpt', 'Markdown', 6); 108 | add_filter('get_comment_excerpt', 'mdwp_strip_p', 7); 109 | 110 | global $mdwp_hidden_tags, $mdwp_placeholders; 111 | $mdwp_hidden_tags = explode(' ', 112 | '

 
  • '); 113 | $mdwp_placeholders = explode(' ', str_rot13( 114 | 'pEj07ZbbBZ U1kqgh4w4p pre2zmeN6K QTi31t9pre ol0MP1jzJR '. 115 | 'ML5IjmbRol ulANi1NsGY J7zRLJqPul liA8ctl16T K9nhooUHli')); 116 | } 117 | 118 | function mdwp_add_p($text) { 119 | if (!preg_match('{^$|^<(p|ul|ol|dl|pre|blockquote)>}i', $text)) { 120 | $text = '

    '.$text.'

    '; 121 | $text = preg_replace('{\n{2,}}', "

    \n\n

    ", $text); 122 | } 123 | return $text; 124 | } 125 | 126 | function mdwp_strip_p($t) { return preg_replace('{}i', '', $t); } 127 | 128 | function mdwp_hide_tags($text) { 129 | global $mdwp_hidden_tags, $mdwp_placeholders; 130 | return str_replace($mdwp_hidden_tags, $mdwp_placeholders, $text); 131 | } 132 | function mdwp_show_tags($text) { 133 | global $mdwp_hidden_tags, $mdwp_placeholders; 134 | return str_replace($mdwp_placeholders, $mdwp_hidden_tags, $text); 135 | } 136 | } 137 | 138 | 139 | ### bBlog Plugin Info ### 140 | 141 | function identify_modifier_markdown() { 142 | return array( 143 | 'name' => 'markdown', 144 | 'type' => 'modifier', 145 | 'nicename' => 'Markdown', 146 | 'description' => 'A text-to-HTML conversion tool for web writers', 147 | 'authors' => 'Michel Fortin and John Gruber', 148 | 'licence' => 'BSD-like', 149 | 'version' => MARKDOWN_VERSION, 150 | 'help' => 'Markdown syntax allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by John Gruber. More...' 151 | ); 152 | } 153 | 154 | 155 | ### Smarty Modifier Interface ### 156 | 157 | function smarty_modifier_markdown($text) { 158 | return Markdown($text); 159 | } 160 | 161 | 162 | ### Textile Compatibility Mode ### 163 | 164 | # Rename this file to "classTextile.php" and it can replace Textile everywhere. 165 | 166 | if (strcasecmp(substr(__FILE__, -16), "classTextile.php") == 0) { 167 | # Try to include PHP SmartyPants. Should be in the same directory. 168 | @include_once 'smartypants.php'; 169 | # Fake Textile class. It calls Markdown instead. 170 | class Textile { 171 | function TextileThis($text, $lite='', $encode='') { 172 | if ($lite == '' && $encode == '') $text = Markdown($text); 173 | if (function_exists('SmartyPants')) $text = SmartyPants($text); 174 | return $text; 175 | } 176 | # Fake restricted version: restrictions are not supported for now. 177 | function TextileRestricted($text, $lite='', $noimage='') { 178 | return $this->TextileThis($text, $lite); 179 | } 180 | # Workaround to ensure compatibility with TextPattern 4.0.3. 181 | function blockLite($text) { return $text; } 182 | } 183 | } 184 | 185 | 186 | 187 | # 188 | # Markdown Parser Class 189 | # 190 | 191 | class Markdown_Parser { 192 | 193 | # Regex to match balanced [brackets]. 194 | # Needed to insert a maximum bracked depth while converting to PHP. 195 | var $nested_brackets_depth = 6; 196 | var $nested_brackets_re; 197 | 198 | var $nested_url_parenthesis_depth = 4; 199 | var $nested_url_parenthesis_re; 200 | 201 | # Table of hash values for escaped characters: 202 | var $escape_chars = '\`*_{}[]()>#+-.!'; 203 | var $escape_chars_re; 204 | 205 | # Change to ">" for HTML output. 206 | var $empty_element_suffix = MARKDOWN_EMPTY_ELEMENT_SUFFIX; 207 | var $tab_width = MARKDOWN_TAB_WIDTH; 208 | 209 | # Change to `true` to disallow markup or entities. 210 | var $no_markup = false; 211 | var $no_entities = false; 212 | 213 | # Predefined urls and titles for reference links and images. 214 | var $predef_urls = array(); 215 | var $predef_titles = array(); 216 | 217 | 218 | function Markdown_Parser() { 219 | # 220 | # Constructor function. Initialize appropriate member variables. 221 | # 222 | $this->_initDetab(); 223 | $this->prepareItalicsAndBold(); 224 | 225 | $this->nested_brackets_re = 226 | str_repeat('(?>[^\[\]]+|\[', $this->nested_brackets_depth). 227 | str_repeat('\])*', $this->nested_brackets_depth); 228 | 229 | $this->nested_url_parenthesis_re = 230 | str_repeat('(?>[^()\s]+|\(', $this->nested_url_parenthesis_depth). 231 | str_repeat('(?>\)))*', $this->nested_url_parenthesis_depth); 232 | 233 | $this->escape_chars_re = '['.preg_quote($this->escape_chars).']'; 234 | 235 | # Sort document, block, and span gamut in ascendent priority order. 236 | asort($this->document_gamut); 237 | asort($this->block_gamut); 238 | asort($this->span_gamut); 239 | } 240 | 241 | 242 | # Internal hashes used during transformation. 243 | var $urls = array(); 244 | var $titles = array(); 245 | var $html_hashes = array(); 246 | 247 | # Status flag to avoid invalid nesting. 248 | var $in_anchor = false; 249 | 250 | 251 | function setup() { 252 | # 253 | # Called before the transformation process starts to setup parser 254 | # states. 255 | # 256 | # Clear global hashes. 257 | $this->urls = $this->predef_urls; 258 | $this->titles = $this->predef_titles; 259 | $this->html_hashes = array(); 260 | 261 | $in_anchor = false; 262 | } 263 | 264 | function teardown() { 265 | # 266 | # Called after the transformation process to clear any variable 267 | # which may be taking up memory unnecessarly. 268 | # 269 | $this->urls = array(); 270 | $this->titles = array(); 271 | $this->html_hashes = array(); 272 | } 273 | 274 | 275 | function transform($text) { 276 | # 277 | # Main function. Performs some preprocessing on the input text 278 | # and pass it through the document gamut. 279 | # 280 | $this->setup(); 281 | 282 | # Remove UTF-8 BOM and marker character in input, if present. 283 | $text = preg_replace('{^\xEF\xBB\xBF|\x1A}', '', $text); 284 | 285 | # Standardize line endings: 286 | # DOS to Unix and Mac to Unix 287 | $text = preg_replace('{\r\n?}', "\n", $text); 288 | 289 | # Make sure $text ends with a couple of newlines: 290 | $text .= "\n\n"; 291 | 292 | # Convert all tabs to spaces. 293 | $text = $this->detab($text); 294 | 295 | # Turn block-level HTML blocks into hash entries 296 | $text = $this->hashHTMLBlocks($text); 297 | 298 | # Strip any lines consisting only of spaces and tabs. 299 | # This makes subsequent regexen easier to write, because we can 300 | # match consecutive blank lines with /\n+/ instead of something 301 | # contorted like /[ ]*\n+/ . 302 | $text = preg_replace('/^[ ]+$/m', '', $text); 303 | 304 | # Run document gamut methods. 305 | foreach ($this->document_gamut as $method => $priority) { 306 | $text = $this->$method($text); 307 | } 308 | 309 | $this->teardown(); 310 | 311 | return $text . "\n"; 312 | } 313 | 314 | var $document_gamut = array( 315 | # Strip link definitions, store in hashes. 316 | "stripLinkDefinitions" => 20, 317 | 318 | "runBasicBlockGamut" => 30, 319 | ); 320 | 321 | 322 | function stripLinkDefinitions($text) { 323 | # 324 | # Strips link definitions from text, stores the URLs and titles in 325 | # hash references. 326 | # 327 | $less_than_tab = $this->tab_width - 1; 328 | 329 | # Link defs are in the form: ^[id]: url "optional title" 330 | $text = preg_replace_callback('{ 331 | ^[ ]{0,'.$less_than_tab.'}\[(.+)\][ ]?: # id = $1 332 | [ ]* 333 | \n? # maybe *one* newline 334 | [ ]* 335 | ? # url = $2 336 | [ ]* 337 | \n? # maybe one newline 338 | [ ]* 339 | (?: 340 | (?<=\s) # lookbehind for whitespace 341 | ["(] 342 | (.*?) # title = $3 343 | [")] 344 | [ ]* 345 | )? # title is optional 346 | (?:\n+|\Z) 347 | }xm', 348 | array(&$this, '_stripLinkDefinitions_callback'), 349 | $text); 350 | return $text; 351 | } 352 | function _stripLinkDefinitions_callback($matches) { 353 | $link_id = strtolower($matches[1]); 354 | $this->urls[$link_id] = $matches[2]; 355 | $this->titles[$link_id] =& $matches[3]; 356 | return ''; # String that will replace the block 357 | } 358 | 359 | 360 | function hashHTMLBlocks($text) { 361 | if ($this->no_markup) return $text; 362 | 363 | $less_than_tab = $this->tab_width - 1; 364 | 365 | # Hashify HTML blocks: 366 | # We only want to do this for block-level HTML tags, such as headers, 367 | # lists, and tables. That's because we still want to wrap

    s around 368 | # "paragraphs" that are wrapped in non-block-level tags, such as anchors, 369 | # phrase emphasis, and spans. The list of tags we're looking for is 370 | # hard-coded: 371 | # 372 | # * List "a" is made of tags which can be both inline or block-level. 373 | # These will be treated block-level when the start tag is alone on 374 | # its line, otherwise they're not matched here and will be taken as 375 | # inline later. 376 | # * List "b" is made of tags which are always block-level; 377 | # 378 | $block_tags_a_re = 'ins|del'; 379 | $block_tags_b_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|'. 380 | 'script|noscript|form|fieldset|iframe|math|textarea'; 381 | 382 | # Regular expression for the content of a block tag. 383 | $nested_tags_level = 4; 384 | $attr = ' 385 | (?> # optional tag attributes 386 | \s # starts with whitespace 387 | (?> 388 | [^>"/]+ # text outside quotes 389 | | 390 | /+(?!>) # slash not followed by ">" 391 | | 392 | "[^"]*" # text inside double quotes (tolerate ">") 393 | | 394 | \'[^\']*\' # text inside single quotes (tolerate ">") 395 | )* 396 | )? 397 | '; 398 | $content = 399 | str_repeat(' 400 | (?> 401 | [^<]+ # content without tag 402 | | 403 | <\2 # nested opening tag 404 | '.$attr.' # attributes 405 | (?> 406 | /> 407 | | 408 | >', $nested_tags_level). # end of opening tag 409 | '.*?'. # last level nested tag content 410 | str_repeat(' 411 | # closing nested tag 412 | ) 413 | | 414 | <(?!/\2\s*> # other tags with a different name 415 | ) 416 | )*', 417 | $nested_tags_level); 418 | $content2 = str_replace('\2', '\3', $content); 419 | 420 | # First, look for nested blocks, e.g.: 421 | #

    422 | #
    423 | # tags for inner block must be indented. 424 | #
    425 | #
    426 | # 427 | # The outermost tags must start at the left margin for this to match, and 428 | # the inner nested divs must be indented. 429 | # We need to do this before the next, more liberal match, because the next 430 | # match will start at the first `
    ` and stop at the first `
    `. 431 | $text = preg_replace_callback('{(?> 432 | (?> 433 | (?<=\n\n) # Starting after a blank line 434 | | # or 435 | \A\n? # the beginning of the doc 436 | ) 437 | ( # save in $1 438 | 439 | # Match from `\n` to `\n`, handling nested tags 440 | # in between. 441 | 442 | [ ]{0,'.$less_than_tab.'} 443 | <('.$block_tags_b_re.')# start tag = $2 444 | '.$attr.'> # attributes followed by > and \n 445 | '.$content.' # content, support nesting 446 | # the matching end tag 447 | [ ]* # trailing spaces/tabs 448 | (?=\n+|\Z) # followed by a newline or end of document 449 | 450 | | # Special version for tags of group a. 451 | 452 | [ ]{0,'.$less_than_tab.'} 453 | <('.$block_tags_a_re.')# start tag = $3 454 | '.$attr.'>[ ]*\n # attributes followed by > 455 | '.$content2.' # content, support nesting 456 | # the matching end tag 457 | [ ]* # trailing spaces/tabs 458 | (?=\n+|\Z) # followed by a newline or end of document 459 | 460 | | # Special case just for
    . It was easier to make a special 461 | # case than to make the other regex more complicated. 462 | 463 | [ ]{0,'.$less_than_tab.'} 464 | <(hr) # start tag = $2 465 | '.$attr.' # attributes 466 | /?> # the matching end tag 467 | [ ]* 468 | (?=\n{2,}|\Z) # followed by a blank line or end of document 469 | 470 | | # Special case for standalone HTML comments: 471 | 472 | [ ]{0,'.$less_than_tab.'} 473 | (?s: 474 | 475 | ) 476 | [ ]* 477 | (?=\n{2,}|\Z) # followed by a blank line or end of document 478 | 479 | | # PHP and ASP-style processor instructions ( 486 | ) 487 | [ ]* 488 | (?=\n{2,}|\Z) # followed by a blank line or end of document 489 | 490 | ) 491 | )}Sxmi', 492 | array(&$this, '_hashHTMLBlocks_callback'), 493 | $text); 494 | 495 | return $text; 496 | } 497 | function _hashHTMLBlocks_callback($matches) { 498 | $text = $matches[1]; 499 | $key = $this->hashBlock($text); 500 | return "\n\n$key\n\n"; 501 | } 502 | 503 | 504 | function hashPart($text, $boundary = 'X') { 505 | # 506 | # Called whenever a tag must be hashed when a function insert an atomic 507 | # element in the text stream. Passing $text to through this function gives 508 | # a unique text-token which will be reverted back when calling unhash. 509 | # 510 | # The $boundary argument specify what character should be used to surround 511 | # the token. By convension, "B" is used for block elements that needs not 512 | # to be wrapped into paragraph tags at the end, ":" is used for elements 513 | # that are word separators and "X" is used in the general case. 514 | # 515 | # Swap back any tag hash found in $text so we do not have to `unhash` 516 | # multiple times at the end. 517 | $text = $this->unhash($text); 518 | 519 | # Then hash the block. 520 | static $i = 0; 521 | $key = "$boundary\x1A" . ++$i . $boundary; 522 | $this->html_hashes[$key] = $text; 523 | return $key; # String that will replace the tag. 524 | } 525 | 526 | 527 | function hashBlock($text) { 528 | # 529 | # Shortcut function for hashPart with block-level boundaries. 530 | # 531 | return $this->hashPart($text, 'B'); 532 | } 533 | 534 | 535 | var $block_gamut = array( 536 | # 537 | # These are all the transformations that form block-level 538 | # tags like paragraphs, headers, and list items. 539 | # 540 | "doHeaders" => 10, 541 | "doHorizontalRules" => 20, 542 | 543 | "doLists" => 40, 544 | "doCodeBlocks" => 50, 545 | "doBlockQuotes" => 60, 546 | ); 547 | 548 | function runBlockGamut($text) { 549 | # 550 | # Run block gamut tranformations. 551 | # 552 | # We need to escape raw HTML in Markdown source before doing anything 553 | # else. This need to be done for each block, and not only at the 554 | # begining in the Markdown function since hashed blocks can be part of 555 | # list items and could have been indented. Indented blocks would have 556 | # been seen as a code block in a previous pass of hashHTMLBlocks. 557 | $text = $this->hashHTMLBlocks($text); 558 | 559 | return $this->runBasicBlockGamut($text); 560 | } 561 | 562 | function runBasicBlockGamut($text) { 563 | # 564 | # Run block gamut tranformations, without hashing HTML blocks. This is 565 | # useful when HTML blocks are known to be already hashed, like in the first 566 | # whole-document pass. 567 | # 568 | foreach ($this->block_gamut as $method => $priority) { 569 | $text = $this->$method($text); 570 | } 571 | 572 | # Finally form paragraph and restore hashed blocks. 573 | $text = $this->formParagraphs($text); 574 | 575 | return $text; 576 | } 577 | 578 | 579 | function doHorizontalRules($text) { 580 | # Do Horizontal Rules: 581 | return preg_replace( 582 | '{ 583 | ^[ ]{0,3} # Leading space 584 | ([-*_]) # $1: First marker 585 | (?> # Repeated marker group 586 | [ ]{0,2} # Zero, one, or two spaces. 587 | \1 # Marker character 588 | ){2,} # Group repeated at least twice 589 | [ ]* # Tailing spaces 590 | $ # End of line. 591 | }mx', 592 | "\n".$this->hashBlock("empty_element_suffix")."\n", 593 | $text); 594 | } 595 | 596 | 597 | var $span_gamut = array( 598 | # 599 | # These are all the transformations that occur *within* block-level 600 | # tags like paragraphs, headers, and list items. 601 | # 602 | # Process character escapes, code spans, and inline HTML 603 | # in one shot. 604 | "parseSpan" => -30, 605 | 606 | # Process anchor and image tags. Images must come first, 607 | # because ![foo][f] looks like an anchor. 608 | "doImages" => 10, 609 | "doAnchors" => 20, 610 | 611 | # Make links out of things like `` 612 | # Must come after doAnchors, because you can use < and > 613 | # delimiters in inline links like [this](). 614 | "doAutoLinks" => 30, 615 | "encodeAmpsAndAngles" => 40, 616 | 617 | "doItalicsAndBold" => 50, 618 | "doHardBreaks" => 60, 619 | ); 620 | 621 | function runSpanGamut($text) { 622 | # 623 | # Run span gamut tranformations. 624 | # 625 | foreach ($this->span_gamut as $method => $priority) { 626 | $text = $this->$method($text); 627 | } 628 | 629 | return $text; 630 | } 631 | 632 | 633 | function doHardBreaks($text) { 634 | # Do hard breaks: 635 | return preg_replace_callback('/ {2,}\n/', 636 | array(&$this, '_doHardBreaks_callback'), $text); 637 | } 638 | function _doHardBreaks_callback($matches) { 639 | return $this->hashPart("empty_element_suffix\n"); 640 | } 641 | 642 | 643 | function doAnchors($text) { 644 | # 645 | # Turn Markdown link shortcuts into XHTML tags. 646 | # 647 | if ($this->in_anchor) return $text; 648 | $this->in_anchor = true; 649 | 650 | # 651 | # First, handle reference-style links: [link text] [id] 652 | # 653 | $text = preg_replace_callback('{ 654 | ( # wrap whole match in $1 655 | \[ 656 | ('.$this->nested_brackets_re.') # link text = $2 657 | \] 658 | 659 | [ ]? # one optional space 660 | (?:\n[ ]*)? # one optional newline followed by spaces 661 | 662 | \[ 663 | (.*?) # id = $3 664 | \] 665 | ) 666 | }xs', 667 | array(&$this, '_doAnchors_reference_callback'), $text); 668 | 669 | # 670 | # Next, inline-style links: [link text](url "optional title") 671 | # 672 | $text = preg_replace_callback('{ 673 | ( # wrap whole match in $1 674 | \[ 675 | ('.$this->nested_brackets_re.') # link text = $2 676 | \] 677 | \( # literal paren 678 | [ ]* 679 | (?: 680 | <(\S*)> # href = $3 681 | | 682 | ('.$this->nested_url_parenthesis_re.') # href = $4 683 | ) 684 | [ ]* 685 | ( # $5 686 | ([\'"]) # quote char = $6 687 | (.*?) # Title = $7 688 | \6 # matching quote 689 | [ ]* # ignore any spaces/tabs between closing quote and ) 690 | )? # title is optional 691 | \) 692 | ) 693 | }xs', 694 | array(&$this, '_doAnchors_inline_callback'), $text); 695 | 696 | # 697 | # Last, handle reference-style shortcuts: [link text] 698 | # These must come last in case you've also got [link text][1] 699 | # or [link text](/foo) 700 | # 701 | // $text = preg_replace_callback('{ 702 | // ( # wrap whole match in $1 703 | // \[ 704 | // ([^\[\]]+) # link text = $2; can\'t contain [ or ] 705 | // \] 706 | // ) 707 | // }xs', 708 | // array(&$this, '_doAnchors_reference_callback'), $text); 709 | 710 | $this->in_anchor = false; 711 | return $text; 712 | } 713 | function _doAnchors_reference_callback($matches) { 714 | $whole_match = $matches[1]; 715 | $link_text = $matches[2]; 716 | $link_id =& $matches[3]; 717 | 718 | if ($link_id == "") { 719 | # for shortcut links like [this][] or [this]. 720 | $link_id = $link_text; 721 | } 722 | 723 | # lower-case and turn embedded newlines into spaces 724 | $link_id = strtolower($link_id); 725 | $link_id = preg_replace('{[ ]?\n}', ' ', $link_id); 726 | 727 | if (isset($this->urls[$link_id])) { 728 | $url = $this->urls[$link_id]; 729 | $url = $this->encodeAttribute($url); 730 | 731 | $result = "titles[$link_id] ) ) { 733 | $title = $this->titles[$link_id]; 734 | $title = $this->encodeAttribute($title); 735 | $result .= " title=\"$title\""; 736 | } 737 | 738 | $link_text = $this->runSpanGamut($link_text); 739 | $result .= ">$link_text"; 740 | $result = $this->hashPart($result); 741 | } 742 | else { 743 | $result = $whole_match; 744 | } 745 | return $result; 746 | } 747 | function _doAnchors_inline_callback($matches) { 748 | $whole_match = $matches[1]; 749 | $link_text = $this->runSpanGamut($matches[2]); 750 | $url = $matches[3] == '' ? $matches[4] : $matches[3]; 751 | $title =& $matches[7]; 752 | 753 | $url = $this->encodeAttribute($url); 754 | 755 | $result = "encodeAttribute($title); 758 | $result .= " title=\"$title\""; 759 | } 760 | 761 | $link_text = $this->runSpanGamut($link_text); 762 | $result .= ">$link_text"; 763 | 764 | return $this->hashPart($result); 765 | } 766 | 767 | 768 | function doImages($text) { 769 | # 770 | # Turn Markdown image shortcuts into tags. 771 | # 772 | # 773 | # First, handle reference-style labeled images: ![alt text][id] 774 | # 775 | $text = preg_replace_callback('{ 776 | ( # wrap whole match in $1 777 | !\[ 778 | ('.$this->nested_brackets_re.') # alt text = $2 779 | \] 780 | 781 | [ ]? # one optional space 782 | (?:\n[ ]*)? # one optional newline followed by spaces 783 | 784 | \[ 785 | (.*?) # id = $3 786 | \] 787 | 788 | ) 789 | }xs', 790 | array(&$this, '_doImages_reference_callback'), $text); 791 | 792 | # 793 | # Next, handle inline images: ![alt text](url "optional title") 794 | # Don't forget: encode * and _ 795 | # 796 | $text = preg_replace_callback('{ 797 | ( # wrap whole match in $1 798 | !\[ 799 | ('.$this->nested_brackets_re.') # alt text = $2 800 | \] 801 | \s? # One optional whitespace character 802 | \( # literal paren 803 | [ ]* 804 | (?: 805 | <(\S*)> # src url = $3 806 | | 807 | ('.$this->nested_url_parenthesis_re.') # src url = $4 808 | ) 809 | [ ]* 810 | ( # $5 811 | ([\'"]) # quote char = $6 812 | (.*?) # title = $7 813 | \6 # matching quote 814 | [ ]* 815 | )? # title is optional 816 | \) 817 | ) 818 | }xs', 819 | array(&$this, '_doImages_inline_callback'), $text); 820 | 821 | return $text; 822 | } 823 | function _doImages_reference_callback($matches) { 824 | $whole_match = $matches[1]; 825 | $alt_text = $matches[2]; 826 | $link_id = strtolower($matches[3]); 827 | 828 | if ($link_id == "") { 829 | $link_id = strtolower($alt_text); # for shortcut links like ![this][]. 830 | } 831 | 832 | $alt_text = $this->encodeAttribute($alt_text); 833 | if (isset($this->urls[$link_id])) { 834 | $url = $this->encodeAttribute($this->urls[$link_id]); 835 | $result = "\"$alt_text\"";titles[$link_id])) { 837 | $title = $this->titles[$link_id]; 838 | $title = $this->encodeAttribute($title); 839 | $result .= " title=\"$title\""; 840 | } 841 | $result .= $this->empty_element_suffix; 842 | $result = $this->hashPart($result); 843 | } 844 | else { 845 | # If there's no such link ID, leave intact: 846 | $result = $whole_match; 847 | } 848 | 849 | return $result; 850 | } 851 | function _doImages_inline_callback($matches) { 852 | $whole_match = $matches[1]; 853 | $alt_text = $matches[2]; 854 | $url = $matches[3] == '' ? $matches[4] : $matches[3]; 855 | $title =& $matches[7]; 856 | 857 | $alt_text = $this->encodeAttribute($alt_text); 858 | $url = $this->encodeAttribute($url); 859 | $result = "\"$alt_text\"";encodeAttribute($title); 862 | $result .= " title=\"$title\""; # $title already quoted 863 | } 864 | $result .= $this->empty_element_suffix; 865 | 866 | return $this->hashPart($result); 867 | } 868 | 869 | 870 | function doHeaders($text) { 871 | # Setext-style headers: 872 | # Header 1 873 | # ======== 874 | # 875 | # Header 2 876 | # -------- 877 | # 878 | $text = preg_replace_callback('{ ^(.+?)[ ]*\n(=+|-+)[ ]*\n+ }mx', 879 | array(&$this, '_doHeaders_callback_setext'), $text); 880 | 881 | # atx-style headers: 882 | # # Header 1 883 | # ## Header 2 884 | # ## Header 2 with closing hashes ## 885 | # ... 886 | # ###### Header 6 887 | # 888 | $text = preg_replace_callback('{ 889 | ^(\#{1,6}) # $1 = string of #\'s 890 | [ ]* 891 | (.+?) # $2 = Header text 892 | [ ]* 893 | \#* # optional closing #\'s (not counted) 894 | \n+ 895 | }xm', 896 | array(&$this, '_doHeaders_callback_atx'), $text); 897 | 898 | return $text; 899 | } 900 | function _doHeaders_callback_setext($matches) { 901 | # Terrible hack to check we haven't found an empty list item. 902 | if ($matches[2] == '-' && preg_match('{^-(?: |$)}', $matches[1])) 903 | return $matches[0]; 904 | 905 | $level = $matches[2]{0} == '=' ? 1 : 2; 906 | $block = "".$this->runSpanGamut($matches[1]).""; 907 | return "\n" . $this->hashBlock($block) . "\n\n"; 908 | } 909 | function _doHeaders_callback_atx($matches) { 910 | $level = strlen($matches[1]); 911 | $block = "".$this->runSpanGamut($matches[2]).""; 912 | return "\n" . $this->hashBlock($block) . "\n\n"; 913 | } 914 | 915 | 916 | function doLists($text) { 917 | # 918 | # Form HTML ordered (numbered) and unordered (bulleted) lists. 919 | # 920 | $less_than_tab = $this->tab_width - 1; 921 | 922 | # Re-usable patterns to match list item bullets and number markers: 923 | $marker_ul_re = '[*+-]'; 924 | $marker_ol_re = '\d+[.]'; 925 | $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)"; 926 | 927 | $markers_relist = array($marker_ul_re, $marker_ol_re); 928 | 929 | foreach ($markers_relist as $marker_re) { 930 | # Re-usable pattern to match any entirel ul or ol list: 931 | $whole_list_re = ' 932 | ( # $1 = whole list 933 | ( # $2 934 | [ ]{0,'.$less_than_tab.'} 935 | ('.$marker_re.') # $3 = first list item marker 936 | [ ]+ 937 | ) 938 | (?s:.+?) 939 | ( # $4 940 | \z 941 | | 942 | \n{2,} 943 | (?=\S) 944 | (?! # Negative lookahead for another list item marker 945 | [ ]* 946 | '.$marker_re.'[ ]+ 947 | ) 948 | ) 949 | ) 950 | '; // mx 951 | 952 | # We use a different prefix before nested lists than top-level lists. 953 | # See extended comment in _ProcessListItems(). 954 | 955 | if ($this->list_level) { 956 | $text = preg_replace_callback('{ 957 | ^ 958 | '.$whole_list_re.' 959 | }mx', 960 | array(&$this, '_doLists_callback'), $text); 961 | } 962 | else { 963 | $text = preg_replace_callback('{ 964 | (?:(?<=\n)\n|\A\n?) # Must eat the newline 965 | '.$whole_list_re.' 966 | }mx', 967 | array(&$this, '_doLists_callback'), $text); 968 | } 969 | } 970 | 971 | return $text; 972 | } 973 | function _doLists_callback($matches) { 974 | # Re-usable patterns to match list item bullets and number markers: 975 | $marker_ul_re = '[*+-]'; 976 | $marker_ol_re = '\d+[.]'; 977 | $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)"; 978 | 979 | $list = $matches[1]; 980 | $list_type = preg_match("/$marker_ul_re/", $matches[3]) ? "ul" : "ol"; 981 | 982 | $marker_any_re = ( $list_type == "ul" ? $marker_ul_re : $marker_ol_re ); 983 | 984 | $list .= "\n"; 985 | $result = $this->processListItems($list, $marker_any_re); 986 | 987 | $result = $this->hashBlock("<$list_type>\n" . $result . ""); 988 | return "\n". $result ."\n\n"; 989 | } 990 | 991 | var $list_level = 0; 992 | 993 | function processListItems($list_str, $marker_any_re) { 994 | # 995 | # Process the contents of a single ordered or unordered list, splitting it 996 | # into individual list items. 997 | # 998 | # The $this->list_level global keeps track of when we're inside a list. 999 | # Each time we enter a list, we increment it; when we leave a list, 1000 | # we decrement. If it's zero, we're not in a list anymore. 1001 | # 1002 | # We do this because when we're not inside a list, we want to treat 1003 | # something like this: 1004 | # 1005 | # I recommend upgrading to version 1006 | # 8. Oops, now this line is treated 1007 | # as a sub-list. 1008 | # 1009 | # As a single paragraph, despite the fact that the second line starts 1010 | # with a digit-period-space sequence. 1011 | # 1012 | # Whereas when we're inside a list (or sub-list), that line will be 1013 | # treated as the start of a sub-list. What a kludge, huh? This is 1014 | # an aspect of Markdown's syntax that's hard to parse perfectly 1015 | # without resorting to mind-reading. Perhaps the solution is to 1016 | # change the syntax rules such that sub-lists must start with a 1017 | # starting cardinal number; e.g. "1." or "a.". 1018 | 1019 | $this->list_level++; 1020 | 1021 | # trim trailing blank lines: 1022 | $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str); 1023 | 1024 | $list_str = preg_replace_callback('{ 1025 | (\n)? # leading line = $1 1026 | (^[ ]*) # leading whitespace = $2 1027 | ('.$marker_any_re.' # list marker and space = $3 1028 | (?:[ ]+|(?=\n)) # space only required if item is not empty 1029 | ) 1030 | ((?s:.*?)) # list item text = $4 1031 | (?:(\n+(?=\n))|\n) # tailing blank line = $5 1032 | (?= \n* (\z | \2 ('.$marker_any_re.') (?:[ ]+|(?=\n)))) 1033 | }xm', 1034 | array(&$this, '_processListItems_callback'), $list_str); 1035 | 1036 | $this->list_level--; 1037 | return $list_str; 1038 | } 1039 | function _processListItems_callback($matches) { 1040 | $item = $matches[4]; 1041 | $leading_line =& $matches[1]; 1042 | $leading_space =& $matches[2]; 1043 | $marker_space = $matches[3]; 1044 | $tailing_blank_line =& $matches[5]; 1045 | 1046 | if ($leading_line || $tailing_blank_line || 1047 | preg_match('/\n{2,}/', $item)) 1048 | { 1049 | # Replace marker with the appropriate whitespace indentation 1050 | $item = $leading_space . str_repeat(' ', strlen($marker_space)) . $item; 1051 | $item = $this->runBlockGamut($this->outdent($item)."\n"); 1052 | } 1053 | else { 1054 | # Recursion for sub-lists: 1055 | $item = $this->doLists($this->outdent($item)); 1056 | $item = preg_replace('/\n+$/', '', $item); 1057 | $item = $this->runSpanGamut($item); 1058 | } 1059 | 1060 | return "
  • " . $item . "
  • \n"; 1061 | } 1062 | 1063 | 1064 | function doCodeBlocks($text) { 1065 | # 1066 | # Process Markdown `
    ` blocks.
    1067 | 	#
    1068 | 		$text = preg_replace_callback('{
    1069 | 				(?:\n\n|\A\n?)
    1070 | 				(	            # $1 = the code block -- one or more lines, starting with a space/tab
    1071 | 				  (?>
    1072 | 					[ ]{'.$this->tab_width.'}  # Lines must start with a tab or a tab-width of spaces
    1073 | 					.*\n+
    1074 | 				  )+
    1075 | 				)
    1076 | 				((?=^[ ]{0,'.$this->tab_width.'}\S)|\Z)	# Lookahead for non-space at line-start, or end of doc
    1077 | 			}xm',
    1078 | 			array(&$this, '_doCodeBlocks_callback'), $text);
    1079 | 
    1080 | 		return $text;
    1081 | 	}
    1082 | 	function _doCodeBlocks_callback($matches) {
    1083 | 		$codeblock = $matches[1];
    1084 | 
    1085 | 		$codeblock = $this->outdent($codeblock);
    1086 | 		$codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
    1087 | 
    1088 | 		# trim leading newlines and trailing newlines
    1089 | 		$codeblock = preg_replace('/\A\n+|\n+\z/', '', $codeblock);
    1090 | 
    1091 | 		$codeblock = "
    $codeblock\n
    "; 1092 | return "\n\n".$this->hashBlock($codeblock)."\n\n"; 1093 | } 1094 | 1095 | 1096 | function makeCodeSpan($code) { 1097 | # 1098 | # Create a code span markup for $code. Called from handleSpanToken. 1099 | # 1100 | $code = htmlspecialchars(trim($code), ENT_NOQUOTES); 1101 | return $this->hashPart("$code"); 1102 | } 1103 | 1104 | 1105 | var $em_relist = array( 1106 | '' => '(?:(? '(?<=\S)(? '(?<=\S)(? '(?:(? '(?<=\S)(? '(?<=\S)(? '(?:(? '(?<=\S)(? '(?<=\S)(?em_relist as $em => $em_re) { 1128 | foreach ($this->strong_relist as $strong => $strong_re) { 1129 | # Construct list of allowed token expressions. 1130 | $token_relist = array(); 1131 | if (isset($this->em_strong_relist["$em$strong"])) { 1132 | $token_relist[] = $this->em_strong_relist["$em$strong"]; 1133 | } 1134 | $token_relist[] = $em_re; 1135 | $token_relist[] = $strong_re; 1136 | 1137 | # Construct master expression from list. 1138 | $token_re = '{('. implode('|', $token_relist) .')}'; 1139 | $this->em_strong_prepared_relist["$em$strong"] = $token_re; 1140 | } 1141 | } 1142 | } 1143 | 1144 | function doItalicsAndBold($text) { 1145 | $token_stack = array(''); 1146 | $text_stack = array(''); 1147 | $em = ''; 1148 | $strong = ''; 1149 | $tree_char_em = false; 1150 | 1151 | while (1) { 1152 | # 1153 | # Get prepared regular expression for seraching emphasis tokens 1154 | # in current context. 1155 | # 1156 | $token_re = $this->em_strong_prepared_relist["$em$strong"]; 1157 | 1158 | # 1159 | # Each loop iteration search for the next emphasis token. 1160 | # Each token is then passed to handleSpanToken. 1161 | # 1162 | $parts = preg_split($token_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE); 1163 | $text_stack[0] .= $parts[0]; 1164 | $token =& $parts[1]; 1165 | $text =& $parts[2]; 1166 | 1167 | if (empty($token)) { 1168 | # Reached end of text span: empty stack without emitting. 1169 | # any more emphasis. 1170 | while ($token_stack[0]) { 1171 | $text_stack[1] .= array_shift($token_stack); 1172 | $text_stack[0] .= array_shift($text_stack); 1173 | } 1174 | break; 1175 | } 1176 | 1177 | $token_len = strlen($token); 1178 | if ($tree_char_em) { 1179 | # Reached closing marker while inside a three-char emphasis. 1180 | if ($token_len == 3) { 1181 | # Three-char closing marker, close em and strong. 1182 | array_shift($token_stack); 1183 | $span = array_shift($text_stack); 1184 | $span = $this->runSpanGamut($span); 1185 | $span = "$span"; 1186 | $text_stack[0] .= $this->hashPart($span); 1187 | $em = ''; 1188 | $strong = ''; 1189 | } else { 1190 | # Other closing marker: close one em or strong and 1191 | # change current token state to match the other 1192 | $token_stack[0] = str_repeat($token{0}, 3-$token_len); 1193 | $tag = $token_len == 2 ? "strong" : "em"; 1194 | $span = $text_stack[0]; 1195 | $span = $this->runSpanGamut($span); 1196 | $span = "<$tag>$span"; 1197 | $text_stack[0] = $this->hashPart($span); 1198 | $$tag = ''; # $$tag stands for $em or $strong 1199 | } 1200 | $tree_char_em = false; 1201 | } else if ($token_len == 3) { 1202 | if ($em) { 1203 | # Reached closing marker for both em and strong. 1204 | # Closing strong marker: 1205 | for ($i = 0; $i < 2; ++$i) { 1206 | $shifted_token = array_shift($token_stack); 1207 | $tag = strlen($shifted_token) == 2 ? "strong" : "em"; 1208 | $span = array_shift($text_stack); 1209 | $span = $this->runSpanGamut($span); 1210 | $span = "<$tag>$span"; 1211 | $text_stack[0] .= $this->hashPart($span); 1212 | $$tag = ''; # $$tag stands for $em or $strong 1213 | } 1214 | } else { 1215 | # Reached opening three-char emphasis marker. Push on token 1216 | # stack; will be handled by the special condition above. 1217 | $em = $token{0}; 1218 | $strong = "$em$em"; 1219 | array_unshift($token_stack, $token); 1220 | array_unshift($text_stack, ''); 1221 | $tree_char_em = true; 1222 | } 1223 | } else if ($token_len == 2) { 1224 | if ($strong) { 1225 | # Unwind any dangling emphasis marker: 1226 | if (strlen($token_stack[0]) == 1) { 1227 | $text_stack[1] .= array_shift($token_stack); 1228 | $text_stack[0] .= array_shift($text_stack); 1229 | } 1230 | # Closing strong marker: 1231 | array_shift($token_stack); 1232 | $span = array_shift($text_stack); 1233 | $span = $this->runSpanGamut($span); 1234 | $span = "$span"; 1235 | $text_stack[0] .= $this->hashPart($span); 1236 | $strong = ''; 1237 | } else { 1238 | array_unshift($token_stack, $token); 1239 | array_unshift($text_stack, ''); 1240 | $strong = $token; 1241 | } 1242 | } else { 1243 | # Here $token_len == 1 1244 | if ($em) { 1245 | if (strlen($token_stack[0]) == 1) { 1246 | # Closing emphasis marker: 1247 | array_shift($token_stack); 1248 | $span = array_shift($text_stack); 1249 | $span = $this->runSpanGamut($span); 1250 | $span = "$span"; 1251 | $text_stack[0] .= $this->hashPart($span); 1252 | $em = ''; 1253 | } else { 1254 | $text_stack[0] .= $token; 1255 | } 1256 | } else { 1257 | array_unshift($token_stack, $token); 1258 | array_unshift($text_stack, ''); 1259 | $em = $token; 1260 | } 1261 | } 1262 | } 1263 | return $text_stack[0]; 1264 | } 1265 | 1266 | 1267 | function doBlockQuotes($text) { 1268 | $text = preg_replace_callback('/ 1269 | ( # Wrap whole match in $1 1270 | (?> 1271 | ^[ ]*>[ ]? # ">" at the start of a line 1272 | .+\n # rest of the first line 1273 | (.+\n)* # subsequent consecutive lines 1274 | \n* # blanks 1275 | )+ 1276 | ) 1277 | /xm', 1278 | array(&$this, '_doBlockQuotes_callback'), $text); 1279 | 1280 | return $text; 1281 | } 1282 | function _doBlockQuotes_callback($matches) { 1283 | $bq = $matches[1]; 1284 | # trim one level of quoting - trim whitespace-only lines 1285 | $bq = preg_replace('/^[ ]*>[ ]?|^[ ]+$/m', '', $bq); 1286 | $bq = $this->runBlockGamut($bq); # recurse 1287 | 1288 | $bq = preg_replace('/^/m', " ", $bq); 1289 | # These leading spaces cause problem with
     content, 
    1290 | 		# so we need to fix that:
    1291 | 		$bq = preg_replace_callback('{(\s*
    .+?
    )}sx', 1292 | array(&$this, '_doBlockQuotes_callback2'), $bq); 1293 | 1294 | return "\n". $this->hashBlock("
    \n$bq\n
    ")."\n\n"; 1295 | } 1296 | function _doBlockQuotes_callback2($matches) { 1297 | $pre = $matches[1]; 1298 | $pre = preg_replace('/^ /m', '', $pre); 1299 | return $pre; 1300 | } 1301 | 1302 | 1303 | function formParagraphs($text) { 1304 | # 1305 | # Params: 1306 | # $text - string to process with html

    tags 1307 | # 1308 | # Strip leading and trailing lines: 1309 | $text = preg_replace('/\A\n+|\n+\z/', '', $text); 1310 | 1311 | $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY); 1312 | 1313 | # 1314 | # Wrap

    tags and unhashify HTML blocks 1315 | # 1316 | foreach ($grafs as $key => $value) { 1317 | if (!preg_match('/^B\x1A[0-9]+B$/', $value)) { 1318 | # Is a paragraph. 1319 | $value = $this->runSpanGamut($value); 1320 | $value = preg_replace('/^([ ]*)/', "

    ", $value); 1321 | $value .= "

    "; 1322 | $grafs[$key] = $this->unhash($value); 1323 | } 1324 | else { 1325 | # Is a block. 1326 | # Modify elements of @grafs in-place... 1327 | $graf = $value; 1328 | $block = $this->html_hashes[$graf]; 1329 | $graf = $block; 1330 | // if (preg_match('{ 1331 | // \A 1332 | // ( # $1 =
    tag 1333 | //
    ]* 1335 | // \b 1336 | // markdown\s*=\s* ([\'"]) # $2 = attr quote char 1337 | // 1 1338 | // \2 1339 | // [^>]* 1340 | // > 1341 | // ) 1342 | // ( # $3 = contents 1343 | // .* 1344 | // ) 1345 | // (
    ) # $4 = closing tag 1346 | // \z 1347 | // }xs', $block, $matches)) 1348 | // { 1349 | // list(, $div_open, , $div_content, $div_close) = $matches; 1350 | // 1351 | // # We can't call Markdown(), because that resets the hash; 1352 | // # that initialization code should be pulled into its own sub, though. 1353 | // $div_content = $this->hashHTMLBlocks($div_content); 1354 | // 1355 | // # Run document gamut methods on the content. 1356 | // foreach ($this->document_gamut as $method => $priority) { 1357 | // $div_content = $this->$method($div_content); 1358 | // } 1359 | // 1360 | // $div_open = preg_replace( 1361 | // '{\smarkdown\s*=\s*([\'"]).+?\1}', '', $div_open); 1362 | // 1363 | // $graf = $div_open . "\n" . $div_content . "\n" . $div_close; 1364 | // } 1365 | $grafs[$key] = $graf; 1366 | } 1367 | } 1368 | 1369 | return implode("\n\n", $grafs); 1370 | } 1371 | 1372 | 1373 | function encodeAttribute($text) { 1374 | # 1375 | # Encode text for a double-quoted HTML attribute. This function 1376 | # is *not* suitable for attributes enclosed in single quotes. 1377 | # 1378 | $text = $this->encodeAmpsAndAngles($text); 1379 | $text = str_replace('"', '"', $text); 1380 | return $text; 1381 | } 1382 | 1383 | 1384 | function encodeAmpsAndAngles($text) { 1385 | # 1386 | # Smart processing for ampersands and angle brackets that need to 1387 | # be encoded. Valid character entities are left alone unless the 1388 | # no-entities mode is set. 1389 | # 1390 | if ($this->no_entities) { 1391 | $text = str_replace('&', '&', $text); 1392 | } else { 1393 | # Ampersand-encoding based entirely on Nat Irons's Amputator 1394 | # MT plugin: 1395 | $text = preg_replace('/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/', 1396 | '&', $text);; 1397 | } 1398 | # Encode remaining <'s 1399 | $text = str_replace('<', '<', $text); 1400 | 1401 | return $text; 1402 | } 1403 | 1404 | 1405 | function doAutoLinks($text) { 1406 | $text = preg_replace_callback('{<((https?|ftp|dict):[^\'">\s]+)>}i', 1407 | array(&$this, '_doAutoLinks_url_callback'), $text); 1408 | 1409 | # Email addresses: 1410 | $text = preg_replace_callback('{ 1411 | < 1412 | (?:mailto:)? 1413 | ( 1414 | [-.\w\x80-\xFF]+ 1415 | \@ 1416 | [-a-z0-9\x80-\xFF]+(\.[-a-z0-9\x80-\xFF]+)*\.[a-z]+ 1417 | ) 1418 | > 1419 | }xi', 1420 | array(&$this, '_doAutoLinks_email_callback'), $text); 1421 | 1422 | return $text; 1423 | } 1424 | function _doAutoLinks_url_callback($matches) { 1425 | $url = $this->encodeAttribute($matches[1]); 1426 | $link = "$url"; 1427 | return $this->hashPart($link); 1428 | } 1429 | function _doAutoLinks_email_callback($matches) { 1430 | $address = $matches[1]; 1431 | $link = $this->encodeEmailAddress($address); 1432 | return $this->hashPart($link); 1433 | } 1434 | 1435 | 1436 | function encodeEmailAddress($addr) { 1437 | # 1438 | # Input: an email address, e.g. "foo@example.com" 1439 | # 1440 | # Output: the email address as a mailto link, with each character 1441 | # of the address encoded as either a decimal or hex entity, in 1442 | # the hopes of foiling most address harvesting spam bots. E.g.: 1443 | # 1444 | #

    foo@exampl 1447 | # e.com

    1448 | # 1449 | # Based by a filter by Matthew Wickline, posted to BBEdit-Talk. 1450 | # With some optimizations by Milian Wolff. 1451 | # 1452 | $addr = "mailto:" . $addr; 1453 | $chars = preg_split('/(? $char) { 1457 | $ord = ord($char); 1458 | # Ignore non-ascii chars. 1459 | if ($ord < 128) { 1460 | $r = ($seed * (1 + $key)) % 100; # Pseudo-random function. 1461 | # roughly 10% raw, 45% hex, 45% dec 1462 | # '@' *must* be encoded. I insist. 1463 | if ($r > 90 && $char != '@') /* do nothing */; 1464 | else if ($r < 45) $chars[$key] = '&#x'.dechex($ord).';'; 1465 | else $chars[$key] = '&#'.$ord.';'; 1466 | } 1467 | } 1468 | 1469 | $addr = implode('', $chars); 1470 | $text = implode('', array_slice($chars, 7)); # text without `mailto:` 1471 | $addr = "$text"; 1472 | 1473 | return $addr; 1474 | } 1475 | 1476 | 1477 | function parseSpan($str) { 1478 | # 1479 | # Take the string $str and parse it into tokens, hashing embeded HTML, 1480 | # escaped characters and handling code spans. 1481 | # 1482 | $output = ''; 1483 | 1484 | $span_re = '{ 1485 | ( 1486 | \\\\'.$this->escape_chars_re.' 1487 | | 1488 | (?no_markup ? '' : ' 1491 | | 1492 | # comment 1493 | | 1494 | <\?.*?\?> | <%.*?%> # processing instruction 1495 | | 1496 | <[/!$]?[-a-zA-Z0-9:]+ # regular tags 1497 | (?> 1498 | \s 1499 | (?>[^"\'>]+|"[^"]*"|\'[^\']*\')* 1500 | )? 1501 | > 1502 | ').' 1503 | ) 1504 | }xs'; 1505 | 1506 | while (1) { 1507 | # 1508 | # Each loop iteration seach for either the next tag, the next 1509 | # openning code span marker, or the next escaped character. 1510 | # Each token is then passed to handleSpanToken. 1511 | # 1512 | $parts = preg_split($span_re, $str, 2, PREG_SPLIT_DELIM_CAPTURE); 1513 | 1514 | # Create token from text preceding tag. 1515 | if ($parts[0] != "") { 1516 | $output .= $parts[0]; 1517 | } 1518 | 1519 | # Check if we reach the end. 1520 | if (isset($parts[1])) { 1521 | $output .= $this->handleSpanToken($parts[1], $parts[2]); 1522 | $str = $parts[2]; 1523 | } 1524 | else { 1525 | break; 1526 | } 1527 | } 1528 | 1529 | return $output; 1530 | } 1531 | 1532 | 1533 | function handleSpanToken($token, &$str) { 1534 | # 1535 | # Handle $token provided by parseSpan by determining its nature and 1536 | # returning the corresponding value that should replace it. 1537 | # 1538 | switch ($token{0}) { 1539 | case "\\": 1540 | return $this->hashPart("&#". ord($token{1}). ";"); 1541 | case "`": 1542 | # Search for end marker in remaining text. 1543 | if (preg_match('/^(.*?[^`])'.preg_quote($token).'(?!`)(.*)$/sm', 1544 | $str, $matches)) 1545 | { 1546 | $str = $matches[2]; 1547 | $codespan = $this->makeCodeSpan($matches[1]); 1548 | return $this->hashPart($codespan); 1549 | } 1550 | return $token; // return as text since no ending marker found. 1551 | default: 1552 | return $this->hashPart($token); 1553 | } 1554 | } 1555 | 1556 | 1557 | function outdent($text) { 1558 | # 1559 | # Remove one level of line-leading tabs or spaces 1560 | # 1561 | return preg_replace('/^(\t|[ ]{1,'.$this->tab_width.'})/m', '', $text); 1562 | } 1563 | 1564 | 1565 | # String length function for detab. `_initDetab` will create a function to 1566 | # hanlde UTF-8 if the default function does not exist. 1567 | var $utf8_strlen = 'mb_strlen'; 1568 | 1569 | function detab($text) { 1570 | # 1571 | # Replace tabs with the appropriate amount of space. 1572 | # 1573 | # For each line we separate the line in blocks delemited by 1574 | # tab characters. Then we reconstruct every line by adding the 1575 | # appropriate number of space between each blocks. 1576 | 1577 | $text = preg_replace_callback('/^.*\t.*$/m', 1578 | array(&$this, '_detab_callback'), $text); 1579 | 1580 | return $text; 1581 | } 1582 | function _detab_callback($matches) { 1583 | $line = $matches[0]; 1584 | $strlen = $this->utf8_strlen; # strlen function for UTF-8. 1585 | 1586 | # Split in blocks. 1587 | $blocks = explode("\t", $line); 1588 | # Add each blocks to the line. 1589 | $line = $blocks[0]; 1590 | unset($blocks[0]); # Do not add first block twice. 1591 | foreach ($blocks as $block) { 1592 | # Calculate amount of space, insert spaces, insert block. 1593 | $amount = $this->tab_width - 1594 | $strlen($line, 'UTF-8') % $this->tab_width; 1595 | $line .= str_repeat(" ", $amount) . $block; 1596 | } 1597 | return $line; 1598 | } 1599 | function _initDetab() { 1600 | # 1601 | # Check for the availability of the function in the `utf8_strlen` property 1602 | # (initially `mb_strlen`). If the function is not available, create a 1603 | # function that will loosely count the number of UTF-8 characters with a 1604 | # regular expression. 1605 | # 1606 | if (function_exists($this->utf8_strlen)) return; 1607 | $this->utf8_strlen = create_function('$text', 'return preg_match_all( 1608 | "/[\\\\x00-\\\\xBF]|[\\\\xC0-\\\\xFF][\\\\x80-\\\\xBF]*/", 1609 | $text, $m);'); 1610 | } 1611 | 1612 | 1613 | function unhash($text) { 1614 | # 1615 | # Swap back in all the tags hashed by _HashHTMLBlocks. 1616 | # 1617 | return preg_replace_callback('/(.)\x1A[0-9]+\1/', 1618 | array(&$this, '_unhash_callback'), $text); 1619 | } 1620 | function _unhash_callback($matches) { 1621 | return $this->html_hashes[$matches[0]]; 1622 | } 1623 | 1624 | } 1625 | 1626 | /* 1627 | 1628 | PHP Markdown 1629 | ============ 1630 | 1631 | Description 1632 | ----------- 1633 | 1634 | This is a PHP translation of the original Markdown formatter written in 1635 | Perl by John Gruber. 1636 | 1637 | Markdown is a text-to-HTML filter; it translates an easy-to-read / 1638 | easy-to-write structured text format into HTML. Markdown's text format 1639 | is most similar to that of plain text email, and supports features such 1640 | as headers, *emphasis*, code blocks, blockquotes, and links. 1641 | 1642 | Markdown's syntax is designed not as a generic markup language, but 1643 | specifically to serve as a front-end to (X)HTML. You can use span-level 1644 | HTML tags anywhere in a Markdown document, and you can use block level 1645 | HTML tags (like
    and as well). 1646 | 1647 | For more information about Markdown's syntax, see: 1648 | 1649 | 1650 | 1651 | 1652 | Bugs 1653 | ---- 1654 | 1655 | To file bug reports please send email to: 1656 | 1657 | 1658 | 1659 | Please include with your report: (1) the example input; (2) the output you 1660 | expected; (3) the output Markdown actually produced. 1661 | 1662 | 1663 | Version History 1664 | --------------- 1665 | 1666 | See the readme file for detailed release notes for this version. 1667 | 1668 | 1669 | Copyright and License 1670 | --------------------- 1671 | 1672 | PHP Markdown 1673 | Copyright (c) 2004-2008 Michel Fortin 1674 | 1675 | All rights reserved. 1676 | 1677 | Based on Markdown 1678 | Copyright (c) 2003-2006 John Gruber 1679 | 1680 | All rights reserved. 1681 | 1682 | Redistribution and use in source and binary forms, with or without 1683 | modification, are permitted provided that the following conditions are 1684 | met: 1685 | 1686 | * Redistributions of source code must retain the above copyright notice, 1687 | this list of conditions and the following disclaimer. 1688 | 1689 | * Redistributions in binary form must reproduce the above copyright 1690 | notice, this list of conditions and the following disclaimer in the 1691 | documentation and/or other materials provided with the distribution. 1692 | 1693 | * Neither the name "Markdown" nor the names of its contributors may 1694 | be used to endorse or promote products derived from this software 1695 | without specific prior written permission. 1696 | 1697 | This software is provided by the copyright holders and contributors "as 1698 | is" and any express or implied warranties, including, but not limited 1699 | to, the implied warranties of merchantability and fitness for a 1700 | particular purpose are disclaimed. In no event shall the copyright owner 1701 | or contributors be liable for any direct, indirect, incidental, special, 1702 | exemplary, or consequential damages (including, but not limited to, 1703 | procurement of substitute goods or services; loss of use, data, or 1704 | profits; or business interruption) however caused and on any theory of 1705 | liability, whether in contract, strict liability, or tort (including 1706 | negligence or otherwise) arising in any way out of the use of this 1707 | software, even if advised of the possibility of such damage. 1708 | 1709 | */ 1710 | ?> 1711 | --------------------------------------------------------------------------------