陈德本 hace 5 años
commit
85dc24c166
Se han modificado 100 ficheros con 11362 adiciones y 0 borrados
  1. 23 0
      App/Api/Config/Route.php
  2. 15 0
      App/Api/Config/SiteRule.php
  3. 95 0
      App/Api/Controller.php
  4. 33 0
      App/Api/Controller/Index.php
  5. 54 0
      App/Api/Controller/Message.php
  6. 23 0
      App/Api/Package/Database/Num.php
  7. 41 0
      App/Api/RestfulController.php
  8. 39 0
      App/Api/Template/Head.phtml
  9. 80 0
      App/Api/Template/Index.phtml
  10. 59 0
      App/Api/Template/Message.phtml
  11. 35 0
      App/Script/Controller.php
  12. 32 0
      App/Script/Controller/Server.php
  13. 18 0
      App/Script/Package/Database/Code.php
  14. 29 0
      Autoload.php
  15. 37 0
      Config/MySQL/Core.php
  16. 28 0
      Config/Redis/Core.php
  17. 32 0
      Config/Route.php
  18. 98 0
      Frame/Config.php
  19. 283 0
      Frame/Controller.php
  20. 187 0
      Frame/Dispatcher.php
  21. 20 0
      Frame/Exception.php
  22. 13 0
      Frame/Exception/Database.php
  23. 12 0
      Frame/Exception/Validate.php
  24. 251 0
      Frame/Filter.php
  25. 341 0
      Frame/Helper.php
  26. 67 0
      Frame/Helper/Date.php
  27. 39 0
      Frame/Helper/File.php
  28. 92 0
      Frame/Helper/Format.php
  29. 269 0
      Frame/Helper/Html.php
  30. 31 0
      Frame/Helper/Location.php
  31. 11 0
      Frame/Helper/Network.php
  32. 54 0
      Frame/Helper/Validate.php
  33. 77 0
      Frame/Init.php
  34. 58 0
      Frame/Library.php
  35. 83 0
      Frame/Library/AntiSpam.php
  36. 161 0
      Frame/Library/BaseConverter.php
  37. 8 0
      Frame/Library/Benchmark.php
  38. 19 0
      Frame/Library/Curl.php
  39. 44 0
      Frame/Library/Memcache.php
  40. 41 0
      Frame/Library/Memcached.php
  41. 252 0
      Frame/Library/MySQL.php
  42. 402 0
      Frame/Library/MySQL/Query.php
  43. 63 0
      Frame/Library/Redis.php
  44. 79 0
      Frame/Library/Ticket.php
  45. 347 0
      Frame/Logger.php
  46. 85 0
      Frame/Model.php
  47. 59 0
      Frame/Object.php
  48. 56 0
      Frame/Package.php
  49. 11 0
      Frame/Package/Cache.php
  50. 152 0
      Frame/Package/Cache/Memcache.php
  51. 179 0
      Frame/Package/Cache/Redis.php
  52. 11 0
      Frame/Package/Counter.php
  53. 148 0
      Frame/Package/Counter/Hash.php
  54. 141 0
      Frame/Package/Counter/Single.php
  55. 13 0
      Frame/Package/Database.php
  56. 275 0
      Frame/Package/Database/Common.php
  57. 342 0
      Frame/Package/Database/Sharding.php
  58. 421 0
      Frame/Package/Database/Single.php
  59. 62 0
      Frame/Package/Queue.php
  60. 82 0
      Frame/Package/RedisQueue.php
  61. 291 0
      Frame/Package/Set.php
  62. 203 0
      Frame/Request.php
  63. 104 0
      Frame/Response.php
  64. 39 0
      Frame/Router.php
  65. 148 0
      Frame/Session.php
  66. 234 0
      Frame/Template.php
  67. 218 0
      Library/Curl.php
  68. 241 0
      Library/Pager.php
  69. 14 0
      Library/Sms.php
  70. 133 0
      Library/Sms/ReceiveSmsFree.php
  71. 8 0
      apidoc.json
  72. 7 0
      composer.json
  73. 1447 0
      composer.lock
  74. BIN
      composer.phar
  75. 18 0
      constants.php
  76. 58 0
      functions.php
  77. 6 0
      index.php
  78. 521 0
      public/api/css/sms_receive.css
  79. 11 0
      public/api/index.php
  80. 14 0
      public/script/index.php
  81. 2 0
      vendor/ark/filecache/.gitignore
  82. 3 0
      vendor/ark/filecache/.travis.yml
  83. 19 0
      vendor/ark/filecache/LICENSE
  84. 50 0
      vendor/ark/filecache/README.md
  85. 17 0
      vendor/ark/filecache/composer.json
  86. 9 0
      vendor/ark/filecache/phpunit.xml
  87. 242 0
      vendor/ark/filecache/src/FileCache.php
  88. 55 0
      vendor/ark/filecache/src/Util.php
  89. 56 0
      vendor/ark/filecache/tests/baseTest.php
  90. 7 0
      vendor/autoload.php
  91. 1 0
      vendor/bin/var-dump-server
  92. 559 0
      vendor/cache/adapter-common/AbstractCachePool.php
  93. 269 0
      vendor/cache/adapter-common/CacheItem.php
  94. 67 0
      vendor/cache/adapter-common/Changelog.md
  95. 23 0
      vendor/cache/adapter-common/Exception/CacheException.php
  96. 21 0
      vendor/cache/adapter-common/Exception/CachePoolException.php
  97. 19 0
      vendor/cache/adapter-common/Exception/InvalidArgumentException.php
  98. 26 0
      vendor/cache/adapter-common/HasExpirationTimestampInterface.php
  99. 68 0
      vendor/cache/adapter-common/JsonBinaryArmoring.php
  100. 22 0
      vendor/cache/adapter-common/LICENSE

+ 23 - 0
App/Api/Config/Route.php

@@ -0,0 +1,23 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: chendeben
+ * Date: 2018/6/20
+ * Time: 下午2:26
+ */
+
+namespace Heanup\App\Api\Config;
+
+use Heanup\Frame\Config;
+
+class Route extends Config
+{
+    protected static $data = [
+        //高级美妆
+        [
+            "name" => "域名列表",
+            "pattern" => "/vestacp/domain/:s",//api/material/sort/beauty/pre
+            "action" => "Heanup\App\Api\Controller\Vestacp\Domain"
+        ]
+    ];
+}

+ 15 - 0
App/Api/Config/SiteRule.php

@@ -0,0 +1,15 @@
+<?php
+
+
+namespace Heanup\App\Api\Config;
+
+
+use Heanup\Frame\Config;
+
+class SiteRule extends Config
+{
+    protected static $data=[
+        'receive-sms-free.net'=>'ReceiveSmsFree'
+    ];
+
+}

+ 95 - 0
App/Api/Controller.php

@@ -0,0 +1,95 @@
+<?php
+
+namespace Heanup\App\Api;
+
+
+use Exception;
+
+abstract class Controller extends \Heanup\Frame\Controller
+{
+
+    protected function main()
+    {
+
+    }
+
+    /**
+     * 验证登录信息
+     * 无需验证或者单独验证的类可以覆盖该方法
+     *
+     * @param bool $checkLogin
+     * @return bool
+     * @throws Exception
+     */
+    protected function checkAuth($checkLogin = false)
+    {
+        return true;
+    }
+
+
+    /**
+     * 输出模板,自动查找模板路径
+     *
+     * @param string $layout
+     */
+    protected function render($layout = 'Common/Layout')
+    {
+        header("Content-type: text/html; charset=utf-8");
+        $this->getTemplate()->render(false, $layout);
+    }
+
+
+    /**
+     * 输出json信息
+     *
+     * @param int $code
+     * @param string $msg
+     * @param array $data
+     * @param string $callback
+     */
+    protected function output($code = 0, $msg = '', $data = array(), $callback = "")
+    {
+        $output = array(
+            'code' => $code,
+            'msg' => $msg,
+            'data' => $data ?: array()
+        );
+        header('Content-type: application/json');
+        if ($callback) {
+            echo $response = $callback . "(" . json_encode($output) . ")";
+            exit();
+        } else {
+            echo $response = json_encode($output);
+            exit();
+        }
+        // 记录调试信息
+    }
+
+    /**
+     * 输出错误信息
+     *
+     * @param int $code
+     * @param string $message
+     */
+    protected function showError($code = 0, $message = '')
+    {
+        $output = array(
+            'code' => intval($code),
+            'message' => $message,
+            'data' => []
+        );
+        header('Content-type: application/json');
+        echo json_encode($output);
+        exit();
+    }
+
+    /**
+     * 显示成功信息
+     * @param $data
+     */
+    protected function showSuccess($data = array())
+    {
+        $this->output(0, '请求成功', $data);
+    }
+
+}

+ 33 - 0
App/Api/Controller/Index.php

@@ -0,0 +1,33 @@
+<?php
+
+
+namespace Heanup\App\Api\Controller;
+
+
+use Heanup\App\Api\Controller;
+use Heanup\App\Api\Package\Database\Num;
+use Heanup\Library\Pager;
+use Heanup\Library\Sms\ReceiveSmsFree;
+
+class Index extends Controller
+{
+    protected function main()
+    {
+        $country=$this->getQuery()->string("country");
+        if ($country){
+            $where=['country'=>"+".$country];
+        }else{
+            $where=[];
+        }
+        $page = $this->getQuery()->intval("page", 1);
+        $pgSize = 20;
+//        $country="+86";
+//        $phone_num="17771934420";
+//        $data = $lib->getMessage($country, $phone_num);
+//        $data=$lib->getCountry();
+        $this->_data = Num::getPageList($where, "", $page, $pgSize, ['id' => true]);
+        $this->pager = Pager::pager($this->_data['total_count'], $pgSize, http_build_query($_GET), $page);
+        $this->render();
+//        $this->showSuccess($this->data);
+    }
+}

+ 54 - 0
App/Api/Controller/Message.php

@@ -0,0 +1,54 @@
+<?php
+
+
+namespace Heanup\App\Api\Controller;
+
+
+use Heanup\App\Api\Package\Database\Num;
+use Heanup\App\Api\RestfulController;
+use Heanup\Library\Sms\ReceiveSmsFree;
+
+class Message extends RestfulController
+{
+
+    /**
+     * @inheritDoc
+     */
+    protected function getData()
+    {
+        $id=$this->getQuery()->intval("id");
+        $num_info=Num::getLine($id);
+        if ($num_info){
+            $lib=new ReceiveSmsFree();
+            $this->message=$lib->getMessage($num_info['country'], $num_info['phone']);
+            $this->number_info=$num_info;
+            $this->render();
+//            $this->showSuccess($this->message);
+        }
+
+    }
+
+    /**
+     * @inheritDoc
+     */
+    protected function postData()
+    {
+
+    }
+
+    /**
+     * @inheritDoc
+     */
+    protected function putData()
+    {
+
+    }
+
+    /**
+     * @inheritDoc
+     */
+    protected function deleteData()
+    {
+
+    }
+}

+ 23 - 0
App/Api/Package/Database/Num.php

@@ -0,0 +1,23 @@
+<?php
+
+
+namespace Heanup\App\Api\Package\Database;
+
+
+use Heanup\Frame\Package\Database\Single;
+
+class Num extends Single
+{
+    const CONFIG_CLASS = 'MySQL\\Core';
+    protected static $tableName = 'phone_num';
+    protected static $fields = [];
+    protected static $primaryKey = 'id';
+    public static function insert($data, $returnLastInsertId = true)
+    {
+        if (self::getOne(['country'=>$data['country'],'phone'=>$data['phone']])){
+            return true;
+        }
+        return parent::insert($data, $returnLastInsertId);
+    }
+
+}

+ 41 - 0
App/Api/RestfulController.php

@@ -0,0 +1,41 @@
+<?php
+
+namespace Heanup\App\Api;
+
+abstract class RestfulController extends Controller
+{
+
+    protected $isrestful = true;
+
+    /**
+     * Get Method|获取数据
+     * @return void
+     */
+    protected abstract function getData();
+
+    /**
+     * Post Method|新增数据
+     * @return void
+     */
+    protected abstract function postData();
+
+    /**
+     * Put Method|更新数据
+     * @return void
+     */
+    protected abstract function putData();
+
+    /**
+     * Delete Method|删除数据
+     * @return void
+     */
+    protected abstract function deleteData();
+
+    /**
+     * Patch Method|更新部分字段,如:status
+     * @return void
+     *
+     */
+    protected function patchData(){}
+
+}

+ 39 - 0
App/Api/Template/Head.phtml

@@ -0,0 +1,39 @@
+<meta charset="utf-8">
+<meta http-equiv="X-UA-Compatible" content="IE=edge">
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<meta name="description" content="">
+<meta name="author" content="">
+<link rel="icon" href="/favicon.ico">
+<title><?php echo $this->title ?></title>
+<!-- Bootstrap core CSS -->
+<link href="/static/css/zui.lite.min.css" rel="stylesheet">
+<link href="//<?php echo $this->getDomainHost('lib'); ?>/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
+<link href="//<?php echo $this->getDomainHost('lib'); ?>/bootstrap/3.3.5/css/bootstrap-theme.min.css" rel="stylesheet">
+<!--awesome-->
+<link href="//<?php echo $this->getDomainHost('lib'); ?>/font-awesome/4.5.0/css/font-awesome.min.css" rel="stylesheet">
+<!-- metisMenu -->
+<link href="//<?php echo $this->getDomainHost('lib'); ?>/metisMenu/metisMenu.min.css" rel="stylesheet" >
+<!-- Custom styles for this template -->
+<link href="/static/css/dashboard.css" rel="stylesheet">
+<!--colorbox -->
+<link href="/lib/colorbox/colorbox.css" rel="stylesheet">
+<!--colorbox -->
+<link href="/static/css/main.css" rel="stylesheet">
+
+<!--正常样式是foot,不过作为后台这个可以简单些, ^_^-->
+<script src="//<?php echo $this->getDomainHost('lib'); ?>/jquery/1.11.3/jquery.min.js"></script>
+<!-- <script src="//<?php echo $this->getDomainHost('lib'); ?>/bootstrap/3.3.5/js/bootstrap.min.js"></script>-->
+<script type="text/javascript" src="/lib/Js/zui.min.js"></script>
+<script src="//<?php echo $this->getDomainHost('lib'); ?>/metisMenu/metisMenu.js"></script>
+<script src="//<?php echo $this->getDomainHost('lib'); ?>/jquery/jquery.form.min.js"></script>
+<script src="//<?php echo $this->getDomainHost('lib'); ?>/js-cookie/js.cookie.js"></script>
+<script src="//<?php echo $this->getDomainHost('lib'); ?>/colorbox/jquery.colorbox-min.js"></script>
+<script src="//<?php echo $this->getDomainHost('lib'); ?>/Js/DatePicker/WdatePicker.js"></script>
+<script src="//<?php echo $this->getDomainHost('lib'); ?>/Js/ajaxfileupload.js"></script>
+<script src="//<?php echo $this->getDomainHost('lib'); ?>/jquery/jquery.validate.min.js"></script>
+<script src="//<?php echo $this->getDomainHost('static'); ?>/js/validate_zh.js"></script>
+<script src="https://cdn.ravenjs.com/3.21.0/raven.min.js" crossorigin="anonymous"></script>
+<script>
+    Raven.config('http://3b665129ce614f9481a8e7bb886383cc@sentry.97admin.com/3').install();
+</script>
+

+ 80 - 0
App/Api/Template/Index.phtml

@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
+    <meta name="description"
+          content="本平台可以在线接收短信,接收短信验证码,显示迅速,与国外类似短信验证码接收更快捷。在线短信接收平台,免费验证码接收平台,虚拟手机号接收短信app,哪个短信平台比较好,代收手机短信验证码,虚拟手机号码接收短信,国外短信接收平台,手机短信验证码,手机验证码平台,接码平台,短信验证码,验证码平台,云验证码平台,短信验证码是多少,手机短信验证码接收系统,验证码短信平台,虚拟手机号验证码平台,手机收不到验证码,手机验证码接收软件,免费的临时手机号软件,手机获取验证码收费吗,虚拟手机验证码生成器,免费的临时手机号软件,网站验证码短信平台,接收手机验证码平台,发短信最便宜,手机收验证码服务,网站注册手机验证码">
+    <meta name="keywords"
+          content="在线短信接收,sms_receive,短信接收,短信验证码接收,在线短信接收平台,免费验证码接收平台,虚拟手机号接收短信app,哪个短信平台比较好,代收手机短信验证码,虚拟手机号码接收短信,国外短信接收平台,手机短信验证码,手机验证码平台,接码平台,短信验证码,验证码平台,云验证码平台,短信验证码是多少,手机短信验证码接收系统,验证码短信平台,虚拟手机号验证码平台,手机收不到验证码,手机验证码接收软件,免费的临时手机号软件,手机获取验证码收费吗,虚拟手机验证码生成器,免费的临时手机号软件,网站验证码短信平台,接收手机验证码平台,发短信最便宜,手机收验证码服务,网站注册手机验证码">
+    <title>云短信 - 在线短信接收</title>
+    <link href="https://static.pdf-lib.org/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
+    <link href="https://static.pdf-lib.org/sms_resouce/static/css/style.css" rel="stylesheet">
+    <link href="/css/sms_receive.css" rel="stylesheet">
+</head>
+<body>
+
+<div class="container">
+    <ul class="nav nav-pills">
+    <li role="presentation"><a href="/">在线短信接收</a></li>
+    </ul>
+    <div class="row" style="margin-top: 15px;margin-bottom: 15px">
+        <h2>使用须知</h2>
+        <hr>
+        <nav class="col-xs-12 col-sm-12 col-md-12 col-lg-6">
+            <ul>
+                <li>在使用前你应该知晓下面的电话号码短信内容所有人都可以查看</li>
+                <li>请不要用这个电话号码接收重要内容</li>
+                <li>下面的电话号码只用于注册一些网站,<b class="text-info">防止被骚扰</b></li>
+                <li>他人可以通过此电话号码找回密码,所以注册时应注意个人信息</li>
+                <li>严禁用作非法用途,所产生的法律后果由使用者自行承担</li>
+                <li><b class="text-danger">由此造成经济损失概不负责</b></li>
+                <li>在使用时即代表以上条款已同意</li>
+            </ul>
+        </nav>
+        <hr>
+    </div>
+</div>
+<section class="sms_content">
+    <div class="container-fluid">
+        <?php
+        //            print_r($this->_data);
+        foreach ($this->_data['data'] as $item) {
+            ?>
+            <div class="sms-number-list row show-grid container">
+                <div class="number-list-flag col-xs-12 col-sm-12 col-md-2 col-lg-2 margin-3">
+
+                </div>
+                <div class="number-list-phone_number col-xs-12 col-sm-12 col-md-3 col-lg-3">
+                    <div class="phone_number-text">
+                        <small><?php echo $item['country']; ?></small>
+                        <h3><?php echo $item['phone']; ?></h3>
+                    </div>
+
+                </div>
+                <div class="number-list-info col-xs-12 col-sm-12 col-md-4 col-lg-4 margin-3">
+
+
+                </div>
+                <div class="sms-number-read col-xs-12 col-sm-12 col-md-3 col-lg-3 margin-3">
+                    <a href="/message/<?php echo $item['id']; ?>" type="button"
+                       class="btn btn-w-m btn-success btn-lg">
+                        <i></i>
+                        <b>阅读短信</b>
+                    </a>
+                </div>
+            </div>
+            <?php
+        }
+        ?>
+
+
+    </div>
+
+    <div class="sms-content-pager">
+        <?php echo $this->pager; ?>
+    </div>
+</section>
+
+</body>
+</html>

+ 59 - 0
App/Api/Template/Message.phtml

@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
+    <meta name="description"
+          content="本平台可以在线接收短信,接收短信验证码,显示迅速,与国外类似短信验证码接收更快捷。在线短信接收平台,免费验证码接收平台,虚拟手机号接收短信app,哪个短信平台比较好,代收手机短信验证码,虚拟手机号码接收短信,国外短信接收平台,手机短信验证码,手机验证码平台,接码平台,短信验证码,验证码平台,云验证码平台,短信验证码是多少,手机短信验证码接收系统,验证码短信平台,虚拟手机号验证码平台,手机收不到验证码,手机验证码接收软件,免费的临时手机号软件,手机获取验证码收费吗,虚拟手机验证码生成器,免费的临时手机号软件,网站验证码短信平台,接收手机验证码平台,发短信最便宜,手机收验证码服务,网站注册手机验证码">
+    <meta name="keywords"
+          content="在线短信接收,sms_receive,短信接收,短信验证码接收,在线短信接收平台,免费验证码接收平台,虚拟手机号接收短信app,哪个短信平台比较好,代收手机短信验证码,虚拟手机号码接收短信,国外短信接收平台,手机短信验证码,手机验证码平台,接码平台,短信验证码,验证码平台,云验证码平台,短信验证码是多少,手机短信验证码接收系统,验证码短信平台,虚拟手机号验证码平台,手机收不到验证码,手机验证码接收软件,免费的临时手机号软件,手机获取验证码收费吗,虚拟手机验证码生成器,免费的临时手机号软件,网站验证码短信平台,接收手机验证码平台,发短信最便宜,手机收验证码服务,网站注册手机验证码">
+    <title>云短信 - 在线短信接收</title>
+    <link href="https://static.pdf-lib.org/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
+</head>
+<body>
+<div class="container">
+    <ul class="nav nav-pills">
+        <li role="presentation"><a href="/">在线短信接收</a></li>
+        <li role="presentation" class="active"><a href="#">短信列表</a></li>
+    </ul>
+    <div class="well  panel-info">
+        <div class="panel-heading panel-title"> 当前号码:<?php
+            echo $this->number_info['country'] . " " . $this->number_info['phone'];
+            ?></div>
+        <div class="panel-body ">
+            <table class="table table-hover">
+                <thead>
+                <tr>
+                    <th width="3%"> #</th>
+                    <th width="12%"> 电话号码</th>
+                    <th width="70%"> 短信内容</th>
+                    <th width="15%"> 发送时间</th>
+                </tr>
+                </thead>
+                <tbody>
+                <?php
+                $i = 1;
+                foreach ($this->message as $item) {
+                    ?>
+                    <tr>
+                        <td><?php echo $i++; ?></td>
+
+                        <td> <?php echo $item['from']; ?> </td>
+
+                        <td> <?php echo $item['message']; ?> </td>
+                        <td>
+                            <time><?php echo $item['time']; ?></time>
+                        </td>
+                    </tr>
+                    <?php
+                }
+                ?>
+
+                </tbody>
+            </table>
+        </div>
+       </div>
+
+</div>
+</body>
+</html>

+ 35 - 0
App/Script/Controller.php

@@ -0,0 +1,35 @@
+<?php
+
+namespace Heanup\App\Script;
+
+
+use Heanup\Frame\Helper;
+use Heanup\Frame\Logger;
+
+abstract class Controller extends \Heanup\Frame\Controller
+{
+
+    /**
+     * 获取命令行参数
+     * @return array
+     */
+    protected function getArguments()
+    {
+        return Helper::getArguments();
+    }
+
+    /**
+     * 记录下运行日志
+     */
+    public function after()
+    {
+        parent::after();
+        Logger::debug('', 'runtime');
+    }
+
+    protected function addLogs($msg,$file="admin")
+    {
+        Logger::setLog($msg, $file);
+    }
+
+}

+ 32 - 0
App/Script/Controller/Server.php

@@ -0,0 +1,32 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: chendeben
+ * Date: 2018/8/31
+ * Time: 下午9:35
+ */
+
+namespace Heanup\App\Script\Controller;
+
+
+use Heanup\App\Script\Controller;
+use Heanup\Library\Sms\ReceiveSmsFree;
+
+class Server extends Controller
+{
+
+    /**
+     * 执行逻辑
+     */
+    protected function main()
+    {
+        $lib = new ReceiveSmsFree();
+        $data = $lib->getCountry();
+        foreach ($data as $item) {
+            $lib->getPhoneNums($item['link']);
+        }
+        $this->showSuccess($data);
+
+
+    }
+}

+ 18 - 0
App/Script/Package/Database/Code.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace Heanup\App\Script\Package\Database;
+
+use Heanup\Frame\Package\Database\Single;
+
+class Code extends Single
+{
+
+    const CONFIG_CLASS = 'MySQL\\Shadowsocks';
+
+    protected static $tableName = 'invite_code';
+
+    protected static $fields = array();
+
+    protected static $primaryKey = 'id';
+
+}

+ 29 - 0
Autoload.php

@@ -0,0 +1,29 @@
+<?php
+
+namespace Heanup;
+
+// 设置自动加载
+spl_autoload_register('\Heanup\Autoload::main');
+
+class Autoload
+{
+    /**
+     * 自动加载
+     * @param $className
+     */
+    public static function main($className)
+    {
+        $file = trim($className, '\\');
+        $file = explode('\\', $file);
+        // 判断顶级命名空间
+        $path = __DIR__;
+        $topNameSpace = array_shift($file);
+        if ($topNameSpace != 'Heanup') {
+            $path = $path . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . $topNameSpace;
+        }
+        $file = $path . DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $file) . '.php';
+        if (is_file($file)) {
+            require_once($file);
+        }
+    }
+}

+ 37 - 0
Config/MySQL/Core.php

@@ -0,0 +1,37 @@
+<?php
+
+namespace Heanup\Config\MySQL;
+
+use Heanup\Frame\Config;
+
+/**
+ * 默认数据库配置,不同的库使用不同的配置文件
+ */
+class Core extends Config
+{
+    protected static $data = array(
+
+        "master" => [
+            "host" => "myhome.97admin.com",
+            "port" => "3306",
+            "username" => "chendeben",
+            "password" => "6485882",
+            "dbname" => "receive_sms",
+            "charset" => "utf8",
+            "timeout" => 3
+        ],
+        "slave_list" => [
+            [
+                "host" => "myhome.97admin.com",
+                "port" => "3306",
+                "username" => "chendeben",
+                "password" => "6485882",
+                "dbname" => "receive_sms",
+                "charset" => "utf8",
+                "timeout" => 3
+            ]
+        ]
+
+    );
+
+}

+ 28 - 0
Config/Redis/Core.php

@@ -0,0 +1,28 @@
+<?php
+
+namespace Heanup\Config\Redis;
+
+use Heanup\Frame\Config;
+
+/**
+ * 默认Redis配置
+ */
+class Core extends Config
+{
+    protected static $data = [
+        "master" => [
+            "host" => "myhome.97admin.com",
+            "port" => "63790",
+            "timeout" => 3
+        ],
+        "slave_list" => [
+            [
+                "host" => "myhome.97admin.com",
+                "port" => "63790",
+                "timeout" => 3
+            ]
+        ]
+    ];
+
+
+}

+ 32 - 0
Config/Route.php

@@ -0,0 +1,32 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: chendeben
+ * Date: 2019-02-22
+ * Time: 16:50
+ */
+
+namespace Heanup\Config;
+
+use Heanup\Frame\Config;
+
+class Route extends Config
+{
+    public static function getData($section = null)
+    {
+        $class="Heanup\App\\".$section."\Config\Route";
+        return $class::getData();
+    }
+//    /**
+//     * 获取配置信息,
+//     * 如果配置了$dataFile则读取后者中的数据
+//     * @param string $section
+//     * @return mixed
+//     */
+//    public static function getData($section = null)
+//    {
+//        $class="Heanup\App\\".$section."\Config\Route";
+//        return $class::getData();
+//    }
+
+}

+ 98 - 0
Frame/Config.php

@@ -0,0 +1,98 @@
+<?php
+
+namespace Heanup\Frame;
+
+/**
+ * 配置文件
+ */
+class Config
+{
+    protected static $dataFile = ''; // 配置文件目录,绝对路径,json格式,优先使用覆盖$data
+    protected static $data = array();
+
+    /**
+     * 获取配置信息,
+     * 如果配置了$dataFile则读取后者中的数据
+     * @param null $section
+     * @return mixed
+     */
+    public static function getData($section = null)
+    {
+        $class = get_called_class();
+        // 只读取一次文件
+        if ($class::$dataFile) {
+            $path = self::getPathByMode($class::$dataFile);
+            $class::$data = self::readFile($path);
+            $class::$dataFile = '';
+        }
+        $data = $class::$data;
+        if (!is_null($section)) {
+            if (isset($data[$section])) {
+                return $data[$section];
+            } else {
+                return null;
+            }
+        }
+        return $data;
+    }
+
+    /**
+     * 根据后缀读取文件内容并且返回
+     * ini 直接解析
+     * json 读取后解析,如果解析失败返回空
+     * php 文件, include
+     * 其他文件 直接读取内容并且返回
+     *
+     * @param $path
+     * @return array | string;
+     */
+    public static function readFile($path)
+    {
+        $data = array();
+        if (!file_exists($path)) {
+            return $data;
+        }
+        $pathInfo = pathinfo($path);
+        $extension = strtolower($pathInfo['extension']);
+        if ($extension == 'ini') {
+            $data = parse_ini_file($path);
+        } elseif ($extension == 'json') {
+            $content = file_get_contents($path);
+            if (!$content) {
+                return $data;
+            }
+            $content = json_decode($content, true);
+            if (!$content) {
+                return $data;
+            }
+            $data = $content;
+            unset($content);
+        } elseif ($extension == 'php') {
+            $data = include($path);
+        } else {
+            $data = file_get_contents($path);
+        }
+        return $data;
+    }
+
+    /**
+     * 根据环境变量$mode获取文件路径
+     * @param $filename
+     * @return string
+     */
+    protected static function getPathByMode($filename)
+    {
+        $envPath = dirname(__DIR__) . '/env';
+        $mode = Request::getMode();
+        $path = $envPath.'/product/';
+        switch ($mode) {
+            case Request::MODE_ONLINE:
+                $path = $envPath.'/product/';
+                break;
+        }
+        $filename = $path . ltrim($filename, '/');
+        return $filename;
+    }
+
+
+}

+ 283 - 0
Frame/Controller.php

@@ -0,0 +1,283 @@
+<?php
+
+namespace Heanup\Frame;
+
+abstract class Controller
+{
+    protected $tpl; // 模板路径, 可选
+    protected $queryObj;
+    protected $data_name = false;
+    protected $isrestful = false;
+
+    final public function __construct()
+    {
+        $this->queryObj = $this->getQuery();
+    }
+
+    /**
+     * 主逻辑
+     */
+    final public function run()
+    {
+        try {
+            $this->before();
+            $method = strtolower($_SERVER['REQUEST_METHOD']);
+            if ($method == 'options') {
+                $this->optionsData();
+            }
+            $this->checkAuth();
+            $data_name = $method . 'Data';
+            if ($this->isrestful) {
+                if (!method_exists(get_called_class(), $data_name)) {
+                    Response::sendResponse(405, ['method' => strtoupper($method)], 'Method not allowed');
+                    die();
+                }
+                $this->$data_name();
+            } else {
+                $this->main();
+            }
+        } catch (\Exception $ex) {
+            $this->showError($ex->getCode(), $ex->getMessage());
+        }
+    }
+
+    public function optionsData()
+    {
+        header("HTTP/1.1 200");
+        header("Access-Control-Allow-Origin: *");
+        header("Access-Control-Allow-Methods: OPTIONS, GET, POST, PUT, PATCH, DELETE");
+//        header("Access-Control-Max-Age: 3");
+        header("Access-Control-Allow-Headers: lang,token,content-type");
+        die();
+
+    }
+
+    /**
+     * 尽快输出结果,然后执行后续逻辑
+     * 本方法的的信息不会被输出
+     * 即使exit之后仍旧能被执行
+     */
+    final public function __destruct()
+    {
+        if (function_exists('fastcgi_finish_request')) {
+            fastcgi_finish_request();
+        }
+        $this->after();
+    }
+
+    /**
+     * 前置方法,处理参数
+     */
+    protected function before()
+    {
+
+    }
+
+    /**
+     * 执行验证
+     */
+    protected function checkAuth()
+    {
+
+    }
+
+    /**
+     * 执行逻辑
+     */
+    protected function main()
+    {
+    }
+
+    /**
+     * 后续方法
+     */
+    protected function after()
+    {
+
+    }
+    /**
+     * @return Filter
+     */
+    protected function getRequest()
+    {
+        static $instance = null;
+        parse_str(file_get_contents('php://input'), $data);
+        $data = array_merge($_POST, $_GET, $_COOKIE, $data);
+        if (is_null($instance)) {
+            $instance = new Filter($data);
+        }
+        return $instance;
+    }
+
+    /**
+     * @return Filter
+     */
+    protected function getPut()
+    {
+        static $instance = null;
+        try {
+            $putData = file_get_contents("php://input");
+            $resultData = json_decode($putData, true);
+            if (is_array($resultData)) {
+                //解析IOS提交的PUT数据
+                $data = $resultData;
+            } elseif (!strstr($putData, "\r\n")) {
+                //解析本地测试工具提交的PUT数据
+                parse_str($putData, $putData);
+                $data = $putData;
+            } else {
+                //解析PHP CURL提交的PUT数据
+                $putData = explode("\r\n", $putData);
+                $resultData = [];
+                foreach ($putData as $key => $data) {
+                    if (substr($data, 0, 20) == 'Content-Disposition:') {
+                        $match = explode(";", $data);
+                        if (count($match) == 2) {
+                            preg_match('/.*\"(.*)\"/', $data, $matchName);
+                            $resultData[$matchName[1]] = $putData[$key + 2];
+                        } else {
+                            $info = self::parserInfo($data);
+                            $file = fopen($info['tmp_name'], "r+");
+                            fwrite($file, $putData[$key + 3]);
+                            fclose($file);
+                            $resultData[$info['field']] = $info;
+                        }
+
+                    }
+                }
+                $data = $resultData;
+            }
+
+        } catch (\Exception $e) {
+            $data = [];
+        }
+
+
+        if (is_null($instance)) {
+            $instance = new Filter($data);
+        }
+        return $instance;
+    }
+
+    private static function parserInfo($data, $options = ['saveFile' => true])
+    {
+        //获取参数名称, type
+        $infoPattern = '/name="(.+?)"(; )?(filename="(.+?)")?/'; //todo: 待优化
+        preg_match($infoPattern, $data, $matches);
+
+        $info['field'] = $matches[1];
+        $info['type'] = 'json';
+
+        //如果是文件
+        if (count($matches) > 4) {
+            $info['type'] = 'file';
+            $info['name'] = $matches[4];
+            //如果设置保存文件, 保存到临时文件
+            if (isset($options['saveFile']) && $options['saveFile']) {
+                $tmpFile = tempnam(sys_get_temp_dir(), 'FD');
+
+                $info['tmp_name'] = $tmpFile;
+            }
+        }
+
+        return $info;
+    }
+
+    protected function getJson()
+    {
+        static $instance = null;
+        $data = file_get_contents('php://input');
+        $data = json_decode($data, true) ?: [];
+        if (is_null($instance)) {
+            $instance = new Filter($data);
+        }
+        return $instance;
+    }
+
+    /**
+     * @return Filter
+     */
+    protected function getQuery()
+    {
+        static $instance = null;
+        if (is_null($instance)) {
+            $instance = new Filter($_GET);
+        }
+        return $instance;
+    }
+
+    protected function getFilter()
+    {
+        $str = $this->getQuery()->string("filter");
+        $str = explode(";", $str);
+        if ($str) {
+            foreach ($str as $item) {
+                $item = explode(":", $item);
+                if ($item[1] == "") {
+                    continue;
+                }
+                $_Filter[$item[0]] = $item[1];
+            }
+        }
+        $_Filter['page'] = $_GET['page'] ?: ($_Filter['page'] ?: 1);
+        $_Filter['pageSize'] = $_GET['pageSize'] ?: ($_Filter['pageSize'] ?: 20);
+        static $instance = null;
+        if (is_null($instance)) {
+            $instance = new Filter($_Filter);
+        }
+        return $instance;
+    }
+
+    /**
+     * @return Filter
+     */
+    protected function getPost()
+    {
+        static $instance = null;
+        if (is_null($instance)) {
+            $instance = new Filter($_POST);
+        }
+        return $instance;
+    }
+
+    /**
+     * 获取模板, 固定模板, 与controller名称类似
+     * @return Template
+     */
+    protected function getTemplate()
+    {
+        static $template;
+        if ($template) {
+            return $template;
+        }
+        $class = get_called_class();
+        $class = explode('\\', $class);
+        $path = dirname(__DIR__) . '/' . implode('/', array_slice($class, 1, 2)) . '/Template/';
+        if (!$this->tpl) {
+            $this->tpl = implode('/', array_slice($class, 4));
+        }
+        $template = new Template();
+        $template->setPath($path)->setTpl($this->tpl);
+        $data = get_object_vars($this);
+        if ($data) {
+            $template->assign($data);
+        }
+        return $template;
+    }
+
+    /**
+     * 检查CSRF攻击
+     * @return bool
+     */
+    protected function checkCsrf()
+    {
+        $host = strtolower(trim($_SERVER['HTTP_HOST']));
+        $referer = parse_url($_SERVER['HTTP_REFERER']);
+        $referer = substr(strtolower(trim($referer['host'])), -(strlen($host)));
+        if (!empty($referer) AND $referer != $host AND $referer != ('www.' . $host)) {
+            return false;
+        }
+        return true;
+    }
+
+}

+ 187 - 0
Frame/Dispatcher.php

