├── .env_example ├── .gitignore ├── .htaccess ├── LICENSE ├── README.md ├── composer.json ├── composer.lock ├── doc ├── 1.x_.env ├── 1.x_README.md ├── 1.x_esu.sql ├── 1.x_upgrade_2.x.md └── 2.x_esu.sql ├── index.php ├── src ├── Cache.php ├── DB.php ├── EasyShortUrl.php ├── Exception │ └── EasyShortUrlException.php ├── Helpers │ ├── Constant.php │ └── Functions.php ├── Router.php └── WebAdmin.php └── static └── img.png /.env_example: -------------------------------------------------------------------------------- 1 | # 短网址服务域名 2 | ESU_DOMAIN=http://s.lukachen.com 3 | 4 | # 数据库配置 5 | ESU_DB_HOST=127.0.0.1 6 | ESU_DB_DBNAME=esu 7 | ESU_DB_USERNAME=root 8 | ESU_DB_PASSWORD=root 9 | ESU_DB_PORT=3306 10 | ESU_DB_CHARSET=utf8 11 | 12 | # Redis 配置 13 | ESU_REDIS_DSN=tcp://127.0.0.1:6379 14 | 15 | # 是否开启缓存,可选项 0: 不开启, 1: 开启 (开启缓存,数据表跳转统计将失效) 16 | ESU_CACHE_OPEN=0 17 | 18 | # 缓存方式,可选项 Filesystem: 本地文件缓存, Redis: 缓存 (Redis 缓存,依赖 ESU_REDIS_DSN 配置) 19 | ESU_CACHE_CLIENT=Filesystem 20 | 21 | # 默认缓存时间 604800 秒 (1星期) 22 | ESU_CACHE_LIFETIME=604800 23 | 24 | # web_admin 页 access_key 25 | ESU_WEB_ADMIN_ACCESS_KEY=esu -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | /ErrorFiles/ 3 | .idea/ 4 | .env 5 | u.php -------------------------------------------------------------------------------- /.htaccess: -------------------------------------------------------------------------------- 1 | 2 | RewriteEngine On 3 | 4 | RewriteBase / 5 | 6 | RewriteCond %{REQUEST_FILENAME} !-f 7 | 8 | RewriteCond %{REQUEST_FILENAME} !-d 9 | 10 | RewriteRule ^(.*)$ index.php [L,E=PATH_INFO:$1] 11 | 12 | 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 LukaChen 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 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, 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # easy-short-url 短网址 2.x 2 | - 使用方式: 可在 Laravel、Yii、ThinkPHP 等框架 Composer 包引入,也可以独立搭建短网址站点 3 | - 实现原理: id 自增(转自定义62进制) 4 | - 存储: MySQL 5 | - 缓存: 可在配置项 ESU_CACHE_OPEN、ESU_CACHE_CLIENT、ESU_CACHE_LIFETIME 定制 6 | - 安全: 转短网址、跳转长网址授权 7 | 8 | ## 导航 9 | - [1.x 版本](doc/1.x_README.md) 10 | - [1.x 升级 2.x 指南](doc/1.x_upgrade_2.x.md) 11 | 12 | ## 2.x 相比 1.x 新特性 13 | - 安全跳转,授权请求密钥、跳转域。基于安全考虑,跳转长网址域名,必须授权才可跳转 14 | - 缓存策略,可配置。可在配置项 ESU_CACHE_OPEN、ESU_CACHE_CLIENT、ESU_CACHE_LIFETIME 定制 15 | 16 | ## 使用步骤 17 | 1.获取包 18 | ``` 19 | composer require chenlongqiang/easy-short-url "^2" 20 | ``` 21 | 22 | 2.创建数据库 23 | ``` 24 | mysql -u username -ppassword 25 | create database esu character set utf8 collate utf8_general_ci; 26 | ``` 27 | 28 | 3.创建数据表 29 | ``` 30 | mysql -u username -ppassword esu < doc/2.x_esu.sql 31 | ``` 32 | 33 | 4.在项目根目录下,创建配置文件 .env 34 | ``` 35 | cd 你的项目根目录 36 | cp ./vendor/chenlongqiang/easy-short-url/.env_example ./.env 37 | ``` 38 | 39 | 5.vi .env 修改配置项 40 | ``` 41 | # 短网址服务域名 42 | ESU_DOMAIN=http://s.lukachen.com 43 | 44 | # 数据库配置 45 | ESU_DB_HOST=127.0.0.1 46 | ESU_DB_DBNAME=esu 47 | ESU_DB_USERNAME=root 48 | ESU_DB_PASSWORD=root 49 | ESU_DB_PORT=3306 50 | ESU_DB_CHARSET=utf8 51 | 52 | # Redis 配置 53 | ESU_REDIS_DSN=tcp://127.0.0.1:6379 54 | 55 | # 是否开启缓存,可选项 0: 不开启, 1: 开启 (开启缓存,数据表跳转统计将失效) 56 | ESU_CACHE_OPEN=0 57 | 58 | # 缓存方式,可选项 Filesystem: 本地文件缓存, Redis: 缓存 (Redis 缓存,依赖 ESU_REDIS_DSN 配置) 59 | ESU_CACHE_CLIENT=Filesystem 60 | 61 | # 默认缓存时间 604800 秒 (1星期) 62 | ESU_CACHE_LIFETIME=604800 63 | 64 | # web_admin 页 access_key 65 | ESU_WEB_ADMIN_ACCESS_KEY=esu 66 | ``` 67 | 68 | 6.授权请求密钥、跳转域名 69 | ``` 70 | 在数据表 esu_access 添加数据即可 71 | ``` 72 | 73 | ## 方法列表 74 | 1.生成短网址 toShort 75 | ``` 76 | $shortUrl = \EasyShortUrl\EasyShortUrl::getInstance()->toShort('http://lukachen.com/archives/328/'); 77 | ``` 78 | 79 | 2.获取原网址 toLong 80 | ``` 81 | $longUrl = \EasyShortUrl\EasyShortUrl::getInstance()->toLong($code); 82 | ``` 83 | 84 | 完成以上步骤,即可在项目中引入本包,toShort、toLong 完成长短链接转化 85 | 如果不需要配置独立的转链网站,后面就不用看了 :) 86 | 87 | ## 需要搭建转链网站 88 | 需搭建类似 http://s.lukachen.com/web_admin 这样的网站,继续以下步骤(本项目已经提供前端页面,做好域名和服务器配置即可) 89 | 90 | 1.服务器配置 91 | ``` 92 | 1) apache or nginx 配置 root 目录至 vendor/chenlongqiang/easy-short-url/ 93 | 2) 配置 rewrite 重写至 index.php,不清楚的自行 baidu、google 或联系我 94 | ``` 95 | 96 | 2.web页 97 | ``` 98 | 地址: http://(你的短网址域名 ESU_DOMAIN 配置项)/web_admin 99 | 授权: web_admin 页,使用 ESU_WEB_ADMIN_ACCESS_KEY 配置项作为 access_key 100 | ``` 101 | 102 | 3.api 103 | ``` 104 | 地址: http://(你的短网址域名 ESU_DOMAIN 配置项)/api_gen 105 | 方法: POST 106 | 参数: 107 | type: to_short 或 to_long 108 | content: url 109 | access_key: api 授权密钥,可在 esu_access 新增 110 | ``` 111 | 112 | 转链网站搭建完成 :) 113 | 114 | ## 我的短网址服务,体验地址 115 | http://s.lukachen.com/web_admin 116 | 117 | - 默认授权码 esu,已添加 lukachen.com 域名为合法跳转域,可用该跳转域名体验 118 | - 如长网址为:http://lukachen.com/friends 可缩短网址为 http://s.lukachen.com/LS 119 | 120 | ### 我的短网址,提供授权使用 121 | 需使用我搭建的短网址服务,请发邮件 365499684@qq.com 申请。跳转域名合理,我将会邮件回复授权码,并添加合法跳转域名 122 | 特别说明: 123 | - `我的短网址服务 s.lukachen.com` 不对微信业务开放,因为微信分享非常容易封禁域名,影响到大家使用 124 | - 使用本库自建服务,`自己决定跳转域名范围和使用场景`,键盘侠请自己睁大眼睛看清楚,懒得争论 125 | 126 | ### 我的短网址,申请模版 127 | ``` 128 | 因 xxx 业务需要,申请短网址服务,跳转目标域名为 129 | lukachen.com 130 | google.com 131 | baidu.com 132 | ``` 133 | 134 | ## 联系我 135 | - QQ: 365499684 (添加时请备注【短网址】) 136 | - Blog: http://lukachen.com/projects 137 | - 短网址 Demo 站点: http://s.lukachen.com/web_admin 138 | - 有疑问,欢迎 Issues 139 | - 有更棒的 Code 建议,欢迎 Pull Requests 140 | - 对你有帮助,请动动小手 Star Thank You :) 141 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chenlongqiang/easy-short-url", 3 | "description": "generate short url, use mysql.", 4 | "homepage": "https://github.com/chenlongqiang/easy-short-url", 5 | "type": "library", 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "陈龙强", 10 | "email": "365499684@qq.com", 11 | "homepage": "http://lukachen.com" 12 | } 13 | ], 14 | "require": { 15 | "php": ">=5.6.0", 16 | "paragonie/easydb": "^1", 17 | "vlucas/phpdotenv": "^2.4", 18 | "symfony/cache": "3.4.47", 19 | "predis/predis": "^1.1" 20 | }, 21 | "autoload": { 22 | "psr-4": { 23 | "EasyShortUrl\\": "src/" 24 | }, 25 | "files": [ 26 | "src/Helpers/Functions.php" 27 | ] 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "9baa3a0dab1ca7fb0f95618b1927a3e5", 8 | "packages": [ 9 | { 10 | "name": "paragonie/easydb", 11 | "version": "v1.6.1", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/paragonie/easydb.git", 15 | "reference": "5372673ba5980dc94cc33e9d7d23e7df055a8632" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/paragonie/easydb/zipball/5372673ba5980dc94cc33e9d7d23e7df055a8632", 20 | "reference": "5372673ba5980dc94cc33e9d7d23e7df055a8632", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "ext-pdo": "*", 25 | "php": "^5.5|^7" 26 | }, 27 | "require-dev": { 28 | "phpunit/phpunit": "^4|^5", 29 | "squizlabs/php_codesniffer": "^2.7" 30 | }, 31 | "type": "library", 32 | "autoload": { 33 | "psr-4": { 34 | "ParagonIE\\EasyDB\\": "src" 35 | } 36 | }, 37 | "notification-url": "https://packagist.org/downloads/", 38 | "license": [ 39 | "MIT" 40 | ], 41 | "authors": [ 42 | { 43 | "name": "Scott Arciszewski", 44 | "email": "scott@paragonie.com", 45 | "homepage": "https://paragonie.com", 46 | "role": "Developer" 47 | }, 48 | { 49 | "name": "Woody Gilk", 50 | "homepage": "https://github.com/shadowhand", 51 | "role": "Contributor" 52 | }, 53 | { 54 | "name": "SignpostMarv", 55 | "homepage": "https://github.com/SignpostMarv", 56 | "role": "Contributor" 57 | } 58 | ], 59 | "description": "Easy-to-use database abstraction", 60 | "keywords": [ 61 | "database", 62 | "pdo", 63 | "security", 64 | "sql" 65 | ], 66 | "time": "2018-05-01T06:44:32+00:00" 67 | }, 68 | { 69 | "name": "predis/predis", 70 | "version": "v1.1.6", 71 | "source": { 72 | "type": "git", 73 | "url": "https://github.com/predis/predis.git", 74 | "reference": "9930e933c67446962997b05201c69c2319bf26de" 75 | }, 76 | "dist": { 77 | "type": "zip", 78 | "url": "https://api.github.com/repos/predis/predis/zipball/9930e933c67446962997b05201c69c2319bf26de", 79 | "reference": "9930e933c67446962997b05201c69c2319bf26de", 80 | "shasum": "" 81 | }, 82 | "require": { 83 | "php": ">=5.3.9" 84 | }, 85 | "require-dev": { 86 | "cweagans/composer-patches": "^1.6", 87 | "phpunit/phpunit": "~4.8" 88 | }, 89 | "suggest": { 90 | "ext-curl": "Allows access to Webdis when paired with phpiredis", 91 | "ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol" 92 | }, 93 | "type": "library", 94 | "extra": { 95 | "composer-exit-on-patch-failure": true, 96 | "patches": { 97 | "phpunit/phpunit-mock-objects": { 98 | "Fix PHP 7 and 8 compatibility": "./tests/phpunit_mock_objects.patch" 99 | }, 100 | "phpunit/phpunit": { 101 | "Fix PHP 7 compatibility": "./tests/phpunit_php7.patch", 102 | "Fix PHP 8 compatibility": "./tests/phpunit_php8.patch" 103 | } 104 | } 105 | }, 106 | "autoload": { 107 | "psr-4": { 108 | "Predis\\": "src/" 109 | } 110 | }, 111 | "notification-url": "https://packagist.org/downloads/", 112 | "license": [ 113 | "MIT" 114 | ], 115 | "authors": [ 116 | { 117 | "name": "Daniele Alessandri", 118 | "email": "suppakilla@gmail.com", 119 | "homepage": "http://clorophilla.net", 120 | "role": "Creator & Maintainer" 121 | }, 122 | { 123 | "name": "Till Krüss", 124 | "homepage": "https://till.im", 125 | "role": "Maintainer" 126 | } 127 | ], 128 | "description": "Flexible and feature-complete Redis client for PHP and HHVM", 129 | "homepage": "http://github.com/predis/predis", 130 | "keywords": [ 131 | "nosql", 132 | "predis", 133 | "redis" 134 | ], 135 | "funding": [ 136 | { 137 | "url": "https://github.com/sponsors/tillkruss", 138 | "type": "github" 139 | } 140 | ], 141 | "time": "2020-09-11T19:18:05+00:00" 142 | }, 143 | { 144 | "name": "psr/cache", 145 | "version": "1.0.1", 146 | "source": { 147 | "type": "git", 148 | "url": "https://github.com/php-fig/cache.git", 149 | "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" 150 | }, 151 | "dist": { 152 | "type": "zip", 153 | "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8", 154 | "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", 155 | "shasum": "" 156 | }, 157 | "require": { 158 | "php": ">=5.3.0" 159 | }, 160 | "type": "library", 161 | "extra": { 162 | "branch-alias": { 163 | "dev-master": "1.0.x-dev" 164 | } 165 | }, 166 | "autoload": { 167 | "psr-4": { 168 | "Psr\\Cache\\": "src/" 169 | } 170 | }, 171 | "notification-url": "https://packagist.org/downloads/", 172 | "license": [ 173 | "MIT" 174 | ], 175 | "authors": [ 176 | { 177 | "name": "PHP-FIG", 178 | "homepage": "http://www.php-fig.org/" 179 | } 180 | ], 181 | "description": "Common interface for caching libraries", 182 | "keywords": [ 183 | "cache", 184 | "psr", 185 | "psr-6" 186 | ], 187 | "time": "2016-08-06T20:24:11+00:00" 188 | }, 189 | { 190 | "name": "psr/log", 191 | "version": "1.1.3", 192 | "source": { 193 | "type": "git", 194 | "url": "https://github.com/php-fig/log.git", 195 | "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc" 196 | }, 197 | "dist": { 198 | "type": "zip", 199 | "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc", 200 | "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc", 201 | "shasum": "" 202 | }, 203 | "require": { 204 | "php": ">=5.3.0" 205 | }, 206 | "type": "library", 207 | "extra": { 208 | "branch-alias": { 209 | "dev-master": "1.1.x-dev" 210 | } 211 | }, 212 | "autoload": { 213 | "psr-4": { 214 | "Psr\\Log\\": "Psr/Log/" 215 | } 216 | }, 217 | "notification-url": "https://packagist.org/downloads/", 218 | "license": [ 219 | "MIT" 220 | ], 221 | "authors": [ 222 | { 223 | "name": "PHP-FIG", 224 | "homepage": "http://www.php-fig.org/" 225 | } 226 | ], 227 | "description": "Common interface for logging libraries", 228 | "homepage": "https://github.com/php-fig/log", 229 | "keywords": [ 230 | "log", 231 | "psr", 232 | "psr-3" 233 | ], 234 | "time": "2020-03-23T09:12:05+00:00" 235 | }, 236 | { 237 | "name": "psr/simple-cache", 238 | "version": "1.0.1", 239 | "source": { 240 | "type": "git", 241 | "url": "https://github.com/php-fig/simple-cache.git", 242 | "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" 243 | }, 244 | "dist": { 245 | "type": "zip", 246 | "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", 247 | "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", 248 | "shasum": "" 249 | }, 250 | "require": { 251 | "php": ">=5.3.0" 252 | }, 253 | "type": "library", 254 | "extra": { 255 | "branch-alias": { 256 | "dev-master": "1.0.x-dev" 257 | } 258 | }, 259 | "autoload": { 260 | "psr-4": { 261 | "Psr\\SimpleCache\\": "src/" 262 | } 263 | }, 264 | "notification-url": "https://packagist.org/downloads/", 265 | "license": [ 266 | "MIT" 267 | ], 268 | "authors": [ 269 | { 270 | "name": "PHP-FIG", 271 | "homepage": "http://www.php-fig.org/" 272 | } 273 | ], 274 | "description": "Common interfaces for simple caching", 275 | "keywords": [ 276 | "cache", 277 | "caching", 278 | "psr", 279 | "psr-16", 280 | "simple-cache" 281 | ], 282 | "time": "2017-10-23T01:57:42+00:00" 283 | }, 284 | { 285 | "name": "symfony/cache", 286 | "version": "v3.4.47", 287 | "source": { 288 | "type": "git", 289 | "url": "https://github.com/symfony/cache.git", 290 | "reference": "a7a14c4832760bd1fbd31be2859ffedc9b6ff813" 291 | }, 292 | "dist": { 293 | "type": "zip", 294 | "url": "https://api.github.com/repos/symfony/cache/zipball/a7a14c4832760bd1fbd31be2859ffedc9b6ff813", 295 | "reference": "a7a14c4832760bd1fbd31be2859ffedc9b6ff813", 296 | "shasum": "" 297 | }, 298 | "require": { 299 | "php": "^5.5.9|>=7.0.8", 300 | "psr/cache": "~1.0", 301 | "psr/log": "~1.0", 302 | "psr/simple-cache": "^1.0", 303 | "symfony/polyfill-apcu": "~1.1" 304 | }, 305 | "conflict": { 306 | "symfony/var-dumper": "<3.3" 307 | }, 308 | "provide": { 309 | "psr/cache-implementation": "1.0", 310 | "psr/simple-cache-implementation": "1.0" 311 | }, 312 | "require-dev": { 313 | "cache/integration-tests": "dev-master", 314 | "doctrine/cache": "^1.6", 315 | "doctrine/dbal": "^2.4|^3.0", 316 | "predis/predis": "^1.0" 317 | }, 318 | "type": "library", 319 | "autoload": { 320 | "psr-4": { 321 | "Symfony\\Component\\Cache\\": "" 322 | }, 323 | "exclude-from-classmap": [ 324 | "/Tests/" 325 | ] 326 | }, 327 | "notification-url": "https://packagist.org/downloads/", 328 | "license": [ 329 | "MIT" 330 | ], 331 | "authors": [ 332 | { 333 | "name": "Nicolas Grekas", 334 | "email": "p@tchwork.com" 335 | }, 336 | { 337 | "name": "Symfony Community", 338 | "homepage": "https://symfony.com/contributors" 339 | } 340 | ], 341 | "description": "Symfony Cache component with PSR-6, PSR-16, and tags", 342 | "homepage": "https://symfony.com", 343 | "keywords": [ 344 | "caching", 345 | "psr6" 346 | ], 347 | "funding": [ 348 | { 349 | "url": "https://symfony.com/sponsor", 350 | "type": "custom" 351 | }, 352 | { 353 | "url": "https://github.com/fabpot", 354 | "type": "github" 355 | }, 356 | { 357 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 358 | "type": "tidelift" 359 | } 360 | ], 361 | "time": "2020-10-24T10:57:07+00:00" 362 | }, 363 | { 364 | "name": "symfony/polyfill-apcu", 365 | "version": "v1.20.0", 366 | "source": { 367 | "type": "git", 368 | "url": "https://github.com/symfony/polyfill-apcu.git", 369 | "reference": "f5191eb0e98e08d12eb49fc0ed0820e37de89fdf" 370 | }, 371 | "dist": { 372 | "type": "zip", 373 | "url": "https://api.github.com/repos/symfony/polyfill-apcu/zipball/f5191eb0e98e08d12eb49fc0ed0820e37de89fdf", 374 | "reference": "f5191eb0e98e08d12eb49fc0ed0820e37de89fdf", 375 | "shasum": "" 376 | }, 377 | "require": { 378 | "php": ">=7.1" 379 | }, 380 | "type": "library", 381 | "extra": { 382 | "branch-alias": { 383 | "dev-main": "1.20-dev" 384 | }, 385 | "thanks": { 386 | "name": "symfony/polyfill", 387 | "url": "https://github.com/symfony/polyfill" 388 | } 389 | }, 390 | "autoload": { 391 | "psr-4": { 392 | "Symfony\\Polyfill\\Apcu\\": "" 393 | }, 394 | "files": [ 395 | "bootstrap.php" 396 | ] 397 | }, 398 | "notification-url": "https://packagist.org/downloads/", 399 | "license": [ 400 | "MIT" 401 | ], 402 | "authors": [ 403 | { 404 | "name": "Nicolas Grekas", 405 | "email": "p@tchwork.com" 406 | }, 407 | { 408 | "name": "Symfony Community", 409 | "homepage": "https://symfony.com/contributors" 410 | } 411 | ], 412 | "description": "Symfony polyfill backporting apcu_* functions to lower PHP versions", 413 | "homepage": "https://symfony.com", 414 | "keywords": [ 415 | "apcu", 416 | "compatibility", 417 | "polyfill", 418 | "portable", 419 | "shim" 420 | ], 421 | "funding": [ 422 | { 423 | "url": "https://symfony.com/sponsor", 424 | "type": "custom" 425 | }, 426 | { 427 | "url": "https://github.com/fabpot", 428 | "type": "github" 429 | }, 430 | { 431 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 432 | "type": "tidelift" 433 | } 434 | ], 435 | "time": "2020-10-23T14:02:19+00:00" 436 | }, 437 | { 438 | "name": "vlucas/phpdotenv", 439 | "version": "v2.4.0", 440 | "source": { 441 | "type": "git", 442 | "url": "https://github.com/vlucas/phpdotenv.git", 443 | "reference": "3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c" 444 | }, 445 | "dist": { 446 | "type": "zip", 447 | "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c", 448 | "reference": "3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c", 449 | "shasum": "" 450 | }, 451 | "require": { 452 | "php": ">=5.3.9" 453 | }, 454 | "require-dev": { 455 | "phpunit/phpunit": "^4.8 || ^5.0" 456 | }, 457 | "type": "library", 458 | "extra": { 459 | "branch-alias": { 460 | "dev-master": "2.4-dev" 461 | } 462 | }, 463 | "autoload": { 464 | "psr-4": { 465 | "Dotenv\\": "src/" 466 | } 467 | }, 468 | "notification-url": "https://packagist.org/downloads/", 469 | "license": [ 470 | "BSD-3-Clause-Attribution" 471 | ], 472 | "authors": [ 473 | { 474 | "name": "Vance Lucas", 475 | "email": "vance@vancelucas.com", 476 | "homepage": "http://www.vancelucas.com" 477 | } 478 | ], 479 | "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", 480 | "keywords": [ 481 | "dotenv", 482 | "env", 483 | "environment" 484 | ], 485 | "time": "2016-09-01T10:05:43+00:00" 486 | } 487 | ], 488 | "packages-dev": [], 489 | "aliases": [], 490 | "minimum-stability": "stable", 491 | "stability-flags": [], 492 | "prefer-stable": false, 493 | "prefer-lowest": false, 494 | "platform": { 495 | "php": ">=5.6.0" 496 | }, 497 | "platform-dev": [], 498 | "plugin-api-version": "1.1.0" 499 | } 500 | -------------------------------------------------------------------------------- /doc/1.x_.env: -------------------------------------------------------------------------------- 1 | # 生成的短网址域名 2 | DOMAIN=http://s.lukachen.com 3 | # web 页 session 有效时间 4 | WEB_SESSION_LIFE=600 5 | # api ACCESS_KEY 6 | ACCESS_KEY=easy123456|short099876|url123567 7 | 8 | DB_HOST=127.0.0.1 9 | DB_DBNAME=esu 10 | DB_USERNAME=root 11 | DB_PASSWORD=root 12 | 13 | TABLE_URL=esu_url -------------------------------------------------------------------------------- /doc/1.x_README.md: -------------------------------------------------------------------------------- 1 | # easy-short-url 短网址 1.x 2 | - 使用方式: 可在 laravel、yii、thinkphp 等框架 composer 包引入,也可以独立搭建短网址网站 3 | - 实现原理: id 自增(转自定义62进制) 4 | - 存储: mysql 5 | - 统计: 302 重定向, 数据库 request_num 字段统计(如考虑高并发的查询性能瓶颈,可自己在调用层做 redis or memcache 缓存,统计将失效) 6 | 7 | ## 使用步骤 8 | 9 | 1.获取包 10 | ``` 11 | composer require chenlongqiang/easy-short-url ^1 12 | ``` 13 | 14 | 2.创建数据库 15 | ``` 16 | mysql -u username -ppassword 17 | create database esu character set utf8 collate utf8_general_ci; 18 | ``` 19 | 20 | 3.创建数据表 21 | ``` 22 | mysql -u username -ppassword esu < doc/1.x_esu.sql 23 | ``` 24 | 25 | 4.在项目根目录下,创建配置文件 .env 26 | ``` 27 | cd 你的项目根目录 28 | cp ./vendor/chenlongqiang/easy-short-url/.env_example ./.env 29 | ``` 30 | 31 | 5.vi .env 修改配置项 32 | ``` 33 | # 生成的短网址域名 34 | DOMAIN=http://s.lukachen.com 35 | # web 页 session 有效时间 36 | WEB_SESSION_LIFE=600 37 | # api ACCESS_KEY 38 | ACCESS_KEY=easy123456|short099876|url123567 39 | 40 | DB_HOST=127.0.0.1 41 | DB_DBNAME=esu 42 | DB_USERNAME=root 43 | DB_PASSWORD=root 44 | 45 | TABLE_URL=esu_url 46 | ``` 47 | 48 | ## 方法列表 49 | 50 | 0.config 51 | ``` 52 | $dbConfig = [ 53 | 'host' => env('DB_HOST'), 54 | 'dbname' => env('DB_DBNAME'), 55 | 'username' => env('DB_USERNAME'), 56 | 'password' => env('DB_PASSWORD'), 57 | ]; 58 | $options = [ 59 | 'domain' => env('DOMAIN'), 60 | 'tableUrl' => env('TABLE_URL'), 61 | ]; 62 | ``` 63 | 64 | 1.生成短网址 toShort 65 | ``` 66 | $shortUrl = \EasyShortUrl\EasyShortUrl::getInstance($dbConfig, $options)->toShort('http://lukachen.com/archives/328/'); 67 | ``` 68 | 69 | 2.获取原网址 toLong 70 | ``` 71 | $longUrl = \EasyShortUrl\EasyShortUrl::getInstance($dbConfig, $options)->toLong($code); 72 | ``` 73 | 74 | 完成以上步骤,即可在项目中引入本包,toShort、toLong 完成长短链接转化。 75 | 如果不需要配置独立的转链网站,后面就不用看了 :) 76 | 77 | ## 需要搭建转链网站 78 | 79 | 需搭建类似 http://s.lukachen.com/web_admin 这样的网站,继续以下步骤(本项目已经提供前端页面,做好域名和服务器配置即可) 80 | 81 | 1.服务器配置 82 | ``` 83 | 1) apache or nginx 配置 root 目录至 vendor/chenlongqiang/easy-short-url/ 84 | 2) 配置 rewrite 重写至 index.php,不清楚的自行 baidu、google 或联系我 85 | ``` 86 | 87 | 2.web页(.env DOMAIN 改成自己的域名) 88 | ``` 89 | 地址: http://(你的短网址域名)/web_admin 90 | 授权: web页自带 session_key 授权,session_key 有效期在 .env 中配置 WEB_SESSION_LIFE 单位秒 91 | ``` 92 | 93 | 3.api 94 | ``` 95 | 地址: http://(你的短网址域名)/api_gen 96 | 方法: POST 97 | 参数: 98 | type: to_short 或 to_long 99 | content: url 100 | access_key: api 授权 key 在 .env 中新增,多个 ACCESS_KEY 使用 | 分割 101 | ``` 102 | 103 | ## 作者 104 | - QQ 365499684 (添加时备注【短网址】) 105 | - Blog http://lukachen.com 106 | - 短网址 http://s.lukachen.com/web_admin 107 | - 觉得对你有所帮助,请点个 star 谢谢 :) 108 | -------------------------------------------------------------------------------- /doc/1.x_esu.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat MySQL Data Transfer 3 | Source Server : lukachen 4 | Target Server Type : MYSQL 5 | Target Server Version : 50148 6 | Date: 2018-05-02 11:10:54 7 | */ 8 | 9 | SET FOREIGN_KEY_CHECKS=0; 10 | 11 | -- ---------------------------- 12 | -- Table structure for esu_url 13 | -- ---------------------------- 14 | DROP TABLE IF EXISTS `esu_url`; 15 | CREATE TABLE `esu_url` ( 16 | `id` int(11) NOT NULL AUTO_INCREMENT, 17 | `code` varchar(8) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '' COMMENT '短网址code', 18 | `long_url` varchar(1024) NOT NULL DEFAULT '' COMMENT '长网址', 19 | `long_url_hash` varchar(32) NOT NULL DEFAULT '' COMMENT '长网址做hash后的值', 20 | `request_num` int(11) NOT NULL DEFAULT '0' COMMENT '请求次数', 21 | `ip` varchar(32) NOT NULL DEFAULT '' COMMENT '请求ip', 22 | `created_at` datetime NOT NULL COMMENT '创建时间', 23 | PRIMARY KEY (`id`) 24 | ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='长短网址对应表'; 25 | -------------------------------------------------------------------------------- /doc/1.x_upgrade_2.x.md: -------------------------------------------------------------------------------- 1 | # 1.x 升级 2.x 指南 2 | 3 | ## 1.备份当前数据表 esu_url、代码(如有问题,方便快速回滚) 4 | 5 | ## 2.根据 2.x .env 修改配置文件 6 | ``` 7 | # 短网址服务域名 8 | ESU_DOMAIN=http://s.lukachen.com 9 | 10 | # 数据库配置 11 | ESU_DB_HOST=127.0.0.1 12 | ESU_DB_DBNAME=esu 13 | ESU_DB_USERNAME=root 14 | ESU_DB_PASSWORD=root 15 | ESU_DB_PORT=3306 16 | ESU_DB_CHARSET=utf8 17 | 18 | # Redis 配置 19 | ESU_REDIS_DSN=tcp://127.0.0.1:6379 20 | 21 | # 是否开启缓存,可选项 0: 不开启, 1: 开启 (开启缓存,数据表跳转统计将失效) 22 | ESU_CACHE_OPEN=0 23 | 24 | # 缓存方式,可选项 Filesystem: 本地文件缓存, Redis: 缓存 (Redis 缓存,依赖 ESU_REDIS_DSN 配置) 25 | ESU_CACHE_CLIENT=Filesystem 26 | 27 | # 默认缓存时间 604800 秒 (1星期) 28 | ESU_CACHE_LIFETIME=604800 29 | 30 | # web_admin 页 access_key 31 | ESU_WEB_ADMIN_ACCESS_KEY=esu 32 | ``` 33 | 34 | ## 3.执行 SQL,添加数据表 35 | ``` 36 | ALTER TABLE esu_url ADD COLUMN `access_key` VARCHAR ( 32 ) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '' COMMENT '密钥' AFTER `ip`; 37 | ALTER TABLE esu_url ADD COLUMN `updated_at` datetime(0) NOT NULL COMMENT '更新时间'; 38 | UPDATE esu_url SET updated_at = created_at; 39 | 40 | UPDATE esu_url SET access_key = 'esu'; 41 | 42 | CREATE TABLE `esu_access` ( 43 | `id` int(11) NOT NULL AUTO_INCREMENT, 44 | `access_key` varchar(32) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '' COMMENT '密钥', 45 | `access_domain` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '可跳转域名', 46 | `created_at` datetime(0) NOT NULL COMMENT '创建时间', 47 | `updated_at` datetime(0) NOT NULL COMMENT '更新时间', 48 | PRIMARY KEY (`id`) USING BTREE 49 | ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '授权表' ROW_FORMAT = Dynamic; 50 | 51 | INSERT INTO `esu_access` VALUES (1, 'esu', 'lukachen.com', '2021-01-05 18:34:48', '2021-01-05 18:34:48'); 52 | ``` 53 | 54 | ## 4.添加授权数据 55 | 整理当前 esu_url 表中,需要跳转的域名 56 | ``` 57 | INSERT INTO `esu_access`(`access_key`, `access_domain`, `created_at`, `updated_at`) VALUES ('esu', 'todo1.com', now(), now()); 58 | INSERT INTO `esu_access`(`access_key`, `access_domain`, `created_at`, `updated_at`) VALUES ('esu', 'todo2.com', now(), now()); 59 | INSERT INTO `esu_access`(`access_key`, `access_domain`, `created_at`, `updated_at`) VALUES ('esu', 'todo3.com', now(), now()); 60 | ``` 61 | 62 | ## 5.修改 composer.json 文件 63 | 修改版本号为 ^2 64 | ``` 65 | "require": { 66 | "chenlongqiang/easy-short-url": "^2" 67 | } 68 | ``` 69 | 70 | ## 6.执行 composer update 完成升级 -------------------------------------------------------------------------------- /doc/2.x_esu.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat Premium Data Transfer 3 | Source Server Type : MySQL 4 | Date: 06/01/2021 10:54:34 5 | */ 6 | 7 | SET FOREIGN_KEY_CHECKS = 0; 8 | 9 | -- ---------------------------- 10 | -- Table structure for esu_url 11 | -- ---------------------------- 12 | DROP TABLE IF EXISTS `esu_url`; 13 | CREATE TABLE `esu_url` ( 14 | `id` int(11) NOT NULL AUTO_INCREMENT, 15 | `code` varchar(8) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '' COMMENT '短网址code', 16 | `long_url` varchar(1024) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '长网址', 17 | `long_url_hash` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '长网址做hash后的值', 18 | `request_num` int(11) NOT NULL DEFAULT 0 COMMENT '请求次数', 19 | `ip` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '请求ip', 20 | `access_key` varchar(32) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '' COMMENT '密钥', 21 | `created_at` datetime(0) NOT NULL COMMENT '创建时间', 22 | `updated_at` datetime(0) NOT NULL COMMENT '更新时间', 23 | PRIMARY KEY (`id`) USING BTREE 24 | ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '长短网址对应表' ROW_FORMAT = Dynamic; 25 | 26 | -- ---------------------------- 27 | -- Table structure for esu_access 28 | -- ---------------------------- 29 | DROP TABLE IF EXISTS `esu_access`; 30 | CREATE TABLE `esu_access` ( 31 | `id` int(11) NOT NULL AUTO_INCREMENT, 32 | `access_key` varchar(32) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '' COMMENT '密钥', 33 | `access_domain` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '可跳转域名', 34 | `created_at` datetime(0) NOT NULL COMMENT '创建时间', 35 | `updated_at` datetime(0) NOT NULL COMMENT '更新时间', 36 | PRIMARY KEY (`id`) USING BTREE 37 | ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '授权表' ROW_FORMAT = Dynamic; 38 | 39 | -- ---------------------------- 40 | -- Records of esu_access 41 | -- ---------------------------- 42 | INSERT INTO `esu_access` VALUES (1, 'esu', 'lukachen.com', '2021-01-05 18:34:48', '2021-01-05 18:34:48'); 43 | 44 | SET FOREIGN_KEY_CHECKS = 1; 45 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | load(); 23 | 24 | (new EasyShortUrl\Router(trim($_SERVER['REQUEST_URI'], '/')))->dispatch(); -------------------------------------------------------------------------------- /src/Cache.php: -------------------------------------------------------------------------------- 1 | 'map_{code}', 54 | ]; 55 | 56 | $find = $replace = []; 57 | foreach ($params as $k => $v) { 58 | $find[] = '{' . $k . '}'; 59 | $replace[] = $v; 60 | } 61 | return str_replace($find, $replace, $mapping[$key]); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/DB.php: -------------------------------------------------------------------------------- 1 | validateAccess($accessKey, $longUrl); 54 | 55 | $longUrlHash = md5($longUrl . $accessKey); 56 | 57 | $existsCode = DB::getInstance()->single("SELECT code FROM esu_url WHERE long_url_hash = ?", [$longUrlHash]); 58 | if ($existsCode !== false) { 59 | return $this->buildShortUrl($existsCode); 60 | } 61 | 62 | $id = DB::getInstance()->insertReturnId('esu_url', [ 63 | 'code' => '', 64 | 'long_url' => $longUrl, 65 | 'long_url_hash' => $longUrlHash, 66 | 'request_num' => 0, 67 | 'ip' => esu_get_ip(), 68 | 'access_key' => $accessKey, 69 | 'created_at' => date('Y-m-d H:i:s'), 70 | 'updated_at' => date('Y-m-d H:i:s'), 71 | ]); 72 | 73 | $code = $this->base10To62($id); 74 | 75 | DB::getInstance()->update('esu_url', ['code' => $code], ['id' => $id]); 76 | 77 | return $this->buildShortUrl($code); 78 | } 79 | 80 | /** 81 | * 短网址还原为长网址 82 | * short url restore long url 83 | * @param $code 84 | * @return bool 85 | */ 86 | public function toLong($code) 87 | { 88 | $this->validateAccessByCode($code); 89 | 90 | $res = DB::getInstance()->row("SELECT id,long_url,request_num FROM esu_url WHERE code = ?", $code); 91 | 92 | return $res['long_url']; 93 | } 94 | 95 | /** 96 | * 跳转次数递增 97 | * redirect num ++ 98 | * @param $code 99 | * @return bool 100 | */ 101 | public function requestNum($code) 102 | { 103 | $res = DB::getInstance()->row("SELECT id,request_num FROM esu_url WHERE code = ?", $code); 104 | if (empty($res)) { 105 | return false; 106 | } 107 | 108 | DB::getInstance()->update('esu_url', [ 109 | 'request_num' => $res['request_num'] + 1, 110 | 'updated_at' => date('Y-m-d H:i:s') 111 | ], ['id' => $res['id']]); 112 | return true; 113 | } 114 | 115 | /** 116 | * 10 进制数转自定义 62 进制数 117 | * decimal to custom 62 hex 118 | * @param $num 119 | * @return string 120 | */ 121 | public function base10To62($num) 122 | { 123 | $res = ''; 124 | while ($num > 0) { 125 | $res = substr(self::STR_SHUFFLE_62, $num % 62, 1) . $res; 126 | $num = floor($num / 62); 127 | } 128 | return $res; 129 | } 130 | 131 | /** 132 | * 校验授权 133 | * validate access 134 | * @param $accessKey 135 | * @param $longUrl 136 | * @throws EasyShortUrlException 137 | */ 138 | private function validateAccess($accessKey, $longUrl) 139 | { 140 | $parseResult = parse_url($longUrl); 141 | if (!isset($parseResult['host']) || empty($parseResult['host'])) { 142 | throw new EasyShortUrlException('error long url'); 143 | } 144 | $res = DB::getInstance()->row("SELECT * FROM esu_access WHERE access_key = ? AND access_domain = ?", $accessKey, $parseResult['host']); 145 | if (empty($res)) { 146 | throw new EasyShortUrlException('not access'); 147 | } 148 | } 149 | 150 | /** 151 | * 校验跳转授权 152 | * validate access by code 153 | */ 154 | private function validateAccessByCode($code) 155 | { 156 | $res = DB::getInstance()->row("SELECT id,long_url,access_key FROM esu_url WHERE code = ?", $code); 157 | if (empty($res)) { 158 | throw new EasyShortUrlException('not found long url'); 159 | } 160 | 161 | $this->validateAccess($res['access_key'], $res['long_url']); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/Exception/EasyShortUrlException.php: -------------------------------------------------------------------------------- 1 | '; 32 | print_r($data); 33 | echo PHP_EOL; 34 | } 35 | } 36 | 37 | if (!function_exists('pe')) { 38 | /** 39 | * 开发调试 40 | * @param $data 41 | */ 42 | function pe($data) 43 | { 44 | p($data); 45 | exit; 46 | } 47 | } 48 | 49 | /** 50 | * 不同环境下获取真实的IP 51 | * @return array|false|mixed|string 52 | */ 53 | function esu_get_ip() { 54 | // 判断服务器是否允许$_SERVER 55 | if(isset($_SERVER)){ 56 | if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){ 57 | $realip = $_SERVER['HTTP_X_FORWARDED_FOR']; 58 | }elseif(isset($_SERVER['HTTP_CLIENT_IP'])) { 59 | $realip = $_SERVER['HTTP_CLIENT_IP']; 60 | }else{ 61 | $realip = $_SERVER['REMOTE_ADDR']; 62 | } 63 | }else{ 64 | // 不允许就使用getenv获取 65 | if(getenv('HTTP_X_FORWARDED_FOR')){ 66 | $realip = getenv( "HTTP_X_FORWARDED_FOR"); 67 | }elseif(getenv('HTTP_CLIENT_IP')) { 68 | $realip = getenv('HTTP_CLIENT_IP'); 69 | }else{ 70 | $realip = getenv('REMOTE_ADDR'); 71 | } 72 | } 73 | return $realip; 74 | } 75 | 76 | /** 77 | * 获取 session key 78 | * @param bool $refresh 79 | * @return mixed 80 | */ 81 | function esu_session_key($refresh = false) { 82 | session_start(); 83 | $sid = session_id(); 84 | 85 | $lifeTime = env('WEB_SESSION_LIFE'); // 一个 session key 有效期 xx 秒 86 | 87 | if (isset($_SESSION[$sid]) && $_SESSION[$sid]['expire'] > time()) { 88 | if ($refresh) { 89 | // 刷新有效期 90 | $_SESSION[$sid]['expire'] = time() + $lifeTime; 91 | return $_SESSION[$sid]['key']; 92 | } else { 93 | return $_SESSION[$sid]['key']; 94 | } 95 | } else { 96 | $_SESSION[$sid] = [ 97 | 'key' => esu_session_key_gen($sid), 98 | 'expire' => time() + $lifeTime, 99 | ]; 100 | return $_SESSION[$sid]['key']; 101 | } 102 | } 103 | 104 | /** 105 | * session key 生成 106 | * @param $sid 107 | * @return string 108 | */ 109 | function esu_session_key_gen($sid) { 110 | return md5($sid . time() . 'hey!easy-short-url'); 111 | } 112 | 113 | /** 114 | * 授权校验 115 | */ 116 | function esu_validate_access() 117 | { 118 | $accessPass = false; 119 | if (isset($_POST['access_key']) && in_array($_POST['access_key'], explode('|', env('ACCESS_KEY')))) { // API开发者授权 120 | $accessPass = true; 121 | } 122 | if (isset($_POST['session_key']) && $_POST['session_key'] == esu_session_key()) { // web授权 123 | $accessPass = true; 124 | } 125 | if (!$accessPass) { 126 | header('HTTP/1.1 401 Unauthorized'); 127 | exit; 128 | } 129 | } 130 | 131 | function response($data, $code, $msg = '') 132 | { 133 | return [ 134 | 'code' => $code, 135 | 'data' => $data, 136 | 'msg' => $msg, 137 | ]; 138 | } 139 | 140 | function response_success($data = []) 141 | { 142 | return response($data, '0', 'ok'); 143 | } 144 | 145 | function response_error($msg = 'error', $data = []) 146 | { 147 | return response($data, '1', $msg); 148 | } 149 | 150 | function api_success($data) 151 | { 152 | exit(json_encode(response_success($data))); 153 | } 154 | 155 | function api_error($msg = 'error', $data = []) 156 | { 157 | exit(json_encode(response_error($msg, $data))); 158 | } 159 | 160 | function api_response($data, $code, $msg = '') 161 | { 162 | exit(json_encode(response($data, $code, $msg))); 163 | } -------------------------------------------------------------------------------- /src/Router.php: -------------------------------------------------------------------------------- 1 | uri = $uri; 20 | } 21 | 22 | /** 23 | * 路由分发 24 | * router dispatch 25 | */ 26 | public function dispatch() 27 | { 28 | if (method_exists($this, $this->uri)) { 29 | call_user_func([$this, $this->uri]); 30 | } else { 31 | $this->redirectLongUrl(); 32 | } 33 | exit; 34 | } 35 | 36 | /** 37 | * web admin 38 | */ 39 | public function web_admin() 40 | { 41 | require ESU_ROOT_PATH . '/src/WebAdmin.php'; 42 | exit; 43 | } 44 | 45 | /** 46 | * web api 47 | */ 48 | public function api_gen() 49 | { 50 | try { 51 | if ($_POST['type'] == 'to_short') { 52 | $longUrl = urldecode($_POST['content']); 53 | if ( 54 | empty($_POST['content']) 55 | || (strpos($longUrl, 'http://') !== 0 && strpos($longUrl, 'https://') !== 0) 56 | ) { 57 | throw new EasyShortUrlException('error scheme, please start with http:// or https://'); 58 | } 59 | $shortUrl = (EasyShortUrl::getInstance())->toShort($longUrl, $_POST['access_key']);; 60 | api_success($shortUrl); 61 | } elseif ($_POST['type'] == 'to_long') { 62 | $code = trim(parse_url(urldecode($_POST['content']), PHP_URL_PATH), '/'); 63 | $longUrl = (EasyShortUrl::getInstance())->toLong($code);; 64 | api_success($longUrl); 65 | } else { 66 | throw new EasyShortUrlException('api not found'); 67 | } 68 | } catch (\Exception $e) { 69 | api_error($e->getMessage()); 70 | } 71 | } 72 | 73 | /** 74 | * 跳转长网址 75 | * redirect to long url 76 | */ 77 | public function redirectLongUrl() 78 | { 79 | try { 80 | if (env('ESU_CACHE_OPEN') === '1') { 81 | $cacheKey = Cache::key('mapping_code_and_long_url', ['code' => $this->uri]); 82 | $cache = Cache::client(); 83 | $mapping = $cache->getItem($cacheKey); 84 | if (!$mapping->isHit()) { 85 | // 元素在缓存中不存在,被动缓存 86 | $longUrl = (EasyShortUrl::getInstance())->toLong($this->uri);; 87 | $mapping->set($longUrl); 88 | $cache->save($mapping); 89 | } else { 90 | $longUrl = $mapping->get(); 91 | } 92 | } 93 | 94 | // 未启用缓存或从缓存取出失败 95 | if (!isset($longUrl) || empty($longUrl)) { 96 | $longUrl = (EasyShortUrl::getInstance())->toLong($this->uri);; 97 | } 98 | 99 | // 不启用缓存,才做数据统计 100 | if (env('ESU_CACHE_OPEN') !== '1') { 101 | (EasyShortUrl::getInstance())->requestNum($this->uri);; 102 | } 103 | 104 | header('Location:' . $longUrl, true, 302); 105 | exit; 106 | } catch (\Exception $e) { 107 | // todo write log 108 | header("HTTP/1.1 404 Not Found"); 109 | exit; 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/WebAdmin.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 短网址 5 | 6 | 262 | 263 |
264 |
265 |
266 |

短网址

267 |
268 |
269 |
270 |
271 |
272 | 280 |
    281 |
  • 282 | GitHub 283 |
  • 284 |
285 |
286 |
287 |
288 |
289 | 290 |
291 | 292 |
293 | 294 |
295 | 296 |
297 | 298 |
299 | 300 |
301 |
302 | 缩短网址 303 |
304 |
305 |
306 |
307 |
308 | 309 |
310 | 311 |
312 | 313 |
314 | 315 |
316 | 317 |
318 | 319 |
320 |
321 | 还原网址 322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 | 332 |
333 | 334 | 335 | 367 | 368 | 369 | -------------------------------------------------------------------------------- /static/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenlongqiang/easy-short-url/5a265ef9f6b96f878c932a5366dccfc60c83bb98/static/img.png --------------------------------------------------------------------------------