├── src ├── .gitignore └── Dht.php ├── CHANGELOG.md ├── LICENSE.md ├── composer.json ├── CONTRIBUTING.md ├── SUPPORT_ALGOS.md ├── CODE_OF_CONDUCT.md └── README.md /src/.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /.vscode 3 | /Config 4 | /.vagrant 5 | .DS_Store 6 | */.DS_Store 7 | build 8 | composer.lock 9 | vendor 10 | phpcs.xml 11 | phpunit.xml 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `dht` will be documented in this file. 4 | 5 | Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) principles. 6 | 7 | ## NEXT - YYYY-MM-DD 8 | 9 | ### Added 10 | - Nothing 11 | 12 | ### Deprecated 13 | - Nothing 14 | 15 | ### Fixed 16 | - Nothing 17 | 18 | ### Removed 19 | - Nothing 20 | 21 | ### Security 22 | - Nothing 23 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2019 yiranzai 4 | 5 | > Permission is hereby granted, free of charge, to any person obtaining a copy 6 | > of this software and associated documentation files (the "Software"), to deal 7 | > in the Software without restriction, including without limitation the rights 8 | > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | > copies of the Software, and to permit persons to whom the Software is 10 | > furnished to do so, subject to the following conditions: 11 | > 12 | > The above copyright notice and this permission notice shall be included in 13 | > all 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, 17 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | > THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yiranzai/dht", 3 | "type": "library", 4 | "description": "PHP Distributed Hash Table", 5 | "keywords": [ 6 | "yiranzai", 7 | "dht" 8 | ], 9 | "homepage": "https://github.com/yiranzai/php-dht", 10 | "license": "MIT", 11 | "authors": [ 12 | { 13 | "name": "yiranzai", 14 | "email": "wuqingdzx@gmail.com", 15 | "homepage": "https://blog.yiranzai.cn", 16 | "role": "Developer" 17 | } 18 | ], 19 | "require": { 20 | "php" : "~7.1", 21 | "ext-json": "*" 22 | }, 23 | "require-dev": { 24 | "phpunit/phpunit" : ">=7.0", 25 | "squizlabs/php_codesniffer": "^3.0" 26 | }, 27 | "autoload": { 28 | "psr-4": { 29 | "Yiranzai\\Dht\\": "src" 30 | } 31 | }, 32 | "autoload-dev": { 33 | "psr-4": { 34 | "Yiranzai\\Dht\\": "tests" 35 | } 36 | }, 37 | "scripts": { 38 | "test": "phpunit", 39 | "check-style": "phpcs src tests", 40 | "fix-style": "phpcbf src tests" 41 | }, 42 | "extra": { 43 | "branch-alias": { 44 | "dev-master": "1.0-dev" 45 | } 46 | }, 47 | "config": { 48 | "sort-packages": true 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are **welcome** and will be fully **credited**. 4 | 5 | We accept contributions via Pull Requests on [Github](https://github.com/yiranzai/php-dht). 6 | 7 | 8 | ## Pull Requests 9 | 10 | - **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - Check the code style with ``$ composer check-style`` and fix it with ``$ composer fix-style``. 11 | 12 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests. 13 | 14 | - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. 15 | 16 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option. 17 | 18 | - **Create feature branches** - Don't ask us to pull from your master branch. 19 | 20 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. 21 | 22 | - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. 23 | 24 | 25 | ## Running Tests 26 | 27 | ``` bash 28 | $ composer test 29 | ``` 30 | 31 | 32 | **Happy coding**! 33 | -------------------------------------------------------------------------------- /SUPPORT_ALGOS.md: -------------------------------------------------------------------------------- 1 | # SUPPROT_ALGOS 2 | 3 | default algo is time33 4 | ```bash 5 | $ php -a 6 | php > print_r(hash_algos()); 7 | Array 8 | ( 9 | [0] => md2 10 | [1] => md4 11 | [2] => md5 12 | [3] => sha1 13 | [4] => sha224 14 | [5] => sha256 15 | [6] => sha384 16 | [7] => sha512/224 17 | [8] => sha512/256 18 | [9] => sha512 19 | [10] => sha3-224 20 | [11] => sha3-256 21 | [12] => sha3-384 22 | [13] => sha3-512 23 | [14] => ripemd128 24 | [15] => ripemd160 25 | [16] => ripemd256 26 | [17] => ripemd320 27 | [18] => whirlpool 28 | [19] => tiger128,3 29 | [20] => tiger160,3 30 | [21] => tiger192,3 31 | [22] => tiger128,4 32 | [23] => tiger160,4 33 | [24] => tiger192,4 34 | [25] => snefru 35 | [26] => snefru256 36 | [27] => gost 37 | [28] => gost-crypto 38 | [29] => adler32 39 | [30] => crc32 40 | [31] => crc32b 41 | [32] => fnv132 42 | [33] => fnv1a32 43 | [34] => fnv164 44 | [35] => fnv1a64 45 | [36] => joaat 46 | [37] => haval128,3 47 | [38] => haval160,3 48 | [39] => haval192,3 49 | [40] => haval224,3 50 | [41] => haval256,3 51 | [42] => haval128,4 52 | [43] => haval160,4 53 | [44] => haval192,4 54 | [45] => haval224,4 55 | [46] => haval256,4 56 | [47] => haval128,5 57 | [48] => haval160,5 58 | [49] => haval192,5 59 | [50] => haval224,5 60 | [51] => haval256,5 61 | ) 62 | ``` 63 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to make participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at `wuqingdzx@gmail.com`. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # php-dht 2 | 3 |