@@ -0,0 +1,187 @@
+<?php
+
+namespace Heanup\Frame;
+
+
+use Heanup\Config\Route;
+use Heanup\Frame\Logger;
+
+class Dispatcher
+{
+    const CONFIG_PREFIX = '\\Heanup\\Config';
+
+    const CONTROLLER_ERROR = 'Error';
+    const CONTROLLER_DEFAULT = 'Index';
+    const METHOD_DEFAULT = 'main';
+
+    /**
+     * 使用方法
+     * @param $customPrefix
+     * @param string $pathInfo
+     */
+    public static function run($customPrefix, $pathInfo = '')
+    {
+        // 获取前缀
+        $prefix = '\Heanup\\App\\' . self::convertToClassName($customPrefix) . "\\Controller";
+        if (!$pathInfo) {
+            $pathInfo = self::getPathInfo();
+        }
+        $pathInfo = self::convertToClassName($pathInfo);
+        $pathInfo = explode('\\', $pathInfo);
+        if (count($pathInfo) > 1) {
+            $method = array_pop($pathInfo);
+        } else {
+            $method = self::METHOD_DEFAULT;
+        }
+        $controller = $prefix . '\\' . implode('\\', $pathInfo);
+        // 加入一次出错重试的机会
+        for ($i = 0; $i < 2; $i++) {
+            try {
+                // 判断是否是公开方法
+                $reflection = new \ReflectionMethod($controller, $method);
+                if ($reflection->isPublic() == false) {
+                    throw new \Exception($controller . "::$method is not public");
+                }
+                $controller = new $controller();
+                $controller->$method();
+                break;
+            } catch (\Exception $ex) {
+                Logger::emerg($ex, 'dispatcher.run');
+                $controller = $prefix . '\\' . self::CONTROLLER_ERROR;
+                $method = self::METHOD_DEFAULT;
+            }
+        }
+    }
+
+    /**
+     * 分发请求
+     * @param string $customPrefix
+     * @param string $pathInfo
+     */
+    public static function dispatch($customPrefix, $pathInfo = '')
+    {
+        // 获取前缀
+        $prefix = '\Heanup\\App\\' . self::convertToClassName($customPrefix) . "\\Controller";
+        if (!$pathInfo) {
+            $pathInfo = self::getPathInfo();
+        }
+        $pathInfo = self::convertToClassName($pathInfo);
+        try {
+            $path = explode('\\', $pathInfo);
+            $id = array_pop($path);
+            if (is_numeric($id)) {
+                $_GET['id'] = $id;
+                $pathInfo = implode('\\', $path);
+            }
+            $controller = $prefix . '\\' . $pathInfo;
+            // 如果类不存在则使用默认的错误类
+            if (!class_exists($controller)) {
+                //类不存在则尝试通过自定义路由解析
+                $url = $_SERVER["REQUEST_URI"];
+                // 去除 ?后面的部分
+                $request = parse_url($url);
+                $url = $request['path'];
+                $route = Route::getData($customPrefix);
+                foreach ($route as $val) {
+                    $pattern = $val["pattern"];
+                    $params = self::checkUrl(strtolower($url), strtolower($pattern));
+                    if ($params != null) {
+                        $controller = $val["action"];
+                        $count = count($params);
+                        for ($i = 0; $i < $count; $i++) {
+                            $_GET['args_' . $i] = $params[$i];
+                        }
+                        break;// 寻找到第一个匹配的路由即执行,然后返回
+                    }
+                }
+                if (!class_exists($controller)) {
+                    Logger::error($controller, 'dispatcher.controller_not_exist');
+                    throw new \Exception($controller . ' not exist');
+                }
+
+            }
+            // 判断主方法
+            if (!method_exists($controller, 'run')) {
+                throw new \Exception($controller . ' has no method called "run"');
+            }
+            $controller = new $controller();
+            $controller->run();
+        } catch (\Exception $ex) {
+            $method = strtolower($_SERVER['REQUEST_METHOD']);
+            if ($method == 'options') {
+                header("HTTP/1.1 200");
+                header("Access-Control-Allow-Origin: *");
+                header("Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE");
+                header("Access-Control-Allow-Headers: lang,token,content-type");
+                die();
+            }
+            Response::sendResponse(404, [], $ex->getMessage());
+            Logger::emerg($ex, 'dispatcher.dispatch');
+        }
+    }
+
+    /**
+     * 正则匹配检查,并提取出参数
+     * @param $str
+     * @param $pattern
+     * @return array|null
+     */
+    private static function checkUrl($str, $pattern)
+    {
+        $ma = array();
+        $pattern = ltrim(rtrim($pattern, "/"));
+        $pattern = "/" . str_replace("/", "\/", $pattern) . "\/?$/";
+        $pattern = str_replace(":s", "([^\/]+)", $pattern);
+        if (preg_match($pattern, $str, $ma) > 0) {
+            array_shift($ma);
+            return $ma;
+        }
+        return null;
+    }
+
+    /**
+     * 获取pathInfo, 兼容Apache, Nginx
+     * 如果使用Apache的话可以直接使用 $_SERVER['PATH_INFO']的
+     * @return string
+     */
+    protected static function getPathInfo()
+    {
+        //去掉uri中的PHP_SELF 信息
+        $length = strlen($_SERVER['PHP_SELF']);
+        if (substr($_SERVER['REQUEST_URI'], 0, $length) == $_SERVER['PHP_SELF']) {
+            $request = substr($_SERVER['REQUEST_URI'], $length);
+        } else {
+            $request = $_SERVER['REQUEST_URI'];
+        }
+        // 去除 ?后面的部分
+        $request = parse_url($request);
+        $request = $request['path'];
+        $request = urldecode($request);
+        $request = trim($request, '/_ ');
+        // 去掉 '.' 后缀
+        if (strpos($request, '.') !== false) {
+            $request = explode('.', $request);
+            $request = array_shift($request);
+        }
+        if (empty($request)) {
+            $request = self::CONTROLLER_DEFAULT;
+        }
+        return $request;
+    }
+
+    /**
+     * 将字符串转成带命名空间的类名
+     * @param $string
+     * @return string
+     */
+    public static function convertToClassName($string)
+    {
+        $string = trim($string, '\\/_ ');
+        $string = strtolower($string);
+        $string = str_replace('/', '\\', $string);
+        $string = str_replace(' ', '\\', ucwords(str_replace('\\', ' ', $string)));
+        $string = str_replace(' ', '', ucwords(str_replace('_', ' ', $string)));
+        return $string;
+    }
+
+}

+ 20 - 0
Frame/Exception.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace Heanup\Frame;
+
+/**
+ * 主要是在抛出异常的时候记录出错信息
+ * Class Exception
+ * @package Heanup\Frame
+ */
+class Exception extends \Exception
+{
+    protected $filename = 'exception';
+
+    public function __construct($message = '', $code = 0, $previous = null)
+    {
+        $data = array('message' => $message, 'code' => $code, 'previous' => $previous);
+        Logger::info($data, $this->filename);
+        parent::__construct($message, $code, $previous);
+    }
+}

+ 13 - 0
Frame/Exception/Database.php

@@ -0,0 +1,13 @@
+<?php
+
+namespace Heanup\Frame\Exception;
+
+/**
+ * 数据库异常
+ * @package Heanup\Frame\Exception
+ */
+class Database extends \Heanup\Frame\Exception
+{
+    protected $filename = 'exception.database';
+
+}

+ 12 - 0
Frame/Exception/Validate.php

@@ -0,0 +1,12 @@
+<?php
+
+namespace Heanup\Frame\Exception;
+
+/**
+ * 输入信息验证出错
+ */
+class Validate extends \Heanup\Frame\Exception
+{
+    protected $filename = 'exception.validate';
+
+}

+ 251 - 0
Frame/Filter.php

@@ -0,0 +1,251 @@
+<?php
+namespace Heanup\Frame;
+
+use Heanup\Frame\Helper\Validate;
+
+/**
+ * 参数过滤类
+ */
+class Filter
+{
+
+    protected $_data = array();
+
+    public function __construct(Array $data = array())
+    {
+        foreach ($data as $name => $value) {
+            $this->_data[$name] = $value;
+        }
+    }
+
+    /**
+     * 过滤字符串
+     *
+     * @param string $name
+     * @param string $default
+     * @return string
+     */
+    public function string($name, $default = null)
+    {
+        $result = null;
+        if (isset($this->_data[$name])) {
+            $result = $this->_data[$name];
+            $result = trim($result);
+            $result = (string)$result;
+        } elseif (!is_null($default)) {
+            $result = $default;
+        }
+        return $result;
+    }
+
+    /**
+     * 批量获取字符串
+     *
+     * @param string $name
+     * @param string $default
+     * @return string
+     */
+    public function getBatchString($arr, $isString = TRUE)
+    {
+        $result = null;
+        if (is_array($arr)) {
+            foreach ($arr as $v) {
+                if (isset($this->_data[$v])) {
+                    $result[$v] = $isString ? (string)trim($this->_data[$v]) : $this->_data[$v];
+                }
+            }
+        }
+        return $result;
+    }
+
+    /**
+     * 获取手机区号
+     *
+     * @param string $name
+     * @param string $default
+     * @return string
+     */
+    public function mobileCode($name = 'mobile_code', $default = '+86')
+    {
+        $mobileCode = $this->string($name, $default);
+        $mobileCode = '+' . ltrim($mobileCode, '+');
+        return $mobileCode;
+    }
+
+    /**
+     * 过滤整型
+     *
+     * @param string $name
+     * @param int $default
+     * @return int
+     */
+    public function intval($name, $default = null)
+    {
+        $result = null;
+        if (isset($this->_data[$name])) {
+            $result = $this->_data[$name];
+            $result = trim($result);
+            $result = intval($result);
+        } elseif (!is_null($default)) {
+            $result = $default;
+        }
+        return $result;
+    }
+
+    /**
+     * 过滤浮点类型
+     *
+     * @param string $name
+     * @param float $default
+     * @return float
+     */
+    public function float($name, $default = null)
+    {
+        $result = null;
+        if (isset($this->_data[$name])) {
+            $result = $this->_data[$name];
+            $result = trim($result);
+            $result = floatval($result);
+        } elseif (!is_null($default)) {
+            $result = $default;
+        }
+        return $result;
+    }
+
+    /**
+     * 验证手机号
+     *
+     * @param string $message
+     * @param int $code
+     * @param string $name
+     * @return int|string
+     * @throws \Exception
+     */
+    public function mobile($message = '手机号格式错误', $code = 45050, $name = 'mobile')
+    {
+        $result = '';
+        if (isset($this->_data[$name])) {
+            $result = $this->_data[$name];
+            $result = trim($result);
+            $result = intval($result);
+        }
+        if (!$result || Validate::isMobile($result) == false) {
+            throw new \Heanup\Frame\Exception\Validate($message, $code);
+        }
+        return $result;
+    }
+
+    /**
+     * 获取json数据格式
+     *
+     * @param
+     *            $name
+     * @param
+     *            $default
+     * @return array
+     */
+    public function json($name, $default = null)
+    {
+        $result = null;
+        if (isset($this->_data[$name])) {
+            $result = $this->_data[$name];
+            $result = trim($result);
+            $decoded = json_decode($result,true);
+            if ($decoded) {
+                $result = $decoded;
+            } else {
+                $result = array();
+            }
+        } elseif (!is_null($default)) {
+            $result = $default;
+        }
+        return $result;
+    }
+
+    /**
+     * 检查字段是否存在,可选检查是否为空
+     *
+     * @param string $name
+     * @param bool $checkEmpty
+     * @param string $message
+     * @param int $code
+     * @throws \Exception
+     * @return bool
+     */
+    public function exists($name, $checkEmpty = false, $message = '', $code = 0)
+    {
+        if ($checkEmpty) {
+            $result = isset($this->_data[$name]) && (is_string($this->_data[$name]) && strlen($this->_data[$name]) > 0);
+        } else {
+            $result = isset($this->_data[$name]);
+        }
+        if (!$result && $message) {
+            throw new \Heanup\Frame\Exception\Validate($message, $code);
+        }
+        return $result;
+    }
+
+    /**
+     * 获取数组数据
+     *
+     * @param
+     *            $name
+     * @param array $default
+     * @return array|mixed
+     */
+    public function getArray($name, $default = array())
+    {
+        $result = array();
+        if (isset($this->_data[$name])) {
+            $result = $this->_data[$name];
+        } elseif (!is_null($default)) {
+            $result = $default;
+        }
+        return $result;
+    }
+
+    /**
+     * 参数验证
+     * @param $data -参数
+     * @param bool $required -是否必须
+     * @return array
+     * @throws Exception
+     */
+    public function params($data, $required = false)
+    {
+
+        $res = array();
+        foreach ($data as $name => $rule) {
+            if ($required) {
+                $this->required($name);
+            }
+            $res[$name] = $this->$rule($name);
+        }
+        return $res;
+    }
+
+    public function required($name)
+    {
+        if($this->_data[$name] || strlen($this->_data[$name]) > 0){
+            return $this->_data[$name];
+        }else{
+            throw new Exception('缺少必填参数:' . $name,400);
+        }
+    }
+
+    public function compare($name, $value, $echo = 'selected')
+    {
+        if (isset($this->_data[$name]) && $this->_data[$name] !== '' && $this->_data[$name] == $value) {
+            echo $echo;
+            return;
+        } else {
+            echo '';
+            return;
+        }
+    }
+
+    public function toArray()
+    {
+        return $this->_data;
+    }
+}

+ 341 - 0
Frame/Helper.php

