├── .gitattributes ├── .travis.yml ├── LICENSE ├── README.md ├── composer.json ├── pangu-cli.php ├── pangu-test.php ├── pangu.php └── phpunit.xml /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | sudo: false 4 | 5 | php: 6 | - 7.1 7 | - 7.2 8 | - hhvm 9 | 10 | install: 11 | - travis_retry composer install --no-interaction --no-suggest 12 | 13 | script: ./vendor/bin/phpunit 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Rakume Hayashi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | pangu.php 2 | === 3 | ![TravisCI](https://travis-ci.org/Kunr/pangu.php.svg) 4 | 5 | Paranoid text spacing for good readability, to automatically insert whitespace between CJK (Chinese, Japanese, Korean) and half-width characters (alphabetical letters, numerical digits and symbols). 6 | 7 | * Go version: [pangu.go](https://github.com/vinta/pangu) 8 | * Java version: [pangu.java](https://github.com/vinta/pangu.java) 9 | * JavaScript version: [pangu.js](https://github.com/vinta/paranoid-auto-spacing) 10 | * Node.js version: [pangu.node](https://github.com/huei90/pangu.node) 11 | * Object-C version [pangu.objective-c](https://github.com/Cee/pangu.objective-c) 12 | * Python version: [pangu.py](https://github.com/vinta/pangu.py) 13 | * Ruby version: [pangu.rb](https://github.com/dlackty/pangu.rb) 14 | 15 | ## Installation 16 | 17 | Using composer 18 | ``` 19 | composer require kunr/pangu.php 20 | ``` 21 | 22 | Or manually 23 | 24 | ``` 25 | git clone https://github.com/Kunr/pangu.php.git 26 | ``` 27 | 28 | ## Usage 29 | 30 | Require `pangu.php` 31 | ``` 32 | require 'pangu.php'; 33 | echo pangu('Jackie的鼻子有幾個?123個!'); // Jackie 的鼻子有幾個?123 個! 34 | ``` 35 | 36 | Or 37 | 38 | Use `pangu-cli.php` 39 | ``` 40 | php pangu-cli.php "Jackie的鼻子有幾個?123個!" //// Jackie 的鼻子有幾個?123 個! 41 | ``` 42 | 43 | ## License 44 | Released under the MIT License. 45 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "clanceylin/pangu.php", 3 | "description": "Paranoid text spacing in PHP", 4 | "license": "MIT", 5 | "require": { 6 | "php": ">=5.3.0" 7 | }, 8 | "authors": [ 9 | { 10 | "name": "Clancey Lin", 11 | "email": "h@rakume.com", 12 | "homepage": "http://rakume.com" 13 | } 14 | ], 15 | "require-dev": { 16 | "phpunit/phpunit": "^6.5" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /pangu-cli.php: -------------------------------------------------------------------------------- 1 | assertEquals('請問 Jackie 的鼻子有幾個?123 個!', pangu('請問Jackie的鼻子有幾個?123個!')); 10 | $this->assertEquals('請問 Jackie 的鼻子有幾個?123 個!', pangu('請問 Jackie 的鼻子有幾個?123 個!')); 11 | } 12 | 13 | 14 | public function testTilde() { 15 | $this->assertEquals('前面~ 後面', pangu('前面~後面')); 16 | $this->assertEquals('前面 ~ 後面', pangu('前面 ~ 後面')); 17 | } 18 | 19 | 20 | public function testBackQuote() { 21 | $this->assertEquals('前面 ` 後面', pangu('前面`後面')); 22 | } 23 | 24 | 25 | public function testExclamationMark() { 26 | $this->assertEquals('前面! 後面', pangu('前面!後面')); 27 | $this->assertEquals('前面! 後面', pangu('前面! 後面')); 28 | $this->assertEquals('前面 ! 後面', pangu('前面 ! 後面')); 29 | } 30 | 31 | 32 | public function testAt1() { 33 | $this->assertEquals('請 @vinta 吃大便', pangu('請@vinta吃大便')); 34 | } 35 | 36 | 37 | public function testAt2() { 38 | $this->assertEquals('請 @陳上進 吃大便', pangu('請@陳上進 吃大便')); 39 | } 40 | 41 | 42 | public function testHash1() { 43 | $this->assertEquals('前面 #H2G2 後面', pangu('前面#H2G2後面')); 44 | } 45 | 46 | 47 | public function testHash2() { 48 | $this->assertEquals('前面 #銀河便車指南 後面', pangu('前面#銀河便車指南 後面')); 49 | } 50 | 51 | 52 | public function testHash3() { 53 | $this->assertEquals('前面 #銀河公車指南 #銀河大客車指南 後面', pangu('前面#銀河公車指南 #銀河大客車指南 後面')); 54 | } 55 | 56 | 57 | public function testHash4() { 58 | $this->assertEquals('前面 #銀河閃電霹靂車指南# 後面', pangu('前面#銀河閃電霹靂車指南#後面')); 59 | } 60 | 61 | 62 | public function testDollar() { 63 | $this->assertEquals('前面 $ 後面', pangu('前面$後面')); 64 | } 65 | 66 | 67 | public function testPercent() { 68 | $this->assertEquals('前面 % 後面', pangu('前面%後面')); 69 | } 70 | 71 | 72 | public function testCarat() { 73 | $this->assertEquals('前面 ^ 後面', pangu('前面^後面')); 74 | } 75 | 76 | 77 | public function testAmpersand() { 78 | $this->assertEquals('前面 & 後面', pangu('前面&後面')); 79 | } 80 | 81 | 82 | public function testAsterisk() { 83 | $this->assertEquals('前面 * 後面', pangu('前面*後面')); 84 | } 85 | 86 | 87 | public function testParenthesis() { 88 | $this->assertEquals('前面 ( 後面', pangu('前面(後面')); 89 | $this->assertEquals('前面 ( 後面', pangu('前面 ( 後面')); 90 | $this->assertEquals('前面 ) 後面', pangu('前面)後面')); 91 | $this->assertEquals('前面 ) 後面', pangu('前面 ) 後面')); 92 | } 93 | 94 | 95 | public function testParenthesisPair() { 96 | $this->assertEquals('前面 (中文 123 漢字) 後面', pangu('前面(中文123漢字)後面')); 97 | $this->assertEquals('前面 (中文 123) 後面', pangu('前面(中文123)後面')); 98 | $this->assertEquals('前面 (123 漢字) 後面', pangu('前面(123漢字)後面')); 99 | $this->assertEquals('前面 (中文 123 漢字) tail', pangu('前面(中文123漢字) tail')); 100 | $this->assertEquals('head (中文 123 漢字) 後面', pangu('head (中文123漢字)後面')); 101 | $this->assertEquals('head (中文 123 漢字) tail', pangu('head (中文123漢字) tail')); 102 | } 103 | 104 | 105 | public function testMinus() { 106 | $this->assertEquals('前面 - 後面', pangu('前面-後面')); 107 | } 108 | 109 | 110 | public function testUnderscore() { 111 | $this->assertEquals('前面_後面', pangu('前面_後面')); 112 | $this->assertEquals('前面 _ 後面', pangu('前面 _ 後面')); 113 | } 114 | 115 | 116 | public function testPlus() { 117 | $this->assertEquals('前面 + 後面', pangu('前面+後面')); 118 | } 119 | 120 | 121 | public function testEqual() { 122 | $this->assertEquals('前面 = 後面', pangu('前面=後面')); 123 | } 124 | 125 | 126 | public function testBrace() { 127 | $this->assertEquals('前面 { 後面', pangu('前面{後面')); 128 | } 129 | 130 | 131 | public function testBracket() { 132 | $this->assertEquals('前面 [ 後面', pangu('前面[後面')); 133 | } 134 | 135 | 136 | public function testPipe() { 137 | $this->assertEquals('前面 | 後面', pangu('前面|後面')); 138 | } 139 | 140 | 141 | public function testBackslash() { 142 | $this->assertEquals('前面 \\ 後面', pangu('前面\\後面')); 143 | } 144 | 145 | 146 | public function testColon() { 147 | $this->assertEquals('前面: 後面', pangu('前面:後面')); 148 | $this->assertEquals('前面: 後面', pangu('前面: 後面')); 149 | $this->assertEquals('前面 : 後面', pangu('前面 : 後面')); 150 | } 151 | 152 | 153 | public function testSemicolon() { 154 | $this->assertEquals('前面; 後面', pangu('前面;後面')); 155 | $this->assertEquals('前面; 後面', pangu('前面; 後面')); 156 | $this->assertEquals('前面 ; 後面', pangu('前面 ; 後面')); 157 | } 158 | 159 | 160 | public function testQuote() { 161 | $this->assertEquals('前面 " 後面', pangu('前面"後面')); 162 | $this->assertEquals('前面 "中文 123 漢字" 後面', pangu('前面 "中文123漢字" 後面')); 163 | $this->assertEquals('前面 "" 後面', pangu('前面""後面')); 164 | $this->assertEquals('前面 " " 後面', pangu('前面" "後面')); 165 | } 166 | 167 | 168 | public function testSingleQuote() { 169 | $this->assertEquals('前面 \' 後面', pangu('前面\'後面')); 170 | $this->assertEquals('前面 \'中文 123 漢字\' 後面', pangu('前面\'中文123漢字\'後面')); 171 | $this->assertEquals('前面 \'\' 後面', pangu('前面\'\'後面')); 172 | $this->assertEquals('前面 \' \' 後面', pangu('前面\' \'後面')); 173 | } 174 | 175 | 176 | public function testLessThan() { 177 | $this->assertEquals('前面 < 後面', pangu('前面<後面')); 178 | } 179 | 180 | 181 | public function testComma() { 182 | $this->assertEquals('前面, 後面', pangu('前面,後面')); 183 | $this->assertEquals('前面, 後面', pangu('前面, 後面')); 184 | $this->assertEquals('前面, 後面', pangu('前面, 後面')); 185 | } 186 | 187 | 188 | public function testGreaterThan() { 189 | $this->assertEquals('前面 > 後面', pangu('前面>後面')); 190 | } 191 | 192 | 193 | public function testPeriod() { 194 | $this->assertEquals('前面. 後面', pangu('前面.後面'));$this->assertEquals('前面. 後面', pangu('前面. 後面')); 195 | 196 | $this->assertEquals('前面. 後面', pangu('前面. 後面')); 197 | } 198 | 199 | 200 | public function testQuestionMark() { 201 | $this->assertEquals('前面? 後面', pangu('前面?後面')); 202 | $this->assertEquals('前面? 後面', pangu('前面? 後面'));$this->assertEquals('前面? 後面', pangu('前面? 後面')); 203 | } 204 | 205 | public function testSlash() { 206 | $this->assertEquals('前面 / 後面', pangu('前面/後面')); 207 | } 208 | } -------------------------------------------------------------------------------- /pangu.php: -------------------------------------------------------------------------------- 1 | array( 24 | '([' . $cjk . '])(["\'])', 25 | '$1 $2' 26 | ), 27 | 28 | 'quote_cjk' => array( 29 | '(["\'])([' . $cjk . '])', 30 | '$1 $2' 31 | ), 32 | 33 | 'fix_quote' => array( 34 | '(["\']+)(\s*)(.+?)(\s*)(["\']+)', 35 | '$1$3$5' 36 | ), 37 | 38 | 'cjk_hash' => array( 39 | '([' . $cjk . '])(#(\S+))', 40 | '$1 $2' 41 | ), 42 | 43 | 'hash_cjk' => array( 44 | '((\S+)#)([' . $cjk . '])', 45 | '$1 $3' 46 | ), 47 | 48 | 'cjk_operator_ans' => array( 49 | '([' . $cjk . '])([A-Za-zΑ-Ωα-ω0-9])([\+\-\*\/=&\\|<>])', 50 | '$1 $2 $3' 51 | ), 52 | 53 | 'ans_operator_cjk' => array( 54 | '([\+\-\*\/=&\\|<>])([A-Za-zΑ-Ωα-ω0-9])([' . $cjk . '])', 55 | '$1 $2 $3' 56 | ), 57 | 58 | 'bracket' => array( 59 | array( 60 | '([' . $cjk . '])([<\[\{\(]+(.*?)[>\]\}\)]+)([' . $cjk . '])', 61 | '$1 $2 $4' 62 | ), 63 | 64 | array( 65 | 'cjk_bracket' => array( 66 | '([' . $cjk . '])([<>\[\]\{\}\(\)])', 67 | '$1 $2' 68 | ), 69 | 70 | 'bracket_cjk' => array( 71 | '([<>\[\]\{\}\(\)])([' . $cjk . '])', 72 | '$1 $2' 73 | ) 74 | ) 75 | ), 76 | 77 | 'fix_bracket' => array( 78 | '([<\[\{\(]+)(\s*)(.+?)(\s*)([>\]\}\)]+)', 79 | '$1$3$5' 80 | ), 81 | 82 | 'cjk_ans' => array( 83 | '([' . $cjk . '])([A-Za-zΑ-Ωα-ω0-9`@&%\=\$\^\*\-\+\\/|\\\])', 84 | '$1 $2' 85 | ), 86 | 87 | 'ans_cjk' => array( 88 | '([A-Za-zΑ-Ωα-ω0-9`~!%&=;\|\,\.\:\?\$\^\*\-\+\/\\\])([' . $cjk . '])', 89 | '$1 $2' 90 | ) 91 | ); 92 | 93 | foreach ($patterns as $key => $value) { 94 | if ($key === 'bracket') { 95 | $old = $text; 96 | $new = preg_replace('/' . $value[0][0] . '/iu', $value[0][1], $text); 97 | $text = $new; 98 | 99 | if ($old === $new) { 100 | foreach ($value[1] as $val) { 101 | $text = preg_replace('/' . $val[0] . '/iu', $val[1], $text); 102 | } 103 | } 104 | 105 | continue; 106 | } 107 | 108 | $text = preg_replace('/' . $value[0] . '/iu', $value[1], $text); 109 | } 110 | 111 | return $text; 112 | } 113 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | pangu-test.php 15 | 16 | 17 | --------------------------------------------------------------------------------