4 | 5 |

6 | 7 | [![Latest Version on Packagist][ico-version]][link-packagist] 8 | [![Software License][ico-license]](LICENSE.md) 9 | [![Build Status][ico-travis]][link-travis] 10 | [![Coverage Status][ico-scrutinizer]][link-scrutinizer] 11 | [![Quality Score][ico-code-quality]][link-code-quality] 12 | [![Total Downloads][ico-downloads]][link-downloads] 13 | 14 | PHP Distributed Hash Table, Suitable for assisting in finding distributed nodes corresponding to key. 15 | 16 | You have to cache it and pass it in the next time you use it. 17 | 18 | ## Install 19 | 20 | Via Composer 21 | 22 | ``` bash 23 | $ composer require yiranzai/dht 24 | ``` 25 | 26 | ## Usage 27 | 28 | easy 29 | 30 | ### init 31 | 32 | ``` php 33 | $hash = new Yiranzai\Dht\Dht(); 34 | $hash->addEntityNode('db_server_one')->addEntityNode('db_server_two'); 35 | $dbServer = $hash->getLocation('key_one'); 36 | 37 | //or 38 | 39 | $dhtOne = new Yiranzai\Dht\Dht([ 40 | 'virtualNodeNum' => 3, 41 | 'algo' => 'sha256', 42 | ]); 43 | $dhtOne->addEntityNode('db_server_one'); 44 | ``` 45 | 46 | ### Reuse it 47 | 48 | You have to cache it and pass it in the next time you use it. 49 | 50 | ```php 51 | $hash = new Yiranzai\Dht\Dht(); 52 | $hash->addEntityNode('db_server_one')->addEntityNode('db_server_two'); 53 | $dbServer = $hash->getLocation('key_one'); 54 | $cache = $hash->toArray(); 55 | // please cache this data 56 | $hash = new Yiranzai\Dht\Dht($cache); 57 | $dbServer = $hash->getLocation('key_one'); 58 | ``` 59 | 60 | ### Delete Entity Node 61 | 62 | Delete entity node 63 | 64 | ```php 65 | $hash = new Yiranzai\Dht\Dht(); 66 | $hash->deleteEntityNode('db_server_one'); 67 | ``` 68 | 69 | ### Change algo 70 | 71 | default algo is time33, [See more support](SUPPORT_ALGOS.md) 72 | 73 | ```php 74 | $hash = new Yiranzai\Dht\Dht(); 75 | $hash->algo('sha256'); 76 | 77 | //or 78 | 79 | $hash = new Yiranzai\Dht\Dht(['algo' => YOUR_ALGO]); 80 | ``` 81 | 82 | ## Change log 83 | 84 | Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. 85 | 86 | ## Testing 87 | 88 | ``` bash 89 | $ composer test 90 | ``` 91 | 92 | ## Contributing 93 | 94 | Please see [CONTRIBUTING](CONTRIBUTING.md) and [CODE_OF_CONDUCT](CODE_OF_CONDUCT.md) for details. 95 | 96 | ## Security 97 | 98 | If you discover any security related issues, please email wuqingdzx@gmail.com instead of using the issue tracker. 99 | 100 | ## Credits 101 | 102 | - [yiranzai][link-author] 103 | - [All Contributors][link-contributors] 104 | 105 | ## License 106 | 107 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 108 | 109 | [ico-version]: https://img.shields.io/packagist/v/yiranzai/dht.svg?style=flat-square 110 | [ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square 111 | [ico-travis]: https://img.shields.io/travis/yiranzai/php-dht/master.svg?style=flat-square 112 | [ico-scrutinizer]: https://img.shields.io/scrutinizer/coverage/g/yiranzai/php-dht.svg?style=flat-square 113 | [ico-code-quality]: https://img.shields.io/scrutinizer/g/yiranzai/php-dht.svg?style=flat-square 114 | [ico-downloads]: https://img.shields.io/packagist/dt/yiranzai/dht.svg?style=flat-square 115 | 116 | [link-packagist]: https://packagist.org/packages/yiranzai/dht 117 | [link-travis]: https://travis-ci.org/yiranzai/php-dht 118 | [link-scrutinizer]: https://scrutinizer-ci.com/g/yiranzai/php-dht/code-structure 119 | [link-code-quality]: https://scrutinizer-ci.com/g/yiranzai/php-dht 120 | [link-downloads]: https://packagist.org/packages/yiranzai/dht 121 | [link-author]: https://github.com/yiranzai 122 | [link-contributors]: ../../contributors 123 | -------------------------------------------------------------------------------- /src/Dht.php: -------------------------------------------------------------------------------- 1 | $item) { 44 | if ($key === 'algo') { 45 | $this->algo($item); 46 | continue; 47 | } 48 | $this->$key = $item; 49 | } 50 | } 51 | } 52 | 53 | /** 54 | * @param string $str 55 | * @return $this 56 | */ 57 | public function algo(string $str): self 58 | { 59 | if ($this->isSupportHashAlgos($str)) { 60 | $this->algo = $str; 61 | } 62 | return $this; 63 | } 64 | 65 | /** 66 | * @param string $str 67 | * @return bool 68 | */ 69 | public function isSupportHashAlgos(string $str): bool 70 | { 71 | return $str === self::DEFAULT_ALGO || in_array($str, $this->supportHashAlgos(), true); 72 | } 73 | 74 | /** 75 | * @return array 76 | */ 77 | public function supportHashAlgos(): array 78 | { 79 | return hash_algos(); 80 | } 81 | 82 | /** 83 | * @return string 84 | */ 85 | public function toJson(): string 86 | { 87 | $json = json_encode($this->jsonSerialize()); 88 | if (json_last_error() !== JSON_ERROR_NONE) { 89 | throw new \RuntimeException(json_last_error_msg()); 90 | } 91 | 92 | return $json; 93 | } 94 | 95 | /** 96 | * Convert the object into something JSON serializable. 97 | * 98 | * @return array 99 | */ 100 | public function jsonSerialize(): array 101 | { 102 | return $this->toArray(); 103 | } 104 | 105 | /** 106 | * @return array 107 | */ 108 | public function toArray(): array 109 | { 110 | $array = array(); 111 | foreach ($this as $key => $value) { 112 | $array[$key] = $value; 113 | } 114 | return $array; 115 | } 116 | 117 | /** 118 | * @param string $str 119 | * @return int 120 | */ 121 | public function hashGenerate(string $str): int 122 | { 123 | if ($this->algo === self::DEFAULT_ALGO) { 124 | return $this->time33($str); 125 | } 126 | return (int)sprintf('%u', hash($this->algo, $str)); 127 | } 128 | 129 | /** 130 | * @param string $str 131 | * @return int 132 | */ 133 | protected function time33(string $str): int 134 | { 135 | $hash = 0; 136 | $str = md5($str); 137 | $len = 32; 138 | for ($i = 0; $i < $len; $i++) { 139 | $hash = ($hash << 5) + $hash + ord($str{$i}); 140 | } 141 | return $hash & 0x7FFFFFFF; 142 | } 143 | 144 | /** 145 | * 寻找字符串所在的机器位置 146 | * @param string $key 147 | * @return string 148 | */ 149 | public function getLocation(string $key): string 150 | { 151 | if (empty($this->locations)) { 152 | throw new \RuntimeException('This nodes is empty, please add a node'); 153 | } 154 | 155 | $position = $this->hashGenerate($key); 156 | //默认取第一个节点 157 | $node = current($this->locations); 158 | foreach ($this->locations as $k => $v) { 159 | //如果当前的位置,小于或等于节点组中的一个节点,那么当前位置对应该节点 160 | if ($position <= $k) { 161 | $node = $v; 162 | break; 163 | } 164 | } 165 | return $node; 166 | } 167 | 168 | /** 169 | * 添加一个节点 170 | * @param string $node 171 | * @return Dht 172 | */ 173 | public function addEntityNode(string $node): self 174 | { 175 | if ($this->existsNode($node)) { 176 | throw new \RuntimeException('This node already exists'); 177 | } 178 | $this->nodes[$node] = []; 179 | //生成虚拟节点 180 | for ($i = 0; $i < $this->virtualNodeNum; $i++) { 181 | $tmp = $this->hashGenerate($node . $i); 182 | $this->locations[$tmp] = $node; 183 | $this->nodes[$node][] = $tmp; 184 | } 185 | //对节点排序 186 | ksort($this->locations, SORT_NUMERIC); 187 | return $this; 188 | } 189 | 190 | /** 191 | * @param string $node 192 | * @return bool 193 | */ 194 | public function existsNode(string $node): bool 195 | { 196 | return array_key_exists($node, $this->nodes); 197 | } 198 | 199 | /** 200 | * delete a entity node 201 | * 202 | * @param string $node 203 | * @return Dht 204 | */ 205 | public function deleteEntityNode(string $node): self 206 | { 207 | foreach ($this->nodes[$node] as $v) { 208 | unset($this->locations[$v]); 209 | } 210 | unset($this->nodes[$node]); 211 | return $this; 212 | } 213 | 214 | /** 215 | * @param int $num 216 | * @return $this 217 | */ 218 | public function virtualNodeNum(int $num): self 219 | { 220 | $this->virtualNodeNum = $num; 221 | return $this; 222 | } 223 | } 224 | --------------------------------------------------------------------------------