@@ -0,0 +1,341 @@
+<?php
+
+namespace Heanup\Frame;
+
+
+class Helper
+{
+
+    /*
+    * 检测链接是否是SSL连接
+    * @return bool
+    */
+    public static function isSSL()
+    {
+        if (!isset($_SERVER['HTTPS']))
+            return false;
+        if ($_SERVER['HTTPS'] === 1) {  //Apache
+            return true;
+        } elseif ($_SERVER['HTTPS'] === 'on') { //IIS
+            return true;
+        } elseif ($_SERVER['SERVER_PORT'] == 443) { //其他
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * 随机数生成器
+     */
+    public static function random()
+    {
+        return md5(microtime() . rand() . uniqid() . $_SERVER['REMOTE_ADDR']);
+    }
+
+    /**
+     * 简单验证email
+     * @param $email
+     * @return bool
+     */
+    public static function validateEmail($email)
+    {
+        $email = trim($email);
+        if (!preg_match("/([\w\-]+\@[\w\-]+\.[\w\-]+)/", $email)) {
+            return False;
+        }
+        return True;
+    }
+
+    /**
+     * 简单验证是否有效的URL
+     * @param $url
+     * @return bool
+     */
+    public static function validateURL($url)
+    {
+        $url = trim($url);
+        if (!preg_match("/\b(?:(?:https?|ftp):\/\/|www\.)[-a-z0-9+&@#\/%?=~_|!:,.;]*[-a-z0-9+&@#\/%=~_|]/i", $url)) {
+            return False;
+        }
+        return True;
+    }
+
+
+    /**
+     * 转换为友好时间
+     * @param int|string $timestamp
+     * @return string
+     */
+    public static function convertToFriendlyTime($timestamp)
+    {
+        if (!is_numeric($timestamp)) {
+            $timestamp = strtotime($timestamp);
+        }
+        $now = time();
+        $interval = abs($now - $timestamp);
+        $today = strtotime(date("Ymd"));
+        $tomorrow = strtotime(date("Ymd", $now + 86400));
+        $thisYear = strtotime(date("Y-01-01 00:00"));
+        if ($interval < 60) {
+            if ($interval < 10) {
+                $interval = 10;
+            }
+            $string = $interval . "秒前";
+        } else if ($interval < 3600) {
+            $string = floor($interval / 60) . '分钟前';
+        } else if ($today < $timestamp AND $timestamp < $tomorrow) {
+            $string = '今天' . date('H:i', $timestamp);
+        } else if ($thisYear < $timestamp) {
+            $string = date('n月j日', $timestamp);
+        } else {
+            $string = date('Y-m-d', $timestamp);
+        }
+        return $string;
+    }
+
+
+    /**
+     * 计算字符串长度,汉字按两个字符
+     * @param string $str
+     * @return int
+     */
+    public static function strlenUtf8($str)
+    {
+        $length = strlen(preg_replace('/[u4e00-u9fa5 ]/', '', $str));
+
+        if ($length) {
+            if($length < 3){
+                return $length;
+            }
+            return strlen($str) - $length + intval($length / 3) * 2;
+        } else {
+            return strlen($str);
+        }
+    }
+
+    /**
+     * 获取服务器IP
+     * @param bool $toLong 是否返回整形
+     * @return string
+     */
+    public static function getServerIp($toLong = false)
+    {
+        if (isset($_SERVER['SERVER_ADDR'])) {
+            $ip = $_SERVER['SERVER_ADDR'];
+        } else {
+            $domain = gethostname();
+            $ip = gethostbyname($domain);
+        }
+        if ($toLong) {
+            $ip = ip2long($ip);
+        }
+        return $ip;
+    }
+
+    /**
+     * 获取客户端Ip
+     * @param bool $toLong 是否返回整形
+     * @return string|int
+     */
+    public static function getClientIp($toLong = false)
+    {
+        $ip = '';
+        if (isset($_SERVER["HTTP_X_FORWARDED_FOR"]) && $_SERVER["HTTP_X_FORWARDED_FOR"]) {
+            $ip = $_SERVER["HTTP_X_FORWARDED_FOR"];
+        } elseif (isset($_SERVER["HTTP_CLIENT_IP"]) && $_SERVER["HTTP_CLIENT_IP"]) {
+            $ip = $_SERVER["HTTP_CLIENT_IP"];
+        } elseif (isset($_SERVER["REMOTE_ADDR"]) && $_SERVER["REMOTE_ADDR"]) {
+            $ip = $_SERVER["REMOTE_ADDR"];
+        }
+        $pos = strpos($ip, ',');
+        if ($pos) {
+            $ip = substr($ip, 0, $pos);
+        }
+        $ip = trim($ip);
+        if ($toLong) {
+            $ip = ip2long($ip);
+        }
+        return $ip;
+    }
+
+    /**
+     * 从 $argv 中解析合并参数, 兼容一下情况:
+     *      无前缀, 被解析成索引数组
+     *      单前缀(只能是单字母, 后面可以直接跟value, 也可以空格后面跟着value,二者不能混用), 被解析成关联数组
+     *      双前缀形式(可以是单字母也可以是多字母,后面必需跟着 '='), 被解析成关联数组
+     * 如果不确定, 请自行打印结果查看是否正确
+     * @example : php index.php first -s 'second' --third='third'
+     * @return array('first', 's' => 'second', 'third' => 'third')
+     */
+    public static function getArguments()
+    {
+        $arguments = $_SERVER['argv'];
+        for ($i = 0; $i < $_SERVER['argc']; $i++) {
+            if (!isset($arguments[$i][0])) {
+                continue;
+            }
+
+            //判断第一位是否是 '-'
+            if (substr($arguments[$i], 0, 1) != '-') {
+                continue;
+            }
+
+            //判断第二位是否是 '-'
+            if (substr($arguments[$i], 1, 1) != '-') {
+                if (strlen($arguments[$i]) == 2) {
+                    $key = substr($arguments[$i], 1);
+                    $value = $arguments[$i + 1];
+                    $arguments[$key] = trim($value);
+                    unset($arguments[$i]);
+                    unset($arguments[$i + 1]);
+                } elseif (strlen($arguments[$i]) > 2) {
+                    $key = substr($arguments[$i], 1, 1);
+                    $value = substr($arguments[$i], 2);
+                    $arguments[$key] = trim($value);
+                    unset($arguments[$i]);
+                }
+            } else {
+                $value = substr($arguments[$i], 2);
+                $value = explode('=', $value, 2);
+                $arguments[$value[0]] = trim($value[1]);
+                unset($arguments[$i]);
+            }
+        }
+        return $arguments;
+    }
+
+    /**
+     * 尝试解析字符串,如果解析失败则返回原值
+     * @param $string
+     * @return string | array
+     */
+    public static function jsonDecode($string)
+    {
+        if (!$string) {
+            return $string;
+        }
+        $result = json_decode($string, true);
+        if ($result) {
+            $string = $result;
+        }
+        return $string;
+    }
+
+    /**
+     * 获取数组分片, 从指定列表中获取指定元素之后的若干个切片
+     * @param $idList
+     * @param $lastId
+     * @param $count
+     * @return array
+     */
+    public static function getSlice($idList, $lastId = 0, $count = 20)
+    {
+        $result = array();
+        if (!$idList || ($lastId && !in_array($lastId, $idList))) {
+            return $result;
+        }
+        if ($lastId) {
+            $position = array_search($lastId, $idList) + 1;
+        } else {
+            $position = 0;
+        }
+        $result = array_slice($idList, $position, $count);
+        return $result;
+    }
+
+    /**
+     * 创建目录
+     * @param string $dir
+     * @param integer $mode
+     */
+    public static function mkdir($dir, $mode = 0755)
+    {
+        if (!is_dir($dir)) {
+            $temp = explode('/', $dir);
+            $cur_dir = '';
+            for ($i = 0; $i < count($temp); $i++) {
+                $cur_dir .= $temp[$i] . '/';
+                if (!is_dir($cur_dir)) {
+                    @mkdir($cur_dir, 0777);
+                    @chmod($cur_dir, $mode);
+                }
+            }
+        }
+    }
+
+    public static function copydir($strSrcDir, $strDstDir)
+    {
+        $dir = opendir($strSrcDir);
+        if (!$dir) {
+            return false;
+        }
+        if (!is_dir($strDstDir)) {
+            if (!mkdir($strDstDir)) {
+                return false;
+            }
+        }
+        while (false !== ($file = readdir($dir))) {
+            if (($file != '.') && ($file != '..')) {
+                if (is_dir($strSrcDir . '/' . $file)) {
+                    if (!self::copydir($strSrcDir . '/' . $file, $strDstDir . '/' . $file)) {
+                        return false;
+                    }
+                } else {
+                    if (!copy($strSrcDir . '/' . $file, $strDstDir . '/' . $file)) {
+                        return false;
+                    }
+                }
+            }
+        }
+        closedir($dir);
+        return true;
+    }
+
+    /**
+     * 获取强密码
+     * @param int $length
+     * @return string
+     */
+    public static function getStrongCode($length = 8)
+    {
+        $length = $length > 8 ? intval($length) : 8;
+        $arr = array();
+//        $arr[] = self::getRandoms($length - 4);
+        $arr[] = strtolower(self::getRandoms($length, 'char'));
+//        $arr[] = strtolower(self::getRandoms(1, 'char'));
+        $arr[] = mt_rand(0, 9);
+//        $special = '!@#$%^*()';
+//        $arr[] = $special{mt_rand(0, 11)};
+        shuffle($arr);
+        return implode('', $arr);
+    }
+
+    /**
+     * 获取随机数
+     *
+     * @param int $length
+     * @param string $type (可选)
+     * @return string
+     */
+    public static function getRandoms($length = 4, $type = '')
+    {
+        $returnStr = '';
+        $pattern = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLOMNOPQRSTUVWXYZ';
+        if ($type == 'char') {
+            $start = 10;
+            $end = 61;
+        } elseif ($type == 'num') {
+            $start = 0;
+            $end = 9;
+        } else {
+            $start = 0;
+            $end = 61;
+        }
+        for ($i = 0; $i < $length; $i++) {
+            $returnStr .= $pattern{mt_rand($start, $end)}; // 生成php随机数
+        }
+        return $returnStr;
+    }
+
+
+}

+ 67 - 0
Frame/Helper/Date.php

@@ -0,0 +1,67 @@
+<?php
+
+namespace Heanup\Frame\Helper;
+
+/**
+ * 文件操作
+ */
+class Date extends \Heanup\Frame\Helper
+{
+
+    /**
+     * 转换为友好时间
+     * @param int|string $timestamp
+     * @return string
+     */
+    public static function convert($timestamp)
+    {
+        if (!is_numeric($timestamp)) {
+            $timestamp = strtotime($timestamp);
+        }
+        $now = time();
+        $interval = abs($now - $timestamp);
+        $today = strtotime(date("Ymd"));
+        $tomorrow = strtotime(date("Ymd", $now + 86400));
+        $thisYear = strtotime(date("Y-01-01 00:00"));
+        if ($interval < 60) {
+            $string = "1分钟前";
+        } else if ($interval < 3600) {
+            $string = floor($interval / 60) . '分钟前';
+        } else if ($today < $timestamp AND $timestamp < $tomorrow) {
+            $string = '今天' . date('H:i', $timestamp);
+        } else if ($thisYear < $timestamp) {
+            $string = date('m-d H:i', $timestamp);
+        } else {
+            $string = date('y-m-d H:i', $timestamp);
+        }
+        return $string;
+    }
+
+    /**
+     * 获取相对时间
+     * @param $timestamp
+     * @param $customString ;
+     * @return string
+     */
+    public static function related($timestamp, $customString = '')
+    {
+        if (!is_numeric($timestamp)) {
+            $timestamp = strtotime($timestamp);
+        }
+        $interval = abs(time() - $timestamp);
+        if ($interval == 0) {
+            $interval = 1;
+        }
+        if ($interval < 3600) {
+            $string = ceil($interval / 60) . '分钟前';
+        } elseif ($interval < 86400) {
+            $string = ceil($interval / 3600) . '小时前';
+        } else {
+            $string = ceil($interval / 86400) . '天前';
+        }
+        $string = $string . $customString;
+        return $string;
+    }
+
+
+}

+ 39 - 0
Frame/Helper/File.php

@@ -0,0 +1,39 @@
+<?php
+
+namespace Heanup\Frame\Helper;
+
+/**
+ * 文件操作
+ */
+class File extends \Heanup\Frame\Helper
+{
+
+    /**
+     * 写入ini文件
+     * @param string $path
+     * @param string|array $data
+     * @return int|bool
+     */
+    public static function writeIniFile($path, $data)
+    {
+        $content = null;
+        foreach ($data as $key => $item) {
+            if (is_array($item)) {
+                $content .= "\n[{$key}]\n";
+                foreach ($item as $k => $v) {
+                    if (is_numeric($v) || is_bool($v)) {
+                        $v = '"' . $v . '"';
+                    }
+                    $content .= "{$k} = {$v}\n";
+                }
+            } else {
+                if (is_numeric($item) || is_bool($item)) {
+                    $item = '"' . $item . '"';
+                }
+                $content .= "{$key} = {$item}\n";
+            }
+        }
+        return file_put_contents($path, $content);
+    }
+
+}

+ 92 - 0
Frame/Helper/Format.php

@@ -0,0 +1,92 @@
+<?php
+
+namespace Heanup\Frame\Helper;
+
+/**
+ * 格式转换
+ */
+class Format extends \Heanup\Frame\Helper
+{
+    /**
+     * @hack 转换数字类型到字符串
+     * @param $data
+     * @return mixed
+     */
+    public static function convertNumberToString($data)
+    {
+        if (is_array($data)) {
+            foreach ($data as $k => $v) {
+                if (is_array($v)) {
+                    $data[$k] = self::convertNumberToString($v);
+                } else if (is_numeric($v)) {
+                    $data[$k] = (string)$v;
+                }
+            }
+        } else if (is_numeric($data)) {
+            $data = (string)$data;
+        }
+        return $data;
+    }
+
+    /**
+     * Json数据格式化,拷贝自网上
+     * @param  Mixed $data 数据
+     * @param  String $indent 缩进字符,默认4个空格
+     * @return JSON
+     */
+    public static function jsonFormat($data, $indent = null)
+    {
+        // 对数组中每个元素递归进行url_encode操作,保护中文字符
+        array_walk_recursive($data, 'self::jsonFormatProtect');
+        // json encode
+        $data = json_encode($data);
+        // 将url_encode的内容进行url_decode
+        $data = urldecode($data);
+        // 缩进处理
+        $ret = '';
+        $pos = 0;
+        $length = strlen($data);
+        $indent = isset($indent) ? $indent : '    ';
+        $newline = "\n";
+        $prevChar = '';
+        $outOfQuotes = true;
+
+        for ($i = 0; $i <= $length; $i++) {
+            $char = substr($data, $i, 1);
+            if ($char == '"' && $prevChar != '\\') {
+                $outOfQuotes = !$outOfQuotes;
+            } elseif (($char == '}' || $char == ']') && $outOfQuotes) {
+                $ret .= $newline;
+                $pos--;
+                for ($j = 0; $j < $pos; $j++) {
+                    $ret .= $indent;
+                }
+            }
+            $ret .= $char;
+            if (($char == ',' || $char == '{' || $char == '[') && $outOfQuotes) {
+                $ret .= $newline;
+                if ($char == '{' || $char == '[') {
+                    $pos++;
+                }
+                for ($j = 0; $j < $pos; $j++) {
+                    $ret .= $indent;
+                }
+            }
+            $prevChar = $char;
+        }
+        return $ret;
+    }
+
+    /**
+     * 将数组元素进行
+     * @param String $val
+     */
+    protected static function jsonFormatProtect(&$val)
+    {
+        if ($val !== true && $val !== false && $val !== null) {
+            $val = urldecode($val);
+        }
+    }
+
+}
+

+ 269 - 0
Frame/Helper/Html.php

@@ -0,0 +1,269 @@
+<?php
+
+namespace Heanup\Frame\Helper;
+
+/**
+ * 格式转换
+ */
+class Html extends \Heanup\Frame\Helper
+{
+
+    /**
+     * @param null $msg
+     * @param null $url
+     * @param array $get
+     * @param int $wait
+     */
+    public static function location($msg = null, $url = null,$isParent=FALSE, $get = Array(), $wait = 1)
+    {
+        $url = self::getSiteUrl($url, $get);
+        $target=$isParent?".parent":"";
+        $str = "<script type='text/javascript'>function flash(){window".$target.".location.href='$url';}setTimeout('flash()','" . intval($wait) * 500 . "');</script>";
+        if (!is_string($msg)) {
+            $msg = $msg ? "操作成功!" : "操作失败!";
+        }
+        echo $str . $msg . "&nbsp;&nbsp;正在<a href='$url'>跳转中</a>.....";
+        die();
+    }
+
+    /**
+     * 取得当前页地址
+     * @return mixed
+     */
+    public static function self()
+    {
+        return $_SERVER['REQUEST_URI'];
+    }
+
+    /**
+     * 调整url自动生成规则
+     * @param string $action
+     * @param array $get
+     * @param bool $mergeGet
+     * @return string
+     */
+    public static function getSiteUrl($action = '', Array $get = array(), $mergeGet = false)
+    {
+        $url = '/' . trim($action, '/');
+        if ($mergeGet) {
+            $get = array_merge($_GET, $get);
+        }
+        if ($get) {
+            $get = urldecode(http_build_query($get));
+        }
+        if ($get) {
+            $url .= '?' . $get;
+        }
+        return $url;
+    }
+
+    /**
+     * 链接生成器
+     * @param $url
+     * @param array $get
+     * @param null $text
+     * @param null $attributes
+     * @return string
+     */
+    public static function anchor($url, Array $get = array(), $text = null, $attributes = null)
+    {
+        $url = '/' . trim($url, "/") . '/?' . http_build_query($get);
+        $text = strlen($text) <= 0 ? $url : $text;
+        $string = "<a href='$url' $attributes>$text</a>";
+        return $string;
+    }
+
+    /**
+     * 简易分页函数
+     * @param string $url 跳转地址
+     * @param int $totalPage 总页数
+     * @param int $current 当前页
+     * @param int $records 总记录数
+     * @param int $displayCount 总共显示的页数
+     * @return string
+     */
+    public static function pagination($url, $totalPage = 10, $current = 1, $records = 100, $displayCount = 10)
+    {
+        $current = ($current < $totalPage) ? $current : $totalPage;
+        $current = ($current >= 1) ? $current : 1;
+        $first = 1;
+        $half = ceil($displayCount / 2);
+        $end = $totalPage;
+
+        $prev = $current - 1;
+        $next = $current + 1;
+        foreach ($_GET as $key => $value) {
+            if($value === ''){
+                unset($_GET[$key]);
+            }
+        }
+        $linkSum = "<li><span class='sum'>总数:$records</span></li>";
+        $linkFirst = '<li>' . self::anchor($url, array_merge($_GET, array('page' => $first)), '&laquo;') . '</li>';
+        $linkPrev = '<li>' . self::anchor($url, array_merge($_GET, array('page' => $prev)), '&#8249;') . '</li>';
+        $linkNext = '<li>' . self::anchor($url, array_merge($_GET, array('page' => $next)), '&#8250;') . '</li>';
+        $linkEnd = '<li>' . self::anchor($url, array_merge($_GET, array('page' => $end)), '&raquo;') . '</li>';
+
+
+        if ($current <= $half) {
+            $linkFirst = '';
+            $last = $displayCount;
+            if ($last > $end) {
+                $last = $end;
+            }
+            if ($current == 1) {
+                $linkPrev = '';
+            }
+            if ($current == $end) {
+                $linkNext = '';
+            }
+        } elseif ($current > ($end - $half) AND $current <= $end) {
+            $first = $current - $half + 1;
+            $last = $end;
+            $linkEnd = '';
+            if ($current == $end) {
+                $linkNext = '';
+            }
+        } else {
+            $first = $current - $half + 1;
+            $last = $current + $half;
+        }
+
+        $string = null;
+        for ($i = $first; $i <= $last; $i++) {
+            if ($i == $current) {
+                $string .= '<li class="active">' . self::anchor($url, array_merge($_GET, array('page' => $i)), "$i", "class='active'") . '</li>';
+            } else {
+                $string .= '<li>' . self::anchor($url, array_merge($_GET, array('page' => $i)), "$i") . '</li>';
+            }
+        }
+
+        return $linkSum . $linkFirst . $linkPrev . $string . $linkNext . $linkEnd;
+    }
+
+    /**
+     * 下拉菜单生成器
+     * @param $list
+     * @param $key
+     * @param $text
+     * @param null $attributes
+     * @return null|string
+     */
+    public static function loopOutputSelect($list, $key = '', $text = '', $attributes = null)
+    {
+        if (!is_array($list) OR empty($list)) {
+            return null;
+        }
+        $string = null;
+        foreach ($list as $k => $v) {
+            $selected = null;
+            if ($k == $key) {
+                $selected = "selected='selected'";
+            }
+            $string .= "<option value='$k' $selected >$v</option>\n";
+        }
+        if (!empty($text)) {
+            $string = "<select name='$text' id='$text' $attributes>" . $string . "</select>";
+        }
+        return $string;
+    }
+
+    /**
+     * 单选按钮生成器
+     * @param $list
+     * @param $key
+     * @param $text
+     * @param null $attributes
+     * @return null|string
+     */
+    public static function loopOutputRadio($list, $key = '', $text = '', $attributes = null)
+    {
+        if (!is_array($list) OR empty($list)) {
+            return null;
+        }
+        $i = 0;
+        $string = null;
+        foreach ($list as $k => $v) {
+            $checked = null;
+            if ($k == $key) {
+                $checked = "checked='checked'";
+            }
+            $string .= "<input type='radio' name='$text' value='$k' id='$text-$i' $checked $attributes /><label for='$text-$i'>$v</label>\n";
+            $i++;
+        }
+        return $string;
+    }
+
+    /**
+     * 多选框生成器
+     * @param $list
+     * @param string $key
+     * @param string $text
+     * @param string $attributes
+     * @param string $separator
+     * @return bool|string
+     */
+    public static function loopOutputCheckbox($list, $key = "", $text = '', $attributes = "", $separator = "\n")
+    {
+        if (!is_array($list) || empty($list)) {
+            return false;
+        }
+        $string = '';
+
+        if (!is_array($key)) {
+            $key = array($key);
+        }
+        foreach ($list as $k => $v) {
+            $string .= "<input type='checkbox' name='$text" . "[]' value='$k' ";
+            $string .= in_array($k, $key) ? " checked ='checked'" : "";
+            $string .= $attributes;
+            $string .= " />$v" . $separator;
+        }
+        return $string;
+    }
+    
+    /**
+     * 获取强密码
+     * @param int $length
+     * @return string
+     */
+    public static function getStrongCode($length = 8)
+    {
+        $length = $length > 8 ? intval($length) : 8;
+        $arr = array();
+        $arr[] = self::getRandoms($length - 4);
+        $arr[] = strtoupper(self::getRandoms(1, 'char'));
+        $arr[] = strtolower(self::getRandoms(1, 'char'));
+        $arr[] = mt_rand(0, 9);
+        $special = '!@#$%^*()';
+        $arr[] = $special{mt_rand(0, 11)};
+        shuffle($arr);
+        return implode('', $arr);
+    }
+    /**
+     * 获取随机数
+     *
+     * @param int $length
+     * @param string $type (可选)
+     * @return string
+     */
+    public static function getRandoms($length = 4, $type = '')
+    {
+        $returnStr = '';
+        $pattern = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLOMNOPQRSTUVWXYZ';
+        if ($type == 'char') {
+            $start = 10;
+            $end = 61;
+        } elseif ($type == 'num') {
+            $start = 0;
+            $end = 9;
+        } else {
+            $start = 0;
+            $end = 61;
+        }
+        for ($i = 0; $i < $length; $i++) {
+            $returnStr .= $pattern{mt_rand($start, $end)}; // 生成php随机数
+        }
+        return $returnStr;
+    }
+
+}

+ 31 - 0
Frame/Helper/Location.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace Heanup\Frame\Helper;
+
+/**
+ * 地理位置
+ */
+class Location extends \Heanup\Frame\Helper
+{
+
+    /**
+     * 求两个已知经纬度之间的距离,单位为米
+     * @url http://www.phpernote.com/php-function/890.html
+     * @param lng1 ,lng2 经度
+     * @param lat1 ,lat2 纬度
+     * @return float 距离,单位米
+     **/
+    public static function getDistance($lng1, $lat1, $lng2, $lat2)
+    {
+        //将角度转为弧度, deg2rad()函数将角度转换为弧度
+        $radLat1 = deg2rad($lat1);
+        $radLat2 = deg2rad($lat2);
+        $radLng1 = deg2rad($lng1);
+        $radLng2 = deg2rad($lng2);
+        $a = $radLat1 - $radLat2;
+        $b = $radLng1 - $radLng2;
+        $s = 2 * asin(sqrt(pow(sin($a / 2), 2) + cos($radLat1) * cos($radLat2) * pow(sin($b / 2), 2))) * 6378.137 * 1000;
+        return $s;
+    }
+
+}

+ 11 - 0
Frame/Helper/Network.php

@@ -0,0 +1,11 @@
+<?php
+
+namespace Heanup\Frame\Helper;
+
+/**
+ * 网络操作
+ */
+class Network extends \Heanup\Frame\Helper
+{
+
+}

+ 54 - 0
Frame/Helper/Validate.php

@@ -0,0 +1,54 @@
+<?php
+
+namespace Heanup\Frame\Helper;
+
+/**
+ * 校验类, 尚未严格验证
+ * @package Heanup\Frame\Helper
+ */
+class Validate extends \Heanup\Frame\Helper
+{
+
+    public static function isUrl($url)
+    {
+        $regex = '/((https?|ftp|news):\/\/)?([a-z]*([a-z0-9\-]*[\.。])+([a-z]{2}|aero|arpa|biz|com|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel)|(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))(\/[a-z0-9_\-\.~]+)*(\/([a-z0-9_\-\.]*)(\?[a-z0-9+_\-\.%=&]*)?)?(#[a-z][a-z0-9_]*)?/i';
+        return preg_match($regex, $url);
+    }
+
+    public static function isEmail($email)
+    {
+        return preg_match("/^(\w+((-\w+)|(\w.\w+))*)\@(\w+((\.|-)\w+)*\.\w+$)/", $email);
+    }
+
+    public static function isMobile($phone)
+    {
+        return preg_match("/^1\d{10}$/", $phone);
+    }
+
+    public static function isCountryMobile($phone)
+    {
+        return preg_match("/^[1-9]{1}\d{3,14}$/", $phone);
+    }
+
+
+    public static function isDate($str)
+    {
+        if (!preg_match("/^\d{4}-\d{2}-\d{2}$/", $str)) {
+            return FALSE;
+        }
+        $stamp = strtotime($str);
+        if (!is_numeric($stamp)) {
+            return FALSE;
+        }
+        if (checkdate(date('m', $stamp), date('d', $stamp), date('Y', $stamp))) {
+            return TRUE;
+        }
+        return FALSE;
+    }
+
+    public static function isIntval($mixed)
+    {
+        return (preg_match('/^\d*$/', $mixed) == 1);
+    }
+
+}

+ 77 - 0
Frame/Init.php

@@ -0,0 +1,77 @@
+<?php
+
+namespace Heanup\Frame;
+
+// 引入全局常量
+
+use Heanup\Library\Sentry;
+
+require_once dirname(__DIR__) . '/constants.php';
+// 引入全局函数
+require_once dirname(__DIR__) . '/functions.php';
+// 引入自动加载
+require_once dirname(__DIR__) . '/Autoload.php';
+require_once dirname(__DIR__) . '/vendor/autoload.php';
+
+class Init
+{
+    /**
+     * 环境变量设置
+     * @param $configFile
+     * @return bool
+     */
+    public static function setEnvironment($configFile = '')
+    {
+        // 设置错误显示
+        error_reporting(E_ERROR | E_WARNING | E_PARSE);
+        ini_set('display_errors', false);
+        // 无需浏览器缓存
+        header("Cache-Control: no-cache, must-revalidate");
+        header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");
+        // 设置时区和编码
+        date_default_timezone_set('Asia/Shanghai');
+        setlocale(LC_ALL, 'en_US.utf-8');
+        header('Content-type:text/html;charset=utf-8');
+        // 设置错误捕获机制
+//        set_error_handler('\Heanup\Frame\Init::errorHandler');
+//        set_exception_handler('\Heanup\Frame\Init::exceptionHandler');
+//        Sentry::instance()->setException();
+
+        // 如果没有附加配置文件的话则可以结束配置了,反之,则要重新设置一些变量
+        if (!$configFile || !file_exists($configFile)) {
+            return true;
+        }
+        // 解析配置文件
+        $config = parse_ini_file($configFile);
+        if (!$config) {
+            return true;
+        }
+        // 调整错误显示
+        if (isset($config['display_errors'])) {
+            ini_set('display_errors', intval($config['display_errors']));
+        }
+        // 设置日志路径
+        if (isset($config['log_path'])) {
+            Logger::setLogPath($config['log_path']);
+        }
+        // 设置日志等级
+        if (isset($config['log_level'])) {
+            Logger::setLogLevel(intval($config['log_level']));
+        }
+        // 设置环境变量
+        if (isset($config['mode'])) {
+            Request::setMode($config['mode']);
+        }
+        return true;
+    }
+
+    /**
+     * 异常捕获
+     * @param $ex
+     */
+    public static function exceptionHandler($ex)
+    {
+//        Sentry::instance()->exceptionHandler($ex);
+//        Logger::error($ex, 'uncaught_exception');
+    }
+}

+ 58 - 0
Frame/Library.php

@@ -0,0 +1,58 @@
+<?php
+
+namespace Heanup\Frame;
+
+/**
+ * Library的父类, 基础类库
+ */
+class Library
+{
+
+    protected $configClass = null;
+    protected $configSection = null;
+
+    /**
+     * 构造函数
+     * @param null $configClass
+     * @param null $configSection
+     */
+    public function __construct($configClass = null, $configSection = null)
+    {
+        if ($configClass) {
+            // 判断是否是全路径
+            if(substr($configClass, 0, strlen(Dispatcher::CONFIG_PREFIX)) != Dispatcher::CONFIG_PREFIX){
+                $this->configClass = Dispatcher::CONFIG_PREFIX . '\\' . trim($configClass, '\\');
+            }else{
+                $this->configClass = $configClass;
+            }
+        }
+        // 设置配置文件内容片段
+        if (!is_null($configSection)) {
+            $this->configSection = $configSection;
+        }
+    }
+
+    /**
+     * 根据索引取出详细配置信息
+     */
+    protected function getConfig()
+    {
+        $class = $this->configClass;
+        if (class_exists($class) && method_exists($class, 'getData')) {
+            return $class::getData($this->configSection);
+        }
+        return array();
+    }
+
+    /**
+     * 获取一组标识
+     * @param $flag
+     * @return string
+     */
+    protected function getFlag($flag = false)
+    {
+        $flag = $this->configClass . ':' . $this->configSection . ':' . intval($flag);
+        return $flag;
+    }
+
+}

+ 83 - 0
Frame/Library/AntiSpam.php

@@ -0,0 +1,83 @@
+<?php
+
+namespace Heanup\Frame\Library;
+
+/**
+ * IP频率限制,一定程度上反垃圾
+ */
+class AntiSpam
+{
+    const ANTI_SPAM_MINUTE = 'anti_spam:minute';
+    const ANTI_SPAM_HOUR = 'anti_spam:hour';
+    const ANTI_SPAM_DAY = 'anti_spam:year';
+    const ANTI_SPAM_LOCKED = 'anti_spam:locked';
+    // 频率设置
+    protected static $limits = array(
+        self::ANTI_SPAM_MINUTE => 500,
+        self::ANTI_SPAM_HOUR => 2000,
+        self::ANTI_SPAM_DAY => 10000,
+    );
+    // 正常过期
+    protected static $expire = array(
+        self::ANTI_SPAM_MINUTE => 60,
+        self::ANTI_SPAM_HOUR => 3600,
+        self::ANTI_SPAM_DAY => 86400,
+    );
+    // 封禁时间
+    protected static $forbiddenTime = array(
+        self::ANTI_SPAM_MINUTE => 60,
+        self::ANTI_SPAM_HOUR => 3600,
+        self::ANTI_SPAM_DAY => 86400,
+    );
+
+    public static function check()
+    {
+        // 检查是否已经被封杀
+        if (self::getConnection()->get(self::getKey(self::ANTI_SPAM_LOCKED))) {
+            return FALSE;
+        }
+        foreach (self::$limits as $key => $limit) {
+            $expire = self::$expire[$key];
+            $key = self::getKey($key);
+            $value = self::getConnection()->get($key);
+            if (!$value) {
+                // 值不存在就设置
+                self::getConnection()->set($key, 1, $expire);
+            } elseif ($value > $limit) {
+                // 封禁时间
+                $expire = self::$forbiddenTime[$key];
+                self::getConnection()->set(self::getKey(self::ANTI_SPAM_LOCKED), 1, $expire);
+                return FALSE;
+            } else {
+                // 正常情况下+1
+                self::getConnection()->increment($key, 1);
+            }
+        }
+        return TRUE;
+    }
+
+    protected static function getKey($key)
+    {
+        $ip = \Heanup\Frame\Helper::getClientIp(TRUE);
+        return $key . ':' . $ip;
+    }
+
+    protected static function getConnection()
+    {
+        static $connection = null;
+        if ($connection) {
+            return $connection;
+        }
+        $cfg = \Heanup\Config\Memcache\AntiSpam::getData();
+        $connection = new \Memcached;
+        $connection->setOption(\Memcached::OPT_COMPRESSION, TRUE);
+        $connection->setOption(\Memcached::OPT_DISTRIBUTION, TRUE);
+        $connection->setOption(\Memcached::OPT_LIBKETAMA_COMPATIBLE, TRUE);
+        $connection->setOption(\Memcached::OPT_NO_BLOCK, TRUE);
+        $connection->setOption(\Memcached::OPT_CONNECT_TIMEOUT, 50);
+        $connection->setOption(\Memcached::OPT_POLL_TIMEOUT, 50);
+        $connection->addServers($cfg);
+        return $connection;
+    }
+
+}

+ 161 - 0
Frame/Library/BaseConverter.php

@@ -0,0 +1,161 @@
+<?php
+
+namespace Heanup\Frame\Library;
+
+/**
+ * BASE62 解析类,专门针对长10进制与62进制互转
+ */
+class BaseConverter
+{
+    private static $string = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+    private static $encodeBlockSize = 7;
+    private static $decodeBlockSize = 4;
+
+    /**
+     * 批量从10进制转换到62进制
+     *
+     * @param array $mids
+     * @return array 以10进制mid为key的62进制mid数组
+     */
+    public static function multi_from10to62(array $mids)
+    {
+        $ret = array();
+        foreach ($mids as $mid) {
+            $ret[$mid] = self::from10to62($mid);
+        }
+        return $ret;
+    }
+
+    /**
+     * 批量从62进制转换到10进制
+     *
+     * @param array $mids
+     * @param bool $compat
+     * @param bool $for_mid
+     * @return array 以62进制mid为key的10进制mid数组
+     */
+    public static function multi_from62to10(array $mids, $compat = false, $for_mid = true)
+    {
+        $ret = array();
+        foreach ($mids as $mid) {
+            $ret[$mid] = self::from62to10($mid, $compat, $for_mid);
+        }
+        return $ret;
+    }
+
+    /**
+     * 将mid从10进制转换成62进制字符串
+     *
+     * @param    string $mid
+     * @return    string
+     */
+    public static function from10to62($mid)
+    {
+        $str = "";
+        $midlen = strlen($mid);
+        $segments = ceil($midlen / self::$encodeBlockSize);
+        $start = $midlen;
+        for ($i = 1; $i < $segments; $i += 1) {
+            $start -= self::$encodeBlockSize;
+            $seg = substr($mid, $start, self::$encodeBlockSize);
+            $seg = self::encodeSegment($seg);
+            $str = str_pad($seg, self::$decodeBlockSize, '0', STR_PAD_LEFT) . $str;
+        }
+        $str = self::encodeSegment(substr($mid, 0, $start)) . $str;
+        return $str;
+    }
+
+    /**
+     * 将62进制字符串转成10进制mid
+     *
+     * $compat $for_mid 参数意图不确定,直接从原作者处考过来。使用时直接使用默认参数即可。
+     *
+     * @param    string $str
+     * @param    bool $compat
+     * @param    bool $for_mid
+     * @return    string
+     */
+    public static function from62to10($str, $compat = false, $for_mid = true)
+    {
+        $mid = "";
+        $strlen = strlen($str);
+        $segments = ceil($strlen / self::$decodeBlockSize);
+        $start = $strlen;
+        for ($i = 1; $i < $segments; $i += 1) {
+            $start -= self::$decodeBlockSize;
+            $seg = substr($str, $start, self::$decodeBlockSize);
+            $seg = self::decodeSegment($seg);
+            /* Note by Rodin:
+             * 由于 decodeBlockSize设置为4,而 encodeBlockSize 设置为 7
+             * 而 base 62里4位最大的数为 ZZZZ,对应到10进制为 14776336,比encodeBlockSize
+             * 多1位……所以,将10进制转成62进制且在不足位时左补0,不会有任何问题。反之,
+             * 在62转成10进制的时候就可能会出现溢出,这是个潜在的bug,不清楚为什么目前没有
+             * bug爆出且当初采用了这种不标准不严谨的62进制转换法……
+             * 目前常用的mid里存在类似: l4ETJ4DfL (9位),在每segment大约出现大于 GZZZ 的串以后
+             * 总位数就会不符合目前的预期……
+             * 例如:
+             * ZZZZZZZZZ转换成10进制会成为 611477633614776336,总位数已经达到了20位,而常见的7位
+             * mid的62进制数字通常只有16位
+             */
+            $mid = str_pad($seg, self::$encodeBlockSize, '0', STR_PAD_LEFT) . $mid;
+        }
+        $mid = self::decodeSegment(substr($str, 0, $start)) . $mid;
+
+        //判断v3、v4版本mid
+        if ($for_mid) {
+            $midlen = strlen($mid);
+            $first = substr($mid, 0, 1);
+            if ($midlen == 16 && ($first == '3' || $first == '4')) {
+                return $mid;
+            }
+            if ($midlen == 19 && $first == '5') {
+                return $mid;
+            }
+        }
+        //end
+        if ($compat && !in_array(substr($mid, 0, 3), array('109', '110', '201', '211', '221', '231', '241'))) {
+            $mid = self::decodeSegment(substr($str, 0, 4)) . self::decodeSegment(substr($str, 4));
+        }
+        if ($for_mid) {
+            if (substr($mid, 0, 1) == '1' && substr($mid, 7, 1) == '0') {
+                $mid = substr($mid, 0, 7) . substr($mid, 8);
+            }
+        }
+        return $mid;
+    }
+
+    /**
+     * 将10进制转换成62进制
+     *
+     * @param    string $str 10进制字符串
+     * @return    string
+     */
+    private static function encodeSegment($str)
+    {
+        $out = '';
+        while ($str > 0) {
+            $idx = $str % 62;
+            $out = substr(self::$string, $idx, 1) . $out;
+            $str = floor($str / 62);
+        }
+        return $out;
+    }
+
+    /**
+     * 将62进制转换成10进制
+     *
+     * @param    string $str 62进制字符串
+     * @return    string
+     */
+    private static function decodeSegment($str)
+    {
+        $out = 0;
+        $base = 1;
+        for ($t = strlen($str) - 1; $t >= 0; $t -= 1) {
+            $out = $out + $base * strpos(self::$string, substr($str, $t, 1));
+            $base *= 62;
+        }
+        return strval($out);
+    }
+
+}

+ 8 - 0
Frame/Library/Benchmark.php

@@ -0,0 +1,8 @@
+<?php
+
+namespace Heanup\Frame\Library;
+
+class Benchmark extends \Heanup\Frame\Library
+{
+
+}

+ 19 - 0
Frame/Library/Curl.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace Heanup\Frame\Library;
+
+class Curl extends \Heanup\Frame\Library
+{
+
+
+    public static function get()
+    {
+
+    }
+
+    public static function post()
+    {
+
+    }
+
+}

+ 44 - 0
Frame/Library/Memcache.php

@@ -0,0 +1,44 @@
+<?php
+
+namespace Heanup\Frame\Library;
+
+/**
+ * 缓存设置,使用memcache缓存数据
+ */
+class Memcache extends \Heanup\Frame\Library
+{
+
+    /**
+     * 获取连接池
+     * @return \Memcached
+     */
+    public function getConnection()
+    {
+        static $pool = array();
+        $connectionName = $this->getFlag(true);
+        if (!isset($pool[$connectionName]) || ($pool[$connectionName] == false)) {
+            $cfg = $this->getConfig();
+            $pool[$connectionName] = $this->connect($cfg);
+        }
+        return $pool[$connectionName];
+    }
+
+    /**
+     * 连接Memcache
+     * @param $cfg
+     * @return \Memcached
+     */
+    protected function connect($cfg)
+    {
+        $connection = new \Memcached;
+        $connection->setOption(\Memcached::OPT_COMPRESSION, true);
+        $connection->setOption(\Memcached::OPT_DISTRIBUTION, true);
+        $connection->setOption(\Memcached::OPT_LIBKETAMA_COMPATIBLE, true);
+        $connection->setOption(\Memcached::OPT_NO_BLOCK, true);
+        $connection->setOption(\Memcached::OPT_CONNECT_TIMEOUT, 50);
+        $connection->setOption(\Memcached::OPT_POLL_TIMEOUT, 50);
+        $connection->addServers($cfg);
+        return $connection;
+    }
+
+}

+ 41 - 0
Frame/Library/Memcached.php

@@ -0,0 +1,41 @@
+<?php
+namespace Heanup\Frame\Library;
+
+/**
+ * 缓存设置,使用memcache缓存数据
+ */
+class Memcached extends \Heanup\Frame\Library
+{
+
+    /**
+     * 获取连接池
+     * 
+     * @return \Memcached
+     */
+    public function getConnection()
+    {
+        static $pool = array();
+        $connectionName = $this->getFlag(true);
+        if (! isset($pool[$connectionName]) || ($pool[$connectionName] == false)) {
+            $cfg = $this->getConfig();
+            $pool[$connectionName] = $this->connect($cfg);
+        }
+        return $pool[$connectionName];
+    }
+
+    /**
+     * 连接Memcache
+     * 
+     * @param
+     *            $cfg
+     * @return \Memcached
+     */
+    protected function connect($cfg)
+    {
+        $connection = new \Memcache();
+        foreach ($cfg as $val) {
+            $connection->addServer($val['host'], $val['port']);
+        }
+        return $connection;
+    }
+}

+ 252 - 0
Frame/Library/MySQL.php

@@ -0,0 +1,252 @@
+<?php
+namespace Heanup\Frame\Library;
+
+use Heanup\Frame\Logger;
+use Heanup\Frame\Exception;
+
+/**
+ * 处理MySQL连接、查询和写入
+ *
+ * @package Heanup\Frame\Library
+ */
+class MySQL extends \Heanup\Frame\Library
+{
+
+    private static $throwException = false;
+    // 出错后是否抛出异常
+    private static $inTransaction = false;
+    // 是否在事务中
+    private static $newConnection = false;
+    // 是否使用新连接
+    const CONNECTION_TIMEOUT = 21600;
+    // 每个连接最多可以使用时间,超过这个时间之后创建新连接
+    protected $exception;
+
+    /**
+     * 执行查询操作
+     *
+     * @param string $sql            
+     * @param array $data            
+     * @param bool $isMaster            
+     * @param bool $fetchAll            
+     * @throws \Exception
+     * @return array|mixed
+     */
+    public function read($sql, $data, $isMaster = false, $fetchAll = true)
+    {
+        try {
+            $stmt = $this->getConnection($isMaster)->prepare($sql);
+            $stmt->execute($data);
+            $stmt->setFetchMode(\PDO::FETCH_ASSOC);
+            if ($fetchAll) {
+                $result = $stmt->fetchAll();
+            } else {
+                $result = $stmt->fetch();
+            }
+        } catch (\Exception $ex) {
+            $result = null;
+            $this->exception = $ex;
+            $message = array(
+                'info' => $ex->getCode() . '=>' . $ex->getMessage() . ';' . json_encode($ex->errorInfo),
+                'sql' => $sql,
+                'data' => $data
+            );
+            Logger::error($message, 'mysql.read.error');
+            // 如果是失去连接的错误,则进行重试
+            if (in_array($ex->errorInfo[1], array(
+                '2006',
+                '2013'
+            ))) {
+                self::$newConnection = true;
+                return $this->read($sql, $data, $isMaster, $fetchAll);
+            }
+            // 处理事务时抛出异常
+            if (self::$throwException) {
+                throw new \Exception($ex->getCode() . '=>' . $ex->getMessage(), '90203');
+            }
+        }
+        Logger::debug(array(
+            'sql' => $sql,
+            'data' => $data,
+            'result' => $result
+        ), 'mysql.read.debug');
+        return $result;
+    }
+
+    /**
+     * 执行写入操作
+     *
+     * @param string $sql            
+     * @param
+     *            $data
+     * @throws \Exception
+     * @return boolean
+     */
+    public function write($sql, $data)
+    {
+        try {
+            $stmt = $this->getConnection(true)->prepare($sql);
+            $stmt->execute($data);
+            $result = $stmt->rowCount();
+        } catch (\Exception $ex) {
+            $result = false;
+            $this->exception = $ex;
+            $message = array(
+                'info' => $ex->getCode() . '=>' . $ex->getMessage() . ';' . json_encode($ex->errorInfo),
+                'sql' => $sql,
+                'data' => $data
+            );
+            Logger::error($message, 'mysql.write.error');
+            // 如果是失去连接的错误,则进行重试
+            if (in_array($ex->errorInfo[1], array(
+                '2006',
+                '2013'
+            ))) {
+                self::$newConnection = true;
+                return $this->write($sql, $data);
+            }
+            // 处理事务时抛出异常
+            if (self::$throwException) {
+                throw new \Exception($ex->getCode() . '=>' . $ex->getMessage(), '90202');
+            }
+        }
+        Logger::debug(array(
+            'sql' => $sql,
+            'data' => $data,
+            'result' => $result
+        ), 'mysql.write.debug');
+        return $result;
+    }
+
+    /**
+     * 事务处理, $callback中如果出错必需抛出异常,否则会真正执行
+     * 开始时会设置为主动抛出异常,结束时会设置为关闭抛出异常
+     *
+     * @param
+     *            $callback
+     * @param
+     *            $throwException
+     * @return bool
+     */
+    public function transaction($callback, $throwException = false)
+    {
+        self::$inTransaction = true; // 处于事务中
+        self::$throwException = $throwException; // 执行SQL出错时候是否抛出异常
+        try {
+            $connection = $this->getConnection(true);
+            $connection->beginTransaction();
+            $callback();
+            $connection->commit();
+            $result = true;
+        } catch (\Exception $ex) {
+            $result = false;
+            $message = array(
+                'info' => $ex->getCode() . '=>' . $ex->getMessage() . ';' . json_encode($ex->errorInfo)
+            );
+            Logger::error($message, 'mysql.transaction.error');
+            // 如果是失去连接的错误,则进行重试
+            if (in_array($ex->errorInfo[1], array(
+                '2006',
+                '2013'
+            ))) {
+                self::$newConnection = true;
+                return $this->transaction($callback, $throwException);
+            }
+         
+            try {
+                $this->getConnection(true)->rollBack();
+               
+            } catch (\Exception $e) {
+                
+                // rollback failed
+            }
+            if($throwException){
+                throw new Exception($ex->getMessage(),$ex->getCode());
+            }
+        }
+        self::$throwException = false; // 关闭抛出异常
+        self::$inTransaction = false; // 事务结束
+        Logger::debug(array(
+            'result' => $result
+        ), 'mysql.transaction.debug');
+        return $result;
+    }
+
+    /**
+     * 获取自增ID
+     *
+     * @return string
+     */
+    public function getLastInsertId()
+    {
+        return $this->getConnection(true)->lastInsertId();
+    }
+
+    /**
+     * 获取异常
+     *
+     * @return mixed|\Exception
+     */
+    public function getLastException()
+    {
+        return $this->exception;
+    }
+
+    /**
+     * 获取连接池
+     * 如果超时时处于事务中,则不主动断开重连
+     *
+     * @param bool $isMaster            
+     * @return \PDO
+     */
+    protected function getConnection($isMaster = false)
+    {
+        static $pool = array();
+        $connectionName = $this->getFlag($isMaster);
+        $connectionTimeout = $connectionName . '_timeout';
+        if (self::$newConnection || ! isset($pool[$connectionName]) || $pool[$connectionName] == false || (self::$inTransaction == false && (time() - $pool[$connectionTimeout] > self::CONNECTION_TIMEOUT))) {
+            $cfg = $this->getConfig();
+            if ($isMaster) {
+                $cfg = $cfg['master'];
+            } else {
+                $cfg = $cfg['slave_list'][array_rand($cfg['slave_list'])];
+            }
+            $pool[$connectionName] = $this->connect($cfg);
+            $pool[$connectionTimeout] = time();
+            self::$newConnection = false;
+        }
+        return $pool[$connectionName];
+    }
+
+    /**
+     * 连接数据库
+     *
+     * @param
+     *            $cfg
+     * @return \PDO
+     * @throws \Exception
+     */
+    protected function connect($cfg)
+    {
+        $opts = array(
+            \PDO::ATTR_AUTOCOMMIT => true,
+            \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
+            \PDO::ATTR_TIMEOUT => $cfg['timeout'],
+            \PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES '" . $cfg['charset'] . "'",
+            \PDO::ATTR_PERSISTENT => $cfg['persistent']?true : false
+        ); // 启用长连接
+        
+        $dsn = 'mysql:dbname=' . $cfg['dbname'] . ';host=' . $cfg['host'] . ';port=' . $cfg['port'];
+        try {
+            $db = new \PDO($dsn, $cfg['username'], $cfg['password'], $opts);
+        } catch (\PDOException $ex) {
+            $this->exception = $ex;
+            $message = "MySQL connection failed [" . $dsn . ';' . $cfg['username'] . ';password]' . $ex->getCode() . '=>' . $ex->getMessage();
+            Logger::emerg($message, 'mysql.connect.error');
+            throw new \PDOException($message, 90201);
+        }
+        $message = "MySQL connect [" . $dsn . ';' . $cfg['username'] . ';password]';
+        Logger::debug($message, 'mysql.connect.debug');
+        return $db;
+    }
+}

+ 402 - 0
Frame/Library/MySQL/Query.php

@@ -0,0 +1,402 @@
+<?php
+
+namespace Heanup\Frame\Library\MySQL;
+
+/**
+ * 查询生成器
+ */
+class Query
+{
+
+    const PREPARE_PREFIX = ':prepared_';
+
+    private $tableName;
+    private $operation = 'SELECT';
+    private $field = array();
+    private $primaryKey = '';
+    private $data = array();
+    private $where;
+    private $orderBy;
+    private $groupBy;
+    private $limit = 'LIMIT 2000';
+    //替换后的映射表
+    private $prepared = array();
+    private $sql = '';
+
+    /**
+     * 实例
+     * @return Query
+     */
+    public static function newInstance()
+    {
+        return new Query();
+    }
+
+    /**
+     * 获取select
+     * @return Query
+     */
+    public function select()
+    {
+        $this->operation = 'SELECT';
+        return $this;
+    }
+
+    /**
+     * 获取count
+     * @return Query
+     */
+    public function count()
+    {
+        $this->operation = 'COUNT';
+        return $this;
+    }
+
+    /**
+     * 插入条记录
+     * @return Query
+     */
+    public function insert()
+    {
+        $this->operation = 'INSERT';
+        return $this;
+    }
+
+    /**
+     * replace into
+     * @return Query
+     */
+    public function replace()
+    {
+        $this->operation = 'REPLACE';
+        return $this;
+    }
+
+    /**
+     * 更新
+     * @return Query
+     */
+    public function update()
+    {
+        $this->operation = 'UPDATE';
+        return $this;
+    }
+
+    /**
+     * 删除
+     * @return Query
+     */
+    public function delete()
+    {
+        $this->operation = 'DELETE';
+        return $this;
+    }
+
+    /**
+     * 设置表名
+     * @param string $tableName
+     * @return Query
+     */
+    public function setTableName($tableName)
+    {
+        if ($tableName) {
+            $this->tableName = $this->_prepareFieldName($tableName);
+        }
+        return $this;
+    }
+
+    /**
+     * 设置字段
+     * @param Array /String $field
+     * @return Query
+     */
+    public function setField($field)
+    {
+        if ($field) {
+            if (!is_array($field)) {
+                $field = explode(',', $field);
+            }
+            $this->field = $field;
+        }
+        return $this;
+    }
+
+    /**
+     * 设置主键名
+     * @param $primaryKey
+     * @return $this
+     */
+    public function setPrimaryKey($primaryKey)
+    {
+        if ($primaryKey) {
+            $this->primaryKey = trim($primaryKey);
+        }
+        return $this;
+    }
+
+    /**
+     * 设置where条件
+     * @param array $where
+     * @param string $whereString
+     * @return Query
+     */
+    public function setWhere(Array $where, $whereString = '')
+    {
+        if (empty($where) && empty($whereString)) {
+            return $this;
+        }
+        if ($whereString) {
+            foreach ($where as $key => $val) {
+                $preparedKeys = array();
+                if (is_array($val)) {
+                    foreach ($val as $row) {
+                        $preparedKeys[] = $this->_prepareData($row);
+                    }
+                } else {
+                    $preparedKeys[] = $this->_prepareData($val);
+                }
+                $key = ':' . ltrim($key, ':');
+                $whereString = str_replace($key, implode(',', $preparedKeys), $whereString);
+            }
+            $this->where = 'WHERE ' . $whereString;
+        } elseif ($where) {
+            $data = array();
+            foreach ($where as $key => $val) {
+                if (is_array($val)) {
+                    $preparedKeys = array();
+                    foreach ($val as $row) {
+                        $preparedKeys[] = $this->_prepareData($row);
+                    }
+                    $data[] = $this->_prepareFieldName($key) . ' IN (' . implode(',', $preparedKeys) . ')';
+                } else {
+                    $data[] = $this->_prepareFieldName($key) . " = " . $this->_prepareData($val);
+                }
+            }
+            $this->where = 'WHERE ' . implode(' AND ', $data);
+        }
+        return $this;
+    }
+
+    /**
+     * 设置排序
+     * @param Array $orderBy = array('id' => 1); value 是 true 表示倒序,反之是正序
+     * @return Query
+     */
+    public function setOrderBy(Array $orderBy)
+    {
+        if ($orderBy && is_array($orderBy)) {
+            $tmp = array();
+            foreach ($orderBy as $key => $val) {
+                if (is_numeric($key)) {
+                    continue;
+                }
+                $order = $val ? 'DESC' : 'ASC';
+                $tmp[] = $this->_prepareFieldName($key) . ' ' . $order;
+            }
+            $tmp = implode(',', $tmp);
+            $this->orderBy = "ORDER BY " . $tmp;
+        }
+        return $this;
+    }
+
+    /**
+     * 设置范围
+     * @param int $limit
+     * @param int $offset
+     * @return Query
+     */
+    public function setLimit($limit = 2000, $offset = 0)
+    {
+        if ($limit) {
+            $this->limit = 'LIMIT ' . intval($offset) . ',' . intval($limit);
+        } else {
+            $this->limit = '';
+        }
+        return $this;
+    }
+
+    /**
+     * 设置分组
+     * @param Array /String $groupBy
+     * @return Query
+     */
+    public function setGroupBy($groupBy)
+    {
+        if (empty($groupBy)) {
+            return $this;
+        }
+        if (is_array($groupBy)) {
+            $tmp = array();
+            foreach ($groupBy as $v) {
+                $tmp[] = $this->_prepareFieldName($v);
+            }
+            $this->groupBy = 'GROUP BY ' . implode(",", $tmp);
+        } else {
+            $this->groupBy = 'GROUP BY ' . $this->_prepareFieldName($groupBy);
+        }
+        return $this;
+    }
+
+    /**
+     * 设置数据
+     * @param Array $data
+     * @return Query
+     */
+    public function setData(Array $data)
+    {
+        if ($data) {
+            $this->data = $data;
+        }
+        return $this;
+    }
+
+    /**
+     * 获取相关SQL
+     * @param boolean $raw
+     * @return String $sql
+     */
+    public function getSQL($raw = false)
+    {
+        $sql = $this->_getSQL();
+        if ($raw) {
+            foreach ($this->prepared as $key => $value) {
+                $key = ':' . trim($key, ':');
+                if (is_array($value)) {
+                    foreach ($value as $k => $v) {
+                        $value[$k] = addslashes($v);
+                    }
+                    $value = implode(',', $value);
+                } else {
+                    $value = "'" . addslashes($value) . "'";
+                }
+                $sql = str_replace($key, $value, $sql);
+            }
+        }
+        return $sql;
+    }
+
+    /**
+     * 返回Prepare过的数据
+     * @return Array
+     */
+    public function getData()
+    {
+        $sql = $this->_getSQL();
+        $data = array();
+        foreach ($this->prepared as $key => $value) {
+            if (strpos($sql, $key) !== false) {
+                $key = ltrim($key, ':');
+                $data[$key] = $value;
+            }
+        }
+        return $data;
+    }
+
+    /**
+     * 拼SQL
+     * @return string
+     */
+    private function _getSQL()
+    {
+        if ($this->sql) {
+            return $this->sql;
+        }
+        switch ($this->operation) {
+            case 'INSERT':
+                $sql = 'INSERT INTO  ' . $this->tableName . ' SET ' . $this->_getData();
+                break;
+            case 'UPDATE':
+                $sql = 'UPDATE ' . $this->tableName . ' SET ' . $this->_getData() . ' ' . $this->where;
+                break;
+            case 'REPLACE':
+                $sql = 'REPLACE INTO  ' . $this->tableName . ' SET ' . $this->_getData();
+                break;
+            case 'DELETE':
+                $sql = 'DELETE FROM ' . $this->tableName . ' ' . $this->where;
+                break;
+            case 'COUNT':
+                $sql = 'SELECT COUNT(*) AS `count`  FROM ' . $this->tableName . ' ' . $this->where . ' ' . $this->groupBy;
+                break;
+            case 'SELECT':
+            default:
+                $sql = 'SELECT ' . $this->_getField() . ' FROM ' . $this->tableName . ' ' . $this->where . ' ' . $this->orderBy . ' ' . $this->groupBy . ' ' . $this->limit;
+                break;
+        }
+        return $this->sql = $sql;
+    }
+
+    /**
+     * 获取字段名称
+     * @return String
+     */
+    private function _getField()
+    {
+        if (empty($this->field)) {
+            $field = '*';
+        } else {
+            $tmp = array();
+            foreach ($this->field as $v) {
+                if(strstr($v,'sum')){
+                    $tmp[] = $v;
+                }else{
+                    $tmp[] = $this->_prepareFieldName($v);
+                }
+            }
+            $field = implode(",", $tmp);
+        }
+        return $field;
+    }
+
+    /**
+     * 获取prepare后的数据
+     * @return String
+     */
+    private function _getData()
+    {
+        $data = array();
+        foreach ($this->data as $key => $value) {
+            //简单字段检查
+            if (($this->field AND !in_array($key, $this->field)) || is_null($value)) {
+                continue;
+            }
+            $data[$key] = $this->_prepareFieldName($key) . ' = ' . $this->_prepareData($value);
+        }
+        $data = implode(",", $data);
+        return $data;
+    }
+
+    /**
+     * 过滤字段名,表名
+     * @param String $value
+     * @return string
+     */
+    private function _prepareFieldName($value)
+    {
+        $value = strtr($value, array(' ' => '', '`' => ''));
+        $value = "`" . $value . "`";
+        return $value;
+    }
+
+    /**
+     * prepare替换
+     * @param String $value
+     * @return string
+     */
+    private function _prepareData($value)
+    {
+        $count = count($this->prepared);
+        $key = self::PREPARE_PREFIX . $count . '_';
+        $this->prepared[$key] = $value;
+        return $key;
+    }
+
+    /**
+     * 魔术输出方法
+     * @return String
+     */
+    public function __toString()
+    {
+        return $this->getSQL();
+    }
+
+}

+ 63 - 0
Frame/Library/Redis.php

@@ -0,0 +1,63 @@
+<?php
+
+namespace Heanup\Frame\Library;
+
+/**
+ * Redis连接
+ */
+class Redis extends \Heanup\Frame\Library
+{
+    protected $exception;
+
+    public function getLastException()
+    {
+        return $this->exception;
+    }
+
+    /**
+     * 获取redis连接池
+     * @param bool $isMaster
+     * @return \Redis
+     */
+    public function getConnection($isMaster = false)
+    {
+        static $pool = array();
+        $cacheName = $this->getFlag($isMaster);
+        if (!isset($pool[$cacheName]) || $pool[$cacheName] == false) {
+            $cfg = $this->getConfig();
+            if ($isMaster) {
+                $cfg = $cfg['master'];
+            } else {
+                $cfg = $cfg['slave_list'][array_rand($cfg['slave_list'])];
+            }
+            $pool[$cacheName] = $this->connect($cfg);
+        }
+        return $pool[$cacheName];
+    }
+
+    /**
+     * 连接Redis
+     * @param $cfg
+     * @return \Redis
+     * @throws \Exception
+     */
+    protected function connect($cfg)
+    {
+        try {
+            $redis = new \Redis();
+            if (isset($cfg['pconnect']) && $cfg['pconnect'] == 1) {
+                $redis->pconnect($cfg['host'], $cfg['port'], $cfg['timeout']);
+            } else {
+                $redis->connect($cfg['host'], $cfg['port'], $cfg['timeout']);
+            }
+            if (isset($cfg['password']) && $cfg['password']) {
+                $redis->auth($cfg['password']);
+            }
+        } catch (\Exception $ex) {
+            $this->exception = $ex;
+            $message = "Redis connection failed : [" . $cfg['host'] . ';' . $cfg['port'] . ']';
+            throw new \Exception($message, 90301);
+        }
+        return $redis;
+    }
+}

+ 79 - 0
Frame/Library/Ticket.php

@@ -0,0 +1,79 @@
+<?php
+
+namespace Heanup\Frame\Library;
+
+/**
+ * 发号器类
+ */
+class Ticket extends \Heanup\Frame\Library
+{
+    const TICKET_START_TIME = 1262275200; // 2010-1-1 00:00:00
+    const DEFAULT_SERVER_ID = 9; //使用微秒发出去的id编号
+    const TICKET_KEY_PREFIX = 'id_';
+
+    /**
+     * 获取发号
+     * @param $businessId
+     * @return mixed
+     * @throws \Exception
+     */
+    public static function getTicket($businessId = 0)
+    {
+//         for ($i = 0; $i < 10; $i++) {
+//             $result = self::getFromRemote($businessId);
+//             if ($result) {
+//                 return $result;
+//             }
+//         }
+//         \Heanup\Frame\Logger::crit('ticket error', 'ticket');
+        $result = self::getTicketFromTimestamp();
+        return $result;
+    }
+
+    /**
+     * 从发号器获取号码
+     * @param $businessId
+     * @return mixed
+     */
+    protected static function getFromRemote($businessId = 0)
+    {
+        $configClass = 'Memcache\\Ticket';
+        $memcached = new Memcache($configClass);
+        $connection = $memcached->getConnection();
+        $key = self::TICKET_KEY_PREFIX . intval($businessId);
+        $result = $connection->get($key);
+        return $result;
+    }
+
+    /**
+     * 获取发号
+     * @return int
+     */
+    protected static function getTicketFromTimestamp()
+    {
+        $time = self::getTimestamp();
+        $time[0] = $time[0] - self::TICKET_START_TIME;
+        $time = implode('', $time);
+        $rand = sprintf('%03d', rand(0, 999));
+        $code = $time . $rand . self::DEFAULT_SERVER_ID;
+        return $code;
+    }
+
+    /**
+     * 获取时间戳信息,
+     * 返回值分别是秒,毫秒,微秒
+     * @return array
+     */
+    protected static function getTimestamp()
+    {
+        list($microTime, $seconds) = explode(" ", microtime());
+        $microTime = intval($microTime * 1000000);
+        $timeStamp = array(
+            0 => $seconds,
+            1 => substr($microTime, 0, 3),
+            2 => substr($microTime, 3, 3),
+        );
+        return $timeStamp;
+    }
+
+}

+ 347 - 0
Frame/Logger.php

@@ -0,0 +1,347 @@
+<?php
+
+namespace Heanup\Frame;
+
+/**
+ * 简单日志记录
+ */
+class Logger
+{
+
+    private static $logPath = '/data/wwwlogs/';
+
+    private static $logLevel = LOG_ERR;
+
+    private static $maps = array(
+        LOG_DEBUG => 'debug',
+        LOG_INFO => 'info',
+        LOG_NOTICE => 'notice',
+        LOG_WARNING => 'warning',
+        LOG_ERR => 'err',
+        LOG_CRIT => 'crit',
+        LOG_ALERT => 'alert',
+        LOG_EMERG => 'emerg'
+    );
+
+    /**
+     * 获取单例
+     *
+     * @return Logger
+     */
+    public static function getInstance()
+    {
+        static $instance = null;
+        if (is_null($instance)) {
+            $instance = new Logger();
+        }
+        return $instance;
+    }
+
+    /**
+     * 设置日志记录错误等级
+     *
+     * @param int $level
+     * @return Logger
+     */
+    public static function setLogLevel($level = LOG_DEBUG)
+    {
+        self::$logLevel = intval($level);
+        return self::getInstance();
+    }
+
+    /**
+     * 设置日志记录路径
+     *
+     * @param
+     *            $path
+     * @return Logger
+     */
+    public static function setLogPath($path)
+    {
+        $path = trim($path);
+        if (!empty($path) and is_writable($path)) {
+            self::$logPath = $path;
+        }
+        return self::getInstance();
+    }
+
+    public static function debug($message = '', $filename = null)
+    {
+        return self::log($message, $filename, LOG_DEBUG);
+    }
+
+    public static function info($message = '', $filename = null)
+    {
+        return self::log($message, $filename, LOG_INFO);
+    }
+
+    public static function notice($message = '', $filename = null)
+    {
+        return self::log($message, $filename, LOG_NOTICE);
+    }
+
+    public static function warning($message = '', $filename = null)
+    {
+        return self::log($message, $filename, LOG_WARNING);
+    }
+
+    public static function error($message = '', $filename = null)
+    {
+        return self::log($message, $filename, LOG_ERR);
+    }
+
+    public static function crit($message = '', $filename = null)
+    {
+        return self::log($message, $filename, LOG_CRIT);
+    }
+
+    public static function alert($message = '', $filename = null)
+    {
+        return self::log($message, $filename, LOG_ALERT);
+    }
+
+    public static function emerg($message = '', $filename = null)
+    {
+        return self::log($message, $filename, LOG_EMERG);
+    }
+
+    public static function log($message = '', $filename = null, $logType = LOG_DEBUG)
+    {
+        // 达不到错误记录级别的不与记录,主要是线上环境忽略debug信息
+        if ($logType > self::getLogLevel()) {
+            return false;
+        }
+        self::file($message, $filename, $logType);
+        return true;
+    }
+
+    /**
+     * 自定义记录日志
+     *
+     * @param null $email
+     * @param string $message
+     * @param string $filename
+     * @return bool
+     */
+    public static function setLog($email = NULL, $message = "", $filename = "")
+    {
+        if (empty($message) || empty($filename))
+            return false;
+        $filename = self::getLogPath($filename);
+        $message = "[" . date('Y-m-d H:i:s') . "]" . "-[IP:" . self::getClientIp() . "]" . "-[http://" . self::getServerIp() . $_SERVER['REQUEST_URI'] . "]" . ($email ? "-[EMAIL:$email]" : "") . "-[操作:$message]" . "\n";
+        return error_log($message, 3, $filename);
+    }
+
+    /**
+     * 自定义记录日志
+     *
+     * @param string $message
+     * @param string $filename
+     * @return bool
+     */
+    public static function logApi($message = "", $filename = "")
+    {
+        if (empty($message) || empty($filename))
+            return false;
+        $filename = self::getLogPath($filename);
+        $message = "[" . date('Y-m-d H:i:s') . "]" . "-[IP:" . self::getClientIp() . "]" . "-[http://" . self::getServerIp() . $_SERVER['REQUEST_URI'] . "]" . '[post:' . http_build_query($_POST) . ']' . '[get:' . http_build_query($_GET) . ']' . "-[msg:$message]" . "\n";
+        return error_log($message, 3, $filename);
+    }
+
+    /**
+     * 记录大数据的日志
+     *
+     * @param
+     *            $message
+     * @param
+     *            $filename
+     * @return bool
+     */
+    public static function bigLog($data)
+    {
+        if (empty($data))
+            return false;
+        $filename = self::getBigdataLogPath();
+        $message = json_encode($data) . "\r\n";
+        return error_log($message, 3, $filename);
+    }
+
+    /**
+     * 记录简单的日志内容
+     *
+     * @param
+     *            $message
+     * @param
+     *            $filename
+     * @return bool
+     */
+    public static function singleLog($data, $filename)
+    {
+        if (is_array($data)) {
+            $msg = http_build_query($data);
+        } else {
+            $msg = $data;
+        }
+        $filename = self::getLogPath($filename);
+        $message = "[" . date('Y-m-d H:i:s') . "][" . $_SERVER['REQUEST_URI'] . "]-[IP:" . self::getClientIp() . "][data:" . $msg . "]\r\n";
+        return error_log($message, 3, $filename);
+    }
+
+    /**
+     * 使用文件记录推送日志
+     *
+     * @param string $message
+     * @param string $filename
+     * @param int $logType
+     * @return bool
+     */
+    private static function file($message = '', $filename = null, $logType = LOG_INFO)
+    {
+        if (!$filename) {
+            $logType = intval($logType);
+            $filename = self::$maps[$logType];
+        }
+        $filename = self::getLogPath($filename);
+        $message = self::formatFields($message);
+        return error_log($message, 3, $filename);
+    }
+
+    /**
+     * 格式日志记录
+     *
+     * @param string $message
+     * @return string
+     */
+    private static function formatFields($message)
+    {
+        if ($message instanceof \Exception) {
+            $trace = $message->getTrace();
+            $trace = $trace[1];
+            $trace = ' ;trace => ' . json_encode($trace);
+            $message = $message->getCode() . ' => ' . $message->getMessage() . $trace;
+        } elseif ($message) {
+            $message = (is_string($message) || is_numeric($message)) ? $message : json_encode($message);
+        } else {
+            $message = ' - ';
+        }
+        $data = array();
+        $data['time'] = date('Y-m-d H:i:s');
+        $data['message'] = trim($message);
+        $data['client_ip'] = self::getClientIp();
+        $string = '[' . implode("]  [", $data) . ']' . "\n";
+        return $string;
+    }
+
+    /**
+     * 获取错误等级
+     *
+     * @return int
+     */
+    private static function getLogLevel()
+    {
+        return self::$logLevel;
+    }
+
+    /**
+     * 获取日志路径,如果不存在或者不可写则使用tmp路径
+     *
+     * @param
+     *            $filename
+     * @return string
+     */
+    private static function getLogPath($filename)
+    {
+        static $path = '';
+        if (!$path) {
+            if ((!file_exists(self::$logPath))) {
+                @mkdir(self::$logPath, 0777, true);
+            }
+            $path = is_writable(self::$logPath) ? self::$logPath : sys_get_temp_dir();
+            $path = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . date('Ym') . DIRECTORY_SEPARATOR . date('d') . DIRECTORY_SEPARATOR;
+            if (!is_writable($path)) {
+                if (!file_exists($path)) {
+                    mkdir($path, 0777, true);
+                }
+            }
+        }
+        $filename = strtolower(strtr($filename, array(
+            ' ' => '',
+            DIRECTORY_SEPARATOR => ''
+        )));
+        $filename = $path . $filename . '.log';
+        return $filename;
+    }
+
+    /**
+     * 获取日志路径,如果不存在或者不可写则使用tmp路径
+     *
+     * @param
+     *            $filename
+     * @return string
+     */
+    private static function getBigdataLogPath()
+    {
+        $path = '/www/privdata/api.makeup.meitu.com/bigdata/' . date('Ymd') . DIRECTORY_SEPARATOR;
+        if ((!file_exists($path))) {
+            mkdir($path, 0777, true);
+        }
+        $filename = date("H") . '_' . self::getServerIp();
+        $filename = $path . $filename . '.log';
+        return $filename;
+    }
+
+
+    /**
+     * 获取客户端Ip
+     *
+     * @return string|int
+     */
+    private static function getClientIp()
+    {
+        static $ip;
+        if ($ip) {
+            return $ip;
+        }
+        if (isset($_SERVER["HTTP_X_FORWARDED_FOR"])) {
+            $ip = $_SERVER["HTTP_X_FORWARDED_FOR"];
+        } elseif (isset($_SERVER["HTTP_CLIENT_IP"])) {
+            $ip = $_SERVER["HTTP_CLIENT_IP"];
+        } elseif (isset($_SERVER["HTTP_CLIENTIP"])) {
+            $ip = $_SERVER["HTTP_CLIENTIP"];
+        } elseif (isset($_SERVER["HTTP_X_REAL_IP"])) {
+            $ip = $_SERVER["HTTP_X_REAL_IP"];
+        } elseif (isset($_SERVER["REMOTE_ADDR"])) {
+            $ip = $_SERVER["REMOTE_ADDR"];
+        } else {
+            $ip = '127.0.0.1';
+        }
+        $pos = strpos($ip, '|');
+        if ($pos) {
+            $ip = substr($ip, 0, $pos);
+        }
+        $ip = trim($ip);
+        return $ip;
+    }
+
+    /**
+     * 获取服务器IP
+     *
+     * @return string
+     */
+    private static function getServerIp()
+    {
+        static $ip;
+        if ($ip) {
+            return $ip;
+        }
+        if (isset($_SERVER['SERVER_ADDR'])) {
+            $ip = $_SERVER['SERVER_ADDR'];
+        } else {
+            $cmd = "/sbin/ifconfig|grep 'inet addr'|awk '{print $2}'|awk -F':' '{print $2}'|awk '$1 !~ /127.0.0.1/{print}'|tail -n 1";
+            $handle = popen($cmd, 'r');
+            $ip = trim(fread($handle, 1024));
+            pclose($handle);
+        }
+        return $ip;
+    }
+}

+ 85 - 0
Frame/Model.php

@@ -0,0 +1,85 @@
+<?php
+
+namespace Heanup\Frame;
+
+class Model
+{
+
+    /**
+     * 创建单例模式
+     * @return Model
+     */
+    public static function getInstance()
+    {
+        static $pool;
+        $class = get_called_class();
+        if (!isset($pool[$class])) {
+            $pool[$class] = new $class();
+        }
+        return $pool[$class];
+    }
+
+    /**
+     * 强大的批量获取方法,从数据库或者缓存中读取数据
+     * 1. 先读取缓存
+     * 2. 然后从数据库中读取缓存没有命中的
+     * 3. 将没有命中的写入缓存
+     * 4. 返回结果
+     * @param $idArray
+     * @param string $databaseClass 完整数据库类路径, 要求拥有getBatch方法
+     * @param string $cacheClass 完整缓存类路径, 要求拥有 getBatch, setBatch 方法
+     * @return bool
+     */
+    public static function getBatchFromDatabaseOrCache($idArray, $databaseClass = '', $cacheClass = '')
+    {
+        $cachedData = array();
+        // 从缓存中读取数据
+        if ($cacheClass && class_exists($cacheClass) && method_exists($cacheClass, 'getBatch')) {
+            $cachedData = $cacheClass::getBatch($idArray);
+        }
+        // 取出剩余的ID
+        $idLeft = array();
+        foreach ($idArray as $id) {
+            if (isset($cachedData[$id]) && $cachedData[$id]) {
+                continue;
+            }
+            $idLeft[] = $id;
+        }
+        // 从数据库取出内容
+        $databaseData = array();
+        if ($idLeft && $databaseClass && class_exists($databaseClass) && method_exists($databaseClass, 'getBatch')) {
+            $databaseData = $databaseClass::getBatch($idLeft);
+        }
+        // 设置从数据库读取的缓存
+        if ($databaseData && $cacheClass && class_exists($cacheClass) && method_exists($cacheClass, 'setBatch')) {
+            $cacheClass::setBatch($databaseData);
+        }
+        // 合并数据
+        $data = array();
+        foreach ($idArray as $id) {
+            if (isset($cachedData[$id]) && $cachedData[$id]) {
+                $data[$id] = $cachedData[$id];
+            } elseif (isset($databaseData[$id]) && $databaseData[$id]) {
+                $data[$id] = $databaseData[$id];
+            }
+        }
+        return $data;
+    }
+
+    /**
+     * 检查状态位, 如果code是布尔, 则直接返回
+     * @param $stat
+     * @param $code
+     * @return int
+     */
+    protected static function checkStat($stat, $code)
+    {
+        if (is_bool($code)) {
+            return $code;
+        }
+        $code = intval($code);
+        $result = $stat & $code;
+        return $result;
+    }
+
+}

+ 59 - 0
Frame/Object.php

@@ -0,0 +1,59 @@
+<?php
+
+namespace Heanup\Frame;
+
+/**
+ * 数据结构父类
+ */
+class Object
+{
+
+    private $_data = array ();
+
+    public function __construct(Array $data = array ())
+    {
+        foreach ($data as $name => $value) {
+            $this->_data[$name] = $value;
+        }
+    }
+
+    public function __isset($name)
+    {
+        return isset($this->_data[$name]);
+    }
+
+    public function __set($name, $value)
+    {
+        if (isset($this->_data[$name])) {
+            $this->_data[$name] = $value;
+        }
+    }
+
+    public function __get($name)
+    {
+        if (isset($this->_data[$name])) {
+            return $this->_data[$name];
+        }
+    }
+
+    public function toArray()
+    {
+        return $this->_data;
+    }
+
+    public function __toString()
+    {
+        return json_encode($this->_data);
+    }
+
+    public function __sleep()
+    {
+        return $this->_data;
+    }
+
+    public function __wakeUp()
+    {
+        // ;
+    }
+
+}

+ 56 - 0
Frame/Package.php

@@ -0,0 +1,56 @@
+<?php
+
+namespace Heanup\Frame;
+
+/**
+ * 数据层父类
+ * Class Package
+ * @package Heanup\Frame
+ */
+class Package
+{
+    const PREFIX = '';
+
+    /**
+     * 创建单例模式
+     * @return Package
+     */
+    public static function getInstance()
+    {
+        static $pool;
+        $class = get_called_class();
+        if (!isset($pool[$class])) {
+            $pool[$class] = new $class();
+        }
+        return $pool[$class];
+    }
+
+    /**
+     * 获取带前缀的key
+     * @param $id
+     * @return string
+     */
+    protected static function getKey($id = 0)
+    {
+        $class = get_called_class();
+        if (!$class::PREFIX) {
+            $prefix = substr(md5($class), 0, 16);
+        } else {
+            $prefix = $class::PREFIX;
+        }
+        $prefix = strtolower($prefix);
+        $key = trim($prefix, ':') . ':' . $id;
+        return $key;
+    }
+
+    /**
+     * 获取发号器ID
+     * @return mixed
+     * @throws \Exception
+     */
+    protected static function getTicket()
+    {
+        return \Heanup\Frame\Library\Ticket::getTicket();
+    }
+
+}

+ 11 - 0
Frame/Package/Cache.php

@@ -0,0 +1,11 @@
+<?php
+
+namespace Heanup\Frame\Package;
+
+/**
+ * 缓存
+ */
+class Cache extends \Heanup\Frame\Package
+{
+
+}

+ 152 - 0
Frame/Package/Cache/Memcache.php

@@ -0,0 +1,152 @@
+<?php
+
+namespace Heanup\Frame\Package\Cache;
+
+use Heanup\Frame\Request;
+
+/**
+ * 使用Memcache做缓存
+ */
+class Memcache extends \Heanup\Frame\Package\Cache
+{
+    const CONFIG_CLASS = 'Memcache\\Core';
+    const PREFIX = '';
+    const EXPIRE = 259200;
+
+    /**
+     * 单个读取
+     * @param $id
+     * @return array
+     */
+    public static function get($id)
+    {
+        if(Request::isCacheEnabled() == false){
+            return array();
+        }
+        $key = self::getKey($id);
+        $result = self::getConnection()->get($key);
+        return $result;
+    }
+
+    /**
+     * 批量读取,如果没有数据,则不在返回的列表中
+     * @param array $idList
+     * @return array
+     */
+    public static function getBatch(Array $idList)
+    {
+        if(Request::isCacheEnabled() == false){
+            return array();
+        }
+        $keyList = array();
+        foreach ($idList as $id) {
+            $keyList[$id] = self::getKey($id);
+        }
+        $result = self::getConnection()->getMulti($keyList);
+        $data = array();
+        if ($result) {
+            $keyList = array_flip($keyList);
+            foreach ($result as $k => $v) {
+                $k = $keyList[$k];
+                $data[$k] = $v;
+            }
+        }
+        unset($result);
+        unset($idList);
+        unset($keyList);
+        return $data;
+    }
+
+    /**
+     * @param $id
+     * @param $data
+     * @param expire
+     * @return bool
+     */
+    public static function set($id, $data, $expire = null)
+    {
+        $key = self::getKey($id);
+        $class = get_called_class();
+        if(is_null($expire)){
+            $expire = $class::EXPIRE;
+        }
+        $expire = time() + intval($expire);
+        $result = self::getConnection()->set($key, $data, $expire);
+        return $result;
+    }
+
+    /**
+     * 批量设置
+     * @param array $data
+     * @param $expire
+     * @return bool
+     */
+    public static function setBatch($data, $expire = null)
+    {
+        $dataList = array();
+        foreach ($data as $k => $v) {
+            $k = self::getKey($k);
+            $dataList[$k] = $v;
+        }
+        $class = get_called_class();
+        if(is_null($expire)){
+            $expire = $class::EXPIRE;
+        }
+        $expire = time() + intval($expire);
+        $result = self::getConnection()->setMulti($dataList, $expire);
+        unset($data);
+        unset($dataList);
+        return $result;
+    }
+
+    /**
+     * 增加
+     * @param $id
+     * @param int $value
+     * @return int
+     */
+    public static function increase($id, $value = 1)
+    {
+        $key = self::getKey($id);
+        $value = abs($value);
+        return self::getConnection()->increment($key, $value);
+    }
+
+    /**
+     * 减少
+     * @param $id
+     * @param int $value
+     * @return int
+     */
+    public static function decrease($id, $value = 1)
+    {
+        $key = self::getKey($id);
+        $value = abs($value);
+        return self::getConnection()->decrement($key, $value);
+    }
+
+    /**
+     * 删除
+     * @param $id
+     * @return bool
+     */
+    public static function delete($id)
+    {
+        $key = self::getKey($id);
+        return self::getConnection()->delete($key);
+    }
+
+    /**
+     * 获取连接
+     * @return \Memcached
+     */
+    protected static function getConnection()
+    {
+        $class = get_called_class();
+        $configClass = $class::CONFIG_CLASS;
+        $memcached = new \Heanup\Frame\Library\Memcache($configClass);
+        $connection = $memcached->getConnection();
+        return $connection;
+    }
+
+}

+ 179 - 0
Frame/Package/Cache/Redis.php

@@ -0,0 +1,179 @@
+<?php
+
+namespace Heanup\Frame\Package\Cache;
+
+/**
+ * 使用Redis做缓存
+ */
+class Redis extends \Heanup\Frame\Package\Cache
+{
+    const CONFIG_CLASS = 'Redis\\Core';
+    const PREFIX = '';
+    const EXPIRE = 0; // 3 天
+
+    /**
+     * 单个读取
+     * @param $id
+     * @return array
+     */
+    public static function get($id)
+    {
+        $key = self::getKey($id);
+        try {
+            $result = self::getConnection()->get($key);
+        } catch (\Exception $ex) {
+            $result = null;
+        }
+        return $result;
+    }
+
+    /**
+     * 批量读取,如果没有数据,则对应的value为空
+     * @param array $idList
+     * @return array
+     */
+    public static function getBatch(Array $idList)
+    {
+        $keyList = array();
+        foreach ($idList as $id) {
+            $keyList[$id] = self::getKey($id);
+        }
+        try {
+            $result = self::getConnection()->mget($keyList);
+        } catch (\Exception $ex) {
+            $result = null;
+        }
+        $data = array();
+        if ($result) {
+            $i = 0;
+            foreach ($keyList as $k => $v) {
+                $data[$k] = $result[$i];
+                $i++;
+            }
+        }
+        unset($result);
+        unset($idList);
+        unset($keyList);
+        return $data;
+    }
+
+    /**
+     * @param $id
+     * @param int $data
+     * @return bool
+     */
+    public static function set($id, $data)
+    {
+        if (is_array($data)) {
+            $data = serialize($data);
+        }
+        $key = self::getKey($id);
+        $class = get_called_class();
+        try {
+            if ($class::EXPIRE) {
+                $result = self::getConnection(true)->setex($key, $class::EXPIRE, $data);
+            } else {
+                $result = self::getConnection(true)->set($key, $data);
+            }
+        } catch (\Exception $ex) {
+            $result = false;
+        }
+        return $result;
+    }
+
+    /**
+     * 批量设置
+     * @param array $data
+     * @return bool
+     */
+    public static function setBatch($data)
+    {
+        $dataList = array();
+        foreach ($data as $k => $v) {
+            $k = self::getKey($k);
+            $v = is_array($v) ? serialize($v) : $v;
+            $dataList[$k] = $v;
+        }
+        $class = get_called_class();
+        try {
+            $result = self::getConnection(true)->mset($dataList);
+            // 设置过期时间
+            if ($class::EXPIRE) {
+                foreach ($dataList as $k => $v) {
+                    self::getConnection()->expire($k, $class::EXPIRE);
+                }
+            }
+        } catch (\Exception $ex) {
+            $result = false;
+        }
+        unset($data);
+        unset($dataList);
+        return $result;
+    }
+
+    /**
+     * 加1
+     * @param $id
+     * @param int $value
+     * @return int
+     */
+    public static function increase($id, $value = 1)
+    {
+        $key = self::getKey($id);
+        $value = abs($value);
+        try {
+            $result = self::getConnection(true)->incr($key, $value);
+        } catch (\Exception $ex) {
+            $result = false;
+        }
+        return $result;
+    }
+
+    /**
+     * @param $id
+     * @param int $value
+     * @return int
+     */
+    public static function decrease($id, $value = 1)
+    {
+        $key = self::getKey($id);
+        $value = abs($value);
+        try {
+            $result = self::getConnection(true)->decr($key, $value);
+        } catch (\Exception $ex) {
+            $result = false;
+        }
+        return $result;
+    }
+
+    /**
+     * 删除
+     * @param $id
+     * @return bool
+     */
+    public static function delete($id)
+    {
+        $key = self::getKey($id);
+        try {
+            $result = self::getConnection(true)->delete($key);
+        } catch (\Exception $ex) {
+            $result = false;
+        }
+        return $result;
+    }
+
+    /**
+     * 获取连接池
+     * @param bool $isMaster
+     * @return \Redis
+     */
+    protected static function getConnection($isMaster = false)
+    {
+        $class = get_called_class();
+        $configClass = $class::CONFIG_CLASS;
+        $redis = new \Heanup\Frame\Library\Redis($configClass);
+        $connection = $redis->getConnection($isMaster);
+        return $connection;
+    }
+
+}

+ 11 - 0
Frame/Package/Counter.php

@@ -0,0 +1,11 @@
+<?php
+
+namespace Heanup\Frame\Package;
+
+/**
+ * 计数器类
+ */
+class Counter extends \Heanup\Frame\Package
+{
+
+}

+ 148 - 0
Frame/Package/Counter/Hash.php

@@ -0,0 +1,148 @@
+<?php
+
+namespace Heanup\Frame\Package\Counter;
+
+/**
+ * 计数器类,使用hash数据结构
+ * redis连不上server会抛出异常!!
+ */
+class Hash extends \Heanup\Frame\Package\Counter
+{
+    const CONFIG_CLASS = 'Redis\\Counter';
+    const PREFIX = '';
+    const EXPIRE = 2592000; // 30 天
+
+    protected static $hashKeyList = array();
+
+    /**
+     * 批量获取一系列的id
+     * @param $idList
+     * @return array
+     */
+    public static function hBatchGetAll($idList)
+    {
+        $data = array();
+        foreach ($idList as $id) {
+            $data[$id] = self::hGetAll($id);
+        }
+        return $data;
+    }
+
+    /**
+     * 读取单个hash
+     * @param $id
+     * @return array
+     */
+    public static function hGetAll($id)
+    {
+        $key = self::getKey($id);
+        try {
+            $result = self::getConnection()->hMGet($key, self::getHashKeyList());
+            foreach ($result as $k => $v) {
+                $result[$k] = intval($v);
+            }
+        } catch (\Exception $ex) {
+            $result = null;
+        }
+        return $result;
+    }
+
+    /**
+     * @param $id
+     * @param $hashKey
+     * @param int $value
+     * @return bool
+     */
+    public static function hSet($id, $hashKey, $value)
+    {
+        $key = self::getKey($id);
+        try {
+            $result = self::getConnection(true)->hSet($key, $hashKey, $value);
+        } catch (\Exception $ex) {
+            $result = false;
+        }
+        return $result;
+    }
+
+    /**
+     * 加1
+     * @param $id
+     * @param $hashKey
+     * @param int $value 负数时减少
+     * @return int
+     */
+    public static function hIncrBy($id, $hashKey, $value = 1)
+    {
+        $key = self::getKey($id);
+        try {
+            $result = self::getConnection(true)->hIncrBy($key, $hashKey, $value);
+        } catch (\Exception $ex) {
+            $result = false;
+        }
+        return $result;
+    }
+
+    /**
+     * 删除
+     * @param $id
+     * @return bool
+     */
+    public static function delete($id)
+    {
+        $key = self::getKey($id);
+        try {
+            $result = self::getConnection(true)->delete($key);
+        } catch (\Exception $ex) {
+            $result = false;
+        }
+        return $result;
+    }
+
+    /**
+     * 设置过期时间
+     * @param $id
+     * @param $expire
+     * @return mixed
+     */
+    public static function setExpire($id, $expire = null)
+    {
+        if (is_null($expire)) {
+            $class = get_called_class();
+            $expire = $class::EXPIRE;
+        } else {
+            $expire = intval($expire);
+        }
+        $key = self::getKey($id);
+        try {
+            $result = self::getConnection(true)->expire($key, $expire);
+        } catch (\Exception $ex) {
+            $result = false;
+        }
+        return $result;
+    }
+
+    /**
+     * 获取hashKey列表
+     * @return mixed
+     */
+    protected static function getHashKeyList()
+    {
+        $class = get_called_class();
+        $hashKeyList = $class::$hashKeyList;
+        return $hashKeyList;
+    }
+
+    /**
+     * 获取连接池
+     * @param bool $isMaster
+     * @return \Redis
+     */
+    protected static function getConnection($isMaster = false)
+    {
+        $class = get_called_class();
+        $configClass = $class::CONFIG_CLASS;
+        $redis = new \Heanup\Frame\Library\Redis($configClass);
+        $connection = $redis->getConnection($isMaster);
+        return $connection;
+    }
+}

+ 141 - 0
Frame/Package/Counter/Single.php

@@ -0,0 +1,141 @@
+<?php
+
+namespace Heanup\Frame\Package\Counter;
+
+/**
+ * 计数器类
+ * redis连不上server会抛出异常!!
+ */
+class Single extends \Heanup\Frame\Package\Counter
+{
+    const CONFIG_CLASS = 'Redis\\Counter';
+    const PREFIX = '';
+    const EXPIRE = 2592000; // 30 天
+
+    /**
+     * 单个读取
+     * @param $id
+     * @return array
+     */
+    public static function get($id)
+    {
+        $key = self::getKey($id);
+        try {
+            $result = self::getConnection()->get($key);
+            $result = intval($result);
+        } catch (\Exception $ex) {
+            $result = null;
+        }
+        return $result;
+    }
+
+    /**
+     * 批量获取
+     * @param array $idList
+     * @return array|null
+     */
+    public static function mGet(Array $idList)
+    {
+        $keyList = array();
+        foreach ($idList as $id) {
+            $k = self::getKey($id);
+            $keyList[$k] = $id;
+        }
+        try {
+            $result = array();
+            $data = self::getConnection()->mget(array_keys($keyList));
+            $i = 0;
+            foreach ($keyList as $k => $v) {
+                $result[$v] = intval($data[$i]);
+                $i++;
+            }
+        } catch (\Exception $ex) {
+            $result = null;
+        }
+        return $result;
+    }
+
+    /**
+     * @param $id
+     * @param int $value
+     * @return bool
+     */
+    public static function set($id, $value)
+    {
+        $key = self::getKey($id);
+        try {
+            $result = self::getConnection(true)->set($key, $value);
+        } catch (\Exception $ex) {
+            $result = false;
+        }
+        return $result;
+    }
+
+    /**
+     * 加1
+     * @param $id
+     * @param int $value
+     * @return int
+     */
+    public static function increase($id, $value = 1)
+    {
+        $key = self::getKey($id);
+        $value = abs($value);
+        try {
+            $result = self::getConnection(true)->incr($key, $value);
+        } catch (\Exception $ex) {
+            $result = false;
+        }
+        return $result;
+    }
+
+    /**
+     * @param $id , 如果值小于0则设为0
+     * @param int $value
+     * @return int
+     */
+    public static function decrease($id, $value = 1)
+    {
+        $key = self::getKey($id);
+        $value = abs($value);
+        try {
+            $result = self::getConnection(true)->decr($key, $value);
+            if ($result < 0) {
+                $result = self::getConnection(true)->set($key, 0);
+            }
+        } catch (\Exception $ex) {
+            $result = false;
+        }
+        return $result;
+    }
+
+    /**
+     * 删除
+     * @param $id
+     * @return bool
+     */
+    public static function delete($id)
+    {
+        $key = self::getKey($id);
+        try {
+            $result = self::getConnection(true)->delete($key);
+        } catch (\Exception $ex) {
+            $result = false;
+        }
+        return $result;
+    }
+
+    /**
+     * 获取连接池
+     * @param bool $isMaster
+     * @return \Redis
+     */
+    protected static function getConnection($isMaster = false)
+    {
+        $class = get_called_class();
+        $configClass = $class::CONFIG_CLASS;
+        $redis = new \Heanup\Frame\Library\Redis($configClass);
+        $connection = $redis->getConnection($isMaster);
+        return $connection;
+    }
+}

+ 13 - 0
Frame/Package/Database.php

@@ -0,0 +1,13 @@
+<?php
+
+namespace Heanup\Frame\Package;
+
+/**
+ * 数据库操作类
+ * 一种是单表模式,一种是分表模式
+ */
+class Database extends \Heanup\Frame\Package
+{
+
+
+}

+ 275 - 0
Frame/Package/Database/Common.php

@@ -0,0 +1,275 @@
+<?php
+namespace Heanup\Frame\Package\Database;
+
+/**
+ * 数据库操作类
+ * 单表模式
+ */
+class Common extends \Heanup\Frame\Package\Database\Single
+{
+
+    /**
+     * 通过ID获取单条记录
+     * 
+     * @param string $id            
+     * @return array
+     */
+    public function getOne($id)
+    {
+        return self::getLine($id);
+    }
+
+    /**
+     * 通过字段条件获取单条记录
+     * 
+     * @param array $where
+     *            查询条件
+     * @param array $fields
+     *            要获取的字段信息
+     * @return array
+     */
+    public function getOneByField($where = array(), $fields = array())
+    {
+        $list = $this->select($fields, $where, '', 1);
+        return ! empty($list) ? $list[0] : null;
+    }
+
+    /**
+     * 通过字段条件获取排序后的限制记录
+     * 
+     * @param array $where
+     *            查询条件
+     * @param array $fields
+     *            要获取的字段信息
+     * @param int $limit
+     *            限制记录数
+     * @param int $offset
+     *            查询偏移量
+     * @param array $orderBy
+     *            排序 array("field1"=>1,"field2"=>0); 0升序1降序
+     * @return array
+     */
+    public function getList($where = array(), $fields = array(), $limit = null, $offset = null, $orderBy = array())
+    {
+        return $this->select($fields, $where, '', $limit, $offset, $orderBy);
+    }
+
+    /**
+     * 复杂语句的计数查询
+     * 
+     * @param array $where            
+     * @return int
+     */
+    public function countByMuti($where = array())
+    {
+        $arr = $this->buildWhere($where);
+        return $this->count($arr['where'], $arr['whereString']);
+    }
+
+    /**
+     * 复杂的查询语句
+     * 
+     * @param array $where   <br>   
+     *              1.'key'=>$value 等于的查询条件<br>
+     *              2.'key'=>array( '_logic'=>'比较运算符或like' , 'value'=>$value ) '<','<=','>','>=','!=','LIKE'的查询条件<br>
+     *              3.'key'=>array($value1,$value2,$value3,...) IN范围查询<br>
+     *              4.'key'=>array( array( '_logic'=>'比较运算符或like' , 'value'=>$value ) ,'key'=>$value ) 一个字段多个查询条件<br>      
+     * @param array $fields            
+     * @param int $limit            
+     * @param int $offset            
+     * @param array $orderBy            
+     * @return array
+     */
+    public function getListByMuti($where = array(), $fields = array(), $limit = null, $offset = null, $orderBy = array())
+    {
+        $arr = $this->buildWhere($where);
+        return $this->select($fields, $arr['where'], $arr['whereString'], $limit, $offset, $orderBy);
+    }
+    /**
+     * 复杂的查询语句
+     *
+     * @param array $where   <br>
+     *              1.'key'=>$value 等于的查询条件<br>
+     *              2.'key'=>array( '_logic'=>'比较运算符或like' , 'value'=>$value ) '<','<=','>','>=','!=','LIKE'的查询条件<br>
+     *              3.'key'=>array($value1,$value2,$value3,...) IN范围查询<br>
+     *              4.'key'=>array( array( '_logic'=>'比较运算符或like' , 'value'=>$value ) ,'key'=>$value ) 一个字段多个查询条件<br>
+     * @param array $fields
+     * @param int $limit
+     * @param int $offset
+     * @param array $orderBy
+     * @return array
+     */
+    public function getAllByMutiple($where = array(), $fields = array(), $orderBy = array(),$limit=null,$offset=null)
+    {
+        $arr = $this->buildWhere($where);
+        return $this->select($fields, $arr['where'], $arr['whereString'], $limit, $offset, $orderBy);
+    }
+    /**
+     * 更新(基于ID)
+     * @param int $id
+     * @param array $data
+     * @return int
+     */
+    public function update($id, $data)
+    {
+        return parent::update($data, array('id'=>$id));
+    }
+
+    /**
+     * 复杂的更新语句
+     * 
+     * @param array $set     
+     *            1.'key'=>$value 等于参数设置
+     *            2.'key'=>array( '_logic'=>'运算符','value'=>$value ) 含 '+', '-','*','/','%'运算符号的参数设置       
+     * @param array $where            
+     * @return bool|int
+     */
+    public function updateByMuti($set = array(), $where = array())
+    {
+        $wArr = $this->buildWhere($where);
+        $sArr = $this->buildSet($set);
+        $sql = " UPDATE `" . $this->tableName . "` SET " . $sArr['setString'] . ' WHERE ' . $wArr['whereString'];
+        $prepared = array_merge($wArr['where'], $sArr['set']);
+        return $this->write($sql, $prepared);
+    }
+
+    /**
+     * 将复杂的查询条件转换为PDO类可解读的参数绑定方式
+     * 
+     * @param array $map
+     *            条件
+     *            $map允许的键值格式(所有的条件是AND组合的):
+     *            1.'key'=>$value 等于的查询条件
+     *            2.'key'=>array( '_logic'=>'比较运算符或like' , 'value'=>$value ) '<','<=','>','>=','!=','LIKE'的查询条件
+     *            3.'key'=>array($value1,$value2,$value3,...) IN范围查询
+     *            4.'key'=>array( array( '_logic'=>'比较运算符或like' , 'value'=>$value ) ,'key'=>$value ) 一个字段多个查询条件
+     * @return array
+     */
+    public function buildWhere($map)
+    {
+        $fhArr = array(
+            '<',
+            '<=',
+            '>',
+            '>=',
+            '!=',
+            'LIKE'
+        );
+        $where = array();
+        $whereString = array();
+        foreach ($map as $k => $v) {
+            if (is_array($v)) {
+                if (! empty($v['_logic'])) {
+                    $v['_logic'] = strtoupper($v['_logic']);
+                    if (in_array($v['_logic'], $fhArr)) {
+                        $whereString[] = self::escapeKey($k) . ' ' . $v['_logic'] . ' ' . self::replaceKey($k, 'wh_') . '';
+                        $where[self::replaceKey($k, 'wh_')] = $v['value'];
+                    }
+                } else {
+                    $mixArr = array();
+                    foreach ($v as $ke => $va) {
+                        if (is_array($va)) {
+                            if (! empty($va['_logic'])) {
+                                $va['_logic'] = strtoupper($va['_logic']);
+                                if (in_array($va['_logic'], $fhArr)) {
+                                    $whereString[] = self::escapeKey($k) . ' ' . $va['_logic'] . ' ' . self::replaceKey($k . $ke, 'wh_') . '';
+                                    $where[self::replaceKey($k . $ke, 'wh_')] = $va['value'];
+                                }
+                            }
+                        } else {
+                            $mixArr[] = $va;
+                        }
+                    }
+                    if (! empty($mixArr)) {
+                        $string = self::escapeKey($k) . ' IN ( ';
+                        $first = '';
+                        foreach ($mixArr as $ke => $va) {
+                            $string .= $first . self::replaceKey($k . $ke, 'wh_');
+                            $where[self::replaceKey($k . $ke, 'wh_')] = $va;
+                            $first = ',';
+                        }
+                        $string .= ' ) ';
+                        $whereString[] = $string;
+                    }
+                }
+            } else {
+                $whereString[] = self::escapeKey($k) . ' = ' . self::replaceKey($k, 'wh_');
+                $where[self::replaceKey($k, 'wh_')] = $v;
+            }
+        }
+        return array(
+            'where' => $where,
+            'whereString' => implode(' AND ', $whereString)
+        );
+    }
+
+    /**
+     * 将复杂的更新条件转换为PDO类可解读的参数绑定方式
+     * 
+     * @param array $map
+     *            参数设置
+     *            $map设置参数的格式:
+     *            1.'key'=>$value 等于参数设置
+     *            2.'key'=>array( '_logic'=>'运算符','value'=>$value ) 含 '+', '-','*','/','%'运算符号的参数设置
+     * @return array()
+     */
+    public function buildSet($map)
+    {
+        if (! is_array($map) || empty($map)) {
+            return '';
+        }
+        $setString = array();
+        $set = array();
+        foreach ($map as $k => $v) {
+            if (is_array($v)) {
+                if (! empty($v['_logic'])) {
+                    if (in_array($v['_logic'], array(
+                        '+',
+                        '-',
+                        '*',
+                        '/',
+                        '%'
+                    ))) {
+                        $setString[] = self::escapeKey($k) . ' = ' . self::escapeKey($k) . ' ' . $v['_logic'] . ' ' . self::replaceKey($k, 'st_');
+                        $set[self::replaceKey($k, 'st_')] = $v['value'];
+                    }
+                }
+            } else {
+                $setString[] = self::escapeKey($k) . '=' . self::replaceKey($k, 'st_');
+                $set[self::replaceKey($k, 'st_')] = $v;
+            }
+        }
+        return array(
+            'set' => $set,
+            'setString' => implode(' , ', $setString)
+        );
+    }
+
+    /**
+     * 安全过滤key
+     * 
+     * @param string $key            
+     * @return string
+     */
+    protected function escapeKey($key)
+    {
+        $key = str_replace(array(
+            '`',
+            ' '
+        ), '', $key);
+        return ' `' . addslashes(stripslashes($key)) . '` ';
+    }
+
+    /**
+     * 参数绑定value
+     * 
+     * @param string $key            
+     * @return string
+     */
+    protected function replaceKey($key, $prefix = '')
+    {
+        return ':' . $prefix . $key;
+    }
+
+  
+}

+ 342 - 0
Frame/Package/Database/Sharding.php

@@ -0,0 +1,342 @@
+<?php
+
+namespace Heanup\Frame\Package\Database;
+
+/**
+ * 数据库操作类
+ * 分表模式
+ */
+class Sharding extends \Heanup\Frame\Package\Database
+{
+    protected $configClass = 'MySQL\\Core';
+    protected $configSection = null;
+    protected $tableName = '';
+    protected $fields = array();
+    protected $primaryKey = 'id';
+
+    /**
+     * 获取计数
+     * @param Array $whereParam
+     * @param string $whereString
+     * @return int
+     */
+    public function count($whereParam, $whereString = '')
+    {
+        $query = $this->getQuery()->setWhere($whereParam, $whereString)->count();
+        $result = $this->getConnection()->read($query->getSQL(), $query->getData());
+        if (!$result) {
+            return 0;
+        }
+        $result = current($result);
+        return intval($result['count']);
+    }
+
+    /**
+     * 获取列表
+     * @param array $fields
+     * @param array $whereParam
+     * @param string $whereString
+     * @param int $limit
+     * @param int $offset
+     * @param array $orderBy
+     * @return array
+     */
+    public function select(
+        $fields = array(),
+        $whereParam = array(),
+        $whereString = '',
+        $limit = 10,
+        $offset = 0,
+        $orderBy = array())
+    {
+        $query = $this->getQuery()
+            ->setField($fields)
+            ->setWhere($whereParam, $whereString)
+            ->setLimit($limit, $offset)
+            ->setOrderBy($orderBy)
+            ->select();
+        $result = $this->getConnection()->read($query->getSQL(), $query->getData());
+        if (!$result) {
+            return $result;
+        }
+        $data = array();
+        foreach ($result as $row) {
+            $data[$row[$this->primaryKey]] = $row;
+        }
+        unset($result);
+        return $data;
+    }
+
+    /**
+     * 获取列表数据,简单版
+     * @param array $whereParam
+     * @param int $limit
+     * @return array
+     */
+    public function getList($whereParam = array(), $limit = 10)
+    {
+        return $this->select(array(), $whereParam, '', $limit, 0, array());
+    }
+
+    /**
+     * 获取列表ID信息
+     * @param string $field
+     * @param array $whereParam
+     * @param $orderBy
+     * @param $limit
+     * @return array
+     */
+    public function getListId($field = '', $whereParam = array(), $orderBy = array(), $limit = 2000)
+    {
+        $query = self::getQuery()
+            ->setField($field)
+            ->setWhere($whereParam)
+            ->setLimit($limit)
+            ->setOrderBy($orderBy)
+            ->select();
+        $result = $this->getConnection()->read($query->getSQL(), $query->getData());
+        if (!$result) {
+            return $result;
+        }
+        $data = array();
+        foreach ($result as $row) {
+            $data[] = $row[$field];
+        }
+        unset($result);
+        return $data;
+    }
+
+    /**
+     * 获取单条记录
+     * @param array $whereParam
+     * @param string $whereString
+     * @param array $orderBy
+     * @return array;
+     */
+    public function getOne($whereParam = array(), $whereString = '', $orderBy = array())
+    {
+        $query = $this->getQuery()
+            ->setWhere($whereParam, $whereString)
+            ->setOrderBy($orderBy)
+            ->setLimit(1)
+            ->select();
+        $result = $this->getConnection()->read($query->getSQL(), $query->getData(), false, false);
+        return $result;
+    }
+
+    /**
+     * 根据主键获取单条记录
+     * @param int $id
+     * @return array
+     */
+    public function getLine($id)
+    {
+        $where = array($this->primaryKey => $id);
+        $query = $this->getQuery()->setWhere($where)->setLimit(1)->select();
+        $result = $this->getConnection()->read($query->getSQL(), $query->getData(), false, false);
+        return $result;
+    }
+
+    /**
+     * 批量根据主键查询
+     * @param $idList
+     * @return array|mixed
+     */
+    public function getBatch($idList)
+    {
+        $where = array($this->primaryKey => $idList);
+        $query = $this->getQuery()->setWhere($where)->select();
+        $result = $this->getConnection()->read($query->getSQL(), $query->getData());
+        if (!$result) {
+            return $result;
+        }
+        $data = array();
+        foreach ($result as $row) {
+            $data[$row[$this->primaryKey]] = $row;
+        }
+        unset($result);
+        return $data;
+    }
+
+    /**
+     * 创建记录
+     * @param $data
+     * @param bool 是否返回最后最后插入ID
+     * @return bool|int
+     */
+    public function insert($data, $returnLastInsertId = false)
+    {
+        $query = $this->getQuery()->setData($data)->insert();
+        $result = $this->getConnection()->write($query->getSQL(), $query->getData());
+        // 返回最后插入的id
+        if ($result && $returnLastInsertId) {
+            $result = $this->getConnection()->getLastInsertId();
+        }
+        return $result;
+    }
+
+    /**
+     * 批量插入
+     * $data = array(
+     *      array(
+     *          'id' => 10001,
+     *          'target_id' => 100,
+     *      ),
+     *      array(
+     *          'id' => 10002,
+     *          'target_id' => 100,
+     *      ),
+     * );
+     * @param $data
+     * @return bool|int
+     */
+    public function insertBatch($data)
+    {
+        $prefix = 'prepared_';
+        // 确定字段名
+        $fields = implode('`, `', array_keys(current($data)));
+        // 拼SQL和数据
+        $dataList = array();
+        $values = array();
+        $i = 0;
+        foreach ($data as $k => $v) {
+            foreach ($v as $x => $y) {
+                $key = $prefix . $i;
+                $dataList[$key] = $y;
+                $v[$x] = ':' . $key;
+                $i++;
+            }
+            $values[] = "(" . implode(", ", $v) . ")";
+        }
+        $values = implode(', ', $values);
+        $sql = "INSERT INTO `" . $this->tableName . "` (`$fields`) VALUES $values";
+        $result = $this->write($sql, $dataList);
+        return $result;
+    }
+
+    /**
+     * 更新记录
+     * @param $data
+     * @param $whereParam
+     * @param $whereString
+     * @return bool
+     */
+    public function update($data, $whereParam, $whereString = '')
+    {
+        $query = $this->getQuery()->setData($data)->setWhere($whereParam, $whereString)->update();
+        $result = $this->getConnection()->write($query->getSQL(), $query->getData());
+        return $result;
+    }
+
+    /**
+     * 替换输入
+     * @param $data
+     * @param $whereParam
+     * @param $whereString
+     * @return bool
+     */
+    public function replace($data, $whereParam, $whereString = '')
+    {
+        $query = $this->getQuery()->setData($data)->setWhere($whereParam, $whereString)->replace();
+        $result = $this->getConnection()->write($query->getSQL(), $query->getData());
+        return $result;
+    }
+
+    /**
+     * 删除逻辑
+     * @param $whereParam
+     * @param $whereString
+     * @return bool
+     */
+    public function delete($whereParam, $whereString = '')
+    {
+        $query = $this->getQuery()->setWhere($whereParam, $whereString)->delete();
+        $result = $this->getConnection()->write($query->getSQL(), $query->getData());
+        return $result;
+    }
+
+
+    /**
+     * 获取一个新的SQL生成器
+     * @return \Heanup\Frame\Library\MySQL\Query
+     */
+    protected function getQuery()
+    {
+        return \Heanup\Frame\Library\MySQL\Query::newInstance()
+            ->setTableName($this->tableName)
+            ->setField($this->fields)
+            ->setPrimaryKey($this->primaryKey);
+    }
+
+    /**
+     * 读取
+     * @param $sql
+     * @param $data
+     * @param bool $isMaster
+     * @return mixed|Array
+     */
+    protected function read($sql, $data, $isMaster = false)
+    {
+        $result = $this->getConnection()->read($sql, $data, $isMaster);
+        return $result;
+    }
+
+    /**
+     * 写入
+     * @param $sql
+     * @param $data
+     * @return bool|int
+     */
+    protected function write($sql, $data)
+    {
+        $result = $this->getConnection()->write($sql, $data);
+        return $result;
+    }
+
+    /**
+     * 获取连接池
+     * @return \Heanup\Frame\Library\MySQL
+     */
+    protected function getConnection()
+    {
+        return new \Heanup\Frame\Library\MySQL($this->configClass, $this->configSection);
+    }
+    /**
+     * 根据数值来确定分表-分库方法
+     * 1.
+     * 可以自定义分表-分库的模板前缀$tbl变量
+     * 2. 可以自定义截取长度
+     * 3. 一般可以根据用户UID来获取分表或者分库
+     *
+     * @param int $num
+     *            数值
+     * @param string $tbl
+     *            模板前缀
+     * @param int $default
+     *            默认截取长度
+     */
+    protected function num_identify($num, $tbl, $default = 1)
+    {
+        $num = (string) $num;
+        $len = strlen($num);
+        if ($len >= $default)
+            $str = substr($num, $len - $default, $default);
+        else
+            $str = str_pad($num, $default, '0', STR_PAD_LEFT);
+        return $tbl . '_' . $str;
+    }
+    /**
+     * 求余数的方式获取分表-分库方法
+     * 1. 求余方式余数比较少,适合小型的分表法
+     * 2. 可以自定义求余除数
+     * Dao中使用方法:$this->dao->db->fmod_identify($num, $tbl, $default = 7)
+     * @param int $num
+     * @param string $tbl
+     * @param int $default
+     * @return
+     */
+    public function fmod_identify($num, $tbl, $default = 7) {
+        return $tbl . '_' . fmod($num/$default);
+    }
+
+}

+ 421 - 0
Frame/Package/Database/Single.php

@@ -0,0 +1,421 @@
+<?php
+
+namespace Heanup\Frame\Package\Database;
+use Heanup\Frame\Filter;
+
+/**
+ * 数据库操作类
+ * 单表模式
+ */
+class Single extends \Heanup\Frame\Package\Database
+{
+    const CONFIG_CLASS = 'MySQL\\Core';
+    protected static $prefix = 'ph_'; // 表前缀
+    protected static $tableName = '';
+    protected static $fields = array();
+    protected static $primaryKey = 'id';
+
+    /**
+     * 获取计数
+     * @param array $whereParam
+     * @param string $whereString
+     * @return int
+     */
+    public static function count($whereParam, $whereString = '')
+    {
+        $query = self::getQuery()->setWhere($whereParam, $whereString)->count();
+        $result = self::getConnection()->read($query->getSQL(), $query->getData());
+        if (!$result) {
+            return 0;
+        }
+        $result = current($result);
+        return intval($result['count']);
+    }
+
+    /**
+     * 获取列表,limit 不传取全部
+     * @param array $fields
+     * @param array $whereParam
+     * @param string $whereString
+     * @param int $limit 不传取全部
+     * @param int $offset
+     * @param array $orderBy
+     * @return array
+     */
+    public static function select(
+        $fields = array(),
+        $whereParam = array(),
+        $whereString = '',
+        $limit = null,
+        $offset = 0,
+        $orderBy = array())
+    {
+        $query = self::getQuery()
+            ->setField($fields)
+            ->setWhere($whereParam, $whereString)
+            ->setLimit($limit, $offset)
+            ->setOrderBy($orderBy)
+            ->select();
+        $result = self::getConnection()->read($query->getSQL(), $query->getData());
+        if (!$result) {
+            return $result;
+        }
+//        $data = array();
+//        $class = get_called_class();
+//        foreach ($result as $row) {
+//            $data[$row[$class::$primaryKey]] = $row;
+//        }
+//        unset($data);
+        return $result;
+    }
+
+    /**
+     * 获取列表数据,简单版
+     * @param array $whereParam
+     * @param int $limit
+     * @param $orderBy
+     * @return array
+     */
+    public static function getList($whereParam = array(), $limit = 10, $orderBy = array())
+    {
+        return self::select(array(), $whereParam, '', $limit, 0, $orderBy);
+    }
+
+    /**
+     * 获取列表ID信息
+     * @param string $field
+     * @param array $whereParam
+     * @param $orderBy
+     * @param $limit
+     * @return array
+     */
+    public static function getListId($field = '', $whereParam = array(), $orderBy = array(), $limit = 2000)
+    {
+        $query = self::getQuery()
+            ->setField($field)
+            ->setWhere($whereParam)
+            ->setLimit($limit)
+            ->setOrderBy($orderBy)
+            ->select();
+        $result = self::getConnection()->read($query->getSQL(), $query->getData());
+        if (!$result) {
+            return $result;
+        }
+        $data = array();
+        foreach ($result as $row) {
+            $data[] = $row[$field];
+        }
+        unset($result);
+        return $data;
+    }
+
+    /**
+     * 分页获取数据
+     * @param array $whereParam
+     * @param string $whereString
+     * @param int $page
+     * @param int $pageSize
+     * @param array $orderBy
+     * @param array $fields
+     * @return array
+     */
+    public static function getPageList($whereParam = array(), $whereString='', $page = 1, $pageSize = 20, $orderBy = array(), $fields = array()){
+        $page = $page ? $page : 1;
+        $offset = ($page - 1) * $pageSize;
+        //获取总条数
+        $total_count = self::count($whereParam,$whereString);
+        $pager = array(
+            'total_count' => $total_count,
+            'total_page' => 0,
+            'current_page' => $page,
+            'page_size' => $pageSize,
+            'data' => array()
+        );
+        if($total_count > 0){
+            list($url,) = explode('?',$_SERVER['REQUEST_URI']);
+            unset($_GET['page']);
+            //链接
+            $pager['url'] = $url;
+            //请求参数
+            $pager['filter'] =  $_GET ? http_build_query($_GET) : '';
+            //总页数
+            $pager['total_page'] = ceil($total_count / $pageSize);
+            //内容
+            $pager['data'] = self::select($fields,$whereParam,$whereString,$pageSize,$offset,$orderBy);
+        }
+        return $pager;
+    }
+
+    /**
+     * 获取单条记录
+     * @param array $whereParam
+     * @param string $whereString
+     * @param array $orderBy
+     * @return array;
+     */
+    public static function getOne($whereParam = array(), $whereString = '', $orderBy = array())
+    {
+        $query = self::getQuery()
+            ->setWhere($whereParam, $whereString)
+            ->setOrderBy($orderBy)
+            ->setLimit(1)
+            ->select();
+        $result = self::getConnection()->read($query->getSQL(), $query->getData(), false, false);
+        return $result;
+    }
+
+    /**
+     * 根据主键获取单条记录
+     * @param int $id
+     * @return array
+     */
+    public static function getLine($id)
+    {
+        if (!$id) {
+            return array();
+        }
+        $class = get_called_class();
+        $where = array($class::$primaryKey => $id);
+        $query = self::getQuery()->setWhere($where)->setLimit(1)->select();
+        $result = self::getConnection()->read($query->getSQL(), $query->getData(), false, false);
+        return $result;
+    }
+
+    /**
+     * 批量根据主键查询, 按照传入的ID进行排序
+     * @param $idList
+     * @return array|mixed
+     */
+    public static function getBatch($idList)
+    {
+        if (!$idList) {
+            return array();
+        }
+        $class = get_called_class();
+        $where = array($class::$primaryKey => $idList);
+        $query = self::getQuery()->setWhere($where)->select();
+        $result = self::getConnection()->read($query->getSQL(), $query->getData());
+        if (!$result) {
+            return $result;
+        }
+        $data = array();
+        foreach ($result as $row) {
+            $data[$row[$class::$primaryKey]] = $row;
+        }
+        // 按照传入的id顺序进行重新排序,如果id对应的value不存在则忽略
+        $result = array();
+        foreach ($idList as $id) {
+            if (isset($data[$id])) {
+                $result[$id] = $data[$id];
+            }
+        }
+        unset($data);
+        return $result;
+    }
+
+    /**
+     * 聚合获取列表数据
+     * @param $field
+     * @param $whereParam
+     * @param $whereString
+     * @param $groupBy
+     * @param $limit
+     * @return array|mixed
+     */
+    public static function getListByGroup($field,$whereParam,$whereString,$groupBy,$limit=null){
+        $query = self::getQuery()
+            ->setField($field)
+            ->setWhere($whereParam,$whereString)
+            ->setGroupBy($groupBy)
+            ->setLimit($limit)
+            ->select();
+        return self::getConnection()->read($query->getSQL(), $query->getData());
+    }
+
+    /**
+     * 联合获取列表数据
+     * @param $whereString
+     * @param $join
+     * @param $sort
+     * @param string $field
+     * @return array|mixed
+     */
+    public static function getListByJoin($whereString,$join,$sort='',$field=''){
+        $class = get_called_class();
+        $tableName = $class::$tableName;
+        $field = $field ? : $tableName.'.*';
+        $sql = 'select '.$field.' from '.$tableName.' left join '.$join[0].' on '.$tableName.'.'.$join[1].'='.$join[0].'.'.$join[2];
+        $whereString ? $sql .= ' where '.$whereString : null;
+        $sort ? $sql .= ' order by '.$sort : null;
+        $sql = str_replace('SELF',$tableName,$sql);//SELF代表主表
+        $sql = str_replace('JOIN',$join[0],$sql);//JOIN代表连接表
+        $result =  self::getConnection()->read($sql, array());
+        $data = array();
+        $class = get_called_class();
+        foreach ($result as $row) {
+            $data[$row[$class::$primaryKey]] = $row;
+        }
+        return $data;
+    }
+
+    /**
+     * 创建记录
+     * @param $data
+     * @param bool 是否返回最后最后插入ID
+     * @return bool|int
+     */
+    public static function insert($data, $returnLastInsertId = true)
+    {
+        $query = self::getQuery()->setData($data)->insert();
+        $result = self::getConnection()->write($query->getSQL(), $query->getData());
+        // 返回最后插入的id
+        if ($result && $returnLastInsertId) {
+            $result = self::getConnection()->getLastInsertId();
+        }
+        return $result;
+    }
+
+    /**
+     * 批量插入
+     * $data = array(
+     *      array(
+     *          'id' => 10001,
+     *          'target_id' => 100,
+     *      ),
+     *      array(
+     *          'id' => 10002,
+     *          'target_id' => 100,
+     *      ),
+     * );
+     * @param $data
+     * @return bool|int
+     */
+    public static function insertBatch($data)
+    {
+        $prefix = 'prepared_';
+        // 确定字段名
+        $fields = implode('`, `', array_keys(current($data)));
+        //print_r($data);
+        // 拼SQL和数据
+        $dataList = array();
+        $values = array();
+        $i = 0;
+        foreach ($data as $k => $v) {
+            foreach ($v as $x => $y) {
+                $key = $prefix . $i;
+                $dataList[$key] = $y;
+                $v[$x] = ':' . $key;
+                $i++;
+            }
+            $values[] = "(" . implode(", ", $v) . ")";
+        }
+        $values = implode(', ', $values);
+        $class = get_called_class();
+        $tableName = $class::$tableName;
+        //print_r($values);die();
+        $sql = "INSERT INTO `" . $tableName . "` (`$fields`) VALUES $values";
+        //print_r($sql);
+        $result = self::write($sql, $dataList);
+        return $result;
+    }
+
+    /**
+     * 更新记录
+     * @param $data
+     * @param $whereParam
+     * @param $whereString
+     * @return bool
+     */
+    public static function update($data, $whereParam, $whereString = '')
+    {
+        $query = self::getQuery()->setData($data)->setWhere($whereParam, $whereString)->update();
+        $result = self::getConnection()->write($query->getSQL(), $query->getData());
+        return $result;
+    }
+
+    /**
+     * 替换输入
+     * @param $data
+     * @param $whereParam
+     * @param $whereString
+     * @return bool
+     */
+    public static function replace($data, $whereParam, $whereString = '')
+    {
+        $query = self::getQuery()->setData($data)->setWhere($whereParam, $whereString)->replace();
+        $result = self::getConnection()->write($query->getSQL(), $query->getData());
+        return $result;
+    }
+
+    /**
+     * 删除逻辑
+     * @param $whereParam
+     * @param $whereString
+     * @return bool
+     */
+    public static function delete($whereParam, $whereString = '')
+    {
+        $query = self::getQuery()->setWhere($whereParam, $whereString)->delete();
+        $result = self::getConnection()->write($query->getSQL(), $query->getData());
+        return $result;
+    }
+
+
+    /**
+     * 获取一个新的SQL生成器
+     * @return \Heanup\Frame\Library\MySQL\Query
+     */
+    protected static function getQuery()
+    {
+        $class = get_called_class();
+        return \Heanup\Frame\Library\MySQL\Query::newInstance()
+            ->setTableName($class::$tableName)
+            ->setField($class::$fields)
+            ->setPrimaryKey($class::$primaryKey);
+    }
+
+    /**
+     * 从数据库读取数据,可以强制从主库读取
+     * @param string $sql
+     * @param array $data
+     * @param bool $isMaster
+     * @return mixed|Array
+     */
+    protected static function read($sql, $data, $isMaster = false)
+    {
+        $result = self::getConnection()->read($sql, $data, $isMaster);
+        return $result;
+    }
+
+    /**
+     * 写入
+     * @param $sql
+     * @param $data
+     * @return bool|int
+     */
+    protected static function write($sql, $data)
+    {
+        $result = self::getConnection()->write($sql, $data);
+        return $result;
+    }
+
+    /**
+     * 获取连接池
+     * @return \Heanup\Frame\Library\MySQL
+     */
+    static function getConnection()
+    {
+        $class = get_called_class();
+        $configClass = $class::CONFIG_CLASS;
+        return new \Heanup\Frame\Library\MySQL($configClass);
+    }
+
+    /**
+     * 获取表前缀
+     * @return string
+     */
+    public static function getPrefix(){
+        return isset(self::$prefix) ? self::$prefix : '';
+    }
+
+}

+ 62 - 0
Frame/Package/Queue.php

@@ -0,0 +1,62 @@
+<?php
+
+namespace Heanup\Frame\Package;
+
+/**
+ * 队列
+ * 使用redis实现
+ */
+class Queue extends \Heanup\Frame\Package
+{
+    const CONFIG_CLASS = 'Redis\\Queue';
+    const PREFIX = '';
+
+    /**
+     * 获取队列长度
+     * @return int
+     */
+    public static function length()
+    {
+        $key = self::getKey();
+        $result = self::getConnection(true)->lLen($key);
+        return $result;
+    }
+
+    /**
+     * 插入数据
+     * @param $data
+     * @return int
+     */
+    public static function push($data)
+    {
+        $key = self::getKey();
+        $result = self::getConnection(true)->rPush($key, $data);
+        return $result;
+    }
+
+    /**
+     * 取出数据
+     * @return string
+     */
+    public static function pop()
+    {
+        $key = self::getKey();
+        $result = self::getConnection(true)->lPop($key);
+        return $result;
+    }
+
+    /**
+     * 获取连接池
+     * @param bool $isMaster
+     * @return \Redis
+     */
+    protected static function getConnection($isMaster = false)
+    {
+        $class = get_called_class();
+        $configClass = $class::CONFIG_CLASS;
+        $redis = new \Heanup\Frame\Library\Redis($configClass);
+        $connection = $redis->getConnection($isMaster);
+        return $connection;
+    }
+
+}

+ 82 - 0
Frame/Package/RedisQueue.php

@@ -0,0 +1,82 @@
+<?php
+
+namespace Heanup\Frame\Package;
+
+/**
+ * @author cxm@meitu.com
+ * 队列
+ */
+class RedisQueue
+{
+
+    protected static $_instance = NULL;
+    protected $redisObj = NULL;
+    protected $key = NULL;
+
+    /**
+     * 
+     * @param unknown $key
+     * @param string $config
+     * @return self
+     */
+    public static function getInstance($key, $config = "Redis\\Queue")
+    {
+        if (empty($key)) {
+            return false;
+        }
+        if (!isset(self::$_instance[$key][$config]) || !(self::$_instance[$key][$config] instanceof self)) {
+            self::$_instance[$key][$config] = new self($key, $config);
+        }
+        return self::$_instance[$key][$config];
+    }
+
+    /**
+     * 获取队列长度
+     * @return int
+     */
+    public function length()
+    {
+        $result = $this->getConnection(true)->lLen($this->key);
+        return $result;
+    }
+
+    /**
+     * 插入数据
+     * @param $data
+     * @return int
+     */
+    public function push($data)
+    {
+        $result = $this->getConnection(true)->lPush($this->key, $data);
+        return $result;
+    }
+
+    /**
+     * 取出数据
+     * @return string
+     */
+    public function pop()
+    {
+        $result = $this->getConnection(true)->rPop($this->key);
+        return $result;
+    }
+
+    public function delete()
+    {
+        $result = $this->getConnection(true)->delete($this->key);
+        return $result;
+    }
+
+    protected function __construct($key, $config)
+    {
+        $this->key = $key;
+        $this->redisObj = new \Heanup\Frame\Library\Redis($config);
+    }
+
+    protected function getConnection($isMaster)
+    {
+        $connection = $this->redisObj->getConnection($isMaster);
+        return $connection;
+    }
+
+}

+ 291 - 0
Frame/Package/Set.php

@@ -0,0 +1,291 @@
+<?php
+
+namespace Heanup\Frame\Package;
+
+/**
+ * 使用 redis sorted sets
+ */
+class Set extends \Heanup\Frame\Package
+{
+    const CONFIG_CLASS = 'Redis\\Core';
+    const PREFIX = '';
+    const EXPIRE = 0; // 3 天
+
+
+    /**
+     * 获取列表,从$start 到 $end,默认获取全部
+     * 从大到小获取!!
+     * @param $id
+     * @param $lastId
+     * @param $limit
+     * @return array
+     */
+    public static function get($id, $lastId, $limit = 20, $withscores = false)
+    {
+        $key = self::getKey($id);
+        try {
+            if ($lastId <= 0) {
+                $options = ($limit > 0) ? array('limit' => array(0, $limit)) : array();
+                if ($withscores) {
+                    $options['withscores'] = true;
+                }
+                $result = self::getConnection()->zRevRangeByScore($key, PHP_INT_MAX, 0, $options);
+            } else {
+                if ($withscores) {
+                    $options['withscores'] = true;
+                }
+                // 获取$lastId对应的score, 如果没有找到,则返回空
+                $score = self::getConnection()->zScore($key, $lastId);
+
+                if ($score === false) {
+                    return array();
+                }
+                if ($limit <= 0) {
+                    $result = self::getConnection()->zRevRangeByScore($key, $score, 0, $options);
+                    if ($result) {
+                        if ($lastId == current($result)) {
+                            array_shift($result);
+                        }
+                    }
+                } else {
+                    // 排除第一个元素, 多取出一个元素, 然后删除
+                    $limit = $limit + 1;
+                    $options = array('limit' => array(0, $limit));
+                    if ($withscores) {
+                        $options['withscores'] = true;
+                    }
+                    $result = self::getConnection()->zRevRangeByScore($key, $score, 0, $options);
+
+                    if ($result) {
+                        if ($lastId == current($result)) {
+                            array_shift($result);
+                        } elseif (count($result) >= $limit) {
+                            array_pop($result);
+                        }
+                    }
+                }
+            }
+        } catch (\Exception $ex) {
+            $result = array();
+        }
+        return $result;
+    }
+
+    public static function getsByIndex($id, $lastId, $limit = 20, $withscores = false)
+    {
+        $key = self::getKey($id);
+       
+        try {
+            if ($lastId <= 0) { 
+                $start = 0;
+                $stop = $limit-1; 
+                $result = self::getConnection()->zRevRange($key, $start, $stop, $withscores);
+            } else {
+                $last_index = self::getConnection()->zRevRank($key, $lastId);
+                if ($last_index) {
+                    $start = $last_index;
+                    $stop = ($start + $limit);
+                    $result = self::getConnection()->zRevRange($key, $start, $stop, $withscores);
+                    if ($result) {
+                        if ($lastId == current($result)) {
+                            array_shift($result);
+                        }
+                    }
+                } 
+            }
+        } catch (\Exception $ex) {
+            $result = array();
+        }
+        return $result;
+    }
+
+    public static function getByPage($id, $page, $limit = 20, $withscores = false)
+    {
+        $key = self::getKey($id);
+        try {
+            $options = array('limit' => array($page * $limit, $limit));
+            if ($withscores) {
+                $options['withscores'] = true;
+            }
+            $result = self::getConnection()->zRevRangeByScore($key, PHP_INT_MAX, 0, $options);
+
+        } catch (\Exception $ex) {
+            $result = array();
+        }
+        return $result;
+    }
+
+    /**
+     * 获取所有的ID列表
+     * @param $id
+     * @return array
+     */
+    public static function getAll($id, $withscores = true)
+    {
+        $key = self::getKey($id);
+        //89de3bf93d9670af:
+        $options = array();
+        if ($withscores) {
+            $options['withscores'] = true;
+        }
+        try {
+            $result = self::getConnection()->zRevRangeByScore($key, PHP_INT_MAX, 0, $options);
+        } catch (\Exception $ex) {
+            $result = array();
+        }
+        return $result;
+    }
+
+    /**
+     * 获取 $value 的索引值
+     */
+    public static function getIndex($id, $value)
+    {
+        $key = self::getKey($id);
+        try {
+            $result = self::getConnection(true)->zRank($key, $value);
+        } catch (\Exception $ex) {
+            $result = false;
+        }
+        return $result;
+    }
+
+    /**
+     * 添加元素, 注意score相同时可能会有问题, 建议使用 addBatch()
+     * @param $id
+     * @param int $score
+     * @param int $value
+     * @return bool
+     */
+    public static function add($id, $score, $value)
+    {
+        $key = self::getKey($id);
+        try {
+            $result = self::getConnection(true)->zAdd($key, $score, $value);
+            // 可选过期
+            $class = get_called_class();
+            if ($class::EXPIRE) {
+                self::getConnection(true)->expire($key, $class::EXPIRE);
+            }
+        } catch (\Exception $ex) {
+            $result = false;
+        }
+        return $result;
+    }
+
+    /**
+     * 批量添加, 重新计算权重
+     * @param $id
+     * @param array $data key为列表id,value为score,会重新排序,然后计算score
+     * @return bool|int
+     */
+    public static function addBatch($id, Array $data)
+    {
+        asort($data); // 升序排列
+        $i = 1;
+        foreach ($data as $k => $score) {
+            $score = $i * 10000;
+            self::add($id, $score, $k);
+            $i++;
+        }
+        return true;
+    }
+
+    /**
+     * 移除元素
+     * @param $id
+     * @param int $value
+     * @return int
+     */
+    public static function remove($id, $value)
+    {
+        $key = self::getKey($id);
+        try {
+            $result = self::getConnection(true)->zDelete($key, $value);
+        } catch (\Exception $ex) {
+            $result = false;
+        }
+        return $result;
+    }
+
+    /**
+     * 更新集合内容, 先删除(不判断结果),然后插入
+     * @param $id
+     * @param $score
+     * @param $value
+     * @return bool|int
+     */
+    public static function update($id, $score, $value)
+    {
+        self::remove($id, $value);
+        $result = self::add($id, $score, $value);
+        return $result;
+    }
+
+    /**
+     * 移除指定范围内元素
+     * @param $id
+     * @param int $start
+     * @param int $stop
+     * @return int
+     */
+    public static function removeByLex($id, $start, $stop)
+    {
+        $key = self::getKey($id);
+        try {
+            $result = self::getConnection(true)->zDeleteRangeByRank($key, $start, $stop);
+        } catch (\Exception $ex) {
+            $result = false;
+        }
+        return $result;
+    }
+
+    /**
+     * 获取列表长度
+     * @param $id
+     * @param int $start
+     * @param int $end
+     * @return int
+     */
+    public static function count($id, $start = 0, $end = PHP_INT_MAX)
+    {
+        $key = self::getKey($id);
+        try {
+            $result = self::getConnection(true)->zCount($key, $start, $end);
+        } catch (\Exception $ex) {
+            $result = 0;
+        }
+        return $result;
+    }
+
+    /**
+     * 删除
+     * @param $id
+     * @return bool
+     */
+    public static function delete($id)
+    {
+        $key = self::getKey($id);
+        try {
+            $result = self::getConnection(true)->delete($key);
+        } catch (\Exception $ex) {
+            $result = false;
+        }
+        return $result;
+    }
+
+    /**
+     * 获取连接池
+     * @param bool $isMaster
+     * @return \Redis
+     */
+    protected static function getConnection($isMaster = false)
+    {
+        $class = get_called_class();
+        $configClass = $class::CONFIG_CLASS;
+        $redis = new \Heanup\Frame\Library\Redis($configClass);
+        $connection = $redis->getConnection($isMaster);
+        return $connection;
+    }
+
+}

+ 203 - 0
Frame/Request.php

@@ -0,0 +1,203 @@
+<?php
+
+namespace Heanup\Frame;
+
+class Request
+{
+    const MODE_ONLINE = 0; // 正式环境
+    const MODE_BETA = 1; // Beta 环境
+    const MODE_PRE = 2; // Pre 预览环境
+    const MODE_DEVELOP = 3; // 开发环境
+    
+    const MODE_ONLINE_PRE=4;
+    const MODE_BETA_PRE=5;
+    const MODE_DEVLOP_PRE=6;
+
+    protected static $privateDataPath = ''; // 私有文件路径
+    protected static $mode = null;
+    protected static $exception;
+    protected static $currentUserId = null; // 当前登录UserID
+    protected static $sessionId = null;
+    protected static $isCacheEnabled = true; // 缓存是否有效
+
+    protected static $data = array(); // 运行期缓存
+
+    /**
+     * 获取一个随机的唯一的ID
+     * @return null|string
+     */
+    public static function getRequestId()
+    {
+        static $id = null;
+        if (is_null($id)) {
+            $id = md5(uniqid('', true) . microtime() . rand(0, 999999999));
+        }
+        return $id;
+    }
+
+    /**
+     * 设置运行期缓存,尽量不要设置大数据
+     * @param $key
+     * @param $value
+     */
+    public static function set($key, $value)
+    {
+        self::$data[$key] = $value;
+    }
+
+    /**
+     * 读取运行期缓存
+     * @param $key
+     * @return mixed
+     */
+    public static function get($key)
+    {
+        return self::$data[$key];
+    }
+
+    /**
+     * 抛出异常,并记录
+     * @param string $message
+     * @param int $code
+     * @throws \Exception
+     */
+    public static function throwException($message = '', $code = 0)
+    {
+        self::$exception = new \Exception($message, $code);
+        Logger::error(self::$exception, 'request.exception');
+        throw self::$exception;
+    }
+
+    /**
+     * 设置异常
+     * @param $exception
+     * @return bool
+     */
+    public static function setException($exception)
+    {
+        if ($exception instanceof \Exception) {
+            self::$exception = $exception;
+            Logger::error($exception, 'request.exception');
+        }
+        return true;
+    }
+
+    /**
+     * 获取异常
+     * @return mixed
+     */
+    public static function getException()
+    {
+        return self::$exception;
+    }
+
+    /**
+     * 设置环境变量
+     * @param int $value
+     */
+    public static function setMode($value = 0)
+    {
+        self::$mode = $value;
+    }
+
+    /**
+     * 获取环境变量,优先使用程序中设置的,其次使用环境变量
+     * @return int
+     */
+    public static function getMode()
+    {
+        return intval(self::$mode);
+    }
+
+    /**
+     * 检查环境变量
+     * @param $value
+     * @return bool
+     */
+    public static function checkMode($value)
+    {
+        if (self::getMode() == $value) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * 设置当前登录用户ID
+     * @param $userId
+     */
+    public static function setCurrentUserId($userId)
+    {
+        if ($userId) {
+            self::$currentUserId = $userId;
+        }
+    }
+
+    /**
+     * 返回当前登录用户ID
+     * @return int|null
+     */
+    public static function getCurrentUserId()
+    {
+        return intval(self::$currentUserId);
+    }
+
+    /**
+     * 设置会话ID
+     * @param $sessionId
+     */
+    public static function setSessionId($sessionId)
+    {
+        if ($sessionId) {
+            self::$sessionId = $sessionId;
+        }
+    }
+
+    /**
+     * 获取会话ID
+     * @return int|null
+     */
+    public static function getSessionId()
+    {
+        return self::$sessionId;
+    }
+
+    /**
+     * 设置私有文件路径
+     * @param $path
+     */
+    public static function setPrivateDataPath($path)
+    {
+        self::$privateDataPath = rtrim($path, '/');
+    }
+
+    /**
+     * 获取私有文件路径
+     * @param string $filename
+     * @return string
+     */
+    public static function getPrivateDataPath($filename = '')
+    {
+        return self::$privateDataPath . '/' . rtrim($filename) . '/';
+    }
+
+    /**
+     * 判断是否开启缓存
+     * @return bool
+     */
+    public static function isCacheEnabled()
+    {
+        return self::$isCacheEnabled;
+    }
+
+    /**
+     * 是否启用缓存
+     * @param bool $value
+     */
+    public static function setCacheEnabled($value = true)
+    {
+        self::$isCacheEnabled = intval($value);
+    }
+
+}

+ 104 - 0
Frame/Response.php

@@ -0,0 +1,104 @@
+<?php
+
+namespace Heanup\Frame;
+
+class Response
+{
+    const HTTP_VERSION = "HTTP/1.1";
+
+    //返回结果
+    public static function sendResponse($code, $data = [], $msg = 'OK')
+    {
+        //获取数据
+        if ($code == 200) {
+            $status = 200;
+            $message = 'OK';
+        } elseif ($code == 404) {
+            $status = 404;
+            $data = array('error' => 'Not Found');
+            $message = 'Not Found';
+        } elseif ($code == 405) {
+            $status = 405;
+            $data = array('error' => 'Method not allowed');
+            $message = 'Method not allowed';
+        } else {
+            $status = 200;
+            $message = $msg;
+        }
+
+        //输出结果
+        header(self::HTTP_VERSION . " " . $status . " " . $message);
+        $output = array(
+            'code' => (int)intval($code),
+            'message' => $message,
+        );
+        $content_type = isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : $_SERVER['HTTP_ACCEPT'];
+        if (strpos($content_type, 'application/json') !== false) {
+            if (is_array($data) &&  $data['page_config']) {
+                $output = array_merge($output, $data);
+            } else {
+                $output['data'] = $data;
+            }
+            header("Content-Type: application/json");
+            echo self::encodeJson($output);
+            die();
+        } else if (strpos($content_type, 'application/xml') !== false) {
+            header("Content-Type: application/xml");
+            echo self::encodeXml($output);
+            die();
+        } else {
+            if (is_array($data) && $data['page_config']) {
+                $output = array_merge($output, $data);
+            } else {
+                $output['data'] = $data;
+            }
+            header("Content-Type: application/json");
+            echo self::encodeJson($output);
+
+            die();
+        }
+    }
+
+    //json格式
+    private static function encodeJson($responseData)
+    {
+        $str = json_encode($responseData);
+        return $str;
+    }
+
+    //xml格式
+    private static function encodeXml($responseData)
+    {
+        $xml = new \SimpleXMLElement('<?xml version="1.0"?><rest></rest>');
+        foreach ($responseData as $key => $value) {
+            if (is_array($value)) {
+                foreach ($value as $k => $v) {
+                    $xml->addChild($k, $v);
+                }
+            } else {
+                $xml->addChild($key, $value);
+            }
+        }
+        return $xml->asXML();
+    }
+
+    //html格式
+    private static function encodeHtml($responseData)
+    {
+        $html = "<table border='1'>";
+        foreach ($responseData as $key => $value) {
+            $html .= "<tr>";
+            if (is_array($value)) {
+                foreach ($value as $k => $v) {
+                    $html .= "<td>" . $k . "</td><td>" . $v . "</td>";
+                }
+            } else {
+                $html .= "<td>" . $key . "</td><td>" . $value . "</td>";
+            }
+            $html .= "</tr>";
+        }
+        $html .= "</table>";
+        return $html;
+    }
+
+}

+ 39 - 0
Frame/Router.php

@@ -0,0 +1,39 @@
+<?php
+
+namespace Heanup\Frame;
+
+class Router
+{
+    protected static $list = array();
+
+    /**
+     * 添加路由
+     * @param $key
+     * @param $value
+     */
+    public static function add($key, $value)
+    {
+        self::$list[$key] = $value;
+    }
+
+    /**
+     * 移除路由
+     * @param $key
+     */
+    public static function remove($key)
+    {
+        if (isset(self::$list[$key])) {
+            unset(self::$list[$key]);
+        }
+    }
+
+    /**
+     * 获取路由列表
+     * @return array
+     */
+    public static function getList()
+    {
+        return self::$list;
+    }
+
+}

+ 148 - 0
Frame/Session.php

@@ -0,0 +1,148 @@
+<?php
+
+namespace Heanup\Frame;
+
+class Session
+{
+
+    protected static $expire = ADMIN_TOKEN_EXPIRED;
+
+    /**
+     * 设置超时时间, 初始化 cookie等
+     *
+     * @param int $expire
+     * @throws \Exception
+     */
+    public static function start($expire = 1800)
+    {
+        if (is_numeric($expire)) {
+            self::$expire = $expire;
+        }
+        self::getSessionId();
+    }
+
+    /**
+     * 设置会话信息
+     *
+     * @param string $key
+     * @param string $data
+     * @param null $user_id
+     * @return bool
+     */
+    public static function set($key, $data, $user_id = null)
+    {
+        $sessionId = self::getSessionId($user_id);
+        try {
+            $redis = self::getConnection(true);
+            $isExisted = $redis->exists($sessionId);
+            $result = $redis->hSet($sessionId, $key, $data);
+            // if (!$isExisted) {
+            $redis->expire($sessionId, self::$expire);
+            // }
+        } catch (\Exception $ex) {
+            Logger::debug($ex, 'session.set');
+            $result = false;
+        }
+        return $result;
+    }
+
+    /**
+     * 读取会话信息
+     *
+     * @param $key
+     * @param null $user_id
+     * @return mixed
+     */
+    public static function get($key, $user_id = null)
+    {
+
+        $sessionId = self::getSessionId($user_id);
+        try {
+            $redis = self::getConnection();
+            $result = $redis->hGet($sessionId, $key);
+            $redis->expire($sessionId, self::$expire);
+        } catch (\Exception $ex) {
+            Logger::debug($ex, 'session.get');
+            $result = null;
+        }
+        return $result;
+    }
+
+    /**
+     * 获取所有的会话信息
+     *
+     * @param null $user_id
+     * @return array
+     */
+    public static function getAll($user_id = null)
+    {
+        $sessionId = self::getSessionId($user_id);
+        try {
+            $result = self::getConnection()->hGetAll($sessionId);
+        } catch (\Exception $ex) {
+            Logger::debug($ex, 'session.get_all');
+            $result = array();
+        }
+        return $result;
+    }
+
+    /**
+     * 删除会话
+     *
+     * @param null $user_id
+     * @return bool
+     */
+    public static function destroy($user_id = null)
+    {
+        $sessionId = self::getSessionId($user_id);
+        try {
+            $result = self::getConnection(true)->delete($sessionId);
+            $_COOKIE[$sessionId] = null;
+        } catch (\Exception $ex) {
+            Logger::debug($ex, 'session.destroy');
+            $result = false;
+        }
+        return $result;
+    }
+
+    /**
+     * 获取会话ID
+     *
+     * @param null $user_id
+     * @return string
+     * @throws \Exception
+     */
+    public static function getSessionId($user_id = null)
+    {
+        if ($user_id) {
+            $sessionId = md5($user_id);
+        } else {
+            $host = $_SERVER['HTTP_HOST'];
+            $key = 'sid:' . crc32($host);
+            if (!$_COOKIE[$key]) {
+                $sessionId = md5(microtime() . rand() . uniqid());
+                $result = setcookie($key, $sessionId, time() + self::$expire * 10, '/', $host, null, true);
+                if (!$result) {
+                    throw new \Exception('set cookie failed', 9000);
+                }
+            } else {
+                $sessionId = $_COOKIE[$key];
+            }
+        }
+        return $sessionId;
+    }
+
+    /**
+     *
+     * @param bool $isMaster
+     * @return \Redis
+     * @internal param $ $isMaster*            $isMaster
+     */
+    protected static function getConnection($isMaster = false)
+    {
+        $configClass = 'Redis\Session';
+        $redis = new \Heanup\Frame\Library\Redis($configClass);
+        $connection = $redis->getConnection($isMaster);
+        return $connection;
+    }
+}

+ 234 - 0
Frame/Template.php

@@ -0,0 +1,234 @@
+<?php
+
+namespace Heanup\Frame;
+
+use Heanup\Config\Lang\Lang;
+/**
+ * 简易模板类
+ */
+class Template
+{
+    private $data = array();
+    private $path;
+    private $tpl = null;
+    private $suffix = '.phtml';
+
+
+    public function __isset($name)
+    {
+        return isset($this->data[$name]);
+    }
+
+    public function __set($name, $value = null)
+    {
+        $this->data[$name] = $value;
+    }
+
+    public function __get($name)
+    {
+        return isset($this->data[$name]) ? $this->data[$name] : null;
+    }
+
+    /**
+     * 设置模板路径
+     * @param $path
+     * @return $this
+     */
+    public function setPath($path)
+    {
+        if ($path) {
+            $this->path = rtrim($path, '/') . DIRECTORY_SEPARATOR;
+        }
+        return $this;
+    }
+
+    /**
+     * 设置模板后缀
+     * @param $suffix
+     * @return $this
+     */
+    public function setSuffix($suffix)
+    {
+        if ($suffix) {
+            $this->suffix = $suffix;
+        }
+        return $this;
+    }
+
+    /**
+     * 设置模板名称
+     * @param $tpl
+     * @return $this
+     */
+    public function setTpl($tpl)
+    {
+        if ($tpl) {
+            $this->tpl = $tpl;
+        }
+        return $this;
+    }
+
+    /**
+     * 变量赋值, 可以单独赋值,也可以批量赋值
+     * @param $name
+     * @param null $value
+     * @return $this
+     */
+    public function assign($name, $value = null)
+    {
+        if (is_array($name)) {
+            foreach ($name as $k => $v) {
+                $this->data[$k] = $v;
+            }
+        } else {
+            $this->data[$name] = $value;
+        }
+        return $this;
+    }
+
+    /**
+     * 获取全部数据
+     * @return array
+     */
+    public function getData()
+    {
+        return $this->data;
+    }
+
+    /**
+     * 载入其他模板
+     * @param $tpl
+     * @return null
+     */
+    public function load($tpl)
+    {
+        $tpl = $this->path . $tpl . $this->suffix;
+        if (is_readable($tpl)) {
+            include($tpl);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * 渲染页面
+     * @param bool $isReturn
+     * @param string $layout
+     * @return string
+     */
+    public function render($isReturn = false, $layout = '')
+    {
+        $filename = $this->path . $this->tpl . $this->suffix;
+        // 判断模板是否存在
+        if (!is_readable($filename)) {
+            Logger::error($filename, 'template.not_found');
+            return '';
+        }
+        // 判断layout是否存在
+        if ($layout && is_readable($this->path . $layout . $this->suffix)) {
+            $filename = $this->path . $layout . $this->suffix;
+        }
+        if ($isReturn == false) {
+            include($filename);
+        } else {
+            ob_start();
+            include($filename);
+            $content = ob_get_contents();
+            ob_end_clean();
+            return $content;
+        }
+    }
+
+    /**
+     * 输出ajax信息
+     * @param bool $result
+     * @param null $code
+     * @param null $msg
+     * @param null $data
+     */
+    public function output($result = true, $code = null, $msg = null, $data = null)
+    {
+        if ($result instanceof Exception) {
+            $code = $result->getCode();
+            $msg = $result->getMessage();
+            $data = $result->__toString();
+            $result = false;
+        }
+        if ($result AND !is_bool($result) AND is_null($data)) {
+            $data = $result;
+        }
+        $result = (bool)$result;
+        $code = (int)$code;
+        $msg = (!empty($msg)) ? $msg : '';
+        $data = (!empty($data)) ? $data : '';
+
+        $data = array(
+            'result' => $result,
+            'code' => $code,
+            'msg' => $msg,
+            'data' => $data,
+        );
+
+        header('Content-type: application/json');
+        echo json_encode($data);
+    }
+
+    /**
+     * 获取同域名下的其他子域名
+     * @param $domain
+     * @return string
+     */
+    protected function getDomainHost($domain)
+    {
+        $domainHost = $_SERVER['HTTP_HOST'];
+        $domainHost = $domainHost."/".$domain;
+        return $domainHost;
+    }
+    /**
+     * 获取多语言配置文件
+     * @param string $key key
+     * @param string $default 默认值
+     */
+    protected function getLang($key,$default="",$app="Admin",$lang=NULL){
+        return Lang::getData($key,$default,$app,$lang);
+    }
+
+    protected function getPager($pager){
+//        if($pager['total_page'] == 0 || $pager['total_page'] <= $pager['page_size']){
+//            return '';
+//        }
+        $current_start = ($pager['current_page']-1)*$pager['page_size']+1;
+        $current_end = $pager['current_page']*$pager['page_size'] < $pager['total_count'] ? $pager['current_page']*$pager['page_size']:$pager['total_count'];
+        $html = '<div class="row">
+        <div class="col-sm-5">
+        <div class="dataTables_info">当前显示 '.$current_start.' 到 '.$current_end.' 条,共 '.$pager['total_count'].' 条记录</div>
+        </div><div class="col-sm-7">
+        <div class="text-right">
+        <ul class="pagination" style="margin:0">';
+        $start = 1;
+        $skip = 0;
+        $end = $pager['total_page'];
+        if($pager['current_page'] - 3 > $start){
+            $start = $pager['current_page'] - 3;
+            $html .= '<li class="paginate_button"><a href="'.$pager['url'].'?page=1&'.$pager['filter'].'">首页</a></li>';
+        }else{
+            $skip = 4 - $pager['current_page'];
+        }
+        if($pager['current_page'] + 3 < $end){
+            $end = $pager['current_page'] + 3 + $skip;
+        }
+        for($i = $start; $i <= $end; $i++){
+            if($i == $pager['current_page']){
+                $html .= '<li class="paginate_button active"><a href="javascript:;">'.$i.'</a></li>';
+            }else{
+                $html .= '<li class="paginate_button"><a href="'.$pager['url'].'?page='.$i.'&'.$pager['filter'].'">'.$i.'</a></li>';
+            }
+        }
+        if($end < $pager['total_page']){
+            $html .= '<li class="paginate_button"><a href="'.$pager['url'].'?page='.$pager['total_page'].'&'.$pager['filter'].'">末页</a></li>';
+        }
+        $html .= '</ul></div>';
+        return $html;
+    }
+
+}

+ 218 - 0
Library/Curl.php

@@ -0,0 +1,218 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: chendeben
+ * Date: 2018/8/17
+ * Time: 下午5:26
+ */
+
+namespace Heanup\Library;
+
+use Heanup\Frame\Logger;
+
+class Curl
+{
+    private static $cookie_file = "curl_cookie.log";
+
+
+    /**
+     * CURL-get方式获取数据
+     * @param string $url URL
+     * @param string $proxy 是否代理
+     * @param int $timeout 请求时间
+     * @param array $header header信息  array('Host: api.kingdee.meitushop.com' )设置请求的host
+     * @return string
+     */
+    public static function get($url, $proxy = null, $timeout = 10, $header = null,$REFERER=null)
+    {
+        if (!$url)
+            return false;
+        $ssl = stripos($url, 'https://') === 0 ? true : false;
+        $curl = curl_init();
+        if (!is_null($proxy))
+            curl_setopt($curl, CURLOPT_PROXY, $proxy);
+        curl_setopt($curl, CURLOPT_URL, $url);
+        if ($ssl) {
+            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); // 对认证证书来源的检查
+            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 1); // 从证书中检查SSL加密算法是否存在
+        }
+        if ($REFERER){
+            curl_setopt($curl, CURLOPT_REFERER, $REFERER);//模拟来路
+
+        }
+        $cookie_file = self::$cookie_file;
+        curl_setopt($curl, CURLOPT_COOKIEJAR, $cookie_file); //连接结束后保存cookie信息的文件。
+        curl_setopt($curl, CURLOPT_COOKIEFILE, $cookie_file); //包含cookie数据的文件名,cookie文件的格式可以是Netscape格式,或者只是纯HTTP头部信息存入文件。
+        curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); //在HTTP请求中包含一个"User-Agent: "头的字符串。
+        curl_setopt($curl, CURLOPT_HEADER, 0); //启用时会将头文件的信息作为数据流输出。
+        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); //启用时会将服务器服务器返回的"Location: "放在header中递归的返回给服务器,使用CURLOPT_MAXREDIRS可以限定递归返回的数量。
+        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); //文件流形式
+        curl_setopt($curl, CURLOPT_TIMEOUT, $timeout); //设置cURL允许执行的最长秒数。
+        if (is_array($header))
+            curl_setopt($curl, CURLOPT_HTTPHEADER, $header); //设置请求的Header
+
+        $content = curl_exec($curl);
+        $curl_errno = curl_errno($curl);
+        if ($curl_errno > 0) {
+            Logger::setlog($error = sprintf("curl error=%s, errno=%d.", curl_error($curl), $curl_errno));
+            curl_close($curl);
+            return false;
+        }
+//        $this->http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
+        curl_close($curl);
+        return $content;
+    }
+
+    /**
+     * CURL-post方式获取数据
+     * @param string $url URL
+     * @param array $data POST数据
+     * @param string $proxy 是否代理
+     * @param int $timeout 请求时间
+     * @param array $header header信息
+     * @return string
+     */
+    public static function post($url, $data, $proxy = null, $timeout = 10, $header = null)
+    {
+        if (!$url)
+            return false;
+//        if ($data) {
+//            $data = http_build_query($data);
+//        }
+        $ssl = stripos($url, 'https://') === 0 ? true : false;
+        $curl = curl_init();
+        if (!is_null($proxy))
+            curl_setopt($curl, CURLOPT_PROXY, $proxy);
+        curl_setopt($curl, CURLOPT_URL, $url);
+        if ($ssl) {
+            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); // 对认证证书来源的检查
+            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 1); // 从证书中检查SSL加密算法是否存在
+        }
+        $cookie_file = self::$cookie_file;
+        curl_setopt($curl, CURLOPT_COOKIEJAR, $cookie_file); //连接结束后保存cookie信息的文件。
+        curl_setopt($curl, CURLOPT_COOKIEFILE, $cookie_file); //包含cookie数据的文件名,cookie文件的格式可以是Netscape格式,或者只是纯HTTP头部信息存入文件。
+        curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); //在HTTP请求中包含一个"User-Agent: "头的字符串。
+        curl_setopt($curl, CURLOPT_HEADER, 0); //启用时会将头文件的信息作为数据流输出。
+        curl_setopt($curl, CURLOPT_POST, true); //发送一个常规的Post请求
+        curl_setopt($curl, CURLOPT_POSTFIELDS, $data); //Post提交的数据包
+        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); //启用时会将服务器服务器返回的"Location: "放在header中递归的返回给服务器,使用CURLOPT_MAXREDIRS可以限定递归返回的数量。
+        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); //文件流形式
+        curl_setopt($curl, CURLOPT_TIMEOUT, $timeout); //设置cURL允许执行的最长秒数。
+        if (is_array($header))
+            curl_setopt($curl, CURLOPT_HTTPHEADER, $header); //设置请求的Header
+
+        $content = curl_exec($curl);
+        $curl_errno = curl_errno($curl);
+        if ($curl_errno > 0) {
+            Logger::setlog($error = sprintf("curl error=%s, errno=%d.", curl_error($curl), $curl_errno));
+            curl_close($curl);
+            return false;
+        }
+//        $this->http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
+        curl_close($curl);
+        return $content;
+    }
+
+    /**
+     * CURL-put方式获取数据
+     * @param string $url URL
+     * @param array $data POST数据
+     * @param string $proxy 是否代理
+     * @param int $timeout 请求时间
+     * @param array $header header信息
+     * @return string
+     */
+    public static function put($url, $data, $proxy = null, $timeout = 10, $header = null)
+    {
+        if (!$url)
+            return false;
+        if ($data) {
+            $data = http_build_query($data);
+        }
+        $ssl = stripos($url, 'https://') === 0 ? true : false;
+        $curl = curl_init();
+        if (!is_null($proxy))
+            curl_setopt($curl, CURLOPT_PROXY, $proxy);
+        curl_setopt($curl, CURLOPT_URL, $url);
+        if ($ssl) {
+            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); // 对认证证书来源的检查
+            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 1); // 从证书中检查SSL加密算法是否存在
+        }
+        $cookie_file = self::$cookie_file;
+        curl_setopt($curl, CURLOPT_COOKIEJAR, $cookie_file); //连接结束后保存cookie信息的文件。
+        curl_setopt($curl, CURLOPT_COOKIEFILE, $cookie_file); //包含cookie数据的文件名,cookie文件的格式可以是Netscape格式,或者只是纯HTTP头部信息存入文件。
+        curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); //在HTTP请求中包含一个"User-Agent: "头的字符串。
+        curl_setopt($curl, CURLOPT_HEADER, 0); //启用时会将头文件的信息作为数据流输出。
+        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); //启用时会将服务器服务器返回的"Location: "放在header中递归的返回给服务器,使用CURLOPT_MAXREDIRS可以限定递归返回的数量。
+        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); //文件流形式
+        curl_setopt($curl, CURLOPT_TIMEOUT, $timeout); //设置cURL允许执行的最长秒数
+        $data = (is_array($data)) ? http_build_query($data) : $data;
+        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PUT');
+        curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Length: ' . strlen($data)));
+        curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
+        if (is_array($header))
+            curl_setopt($curl, CURLOPT_HTTPHEADER, $header); //设置请求的Header
+
+        $content = curl_exec($curl);
+        $curl_errno = curl_errno($curl);
+        if ($curl_errno > 0) {
+            Logger::setlog($error = sprintf("curl error=%s, errno=%d.", curl_error($curl), $curl_errno));
+            curl_close($curl);
+            return false;
+        }
+        curl_close($curl);
+        return $content;
+    }
+
+    /**
+     * CURL-DEL方式获取数据
+     * @param string $url URL
+     * @param array $data POST数据
+     * @param string $proxy 是否代理
+     * @param int $timeout 请求时间
+     * @param array $header header信息
+     * @return string
+     */
+    public static function del($url, $data, $proxy = null, $timeout = 10, $header = null)
+    {
+        if (!$url)
+            return false;
+        if ($data) {
+            $data = http_build_query($data);
+        }
+        $ssl = stripos($url, 'https://') === 0 ? true : false;
+        $curl = curl_init();
+        if (!is_null($proxy))
+            curl_setopt($curl, CURLOPT_PROXY, $proxy);
+        curl_setopt($curl, CURLOPT_URL, $url);
+        if ($ssl) {
+            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); // 对认证证书来源的检查
+            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 1); // 从证书中检查SSL加密算法是否存在
+        }
+        $cookie_file = self::$cookie_file;
+        curl_setopt($curl, CURLOPT_COOKIEJAR, $cookie_file); //连接结束后保存cookie信息的文件。
+        curl_setopt($curl, CURLOPT_COOKIEFILE, $cookie_file); //包含cookie数据的文件名,cookie文件的格式可以是Netscape格式,或者只是纯HTTP头部信息存入文件。
+        curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); //在HTTP请求中包含一个"User-Agent: "头的字符串。
+        curl_setopt($curl, CURLOPT_HEADER, 0); //启用时会将头文件的信息作为数据流输出。
+        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); //启用时会将服务器服务器返回的"Location: "放在header中递归的返回给服务器,使用CURLOPT_MAXREDIRS可以限定递归返回的数量。
+        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); //文件流形式
+        curl_setopt($curl, CURLOPT_TIMEOUT, $timeout); //设置cURL允许执行的最长秒数
+        $data = (is_array($data)) ? http_build_query($data) : $data;
+        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DEL');
+        curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Length: ' . strlen($data)));
+        curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
+        if (is_array($header))
+            curl_setopt($curl, CURLOPT_HTTPHEADER, $header); //设置请求的Header
+
+        $content = curl_exec($curl);
+        $curl_errno = curl_errno($curl);
+        if ($curl_errno > 0) {
+            Logger::setlog($error = sprintf("curl error=%s, errno=%d.", curl_error($curl), $curl_errno));
+            curl_close($curl);
+            return false;
+        }
+        curl_close($curl);
+        return $content;
+    }
+
+}

+ 241 - 0
Library/Pager.php

@@ -0,0 +1,241 @@
+<?php
+namespace Heanup\Library;
+
+class Pager
+{
+
+    private static $show_num = 9;
+ // 分页中显示多少个
+    private static $conf = array(
+        'first_last' => 1, // 首页-尾页 0-关闭 1-开启
+        'back_next' => 1, // 上一页-下一页 0-关闭 1-开启
+        'total_num' => 1, // 是否显示总页数 0-关闭 1-开启
+        'page_num' => 1, // 翻页数 0-关闭 1-开启
+        'select' => 1
+    );
+ // 下拉列表选择 0-关闭 1-开启
+    private static $style_config = '<style type="text/css">
+	.InitPHP_pages {font:12px/1.6em Helvetica, Arial, sans-serif;overflow:hidden; text-align:center; font-family:Verdana;margin-bottom:5px;  }
+	.InitPHP_pages a, .pages{ margin:0 1px; padding:1px 6px; border:1px solid #E4E4E4; text-decoration:none!important; }
+	.InitPHP_pages a:hover { border-color:#369; }
+	.InitPHP_pages strong { margin:0 1px; padding:2px 6px; border-color:#369; background:#369; color:#FFF; text-decoration:none!important; }
+	.InitPHP_pages .back { padding:4px 6px 1px 20px!important; padding:4px 6px 2 20px;  font-family:simsun; }
+	.InitPHP_pages .next { padding:4px 20px 1px 6px!important; padding:4px 20px 2 6px; font-family:simsun; }
+	.InitPHP_pages .first { padding:4px 6px 1px 4px!important; padding:4px 6px 2 4px;  font-family:simsun; }
+	.InitPHP_pages .last { padding:4px 4px 1px 6px!important; padding:4px 4px 2 6px; font-family:simsun; }
+	</style><script>function go_to(_target){self.location.href=$(_target).attr("turn-url");}</script>';
+
+    /**
+     * 分页-分页入口
+     *
+     * @param int $count
+     *            总共多少数据
+     * @param int $prepage
+     *            每页显示多少页
+     * @param int $url
+     *            URL
+     * @return string
+     */
+    public static function pager($count, $prepage, $url, $page, $default_style = TRUE): string
+    {
+        $count = (int) $count;
+        $prepage = (int) $prepage;
+        $page_num = ceil($count / $prepage); // 总共多少页
+        $page = (int) $page;
+        $page = ($page > $page_num) ? $page_num : ($page = ($page < 1) ? 1 : $page);
+        $url = (strpos($url, '?') === false) ? $url . '?' : $url;
+        return self::pager_html($page_num, $url, $page, $default_style);
+    }
+
+    /**
+     * 分页-获取分页HTML显示
+     *
+     * @param int $page_num
+     *            页数
+     * @param string $url
+     *            URL
+     * @param int $page
+     *            当前页
+     * @return string
+     */
+    private static function pager_html($page_num, $url, $page, $default_style)
+    {
+        list ($start, $end) = self::get_satrt_and_end($page, $page_num);
+        list ($back, $next) = self::get_pager_next_back_html($url, $page, $page_num);
+        list ($first, $last) = self::get_first_last_html($page_num, $url);
+        if ($default_style == true) {
+            $html = self::$style_config . "<div class='InitPHP_pages'>";
+        } else {
+            $html = "<div class='InitPHP_pages'>";
+        }
+        $html .= $back;
+        $html .= $first;
+        $html .= self::get_pager_num_html($start, $end, $url, $page);
+        $html .= $last;
+        $html .= $next;
+        $html .= self::get_total_num_html($page_num);
+        $html .= self::get_select_html($page_num, $url, $page);
+        $html .= '</div>';
+        return $html;
+    }
+
+    /**
+     * 分页-获取分页数字的列表
+     *
+     * @param int $start
+     *            开始数
+     * @param int $end
+     *            结束数
+     * @param string $url
+     *            URL地址
+     * @param int $page
+     *            当前页
+     * @return string
+     */
+    private static function get_pager_num_html($start, $end, $url, $page)
+    {
+        if (self::$conf['page_num'] == 0)
+            return ''; // 是否开启
+        $html = '';
+        for ($i = $start; $i <= $end; $i ++) {
+            if ($i == $page) {
+                $html .= "<strong>{$i}</strong>";
+            } else {
+                $html .= "<a  href='javascript:void(0);' turn-url='{$url}&page={$i}' onclick='go_to(this);'>{$i}</a>";
+            }
+        }
+        return $html;
+    }
+
+    /**
+     * 分页-分页总页数显示
+     *
+     * @param int $page_num
+     *            页数
+     * @return string
+     */
+    private static function get_total_num_html($page_num)
+    {
+        if (self::$conf['total_num'] == 0)
+            return ''; // 是否开启
+        return "&nbsp;&nbsp;共{$page_num}页";
+    }
+
+    /**
+     * 分页-分页首页和尾页显示
+     *
+     * @param int $page_num
+     *            页数
+     * @param string $url
+     *            URL地址
+     * @return string
+     */
+    private static function get_first_last_html($page_num, $url)
+    {
+        if (self::$conf['first_last'] == 0)
+            return array(
+                '',
+                ''
+            ); // 是否开启
+        $first = "<a href='javascript:void(0);' turn-url='{$url}&page=1' onclick='go_to(this);' class='first'>首页</a>";
+        $last = "<a href='javascript:void(0);' turn-url='{$url}&page={$page_num}' onclick='go_to(this);' class='last'>尾页</a>";
+        return array(
+            $first,
+            $last
+        );
+    }
+
+    /**
+     * 分页-获取分页上一页-下一页HTML
+     *
+     * @param string $url
+     *            URL地址
+     * @param int $page
+     *            当前页
+     * @param int $page_num
+     *            页数
+     * @return string
+     */
+    private static function get_pager_next_back_html($url, $page, $page_num)
+    {
+        if (self::$conf['back_next'] == 0)
+            return array(
+                '',
+                ''
+            ); // 是否开启
+        $next_page = $page + 1;
+        $next = "<a href='javascript:void(0);' turn-url='{$url}&page={$next_page}' onclick='go_to(this);' class='next'>下一页</a>";
+        if ($page == $page_num)
+            $next = '';
+        $back_page = $page - 1;
+        $back = "<a href='javascript:void(0);' turn-url='{$url}&page={$back_page}' onclick='go_to(this);' class='back'>上一页</a>";
+        if ($page == 1)
+            $back = '';
+        return array(
+            $back,
+            $next
+        );
+    }
+
+    /**
+     * 分页-Select选择器
+     *
+     * @param int $page_num
+     *            页数
+     * @param string $url
+     *            URL地址
+     * @param int $page
+     *            当前页
+     * @return string
+     */
+    private static function get_select_html($page_num, $url, $page)
+    {
+        if (self::$conf['select'] == 0)
+            return '';
+        $html = '&nbsp;&nbsp;<select name="select" onchange="javascript:window.location.href=this.options[this.selectedIndex].value">';
+        for ($i = 1; $i <= $page_num; $i ++) {
+            if ($page == $i) {
+                $selected = ' selected';
+            } else {
+                $selected = '';
+            }
+            $html .= "<option value='{$url}&page={$i}' {$selected}>{$i}</option>";
+        }
+        $html .= '</select>';
+        return $html;
+    }
+
+    /**
+     * 分页-获取分页显示数字
+     *
+     * @param int $page
+     *            当前页
+     * @param int $page_num
+     *            页数
+     * @return array(start, end)
+     */
+    private static function get_satrt_and_end($page, $page_num)
+    {
+        $temp = floor(self::$show_num / 2);
+        if ($page_num < self::$show_num)
+            return array(
+                1,
+                $page_num
+            );
+        if ($page <= $temp) {
+            $start = 1;
+            $end = self::$show_num;
+        } elseif (($page_num - $temp) < $page) {
+            $start = $page_num - self::$show_num + 1;
+            $end = $page_num;
+        } else {
+            $start = $page - $temp;
+            $end = $page - $temp + self::$show_num - 1;
+        }
+        return array(
+            $start,
+            $end
+        );
+    }
+}
+    

+ 14 - 0
Library/Sms.php

@@ -0,0 +1,14 @@
+<?php
+
+
+namespace Heanup\Library;
+
+
+interface Sms
+{
+    public function getCountry();
+
+    public function getPhoneNums($link);
+
+    public function getMessage($country, $phone_num);
+}

+ 133 - 0
Library/Sms/ReceiveSmsFree.php

@@ -0,0 +1,133 @@
+<?php
+
+
+namespace Heanup\Library\Sms;
+
+
+use Heanup\App\Api\Package\Database\Num;
+use Heanup\Library\Sms;
+use QL\QueryList;
+
+class ReceiveSmsFree implements Sms
+{
+
+
+    /**
+     * @return string[]
+     */
+    public function getCountry(): array
+    {
+        $data = [];
+        for ($i = 1; $i <= 3; $i++) {
+            $url = "https://receive-sms-free.net/regions/" . $i . ".html";
+            $ql = (new QueryList)->get($url);
+            $range = "#ul> .wow";
+            $rules = [
+                'country' => ["h2", "text"],
+                'link' => ["a", "href"]
+            ];
+            $_data = $ql->range($range)->rules($rules)->query()->getData()->all();
+            if ($_data) {
+                $data = array_merge($data, $_data);
+            }
+        }
+        $data = array_map(function ($item) {
+            $item['country'] = trim($item['country']);
+            return $item;
+        }, $data);
+        return $data;
+    }
+
+    /**
+     * @param $link
+     * @return array
+     */
+    public function getPhoneNums($link): array
+    {
+        $ql = (new QueryList)->get($link);
+        $end_page_link = $ql->find(".pagination  a:contains(End)")->attr("href");
+        preg_match("#\d+#", $end_page_link, $all_page);
+        $all_page = $all_page[0];
+        $data = $ql->find(".h2 span")->texts()->all();
+        $this->insert($link, $data);
+        if ($all_page > 0) {
+            for ($i = 2; $i <= $all_page; $i++) {
+                $url = sprintf($link . "%s.html", $i);
+                $_ql = (new QueryList)->get($url);
+                $_data = $_ql->find(".h2 span")->texts()->all();
+                if ($_data) {
+                    $this->insert($link, $_data);
+                    $data = array_merge($data, $_data);
+                } else {
+                    echo "总页数:" . $all_page;
+                    echo "处理异常: " . $url . PHP_EOL;
+                }
+            }
+        }
+        return $data;
+    }
+
+    public function getMessage($country, $phone_num): array
+    {
+        $info = Num::getOne(['country' => $country, 'phone' => $phone_num]);
+        if ($info) {
+//            print_r($info);
+            $curl = curl_init();
+
+            curl_setopt_array($curl, array(
+                CURLOPT_URL => $info['url'],
+                CURLOPT_RETURNTRANSFER => true,
+                CURLOPT_ENCODING => '',
+                CURLOPT_MAXREDIRS => 10,
+                CURLOPT_TIMEOUT => 0,
+                CURLOPT_FOLLOWLOCATION => true,
+                CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
+                CURLOPT_CUSTOMREQUEST => 'GET',
+                CURLOPT_HTTPHEADER => array(
+                    'authority: receive-sms-free.net',
+                    'cache-control: max-age=0',
+                    'dnt: 1',
+                    'upgrade-insecure-requests: 1',
+                    'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36',
+                    'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
+                    'sec-fetch-site: none',
+                    'sec-fetch-mode: navigate',
+                    'sec-fetch-user: ?1',
+                    'sec-fetch-dest: document',
+                    'accept-language: en,zh-CN;q=0.9,zh;q=0.8',
+                    'cookie: SMSSId=aBayAmYhhg9HLHqmViT0jvh%2FGHUbo0QlFET1obfgGD310kML2XzZanFOYbXk950v6yVzgWdaroChz8GyNQbEjCdLj4WBBUmVykTi; __cfduid=d2530c6c2475fe8a930ceda1dcda65b831610621278; _ga=GA1.2.752717170.1610621281; _gid=GA1.2.1285055920.1610621281; UM_distinctid=177008536e6120-07f84d7cecf1fe-326f7007-13c680-177008536e78dc; CNZZDATA1277724716=410208085-1610621466-https%253A%252F%252Freceive-sms-free.net%252F%7C1610621466; __gads=ID=8d8fcd8e633e42c4-22e6a115bfc50000:T=1610872559:RT=1610872559:S=ALNI_MbjZCyvGfLoOil8tWJnEYhSbJB_7Q; SMSSId=aRDmU2Ukhl9HLHqmViT0jvh%2FGHUbo0QlFET1obfgGD310kML2XzZanFOYbXk950v6yVzgWdaroChz861PQXHiCdLj4WBBUmfwEjn; __atuvc=52%7C2%2C9%7C3; __atuvs=60040c9d95de614a001'
+                ),
+            ));
+
+            $response = curl_exec($curl);
+
+            curl_close($curl);
+            $ql = (new QueryList)->setHtml($response);
+            $range = ".row:gt(0)";
+            $rules = [
+                'from' => [".col-xs-12  > .mobile_hide", "text"],
+                'time' => [".col-xs-0", "text"],
+                'message' => [".col-md-8", "text"]
+            ];
+            $ql->find(".mobile_hide:contains(ADS)")->parent()->parent()->remove();
+            $msgs = $ql->range($range)->rules($rules)->query()->getData()->all();
+            return $msgs;
+        }
+    }
+
+    private function insert($link, $data)
+    {
+        foreach ($data as $val) {
+            echo sprintf("正在写入号码:\t\t %s", $val) . PHP_EOL;
+            $num = explode(" ", $val);
+            $_num = str_replace(['+', ' '], ['', ''], $val);
+            $insert = [
+                'country' => $num[0],
+                'phone' => $num[1],
+                'site' => 'receive-sms-free.net',
+                'url' => $link . $_num . "/"
+            ];
+            Num::insert($insert);
+        }
+    }
+}

+ 8 - 0
apidoc.json

@@ -0,0 +1,8 @@
+{
+  "name": "测试",
+  "version": "0.0.1",
+  "description": "API文档测试",
+  "title": "API文档测试",
+  "url" : "http://xxxxxxx",
+  "sampleUrl" : "http://xxxxxxxx"
+}

+ 7 - 0
composer.json

@@ -0,0 +1,7 @@
+{
+    "require": {
+        "php-imap/php-imap": "^3.0",
+        "ark/filecache": "^0.1.0",
+        "jaeger/querylist": "^4.2"
+    }
+}

+ 1447 - 0
composer.lock

@@ -0,0 +1,1447 @@
+{
+    "_readme": [
+        "This file locks the dependencies of your project to a known state",
+        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+        "This file is @generated automatically"
+    ],
+    "content-hash": "f4e78d654951f214bd9f9d7a1ea750db",
+    "packages": [
+        {
+            "name": "ark/filecache",
+            "version": "v0.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/arkphp/filecache.git",
+                "reference": "2646305ff6b9dfec96f3df00a42dc33a7a13560d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/arkphp/filecache/zipball/2646305ff6b9dfec96f3df00a42dc33a7a13560d",
+                "reference": "2646305ff6b9dfec96f3df00a42dc33a7a13560d",
+                "shasum": ""
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Ark\\Filecache\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "dong",
+                    "email": "ddliuhb@gmail.com"
+                }
+            ],
+            "description": "Cache with file system",
+            "support": {
+                "source": "https://github.com/arkphp/filecache/tree/master"
+            },
+            "time": "2015-03-22T07:32:53+00:00"
+        },
+        {
+            "name": "cache/adapter-common",
+            "version": "1.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-cache/adapter-common.git",
+                "reference": "6b87c5cbdf03be42437b595dbe5de8e97cd1d497"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-cache/adapter-common/zipball/6b87c5cbdf03be42437b595dbe5de8e97cd1d497",
+                "reference": "6b87c5cbdf03be42437b595dbe5de8e97cd1d497",
+                "shasum": ""
+            },
+            "require": {
+                "cache/tag-interop": "^1.0",
+                "php": "^5.6 || ^7.0 || ^8.0",
+                "psr/cache": "^1.0",
+                "psr/log": "^1.0",
+                "psr/simple-cache": "^1.0"
+            },
+            "require-dev": {
+                "cache/integration-tests": "^0.16",
+                "phpunit/phpunit": "^5.7.21"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Cache\\Adapter\\Common\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Aaron Scherer",
+                    "email": "aequasi@gmail.com",
+                    "homepage": "https://github.com/aequasi"
+                },
+                {
+                    "name": "Tobias Nyholm",
+                    "email": "tobias.nyholm@gmail.com",
+                    "homepage": "https://github.com/nyholm"
+                }
+            ],
+            "description": "Common classes for PSR-6 adapters",
+            "homepage": "http://www.php-cache.com/en/latest/",
+            "keywords": [
+                "cache",
+                "psr-6",
+                "tag"
+            ],
+            "support": {
+                "source": "https://github.com/php-cache/adapter-common/tree/1.2.0"
+            },
+            "time": "2020-12-14T12:17:39+00:00"
+        },
+        {
+            "name": "cache/filesystem-adapter",
+            "version": "1.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-cache/filesystem-adapter.git",
+                "reference": "1501ca71502f45114844824209e6a41d87afb221"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-cache/filesystem-adapter/zipball/1501ca71502f45114844824209e6a41d87afb221",
+                "reference": "1501ca71502f45114844824209e6a41d87afb221",
+                "shasum": ""
+            },
+            "require": {
+                "cache/adapter-common": "^1.0",
+                "league/flysystem": "^1.0",
+                "php": "^5.6 || ^7.0 || ^8.0",
+                "psr/cache": "^1.0",
+                "psr/simple-cache": "^1.0"
+            },
+            "provide": {
+                "psr/cache-implementation": "^1.0",
+                "psr/simple-cache-implementation": "^1.0"
+            },
+            "require-dev": {
+                "cache/integration-tests": "^0.16",
+                "phpunit/phpunit": "^5.7.21"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Cache\\Adapter\\Filesystem\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Aaron Scherer",
+                    "email": "aequasi@gmail.com",
+                    "homepage": "https://github.com/aequasi"
+                },
+                {
+                    "name": "Tobias Nyholm",
+                    "email": "tobias.nyholm@gmail.com",
+                    "homepage": "https://github.com/nyholm"
+                }
+            ],
+            "description": "A PSR-6 cache implementation using filesystem. This implementation supports tags",
+            "homepage": "http://www.php-cache.com/en/latest/",
+            "keywords": [
+                "cache",
+                "filesystem",
+                "psr-6",
+                "tag"
+            ],
+            "support": {
+                "source": "https://github.com/php-cache/filesystem-adapter/tree/1.1.0"
+            },
+            "time": "2020-12-14T12:17:39+00:00"
+        },
+        {
+            "name": "cache/tag-interop",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-cache/tag-interop.git",
+                "reference": "909a5df87e698f1665724a9e84851c11c45fbfb9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-cache/tag-interop/zipball/909a5df87e698f1665724a9e84851c11c45fbfb9",
+                "reference": "909a5df87e698f1665724a9e84851c11c45fbfb9",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5 || ^7.0 || ^8.0",
+                "psr/cache": "^1.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Cache\\TagInterop\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Tobias Nyholm",
+                    "email": "tobias.nyholm@gmail.com",
+                    "homepage": "https://github.com/nyholm"
+                },
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com",
+                    "homepage": "https://github.com/nicolas-grekas"
+                }
+            ],
+            "description": "Framework interoperable interfaces for tags",
+            "homepage": "https://www.php-cache.com/en/latest/",
+            "keywords": [
+                "cache",
+                "psr",
+                "psr6",
+                "tag"
+            ],
+            "support": {
+                "issues": "https://github.com/php-cache/tag-interop/issues",
+                "source": "https://github.com/php-cache/tag-interop/tree/1.0.1"
+            },
+            "time": "2020-12-04T14:11:04+00:00"
+        },
+        {
+            "name": "guzzlehttp/guzzle",
+            "version": "7.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/guzzle.git",
+                "reference": "0aa74dfb41ae110835923ef10a9d803a22d50e79"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/0aa74dfb41ae110835923ef10a9d803a22d50e79",
+                "reference": "0aa74dfb41ae110835923ef10a9d803a22d50e79",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "guzzlehttp/promises": "^1.4",
+                "guzzlehttp/psr7": "^1.7",
+                "php": "^7.2.5 || ^8.0",
+                "psr/http-client": "^1.0"
+            },
+            "provide": {
+                "psr/http-client-implementation": "1.0"
+            },
+            "require-dev": {
+                "ext-curl": "*",
+                "php-http/client-integration-tests": "^3.0",
+                "phpunit/phpunit": "^8.5.5 || ^9.3.5",
+                "psr/log": "^1.1"
+            },
+            "suggest": {
+                "ext-curl": "Required for CURL handler support",
+                "ext-intl": "Required for Internationalized Domain Name (IDN) support",
+                "psr/log": "Required for using the Log middleware"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "7.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "GuzzleHttp\\": "src/"
+                },
+                "files": [
+                    "src/functions_include.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                },
+                {
+                    "name": "Márk Sági-Kazár",
+                    "email": "mark.sagikazar@gmail.com",
+                    "homepage": "https://sagikazarmark.hu"
+                }
+            ],
+            "description": "Guzzle is a PHP HTTP client library",
+            "homepage": "http://guzzlephp.org/",
+            "keywords": [
+                "client",
+                "curl",
+                "framework",
+                "http",
+                "http client",
+                "psr-18",
+                "psr-7",
+                "rest",
+                "web service"
+            ],
+            "support": {
+                "issues": "https://github.com/guzzle/guzzle/issues",
+                "source": "https://github.com/guzzle/guzzle/tree/7.2.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/Nyholm",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/alexeyshockov",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/gmponos",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-10-10T11:47:56+00:00"
+        },
+        {
+            "name": "guzzlehttp/promises",
+            "version": "1.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/promises.git",
+                "reference": "60d379c243457e073cff02bc323a2a86cb355631"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/promises/zipball/60d379c243457e073cff02bc323a2a86cb355631",
+                "reference": "60d379c243457e073cff02bc323a2a86cb355631",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.5"
+            },
+            "require-dev": {
+                "symfony/phpunit-bridge": "^4.4 || ^5.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "GuzzleHttp\\Promise\\": "src/"
+                },
+                "files": [
+                    "src/functions_include.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                }
+            ],
+            "description": "Guzzle promises library",
+            "keywords": [
+                "promise"
+            ],
+            "support": {
+                "issues": "https://github.com/guzzle/promises/issues",
+                "source": "https://github.com/guzzle/promises/tree/1.4.0"
+            },
+            "time": "2020-09-30T07:37:28+00:00"
+        },
+        {
+            "name": "guzzlehttp/psr7",
+            "version": "1.7.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/psr7.git",
+                "reference": "53330f47520498c0ae1f61f7e2c90f55690c06a3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/psr7/zipball/53330f47520498c0ae1f61f7e2c90f55690c06a3",
+                "reference": "53330f47520498c0ae1f61f7e2c90f55690c06a3",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.4.0",
+                "psr/http-message": "~1.0",
+                "ralouphie/getallheaders": "^2.0.5 || ^3.0.0"
+            },
+            "provide": {
+                "psr/http-message-implementation": "1.0"
+            },
+            "require-dev": {
+                "ext-zlib": "*",
+                "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10"
+            },
+            "suggest": {
+                "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.7-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "GuzzleHttp\\Psr7\\": "src/"
+                },
+                "files": [
+                    "src/functions_include.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                },
+                {
+                    "name": "Tobias Schultze",
+                    "homepage": "https://github.com/Tobion"
+                }
+            ],
+            "description": "PSR-7 message implementation that also provides common utility methods",
+            "keywords": [
+                "http",
+                "message",
+                "psr-7",
+                "request",
+                "response",
+                "stream",
+                "uri",
+                "url"
+            ],
+            "support": {
+                "issues": "https://github.com/guzzle/psr7/issues",
+                "source": "https://github.com/guzzle/psr7/tree/1.7.0"
+            },
+            "time": "2020-09-30T07:37:11+00:00"
+        },
+        {
+            "name": "jaeger/g-http",
+            "version": "V1.7.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/jae-jae/GHttp.git",
+                "reference": "611cfa6986be53d580b19472f10efaa4dde38a80"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/jae-jae/GHttp/zipball/611cfa6986be53d580b19472f10efaa4dde38a80",
+                "reference": "611cfa6986be53d580b19472f10efaa4dde38a80",
+                "shasum": ""
+            },
+            "require": {
+                "cache/filesystem-adapter": "^1.0",
+                "guzzlehttp/guzzle": "^6.0 | ^7.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Jaeger\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jaeger",
+                    "email": "JaegerCode@gmail.com"
+                }
+            ],
+            "description": "Simple Http client base on GuzzleHttp",
+            "support": {
+                "issues": "https://github.com/jae-jae/GHttp/issues",
+                "source": "https://github.com/jae-jae/GHttp/tree/V1.7.1"
+            },
+            "time": "2020-10-26T01:52:47+00:00"
+        },
+        {
+            "name": "jaeger/phpquery-single",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/jae-jae/phpQuery-single.git",
+                "reference": "fb80b4a0e5a337438d26e061ec3fb725c9f2a116"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/jae-jae/phpQuery-single/zipball/fb80b4a0e5a337438d26e061ec3fb725c9f2a116",
+                "reference": "fb80b4a0e5a337438d26e061ec3fb725c9f2a116",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "phpQuery.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Tobiasz Cudnik",
+                    "email": "tobiasz.cudnik@gmail.com",
+                    "homepage": "https://github.com/TobiaszCudnik",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Jaeger",
+                    "role": "Packager"
+                }
+            ],
+            "description": "phpQuery单文件版本,是Querylist的依赖(http://querylist.cc/),phpQuery项目主页:http://code.google.com/p/phpquery/",
+            "homepage": "http://code.google.com/p/phpquery/",
+            "support": {
+                "issues": "https://github.com/jae-jae/phpQuery-single/issues",
+                "source": "https://github.com/jae-jae/phpQuery-single/tree/master"
+            },
+            "time": "2019-11-05T01:50:34+00:00"
+        },
+        {
+            "name": "jaeger/querylist",
+            "version": "V4.2.7",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/jae-jae/QueryList.git",
+                "reference": "5953daac54683700ce6a6725489fa7f3798aad94"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/jae-jae/QueryList/zipball/5953daac54683700ce6a6725489fa7f3798aad94",
+                "reference": "5953daac54683700ce6a6725489fa7f3798aad94",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "jaeger/g-http": "^1.1",
+                "jaeger/phpquery-single": "^1",
+                "php": ">=7.1",
+                "tightenco/collect": ">5.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^8.5",
+                "symfony/var-dumper": "^3.3"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "QL\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jaeger",
+                    "email": "JaegerCode@gmail.com"
+                }
+            ],
+            "description": "Simple, elegant, extensible PHP Web Scraper (crawler/spider),Use the css3 dom selector,Based on phpQuery! 简洁、优雅、可扩展的PHP采集工具(爬虫),基于phpQuery。",
+            "homepage": "http://querylist.cc",
+            "keywords": [
+                "QueryList",
+                "phpQuery",
+                "spider"
+            ],
+            "support": {
+                "issues": "https://github.com/jae-jae/QueryList/issues",
+                "source": "https://github.com/jae-jae/QueryList/tree/V4.2.7"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/querylist",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2020-12-14T02:39:28+00:00"
+        },
+        {
+            "name": "league/flysystem",
+            "version": "1.1.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/thephpleague/flysystem.git",
+                "reference": "9be3b16c877d477357c015cec057548cf9b2a14a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/9be3b16c877d477357c015cec057548cf9b2a14a",
+                "reference": "9be3b16c877d477357c015cec057548cf9b2a14a",
+                "shasum": ""
+            },
+            "require": {
+                "ext-fileinfo": "*",
+                "league/mime-type-detection": "^1.3",
+                "php": "^7.2.5 || ^8.0"
+            },
+            "conflict": {
+                "league/flysystem-sftp": "<1.0.6"
+            },
+            "require-dev": {
+                "phpspec/prophecy": "^1.11.1",
+                "phpunit/phpunit": "^8.5.8"
+            },
+            "suggest": {
+                "ext-fileinfo": "Required for MimeType",
+                "ext-ftp": "Allows you to use FTP server storage",
+                "ext-openssl": "Allows you to use FTPS server storage",
+                "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2",
+                "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3",
+                "league/flysystem-azure": "Allows you to use Windows Azure Blob storage",
+                "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching",
+                "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem",
+                "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files",
+                "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib",
+                "league/flysystem-webdav": "Allows you to use WebDAV storage",
+                "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter",
+                "spatie/flysystem-dropbox": "Allows you to use Dropbox storage",
+                "srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "League\\Flysystem\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Frank de Jonge",
+                    "email": "info@frenky.net"
+                }
+            ],
+            "description": "Filesystem abstraction: Many filesystems, one API.",
+            "keywords": [
+                "Cloud Files",
+                "WebDAV",
+                "abstraction",
+                "aws",
+                "cloud",
+                "copy.com",
+                "dropbox",
+                "file systems",
+                "files",
+                "filesystem",
+                "filesystems",
+                "ftp",
+                "rackspace",
+                "remote",
+                "s3",
+                "sftp",
+                "storage"
+            ],
+            "support": {
+                "issues": "https://github.com/thephpleague/flysystem/issues",
+                "source": "https://github.com/thephpleague/flysystem/tree/1.x"
+            },
+            "funding": [
+                {
+                    "url": "https://offset.earth/frankdejonge",
+                    "type": "other"
+                }
+            ],
+            "time": "2020-08-23T07:39:11+00:00"
+        },
+        {
+            "name": "league/mime-type-detection",
+            "version": "1.5.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/thephpleague/mime-type-detection.git",
+                "reference": "353f66d7555d8a90781f6f5e7091932f9a4250aa"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/353f66d7555d8a90781f6f5e7091932f9a4250aa",
+                "reference": "353f66d7555d8a90781f6f5e7091932f9a4250aa",
+                "shasum": ""
+            },
+            "require": {
+                "ext-fileinfo": "*",
+                "php": "^7.2 || ^8.0"
+            },
+            "require-dev": {
+                "phpstan/phpstan": "^0.12.36",
+                "phpunit/phpunit": "^8.5.8"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "League\\MimeTypeDetection\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Frank de Jonge",
+                    "email": "info@frankdejonge.nl"
+                }
+            ],
+            "description": "Mime-type detection for Flysystem",
+            "support": {
+                "issues": "https://github.com/thephpleague/mime-type-detection/issues",
+                "source": "https://github.com/thephpleague/mime-type-detection/tree/1.5.1"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/frankdejonge",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/league/flysystem",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-10-18T11:50:25+00:00"
+        },
+        {
+            "name": "php-imap/php-imap",
+            "version": "3.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/barbushin/php-imap.git",
+                "reference": "1df5b8b17527829a990475292b6794f06aaa4843"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/barbushin/php-imap/zipball/1df5b8b17527829a990475292b6794f06aaa4843",
+                "reference": "1df5b8b17527829a990475292b6794f06aaa4843",
+                "shasum": ""
+            },
+            "require": {
+                "ext-fileinfo": "*",
+                "ext-iconv": "*",
+                "ext-imap": "*",
+                "ext-mbstring": "*",
+                "php": ">=5.6"
+            },
+            "require-dev": {
+                "friendsofphp/php-cs-fixer": "^2.16",
+                "jakub-onderka/php-parallel-lint": "^1.0",
+                "paragonie/random_compat": "^1",
+                "phpunit/phpunit": "^5.7"
+            },
+            "suggest": {
+                "ext-fileinfo": "To facilitate IncomingMailAttachment::getMimeType() auto-detection"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "PhpImap\\": "src/PhpImap"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Sergey Barbushin",
+                    "email": "barbushin@gmail.com",
+                    "homepage": "http://linkedin.com/in/barbushin"
+                }
+            ],
+            "description": "Manage mailboxes, filter/get/delete emails in PHP (supports IMAP/POP3/NNTP)",
+            "homepage": "https://github.com/barbushin/php-imap",
+            "keywords": [
+                "imap",
+                "mail",
+                "mailbox",
+                "php",
+                "pop3",
+                "receive emails"
+            ],
+            "support": {
+                "issues": "https://github.com/barbushin/php-imap/issues",
+                "source": "https://github.com/barbushin/php-imap/tree/master"
+            },
+            "time": "2020-04-10T11:43:19+00:00"
+        },
+        {
+            "name": "psr/cache",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/cache.git",
+                "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8",
+                "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Cache\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for caching libraries",
+            "keywords": [
+                "cache",
+                "psr",
+                "psr-6"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/cache/tree/master"
+            },
+            "time": "2016-08-06T20:24:11+00:00"
+        },
+        {
+            "name": "psr/http-client",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-client.git",
+                "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+                "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0 || ^8.0",
+                "psr/http-message": "^1.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Http\\Client\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for HTTP clients",
+            "homepage": "https://github.com/php-fig/http-client",
+            "keywords": [
+                "http",
+                "http-client",
+                "psr",
+                "psr-18"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/http-client/tree/master"
+            },
+            "time": "2020-06-29T06:28:15+00:00"
+        },
+        {
+            "name": "psr/http-message",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-message.git",
+                "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
+                "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Http\\Message\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for HTTP messages",
+            "homepage": "https://github.com/php-fig/http-message",
+            "keywords": [
+                "http",
+                "http-message",
+                "psr",
+                "psr-7",
+                "request",
+                "response"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/http-message/tree/master"
+            },
+            "time": "2016-08-06T14:39:51+00:00"
+        },
+        {
+            "name": "psr/log",
+            "version": "1.1.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/log.git",
+                "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc",
+                "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Log\\": "Psr/Log/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for logging libraries",
+            "homepage": "https://github.com/php-fig/log",
+            "keywords": [
+                "log",
+                "psr",
+                "psr-3"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/log/tree/1.1.3"
+            },
+            "time": "2020-03-23T09:12:05+00:00"
+        },
+        {
+            "name": "psr/simple-cache",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/simple-cache.git",
+                "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
+                "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\SimpleCache\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interfaces for simple caching",
+            "keywords": [
+                "cache",
+                "caching",
+                "psr",
+                "psr-16",
+                "simple-cache"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/simple-cache/tree/master"
+            },
+            "time": "2017-10-23T01:57:42+00:00"
+        },
+        {
+            "name": "ralouphie/getallheaders",
+            "version": "3.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ralouphie/getallheaders.git",
+                "reference": "120b605dfeb996808c31b6477290a714d356e822"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
+                "reference": "120b605dfeb996808c31b6477290a714d356e822",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.6"
+            },
+            "require-dev": {
+                "php-coveralls/php-coveralls": "^2.1",
+                "phpunit/phpunit": "^5 || ^6.5"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "src/getallheaders.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ralph Khattar",
+                    "email": "ralph.khattar@gmail.com"
+                }
+            ],
+            "description": "A polyfill for getallheaders.",
+            "support": {
+                "issues": "https://github.com/ralouphie/getallheaders/issues",
+                "source": "https://github.com/ralouphie/getallheaders/tree/develop"
+            },
+            "time": "2019-03-08T08:55:37+00:00"
+        },
+        {
+            "name": "symfony/polyfill-mbstring",
+            "version": "v1.22.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-mbstring.git",
+                "reference": "f377a3dd1fde44d37b9831d68dc8dea3ffd28e13"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/f377a3dd1fde44d37b9831d68dc8dea3ffd28e13",
+                "reference": "f377a3dd1fde44d37b9831d68dc8dea3ffd28e13",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "suggest": {
+                "ext-mbstring": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.22-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Mbstring\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for the Mbstring extension",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "mbstring",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.22.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-07T16:49:33+00:00"
+        },
+        {
+            "name": "symfony/polyfill-php80",
+            "version": "v1.22.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php80.git",
+                "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dc3063ba22c2a1fd2f45ed856374d79114998f91",
+                "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.22-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php80\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ],
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ion Bazan",
+                    "email": "ion.bazan@gmail.com"
+                },
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-php80/tree/v1.22.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-07T16:49:33+00:00"
+        },
+        {
+            "name": "symfony/var-dumper",
+            "version": "v5.2.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/var-dumper.git",
+                "reference": "13e7e882eaa55863faa7c4ad7c60f12f1a8b5089"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/13e7e882eaa55863faa7c4ad7c60f12f1a8b5089",
+                "reference": "13e7e882eaa55863faa7c4ad7c60f12f1a8b5089",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.5",
+                "symfony/polyfill-mbstring": "~1.0",
+                "symfony/polyfill-php80": "^1.15"
+            },
+            "conflict": {
+                "phpunit/phpunit": "<5.4.3",
+                "symfony/console": "<4.4"
+            },
+            "require-dev": {
+                "ext-iconv": "*",
+                "symfony/console": "^4.4|^5.0",
+                "symfony/process": "^4.4|^5.0",
+                "twig/twig": "^2.4|^3.0"
+            },
+            "suggest": {
+                "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).",
+                "ext-intl": "To show region name in time zone dump",
+                "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script"
+            },
+            "bin": [
+                "Resources/bin/var-dump-server"
+            ],
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "Resources/functions/dump.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Component\\VarDumper\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony mechanism for exploring and dumping PHP variables",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "debug",
+                "dump"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/var-dumper/tree/v5.2.1"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-12-16T17:02:19+00:00"
+        },
+        {
+            "name": "tightenco/collect",
+            "version": "v8.19.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/tighten/collect.git",
+                "reference": "0c0243a0dc0b66f54d0ec409f36cd9889665b132"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/tighten/collect/zipball/0c0243a0dc0b66f54d0ec409f36cd9889665b132",
+                "reference": "0c0243a0dc0b66f54d0ec409f36cd9889665b132",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2|^8.0",
+                "symfony/var-dumper": "^3.4 || ^4.0 || ^5.0"
+            },
+            "require-dev": {
+                "mockery/mockery": "^1.0",
+                "nesbot/carbon": "^2.23.0",
+                "phpunit/phpunit": "^8.3"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "src/Collect/Support/helpers.php",
+                    "src/Collect/Support/alias.php"
+                ],
+                "psr-4": {
+                    "Tightenco\\Collect\\": "src/Collect"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylorotwell@gmail.com"
+                }
+            ],
+            "description": "Collect - Illuminate Collections as a separate package.",
+            "keywords": [
+                "collection",
+                "laravel"
+            ],
+            "support": {
+                "issues": "https://github.com/tighten/collect/issues",
+                "source": "https://github.com/tighten/collect/tree/v8.19.0"
+            },
+            "time": "2020-12-19T00:06:29+00:00"
+        }
+    ],
+    "packages-dev": [],
+    "aliases": [],
+    "minimum-stability": "stable",
+    "stability-flags": [],
+    "prefer-stable": false,
+    "prefer-lowest": false,
+    "platform": [],
+    "platform-dev": [],
+    "plugin-api-version": "2.0.0"
+}

BIN
composer.phar


+ 18 - 0
constants.php

@@ -0,0 +1,18 @@
+<?php
+
+// 全局常量
+define('FRAME_START_TIME', microtime(true));
+define('APP_ID', 3);
+define("APP_DIR", dirname(__FILE__));
+if (get_magic_quotes_gpc()) {
+    function stripslashes_deep($value)
+    {
+        $value = is_array($value) ? array_map('stripslashes_deep', $value) : stripslashes($value);
+        return $value;
+    }
+
+    $_POST = array_map('stripslashes_deep', $_POST);
+    $_GET = array_map('stripslashes_deep', $_GET);
+    $_COOKIE = array_map('stripslashes_deep', $_COOKIE);
+    $_REQUEST = array_map('stripslashes_deep', $_REQUEST);
+}

+ 58 - 0
functions.php

@@ -0,0 +1,58 @@
+<?php
+
+// 全局函数
+
+// 捕获所有请求信息,包含fatal error
+// register_shutdown_function('shutdownHandler');
+function shutdownHandler()
+{
+    \Heanup\Frame\Logger::emerg('', 'access');
+}
+
+/**
+ * 获取当前语言环境
+ *
+ * @return string
+ */
+function lang()
+{
+    static $global_lang;
+    if ($global_lang !== null) {
+        return $global_lang;
+    }
+    $global_lang = ! empty($_REQUEST['global_lang']) ? $_REQUEST['global_lang'] : $_COOKIE['global_lang'];
+    if (empty($global_lang) && isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
+        $global_lang = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2);
+        $global_lang=$global_lang=='zh'?$global_lang:"en";
+        setcookie("global_lang", $global_lang, time() + 3600 * 24 * 30, '/');
+    }
+    return $global_lang;
+}
+
+/**
+ * 获取当前平台值
+ *
+ * @return string
+ */
+function getplatform()
+{
+    $platform = ! empty($_REQUEST['platform']) ? intval($_REQUEST['platform']) : intval($_COOKIE['user_platform']);
+    $platform = $platform ?  : 1;
+    if (! $_COOKIE['user_platform'])
+        setcookie("user_platform", $platform, time() + 3600 * 24 * 30, '/');
+    return $platform;
+}
+
+/**
+ * 脚本推送
+ *
+ * @param string $shpath
+ *            执行的脚本路径
+ * @param string $parrams
+ *            附加的参数
+ * @return string
+ */
+function push($shpath, $parrams = "")
+{
+    return shell_exec("/bin/sh " . $shpath . " " . $parrams);
+}

+ 6 - 0
index.php

@@ -0,0 +1,6 @@
+<?php
+
+use Heanup\Frame\Dispatcher;
+
+require_once './Frame/Init.php';
+Dispatcher::dispatch('Api');

+ 521 - 0
public/api/css/sms_receive.css

@@ -0,0 +1,521 @@
+body {
+    font-size: 14px;
+    font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
+    line-height: 1.5;
+    overflow-x: hidden;
+}
+
+/* Wrapping element */
+/* Set some basic padding to keep content from hitting the edges */
+.body-content {
+    padding-left: 15px;
+    padding-right: 15px;
+}
+
+/* Carousel */
+.carousel-caption p {
+    font-size: 20px;
+    line-height: 1.4;
+}
+
+/* Make .svg files in the carousel display properly in older browsers */
+.carousel-inner .item img[src$=".svg"] {
+    width: 100%;
+}
+
+.sms_info_notice {
+    margin: 2%;
+}
+
+.sms_info_ad_content {
+    margin: 0;
+}
+
+.container.sms-content-ad {
+    padding-left: 0;
+    overflow-x: hidden;
+    -moz-box-sizing: border-box;
+    -webkit-box-sizing: border-box;
+    -o-box-sizing: border-box;
+    -ms-box-sizing: border-box;
+    box-sizing: border-box;
+}
+
+.sms_info_graphics h2, .sms_info_graphics i {
+    display: inline-block;
+    margin: 0;
+}
+
+.sms_info_graphics {
+    position: relative;
+}
+
+.sms_info_graphics h2 {
+    position: absolute;
+    top: 8px;
+    left: 64px;
+}
+
+.sms_info_list {
+    margin: 2% 0 0 2%;
+}
+
+.number-list-info, .number-list-flag, .sms-number-read, .number-list-phone_number small, .number-list-phone_number h3 {
+    display: inline-block;
+}
+
+.number-list-info p {
+    font-size: 2rem;
+    line-height: 2rem;
+}
+
+.sms_content .sms-number-list .number-list-info p {
+    padding: 0.5rem;
+}
+
+.sms-number-list {
+    background-color: white;
+    border-radius: 5px;
+    display: -webkit-flex;
+    display: flex;
+    flex-flow: row wrap;
+    justify-content: center;
+    align-items: center;
+    overflow: hidden;
+    margin: 2% auto;
+}
+
+.number-list-phone_number small {
+    font-size: 1.8rem;
+}
+
+.number-list-phone_number h3 {
+    padding-left: 1rem;
+}
+
+.sms-number-read {
+    padding: 10px 0 10px 64px;
+}
+
+.sms-number-read b {
+    position: relative;
+    padding-left: 8px;
+}
+
+/* start SMSContent styles */
+.ad-center {
+    margin: 1% auto;
+}
+
+.sms-content-ad {
+    border-radius: 5px;
+    padding: 5px 0;
+    border: 1px solid #ccc;
+}
+
+.sms_content .container-fluid {
+    padding: 0;
+}
+
+.responsive_ad.container-fluid.sms-number-list-ad {
+    background: white;
+}
+
+#customer_ad img {
+    max-width: 100%;
+    border: 1px solid #ccc;
+}
+
+#customer_ad a {
+    display: block;
+    overflow: hidden;
+}
+
+.sms_content {
+    background-color: #4baad3;
+    padding-bottom: 2%;
+}
+
+.sms-content-list {
+    border-radius: 5px;
+    background-color: white;
+    margin: 2% auto;
+}
+
+section.sms_content .container.sms-content-list.ad-center .sms-content-text {
+    padding: 10px 0;
+}
+
+.sms-content-text {
+    padding: 10px 0;
+}
+
+.sms-content-text h2 {
+    padding: 2rem 0;
+}
+
+section.sms_content .sms-content-link-history {
+    margin: 2rem 0;
+}
+
+section.sms_content .sms-content-link-history p {
+    font-size: 1.5rem;
+    line-height: 3rem;
+}
+
+.sms-number-list-ad {
+    border-radius: 0;
+}
+
+.sms-number-list-ad picture img {
+    margin: 0 auto;
+}
+
+.sms-content-pager {
+    display: block;
+    background-color: white;
+    border-radius: 5px;
+    width: 50%;
+    overflow: hidden;
+    margin: 0 auto;
+    padding: 1%;
+}
+
+.sms-content-pager .btn-group {
+    margin: 0 auto;
+    display: block;
+}
+
+/* end SMSContent styles. */
+
+/* QR code generator */
+#qrCode {
+    margin: 15px;
+}
+
+.sms-content-pager div.pagination, .sms-content-pager ul.pagination {
+    margin: 0 auto;
+    display: block;
+}
+
+.disabled {
+    pointer-events: none;
+}
+
+.sms_info_list nav ul li {
+    font-size: 1.5rem;
+    line-height: 2rem;
+}
+
+.number-list-flag picture {
+    text-align: center;
+}
+
+.number_context:hover {
+
+}
+
+.trans-target-highlight {
+    cursor: pointer;
+    background-color: #b3d4fd;
+    border-radius: 5px;
+    -webkit-border-radius: 5px;
+    -moz-border-radius: 5px
+}
+
+/*start donation page styles*/
+
+.title_content h2 {
+    text-align: center;
+    padding-top: 80px;
+    font-weight: 500;
+    color: rgba(25, 103, 210, 1);
+}
+
+.title_content {
+    background: url("https://pdflib-1253677444.cos.ap-beijing-1.myqcloud.com/sms_resouce/images/homepage_header_background.svg") center no-repeat;
+    height: 305px;
+    margin-top: 5rem;
+}
+
+.donation nav.donation-type {
+    padding-left: 4rem;
+    padding-bottom: 2rem;
+}
+
+nav.donation-type ul li {
+    margin: 20px 0;
+}
+
+.content-text p, .article.container p {
+    font-family: -webkit-pictograph;
+    line-height: 2;
+    font-size: 18px;
+}
+
+/*end donation page styles*/
+
+/*start notices page*/
+.page {
+    background-color: #fff;
+    text-align: left;
+    border: thin solid #ddd;
+    border-radius: 8px;
+    box-sizing: border-box;
+}
+
+.page {
+    border: thin solid #ddd;
+    border-radius: 8px;
+    box-sizing: border-box;
+}
+
+.primary-container {
+    display: flex;
+    justify-content: center;
+    max-width: none;
+    text-align: center;
+    margin-bottom: 1%;
+}
+
+.page-width-container {
+    box-sizing: border-box;
+    margin: 0;
+    max-width: 1202px;
+    padding: 0 16px;
+    width: 100%;
+}
+
+.main-content {
+    max-width: 832px;
+    display: inline-block;
+    position: relative;
+    width: 100%;
+}
+
+.page.no-article-survey {
+    background: transparent;
+    box-shadow: none;
+}
+
+.accordion-homepage .parent {
+    background: #fff;
+    border-top: thin solid #ddd;
+    padding: 0;
+}
+
+.accordion-homepage .parent h2 {
+    outline: none;
+    position: relative;
+}
+
+.accordion-homepage h2 {
+    background-color: #fff;
+    line-height: 20px;
+}
+
+.accordion-homepage h2 {
+    font-size: 14px;
+}
+
+.accordion-homepage h2 {
+    color: #202124;
+    font-size: 16px;
+    font-weight: 500;
+    line-height: 24px;
+    margin: 0;
+    padding: 12px 72px;
+}
+
+.accordion-homepage .parent:first-child, .accordion-homepage .parent:first-child > h2 {
+    border-top-left-radius: 10px;
+    border-top-right-radius: 10px;
+    border-top: none;
+}
+
+.accordion-homepage .parent:not(.selected) h2 {
+    cursor: pointer;
+}
+
+.accordion-homepage .parent h2 svg {
+    fill: #1a73e8;
+    height: 20px;
+    position: absolute;
+    right: 28px;
+    top: calc(50% - 10px);
+    width: 20px;
+}
+
+.notices-ad {
+    margin-top: 2%;
+}
+
+/*end notices page*/
+
+/*start article page*/
+.article.container img {
+    max-width: 100%;
+}
+
+/*end article page*/
+.right-container {
+    margin-top: 1%;
+}
+
+.right-container a {
+    overflow: hidden;
+    display: block;
+}
+
+/* start history page styles*/
+.history-page {
+    text-align: left;
+}
+
+.history-page a {
+    text-decoration: none;
+}
+
+.history-page .history-year {
+    background: #e8f0fe;
+    padding-bottom: .625rem;
+}
+
+.history-page .year-wrap, .history-page h4.panel-title {
+    display: block;
+    line-height: .875rem;
+    padding: .625rem 4rem;
+}
+
+.history-page .year-wrap:hover {
+    background: #d2e3fc;
+}
+
+.history-page .year-wrap a {
+    color: #3c4043;
+    padding-top: .625rem;
+    padding-bottom: .625rem;
+    line-height: 2rem;
+}
+
+.history-content-wrap .title {
+    padding-top: 30px;
+}
+
+.history-content-wrap .title h2 {
+    padding-top: 0;
+}
+
+.history-content .sms-content-pager {
+    width: 80%;
+}
+
+.history-content ol.breadcrumb {
+    text-align: left;
+}
+
+.history-content .history-sms-content thead tr th:nth-child(1) {
+    width: 5%;
+}
+
+.history-content .history-sms-content thead tr th:nth-child(2) {
+    width: 15%;
+}
+
+.history-content .history-sms-content thead tr th:nth-child(3) {
+    width: 65%;
+}
+
+.history-content .history-sms-content thead tr th:nth-child(4) {
+    width: 15%;
+}
+
+.history-content .history-sms-content .pager-wrap {
+    margin: 4rem 0;
+}
+
+/* end history page styles*/
+/*response styles start*/
+/* Hide/rearrange for smaller screens */
+@media screen and (max-width: 768px) {
+    /* Hide captions */
+    .carousel-caption {
+        display: none;
+    }
+
+    .donation.content {
+        max-width: 100%;
+        padding-left: 15px;
+        padding-right: 15px;
+    }
+
+    .donation .title_content {
+        background-image: none;
+        height: auto;
+    }
+
+    .donation .title_content .title h2 {
+        padding-top: 20px;
+        font-size: 2rem;
+    }
+
+    .number-list-flag, .number-list-info {
+        border-bottom: 1px solid #ccc;
+    }
+
+    .number-list-flag {
+        padding-bottom: 1rem;
+    }
+
+    .number-list-flag h3, .sms-number-read, .number-list-info p {
+        display: block;
+        text-align: center;
+        padding: 0;
+        position: static;
+    }
+
+    .phone_number-text {
+        align-self: center;
+        text-align: center;
+    }
+
+    .margin-3 {
+        margin: 3% 0;
+    }
+
+    .number-list-flag picture {
+        text-align: center;
+        display: block;
+    }
+
+    .sms-content-pager, .right-container img {
+        width: 100%;
+    }
+
+    .sms-content-ad {
+        height: auto;
+    }
+
+    .responsive_ad.container {
+        padding-right: 0;
+        padding-left: 0;
+    }
+
+    .right-container {
+        padding: 0;
+    }
+
+    /*start notices style*/
+    .accordion-homepage h2 {
+        padding: 14px 52px 14px 28px;
+    }
+
+    .primary-container {
+        margin-top: 20px;
+    }
+
+    /*end notices styles*/
+
+}
+
+/*response styles end*/

+ 11 - 0
public/api/index.php

@@ -0,0 +1,11 @@
+<?php
+session_start();
+use Heanup\Frame\Dispatcher;
+//use Heanup\Frame\Init;
+
+require_once dirname(dirname(__DIR__)) . '/Frame/Init.php';
+$host = (isset($_SERVER['HTTP_HOST']) && $_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'www.97admin.com';
+// 设置环境变量
+//Init::setEnvironment('/www/privdata/'.$host.'/site.ini');
+// 分发请求
+Dispatcher::dispatch('Api');

+ 14 - 0
public/script/index.php

@@ -0,0 +1,14 @@
+#!/usr/bin/env php
+<?php
+
+// 环境检查
+if ($_SERVER['argc'] < 1) {
+    exit('authorized denied');
+}
+
+require_once dirname(dirname(__DIR__)) . '/Frame/Init.php';
+define('HOST','manage.makeup.meitu.com');
+// 设置环境变量
+\Heanup\Frame\Init::setEnvironment('/www/privdata/'.HOST.'/site.ini');
+// 分发请求
+\Heanup\Frame\Dispatcher::dispatch('Script', $_SERVER['argv'][1]);

+ 2 - 0
vendor/ark/filecache/.gitignore

@@ -0,0 +1,2 @@
+/vendor/
+/tests/cache

+ 3 - 0
vendor/ark/filecache/.travis.yml

@@ -0,0 +1,3 @@
+language: php
+install: 
+ - composer install

+ 19 - 0
vendor/ark/filecache/LICENSE

@@ -0,0 +1,19 @@
+Copyright (c) 2015 dong <ddliuhb@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 50 - 0
vendor/ark/filecache/README.md

@@ -0,0 +1,50 @@
+# filecache
+
+Cache with file system.
+
+## Why File Cache?
+
+In cases you don't want to have other dependencies or don't want to waste your RAM.
+
+## Features
+
+- Compression with `gzcompress`
+- Expiration
+- Multi level cache directories
+
+## Installation
+
+```
+composer require ark/filecache
+```
+
+## Usage
+
+```php
+<?php
+use Ark\Filecache\FileCache;
+
+$cache = new FileCache([
+    'root' => '/path/to/cache/root', // Cache root
+    'ttl' => 0,                    // Time to live
+    'compress' => false,             // Compress data with gzcompress or not
+    'serialize' => 'json',          // How to serialize data: json, php, raw
+]);
+
+$cache->set('key1', 'value1');
+$cache->get('key1');
+
+// Set TTL and compression
+$cache->set('key2', array('hello', 'world'), array(
+    'ttl' => 10,
+    'compress' => true
+)); 
+
+sleep(11);
+
+$cache->get('key2');
+
+$cache->delete('key1');
+
+$cache->clear(); // clear all caches by removing the root path of the cache
+```

+ 17 - 0
vendor/ark/filecache/composer.json

@@ -0,0 +1,17 @@
+{
+    "name": "ark/filecache",
+    "description": "Cache with file system",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "dong",
+            "email": "ddliuhb@gmail.com"
+        }
+    ],
+    "require": {},
+    "autoload": {
+        "psr-4": {
+            "Ark\\Filecache\\": "src/"
+        }
+    }
+}

+ 9 - 0
vendor/ark/filecache/phpunit.xml

@@ -0,0 +1,9 @@
+<phpunit
+    bootstrap="vendor/autoload.php"
+    >
+  <testsuites>
+    <testsuite name="filecache">
+      <directory>tests</directory>
+    </testsuite>
+  </testsuites>
+</phpunit>

+ 242 - 0
vendor/ark/filecache/src/FileCache.php

@@ -0,0 +1,242 @@
+<?php
+/**
+ * filecache
+ * @copyright 2015 Liu Dong <ddliuhb@gmail.com>
+ * @license MIT
+ */
+
+namespace Ark\Filecache;
+
+class FileCache {
+    protected $options = array();
+
+    public function __construct($options = array()) {
+        $this->options = array_merge(array(
+            'root' => '.',
+            'hash' => 'md5',
+            'serialize' => 'php',
+            'depth' => 3,
+            'compress' => false,
+            'ttl' => 0,
+        ), $options);
+    }
+
+    /**
+     * Set cache
+     * @param string $key The cache key
+     * @param mixed $data Data to store
+     * @param array $options Override default options
+     */
+    public function set($key, $data, $options = array())
+    {
+        $path = $this->getPath($key);
+        $options = array_merge(array(
+            'serialize' => $this->options['serialize'],
+            'ttl' => $this->options['ttl'],
+            'compress' => $this->options['compress']?1:0
+        ), $options);
+
+        if (isset($options['ttl']) && $options['ttl']) {
+            $options['expires'] = time() + $options['ttl'];
+        }
+
+        if (isset($options['ttl'])) {
+            unset($options['ttl']);
+        }
+
+        $options['time'] = time();
+
+        $content = '';
+        foreach ($options as $key => $value) {
+            $content .= $key . ':' . $value . "\n";
+        }
+
+        $content .= "\n";
+
+        // serialize
+        $data = $this->serialize($data, $options['serialize']);
+
+        // compress
+        if ($options['compress']) {
+            $data = gzcompress($data);
+        }
+
+        $content .=  $data;
+
+        return Util::writeFile($path, $content);
+    }
+
+    /**
+     * Get cache with key
+     * @param  string $key
+     * @return mixed
+     */
+    public function get($key)
+    {
+        if (!$meta = $this->getMeta($key)) {
+            return false;
+        }
+
+        // cache expired
+        if (!empty($meta['expires']) && $meta['expires'] < time()) {
+            return false;
+        }
+
+        if (!$data = file_get_contents($this->getPath($key), false, null, $meta['_META_SIZE'])) {
+            return false;
+        }
+
+        // uncompress
+        if ($meta['compress']) {
+            $data = gzuncompress($data);
+        }
+
+        // unserialize
+        $data = $this->unserialize($data, $meta['serialize']);
+
+        return $data;
+    }
+
+    /**
+     * Get metadata of the cache
+     * @param  string $key
+     * @return array|false
+     */
+    public function getMeta($key)
+    {
+        $path = $this->getPath($key);
+        if (!$fp = @fopen($path, 'r')) {
+            return false;
+        }
+
+
+        $data = fread($fp, 100);
+        fclose($fp);
+
+        $endPos = strpos($data, "\n\n");
+        if (!$endPos) {
+            return false;
+        }
+
+        $data = substr($data, 0, $endPos);
+        $data = explode("\n", $data);
+
+        $meta = array();
+
+        foreach ($data as $row) {
+            $row = explode(':', $row, 2);
+            $meta[$row[0]] = $row[1];
+        }
+
+        $meta['_META_SIZE'] = $endPos + 2;
+
+        return $meta;
+    }
+
+    /**
+     * Delete cache by key
+     * @param  string $key
+     * @return boolean
+     */
+    public function delete($key)
+    {
+        $path = $this->getPath($key);
+        if (file_exists($path)) {
+            return unlink($path);
+        }
+
+        return true;
+    }
+
+    /**
+     * Clear all caches by removing the cache root
+     * @return boolean
+     */
+    public function clear() {
+        return Util::removeDir($this->options['root']);
+    }
+
+    /**
+     * Get cache path of the key
+     * @param  string $key
+     * @return string
+     */
+    public function getPath($key) {
+        return $this->options['root'] . '/' . $this->getRelativePathWithCache($key);
+    }
+
+    protected function getRelativePathWithCache($key) {
+        static $cache = array();
+        if (!isset($cache[$key])) {
+            $cache[$key] = $this->genPath($this->getKeyHash($key));
+        }
+
+        if (count($cache) > 100) {
+            $cache = array(
+                $key => $cache[$key],
+            );
+        }
+
+        return $cache[$key];
+    }
+
+    protected function getKeyHash($key) {
+        if ($this->options['hash']) {
+            return $this->options['hash']($key);
+        } else {
+            return $key;
+        }
+    }
+
+    protected function genPath($key) {
+        $depth = $this->options['depth'];
+        if ($depth <= 1) {
+            return $key;
+        }
+
+        $len = strlen($key);
+        $parts = array();
+        for ($i = 0; $i < $len; $i++) {
+            if ($i >= $depth) {
+                $parts[] = substr($key, $i);
+                break;
+            }
+
+            $parts[] = $key[$i];
+        }
+
+        return implode('/', $parts);
+    }
+
+    protected function serialize($data, $type) {
+        if ($type === 'raw') {
+            return (string) $data;
+        }
+
+        if ($type === 'json') {
+            return json_encode($data);
+        }
+
+        if ($type === 'php') {
+            return serialize($data);
+        }
+
+        throw new \Exception('Unknown serialization type: '.$type);
+    }
+
+    protected function unserialize($data, $type) {
+        if ($type === 'raw') {
+            return $data;
+        }
+
+        if ($type === 'json') {
+            return json_decode($data, true);
+        }
+
+        if ($type === 'php') {
+            return unserialize($data);
+        }
+
+        throw new \Exception('Unknown serialization type: '.$type);
+    }
+}

+ 55 - 0
vendor/ark/filecache/src/Util.php

@@ -0,0 +1,55 @@
+<?php
+namespace Ark\Filecache;
+
+class Util {
+    public static function writeFile($path, $content) {
+        $dir = dirname($path);
+        if (!is_dir($dir)) {
+            if (!mkdir($dir, 0777, true)) {
+                return false;
+            }
+        }
+
+        return file_put_contents($path, $content);
+    }
+
+    /**
+     * Remove directory recursively.
+     * @param  string $path
+     * @return boolean
+     */
+    public static function removeDir($path) {
+        if (!is_dir($path)) {
+            return true;
+        }
+
+        if (!$dirh = opendir($path)) {
+            return false;
+        }
+
+        $result = true;
+        while (($file = readdir($dirh)) !== false) {
+            if ($file === '.' || $file === '..') {
+                continue;
+            }
+
+            $child = $path . '/' . $file;
+            if (is_dir($child)) {
+                $result = self::removeDir($child);
+            } else {
+                $result = unlink($child);
+            }
+
+            if (!$result) {
+                break;
+            }
+        }
+
+        closedir($dirh);
+        if (!$result) {
+            return false;
+        }
+
+        return rmdir($path);
+    }
+}

+ 56 - 0
vendor/ark/filecache/tests/baseTest.php

@@ -0,0 +1,56 @@
+<?php
+use Ark\Filecache\FileCache;
+
+class BaseTest extends PHPUnit_Framework_TestCase {
+    protected $cache;
+    public function setUp() {
+        $this->cache = new FileCache([
+            'root' => __DIR__ . '/cache',
+        ]);
+    }
+
+    public function tearDown() {
+        $this->cache->clear();
+
+        // test clear twice
+        $this->cache->clear();
+    }
+
+    public function testSet() {
+        $key = __FUNCTION__;
+        $value = 'hello world';
+        $this->cache->set($key, $value);
+        $this->assertEquals($this->cache->get($key), $value);
+    }
+
+    public function testTTL() {
+        $key = __FUNCTION__;
+        $value = 'hello world';
+        $this->cache->set($key, $value, array(
+            'ttl' => 2,
+        ));
+        $this->assertEquals($this->cache->get($key), $value);
+        sleep(3);
+        $this->assertEquals($this->cache->get($key), false);
+    }
+
+    public function testCompress() {
+        $key = __FUNCTION__;
+        $value = 'hello world';
+        $this->cache->set($key, $value, array(
+            'compress' => true
+        ));
+        $meta = $this->cache->getMeta($key);
+        $this->assertEquals($meta['compress'], '1');
+        $this->assertEquals($this->cache->get($key), $value);
+    }
+
+    public function testDelete()
+    {
+        $key = __FUNCTION__;
+        $value = 'hello world';
+        $this->cache->set($key, $value);
+        $this->cache->delete($key);
+        $this->assertEquals($this->cache->get($key), false);
+    }
+}

+ 7 - 0
vendor/autoload.php

@@ -0,0 +1,7 @@
+<?php
+
+// autoload.php @generated by Composer
+
+require_once __DIR__ . '/composer/autoload_real.php';
+
+return ComposerAutoloaderInit94d738e06733adb5a3d0bba9fcfc1b17::getLoader();

+ 1 - 0
vendor/bin/var-dump-server

@@ -0,0 +1 @@
+../symfony/var-dumper/Resources/bin/var-dump-server

+ 559 - 0
vendor/cache/adapter-common/AbstractCachePool.php

@@ -0,0 +1,559 @@
+<?php
+
+/*
+ * This file is part of php-cache organization.
+ *
+ * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace Cache\Adapter\Common;
+
+use Cache\Adapter\Common\Exception\CacheException;
+use Cache\Adapter\Common\Exception\CachePoolException;
+use Cache\Adapter\Common\Exception\InvalidArgumentException;
+use Psr\Cache\CacheItemInterface;
+use Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerInterface;
+use Psr\SimpleCache\CacheInterface;
+
+/**
+ * @author Aaron Scherer <aequasi@gmail.com>
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ */
+abstract class AbstractCachePool implements PhpCachePool, LoggerAwareInterface, CacheInterface
+{
+    const SEPARATOR_TAG = '!';
+
+    /**
+     * @type LoggerInterface
+     */
+    private $logger;
+
+    /**
+     * @type PhpCacheItem[] deferred
+     */
+    protected $deferred = [];
+
+    /**
+     * @param PhpCacheItem $item
+     * @param int|null     $ttl  seconds from now
+     *
+     * @return bool true if saved
+     */
+    abstract protected function storeItemInCache(PhpCacheItem $item, $ttl);
+
+    /**
+     * Fetch an object from the cache implementation.
+     *
+     * If it is a cache miss, it MUST return [false, null, [], null]
+     *
+     * @param string $key
+     *
+     * @return array with [isHit, value, tags[], expirationTimestamp]
+     */
+    abstract protected function fetchObjectFromCache($key);
+
+    /**
+     * Clear all objects from cache.
+     *
+     * @return bool false if error
+     */
+    abstract protected function clearAllObjectsFromCache();
+
+    /**
+     * Remove one object from cache.
+     *
+     * @param string $key
+     *
+     * @return bool
+     */
+    abstract protected function clearOneObjectFromCache($key);
+
+    /**
+     * Get an array with all the values in the list named $name.
+     *
+     * @param string $name
+     *
+     * @return array
+     */
+    abstract protected function getList($name);
+
+    /**
+     * Remove the list.
+     *
+     * @param string $name
+     *
+     * @return bool
+     */
+    abstract protected function removeList($name);
+
+    /**
+     * Add a item key on a list named $name.
+     *
+     * @param string $name
+     * @param string $key
+     */
+    abstract protected function appendListItem($name, $key);
+
+    /**
+     * Remove an item from the list.
+     *
+     * @param string $name
+     * @param string $key
+     */
+    abstract protected function removeListItem($name, $key);
+
+    /**
+     * Make sure to commit before we destruct.
+     */
+    public function __destruct()
+    {
+        $this->commit();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getItem($key)
+    {
+        $this->validateKey($key);
+        if (isset($this->deferred[$key])) {
+            /** @type CacheItem $item */
+            $item = clone $this->deferred[$key];
+            $item->moveTagsToPrevious();
+
+            return $item;
+        }
+
+        $func = function () use ($key) {
+            try {
+                return $this->fetchObjectFromCache($key);
+            } catch (\Exception $e) {
+                $this->handleException($e, __FUNCTION__);
+            }
+        };
+
+        return new CacheItem($key, $func);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getItems(array $keys = [])
+    {
+        $items = [];
+        foreach ($keys as $key) {
+            $items[$key] = $this->getItem($key);
+        }
+
+        return $items;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasItem($key)
+    {
+        try {
+            return $this->getItem($key)->isHit();
+        } catch (\Exception $e) {
+            $this->handleException($e, __FUNCTION__);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function clear()
+    {
+        // Clear the deferred items
+        $this->deferred = [];
+
+        try {
+            return $this->clearAllObjectsFromCache();
+        } catch (\Exception $e) {
+            $this->handleException($e, __FUNCTION__);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function deleteItem($key)
+    {
+        try {
+            return $this->deleteItems([$key]);
+        } catch (\Exception $e) {
+            $this->handleException($e, __FUNCTION__);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function deleteItems(array $keys)
+    {
+        $deleted = true;
+        foreach ($keys as $key) {
+            $this->validateKey($key);
+
+            // Delete form deferred
+            unset($this->deferred[$key]);
+
+            // We have to commit here to be able to remove deferred hierarchy items
+            $this->commit();
+            $this->preRemoveItem($key);
+
+            if (!$this->clearOneObjectFromCache($key)) {
+                $deleted = false;
+            }
+        }
+
+        return $deleted;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function save(CacheItemInterface $item)
+    {
+        if (!$item instanceof PhpCacheItem) {
+            $e = new InvalidArgumentException('Cache items are not transferable between pools. Item MUST implement PhpCacheItem.');
+            $this->handleException($e, __FUNCTION__);
+        }
+
+        $this->removeTagEntries($item);
+        $this->saveTags($item);
+        $timeToLive = null;
+        if (null !== $timestamp = $item->getExpirationTimestamp()) {
+            $timeToLive = $timestamp - time();
+
+            if ($timeToLive < 0) {
+                return $this->deleteItem($item->getKey());
+            }
+        }
+
+        try {
+            return $this->storeItemInCache($item, $timeToLive);
+        } catch (\Exception $e) {
+            $this->handleException($e, __FUNCTION__);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function saveDeferred(CacheItemInterface $item)
+    {
+        $this->deferred[$item->getKey()] = $item;
+
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function commit()
+    {
+        $saved = true;
+        foreach ($this->deferred as $item) {
+            if (!$this->save($item)) {
+                $saved = false;
+            }
+        }
+        $this->deferred = [];
+
+        return $saved;
+    }
+
+    /**
+     * @param string $key
+     *
+     * @throws InvalidArgumentException
+     */
+    protected function validateKey($key)
+    {
+        if (!is_string($key)) {
+            $e = new InvalidArgumentException(sprintf(
+                'Cache key must be string, "%s" given',
+                gettype($key)
+            ));
+            $this->handleException($e, __FUNCTION__);
+        }
+        if (!isset($key[0])) {
+            $e = new InvalidArgumentException('Cache key cannot be an empty string');
+            $this->handleException($e, __FUNCTION__);
+        }
+        if (preg_match('|[\{\}\(\)/\\\@\:]|', $key)) {
+            $e = new InvalidArgumentException(sprintf(
+                'Invalid key: "%s". The key contains one or more characters reserved for future extension: {}()/\@:',
+                $key
+            ));
+            $this->handleException($e, __FUNCTION__);
+        }
+    }
+
+    /**
+     * @param LoggerInterface $logger
+     */
+    public function setLogger(LoggerInterface $logger)
+    {
+        $this->logger = $logger;
+    }
+
+    /**
+     * Logs with an arbitrary level if the logger exists.
+     *
+     * @param mixed  $level
+     * @param string $message
+     * @param array  $context
+     */
+    protected function log($level, $message, array $context = [])
+    {
+        if ($this->logger !== null) {
+            $this->logger->log($level, $message, $context);
+        }
+    }
+
+    /**
+     * Log exception and rethrow it.
+     *
+     * @param \Exception $e
+     * @param string     $function
+     *
+     * @throws CachePoolException
+     */
+    private function handleException(\Exception $e, $function)
+    {
+        $level = 'alert';
+        if ($e instanceof InvalidArgumentException) {
+            $level = 'warning';
+        }
+
+        $this->log($level, $e->getMessage(), ['exception' => $e]);
+        if (!$e instanceof CacheException) {
+            $e = new CachePoolException(sprintf('Exception thrown when executing "%s". ', $function), 0, $e);
+        }
+
+        throw $e;
+    }
+
+    /**
+     * @param array $tags
+     *
+     * @return bool
+     */
+    public function invalidateTags(array $tags)
+    {
+        $itemIds = [];
+        foreach ($tags as $tag) {
+            $itemIds = array_merge($itemIds, $this->getList($this->getTagKey($tag)));
+        }
+
+        // Remove all items with the tag
+        $success = $this->deleteItems($itemIds);
+
+        if ($success) {
+            // Remove the tag list
+            foreach ($tags as $tag) {
+                $this->removeList($this->getTagKey($tag));
+                $l = $this->getList($this->getTagKey($tag));
+            }
+        }
+
+        return $success;
+    }
+
+    public function invalidateTag($tag)
+    {
+        return $this->invalidateTags([$tag]);
+    }
+
+    /**
+     * @param PhpCacheItem $item
+     */
+    protected function saveTags(PhpCacheItem $item)
+    {
+        $tags = $item->getTags();
+        foreach ($tags as $tag) {
+            $this->appendListItem($this->getTagKey($tag), $item->getKey());
+        }
+    }
+
+    /**
+     * Removes the key form all tag lists. When an item with tags is removed
+     * we MUST remove the tags. If we fail to remove the tags a new item with
+     * the same key will automatically get the previous tags.
+     *
+     * @param string $key
+     *
+     * @return $this
+     */
+    protected function preRemoveItem($key)
+    {
+        $item = $this->getItem($key);
+        $this->removeTagEntries($item);
+
+        return $this;
+    }
+
+    /**
+     * @param PhpCacheItem $item
+     */
+    private function removeTagEntries(PhpCacheItem $item)
+    {
+        $tags = $item->getPreviousTags();
+        foreach ($tags as $tag) {
+            $this->removeListItem($this->getTagKey($tag), $item->getKey());
+        }
+    }
+
+    /**
+     * @param string $tag
+     *
+     * @return string
+     */
+    protected function getTagKey($tag)
+    {
+        return 'tag'.self::SEPARATOR_TAG.$tag;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function get($key, $default = null)
+    {
+        $item = $this->getItem($key);
+        if (!$item->isHit()) {
+            return $default;
+        }
+
+        return $item->get();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function set($key, $value, $ttl = null)
+    {
+        $item = $this->getItem($key);
+        $item->set($value);
+        $item->expiresAfter($ttl);
+
+        return $this->save($item);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function delete($key)
+    {
+        return $this->deleteItem($key);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getMultiple($keys, $default = null)
+    {
+        if (!is_array($keys)) {
+            if (!$keys instanceof \Traversable) {
+                throw new InvalidArgumentException('$keys is neither an array nor Traversable');
+            }
+
+            // Since we need to throw an exception if *any* key is invalid, it doesn't
+            // make sense to wrap iterators or something like that.
+            $keys = iterator_to_array($keys, false);
+        }
+
+        $items = $this->getItems($keys);
+
+        return $this->generateValues($default, $items);
+    }
+
+    /**
+     * @param $default
+     * @param $items
+     *
+     * @return \Generator
+     */
+    private function generateValues($default, $items)
+    {
+        foreach ($items as $key => $item) {
+            /** @type $item CacheItemInterface */
+            if (!$item->isHit()) {
+                yield $key => $default;
+            } else {
+                yield $key => $item->get();
+            }
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setMultiple($values, $ttl = null)
+    {
+        if (!is_array($values)) {
+            if (!$values instanceof \Traversable) {
+                throw new InvalidArgumentException('$values is neither an array nor Traversable');
+            }
+        }
+
+        $keys        = [];
+        $arrayValues = [];
+        foreach ($values as $key => $value) {
+            if (is_int($key)) {
+                $key = (string) $key;
+            }
+            $this->validateKey($key);
+            $keys[]            = $key;
+            $arrayValues[$key] = $value;
+        }
+
+        $items       = $this->getItems($keys);
+        $itemSuccess = true;
+        foreach ($items as $key => $item) {
+            $item->set($arrayValues[$key]);
+
+            try {
+                $item->expiresAfter($ttl);
+            } catch (InvalidArgumentException $e) {
+                throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
+            }
+
+            $itemSuccess = $itemSuccess && $this->saveDeferred($item);
+        }
+
+        return $itemSuccess && $this->commit();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function deleteMultiple($keys)
+    {
+        if (!is_array($keys)) {
+            if (!$keys instanceof \Traversable) {
+                throw new InvalidArgumentException('$keys is neither an array nor Traversable');
+            }
+
+            // Since we need to throw an exception if *any* key is invalid, it doesn't
+            // make sense to wrap iterators or something like that.
+            $keys = iterator_to_array($keys, false);
+        }
+
+        return $this->deleteItems($keys);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function has($key)
+    {
+        return $this->hasItem($key);
+    }
+}

+ 269 - 0
vendor/cache/adapter-common/CacheItem.php

@@ -0,0 +1,269 @@
+<?php
+
+/*
+ * This file is part of php-cache organization.
+ *
+ * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace Cache\Adapter\Common;
+
+use Cache\Adapter\Common\Exception\InvalidArgumentException;
+use Cache\TagInterop\TaggableCacheItemInterface;
+
+/**
+ * @author Aaron Scherer <aequasi@gmail.com>
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ */
+class CacheItem implements PhpCacheItem
+{
+    /**
+     * @type array
+     */
+    private $prevTags = [];
+
+    /**
+     * @type array
+     */
+    private $tags = [];
+
+    /**
+     * @type \Closure
+     */
+    private $callable;
+
+    /**
+     * @type string
+     */
+    private $key;
+
+    /**
+     * @type mixed
+     */
+    private $value;
+
+    /**
+     * The expiration timestamp is the source of truth. This is the UTC timestamp
+     * when the cache item expire. A value of zero means it never expires. A nullvalue
+     * means that no expiration is set.
+     *
+     * @type int|null
+     */
+    private $expirationTimestamp = null;
+
+    /**
+     * @type bool
+     */
+    private $hasValue = false;
+
+    /**
+     * @param string        $key
+     * @param \Closure|bool $callable or boolean hasValue
+     */
+    public function __construct($key, $callable = null, $value = null)
+    {
+        $this->key = $key;
+
+        if ($callable === true) {
+            $this->hasValue = true;
+            $this->value    = $value;
+        } elseif ($callable !== false) {
+            // This must be a callable or null
+            $this->callable = $callable;
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getKey()
+    {
+        return $this->key;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function set($value)
+    {
+        $this->value    = $value;
+        $this->hasValue = true;
+        $this->callable = null;
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function get()
+    {
+        if (!$this->isHit()) {
+            return;
+        }
+
+        return $this->value;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isHit()
+    {
+        $this->initialize();
+
+        if (!$this->hasValue) {
+            return false;
+        }
+
+        if ($this->expirationTimestamp !== null) {
+            return $this->expirationTimestamp > time();
+        }
+
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getExpirationTimestamp()
+    {
+        return $this->expirationTimestamp;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function expiresAt($expiration)
+    {
+        if ($expiration instanceof \DateTimeInterface) {
+            $this->expirationTimestamp = $expiration->getTimestamp();
+        } elseif (is_int($expiration) || null === $expiration) {
+            $this->expirationTimestamp = $expiration;
+        } else {
+            throw new InvalidArgumentException('Cache item ttl/expiresAt must be of type integer or \DateTimeInterface.');
+        }
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function expiresAfter($time)
+    {
+        if ($time === null) {
+            $this->expirationTimestamp = null;
+        } elseif ($time instanceof \DateInterval) {
+            $date = new \DateTime();
+            $date->add($time);
+            $this->expirationTimestamp = $date->getTimestamp();
+        } elseif (is_int($time)) {
+            $this->expirationTimestamp = time() + $time;
+        } else {
+            throw new InvalidArgumentException('Cache item ttl/expiresAfter must be of type integer or \DateInterval.');
+        }
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getPreviousTags()
+    {
+        $this->initialize();
+
+        return $this->prevTags;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getTags()
+    {
+        return $this->tags;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setTags(array $tags)
+    {
+        $this->tags = [];
+        $this->tag($tags);
+
+        return $this;
+    }
+
+    /**
+     * Adds a tag to a cache item.
+     *
+     * @param string|string[] $tags A tag or array of tags
+     *
+     * @throws InvalidArgumentException When $tag is not valid.
+     *
+     * @return TaggableCacheItemInterface
+     */
+    private function tag($tags)
+    {
+        $this->initialize();
+
+        if (!is_array($tags)) {
+            $tags = [$tags];
+        }
+        foreach ($tags as $tag) {
+            if (!is_string($tag)) {
+                throw new InvalidArgumentException(sprintf('Cache tag must be string, "%s" given', is_object($tag) ? get_class($tag) : gettype($tag)));
+            }
+            if (isset($this->tags[$tag])) {
+                continue;
+            }
+            if (!isset($tag[0])) {
+                throw new InvalidArgumentException('Cache tag length must be greater than zero');
+            }
+            if (isset($tag[strcspn($tag, '{}()/\@:')])) {
+                throw new InvalidArgumentException(sprintf('Cache tag "%s" contains reserved characters {}()/\@:', $tag));
+            }
+            $this->tags[$tag] = $tag;
+        }
+
+        return $this;
+    }
+
+    /**
+     * If callable is not null, execute it an populate this object with values.
+     */
+    private function initialize()
+    {
+        if ($this->callable !== null) {
+            // $func will be $adapter->fetchObjectFromCache();
+            $func                      = $this->callable;
+            $result                    = $func();
+            $this->hasValue            = $result[0];
+            $this->value               = $result[1];
+            $this->prevTags            = isset($result[2]) ? $result[2] : [];
+            $this->expirationTimestamp = null;
+
+            if (isset($result[3]) && is_int($result[3])) {
+                $this->expirationTimestamp = $result[3];
+            }
+
+            $this->callable = null;
+        }
+    }
+
+    /**
+     * @internal This function should never be used and considered private.
+     *
+     * Move tags from $tags to $prevTags
+     */
+    public function moveTagsToPrevious()
+    {
+        $this->prevTags = $this->tags;
+        $this->tags     = [];
+    }
+}

+ 67 - 0
vendor/cache/adapter-common/Changelog.md

@@ -0,0 +1,67 @@
+# Change Log
+
+The change log describes what is "Added", "Removed", "Changed" or "Fixed" between each release.
+
+## 1.2.0
+
+### Added
+
+* Support for PHP 8
+
+## 1.1.0
+
+### Added
+
+- Support for storing binary data
+
+### Fixed
+
+- Issue with one character variables
+
+### Changed
+
+- Tests are now extending `PHPUnit\Framework\TestCase`
+
+## 1.0.0
+
+* No changes since 0.4.0.
+
+## 0.4.0
+
+### Added
+
+* `AbstractCachePool` has 4 new abstract methods: `getList`, `removeList`, `appendListItem` and `removeListItem`.
+* `AbstractCachePool::invalidateTags` and `AbstractCachePool::invalidateTags`
+* Added interfaces for our items and pools `PhpCachePool` and `PhpCacheItem`
+* Trait to help adapters to support tags. `TagSupportWithArray`.
+
+### Changed
+
+* First parameter to `AbstractCachePool::storeItemInCache` must be a `PhpCacheItem`.
+* Return value from `AbstractCachePool::fetchObjectFromCache` must be a an array with 4 values. Added expiration timestamp.
+* `HasExpirationDateInterface` is replaced by `HasExpirationTimestampInterface`
+* We do not work with `\DateTime` internally anymore. We work with timestamps.
+
+## 0.3.3
+
+### Fixed
+
+* Bugfix when you fetch data from the cache storage that was saved as "non-tagging item" but fetch as a tagging item.
+
+## 0.3.2
+
+### Added
+
+* Cache pools do implement `LoggerAwareInterface`
+
+## 0.3.0
+
+### Changed
+
+* The `AbstractCachePool` does not longer implement `TaggablePoolInterface`. However, the `CacheItem` does still implement `TaggableItemInterface`.
+* `CacheItem::getKeyFromTaggedKey` has been removed
+* The `CacheItem`'s second parameter is a callable that must return an array with 3 elements; [`hasValue`, `value`, `tags`].
+
+## 0.2.0
+
+* No changelog before this version

+ 23 - 0
vendor/cache/adapter-common/Exception/CacheException.php

@@ -0,0 +1,23 @@
+<?php
+
+/*
+ * This file is part of php-cache organization.
+ *
+ * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace Cache\Adapter\Common\Exception;
+
+use Psr\Cache\CacheException as CacheExceptionInterface;
+
+/**
+ * A base exception. All exceptions in this organization will extend this exception.
+ *
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ */
+abstract class CacheException extends \RuntimeException implements CacheExceptionInterface
+{
+}

+ 21 - 0
vendor/cache/adapter-common/Exception/CachePoolException.php

@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of php-cache organization.
+ *
+ * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace Cache\Adapter\Common\Exception;
+
+/**
+ * If an exception is caused by a pool or by the cache storage.
+ *
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ */
+class CachePoolException extends CacheException
+{
+}

+ 19 - 0
vendor/cache/adapter-common/Exception/InvalidArgumentException.php

@@ -0,0 +1,19 @@
+<?php
+
+/*
+ * This file is part of php-cache organization.
+ *
+ * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace Cache\Adapter\Common\Exception;
+
+use Psr\Cache\InvalidArgumentException as CacheInvalidArgumentException;
+use Psr\SimpleCache\InvalidArgumentException as SimpleCacheInvalidArgumentException;
+
+class InvalidArgumentException extends CacheException implements CacheInvalidArgumentException, SimpleCacheInvalidArgumentException
+{
+}

+ 26 - 0
vendor/cache/adapter-common/HasExpirationTimestampInterface.php

@@ -0,0 +1,26 @@
+<?php
+
+/*
+ * This file is part of php-cache organization.
+ *
+ * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace Cache\Adapter\Common;
+
+/**
+ * @author Aaron Scherer <aequasi@gmail.com>
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ */
+interface HasExpirationTimestampInterface
+{
+    /**
+     * The timestamp when the object expires.
+     *
+     * @return int|null
+     */
+    public function getExpirationTimestamp();
+}

+ 68 - 0
vendor/cache/adapter-common/JsonBinaryArmoring.php

@@ -0,0 +1,68 @@
+<?php
+
+/*
+ * This file is part of php-cache organization.
+ *
+ * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace Cache\Adapter\Common;
+
+/**
+ * This trait provides common routines for safely encoding binary and non-UTF8 data in
+ * JSON. This is needed for components that use JSON natively (currently, the MongoDB
+ * adapter and EncryptedCachePool).
+ *
+ * @author Stephen Clouse <stephen.clouse@noaa.gov>
+ */
+trait JsonBinaryArmoring
+{
+    private static $ESCAPE_JSON_CHARACTERS = [
+        "\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07",
+        "\x08", "\x09", "\x0A", "\x0B", "\x0C", "\x0D", "\x0E", "\x0F",
+        "\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17",
+        "\x18", "\x19", "\x1A", "\x1B", "\x1C", "\x1D", "\x1E", "\x1F",
+    ];
+
+    private static $ENCODED_JSON_CHARACTERS = [
+        '\u0000', '\u0001', '\u0002', '\u0003', '\u0004', '\u0005', '\u0006', '\u0007',
+        '\u0008', '\u0009', '\u000A', '\u000B', '\u000C', '\u000D', '\u000E', '\u000F',
+        '\u0010', '\u0011', '\u0012', '\u0013', '\u0014', '\u0015', '\u0016', '\u0017',
+        '\u0018', '\u0019', '\u001A', '\u001B', '\u001C', '\u001D', '\u001E', '\u001F',
+    ];
+
+    /**
+     * Armor a value going into a JSON document.
+     *
+     * @param string $value
+     *
+     * @return string
+     */
+    protected static function jsonArmor($value)
+    {
+        return str_replace(
+            static::$ESCAPE_JSON_CHARACTERS,
+            static::$ENCODED_JSON_CHARACTERS,
+            utf8_encode($value)
+        );
+    }
+
+    /**
+     * De-armor a value from a JSON document.
+     *
+     * @param string $value
+     *
+     * @return string
+     */
+    protected static function jsonDeArmor($value)
+    {
+        return utf8_decode(str_replace(
+            static::$ENCODED_JSON_CHARACTERS,
+            static::$ESCAPE_JSON_CHARACTERS,
+            $value
+        ));
+    }
+}

+ 22 - 0
vendor/cache/adapter-common/LICENSE

@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Aaron Scherer, Tobias Nyholm
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio