陈德本 пре 7 година
комит
1acd586aaa
100 измењених фајлова са 13904 додато и 0 уклоњено
  1. 10 0
      .idea/MailApi.iml
  2. 27 0
      .idea/inspectionProfiles/Project_Default.xml
  3. 26 0
      .idea/misc.xml
  4. 8 0
      .idea/modules.xml
  5. 4 0
      .idea/php.xml
  6. 6 0
      .idea/vcs.xml
  7. 824 0
      .idea/workspace.xml
  8. 23 0
      App/Api/Config/Route.php
  9. 16 0
      App/Api/Config/Vestacp.php
  10. 95 0
      App/Api/Controller.php
  11. 53 0
      App/Api/Controller/Cloudflare.php
  12. 82 0
      App/Api/Controller/Imap.php
  13. 48 0
      App/Api/Controller/Index.php
  14. 63 0
      App/Api/Controller/Mail/Count/Unread.php
  15. 81 0
      App/Api/Controller/Mail/Lists.php
  16. 33 0
      App/Api/Controller/Telegram.php
  17. 19 0
      App/Api/Controller/Test.php
  18. 53 0
      App/Api/Controller/User/Auth.php
  19. 58 0
      App/Api/Controller/User/Lists.php
  20. 78 0
      App/Api/Controller/User/User.php
  21. 149 0
      App/Api/Controller/Vestacp/Domain.php
  22. 48 0
      App/Api/Controller/Vestacp/Users.php
  23. 15 0
      App/Api/Package/Database/Mail.php
  24. 41 0
      App/Api/RestfulController.php
  25. 48 0
      App/Api/Template/Detail.phtml
  26. 85 0
      App/Api/Template/Domain.phtml
  27. 67 0
      App/Api/Template/Index.phtml
  28. 35 0
      App/Script/Controller.php
  29. 45 0
      App/Script/Controller/Server.php
  30. 18 0
      App/Script/Package/Database/Code.php
  31. 24 0
      App/Test/AipTest.php
  32. 50 0
      App/Test/Test.php
  33. 29 0
      Autoload.php
  34. 37 0
      Config/MySQL/Core.php
  35. 32 0
      Config/Route.php
  36. 98 0
      Frame/Config.php
  37. 283 0
      Frame/Controller.php
  38. 187 0
      Frame/Dispatcher.php
  39. 20 0
      Frame/Exception.php
  40. 13 0
      Frame/Exception/Database.php
  41. 12 0
      Frame/Exception/Validate.php
  42. 251 0
      Frame/Filter.php
  43. 341 0
      Frame/Helper.php
  44. 67 0
      Frame/Helper/Date.php
  45. 39 0
      Frame/Helper/File.php
  46. 92 0
      Frame/Helper/Format.php
  47. 269 0
      Frame/Helper/Html.php
  48. 31 0
      Frame/Helper/Location.php
  49. 11 0
      Frame/Helper/Network.php
  50. 54 0
      Frame/Helper/Validate.php
  51. 77 0
      Frame/Init.php
  52. 58 0
      Frame/Library.php
  53. 83 0
      Frame/Library/AntiSpam.php
  54. 161 0
      Frame/Library/BaseConverter.php
  55. 8 0
      Frame/Library/Benchmark.php
  56. 19 0
      Frame/Library/Curl.php
  57. 44 0
      Frame/Library/Memcache.php
  58. 41 0
      Frame/Library/Memcached.php
  59. 252 0
      Frame/Library/MySQL.php
  60. 402 0
      Frame/Library/MySQL/Query.php
  61. 63 0
      Frame/Library/Redis.php
  62. 79 0
      Frame/Library/Ticket.php
  63. 347 0
      Frame/Logger.php
  64. 85 0
      Frame/Model.php
  65. 59 0
      Frame/Object.php
  66. 56 0
      Frame/Package.php
  67. 11 0
      Frame/Package/Cache.php
  68. 152 0
      Frame/Package/Cache/Memcache.php
  69. 179 0
      Frame/Package/Cache/Redis.php
  70. 11 0
      Frame/Package/Counter.php
  71. 148 0
      Frame/Package/Counter/Hash.php
  72. 141 0
      Frame/Package/Counter/Single.php
  73. 13 0
      Frame/Package/Database.php
  74. 275 0
      Frame/Package/Database/Common.php
  75. 342 0
      Frame/Package/Database/Sharding.php
  76. 421 0
      Frame/Package/Database/Single.php
  77. 62 0
      Frame/Package/Queue.php
  78. 82 0
      Frame/Package/RedisQueue.php
  79. 291 0
      Frame/Package/Set.php
  80. 203 0
      Frame/Request.php
  81. 104 0
      Frame/Response.php
  82. 39 0
      Frame/Router.php
  83. 148 0
      Frame/Session.php
  84. 234 0
      Frame/Template.php
  85. 1 0
      Library/BaiduVoice/05107d0a3e97b26cc1ac77ddcc4f79fd
  86. 367 0
      Library/BaiduVoice/AipBCEUtil.php
  87. 343 0
      Library/BaiduVoice/AipBase.php
  88. 192 0
      Library/BaiduVoice/AipHttpClient.php
  89. 36 0
      Library/BaiduVoice/AipImageUtil.php
  90. 218 0
      Library/Curl.php
  91. 1280 0
      Library/Medoo/Medoo.php
  92. 19 0
      Library/Telegram.php
  93. 38 0
      Library/Vestacp.php
  94. 8 0
      apidoc.json
  95. 12 0
      composer.json
  96. 2520 0
      composer.lock
  97. BIN
      composer.phar
  98. 18 0
      constants.php
  99. 58 0
      functions.php
  100. 6 0
      index.php

+ 10 - 0
.idea/MailApi.iml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$" isTestSource="false" packagePrefix="Heanup" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>

+ 27 - 0
.idea/inspectionProfiles/Project_Default.xml

@@ -0,0 +1,27 @@
+<component name="InspectionProjectProfileManager">
+  <profile version="1.0">
+    <option name="myName" value="Project Default" />
+    <inspection_tool class="CheckValidXmlInScriptTagBody" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="HtmlUnknownAttribute" enabled="true" level="WARNING" enabled_by_default="true">
+      <option name="myValues">
+        <value>
+          <list size="1">
+            <item index="0" class="java.lang.String" itemvalue="placeholder" />
+          </list>
+        </value>
+      </option>
+      <option name="myCustomValuesEnabled" value="true" />
+    </inspection_tool>
+    <inspection_tool class="HtmlUnknownTarget" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="JSUnresolvedFunction" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
+    <inspection_tool class="JSUnresolvedVariable" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
+    <inspection_tool class="PhpIncludeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="PhpSignatureMismatchDuringInheritanceInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="PhpUndefinedMethodInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
+      <option name="processCode" value="true" />
+      <option name="processLiterals" value="true" />
+      <option name="processComments" value="true" />
+    </inspection_tool>
+  </profile>
+</component>

+ 26 - 0
.idea/misc.xml

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="JavaScriptSettings">
+    <option name="languageLevel" value="ES6" />
+  </component>
+  <component name="ProjectInspectionProfilesVisibleTreeState">
+    <entry key="Project Default">
+      <profile-state>
+        <expanded-state>
+          <State>
+            <id />
+          </State>
+          <State>
+            <id>Bitwise operation issuesJavaScript</id>
+          </State>
+          <State>
+            <id>DOM issuesJavaScript</id>
+          </State>
+          <State>
+            <id>JavaScript</id>
+          </State>
+        </expanded-state>
+      </profile-state>
+    </entry>
+  </component>
+</project>

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/MailApi.iml" filepath="$PROJECT_DIR$/.idea/MailApi.iml" />
+    </modules>
+  </component>
+</project>

+ 4 - 0
.idea/php.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="PhpProjectSharedConfiguration" php_language_level="7" />
+</project>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$/public/api/ysmailclient" vcs="Git" />
+  </component>
+</project>

+ 824 - 0
.idea/workspace.xml

@@ -0,0 +1,824 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ChangeListManager">
+    <list default="true" id="694fa49d-7430-4dc6-80e8-d6e73ebd57e6" name="Default Changelist" comment="">
+      <change beforePath="$PROJECT_DIR$/public/api/ysmailclient/index.html" beforeDir="false" afterPath="$PROJECT_DIR$/public/api/ysmailclient/index.html" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/public/api/ysmailclient/js/index.js" beforeDir="false" afterPath="$PROJECT_DIR$/public/api/ysmailclient/js/index.js" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/public/api/ysmailclient/js/ysmail.js" beforeDir="false" afterPath="$PROJECT_DIR$/public/api/ysmailclient/js/ysmail.js" afterDir="false" />
+    </list>
+    <option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
+    <option name="SHOW_DIALOG" value="false" />
+    <option name="HIGHLIGHT_CONFLICTS" value="true" />
+    <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
+    <option name="LAST_RESOLUTION" value="IGNORE" />
+  </component>
+  <component name="ComposerSettings">
+    <execution>
+      <executable />
+    </execution>
+  </component>
+  <component name="FileColors">
+    <fileColor scope="Non-Project Files (Material Default)" color="2E3C43" />
+    <fileColor scope="Non-Project Files (Material Darker)" color="323232" />
+    <fileColor scope="Non-Project Files (Material Lighter)" color="eae8e8" />
+    <fileColor scope="Non-Project Files (Material Palenight)" color="2f2e43" />
+  </component>
+  <component name="FileEditorManager">
+    <leaf>
+      <file pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/App/Api/Controller/User/Lists.php">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="380">
+              <caret line="21" column="9" lean-forward="true" selection-start-line="21" selection-start-column="9" selection-end-line="21" selection-end-column="9" />
+              <folding>
+                <element signature="e#52#93#0#PHP" expanded="true" />
+              </folding>
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file pinned="false" current-in-tab="true">
+        <entry file="file://$PROJECT_DIR$/App/Script/Controller/Server.php">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="290">
+              <caret line="42" selection-start-line="42" selection-end-line="42" />
+              <folding>
+                <element signature="e#135#176#0#PHP" expanded="true" />
+              </folding>
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/Library/Vestacp.php">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="272">
+              <caret line="35" selection-start-line="35" selection-end-line="35" />
+              <folding>
+                <element signature="e#36#65#0#PHP" expanded="true" />
+              </folding>
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/vendor/proficlos/vestacp-api/src/VestaCP/Client.php">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="161">
+              <caret line="148" selection-start-line="148" selection-end-line="148" />
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/vendor/proficlos/vestacp-api/src/VestaCP/Module/Mails.php">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="294">
+              <caret line="73" selection-start-line="73" selection-end-line="73" />
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/Frame/Controller.php">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="148">
+              <caret line="41" selection-start-line="41" selection-end-line="41" />
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/Frame/Dispatcher.php">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="104">
+              <caret line="120" selection-start-line="120" selection-end-line="120" />
+              <folding>
+                <element signature="e#33#57#0#PHP" expanded="true" />
+              </folding>
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/vendor/guzzlehttp/promises/src/TaskQueue.php">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="259">
+              <caret line="28" selection-start-line="28" selection-end-line="28" />
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="224">
+              <caret line="58" selection-start-line="58" selection-end-line="58" />
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/public/api/ysmailclient/index.html">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="570">
+              <caret line="30" column="29" selection-start-line="30" selection-start-column="29" selection-end-line="30" selection-end-column="29" />
+            </state>
+          </provider>
+        </entry>
+      </file>
+    </leaf>
+  </component>
+  <component name="FindInProjectRecents">
+    <findStrings>
+      <find>Phplife</find>
+      <find>PhpLife</find>
+      <find>MeiTu</find>
+      <find>:</find>
+      <find>user/u</find>
+      <find>mail/lists</find>
+      <find>$.YSM.ml</find>
+      <find>mail/count/unread</find>
+      <find>$.YSM.mcu</find>
+      <find>imap_search</find>
+      <find>$.YSM.mc</find>
+      <find>authKey</find>
+      <find>$.YSM.u</find>
+    </findStrings>
+    <replaceStrings>
+      <replace>Heanup</replace>
+      <replace>=&gt;</replace>
+    </replaceStrings>
+  </component>
+  <component name="Git.Settings">
+    <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/public/api/ysmailclient" />
+  </component>
+  <component name="IdeDocumentHistory">
+    <option name="CHANGED_PATHS">
+      <list>
+        <option value="$PROJECT_DIR$/public/index.php" />
+        <option value="$PROJECT_DIR$/vendor/phalapi/kernal/src/functions.php" />
+        <option value="$PROJECT_DIR$/vendor/phalapi/kernal/src/PhalApi.php" />
+        <option value="$PROJECT_DIR$/index.php" />
+        <option value="$PROJECT_DIR$/constants.php" />
+        <option value="$PROJECT_DIR$/Frame/Response.php" />
+        <option value="$PROJECT_DIR$/Frame/Dispatcher.php" />
+        <option value="$PROJECT_DIR$/Config/Route.php" />
+        <option value="$PROJECT_DIR$/Frame/Config.php" />
+        <option value="$PROJECT_DIR$/Frame/Logger.php" />
+        <option value="$PROJECT_DIR$/App/Api/RestfulController.php" />
+        <option value="$PROJECT_DIR$/Autoload.php" />
+        <option value="$PROJECT_DIR$/Frame/Controller.php" />
+        <option value="$PROJECT_DIR$/App/Api/Config/Route.php" />
+        <option value="$PROJECT_DIR$/App/Api/Config/Vestacp.php" />
+        <option value="$PROJECT_DIR$/Library/Vestacp.php" />
+        <option value="$PROJECT_DIR$/App/Api/Controller.php" />
+        <option value="$PROJECT_DIR$/Config/MySQL/Core.php" />
+        <option value="$PROJECT_DIR$/App/Api/Package/Database/Mail.php" />
+        <option value="$PROJECT_DIR$/App/Api/Controller/Cloudflare.php" />
+        <option value="$PROJECT_DIR$/App/Api/Controller/Imap.php" />
+        <option value="$PROJECT_DIR$/App/Api/Controller/Index.php" />
+        <option value="$PROJECT_DIR$/App/Api/Template/Index.phtml" />
+        <option value="$PROJECT_DIR$/public/api/index.php" />
+        <option value="$PROJECT_DIR$/App/Api/Template/Detail.phtml" />
+        <option value="$PROJECT_DIR$/App/Api/Controller/Vestacp/Users.php" />
+        <option value="$PROJECT_DIR$/App/Api/Controller/User/U.php" />
+        <option value="$PROJECT_DIR$/App/Api/Template/Domain.phtml" />
+        <option value="$PROJECT_DIR$/App/Api/Controller/Vestacp/Domain.php" />
+        <option value="$PROJECT_DIR$/public/api/ysmailclient/js/ysmail.js" />
+        <option value="$PROJECT_DIR$/App/Api/Controller/Mail/Lists.php" />
+        <option value="$PROJECT_DIR$/vendor/php-imap/php-imap/src/PhpImap/Mailbox.php" />
+        <option value="$PROJECT_DIR$/App/Api/Controller/Mail/Count/Unread.php" />
+        <option value="$PROJECT_DIR$/App/Api/Controller/User/Lists.php" />
+        <option value="$PROJECT_DIR$/App/Api/Controller/User/User.php" />
+        <option value="$PROJECT_DIR$/App/Api/Controller/User/Auth.php" />
+        <option value="$PROJECT_DIR$/Frame/Helper.php" />
+        <option value="$PROJECT_DIR$/Frame/Init.php" />
+        <option value="$PROJECT_DIR$/public/api/ysmailclient/js/index.js" />
+        <option value="$PROJECT_DIR$/public/api/ysmailclient/index.html" />
+        <option value="$PROJECT_DIR$/App/Script/Controller/Server.php" />
+      </list>
+    </option>
+  </component>
+  <component name="PhpDebugGeneral" listening_started="true" />
+  <component name="PhpServers">
+    <servers>
+      <server host="mail.x.97admin.com" id="7b5474da-07a7-46ce-9512-f0cd6d8d2166" name="mail.x.97admin.com" />
+    </servers>
+  </component>
+  <component name="ProjectFrameBounds" fullScreen="true">
+    <option name="width" value="1440" />
+    <option name="height" value="900" />
+  </component>
+  <component name="ProjectLevelVcsManager" settingsEditedManually="true" />
+  <component name="ProjectView">
+    <navigator proportions="" version="1">
+      <foldersAlwaysOnTop value="true" />
+    </navigator>
+    <panes>
+      <pane id="ProjectPane">
+        <subPane>
+          <expand>
+            <path>
+              <item name="MailApi" type="b2602c69:ProjectViewProjectNode" />
+              <item name="MailApi" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="MailApi" type="b2602c69:ProjectViewProjectNode" />
+              <item name="MailApi" type="462c0819:PsiDirectoryNode" />
+              <item name="App" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="MailApi" type="b2602c69:ProjectViewProjectNode" />
+              <item name="MailApi" type="462c0819:PsiDirectoryNode" />
+              <item name="App" type="462c0819:PsiDirectoryNode" />
+              <item name="Api" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="MailApi" type="b2602c69:ProjectViewProjectNode" />
+              <item name="MailApi" type="462c0819:PsiDirectoryNode" />
+              <item name="App" type="462c0819:PsiDirectoryNode" />
+              <item name="Api" type="462c0819:PsiDirectoryNode" />
+              <item name="Controller" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="MailApi" type="b2602c69:ProjectViewProjectNode" />
+              <item name="MailApi" type="462c0819:PsiDirectoryNode" />
+              <item name="App" type="462c0819:PsiDirectoryNode" />
+              <item name="Api" type="462c0819:PsiDirectoryNode" />
+              <item name="Controller" type="462c0819:PsiDirectoryNode" />
+              <item name="Mail" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="MailApi" type="b2602c69:ProjectViewProjectNode" />
+              <item name="MailApi" type="462c0819:PsiDirectoryNode" />
+              <item name="App" type="462c0819:PsiDirectoryNode" />
+              <item name="Api" type="462c0819:PsiDirectoryNode" />
+              <item name="Controller" type="462c0819:PsiDirectoryNode" />
+              <item name="User" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="MailApi" type="b2602c69:ProjectViewProjectNode" />
+              <item name="MailApi" type="462c0819:PsiDirectoryNode" />
+              <item name="App" type="462c0819:PsiDirectoryNode" />
+              <item name="Script" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="MailApi" type="b2602c69:ProjectViewProjectNode" />
+              <item name="MailApi" type="462c0819:PsiDirectoryNode" />
+              <item name="App" type="462c0819:PsiDirectoryNode" />
+              <item name="Script" type="462c0819:PsiDirectoryNode" />
+              <item name="Controller" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="MailApi" type="b2602c69:ProjectViewProjectNode" />
+              <item name="MailApi" type="462c0819:PsiDirectoryNode" />
+              <item name="Frame" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="MailApi" type="b2602c69:ProjectViewProjectNode" />
+              <item name="MailApi" type="462c0819:PsiDirectoryNode" />
+              <item name="public" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="MailApi" type="b2602c69:ProjectViewProjectNode" />
+              <item name="MailApi" type="462c0819:PsiDirectoryNode" />
+              <item name="public" type="462c0819:PsiDirectoryNode" />
+              <item name="api" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="MailApi" type="b2602c69:ProjectViewProjectNode" />
+              <item name="MailApi" type="462c0819:PsiDirectoryNode" />
+              <item name="public" type="462c0819:PsiDirectoryNode" />
+              <item name="api" type="462c0819:PsiDirectoryNode" />
+              <item name="ysmailclient" type="462c0819:PsiDirectoryNode" />
+            </path>
+          </expand>
+          <select />
+        </subPane>
+      </pane>
+      <pane id="Scope" />
+    </panes>
+  </component>
+  <component name="PropertiesComponent">
+    <property name="WebServerToolWindowFactoryState" value="false" />
+    <property name="last_opened_file_path" value="$PROJECT_DIR$/public/api" />
+    <property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" />
+    <property name="nodejs_npm_path_reset_for_default_project" value="true" />
+    <property name="settings.editor.selected.configurable" value="reference.webide.settings.project.settings.php" />
+  </component>
+  <component name="RecentsManager">
+    <key name="CopyFile.RECENT_KEYS">
+      <recent name="$PROJECT_DIR$/public/api" />
+      <recent name="$PROJECT_DIR$/App/Api" />
+      <recent name="$PROJECT_DIR$/Frame" />
+      <recent name="$PROJECT_DIR$/App/Api/Config" />
+      <recent name="$PROJECT_DIR$/Config" />
+    </key>
+  </component>
+  <component name="RunDashboard">
+    <option name="ruleStates">
+      <list>
+        <RuleState>
+          <option name="name" value="ConfigurationTypeDashboardGroupingRule" />
+        </RuleState>
+        <RuleState>
+          <option name="name" value="StatusDashboardGroupingRule" />
+        </RuleState>
+      </list>
+    </option>
+  </component>
+  <component name="SvnConfiguration">
+    <configuration>$USER_HOME$/.subversion</configuration>
+  </component>
+  <component name="TaskManager">
+    <task active="true" id="Default" summary="Default task">
+      <changelist id="694fa49d-7430-4dc6-80e8-d6e73ebd57e6" name="Default Changelist" comment="" />
+      <created>1559905385308</created>
+      <option name="number" value="Default" />
+      <option name="presentableId" value="Default" />
+      <updated>1559905385308</updated>
+      <workItem from="1559905386946" duration="25305000" />
+    </task>
+    <servers />
+  </component>
+  <component name="TimeTrackingManager">
+    <option name="totallyTimeSpent" value="25305000" />
+  </component>
+  <component name="ToolWindowManager">
+    <frame x="0" y="0" width="1440" height="900" extended-state="0" />
+    <layout>
+      <window_info id="Favorites" order="0" side_tool="true" />
+      <window_info content_ui="combo" id="Project" order="1" visible="true" weight="0.24964234" />
+      <window_info id="Structure" order="2" side_tool="true" weight="0.25" />
+      <window_info anchor="bottom" id="Terminal" order="0" weight="0.32961586" />
+      <window_info anchor="bottom" id="Database Changes" order="1" />
+      <window_info anchor="bottom" id="Event Log" order="2" side_tool="true" />
+      <window_info anchor="bottom" id="Version Control" order="3" />
+      <window_info anchor="bottom" id="Docker" order="4" />
+      <window_info anchor="bottom" id="Message" order="5" />
+      <window_info anchor="bottom" id="Find" order="6" />
+      <window_info anchor="bottom" id="Run" order="7" />
+      <window_info active="true" anchor="bottom" id="Debug" order="8" visible="true" weight="0.39900866" />
+      <window_info anchor="bottom" id="Cvs" order="9" weight="0.25" />
+      <window_info anchor="bottom" id="Inspection" order="10" weight="0.4" />
+      <window_info anchor="bottom" id="TODO" order="11" />
+      <window_info anchor="right" id="Database" order="0" />
+      <window_info anchor="right" id="1024 Tools" order="1" />
+      <window_info anchor="right" id="Commander" order="2" weight="0.4" />
+      <window_info anchor="right" id="Ant Build" order="3" weight="0.25" />
+      <window_info anchor="right" content_ui="combo" id="Hierarchy" order="4" weight="0.25" />
+    </layout>
+  </component>
+  <component name="TypeScriptGeneratedFilesManager">
+    <option name="version" value="1" />
+  </component>
+  <component name="XDebuggerManager">
+    <breakpoint-manager>
+      <breakpoints>
+        <line-breakpoint enabled="true" type="php">
+          <url>file://$PROJECT_DIR$/App/Api/Controller/Index.php</url>
+          <line>18</line>
+          <option name="timeStamp" value="2" />
+        </line-breakpoint>
+        <line-breakpoint enabled="true" type="php">
+          <url>file://$PROJECT_DIR$/App/Script/Controller/Server.php</url>
+          <line>29</line>
+          <option name="timeStamp" value="10" />
+        </line-breakpoint>
+        <line-breakpoint enabled="true" type="php">
+          <url>file://$PROJECT_DIR$/App/Script/Controller/Server.php</url>
+          <line>33</line>
+          <option name="timeStamp" value="11" />
+        </line-breakpoint>
+        <line-breakpoint enabled="true" type="php">
+          <url>file://$PROJECT_DIR$/App/Script/Controller/Server.php</url>
+          <line>36</line>
+          <option name="timeStamp" value="12" />
+        </line-breakpoint>
+        <line-breakpoint enabled="true" type="php">
+          <url>file://$PROJECT_DIR$/App/Script/Controller/Server.php</url>
+          <line>37</line>
+          <option name="timeStamp" value="13" />
+        </line-breakpoint>
+      </breakpoints>
+    </breakpoint-manager>
+  </component>
+  <component name="editorHistoryManager">
+    <entry file="file://$PROJECT_DIR$/Config/Route.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="209">
+          <caret line="16" column="16" selection-start-line="16" selection-start-column="16" selection-end-line="16" selection-end-column="16" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Frame/Logger.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="400">
+          <caret line="264" column="13" selection-start-line="264" selection-start-column="13" selection-end-line="264" selection-end-column="13" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/App/Api/Controller/Test.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="76">
+          <caret line="6" column="6" selection-start-line="6" selection-start-column="6" selection-end-line="6" selection-end-column="6" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/App/Api/RestfulController.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="247">
+          <caret line="13" column="42" lean-forward="true" selection-start-line="13" selection-start-column="42" selection-end-line="13" selection-end-column="42" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/vendor/composer/ClassLoader.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="141">
+          <caret line="325" selection-start-line="325" selection-end-line="325" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Autoload.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="345">
+          <caret line="27" selection-start-line="27" selection-end-line="27" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/App/Api/Config/Route.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="266">
+          <caret line="19" column="65" selection-start-line="19" selection-start-column="65" selection-end-line="19" selection-end-column="65" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/composer.json">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="209">
+          <caret line="11" selection-start-line="11" selection-end-line="11" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Library/Postgres.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="81">
+          <caret line="48" column="25" lean-forward="true" selection-start-line="42" selection-start-column="7" selection-end-line="48" selection-end-column="25" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Library/Telegram.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="247">
+          <caret line="18" column="1" selection-start-line="18" selection-start-column="1" selection-end-line="18" selection-end-column="1" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Library/Curl.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="-845">
+          <caret line="12" column="6" selection-start-line="12" selection-start-column="6" selection-end-line="12" selection-end-column="6" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/App/Api/Config/Vestacp.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="209">
+          <caret line="11" column="17" selection-start-line="11" selection-start-column="9" selection-end-line="11" selection-end-column="17" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/App/Api/Controller.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="19">
+          <caret line="61" column="34" selection-start-line="61" selection-start-column="34" selection-end-line="61" selection-end-column="34" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/vendor/qingbing/php-abstracts-component/src/abstracts/Component.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="353">
+          <caret line="30" column="35" selection-start-line="30" selection-start-column="35" selection-end-line="30" selection-end-column="35" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/vendor/qingbing/php-config/src/Config.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="-1252">
+          <caret line="30" column="27" selection-start-line="30" selection-start-column="27" selection-end-line="30" selection-end-column="27" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Config/MySQL/Core.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="589">
+          <caret line="31" column="13" selection-start-line="31" selection-start-column="13" selection-end-line="31" selection-end-column="13" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/App/Api/Package/Database/Mail.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="190">
+          <caret line="10" column="37" selection-start-line="10" selection-start-column="37" selection-end-line="10" selection-end-column="37" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/vendor/jamesryanbell/cloudflare/src/CloudFlare/Api.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="365">
+          <caret line="55" column="33" selection-start-line="55" selection-start-column="33" selection-end-line="55" selection-end-column="33" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/vendor/jamesryanbell/cloudflare/src/CloudFlare/Zone/Dns.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="-1109">
+          <caret line="16" column="6" selection-start-line="16" selection-start-column="6" selection-end-line="16" selection-end-column="6" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/App/Api/Controller/Cloudflare.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="185">
+          <caret line="23" column="36" selection-start-line="23" selection-start-column="36" selection-end-line="23" selection-end-column="36" />
+          <folding>
+            <element signature="e#47#66#0#PHP" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/App/Api/Controller/Index.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="342">
+          <caret line="18" column="24" selection-start-line="18" selection-start-column="24" selection-end-line="18" selection-end-column="24" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/App/Api/Template/Index.phtml">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="152">
+          <caret line="8" selection-start-line="8" selection-end-line="17" selection-end-column="95" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="jar://$APPLICATION_HOME_DIR$/plugins/php/lib/php.jar!/stubs/standard/standard_9.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="279">
+          <caret line="125" column="9" selection-start-line="125" selection-start-column="9" selection-end-line="125" selection-end-column="9" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/App/Api/Controller/Vestacp/Users.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="323">
+          <caret line="17" column="26" lean-forward="true" selection-start-line="17" selection-start-column="26" selection-end-line="17" selection-end-column="26" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/App/Api/Template/Domain.phtml">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="913">
+          <caret line="57" lean-forward="true" selection-start-line="57" selection-end-line="57" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/App/Api/Template/Detail.phtml">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="338">
+          <caret line="29" column="48" selection-start-line="29" selection-start-column="48" selection-end-line="29" selection-end-column="48" />
+          <folding>
+            <element signature="n#style#0;n#div#1;n#div#0;n#body#0;n#html#0;n#!!top" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/public/api/ysmailclient/README.md">
+      <provider selected="true" editor-type-id="split-provider[text-editor;markdown-preview-editor]">
+        <state split_layout="SPLIT">
+          <first_editor relative-caret-position="171">
+            <caret line="9" column="22" selection-start-line="9" selection-start-column="22" selection-end-line="9" selection-end-column="22" />
+          </first_editor>
+          <second_editor />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/public/api/ysmailclient/zeroclipboard/ZeroClipboard.js">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="256">
+          <caret line="721" column="28" selection-start-line="721" selection-start-column="28" selection-end-line="721" selection-end-column="28" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/App/Api/Controller/Imap.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="260">
+          <caret line="30" column="19" selection-start-line="30" selection-start-column="19" selection-end-line="30" selection-end-column="19" />
+          <folding>
+            <element signature="e#47#84#0#PHP" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/vendor/php-imap/php-imap/src/PhpImap/Mailbox.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="188">
+          <caret line="445" column="50" lean-forward="true" selection-start-line="445" selection-start-column="50" selection-end-line="445" selection-end-column="50" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/App/Api/Controller/Mail/Count/Unread.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="589">
+          <caret line="33" column="39" selection-start-line="33" selection-start-column="39" selection-end-line="33" selection-end-column="39" />
+          <folding>
+            <element signature="e#58#99#0#PHP" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/App/Api/Controller/Vestacp/Domain.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="611">
+          <caret line="126" column="9" lean-forward="true" selection-start-line="105" selection-start-column="7" selection-end-line="126" selection-end-column="9" />
+          <folding>
+            <element signature="e#55#96#0#PHP" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/public/api/index.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="114">
+          <caret line="6" column="26" selection-start-line="6" selection-start-column="26" selection-end-line="6" selection-end-column="26" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/App/Api/Controller/User/Auth.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="219">
+          <caret line="22" column="41" lean-forward="true" selection-start-line="22" selection-start-column="41" selection-end-line="22" selection-end-column="41" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Frame/Helper.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="369">
+          <caret line="310" column="5" lean-forward="true" selection-start-line="310" selection-start-column="5" selection-end-line="310" selection-end-column="5" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/App/Api/Controller/User/User.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="399">
+          <caret line="37" column="113" lean-forward="true" selection-start-line="37" selection-start-column="26" selection-end-line="37" selection-end-column="113" />
+          <folding>
+            <element signature="e#52#93#0#PHP" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Frame/Init.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="257">
+          <caret line="37" column="45" lean-forward="true" selection-start-line="37" selection-start-column="45" selection-end-line="37" selection-end-column="45" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/public/api/ysmailclient/js/ysmail.js">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="51">
+          <caret line="10" column="32" lean-forward="true" selection-start-line="10" selection-start-column="32" selection-end-line="10" selection-end-column="32" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/public/api/ysmailclient/js/jquery.js">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="19">
+          <caret line="11" column="1231" selection-start-line="11" selection-start-column="1231" selection-end-line="11" selection-end-column="1231" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/public/api/ysmailclient/js/index.js">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="228">
+          <caret line="89" column="27" selection-start-line="89" selection-start-column="27" selection-end-line="89" selection-end-column="27" />
+          <folding>
+            <element signature="n#style#0;n#button#0;n#li#0;n#!!top" expanded="true" />
+            <element signature="e#141#147#1" expanded="true" />
+            <element signature="n#style#0;n#span#0;n#li#0;n#!!top" expanded="true" />
+            <element signature="n#style#0;n#ul#0;n#li#0;n#!!top" expanded="true" />
+            <element signature="n#style#0;n#div#0;n#div#1;n#div#0;n#!!top" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/App/Api/Controller/Mail/Lists.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="216">
+          <caret line="23" column="29" lean-forward="true" selection-start-line="23" selection-start-column="29" selection-end-line="23" selection-end-column="29" />
+          <folding>
+            <element signature="e#52#93#0#PHP" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/App/Api/Controller/User/Lists.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="380">
+          <caret line="21" column="9" lean-forward="true" selection-start-line="21" selection-start-column="9" selection-end-line="21" selection-end-column="9" />
+          <folding>
+            <element signature="e#52#93#0#PHP" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/public/api/ysmailclient/index.html">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="570">
+          <caret line="30" column="29" selection-start-line="30" selection-start-column="29" selection-end-line="30" selection-end-column="29" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Frame/Controller.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="148">
+          <caret line="41" selection-start-line="41" selection-end-line="41" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Frame/Dispatcher.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="104">
+          <caret line="120" selection-start-line="120" selection-end-line="120" />
+          <folding>
+            <element signature="e#33#57#0#PHP" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/vendor/guzzlehttp/promises/src/TaskQueue.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="259">
+          <caret line="28" selection-start-line="28" selection-end-line="28" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="224">
+          <caret line="58" selection-start-line="58" selection-end-line="58" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Library/Vestacp.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="272">
+          <caret line="35" selection-start-line="35" selection-end-line="35" />
+          <folding>
+            <element signature="e#36#65#0#PHP" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/vendor/proficlos/vestacp-api/src/VestaCP/Client.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="161">
+          <caret line="148" selection-start-line="148" selection-end-line="148" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/vendor/proficlos/vestacp-api/src/VestaCP/Module/Mails.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="294">
+          <caret line="73" selection-start-line="73" selection-end-line="73" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/App/Script/Controller/Server.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="290">
+          <caret line="42" selection-start-line="42" selection-end-line="42" />
+          <folding>
+            <element signature="e#135#176#0#PHP" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+  </component>
+  <component name="masterDetails">
+    <states>
+      <state key="ScopeChooserConfigurable.UI">
+        <settings>
+          <splitter-proportions>
+            <option name="proportions">
+              <list>
+                <option value="0.2" />
+              </list>
+            </option>
+          </splitter-proportions>
+        </settings>
+      </state>
+    </states>
+  </component>
+</project>

+ 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"
+        ]
+    ];
+}

+ 16 - 0
App/Api/Config/Vestacp.php

@@ -0,0 +1,16 @@
+<?php
+
+
+namespace Heanup\App\Api\Config;
+
+
+use Heanup\Frame\Config;
+
+class Vestacp extends Config
+{
+    protected static $data = [
+        "hostname" => "https://myhome.97admin.com",
+        'username' => "admin",
+        'password' => 'admin'
+    ];
+}

+ 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);
+    }
+
+}

+ 53 - 0
App/Api/Controller/Cloudflare.php

@@ -0,0 +1,53 @@
+<?php
+
+
+namespace Heanup\App\Api\Controller;
+
+
+use Cloudflare\Api;
+use Cloudflare\Zone\Dns;
+use Heanup\App\Api\RestfulController;
+
+class Cloudflare extends RestfulController
+{
+
+    /**
+     * Get Method|获取数据
+     * @return void
+     */
+    protected function getData()
+    {
+        $zone_identifier="f1d34a4c94e10f131c76970de4b0dee7";
+        $client = new Api("chendeben@qq.com","093fcd440ed453c2689c42150bdb7d5dbc4fb");
+        $dns=new Dns($client);
+        $record=$dns->list_records($zone_identifier);
+        $this->showSuccess($record);
+    }
+
+    /**
+     * Post Method|新增数据
+     * @return void
+     */
+    protected function postData()
+    {
+        // TODO: 需要实现 postData() 方法.
+    }
+
+    /**
+     * Put Method|更新数据
+     * @return void
+     */
+    protected function putData()
+    {
+        // TODO: 需要实现 putData() 方法.
+    }
+
+    /**
+     * Delete Method|删除数据
+     * @return void
+     */
+    protected function deleteData()
+    {
+        // TODO: 需要实现 deleteData() 方法.
+    }
+}

+ 82 - 0
App/Api/Controller/Imap.php

@@ -0,0 +1,82 @@
+<?php
+
+
+namespace Heanup\App\Api\Controller;
+
+
+use Heanup\App\Api\RestfulController;
+use PhpImap\Exceptions\InvalidParameterException;
+use PhpImap\Mailbox;
+
+class Imap extends RestfulController
+{
+
+    /**
+     * Get Method|获取数据
+     * @return void
+     * @throws InvalidParameterException
+     */
+    protected function getData()
+    {
+        // Create PhpImap\Mailbox instance for all further actions
+        $mailbox = new Mailbox(
+            '{mail.97admin.com:993/imap/ssl/novalidate-cert}INBOX', // IMAP server and mailbox folder
+            'chendeben@a4sky.com', // Username for the before configured mailbox
+            '6485882', // Password for the before configured username
+            __DIR__, // Directory, where attachments will be saved (optional)
+            'UTF-8' // Server encoding (optional)
+        );
+
+        // Get all emails (messages)
+        // PHP.net imap_search criteria: http://php.net/manual/en/function.imap-search.php
+        $mailsIds = $mailbox->searchMailbox('ALL');
+
+// Get the first message
+// If '__DIR__' was defined in the first line, it will automatically
+// save all attachments to the specified directory
+        $mail = $mailbox->getMail(26);
+
+// Show, if $mail has one or more attachments
+//        echo "\nMail has attachments? ";
+//        if ($mail->hasAttachments()) {
+//            echo "Yes\n";
+//        } else {
+//            echo "No\n";
+//        }
+
+        echo ($mail->textHtml);
+// Print all information of $mail
+//        print_r($mail);
+
+// Print all attachements of $mail
+//        echo "\n\nAttachments:\n";
+//        print_r($mail->getAttachments());
+    }
+
+    /**
+     * Post Method|新增数据
+     * @return void
+     */
+    protected function postData()
+    {
+        // TODO: 需要实现 postData() 方法.
+    }
+
+    /**
+     * Put Method|更新数据
+     * @return void
+     */
+    protected function putData()
+    {
+        // TODO: 需要实现 putData() 方法.
+    }
+
+    /**
+     * Delete Method|删除数据
+     * @return void
+     */
+    protected function deleteData()
+    {
+        // TODO: 需要实现 deleteData() 方法.
+    }
+}

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

@@ -0,0 +1,48 @@
+<?php
+
+
+namespace Heanup\App\Api\Controller;
+
+
+use Heanup\App\Api\RestfulController;
+
+class Index extends RestfulController
+{
+
+
+    /**
+     * Get Method|获取数据
+     * @return void
+     */
+    protected function getData()
+    {
+        $this->render();
+    }
+
+    /**
+     * Post Method|新增数据
+     * @return void
+     */
+    protected function postData()
+    {
+        // TODO: 需要实现 postData() 方法.
+    }
+
+    /**
+     * Put Method|更新数据
+     * @return void
+     */
+    protected function putData()
+    {
+        // TODO: 需要实现 putData() 方法.
+    }
+
+    /**
+     * Delete Method|删除数据
+     * @return void
+     */
+    protected function deleteData()
+    {
+        // TODO: 需要实现 deleteData() 方法.
+    }
+}

+ 63 - 0
App/Api/Controller/Mail/Count/Unread.php

@@ -0,0 +1,63 @@
+<?php
+
+
+namespace Heanup\App\Api\Controller\Mail\Count;
+
+
+use Heanup\App\Api\Package\Database\Mail;
+use Heanup\App\Api\RestfulController;
+use PhpImap\Mailbox;
+
+class Unread extends RestfulController
+{
+
+    /**
+     * Get Method|获取数据
+     * @return void
+     * @throws \PhpImap\Exceptions\InvalidParameterException
+     */
+    protected function getData()
+    {
+        $email=$this->getQuery()->string("u");
+        $account = substr($email, 0, strpos($email, "@"));
+        $domain = substr($email, strpos($email, "@") + 1);
+        $_mail = Mail::getOne(['email' => $account, 'domain' => $domain, 'status' => 1]);
+        $password = $_mail['password'];
+        $mailbox = new Mailbox(
+            '{mail.97admin.com:993/imap/ssl/novalidate-cert}INBOX', // IMAP server and mailbox folder
+            $email, // Username for the before configured mailbox
+            $password, // Password for the before configured username
+            __DIR__, // Directory, where attachments will be saved (optional)
+            'UTF-8' // Server encoding (optional)
+        );
+        $count=$mailbox->searchMailbox("UNSEEN");
+        $this->showSuccess(count($count));
+    }
+
+    /**
+     * Post Method|新增数据
+     * @return void
+     */
+    protected function postData()
+    {
+        // TODO: 需要实现 postData() 方法.
+    }
+
+    /**
+     * Put Method|更新数据
+     * @return void
+     */
+    protected function putData()
+    {
+        // TODO: 需要实现 putData() 方法.
+    }
+
+    /**
+     * Delete Method|删除数据
+     * @return void
+     */
+    protected function deleteData()
+    {
+        // TODO: 需要实现 deleteData() 方法.
+    }
+}

+ 81 - 0
App/Api/Controller/Mail/Lists.php

@@ -0,0 +1,81 @@
+<?php
+
+
+namespace Heanup\App\Api\Controller\Mail;
+
+
+use Heanup\App\Api\Package\Database\Mail;
+use Heanup\App\Api\RestfulController;
+use PhpImap\Mailbox;
+
+class Lists extends RestfulController
+{
+
+    /**
+     * Get Method|获取数据
+     * @return void
+     * @throws \PhpImap\Exceptions\InvalidParameterException
+     */
+    protected function getData()
+    {
+        $email=$this->getQuery()->string("u");
+        $account = substr($email, 0, strpos($email, "@"));
+        $domain = substr($email, strpos($email, "@") + 1);
+        $_mail = Mail::getOne(['email' => $account, 'domain' => $domain, 'status' => 1]);
+        $password = $_mail['password'];
+        $mailbox = new Mailbox(
+            '{mail.97admin.com:993/imap/ssl/novalidate-cert}INBOX', // IMAP server and mailbox folder
+            $email, // Username for the before configured mailbox
+            $password, // Password for the before configured username
+            __DIR__, // Directory, where attachments will be saved (optional)
+            'UTF-8' // Server encoding (optional)
+        );
+
+        $mail_ids = $mailbox->searchMailbox('ALL');
+//        $mail_info = $mailbox->getMailsInfo($mail_ids);
+        $mail_info=[];
+        $mail_ids=array_reverse($mail_ids);
+        foreach ($mail_ids as $mail_id) {
+            $detail = $mailbox->getMail($mail_id);
+            $temp=[
+                'subject' => $detail->subject,
+                'content' => $detail->textHtml,
+                'from' => $detail->fromAddress,
+                'fromName' => $detail->fromName,
+                'replyTo' => $detail->replyTo,
+                'date' => $detail->date,
+                'cc' => $detail->cc,
+                'bcc' => $detail->bcc,
+            ];
+            $mail_info[]=$temp;
+        }
+        $this->showSuccess($mail_info);
+    }
+
+    /**
+     * Post Method|新增数据
+     * @return void
+     */
+    protected function postData()
+    {
+        // TODO: 需要实现 postData() 方法.
+    }
+
+    /**
+     * Put Method|更新数据
+     * @return void
+     */
+    protected function putData()
+    {
+        // TODO: 需要实现 putData() 方法.
+    }
+
+    /**
+     * Delete Method|删除数据
+     * @return void
+     */
+    protected function deleteData()
+    {
+        // TODO: 需要实现 deleteData() 方法.
+    }
+}

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

@@ -0,0 +1,33 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: chendeben
+ * Date: 2017/12/6
+ * Time: 11:50
+ */
+namespace Heanup\App\Api\Controller;
+
+use Heanup\App\Api\Package\Database\File;
+use Heanup\App\Api\Package\Database\Hash;
+use Heanup\App\Api\Controller;
+use Heanup\Frame\Logger;
+use Heanup\Library\Sphinx;
+
+class Telegram extends Controller
+{
+    public function  main(){
+        $telegram=new \Telegram("532191649:AAGxdBNPQGrHo0Ralqpg_sSZFrJJwCh0KIk");
+        $msg=$telegram->Text();
+//        Logger::setLog("cdb@meitu.com",json_encode($msg));
+        file_put_contents("/data/wwwlogs/log.txt", json_encode($msg),FILE_APPEND);
+        if ($msg=="/ss5"){
+            $chat_id = $telegram->ChatID();
+            $content = array('chat_id' => $chat_id, 'text' => '最新的官网临时地址是【 https://www.ss5.xyz/ 】');
+            $telegram->sendMessage($content);
+        }else{
+            $chat_id = $telegram->ChatID();
+            $content = array('chat_id' => $chat_id, 'text' => '最新的官网临时地址是【 https://www.ss5.xyz/ 】');
+            $telegram->sendMessage($content);
+        }
+    }
+}

+ 19 - 0
App/Api/Controller/Test.php

@@ -0,0 +1,19 @@
+<?php
+namespace Heanup\App\Api\Controller;
+use Heanup\App\Api\Controller;
+use PHPMailer\PHPMailer\Exception;
+use PHPMailer\PHPMailer\PHPMailer;
+
+class Test extends Controller
+{
+
+    public function main()
+    {
+        $str='第01集$https://new.jsyunbf.com/20180114/PJ31o1AI/index.m3u8#第02集$https://new.jsyunbf.com/20180114/tOjQoagu/index.m3u8#第03集$https://new.jsyunbf.com/20180121/Z8YLjxyu/index.m3u8#第04集$https://new.jsyunbf.com/20180128/jAeiNPXT/index.m3u8#第05集$https://new.jsyunbf.com/20180204/hnoTrcM9/index.m3u8#第06集$https://new.jsyunbf.com/20180212/xps8IZmh/index.m3u8#第07集$https://new.jsyunbf.com/20180218/FkS7Rg1W/index.m3u8#第08集$https://new.jsyunbf.com/20180225/JMODzClF/index.m3u8#第09集$https://new.jsyunbf.com/20180304/fty4brZX/index.m3u8#第10集$https://new.jsyunbf.com/20180311/bvPZVPRD/index.m3u8#第11集$https://dy2.jsyunbf.com/20180325/VDmVdyzZ/index.m3u8#第12集$https://dy2.jsyunbf.com/20180325/hytlUKE0/index.m3u8#第12.5集$https://dy2.jsyunbf.com/20180402/G0Nr8SLR/index.m3u8#第13集$https://new.jsyunbf.com/20180409/J0zM7E4D/index.m3u8#第14集$https://videos3.jsyunbf.com/2018/04/18/Q7WW1jStEPTsQPrD/playlist.m3u8#第15集$https://videos3.jsyunbf.com/2018/04/22/WzA4P9edSptQXjP1/playlist.m3u8#第16集$https://videos3.jsyunbf.com/2018/04/29/qNn8vG7WKa7eoBQU/playlist.m3u8#第17集$https://videos3.jsyunbf.com/2018/05/06/KADh11ModSXszj0I/playlist.m3u8#第18集$https://videos3.jsyunbf.com/2018/05/20/NtxrUxVdQquVW2X2/playlist.m3u8#第19集$https://videos3.jsyunbf.com/2018/05/20/aN5U0lHJnpzXmUZi/playlist.m3u8#第20集$https://dy2.jsyunbf.com/20180526/U09PfiGb/index.m3u821null';
+        $pat="@第(\d+)集@";
+        preg_match_all($pat, $str,$arr);
+        var_dump($arr);
+    }
+
+
+}

+ 53 - 0
App/Api/Controller/User/Auth.php

@@ -0,0 +1,53 @@
+<?php
+
+
+namespace Heanup\App\Api\Controller\User;
+
+
+use Heanup\App\Api\RestfulController;
+
+class Auth extends RestfulController
+{
+
+    /**
+     * Get Method|获取数据
+     * @return void
+     */
+    protected function getData()
+    {
+        $cookie = $_SESSION['user_key'];
+        if ($cookie) {
+            $cookie = md5(time());
+            $_SESSION['user_key'] = $cookie;
+        }
+//        $_SESSION['user_key']="9f6e6800cfae7749eb6c486619254b9c";
+        $this->showSuccess($cookie);
+    }
+
+    /**
+     * Post Method|新增数据
+     * @return void
+     */
+    protected function postData()
+    {
+        // TODO: 需要实现 postData() 方法.
+    }
+
+    /**
+     * Put Method|更新数据
+     * @return void
+     */
+    protected function putData()
+    {
+        // TODO: 需要实现 putData() 方法.
+    }
+
+    /**
+     * Delete Method|删除数据
+     * @return void
+     */
+    protected function deleteData()
+    {
+        // TODO: 需要实现 deleteData() 方法.
+    }
+}

+ 58 - 0
App/Api/Controller/User/Lists.php

@@ -0,0 +1,58 @@
+<?php
+
+
+namespace Heanup\App\Api\Controller\User;
+
+
+use Heanup\App\Api\Package\Database\Mail;
+use Heanup\App\Api\RestfulController;
+
+class Lists extends RestfulController
+{
+
+    /**
+     * Get Method|获取数据
+     * @return void
+     */
+    protected function getData()
+    {
+        $user_key = $this->getRequest()->string("a");
+        if ($user_key) {
+            $accounts = Mail::select([], ['user_key' => $user_key, 'status' => 1]);
+        }
+        $names = [];
+        if (!empty($accounts)) {
+            foreach ($accounts as $account) {
+                $names[] = $account['email'] . "@" . $account['domain'];
+            }
+        }
+        $this->showSuccess($names);
+    }
+
+    /**
+     * Post Method|新增数据
+     * @return void
+     */
+    protected function postData()
+    {
+        // TODO: 需要实现 postData() 方法.
+    }
+
+    /**
+     * Put Method|更新数据
+     * @return void
+     */
+    protected function putData()
+    {
+        // TODO: 需要实现 putData() 方法.
+    }
+
+    /**
+     * Delete Method|删除数据
+     * @return void
+     */
+    protected function deleteData()
+    {
+        // TODO: 需要实现 deleteData() 方法.
+    }
+}

+ 78 - 0
App/Api/Controller/User/User.php

@@ -0,0 +1,78 @@
+<?php
+
+
+namespace Heanup\App\Api\Controller\User;
+
+
+use Heanup\App\Api\Package\Database\Mail;
+use Heanup\App\Api\RestfulController;
+use Heanup\Frame\Helper;
+use Heanup\Library\Vestacp;
+use ProfiCloS\VestaCP\ClientException;
+use ProfiCloS\VestaCP\ProcessException;
+
+class User extends RestfulController
+{
+
+    /**
+     * Get Method|获取数据
+     * @return void
+     */
+    protected function getData()
+    {
+        $domains=[
+          'heanup.me',
+          'a4sky.com',
+          '08dvd.com'
+        ];
+        $domain = $domains[rand(0,count($domains)-1)];
+        $email = Helper::getStrongCode(6);
+        $password = md5(uniqid());
+        $user_key=$_SESSION['user_key'];
+        $data = compact("domain", "email", "password","user_key");
+        $data['expired_time'] = date("Y-m-d H:i:s", time() + 7200);
+        $data['add_time'] = date("Y-m-d H:i:s");
+        $exist = Mail::getOne(['email' => $email, 'domain' => $domain, 'status' => 1]);
+        if (!$exist) {
+            try {
+                $result = Vestacp::getInstance()->getModuleMail("admin")->addAccount($domain, $email, $password);
+                if ($result) {
+                    Mail::insert($data);
+                    $this->render();
+                }
+            } catch (ClientException $e) {
+                $this->showError(400, $e->getMessage());
+            } catch (ProcessException $e) {
+                $this->showError(400, $e->getMessage());
+            }
+        }
+        $this->showSuccess();
+    }
+
+    /**
+     * Post Method|新增数据
+     * @return void
+     */
+    protected function postData()
+    {
+        // TODO: 需要实现 postData() 方法.
+    }
+
+    /**
+     * Put Method|更新数据
+     * @return void
+     */
+    protected function putData()
+    {
+        // TODO: 需要实现 putData() 方法.
+    }
+
+    /**
+     * Delete Method|删除数据
+     * @return void
+     */
+    protected function deleteData()
+    {
+        // TODO: 需要实现 deleteData() 方法.
+    }
+}

+ 149 - 0
App/Api/Controller/Vestacp/Domain.php

@@ -0,0 +1,149 @@
+<?php
+
+
+namespace Heanup\App\Api\Controller\Vestacp;
+
+
+use Heanup\App\Api\Package\Database\Mail;
+use Heanup\App\Api\RestfulController;
+use Heanup\Library\Vestacp;
+use PhpImap\Exceptions\InvalidParameterException;
+use PhpImap\Mailbox;
+use ProfiCloS\VestaCP\ClientException;
+use ProfiCloS\VestaCP\ProcessException;
+
+class Domain extends RestfulController
+{
+    /**
+     * @var $this
+     */
+    public $_data;
+    /**
+     * @var $this
+     */
+    public $email;
+    /**
+     * @var array
+     */
+    public $mail_ids;
+    /**
+     * @var array
+     */
+    public $info;
+
+    /**
+     * Get Method|获取数据
+     * @return void
+     * @throws InvalidParameterException
+     */
+    protected function getData()
+    {
+        $type = $this->getRequest()->string("type", 'default');
+        $this->_data = $_SESSION['data'];
+        $login = $this->_data['email'] . "@" . $this->_data['domain'];
+        $password = $this->_data['password'];
+        $this->email = $this->getQuery()->string("email");
+//        $this->showSuccess([$login,$password]);
+        if ($this->email) {
+            $login = $this->email;
+            $account = substr($this->email, 0, strpos($this->email, "@"));
+            $domain = substr($this->email, strpos($this->email, "@") + 1);
+            $_mail = Mail::getOne(['email' => $account, 'domain' => $domain, 'status' => 1]);
+            $password = $_mail['password'];
+            $this->_data=$_mail;
+        }
+        if ($type == "get_mail") {
+
+            $mailbox = new Mailbox(
+                '{mail.97admin.com:993/imap/ssl/novalidate-cert}INBOX', // IMAP server and mailbox folder
+                $login, // Username for the before configured mailbox
+                $password, // Password for the before configured username
+                __DIR__, // Directory, where attachments will be saved (optional)
+                'UTF-8' // Server encoding (optional)
+            );
+            $mail_ids = $mailbox->searchMailbox('ALL');
+            $mail_info = $mailbox->getMailsInfo($mail_ids);
+            $mail_info = array_reverse($mail_info);
+            $this->showSuccess($mail_info);
+        } elseif ($type == 'get_detail') {
+
+            $mailbox = new Mailbox(
+                '{mail.97admin.com:993/imap/ssl/novalidate-cert}INBOX', // IMAP server and mailbox folder
+                $login, // Username for the before configured mailbox
+                $password, // Password for the before configured username
+                __DIR__, // Directory, where attachments will be saved (optional)
+                'UTF-8' // Server encoding (optional)
+            );
+            $mail_id = $this->getQuery()->intval("id");
+            $detail = $mailbox->getMail($mail_id);
+            $this->info = [
+                'subject' => $detail->subject,
+                'body' => $detail->textHtml,
+                'from' => $detail->fromAddress,
+                'fromName' => $detail->fromName,
+                'replyTo' => $detail->replyTo,
+                'date' => $detail->date,
+                'cc' => $detail->cc,
+                'bcc' => $detail->bcc,
+            ];
+            $this->getTemplate()->setTpl("Detail");
+            $this->render();
+            die();
+
+        }
+
+//        $this->showSuccess($this->_data);
+        $this->getTemplate()->setTpl("Domain");
+        $this->render();
+    }
+
+    /**
+     * 新增一个临时邮箱
+     * @return void
+     */
+    protected function postData()
+    {
+        $domain = $this->getRequest()->string("domain", 'a4sky.com');
+        $email = $this->getRequest()->string("email");
+        $password = md5(uniqid());
+        $data = compact("domain", "email", "password");
+        $data['expired_time'] = date("Y-m-d H:i:s", time() + 7200);
+        $data['add_time'] = date("Y-m-d H:i:s");
+        $exist = Mail::getOne(['email' => $email, 'domain' => $domain, 'status' => 1]);
+        $this->getTemplate()->setTpl("Domain");
+        $this->_data = $_SESSION['data'] = $data;
+        if (!$exist) {
+            try {
+                $result = Vestacp::getInstance()->getModuleMail("admin")->addAccount($domain, $email, $password);
+                if ($result) {
+                    Mail::insert($data);
+                    $this->render();
+                }
+            } catch (ClientException $e) {
+                $this->showError(400, $e->getMessage());
+            } catch (ProcessException $e) {
+                $this->showError(400, $e->getMessage());
+            }
+        }
+        $this->render();
+
+    }
+
+    /**
+     * Put Method|更新数据
+     * @return void
+     */
+    protected function putData()
+    {
+
+    }
+
+    /**
+     * Delete Method|删除数据
+     * @return void
+     */
+    protected function deleteData()
+    {
+
+    }
+}

+ 48 - 0
App/Api/Controller/Vestacp/Users.php

@@ -0,0 +1,48 @@
+<?php
+
+
+namespace Heanup\App\Api\Controller\Vestacp;
+
+
+use Heanup\App\Api\RestfulController;
+
+class Users extends RestfulController
+{
+
+    /**
+     * Get Method|获取数据
+     * @return void
+     */
+    protected function getData()
+    {
+        $_SESSION['data'];
+        $this->showSuccess(['name'=>$_SESSION['data']['email']."@".$_SESSION['data']['domain']]);
+    }
+
+    /**
+     * Post Method|新增数据
+     * @return void
+     */
+    protected function postData()
+    {
+        // TODO: 需要实现 postData() 方法.
+    }
+
+    /**
+     * Put Method|更新数据
+     * @return void
+     */
+    protected function putData()
+    {
+        // TODO: 需要实现 putData() 方法.
+    }
+
+    /**
+     * Delete Method|删除数据
+     * @return void
+     */
+    protected function deleteData()
+    {
+        // TODO: 需要实现 deleteData() 方法.
+    }
+}

+ 15 - 0
App/Api/Package/Database/Mail.php

@@ -0,0 +1,15 @@
+<?php
+
+
+namespace Heanup\App\Api\Package\Database;
+
+
+use Heanup\Frame\Package\Database\Single;
+
+class Mail extends Single
+{
+    const CONFIG_CLASS = 'MySQL\\Core';
+    protected static $tableName = 'vesta_mail';
+    protected static $fields = [];
+    protected static $primaryKey = 'id';
+}

+ 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(){}
+
+}

+ 48 - 0
App/Api/Template/Detail.phtml

@@ -0,0 +1,48 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+    <title>Moakt Email || 临时邮箱专业服务</title>
+    <meta name="description" content="Moakt Mail 是一个专业的临时邮箱服务供应商."/>
+    <meta name="keywords" content="可替代邮箱, 可替换邮箱, 临时邮箱, moakt, mohmal, 一次性邮箱"/>
+    <meta name="viewport" content="width=device-width"/>
+    <link rel="dns-prefetch" href="//ssl.google-analytics.com">
+    <link rel="dns-prefetch" href="//fonts.gstatic.com">
+    <link rel="dns-prefetch" href="//ajax.googleapis.com">
+    <link href="/css/theme.min.css" rel="stylesheet" type="text/css"/>
+    <link href="/css/style-ltr.min.css" rel="stylesheet" type="text/css"/>
+    <link href="/css/material-icons.min.css" rel="stylesheet" type="text/css"/>
+    <script src="/js/jquery.min.js" type="text/javascript"></script>
+    <script src="/js/jquery-ui.min.js" type="text/javascript"></script>
+
+</head>
+<body>
+<div class="message-container">
+    <div class="message-details">
+        <ul>
+            <li class="title"><?php echo $this->info['subject'];?></li>
+            <li class="sender">
+                <i class="material-icons"></i>
+                <label>从:</label>
+                <span><?php echo $this->info['from'];?><<?php echo $this->info['fromName'];?>></span>
+            </li>
+            <li class="date">
+                <i class="material-icons">access_time</i>
+                <label>日期:</label>
+                <span><?php echo $this->info['date'];?></span>
+            </li>
+        </ul>
+    </div>
+    <div class="message-content" style="overflow: scroll; -webkit-overflow-scrolling: touch;">
+        <?php echo $this->info['body'];?>
+    </div>
+<!--    <div class="message-attachments">-->
+<!--        <label class="message-attachments-subtitle">附件-->
+<!--            <span>(0 files, 0 MB)</span>-->
+<!--        </label>-->
+<!--        <ul>-->
+<!--        </ul>-->
+<!--    </div>-->
+</div>
+</body>
+</html>

+ 85 - 0
App/Api/Template/Domain.phtml

@@ -0,0 +1,85 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+    <title>Moakt Email || 临时邮箱专业服务</title>
+    <meta name="description" content="Moakt Mail 是一个专业的临时邮箱服务供应商."/>
+    <meta name="keywords" content="可替代邮箱, 可替换邮箱, 临时邮箱, moakt, mohmal, 一次性邮箱"/>
+    <meta name="viewport" content="width=device-width"/>
+    <link href="/css/theme.min.css" rel="stylesheet" type="text/css"/>
+    <link href="/css/style-ltr.min.css" rel="stylesheet" type="text/css"/>
+    <link href="/css/material-icons.min.css" rel="stylesheet" type="text/css"/>
+    <script src="/js/jquery.min.js" type="text/javascript"></script>
+    <script src="/js/jquery-ui.min.js" type="text/javascript"></script>
+
+</head>
+<body>
+<div id="page_2">
+    <div class="bgr_2">
+        <div id="top_" class="css_spirite">
+            <div class="cntr">
+
+                <span class="clr"></span>
+            </div>
+        </div>
+        <div class="cntr padd22">
+
+            <a href="/" class="hd2-logo-link"><span>Moakt Email || 临时邮箱专业服务</span></a>
+            <span class="clr mrg-in"></span>
+            <div class="page_in">
+                <div class="mail-controller">
+                    <div class="mailbox-details">
+                        <label>您已创建临时邮箱</label>
+                        <div id="email-address"><?php echo $this->_data['email'] . "@" . $this->_data['domain']; ?></div>
+                        <label>5秒刷新一次</label>
+                    </div>
+                </div>
+                <span class="clr"></span>
+                <div id="maillist">
+                    <span class="clr"></span>
+                    <div id="email_message_list">
+                        <div class="email-messages">
+                            <table id="mail_box">
+                                <tr>
+                                    <td id="email-title">邮件主题</td>
+                                    <td id="email-sender">发件人</td>
+                                    <td id="email-control">操作</td>
+                                </tr>
+                            </table>
+                        </div>
+                    </div>
+                    <span class="clr"></span>
+                </div>
+            </div>
+        </div>
+
+
+        <script type="text/javascript">
+
+            $(document).ready(function () {
+               fresh();
+            });
+            function html2Escape(sHtml) {
+                return sHtml.replace(/[<>&"]/g,function(c){return {'<':'&lt;','>':'&gt;','&':'&amp;','"':'&quot;'}[c];});
+            }
+            function fresh() {
+                $("#mail_box").html('<table id="mail_box">\n' +
+                    '                                <tr>\n' +
+                    '                                    <td id="email-title">邮件主题</td>\n' +
+                    '                                    <td id="email-sender">发件人</td>\n' +
+                    '                                    <td id="email-control">操作</td>\n' +
+                    '                                </tr>\n' +
+                    '                            </table>');
+                $.get("/vestacp/domain", {"type":"get_mail"<?php if ($this->email){?>,"email":"<?php echo $this->email;?>"<?php }?> },
+                    function(data){
+                        let html = "";
+                        $.each(data.data,function(i,val){  //遍历二维数组
+                            html+='<tr><td>'+html2Escape(val.subject)+'</td><td>'+html2Escape(val.from)+'</td><td><a target="_blank" href="/vestacp/domain?type=get_detail&id='+val.uid+'">点击查看详情</a></td></tr>';
+                        });
+                        $("#mail_box").append(html);
+                    }, "json");
+                setTimeout("fresh()", 1000 * 5);
+            }
+        </script>
+</body>
+</html>

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

@@ -0,0 +1,67 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr">
+<head>
+    <title>Moakt Email || Temporary disposable Email Service</title>
+    <meta name="description" content="Moakt Mail is a Temporary Disposable Email Service."/>
+    <meta name="keywords"
+          content="guerrilla mail alternative, disposable email, temporary email, moakt, mohmal, one time email"/>
+    <meta name="viewport" content="width=device-width"/>
+    <link rel="dns-prefetch" href="//ssl.google-analytics.com">
+    <link rel="dns-prefetch" href="//fonts.gstatic.com">
+    <link rel="dns-prefetch" href="//ajax.googleapis.com">
+    <link href="/css/theme.min.css" rel="stylesheet" type="text/css"/>
+    <link href="/css/style-ltr.min.css" rel="stylesheet" type="text/css"/>
+    <link href="/css/material-icons.min.css" rel="stylesheet" type="text/css"/>
+    <script src="/js/jquery.min.js" type="abd7698a3ccfc257165eff81-text/javascript"></script>
+    <script async src="/js/jquery-ui.min.js" type="abd7698a3ccfc257165eff81-text/javascript"></script>
+    <script async src="/js/mainjs.js" type="abd7698a3ccfc257165eff81-text/javascript"></script>
+</head>
+<body>
+<div id="page_">
+    <div class="bgr_">
+        <div id="top_" class="css_spirite">
+            <div class="cntr">
+
+            </div>
+        </div>
+        <div class="cntr padd">
+
+            <span class="clr"></span>
+            <div class="mainForm">
+                <a href="/" class="hd-logo-link">
+                    <img src="/picture/logo-en.png"/><span>临时邮箱服务</span></a>
+                <div class="form_" id="mailForm">
+                    <form method="post" action="/vestacp/domain">
+                        <span class="css_spirite domainSpanBefore"></span>
+                        <span class="css_spirite domainSpan">
+                        <span class="slct_">
+                        <span class="css_spirite select_btn"></span>
+                            <label>
+                                <select name="domain">
+                                    <option value="a4sky.com">@a4sky.com</option>
+                                    <option value="heanup.me">@heanup.me</option>
+                                    <option value="97admin.com">@97admin.com</option>
+                                </select>
+                            </label>
+                        </span>
+                        </span>
+                        <span class="css_spirite emailSpan">
+                            <label>
+                                <input type="text" name="email" class="mail_in" placeholder="your mail name !">
+                            </label>
+                        </span>
+                        <input type="submit" class="css_spirite setmail_butt" name="setemail" value="" title="Create">
+                        <span class="clr"></span>
+                        <input type="submit" class="mail_butt" name="random" value="Get a Random Address"/>
+                    </form>
+                </div>
+            </div>
+        </div>
+    </div>
+    <div id="footbox">
+        <div class="cntr">
+        </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);
+    }
+
+}

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

@@ -0,0 +1,45 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: chendeben
+ * Date: 2018/8/31
+ * Time: 下午9:35
+ */
+
+namespace Heanup\App\Script\Controller;
+
+
+use Heanup\App\Api\Package\Database\Mail;
+use Heanup\App\Script\Controller;
+use Heanup\Library\Vestacp;
+use ProfiCloS\VestaCP\ClientException;
+use ProfiCloS\VestaCP\ProcessException;
+
+class Server extends Controller
+{
+
+    /**
+     * 执行逻辑
+     */
+    protected function main()
+    {
+        $whereString = "status=1 AND expired_time<:expired_time";
+        $where = [
+            ':expired_time' => date("Y-m-d H:i:s")
+        ];
+        $emails = Mail::select([], $where, $whereString);
+        if ($emails) {
+            foreach ($emails as $email) {
+                try {
+                    Vestacp::getInstance()->getModuleMail("admin")->deleteAccount($email['domain'], strtolower($email['email']));
+                    Mail::update(['status'=>0], ['id'=>$email['id']]);
+                    echo sprintf("删除账号成功![%s]\r\n", $email['email'] . "@" . $email['domain']);
+                } catch (ClientException $e) {
+                } catch (ProcessException $e) {
+                }
+            }
+        }
+
+    }
+
+}

+ 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';
+
+}

+ 24 - 0
App/Test/AipTest.php

@@ -0,0 +1,24 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: chendeben
+ * Date: 2018/3/30
+ * Time: 下午4:27
+ */
+
+namespace Heanup\Test;
+
+use Heanup\App\Api\Controller\Aip;
+
+class AipTest extends \PHPUnit_Framework_TestCase
+{
+
+    public function testBaidu()
+    {
+
+    }
+
+    public function test()
+    {
+    }
+}

+ 50 - 0
App/Test/Test.php

@@ -0,0 +1,50 @@
+<?php
+namespace Heanup\Test;
+use PHPMailer\PHPMailer\Exception;
+use PHPMailer\PHPMailer\PHPMailer;
+use PHPUnit\Framework\TestCase;
+
+class Test extends TestCase
+{
+
+    public function testCalculate()
+    {
+        $mail=new PHPMailer(true);
+        try {
+            //Server settings
+            $mail->SMTPDebug = 2;                                 // Enable verbose debug output
+            $mail->isSMTP();                                      // Set mailer to use SMTP
+            $mail->Host = 'smtp.gmail.com';  // Specify main and backup SMTP servers
+            $mail->SMTPAuth = true;                               // Enable SMTP authentication
+            $mail->Username = 'cdb@us.meitu.com';                 // SMTP username
+            $mail->Password = 'chendeben6485882';                           // SMTP password
+            $mail->SMTPSecure = 'tls';                            // Enable TLS encryption, `ssl` also accepted
+            $mail->Port = 587;                                    // TCP port to connect to
+
+            //Recipients
+            $mail->setFrom('cdb@us.meitu.com', 'Heanup');
+//            $mail->addAddress('joe@example.net', 'Joe User');     // Add a recipient
+//            $mail->addAddress('ellen@example.com');               // Name is optional
+            $mail->addReplyTo('cdb@us.meitu.com', 'Heanup');
+            $mail->addCC('chendeben@qq.com');
+//            $mail->addBCC('bcc@example.com');
+
+            //Attachments
+//            $mail->addAttachment('/var/tmp/file.tar.gz');         // Add attachments
+//            $mail->addAttachment('/tmp/image.jpg', 'new.jpg');    // Optional name
+
+            //Content
+            $mail->isHTML(true);                                  // Set email format to HTML
+            $mail->Subject = 'Here is the subject';
+            $mail->Body    = 'This is the HTML message body <b>in bold!</b>';
+            $mail->AltBody = 'This is the body in plain text for non-HTML mail clients';
+
+            $mail->send();
+            echo 'Message has been sent';
+        } catch (Exception $e) {
+            echo 'Message could not be sent. Mailer Error: ', $mail->ErrorInfo;
+        }
+    }
+
+
+}

+ 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" => "heanup",
+            "charset" => "utf8",
+            "timeout" => 3
+        ],
+        "slave_list" => [
+            [
+                "host" => "myhome.97admin.com",
+                "port" => "3306",
+                "username" => "chendeben",
+                "password" => "6485882",
+                "dbname" => "heanup",
+                "charset" => "utf8",
+                "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;
+    }
+
+}

+ 1 - 0
Library/BaiduVoice/05107d0a3e97b26cc1ac77ddcc4f79fd

@@ -0,0 +1 @@
+{"access_token":"24.f0258c5d1aef9a4b1f257f96cb423d4e.2592000.1524030736.282335-10537047","session_key":"9mzdXUTDCv+e+3FqVT1f28HXOFHHL1PyQLMhLTJlMw+UsDKSLK2mknSZOX3ITsclBdANMyenfRv2AS2LXgigSmoNSbRxMQ==","scope":"public vis-ocr_ocr brain_ocr_scope brain_ocr_general brain_ocr_general_basic vis-ocr_business_license brain_ocr_webimage brain_all_scope brain_ocr_idcard brain_ocr_driving_license brain_ocr_vehicle_license vis-ocr_plate_number brain_solution brain_ocr_plate_number brain_ocr_accurate brain_ocr_accurate_basic brain_ocr_receipt brain_ocr_business_license audio_voice_assistant_get audio_tts_post wise_adapt lebo_resource_base lightservice_public hetu_basic lightcms_map_poi kaidian_kaidian ApsMisTest_Test\u6743\u9650 vis-classify_flower bnstest_fasf lpq_\u5f00\u653e cop_helloScope ApsMis_fangdi_permission","refresh_token":"25.f3449049b2c7a682bb6c3236bb30e19d.315360000.1836798736.282335-10537047","session_secret":"8dc33709717dc7ec5022b05cdbee581f","expires_in":2592000,"time":1521438764,"is_cloud_user":false}

+ 367 - 0
Library/BaiduVoice/AipBCEUtil.php

@@ -0,0 +1,367 @@
+<?php
+/*
+* Copyright (c) 2017 Baidu.com, Inc. All Rights Reserved
+*
+* Licensed under the Apache License, Version 2.0 (the "License"); you may not
+* use this file except in compliance with the License. You may obtain a copy of
+* the License at
+*
+* Http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+* License for the specific language governing permissions and limitations under
+* the License.
+*/
+
+/**
+ * BCE Util
+ */
+class AipHttpUtil
+{
+    // 根据RFC 3986,除了:
+    //   1.大小写英文字符
+    //   2.阿拉伯数字
+    //   3.点'.'、波浪线'~'、减号'-'以及下划线'_'
+    // 以外都要编码
+    public static $PERCENT_ENCODED_STRINGS;
+
+    //填充编码数组
+    public static function __init()
+    {
+        AipHttpUtil::$PERCENT_ENCODED_STRINGS = array();
+        for ($i = 0; $i < 256; ++$i) {
+            AipHttpUtil::$PERCENT_ENCODED_STRINGS[$i] = sprintf("%%%02X", $i);
+        }
+
+        //a-z不编码
+        foreach (range('a', 'z') as $ch) {
+            AipHttpUtil::$PERCENT_ENCODED_STRINGS[ord($ch)] = $ch;
+        }
+
+        //A-Z不编码
+        foreach (range('A', 'Z') as $ch) {
+            AipHttpUtil::$PERCENT_ENCODED_STRINGS[ord($ch)] = $ch;
+        }
+
+        //0-9不编码
+        foreach (range('0', '9') as $ch) {
+            AipHttpUtil::$PERCENT_ENCODED_STRINGS[ord($ch)] = $ch;
+        }
+
+        //以下4个字符不编码
+        AipHttpUtil::$PERCENT_ENCODED_STRINGS[ord('-')] = '-';
+        AipHttpUtil::$PERCENT_ENCODED_STRINGS[ord('.')] = '.';
+        AipHttpUtil::$PERCENT_ENCODED_STRINGS[ord('_')] = '_';
+        AipHttpUtil::$PERCENT_ENCODED_STRINGS[ord('~')] = '~';
+    }
+
+    /**
+     * 在uri编码中不能对'/'编码
+     * @param  string $path
+     * @return string
+     */
+    public static function urlEncodeExceptSlash($path)
+    {
+        return str_replace("%2F", "/", AipHttpUtil::urlEncode($path));
+    }
+
+    /**
+     * 使用编码数组编码
+     * @param  string $path
+     * @return string
+     */
+    public static function urlEncode($value)
+    {
+        $result = '';
+        for ($i = 0; $i < strlen($value); ++$i) {
+            $result .= AipHttpUtil::$PERCENT_ENCODED_STRINGS[ord($value[$i])];
+        }
+        return $result;
+    }
+
+    /**
+     * 生成标准化QueryString
+     * @param  array $parameters
+     * @return array
+     */
+    public static function getCanonicalQueryString(array $parameters)
+    {
+        //没有参数,直接返回空串
+        if (count($parameters) == 0) {
+            return '';
+        }
+
+        $parameterStrings = array();
+        foreach ($parameters as $k => $v) {
+            //跳过Authorization字段
+            if (strcasecmp('Authorization', $k) == 0) {
+                continue;
+            }
+            if (!isset($k)) {
+                throw new \InvalidArgumentException(
+                    "parameter key should not be null"
+                );
+            }
+            if (isset($v)) {
+                //对于有值的,编码后放在=号两边
+                $parameterStrings[] = AipHttpUtil::urlEncode($k)
+                    . '=' . AipHttpUtil::urlEncode((string) $v);
+            } else {
+                //对于没有值的,只将key编码后放在=号的左边,右边留空
+                $parameterStrings[] = AipHttpUtil::urlEncode($k) . '=';
+            }
+        }
+        //按照字典序排序
+        sort($parameterStrings);
+
+        //使用'&'符号连接它们
+        return implode('&', $parameterStrings);
+    }
+
+    /**
+     * 生成标准化uri
+     * @param  string $path
+     * @return string
+     */
+    public static function getCanonicalURIPath($path)
+    {
+        //空路径设置为'/'
+        if (empty($path)) {
+            return '/';
+        } else {
+            //所有的uri必须以'/'开头
+            if ($path[0] == '/') {
+                return AipHttpUtil::urlEncodeExceptSlash($path);
+            } else {
+                return '/' . AipHttpUtil::urlEncodeExceptSlash($path);
+            }
+        }
+    }
+
+    /**
+     * 生成标准化http请求头串
+     * @param  array $headers
+     * @return array
+     */
+    public static function getCanonicalHeaders($headers)
+    {
+        //如果没有headers,则返回空串
+        if (count($headers) == 0) {
+            return '';
+        }
+
+        $headerStrings = array();
+        foreach ($headers as $k => $v) {
+            //跳过key为null的
+            if ($k === null) {
+                continue;
+            }
+            //如果value为null,则赋值为空串
+            if ($v === null) {
+                $v = '';
+            }
+            //trim后再encode,之后使用':'号连接起来
+            $headerStrings[] = AipHttpUtil::urlEncode(strtolower(trim($k))) . ':' . AipHttpUtil::urlEncode(trim($v));
+        }
+        //字典序排序
+        sort($headerStrings);
+
+        //用'\n'把它们连接起来
+        return implode("\n", $headerStrings);
+    }
+}
+AipHttpUtil::__init();
+
+
+class AipSignOption
+{
+    const EXPIRATION_IN_SECONDS = 'expirationInSeconds';
+
+    const HEADERS_TO_SIGN = 'headersToSign';
+
+    const TIMESTAMP = 'timestamp';
+
+    const DEFAULT_EXPIRATION_IN_SECONDS = 1800;
+
+    const MIN_EXPIRATION_IN_SECONDS = 300;
+
+    const MAX_EXPIRATION_IN_SECONDS = 129600;
+}
+
+
+class AipSampleSigner
+{
+
+    const BCE_AUTH_VERSION = "bce-auth-v1";
+    const BCE_PREFIX = 'x-bce-';
+
+    //不指定headersToSign情况下,默认签名http头,包括:
+    //    1.host
+    //    2.content-length
+    //    3.content-type
+    //    4.content-md5
+    public static $defaultHeadersToSign;
+
+    public static function  __init()
+    {
+        AipSampleSigner::$defaultHeadersToSign = array(
+            "host",
+            "content-length",
+            "content-type",
+            "content-md5",
+        );
+    }
+
+    /**
+     * 签名
+     * @param  array $credentials
+     * @param  string $httpMethod
+     * @param  string $path
+     * @param  array  $headers
+     * @param  string $params
+     * @param  array  $options
+     * @return string
+     */
+    public static function sign(
+        array $credentials,
+        $httpMethod,
+        $path,
+        $headers,
+        $params,
+        $options = array()
+    ) {
+        //设定签名有效时间
+        if (!isset($options[AipSignOption::EXPIRATION_IN_SECONDS])) {
+            //默认值1800秒
+            $expirationInSeconds = AipSignOption::DEFAULT_EXPIRATION_IN_SECONDS;
+        } else {
+            $expirationInSeconds = $options[AipSignOption::EXPIRATION_IN_SECONDS];
+        }
+
+        //解析ak sk
+        $accessKeyId = $credentials['ak'];
+        $secretAccessKey = $credentials['sk'];
+
+        //设定时间戳,注意:如果自行指定时间戳需要为UTC时间
+        if (!isset($options[AipSignOption::TIMESTAMP])) {
+            //默认值当前时间
+            $timestamp = gmdate('Y-m-d\TH:i:s\Z');
+        } else {
+            $timestamp = $options[AipSignOption::TIMESTAMP];
+        }
+
+        //生成authString
+        $authString = AipSampleSigner::BCE_AUTH_VERSION . '/' . $accessKeyId . '/'
+            . $timestamp . '/' . $expirationInSeconds;
+
+        //使用sk和authString生成signKey
+        $signingKey = hash_hmac('sha256', $authString, $secretAccessKey);
+
+        //生成标准化URI
+        $canonicalURI = AipHttpUtil::getCanonicalURIPath($path);
+
+        //生成标准化QueryString
+        $canonicalQueryString = AipHttpUtil::getCanonicalQueryString($params);
+
+        //填充headersToSign,也就是指明哪些header参与签名
+        $headersToSign = null;
+        if (isset($options[AipSignOption::HEADERS_TO_SIGN])) {
+            $headersToSign = $options[AipSignOption::HEADERS_TO_SIGN];
+        }
+
+        //生成标准化header
+        $canonicalHeader = AipHttpUtil::getCanonicalHeaders(
+            AipSampleSigner::getHeadersToSign($headers, $headersToSign)
+        );
+
+        //整理headersToSign,以';'号连接
+        $signedHeaders = '';
+        if ($headersToSign !== null) {
+            $signedHeaders = strtolower(
+                trim(implode(";", $headersToSign))
+            );
+        }
+
+        //组成标准请求串
+        $canonicalRequest = "$httpMethod\n$canonicalURI\n"
+            . "$canonicalQueryString\n$canonicalHeader";
+
+        //使用signKey和标准请求串完成签名
+        $signature = hash_hmac('sha256', $canonicalRequest, $signingKey);
+
+        //组成最终签名串
+        $authorizationHeader = "$authString/$signedHeaders/$signature";
+
+        return $authorizationHeader;
+    }
+
+    /**
+     * 根据headsToSign过滤应该参与签名的header
+     * @param  array $headers
+     * @param  array $headersToSign
+     * @return array
+     */
+    public static function getHeadersToSign($headers, $headersToSign)
+    {
+
+        //value被trim后为空串的header不参与签名
+        $filter_empty = function($v) {
+            return trim((string) $v) !== '';
+        };
+        $headers = array_filter($headers, $filter_empty);
+
+        //处理headers的key:去掉前后的空白并转化成小写
+        $trim_and_lower = function($str){
+            return strtolower(trim($str));
+        };
+        $temp = array();
+        $process_keys = function($k, $v) use(&$temp, $trim_and_lower) {
+            $temp[$trim_and_lower($k)] = $v;
+        };
+        array_map($process_keys, array_keys($headers), $headers);
+        $headers = $temp;
+
+        //取出headers的key以备用
+        $header_keys = array_keys($headers);
+
+        $filtered_keys = null;
+        if ($headersToSign !== null) {
+            //如果有headersToSign,则根据headersToSign过滤
+
+            //预处理headersToSign:去掉前后的空白并转化成小写
+            $headersToSign = array_map($trim_and_lower, $headersToSign);
+
+            //只选取在headersToSign里面的header
+            $filtered_keys = array_intersect_key($header_keys, $headersToSign);
+
+        } else {
+            //如果没有headersToSign,则根据默认规则来选取headers
+            $filter_by_default = function($k) {
+                return AipSampleSigner::isDefaultHeaderToSign($k);
+            };
+            $filtered_keys = array_filter($header_keys, $filter_by_default);
+        }
+
+        //返回需要参与签名的header
+        return array_intersect_key($headers, array_flip($filtered_keys));
+    }
+
+    /**
+     * 检查header是不是默认参加签名的:
+     * 1.是host、content-type、content-md5、content-length之一
+     * 2.以x-bce开头
+     * @param  array $header
+     * @return boolean
+     */
+    public static function isDefaultHeaderToSign($header)
+    {
+        $header = strtolower(trim($header));
+        if (in_array($header, AipSampleSigner::$defaultHeadersToSign)) {
+            return true;
+        }
+        return substr_compare($header, AipSampleSigner::BCE_PREFIX, 0, strlen(AipSampleSigner::BCE_PREFIX)) == 0;
+    }
+}
+AipSampleSigner::__init();

+ 343 - 0
Library/BaiduVoice/AipBase.php

@@ -0,0 +1,343 @@
+<?php
+/*
+* Copyright (c) 2017 Baidu.com, Inc. All Rights Reserved
+*
+* Licensed under the Apache License, Version 2.0 (the "License"); you may not
+* use this file except in compliance with the License. You may obtain a copy of
+* the License at
+*
+* Http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+* License for the specific language governing permissions and limitations under
+* the License.
+*/
+
+require_once 'AipHttpClient.php';
+require_once 'AipBCEUtil.php';
+require_once 'AipImageUtil.php';
+
+/**
+ * Aip Base 基类
+ */
+class AipBase {
+
+    /**
+     * 获取access token url
+     * @var string
+     */
+    protected $accessTokenUrl = 'https://aip.baidubce.com/oauth/2.0/token';
+
+    /**
+     * appId
+     * @var string
+     */
+    protected $appId = '';
+
+    /**
+     * apiKey
+     * @var string
+     */
+    protected $apiKey = '';
+    
+    /**
+     * secretKey
+     * @var string
+     */
+    protected $secretKey = '';
+
+    /**
+     * 权限
+     * @var array
+     */
+    protected $scope = 'brain_all_scope';
+
+    /**
+     * @param string $appId 
+     * @param string $apiKey
+     * @param string $secretKey
+     */
+    public function __construct($appId, $apiKey, $secretKey){
+        $this->appId = trim($appId);
+        $this->apiKey = trim($apiKey);
+        $this->secretKey = trim($secretKey);
+        $this->isCloudUser = null;
+        $this->client = new AipHttpClient();
+        $this->version = '1_6_0';
+    }
+
+    /**
+     * 连接超时
+     * @param int $ms 毫秒
+     */
+    public function setConnectionTimeoutInMillis($ms){
+        $this->client->setConnectionTimeoutInMillis($ms);
+    }
+
+    /**
+     * 响应超时
+     * @param int $ms 毫秒
+     */
+    public function setSocketTimeoutInMillis($ms){
+        $this->client->setSocketTimeoutInMillis($ms);
+    }
+
+    /**
+     * 处理请求参数
+     * @param  string $url
+     * @param array $params
+     * @param array $data
+     * @param array $headers
+     */
+    protected function proccessRequest($url, &$params, &$data, $headers){
+        $params['aipSdk'] = 'php';
+        $params['aipSdkVersion'] = $this->version;
+    }
+
+    /**
+     * Api 请求
+     * @param  string $url
+     * @param  mixed $data
+     * @return mixed
+     */
+    protected function request($url, $data, $headers=array()){
+        try{
+            $result = $this->validate($url, $data);
+            if($result !== true){
+                return $result;
+            }
+
+            $params = array();
+            $authObj = $this->auth();
+
+            if($this->isCloudUser === false){
+                $params['access_token'] = $authObj['access_token'];
+            }
+
+            // 特殊处理
+            $this->proccessRequest($url, $params, $data, $headers);
+ 
+            $headers = $this->getAuthHeaders('POST', $url, $params, $headers);
+            $response = $this->client->post($url, $data, $params, $headers);
+ 
+            $obj = $this->proccessResult($response['content']);
+
+            if(!$this->isCloudUser && isset($obj['error_code']) && $obj['error_code'] == 110){
+                $authObj = $this->auth(true);
+                $params['access_token'] = $authObj['access_token'];
+                $response = $this->client->post($url, $data, $params, $headers);
+                $obj = $this->proccessResult($response['content']);
+            }
+
+            if(empty($obj) || !isset($obj['error_code'])){
+                $this->writeAuthObj($authObj);
+            }
+        }catch(Exception $e){
+            return array(
+                'error_code' => 'SDK108',
+                'error_msg' => 'connection or read data timeout',
+            );
+        }
+
+        return $obj;
+    }
+
+    /**
+     * Api 多个并发请求
+     * @param  string $url
+     * @param  mixed $data
+     * @return mixed
+     */
+    protected function multi_request($url, $data){
+        try{
+            $params = array();
+            $authObj = $this->auth();
+            $headers = $this->getAuthHeaders('POST', $url);
+
+            if($this->isCloudUser === false){
+                $params['access_token'] = $authObj['access_token'];
+            }
+
+            $responses = $this->client->multi_post($url, $data, $params, $headers);
+
+            $is_success = false;
+            foreach($responses as $response){
+                $obj = $this->proccessResult($response['content']);
+
+                if(empty($obj) || !isset($obj['error_code'])){
+                    $is_success = true;
+                }
+
+                if(!$this->isCloudUser && isset($obj['error_code']) && $obj['error_code'] == 110){
+                    $authObj = $this->auth(true);
+                    $params['access_token'] = $authObj['access_token'];
+                    $responses = $this->client->post($url, $data, $params, $headers);
+                    break;
+                }
+            }
+
+            if($is_success){
+                $this->writeAuthObj($authObj);
+            }
+
+            $objs = array();
+            foreach($responses as $response){
+                $objs[] = $this->proccessResult($response['content']);
+            }
+
+        }catch(Exception $e){
+            return array(
+                'error_code' => 'SDK108',
+                'error_msg' => 'connection or read data timeout',
+            );
+        }
+
+        return $objs;
+    }
+
+    /**
+     * 格式检查
+     * @param  string $url
+     * @param  array $data
+     * @return mix
+     */
+    protected function validate($url, &$data){
+        return true;
+    }
+
+    /**
+     * 格式化结果
+     * @param $content string
+     * @return mixed
+     */
+    protected function proccessResult($content){
+        return json_decode($content, true);
+    }
+
+    /**
+     * 返回 access token 路径
+     * @return string
+     */
+    private function getAuthFilePath(){
+        return dirname(__FILE__) . DIRECTORY_SEPARATOR . md5($this->apiKey);
+    }
+
+    /**
+     * 写入本地文件
+     * @param  array $obj
+     * @return void
+     */
+    private function writeAuthObj($obj){
+        if($obj === null || (isset($obj['is_read']) && $obj['is_read'] === true)){
+            return;
+        }
+
+        $obj['time'] = time();
+        $obj['is_cloud_user'] = $this->isCloudUser;
+        @file_put_contents($this->getAuthFilePath(), json_encode($obj));
+    }
+
+    /**
+     * 读取本地缓存
+     * @return array
+     */
+    private function readAuthObj(){
+        $content = @file_get_contents($this->getAuthFilePath());
+        if($content !== false){
+            $obj = json_decode($content, true);
+            $this->isCloudUser = $obj['is_cloud_user'];
+            $obj['is_read'] = true;
+            if($this->isCloudUser || $obj['time'] + $obj['expires_in'] - 30 > time()){
+                return $obj;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * 认证
+     * @param bool $refresh 是否刷新
+     * @return array
+     */
+    private function auth($refresh=false){
+
+        //非过期刷新
+        if(!$refresh){
+            $obj = $this->readAuthObj();
+            if(!empty($obj)){
+                return $obj;
+            }
+        }
+
+        $response = $this->client->get($this->accessTokenUrl, array(
+            'grant_type' => 'client_credentials',
+            'client_id' => $this->apiKey,
+            'client_secret' => $this->secretKey,
+        ));
+
+        $obj = json_decode($response['content'], true);
+
+        $this->isCloudUser = !$this->isPermission($obj);
+        return $obj;
+    }
+
+    /**
+     * 判断认证是否有权限
+     * @param  array   $authObj 
+     * @return boolean          
+     */
+    protected function isPermission($authObj)
+    {
+        if(empty($authObj) || !isset($authObj['scope'])){
+            return false;
+        }
+
+        $scopes = explode(' ', $authObj['scope']);
+
+        return in_array($this->scope, $scopes);
+    }
+
+    /**
+     * @param  string $method HTTP method
+     * @param  string $url
+     * @param  array $param 参数
+     * @return array
+     */
+    private function getAuthHeaders($method, $url, $params=array(), $headers=array()){
+        
+        //不是云的老用户则不用在header中签名 认证
+        if($this->isCloudUser === false){
+            return $headers;
+        }
+
+        $obj = parse_url($url);
+        if(!empty($obj['query'])){        
+            foreach(explode('&', $obj['query']) as $kv){
+                if(!empty($kv)){
+                    list($k, $v) = explode('=', $kv, 2);
+                    $params[$k] = $v;
+                }
+            }
+        }
+
+        //UTC 时间戳
+        $timestamp = gmdate('Y-m-d\TH:i:s\Z');
+        $headers['Host'] = isset($obj['port']) ? sprintf('%s:%s', $obj['host'], $obj['port']) : $obj['host'];
+        $headers['x-bce-date'] = $timestamp;
+
+        //签名
+        $headers['authorization'] = AipSampleSigner::sign(array(
+            'ak' => $this->apiKey,
+            'sk' => $this->secretKey,
+        ), $method, $obj['path'], $headers, $params, array(
+            'timestamp' => $timestamp,
+            'headersToSign' => array_keys($headers),
+        ));
+
+        return $headers;
+    }
+
+}

+ 192 - 0
Library/BaiduVoice/AipHttpClient.php

@@ -0,0 +1,192 @@
+<?php
+/*
+* Copyright (c) 2017 Baidu.com, Inc. All Rights Reserved
+*
+* Licensed under the Apache License, Version 2.0 (the "License"); you may not
+* use this file except in compliance with the License. You may obtain a copy of
+* the License at
+*
+* Http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+* License for the specific language governing permissions and limitations under
+* the License.
+*/
+
+/**
+ * Http Client
+ */
+class AipHttpClient{
+
+    /**
+     * HttpClient
+     * @param array $headers HTTP header
+     */
+    public function __construct($headers=array()){
+        $this->headers = $this->buildHeaders($headers);
+        $this->connectTimeout = 60000;
+        $this->socketTimeout = 60000;
+    }
+
+    /**
+     * 连接超时
+     * @param int $ms 毫秒
+     */
+    public function setConnectionTimeoutInMillis($ms){
+        $this->connectTimeout = $ms;
+    }
+
+    /**
+     * 响应超时
+     * @param int $ms 毫秒
+     */
+    public function setSocketTimeoutInMillis($ms){
+        $this->socketTimeout = $ms;
+    }    
+
+    /**
+     * @param  string $url
+     * @param  array $data HTTP POST BODY
+     * @param  array $param HTTP URL
+     * @param  array $headers HTTP header
+     * @return array
+     */
+    public function post($url, $data=array(), $params=array(), $headers=array()){
+        $url = $this->buildUrl($url, $params);
+        $headers = array_merge($this->headers, $this->buildHeaders($headers));
+
+        $ch = curl_init();
+        curl_setopt($ch, CURLOPT_URL, $url);
+        curl_setopt($ch, CURLOPT_POST, 1);
+        curl_setopt($ch, CURLOPT_HEADER, false);
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+        curl_setopt($ch, CURLOPT_POSTFIELDS, is_array($data) ? http_build_query($data) : $data);
+        curl_setopt($ch, CURLOPT_TIMEOUT_MS, $this->socketTimeout);
+        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, $this->connectTimeout);
+        $content = curl_exec($ch);
+        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+
+        if($code === 0){
+            throw new Exception(curl_error($ch));
+        }
+
+        curl_close($ch);
+        return array(
+            'code' => $code,
+            'content' => $content,
+        );
+    }
+
+    /**
+     * @param  string $url
+     * @param  array $datas HTTP POST BODY
+     * @param  array $param HTTP URL
+     * @param  array $headers HTTP header
+     * @return array
+     */
+    public function multi_post($url, $datas=array(), $params=array(), $headers=array()){
+        $url = $this->buildUrl($url, $params);
+        $headers = array_merge($this->headers, $this->buildHeaders($headers));
+
+        $chs = array();
+        $result = array();
+        $mh = curl_multi_init();
+        foreach($datas as $data){        
+            $ch = curl_init();
+            $chs[] = $ch;
+            curl_setopt($ch, CURLOPT_URL, $url);
+            curl_setopt($ch, CURLOPT_POST, 1);
+            curl_setopt($ch, CURLOPT_HEADER, false);
+            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+            curl_setopt($ch, CURLOPT_POSTFIELDS, is_array($data) ? http_build_query($data) : $data);
+            curl_setopt($ch, CURLOPT_TIMEOUT_MS, $this->socketTimeout);
+            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, $this->connectTimeout);
+            curl_multi_add_handle($mh, $ch);
+        }
+
+        $running = null;
+        do{
+            curl_multi_exec($mh, $running);
+            usleep(100);
+        }while($running);
+
+        foreach($chs as $ch){        
+            $content = curl_multi_getcontent($ch);
+            $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+            $result[] = array(
+                'code' => $code,
+                'content' => $content,
+            );
+            curl_multi_remove_handle($mh, $ch);
+        }
+        curl_multi_close($mh);
+        
+        return $result;
+    }
+
+    /**
+     * @param  string $url
+     * @param  array $param HTTP URL
+     * @param  array $headers HTTP header
+     * @return array
+     */
+    public function get($url, $params=array(), $headers=array()){
+        $url = $this->buildUrl($url, $params);
+        $headers = array_merge($this->headers, $this->buildHeaders($headers));
+
+        $ch = curl_init();
+        curl_setopt($ch, CURLOPT_URL, $url);
+        curl_setopt($ch, CURLOPT_HEADER, false);
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+        curl_setopt($ch, CURLOPT_TIMEOUT_MS, $this->socketTimeout);
+        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, $this->connectTimeout);
+        $content = curl_exec($ch);
+        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+
+        if($code === 0){
+            throw new Exception(curl_error($ch));
+        }
+        
+        curl_close($ch);
+        return array(
+            'code' => $code,
+            'content' => $content,
+        );
+    }
+
+    /**
+     * 构造 header
+     * @param  array $headers
+     * @return array
+     */
+    private function buildHeaders($headers){
+        $result = array();
+        foreach($headers as $k => $v){
+            $result[] = sprintf('%s:%s', $k, $v);
+        }
+        return $result;
+    }
+
+    /**
+     * 
+     * @param  string $url
+     * @param  array $params 参数
+     * @return string
+     */
+    private function buildUrl($url, $params){
+        if(!empty($params)){
+            $str = http_build_query($params);
+            return $url . (strpos($url, '?') === false ? '?' : '&') . $str;
+        }else{
+            return $url;
+        }
+    }
+}

+ 36 - 0
Library/BaiduVoice/AipImageUtil.php

@@ -0,0 +1,36 @@
+<?php
+/*
+* Copyright (c) 2017 Baidu.com, Inc. All Rights Reserved
+*
+* Licensed under the Apache License, Version 2.0 (the "License"); you may not
+* use this file except in compliance with the License. You may obtain a copy of
+* the License at
+*
+* Http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+* License for the specific language governing permissions and limitations under
+* the License.
+*/
+
+class AipImageUtil{
+
+    /**
+     * 获取图片信息
+     * @param  $content string
+     * @return array
+     */
+    public static function getImageInfo($content){
+        $info = getimagesizefromstring($content);
+
+        return array(
+            'mime' => $info['mime'],
+            'width' => $info[0],
+            'height' => $info[1],
+        );
+    }
+}
+
+// var_dump(AipUtil::getImageInfo(file_get_contents('../test/general.png')));

+ 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;
+    }
+
+}

+ 1280 - 0
Library/Medoo/Medoo.php

@@ -0,0 +1,1280 @@
+<?php
+/*!
+ * Medoo database framework
+ * https://medoo.in
+ * Version 1.4.5
+ *
+ * Copyright 2017, Angel Lai
+ * Released under the MIT license
+ */
+
+namespace Medoo;
+
+use PDO;
+use Exception;
+use PDOException;
+
+class Medoo
+{
+    protected $database_type;
+
+    protected $prefix;
+
+    protected $statement;
+
+    protected $option = [];
+
+    protected $logs = [];
+
+    protected $logging = false;
+
+    protected $debug_mode = false;
+
+    protected $guid = 0;
+
+    public function __construct($options = null)
+    {
+        try {
+            if (is_array($options)) {
+                if (isset($options['database_type'])) {
+                    $this->database_type = strtolower($options['database_type']);
+                }
+            } else {
+                return false;
+            }
+
+            if (isset($options['prefix'])) {
+                $this->prefix = $options['prefix'];
+            }
+
+            if (isset($options['option'])) {
+                $this->option = $options['option'];
+            }
+
+            if (isset($options['logging']) && is_bool($options['logging'])) {
+                $this->logging = $options['logging'];
+            }
+
+            if (isset($options['command']) && is_array($options['command'])) {
+                $commands = $options['command'];
+            } else {
+                $commands = [];
+            }
+
+            if (isset($options['dsn'])) {
+                if (isset($options['dsn']['driver'])) {
+                    $attr = $options['dsn'];
+                } else {
+                    return false;
+                }
+            } else {
+                if (
+                    isset($options['port']) &&
+                    is_int($options['port'] * 1)
+                ) {
+                    $port = $options['port'];
+                }
+
+                $is_port = isset($port);
+
+                switch ($this->database_type) {
+                    case 'mariadb':
+                    case 'mysql':
+                        $attr = [
+                            'driver' => 'mysql',
+                            'dbname' => $options['database_name']
+                        ];
+
+                        if (isset($options['socket'])) {
+                            $attr['unix_socket'] = $options['socket'];
+                        } else {
+                            $attr['host'] = $options['server'];
+
+                            if ($is_port) {
+                                $attr['port'] = $port;
+                            }
+                        }
+
+                        // Make MySQL using standard quoted identifier
+                        $commands[] = 'SET SQL_MODE=ANSI_QUOTES';
+                        break;
+
+                    case 'pgsql':
+                        $attr = [
+                            'driver' => 'pgsql',
+                            'host' => $options['server'],
+                            'dbname' => $options['database_name']
+                        ];
+
+                        if ($is_port) {
+                            $attr['port'] = $port;
+                        }
+
+                        break;
+
+                    case 'sybase':
+                        $attr = [
+                            'driver' => 'dblib',
+                            'host' => $options['server'],
+                            'dbname' => $options['database_name']
+                        ];
+
+                        if ($is_port) {
+                            $attr['port'] = $port;
+                        }
+
+                        break;
+
+                    case 'oracle':
+                        $attr = [
+                            'driver' => 'oci',
+                            'dbname' => $options['server'] ?
+                                '//' . $options['server'] . ($is_port ? ':' . $port : ':1521') . '/' . $options['database_name'] :
+                                $options['database_name']
+                        ];
+
+                        if (isset($options['charset'])) {
+                            $attr['charset'] = $options['charset'];
+                        }
+
+                        break;
+
+                    case 'mssql':
+                        if (strstr(PHP_OS, 'WIN')) {
+                            $attr = [
+                                'driver' => 'sqlsrv',
+                                'Server' => $options['server'] . ($is_port ? ',' . $port : ''),
+                                'Database' => $options['database_name']
+                            ];
+                        } else {
+                            $attr = [
+                                'driver' => 'dblib',
+                                'host' => $options['server'] . ($is_port ? ':' . $port : ''),
+                                'dbname' => $options['database_name']
+                            ];
+                        }
+
+                        // Keep MSSQL QUOTED_IDENTIFIER is ON for standard quoting
+                        $commands[] = 'SET QUOTED_IDENTIFIER ON';
+
+                        // Make ANSI_NULLS is ON for NULL value
+                        $commands[] = 'SET ANSI_NULLS ON';
+                        break;
+
+                    case 'sqlite':
+                        $this->pdo = new PDO('sqlite:' . $options['database_file'], null, null, $this->option);
+
+                        return;
+                }
+            }
+
+            $driver = $attr['driver'];
+
+            unset($attr['driver']);
+
+            $stack = [];
+
+            foreach ($attr as $key => $value) {
+                if (is_int($key)) {
+                    $stack[] = $value;
+                } else {
+                    $stack[] = $key . '=' . $value;
+                }
+            }
+
+            $dsn = $driver . ':' . implode($stack, ';');
+
+            if (
+                in_array($this->database_type, ['mariadb', 'mysql', 'pgsql', 'sybase', 'mssql']) &&
+                isset($options['charset'])
+            ) {
+                $commands[] = "SET NAMES '" . $options['charset'] . "'";
+            }
+
+            $this->pdo = new PDO(
+                $dsn,
+                $options['username'],
+                $options['password'],
+                $this->option
+            );
+
+            foreach ($commands as $value) {
+                $this->pdo->exec($value);
+            }
+        } catch (PDOException $e) {
+            throw new PDOException($e->getMessage());
+        }
+    }
+
+    public function query($query, $map = [])
+    {
+        if (!empty($map)) {
+            foreach ($map as $key => $value) {
+                switch (gettype($value)) {
+                    case 'NULL':
+                        $map[$key] = [null, PDO::PARAM_NULL];
+                        break;
+
+                    case 'resource':
+                        $map[$key] = [$value, PDO::PARAM_LOB];
+                        break;
+
+                    case 'boolean':
+                        $map[$key] = [($value ? '1' : '0'), PDO::PARAM_BOOL];
+                        break;
+
+                    case 'integer':
+                    case 'double':
+                        $map[$key] = [$value, PDO::PARAM_INT];
+                        break;
+
+                    case 'string':
+                        $map[$key] = [$value, PDO::PARAM_STR];
+                        break;
+                }
+            }
+        }
+
+        return $this->exec($query, $map);
+    }
+
+    public function exec($query, $map = [])
+    {
+        if ($this->debug_mode) {
+            echo $this->generate($query, $map);
+
+            $this->debug_mode = false;
+
+            return false;
+        }
+
+        if ($this->logging) {
+            $this->logs[] = [$query, $map];
+        } else {
+            $this->logs = [[$query, $map]];
+        }
+
+        $statement = $this->pdo->prepare($query);
+
+        if ($statement) {
+            foreach ($map as $key => $value) {
+                $statement->bindValue($key, $value[0], $value[1]);
+            }
+
+            $statement->execute();
+
+            $this->statement = $statement;
+
+            return $statement;
+        } else {
+            return false;
+        }
+    }
+
+    protected function generate($query, $map)
+    {
+        foreach ($map as $key => $value) {
+            if ($value[1] === PDO::PARAM_STR) {
+                $query = str_replace($key, $this->quote($value[0]), $query);
+            } elseif ($value[1] === PDO::PARAM_NULL) {
+                $query = str_replace($key, 'NULL', $query);
+            } else {
+                $query = str_replace($key, $value[0], $query);
+            }
+        }
+
+        return $query;
+    }
+
+    public function quote($string)
+    {
+        return $this->pdo->quote($string);
+    }
+
+    protected function tableQuote($table)
+    {
+        return '"' . $this->prefix . $table . '"';
+    }
+
+    protected function mapKey()
+    {
+        return ':MeDoO_' . $this->guid++ . '_mEdOo';
+    }
+
+    protected function columnQuote($string)
+    {
+        preg_match('/(^#)?([a-zA-Z0-9_]*)\.([a-zA-Z0-9_]*)(\s*\[JSON\]$)?/', $string, $column_match);
+
+        if (isset($column_match[2], $column_match[3])) {
+            return '"' . $this->prefix . $column_match[2] . '"."' . $column_match[3] . '"';
+        }
+
+        return '"' . $string . '"';
+    }
+
+    protected function columnPush(&$columns)
+    {
+        if ($columns === '*') {
+            return $columns;
+        }
+
+        $stack = [];
+
+        if (is_string($columns)) {
+            $columns = [$columns];
+        }
+
+        foreach ($columns as $key => $value) {
+            if (is_array($value)) {
+                $stack[] = $this->columnPush($value);
+            } else {
+                preg_match('/(?<column>[a-zA-Z0-9_\.]+)(?:\s*\((?<alias>[a-zA-Z0-9_]+)\)|\s*\[(?<type>(String|Bool|Int|Number|Object|JSON))\])?/i', $value, $match);
+
+                if (!empty($match['alias'])) {
+                    $stack[] = $this->columnQuote($match['column']) . ' AS ' . $this->columnQuote($match['alias']);
+
+                    $columns[$key] = $match['alias'];
+                } else {
+                    $stack[] = $this->columnQuote($match['column']);
+                }
+            }
+        }
+
+        return implode($stack, ',');
+    }
+
+    protected function arrayQuote($array)
+    {
+        $stack = [];
+
+        foreach ($array as $value) {
+            $stack[] = is_int($value) ? $value : $this->pdo->quote($value);
+        }
+
+        return implode($stack, ',');
+    }
+
+    protected function innerConjunct($data, $map, $conjunctor, $outer_conjunctor)
+    {
+        $stack = [];
+
+        foreach ($data as $value) {
+            $stack[] = '(' . $this->dataImplode($value, $map, $conjunctor) . ')';
+        }
+
+        return implode($outer_conjunctor . ' ', $stack);
+    }
+
+    protected function fnQuote($column, $string)
+    {
+        return (strpos($column, '#') === 0 && preg_match('/^[A-Z0-9\_]*\([^)]*\)$/', $string)) ?
+
+            $string :
+
+            $this->quote($string);
+    }
+
+    protected function dataImplode($data, &$map, $conjunctor)
+    {
+        $wheres = [];
+
+        foreach ($data as $key => $value) {
+            $map_key = $this->mapKey();
+
+            $type = gettype($value);
+
+            if (
+                preg_match("/^(AND|OR)(\s+#.*)?$/i", $key, $relation_match) &&
+                $type === 'array'
+            ) {
+                $wheres[] = 0 !== count(array_diff_key($value, array_keys(array_keys($value)))) ?
+                    '(' . $this->dataImplode($value, $map, ' ' . $relation_match[1]) . ')' :
+                    '(' . $this->innerConjunct($value, $map, ' ' . $relation_match[1], $conjunctor) . ')';
+            } else {
+                if (
+                    is_int($key) &&
+                    preg_match('/([a-zA-Z0-9_\.]+)\[(?<operator>\>|\>\=|\<|\<\=|\!|\=)\]([a-zA-Z0-9_\.]+)/i', $value, $match)
+                ) {
+                    $wheres[] = $this->columnQuote($match[1]) . ' ' . $match['operator'] . ' ' . $this->columnQuote($match[3]);
+                } else {
+                    preg_match('/(#?)([a-zA-Z0-9_\.]+)(\[(?<operator>\>|\>\=|\<|\<\=|\!|\<\>|\>\<|\!?~)\])?/i', $key, $match);
+                    $column = $this->columnQuote($match[2]);
+
+                    if (!empty($match[1])) {
+                        $wheres[] = $column .
+                            (isset($match['operator']) ? ' ' . $match['operator'] . ' ' : ' = ') .
+                            $this->fnQuote($key, $value);
+
+                        continue;
+                    }
+
+                    if (isset($match['operator'])) {
+                        $operator = $match['operator'];
+
+                        if ($operator === '!') {
+                            switch ($type) {
+                                case 'NULL':
+                                    $wheres[] = $column . ' IS NOT NULL';
+                                    break;
+
+                                case 'array':
+                                    $wheres[] = $column . ' NOT IN (' . $this->arrayQuote($value) . ')';
+                                    break;
+
+                                case 'integer':
+                                case 'double':
+                                    $wheres[] = $column . ' != ' . $map_key;
+                                    $map[$map_key] = [$value, PDO::PARAM_INT];
+                                    break;
+
+                                case 'boolean':
+                                    $wheres[] = $column . ' != ' . $map_key;
+                                    $map[$map_key] = [($value ? '1' : '0'), PDO::PARAM_BOOL];
+                                    break;
+
+                                case 'string':
+                                    $wheres[] = $column . ' != ' . $map_key;
+                                    $map[$map_key] = [$value, PDO::PARAM_STR];
+                                    break;
+                            }
+                        }
+
+                        if ($operator === '<>' || $operator === '><') {
+                            if ($type === 'array') {
+                                if ($operator === '><') {
+                                    $column .= ' NOT';
+                                }
+
+                                $wheres[] = '(' . $column . ' BETWEEN ' . $map_key . 'a AND ' . $map_key . 'b)';
+
+                                $data_type = (is_numeric($value[0]) && is_numeric($value[1])) ? PDO::PARAM_INT : PDO::PARAM_STR;
+
+                                $map[$map_key . 'a'] = [$value[0], $data_type];
+                                $map[$map_key . 'b'] = [$value[1], $data_type];
+                            }
+                        }
+
+                        if ($operator === '~' || $operator === '!~') {
+                            if ($type !== 'array') {
+                                $value = [$value];
+                            }
+
+                            $connector = ' OR ';
+                            $stack = array_values($value);
+
+                            if (is_array($stack[0])) {
+                                if (isset($value['AND']) || isset($value['OR'])) {
+                                    $connector = ' ' . array_keys($value)[0] . ' ';
+                                    $value = $stack[0];
+                                }
+                            }
+
+                            $like_clauses = [];
+
+                            foreach ($value as $index => $item) {
+                                $item = strval($item);
+
+                                if (!preg_match('/(\[.+\]|_|%.+|.+%)/', $item)) {
+                                    $item = '%' . $item . '%';
+                                }
+
+                                $like_clauses[] = $column . ($operator === '!~' ? ' NOT' : '') . ' LIKE ' . $map_key . 'L' . $index;
+                                $map[$map_key . 'L' . $index] = [$item, PDO::PARAM_STR];
+                            }
+
+                            $wheres[] = '(' . implode($connector, $like_clauses) . ')';
+                        }
+
+                        if (in_array($operator, ['>', '>=', '<', '<='])) {
+                            $condition = $column . ' ' . $operator . ' ';
+
+                            if (is_numeric($value)) {
+                                $condition .= $map_key;
+                                $map[$map_key] = [$value, PDO::PARAM_INT];
+                            } else {
+                                $condition .= $map_key;
+                                $map[$map_key] = [$value, PDO::PARAM_STR];
+                            }
+
+                            $wheres[] = $condition;
+                        }
+                    } else {
+                        switch ($type) {
+                            case 'NULL':
+                                $wheres[] = $column . ' IS NULL';
+                                break;
+
+                            case 'array':
+                                $wheres[] = $column . ' IN (' . $this->arrayQuote($value) . ')';
+                                break;
+
+                            case 'integer':
+                            case 'double':
+                                $wheres[] = $column . ' = ' . $map_key;
+                                $map[$map_key] = [$value, PDO::PARAM_INT];
+                                break;
+
+                            case 'boolean':
+                                $wheres[] = $column . ' = ' . $map_key;
+                                $map[$map_key] = [($value ? '1' : '0'), PDO::PARAM_BOOL];
+                                break;
+
+                            case 'string':
+                                $wheres[] = $column . ' = ' . $map_key;
+                                $map[$map_key] = [$value, PDO::PARAM_STR];
+                                break;
+                        }
+                    }
+                }
+            }
+        }
+
+        return implode($conjunctor . ' ', $wheres);
+    }
+
+    protected function whereClause($where, &$map)
+    {
+        $where_clause = '';
+
+        if (is_array($where)) {
+            $where_keys = array_keys($where);
+            $where_AND = preg_grep("/^AND\s*#?$/i", $where_keys);
+            $where_OR = preg_grep("/^OR\s*#?$/i", $where_keys);
+
+            $single_condition = array_diff_key($where, array_flip(
+                ['AND', 'OR', 'GROUP', 'ORDER', 'HAVING', 'LIMIT', 'LIKE', 'MATCH']
+            ));
+
+            if (!empty($single_condition)) {
+                $condition = $this->dataImplode($single_condition, $map, ' AND');
+
+                if ($condition !== '') {
+                    $where_clause = ' WHERE ' . $condition;
+                }
+            }
+
+            if (!empty($where_AND)) {
+                $value = array_values($where_AND);
+                $where_clause = ' WHERE ' . $this->dataImplode($where[$value[0]], $map, ' AND');
+            }
+
+            if (!empty($where_OR)) {
+                $value = array_values($where_OR);
+                $where_clause = ' WHERE ' . $this->dataImplode($where[$value[0]], $map, ' OR');
+            }
+
+            if (isset($where['MATCH'])) {
+                $MATCH = $where['MATCH'];
+
+                if (is_array($MATCH) && isset($MATCH['columns'], $MATCH['keyword'])) {
+                    $mode = '';
+
+                    $mode_array = [
+                        'natural' => 'IN NATURAL LANGUAGE MODE',
+                        'natural+query' => 'IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION',
+                        'boolean' => 'IN BOOLEAN MODE',
+                        'query' => 'WITH QUERY EXPANSION'
+                    ];
+
+                    if (isset($MATCH['mode'], $mode_array[$MATCH['mode']])) {
+                        $mode = ' ' . $mode_array[$MATCH['mode']];
+                    }
+
+                    $columns = implode(array_map([$this, 'columnQuote'], $MATCH['columns']), ', ');
+                    $map_key = $this->mapKey();
+                    $map[$map_key] = [$MATCH['keyword'], PDO::PARAM_STR];
+
+                    $where_clause .= ($where_clause !== '' ? ' AND ' : ' WHERE') . ' MATCH (' . $columns . ') AGAINST (' . $map_key . $mode . ')';
+                }
+            }
+
+            if (isset($where['GROUP'])) {
+                $GROUP = $where['GROUP'];
+
+                if (is_array($GROUP)) {
+                    $stack = [];
+
+                    foreach ($GROUP as $column => $value) {
+                        $stack[] = $this->columnQuote($value);
+                    }
+
+                    $where_clause .= ' GROUP BY ' . implode($stack, ',');
+                } else {
+                    $where_clause .= ' GROUP BY ' . $this->columnQuote($where['GROUP']);
+                }
+
+                if (isset($where['HAVING'])) {
+                    $where_clause .= ' HAVING ' . $this->dataImplode($where['HAVING'], $map, ' AND');
+                }
+            }
+
+            if (isset($where['ORDER'])) {
+                $ORDER = $where['ORDER'];
+
+                if (is_array($ORDER)) {
+                    $stack = [];
+
+                    foreach ($ORDER as $column => $value) {
+                        if (is_array($value)) {
+                            $stack[] = 'FIELD(' . $this->columnQuote($column) . ', ' . $this->arrayQuote($value) . ')';
+                        } else if ($value === 'ASC' || $value === 'DESC') {
+                            $stack[] = $this->columnQuote($column) . ' ' . $value;
+                        } else if (is_int($column)) {
+                            $stack[] = $this->columnQuote($value);
+                        }
+                    }
+
+                    $where_clause .= ' ORDER BY ' . implode($stack, ',');
+                } else {
+                    $where_clause .= ' ORDER BY ' . $this->columnQuote($ORDER);
+                }
+
+                if (
+                    isset($where['LIMIT']) &&
+                    in_array($this->database_type, ['oracle', 'mssql'])
+                ) {
+                    $LIMIT = $where['LIMIT'];
+
+                    if (is_numeric($LIMIT)) {
+                        $where_clause .= ' FETCH FIRST ' . $LIMIT . ' ROWS ONLY';
+                    }
+
+                    if (
+                        is_array($LIMIT) &&
+                        is_numeric($LIMIT[0]) &&
+                        is_numeric($LIMIT[1])
+                    ) {
+                        $where_clause .= ' OFFSET ' . $LIMIT[0] . ' ROWS FETCH NEXT ' . $LIMIT[1] . ' ROWS ONLY';
+                    }
+                }
+            }
+
+            if (isset($where['LIMIT']) && !in_array($this->database_type, ['oracle', 'mssql'])) {
+                $LIMIT = $where['LIMIT'];
+
+                if (is_numeric($LIMIT)) {
+                    $where_clause .= ' LIMIT ' . $LIMIT;
+                }
+
+                if (
+                    is_array($LIMIT) &&
+                    is_numeric($LIMIT[0]) &&
+                    is_numeric($LIMIT[1])
+                ) {
+                    $where_clause .= ' LIMIT ' . $LIMIT[1] . ' OFFSET ' . $LIMIT[0];
+                }
+            }
+        } else {
+            if ($where !== null) {
+                $where_clause .= ' ' . $where;
+            }
+        }
+
+        return $where_clause;
+    }
+
+    protected function selectContext($table, &$map, $join, &$columns = null, $where = null, $column_fn = null)
+    {
+        preg_match('/(?<table>[a-zA-Z0-9_]+)\s*\((?<alias>[a-zA-Z0-9_]+)\)/i', $table, $table_match);
+
+        if (isset($table_match['table'], $table_match['alias'])) {
+            $table = $this->tableQuote($table_match['table']);
+
+            $table_query = $table . ' AS ' . $this->tableQuote($table_match['alias']);
+        } else {
+            $table = $this->tableQuote($table);
+
+            $table_query = $table;
+        }
+
+        $join_key = is_array($join) ? array_keys($join) : null;
+
+        if (
+            isset($join_key[0]) &&
+            strpos($join_key[0], '[') === 0
+        ) {
+            $table_join = [];
+
+            $join_array = [
+                '>' => 'LEFT',
+                '<' => 'RIGHT',
+                '<>' => 'FULL',
+                '><' => 'INNER'
+            ];
+
+            foreach ($join as $sub_table => $relation) {
+                preg_match('/(\[(?<join>\<|\>|\>\<|\<\>)\])?(?<table>[a-zA-Z0-9_]+)\s?(\((?<alias>[a-zA-Z0-9_]+)\))?/', $sub_table, $match);
+
+                if ($match['join'] !== '' && $match['table'] !== '') {
+                    if (is_string($relation)) {
+                        $relation = 'USING ("' . $relation . '")';
+                    }
+
+                    if (is_array($relation)) {
+                        // For ['column1', 'column2']
+                        if (isset($relation[0])) {
+                            $relation = 'USING ("' . implode($relation, '", "') . '")';
+                        } else {
+                            $joins = [];
+
+                            foreach ($relation as $key => $value) {
+                                $joins[] = (
+                                    strpos($key, '.') > 0 ?
+                                        // For ['tableB.column' => 'column']
+                                        $this->columnQuote($key) :
+
+                                        // For ['column1' => 'column2']
+                                        $table . '."' . $key . '"'
+                                    ) .
+                                    ' = ' .
+                                    $this->tableQuote(isset($match['alias']) ? $match['alias'] : $match['table']) . '."' . $value . '"';
+                            }
+
+                            $relation = 'ON ' . implode($joins, ' AND ');
+                        }
+                    }
+
+                    $table_name = $this->tableQuote($match['table']) . ' ';
+
+                    if (isset($match['alias'])) {
+                        $table_name .= 'AS ' . $this->tableQuote($match['alias']) . ' ';
+                    }
+
+                    $table_join[] = $join_array[$match['join']] . ' JOIN ' . $table_name . $relation;
+                }
+            }
+
+            $table_query .= ' ' . implode($table_join, ' ');
+        } else {
+            if (is_null($columns)) {
+                if (is_null($where)) {
+                    if (
+                        is_array($join) &&
+                        isset($column_fn)
+                    ) {
+                        $where = $join;
+                        $columns = null;
+                    } else {
+                        $where = null;
+                        $columns = $join;
+                    }
+                } else {
+                    $where = $join;
+                    $columns = null;
+                }
+            } else {
+                $where = $columns;
+                $columns = $join;
+            }
+        }
+
+        if (isset($column_fn)) {
+            if ($column_fn === 1) {
+                $column = '1';
+
+                if (is_null($where)) {
+                    $where = $columns;
+                }
+            } else {
+                if (empty($columns)) {
+                    $columns = '*';
+                    $where = $join;
+                }
+
+                $column = $column_fn . '(' . $this->columnPush($columns) . ')';
+            }
+        } else {
+            $column = $this->columnPush($columns);
+        }
+
+        return 'SELECT ' . $column . ' FROM ' . $table_query . $this->whereClause($where, $map);
+    }
+
+    protected function columnMap($columns, &$stack)
+    {
+        if ($columns === '*') {
+            return $stack;
+        }
+
+        foreach ($columns as $key => $value) {
+            if (is_int($key)) {
+                preg_match('/(?<column>[a-zA-Z0-9_\.]*)(?:\s*\((?<alias>[a-zA-Z0-9_]+)\)|\s*\[(?<type>(String|Bool|Int|Number|Object|JSON))\])?/i', $value, $key_match);
+
+                $column_key = !empty($key_match['alias']) ?
+                    $key_match['alias'] :
+                    preg_replace('/^[\w]*\./i', '', $key_match['column']);
+
+                if (isset($key_match['type'])) {
+                    $stack[$value] = [$column_key, $key_match['type']];
+                } else {
+                    $stack[$value] = [$column_key, 'String'];
+                }
+            } else {
+                $this->columnMap($value, $stack);
+            }
+        }
+
+        return $stack;
+    }
+
+    protected function dataMap($data, $columns, $column_map, &$stack)
+    {
+        foreach ($columns as $key => $value) {
+            if (is_int($key)) {
+                $map = $column_map[$value];
+                $column_key = $map[0];
+
+                if (isset($map[1])) {
+                    switch ($map[1]) {
+                        case 'Number':
+                        case 'Int':
+                            $stack[$column_key] = (int)$data[$column_key];
+                            break;
+
+                        case 'Bool':
+                            $stack[$column_key] = (bool)$data[$column_key];
+                            break;
+
+                        case 'Object':
+                            $stack[$column_key] = unserialize($data[$column_key]);
+                            break;
+
+                        case 'JSON':
+                            $stack[$column_key] = json_decode($data[$column_key], true);
+                            break;
+
+                        case 'String':
+                            $stack[$column_key] = $data[$column_key];
+                            break;
+                    }
+                } else {
+                    $stack[$column_key] = $data[$column_key];
+                }
+            } else {
+                $current_stack = [];
+
+                $this->dataMap($data, $value, $column_map, $current_stack);
+
+                $stack[$key] = $current_stack;
+            }
+        }
+    }
+
+    public function select($table, $join, $columns = null, $where = null)
+    {
+        $map = [];
+        $stack = [];
+        $column_map = [];
+
+        $index = 0;
+
+        $column = $where === null ? $join : $columns;
+
+        $is_single_column = (is_string($column) && $column !== '*');
+
+        $query = $this->exec($this->selectContext($table, $map, $join, $columns, $where), $map);
+
+        $this->columnMap($columns, $column_map);
+
+        if (!$query) {
+            return false;
+        }
+
+        if ($columns === '*') {
+            return $query->fetchAll(PDO::FETCH_ASSOC);
+        }
+
+        if ($is_single_column) {
+            return $query->fetchAll(PDO::FETCH_COLUMN);
+        }
+
+        while ($data = $query->fetch(PDO::FETCH_ASSOC)) {
+            $current_stack = [];
+
+            $this->dataMap($data, $columns, $column_map, $current_stack);
+
+            $stack[$index] = $current_stack;
+
+            $index++;
+        }
+
+        return $stack;
+    }
+
+    public function insert($table, $datas)
+    {
+        $stack = [];
+        $columns = [];
+        $fields = [];
+        $map = [];
+
+        if (!isset($datas[0])) {
+            $datas = [$datas];
+        }
+
+        foreach ($datas as $data) {
+            foreach ($data as $key => $value) {
+                $columns[] = $key;
+            }
+        }
+
+        $columns = array_unique($columns);
+
+        foreach ($datas as $data) {
+            $values = [];
+
+            foreach ($columns as $key) {
+                if (strpos($key, '#') === 0) {
+                    $values[] = $this->fnQuote($key, $data[$key]);
+                    continue;
+                }
+
+                $map_key = $this->mapKey();
+
+                $values[] = $map_key;
+
+                if (!isset($data[$key])) {
+                    $map[$map_key] = [null, PDO::PARAM_NULL];
+                } else {
+                    $value = $data[$key];
+
+                    switch (gettype($value)) {
+                        case 'NULL':
+                            $map[$map_key] = [null, PDO::PARAM_NULL];
+                            break;
+
+                        case 'array':
+                            $map[$map_key] = [
+                                strpos($key, '[JSON]') === strlen($key) - 6 ?
+                                    json_encode($value) :
+                                    serialize($value),
+                                PDO::PARAM_STR
+                            ];
+                            break;
+
+                        case 'object':
+                            $map[$map_key] = [serialize($value), PDO::PARAM_STR];
+                            break;
+
+                        case 'resource':
+                            $map[$map_key] = [$value, PDO::PARAM_LOB];
+                            break;
+
+                        case 'boolean':
+                            $map[$map_key] = [($value ? '1' : '0'), PDO::PARAM_BOOL];
+                            break;
+
+                        case 'integer':
+                        case 'double':
+                            $map[$map_key] = [$value, PDO::PARAM_INT];
+                            break;
+
+                        case 'string':
+                            $map[$map_key] = [$value, PDO::PARAM_STR];
+                            break;
+                    }
+                }
+            }
+
+            $stack[] = '(' . implode($values, ', ') . ')';
+        }
+
+        foreach ($columns as $key) {
+            $fields[] = $this->columnQuote(preg_replace("/(^#|\s*\[JSON\]$)/i", '', $key));
+        }
+
+        return $this->exec('INSERT INTO ' . $this->tableQuote($table) . ' (' . implode(', ', $fields) . ') VALUES ' . implode(', ', $stack), $map);
+    }
+
+    public function update($table, $data, $where = null)
+    {
+        $fields = [];
+        $map = [];
+
+        foreach ($data as $key => $value) {
+            $column = $this->columnQuote(preg_replace("/(^#|\s*\[(JSON|\+|\-|\*|\/)\]$)/i", '', $key));
+
+            if (strpos($key, '#') === 0) {
+                $fields[] = $column . ' = ' . $value;
+                continue;
+            }
+
+            $map_key = $this->mapKey();
+
+            preg_match('/(?<column>[a-zA-Z0-9_]+)(\[(?<operator>\+|\-|\*|\/)\])?/i', $key, $match);
+
+            if (isset($match['operator'])) {
+                if (is_numeric($value)) {
+                    $fields[] = $column . ' = ' . $column . ' ' . $match['operator'] . ' ' . $value;
+                }
+            } else {
+                $fields[] = $column . ' = ' . $map_key;
+
+                switch (gettype($value)) {
+                    case 'NULL':
+                        $map[$map_key] = [null, PDO::PARAM_NULL];
+                        break;
+
+                    case 'array':
+                        $map[$map_key] = [
+                            strpos($key, '[JSON]') === strlen($key) - 6 ?
+                                json_encode($value) :
+                                serialize($value),
+                            PDO::PARAM_STR
+                        ];
+                        break;
+
+                    case 'object':
+                        $map[$map_key] = [serialize($value), PDO::PARAM_STR];
+                        break;
+
+                    case 'resource':
+                        $map[$map_key] = [$value, PDO::PARAM_LOB];
+                        break;
+
+                    case 'boolean':
+                        $map[$map_key] = [($value ? '1' : '0'), PDO::PARAM_BOOL];
+                        break;
+
+                    case 'integer':
+                    case 'double':
+                        $map[$map_key] = [$value, PDO::PARAM_INT];
+                        break;
+
+                    case 'string':
+                        $map[$map_key] = [$value, PDO::PARAM_STR];
+                        break;
+                }
+            }
+        }
+
+        return $this->exec('UPDATE ' . $this->tableQuote($table) . ' SET ' . implode(', ', $fields) . $this->whereClause($where, $map), $map);
+    }
+
+    public function delete($table, $where)
+    {
+        $map = [];
+
+        return $this->exec('DELETE FROM ' . $this->tableQuote($table) . $this->whereClause($where, $map), $map);
+    }
+
+    public function replace($table, $columns, $where = null)
+    {
+        $map = [];
+
+        if (is_array($columns)) {
+            $replace_query = [];
+
+            foreach ($columns as $column => $replacements) {
+                if (is_array($replacements[0])) {
+                    foreach ($replacements as $replacement) {
+                        $map_key = $this->mapKey();
+
+                        $replace_query[] = $this->columnQuote($column) . ' = REPLACE(' . $this->columnQuote($column) . ', ' . $map_key . 'a, ' . $map_key . 'b)';
+
+                        $map[$map_key . 'a'] = [$replacement[0], PDO::PARAM_STR];
+                        $map[$map_key . 'b'] = [$replacement[1], PDO::PARAM_STR];
+                    }
+                } else {
+                    $map_key = $this->mapKey();
+
+                    $replace_query[] = $this->columnQuote($column) . ' = REPLACE(' . $this->columnQuote($column) . ', ' . $map_key . 'a, ' . $map_key . 'b)';
+
+                    $map[$map_key . 'a'] = [$replacements[0], PDO::PARAM_STR];
+                    $map[$map_key . 'b'] = [$replacements[1], PDO::PARAM_STR];
+                }
+            }
+
+            $replace_query = implode(', ', $replace_query);
+        }
+
+        return $this->exec('UPDATE ' . $this->tableQuote($table) . ' SET ' . $replace_query . $this->whereClause($where, $map), $map);
+    }
+
+    public function get($table, $join = null, $columns = null, $where = null)
+    {
+        $map = [];
+        $stack = [];
+        $column_map = [];
+
+        $column = $where === null ? $join : $columns;
+
+        $is_single_column = (is_string($column) && $column !== '*');
+
+        $query = $this->exec($this->selectContext($table, $map, $join, $columns, $where) . ' LIMIT 1', $map);
+
+        if ($query) {
+            $data = $query->fetchAll(PDO::FETCH_ASSOC);
+
+            if (isset($data[0])) {
+                if ($column === '*') {
+                    return $data[0];
+                }
+
+                $this->columnMap($columns, $column_map);
+
+                $this->dataMap($data[0], $columns, $column_map, $stack);
+
+                if ($is_single_column) {
+                    return $stack[$column_map[$column][0]];
+                }
+
+                return $stack;
+            } else {
+                return false;
+            }
+        } else {
+            return false;
+        }
+    }
+
+    public function has($table, $join, $where = null)
+    {
+        $map = [];
+        $column = null;
+
+        $query = $this->exec('SELECT EXISTS(' . $this->selectContext($table, $map, $join, $column, $where, 1) . ')', $map);
+
+        if ($query) {
+            return $query->fetchColumn() === '1';
+        } else {
+            return false;
+        }
+    }
+
+    public function count($table, $join = null, $column = null, $where = null)
+    {
+        $map = [];
+
+        $query = $this->exec($this->selectContext($table, $map, $join, $column, $where, 'COUNT'), $map);
+
+        return $query ? 0 + $query->fetchColumn() : false;
+    }
+
+    public function max($table, $join, $column = null, $where = null)
+    {
+        $map = [];
+
+        $query = $this->exec($this->selectContext($table, $map, $join, $column, $where, 'MAX'), $map);
+
+        if ($query) {
+            $max = $query->fetchColumn();
+
+            return is_numeric($max) ? $max + 0 : $max;
+        } else {
+            return false;
+        }
+    }
+
+    public function min($table, $join, $column = null, $where = null)
+    {
+        $map = [];
+
+        $query = $this->exec($this->selectContext($table, $map, $join, $column, $where, 'MIN'), $map);
+
+        if ($query) {
+            $min = $query->fetchColumn();
+
+            return is_numeric($min) ? $min + 0 : $min;
+        } else {
+            return false;
+        }
+    }
+
+    public function avg($table, $join, $column = null, $where = null)
+    {
+        $map = [];
+
+        $query = $this->exec($this->selectContext($table, $map, $join, $column, $where, 'AVG'), $map);
+
+        return $query ? 0 + $query->fetchColumn() : false;
+    }
+
+    public function sum($table, $join, $column = null, $where = null)
+    {
+        $map = [];
+
+        $query = $this->exec($this->selectContext($table, $map, $join, $column, $where, 'SUM'), $map);
+
+        return $query ? 0 + $query->fetchColumn() : false;
+    }
+
+    public function action($actions)
+    {
+        if (is_callable($actions)) {
+            $this->pdo->beginTransaction();
+
+            $result = $actions($this);
+
+            if ($result === false) {
+                $this->pdo->rollBack();
+            } else {
+                $this->pdo->commit();
+            }
+        } else {
+            return false;
+        }
+    }
+
+    public function id()
+    {
+        $type = $this->database_type;
+
+        if ($type === 'oracle') {
+            return 0;
+        } elseif ($type === 'mssql') {
+            return $this->pdo->query('SELECT SCOPE_IDENTITY()')->fetchColumn();
+        } elseif ($type === 'pgsql') {
+            return $this->pdo->query('SELECT LASTVAL()')->fetchColumn();
+        }
+
+        return $this->pdo->lastInsertId();
+    }
+
+    public function debug()
+    {
+        $this->debug_mode = true;
+
+        return $this;
+    }
+
+    public function error()
+    {
+        return $this->statement ? $this->statement->errorInfo() : null;
+    }
+
+    public function last()
+    {
+        $log = end($this->logs);
+
+        return $this->generate($log[0], $log[1]);
+    }
+
+    public function log()
+    {
+        return array_map(function ($log) {
+            return $this->generate($log[0], $log[1]);
+        },
+            $this->logs
+        );
+    }
+
+    public function info()
+    {
+        $output = [
+            'server' => 'SERVER_INFO',
+            'driver' => 'DRIVER_NAME',
+            'client' => 'CLIENT_VERSION',
+            'version' => 'SERVER_VERSION',
+            'connection' => 'CONNECTION_STATUS'
+        ];
+
+        foreach ($output as $key => $value) {
+            $output[$key] = @$this->pdo->getAttribute(constant('PDO::ATTR_' . $value));
+        }
+
+        return $output;
+    }
+}
+
+?>

+ 19 - 0
Library/Telegram.php

@@ -0,0 +1,19 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: chendeben
+ * Date: 2017/12/6
+ * Time: 11:55
+ */
+
+namespace Heanup\Library;
+
+class Telegram
+{
+    public static function sendMessage(){
+        $telegram=new \Telegram("532191649:AAGxdBNPQGrHo0Ralqpg_sSZFrJJwCh0KIk");
+        $chat_id = $telegram->ChatID();
+        $content = array('chat_id' => $chat_id, 'text' => 'Test');
+        $telegram->sendMessage($content);
+    }
+}

+ 38 - 0
Library/Vestacp.php

@@ -0,0 +1,38 @@
+<?php
+
+
+namespace Heanup\Library;
+
+
+use ProfiCloS\VestaCP\Client;
+use ProfiCloS\VestaCP\ClientException;
+
+class Vestacp
+{
+
+
+    private static $instance;
+    /**
+     * @var Client
+     */
+    private static $client;
+
+
+    public function __construct()
+    {
+        $vst_hostname = \Heanup\App\Api\Config\Vestacp::getData("hostname");
+        $vst_username = \Heanup\App\Api\Config\Vestacp::getData("username");
+        $vst_password = \Heanup\App\Api\Config\Vestacp::getData("password");
+        try {
+            self::$client = Client::simpleFactory($vst_hostname, $vst_username, $vst_password);
+        } catch (ClientException $e) {
+        }
+    }
+    public static function getInstance(){
+        if (!isset(self::$instance)) {
+            $instance = new self();
+            self::$instance = $instance;
+        }
+        return self::$client;
+    }
+}

+ 8 - 0
apidoc.json

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

+ 12 - 0
composer.json

@@ -0,0 +1,12 @@
+{
+    "require": {
+        "eleirbag89/telegrambotphp": "^1.3",
+        "phpunit/phpunit": "^6.2",
+        "zircote/swagger-php": "*",
+        "phpmailer/phpmailer": "^6.0",
+        "jamesryanbell/cloudflare": "^1.11",
+        "qingbing/php-file-cache": "^1.0",
+        "proficlos/vestacp-api": "^0.1.0",
+        "php-imap/php-imap": "^3.0"
+    }
+}

+ 2520 - 0
composer.lock

@@ -0,0 +1,2520 @@
+{
+    "_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": "b6962c014f681108f1a984a2a0e2f2c2",
+    "packages": [
+        {
+            "name": "doctrine/annotations",
+            "version": "v1.6.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/annotations.git",
+                "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/annotations/zipball/c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5",
+                "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/lexer": "1.*",
+                "php": "^7.1"
+            },
+            "require-dev": {
+                "doctrine/cache": "1.*",
+                "phpunit/phpunit": "^6.4"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.6.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Roman Borschel",
+                    "email": "roman@code-factory.org"
+                },
+                {
+                    "name": "Benjamin Eberlei",
+                    "email": "kontakt@beberlei.de"
+                },
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "guilhermeblanco@gmail.com"
+                },
+                {
+                    "name": "Jonathan Wage",
+                    "email": "jonwage@gmail.com"
+                },
+                {
+                    "name": "Johannes Schmitt",
+                    "email": "schmittjoh@gmail.com"
+                }
+            ],
+            "description": "Docblock Annotations Parser",
+            "homepage": "http://www.doctrine-project.org",
+            "keywords": [
+                "annotations",
+                "docblock",
+                "parser"
+            ],
+            "time": "2017-12-06T07:11:42+00:00"
+        },
+        {
+            "name": "doctrine/instantiator",
+            "version": "1.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/instantiator.git",
+                "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/instantiator/zipball/185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda",
+                "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1"
+            },
+            "require-dev": {
+                "athletic/athletic": "~0.1.8",
+                "ext-pdo": "*",
+                "ext-phar": "*",
+                "phpunit/phpunit": "^6.2.3",
+                "squizlabs/php_codesniffer": "^3.0.2"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.2.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Marco Pivetta",
+                    "email": "ocramius@gmail.com",
+                    "homepage": "http://ocramius.github.com/"
+                }
+            ],
+            "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
+            "homepage": "https://github.com/doctrine/instantiator",
+            "keywords": [
+                "constructor",
+                "instantiate"
+            ],
+            "time": "2017-07-22T11:58:36+00:00"
+        },
+        {
+            "name": "doctrine/lexer",
+            "version": "v1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/lexer.git",
+                "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c",
+                "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.2"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Doctrine\\Common\\Lexer\\": "lib/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Roman Borschel",
+                    "email": "roman@code-factory.org"
+                },
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "guilhermeblanco@gmail.com"
+                },
+                {
+                    "name": "Johannes Schmitt",
+                    "email": "schmittjoh@gmail.com"
+                }
+            ],
+            "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.",
+            "homepage": "http://www.doctrine-project.org",
+            "keywords": [
+                "lexer",
+                "parser"
+            ],
+            "time": "2014-09-09T13:34:57+00:00"
+        },
+        {
+            "name": "eleirbag89/telegrambotphp",
+            "version": "1.3.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Eleirbag89/TelegramBotPHP.git",
+                "reference": "aec5bc4f5873595d1b4f03fd0d955549ba4cfab3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Eleirbag89/TelegramBotPHP/zipball/aec5bc4f5873595d1b4f03fd0d955549ba4cfab3",
+                "reference": "aec5bc4f5873595d1b4f03fd0d955549ba4cfab3",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.0"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "Telegram.php",
+                    "TelegramErrorLogger.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Gabriele Grillo",
+                    "email": "gabry.grillo@alice.it"
+                }
+            ],
+            "description": "A very simple PHP Telegram Bot API",
+            "time": "2017-12-27T13:46:04+00:00"
+        },
+        {
+            "name": "guzzlehttp/guzzle",
+            "version": "6.3.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/guzzle.git",
+                "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba",
+                "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba",
+                "shasum": ""
+            },
+            "require": {
+                "guzzlehttp/promises": "^1.0",
+                "guzzlehttp/psr7": "^1.4",
+                "php": ">=5.5"
+            },
+            "require-dev": {
+                "ext-curl": "*",
+                "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0",
+                "psr/log": "^1.0"
+            },
+            "suggest": {
+                "psr/log": "Required for using the Log middleware"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "6.3-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/functions_include.php"
+                ],
+                "psr-4": {
+                    "GuzzleHttp\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                }
+            ],
+            "description": "Guzzle is a PHP HTTP client library",
+            "homepage": "http://guzzlephp.org/",
+            "keywords": [
+                "client",
+                "curl",
+                "framework",
+                "http",
+                "http client",
+                "rest",
+                "web service"
+            ],
+            "time": "2018-04-22T15:46:56+00:00"
+        },
+        {
+            "name": "guzzlehttp/promises",
+            "version": "v1.3.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/promises.git",
+                "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646",
+                "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.5.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.0"
+            },
+            "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"
+            ],
+            "time": "2016-12-20T10:07:11+00:00"
+        },
+        {
+            "name": "guzzlehttp/psr7",
+            "version": "1.5.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/psr7.git",
+                "reference": "9f83dded91781a01c63574e387eaa769be769115"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/psr7/zipball/9f83dded91781a01c63574e387eaa769be769115",
+                "reference": "9f83dded91781a01c63574e387eaa769be769115",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.4.0",
+                "psr/http-message": "~1.0",
+                "ralouphie/getallheaders": "^2.0.5"
+            },
+            "provide": {
+                "psr/http-message-implementation": "1.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.5-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"
+            ],
+            "time": "2018-12-04T20:46:45+00:00"
+        },
+        {
+            "name": "jamesryanbell/cloudflare",
+            "version": "1.11.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/jamesryanbell/cloudflare.git",
+                "reference": "75e93bb822619d8ddb8b1843588caa2fad739491"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/jamesryanbell/cloudflare/zipball/75e93bb822619d8ddb8b1843588caa2fad739491",
+                "reference": "75e93bb822619d8ddb8b1843588caa2fad739491",
+                "shasum": ""
+            },
+            "require-dev": {
+                "phpunit/phpunit": "< 6",
+                "satooshi/php-coveralls": "dev-master"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Cloudflare\\": "src/CloudFlare"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "James Bell",
+                    "email": "james@james-bell.co.uk"
+                }
+            ],
+            "description": "CloudFlare API - PHP",
+            "keywords": [
+                "api",
+                "cloudflare"
+            ],
+            "time": "2017-08-04T19:01:02+00:00"
+        },
+        {
+            "name": "myclabs/deep-copy",
+            "version": "1.7.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/myclabs/DeepCopy.git",
+                "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e",
+                "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.6 || ^7.0"
+            },
+            "require-dev": {
+                "doctrine/collections": "^1.0",
+                "doctrine/common": "^2.6",
+                "phpunit/phpunit": "^4.1"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "DeepCopy\\": "src/DeepCopy/"
+                },
+                "files": [
+                    "src/DeepCopy/deep_copy.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Create deep copies (clones) of your objects",
+            "keywords": [
+                "clone",
+                "copy",
+                "duplicate",
+                "object",
+                "object graph"
+            ],
+            "time": "2017-10-19T19:58:43+00:00"
+        },
+        {
+            "name": "nette/utils",
+            "version": "v3.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/nette/utils.git",
+                "reference": "bd961f49b211997202bda1d0fbc410905be370d4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/nette/utils/zipball/bd961f49b211997202bda1d0fbc410905be370d4",
+                "reference": "bd961f49b211997202bda1d0fbc410905be370d4",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "require-dev": {
+                "nette/tester": "~2.0",
+                "tracy/tracy": "^2.3"
+            },
+            "suggest": {
+                "ext-gd": "to use Image",
+                "ext-iconv": "to use Strings::webalize() and toAscii()",
+                "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()",
+                "ext-json": "to use Nette\\Utils\\Json",
+                "ext-mbstring": "to use Strings::lower() etc...",
+                "ext-xml": "to use Strings::length() etc. when mbstring is not available"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause",
+                "GPL-2.0",
+                "GPL-3.0"
+            ],
+            "authors": [
+                {
+                    "name": "David Grudl",
+                    "homepage": "https://davidgrudl.com"
+                },
+                {
+                    "name": "Nette Community",
+                    "homepage": "https://nette.org/contributors"
+                }
+            ],
+            "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.",
+            "homepage": "https://nette.org",
+            "keywords": [
+                "array",
+                "core",
+                "datetime",
+                "images",
+                "json",
+                "nette",
+                "paginator",
+                "password",
+                "slugify",
+                "string",
+                "unicode",
+                "utf-8",
+                "utility",
+                "validation"
+            ],
+            "time": "2019-03-22T01:00:30+00:00"
+        },
+        {
+            "name": "phar-io/manifest",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phar-io/manifest.git",
+                "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phar-io/manifest/zipball/2df402786ab5368a0169091f61a7c1e0eb6852d0",
+                "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "ext-phar": "*",
+                "phar-io/version": "^1.0.1",
+                "php": "^5.6 || ^7.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Arne Blankerts",
+                    "email": "arne@blankerts.de",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Sebastian Heuer",
+                    "email": "sebastian@phpeople.de",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "Developer"
+                }
+            ],
+            "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
+            "time": "2017-03-05T18:14:27+00:00"
+        },
+        {
+            "name": "phar-io/version",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phar-io/version.git",
+                "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df",
+                "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.6 || ^7.0"
+            },
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Arne Blankerts",
+                    "email": "arne@blankerts.de",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Sebastian Heuer",
+                    "email": "sebastian@phpeople.de",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "Developer"
+                }
+            ],
+            "description": "Library for handling version information and constraints",
+            "time": "2017-03-05T17:38:23+00:00"
+        },
+        {
+            "name": "php-imap/php-imap",
+            "version": "3.0.22",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/barbushin/php-imap.git",
+                "reference": "a776f84483ee7e5d56dc6eab11957db4187e7914"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/barbushin/php-imap/zipball/a776f84483ee7e5d56dc6eab11957db4187e7914",
+                "reference": "a776f84483ee7e5d56dc6eab11957db4187e7914",
+                "shasum": ""
+            },
+            "require": {
+                "ext-iconv": "*",
+                "ext-imap": "*",
+                "ext-mbstring": "*",
+                "php": ">=5.6"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^5.7"
+            },
+            "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"
+            ],
+            "time": "2019-06-07T18:13:47+00:00"
+        },
+        {
+            "name": "phpdocumentor/reflection-common",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
+                "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6",
+                "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.5"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.6"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "phpDocumentor\\Reflection\\": [
+                        "src"
+                    ]
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jaap van Otterdijk",
+                    "email": "opensource@ijaap.nl"
+                }
+            ],
+            "description": "Common reflection classes used by phpdocumentor to reflect the code structure",
+            "homepage": "http://www.phpdoc.org",
+            "keywords": [
+                "FQSEN",
+                "phpDocumentor",
+                "phpdoc",
+                "reflection",
+                "static analysis"
+            ],
+            "time": "2017-09-11T18:02:19+00:00"
+        },
+        {
+            "name": "phpdocumentor/reflection-docblock",
+            "version": "4.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
+                "reference": "94fd0001232e47129dd3504189fa1c7225010d08"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08",
+                "reference": "94fd0001232e47129dd3504189fa1c7225010d08",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0",
+                "phpdocumentor/reflection-common": "^1.0.0",
+                "phpdocumentor/type-resolver": "^0.4.0",
+                "webmozart/assert": "^1.0"
+            },
+            "require-dev": {
+                "doctrine/instantiator": "~1.0.5",
+                "mockery/mockery": "^1.0",
+                "phpunit/phpunit": "^6.4"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "phpDocumentor\\Reflection\\": [
+                        "src/"
+                    ]
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Mike van Riel",
+                    "email": "me@mikevanriel.com"
+                }
+            ],
+            "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
+            "time": "2017-11-30T07:14:17+00:00"
+        },
+        {
+            "name": "phpdocumentor/type-resolver",
+            "version": "0.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpDocumentor/TypeResolver.git",
+                "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7",
+                "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5 || ^7.0",
+                "phpdocumentor/reflection-common": "^1.0"
+            },
+            "require-dev": {
+                "mockery/mockery": "^0.9.4",
+                "phpunit/phpunit": "^5.2||^4.8.24"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "phpDocumentor\\Reflection\\": [
+                        "src/"
+                    ]
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Mike van Riel",
+                    "email": "me@mikevanriel.com"
+                }
+            ],
+            "time": "2017-07-14T14:27:02+00:00"
+        },
+        {
+            "name": "phpmailer/phpmailer",
+            "version": "v6.0.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/PHPMailer/PHPMailer.git",
+                "reference": "cb3ea134d4d3729e7857737d5f320cce9caf4d32"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/cb3ea134d4d3729e7857737d5f320cce9caf4d32",
+                "reference": "cb3ea134d4d3729e7857737d5f320cce9caf4d32",
+                "shasum": ""
+            },
+            "require": {
+                "ext-ctype": "*",
+                "ext-filter": "*",
+                "php": ">=5.5.0"
+            },
+            "require-dev": {
+                "doctrine/annotations": "1.2.*",
+                "friendsofphp/php-cs-fixer": "^2.2",
+                "phpdocumentor/phpdocumentor": "2.*",
+                "phpunit/phpunit": "^4.8 || ^5.7",
+                "zendframework/zend-eventmanager": "3.0.*",
+                "zendframework/zend-i18n": "2.7.3",
+                "zendframework/zend-serializer": "2.7.*"
+            },
+            "suggest": {
+                "ext-mbstring": "Needed to send email in multibyte encoding charset",
+                "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication",
+                "league/oauth2-google": "Needed for Google XOAUTH2 authentication",
+                "psr/log": "For optional PSR-3 debug logging",
+                "stevenmaguire/oauth2-microsoft": "Needed for Microsoft XOAUTH2 authentication",
+                "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "PHPMailer\\PHPMailer\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "LGPL-2.1"
+            ],
+            "authors": [
+                {
+                    "name": "Jim Jagielski",
+                    "email": "jimjag@gmail.com"
+                },
+                {
+                    "name": "Marcus Bointon",
+                    "email": "phpmailer@synchromedia.co.uk"
+                },
+                {
+                    "name": "Andy Prevost",
+                    "email": "codeworxtech@users.sourceforge.net"
+                },
+                {
+                    "name": "Brent R. Matzelle"
+                }
+            ],
+            "description": "PHPMailer is a full-featured email creation and transfer class for PHP",
+            "time": "2018-03-27T13:49:45+00:00"
+        },
+        {
+            "name": "phpspec/prophecy",
+            "version": "1.7.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpspec/prophecy.git",
+                "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpspec/prophecy/zipball/dfd6be44111a7c41c2e884a336cc4f461b3b2401",
+                "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/instantiator": "^1.0.2",
+                "php": "^5.3|^7.0",
+                "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0",
+                "sebastian/comparator": "^1.1|^2.0",
+                "sebastian/recursion-context": "^1.0|^2.0|^3.0"
+            },
+            "require-dev": {
+                "phpspec/phpspec": "^2.5|^3.2",
+                "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.7.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Prophecy\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Konstantin Kudryashov",
+                    "email": "ever.zet@gmail.com",
+                    "homepage": "http://everzet.com"
+                },
+                {
+                    "name": "Marcello Duarte",
+                    "email": "marcello.duarte@gmail.com"
+                }
+            ],
+            "description": "Highly opinionated mocking framework for PHP 5.3+",
+            "homepage": "https://github.com/phpspec/prophecy",
+            "keywords": [
+                "Double",
+                "Dummy",
+                "fake",
+                "mock",
+                "spy",
+                "stub"
+            ],
+            "time": "2018-02-19T10:16:54+00:00"
+        },
+        {
+            "name": "phpunit/php-code-coverage",
+            "version": "5.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+                "reference": "661f34d0bd3f1a7225ef491a70a020ad23a057a1"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/661f34d0bd3f1a7225ef491a70a020ad23a057a1",
+                "reference": "661f34d0bd3f1a7225ef491a70a020ad23a057a1",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "ext-xmlwriter": "*",
+                "php": "^7.0",
+                "phpunit/php-file-iterator": "^1.4.2",
+                "phpunit/php-text-template": "^1.2.1",
+                "phpunit/php-token-stream": "^2.0.1",
+                "sebastian/code-unit-reverse-lookup": "^1.0.1",
+                "sebastian/environment": "^3.0",
+                "sebastian/version": "^2.0.1",
+                "theseer/tokenizer": "^1.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^6.0"
+            },
+            "suggest": {
+                "ext-xdebug": "^2.5.5"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "5.3.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
+            "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+            "keywords": [
+                "coverage",
+                "testing",
+                "xunit"
+            ],
+            "time": "2017-12-06T09:29:45+00:00"
+        },
+        {
+            "name": "phpunit/php-file-iterator",
+            "version": "1.4.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+                "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4",
+                "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.4.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sb@sebastian-bergmann.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "FilterIterator implementation that filters files based on a list of suffixes.",
+            "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+            "keywords": [
+                "filesystem",
+                "iterator"
+            ],
+            "time": "2017-11-27T13:52:08+00:00"
+        },
+        {
+            "name": "phpunit/php-text-template",
+            "version": "1.2.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-text-template.git",
+                "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
+                "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Simple template engine.",
+            "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+            "keywords": [
+                "template"
+            ],
+            "time": "2015-06-21T13:50:34+00:00"
+        },
+        {
+            "name": "phpunit/php-timer",
+            "version": "1.0.9",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-timer.git",
+                "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
+                "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.3.3 || ^7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sb@sebastian-bergmann.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Utility class for timing",
+            "homepage": "https://github.com/sebastianbergmann/php-timer/",
+            "keywords": [
+                "timer"
+            ],
+            "time": "2017-02-26T11:10:40+00:00"
+        },
+        {
+            "name": "phpunit/php-token-stream",
+            "version": "2.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-token-stream.git",
+                "reference": "791198a2c6254db10131eecfe8c06670700904db"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db",
+                "reference": "791198a2c6254db10131eecfe8c06670700904db",
+                "shasum": ""
+            },
+            "require": {
+                "ext-tokenizer": "*",
+                "php": "^7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^6.2.4"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Wrapper around PHP's tokenizer extension.",
+            "homepage": "https://github.com/sebastianbergmann/php-token-stream/",
+            "keywords": [
+                "tokenizer"
+            ],
+            "time": "2017-11-27T05:48:46+00:00"
+        },
+        {
+            "name": "phpunit/phpunit",
+            "version": "6.5.7",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/phpunit.git",
+                "reference": "6bd77b57707c236833d2b57b968e403df060c9d9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6bd77b57707c236833d2b57b968e403df060c9d9",
+                "reference": "6bd77b57707c236833d2b57b968e403df060c9d9",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "ext-json": "*",
+                "ext-libxml": "*",
+                "ext-mbstring": "*",
+                "ext-xml": "*",
+                "myclabs/deep-copy": "^1.6.1",
+                "phar-io/manifest": "^1.0.1",
+                "phar-io/version": "^1.0",
+                "php": "^7.0",
+                "phpspec/prophecy": "^1.7",
+                "phpunit/php-code-coverage": "^5.3",
+                "phpunit/php-file-iterator": "^1.4.3",
+                "phpunit/php-text-template": "^1.2.1",
+                "phpunit/php-timer": "^1.0.9",
+                "phpunit/phpunit-mock-objects": "^5.0.5",
+                "sebastian/comparator": "^2.1",
+                "sebastian/diff": "^2.0",
+                "sebastian/environment": "^3.1",
+                "sebastian/exporter": "^3.1",
+                "sebastian/global-state": "^2.0",
+                "sebastian/object-enumerator": "^3.0.3",
+                "sebastian/resource-operations": "^1.0",
+                "sebastian/version": "^2.0.1"
+            },
+            "conflict": {
+                "phpdocumentor/reflection-docblock": "3.0.2",
+                "phpunit/dbunit": "<3.0"
+            },
+            "require-dev": {
+                "ext-pdo": "*"
+            },
+            "suggest": {
+                "ext-xdebug": "*",
+                "phpunit/php-invoker": "^1.1"
+            },
+            "bin": [
+                "phpunit"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "6.5.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "The PHP Unit Testing framework.",
+            "homepage": "https://phpunit.de/",
+            "keywords": [
+                "phpunit",
+                "testing",
+                "xunit"
+            ],
+            "time": "2018-02-26T07:01:09+00:00"
+        },
+        {
+            "name": "phpunit/phpunit-mock-objects",
+            "version": "5.0.6",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
+                "reference": "33fd41a76e746b8fa96d00b49a23dadfa8334cdf"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/33fd41a76e746b8fa96d00b49a23dadfa8334cdf",
+                "reference": "33fd41a76e746b8fa96d00b49a23dadfa8334cdf",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/instantiator": "^1.0.5",
+                "php": "^7.0",
+                "phpunit/php-text-template": "^1.2.1",
+                "sebastian/exporter": "^3.1"
+            },
+            "conflict": {
+                "phpunit/phpunit": "<6.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^6.5"
+            },
+            "suggest": {
+                "ext-soap": "*"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "5.0.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Mock Object library for PHPUnit",
+            "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/",
+            "keywords": [
+                "mock",
+                "xunit"
+            ],
+            "abandoned": true,
+            "time": "2018-01-06T05:45:45+00:00"
+        },
+        {
+            "name": "proficlos/vestacp-api",
+            "version": "v0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ProfiCloS/vestacp-api.git",
+                "reference": "e710b015556d9d78b2acc234e8b64b776bc44a64"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ProfiCloS/vestacp-api/zipball/e710b015556d9d78b2acc234e8b64b776bc44a64",
+                "reference": "e710b015556d9d78b2acc234e8b64b776bc44a64",
+                "shasum": ""
+            },
+            "require": {
+                "guzzlehttp/guzzle": "^6.3@dev",
+                "nette/utils": "^3.0@dev",
+                "php": ">=7.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "7.5.x-dev",
+                "roave/security-advisories": "dev-master",
+                "tracy/tracy": "^3.0@dev"
+            },
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PROFI CLOUD SERVICES",
+                    "email": "info@profiservices.cloud"
+                }
+            ],
+            "description": "VestaCP PHP API",
+            "homepage": "https://github.com/ProfiCloS/vestacp-api",
+            "time": "2019-02-23T01:20:08+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"
+            ],
+            "time": "2016-08-06T14:39:51+00:00"
+        },
+        {
+            "name": "qingbing/php-abstracts-component",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/qingbing/php-abstracts-component.git",
+                "reference": "d3bc81875d8451a217df2771810422658956ef70"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/qingbing/php-abstracts-component/zipball/d3bc81875d8451a217df2771810422658956ef70",
+                "reference": "d3bc81875d8451a217df2771810422658956ef70",
+                "shasum": ""
+            },
+            "require": {
+                "qingbing/php-config": ">=1.0, <2.0",
+                "qingbing/php-helper": ">=1.0, <2.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Abstracts\\": "src/abstracts"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "qingbing",
+                    "email": "780042175@qq.com"
+                }
+            ],
+            "description": "pf框架-abstracts:组件 的基类,提供组件实例化过程中的基础支持",
+            "homepage": "http://www.phpcorner.net",
+            "keywords": [
+                "abstract",
+                "php"
+            ],
+            "time": "2018-12-13T05:34:02+00:00"
+        },
+        {
+            "name": "qingbing/php-abstracts-store",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/qingbing/php-abstracts-store.git",
+                "reference": "be017441a5fc639fbe9e9398095837525a9f98cd"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/qingbing/php-abstracts-store/zipball/be017441a5fc639fbe9e9398095837525a9f98cd",
+                "reference": "be017441a5fc639fbe9e9398095837525a9f98cd",
+                "shasum": ""
+            },
+            "require": {
+                "qingbing/php-abstracts-component": ">=1.0, <2.0",
+                "qingbing/php-helper": ">=1.0, <2.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Abstracts\\": "src/abstracts"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "qingbing",
+                    "email": "780042175@qq.com"
+                }
+            ],
+            "description": "store 存储组件的抽象",
+            "homepage": "http://www.phpcorner.net",
+            "keywords": [
+                "abstracts",
+                "cache",
+                "cookie",
+                "php",
+                "session",
+                "store"
+            ],
+            "time": "2018-12-08T16:40:44+00:00"
+        },
+        {
+            "name": "qingbing/php-config",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/qingbing/php-config.git",
+                "reference": "16590f38bbf636ccd940113bf2daf24da0fcbfaa"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/qingbing/php-config/zipball/16590f38bbf636ccd940113bf2daf24da0fcbfaa",
+                "reference": "16590f38bbf636ccd940113bf2daf24da0fcbfaa",
+                "shasum": ""
+            },
+            "require": {
+                "qingbing/php-helper": ">=1.0, <2.0"
+            },
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "src/Config.php"
+                ],
+                "psr-4": {
+                    "Config\\": "src/lib"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "qingbing",
+                    "email": "780042175@qq.com"
+                }
+            ],
+            "description": "config 配置的设置和获取.",
+            "homepage": "http://www.phpcorner.net",
+            "keywords": [
+                "config",
+                "php"
+            ],
+            "time": "2018-12-07T03:16:45+00:00"
+        },
+        {
+            "name": "qingbing/php-file-cache",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/qingbing/php-file-cache.git",
+                "reference": "142c76dc453672c7d875f2fd4271f847a42bec62"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/qingbing/php-file-cache/zipball/142c76dc453672c7d875f2fd4271f847a42bec62",
+                "reference": "142c76dc453672c7d875f2fd4271f847a42bec62",
+                "shasum": ""
+            },
+            "require": {
+                "qingbing/php-abstracts-store": ">=1.0, <2.0",
+                "qingbing/php-config": ">=1.0, <2.0",
+                "qingbing/php-helper": ">=1.0, <2.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Components\\": "src/components"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "qingbing",
+                    "email": "780042175@qq.com"
+                }
+            ],
+            "description": "组件'file-cache' : 文件缓存的相关解析和操作",
+            "homepage": "http://www.phpcorner.net",
+            "keywords": [
+                "cache",
+                "caching",
+                "file-cache",
+                "file-caching",
+                "filecache",
+                "php"
+            ],
+            "time": "2019-05-18T01:38:20+00:00"
+        },
+        {
+            "name": "qingbing/php-helper",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/qingbing/php-helper.git",
+                "reference": "ed25ef23f99de7f0dad6b402295bd417cc072d45"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/qingbing/php-helper/zipball/ed25ef23f99de7f0dad6b402295bd417cc072d45",
+                "reference": "ed25ef23f99de7f0dad6b402295bd417cc072d45",
+                "shasum": ""
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "src/functions.php"
+                ],
+                "psr-4": {
+                    "Abstracts\\": "src/abstracts",
+                    "Helper\\": "src/lib"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "qingbing",
+                    "email": "780042175@qq.com"
+                }
+            ],
+            "description": "Some common helpers and functions.",
+            "homepage": "http://www.phpcorner.net",
+            "keywords": [
+                "function",
+                "helper",
+                "php"
+            ],
+            "time": "2019-05-28T15:52:40+00:00"
+        },
+        {
+            "name": "ralouphie/getallheaders",
+            "version": "2.0.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ralouphie/getallheaders.git",
+                "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/5601c8a83fbba7ef674a7369456d12f1e0d0eafa",
+                "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~3.7.0",
+                "satooshi/php-coveralls": ">=1.0"
+            },
+            "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.",
+            "time": "2016-02-11T07:05:27+00:00"
+        },
+        {
+            "name": "sebastian/code-unit-reverse-lookup",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
+                "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
+                "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.6 || ^7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^5.7 || ^6.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Looks up which function or method a line of code belongs to",
+            "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
+            "time": "2017-03-04T06:30:41+00:00"
+        },
+        {
+            "name": "sebastian/comparator",
+            "version": "2.1.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/comparator.git",
+                "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/34369daee48eafb2651bea869b4b15d75ccc35f9",
+                "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0",
+                "sebastian/diff": "^2.0 || ^3.0",
+                "sebastian/exporter": "^3.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^6.4"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.1.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Jeff Welch",
+                    "email": "whatthejeff@gmail.com"
+                },
+                {
+                    "name": "Volker Dusch",
+                    "email": "github@wallbash.com"
+                },
+                {
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@2bepublished.at"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Provides the functionality to compare PHP values for equality",
+            "homepage": "https://github.com/sebastianbergmann/comparator",
+            "keywords": [
+                "comparator",
+                "compare",
+                "equality"
+            ],
+            "time": "2018-02-01T13:46:46+00:00"
+        },
+        {
+            "name": "sebastian/diff",
+            "version": "2.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/diff.git",
+                "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/347c1d8b49c5c3ee30c7040ea6fc446790e6bddd",
+                "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^6.2"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Kore Nordmann",
+                    "email": "mail@kore-nordmann.de"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Diff implementation",
+            "homepage": "https://github.com/sebastianbergmann/diff",
+            "keywords": [
+                "diff"
+            ],
+            "time": "2017-08-03T08:09:46+00:00"
+        },
+        {
+            "name": "sebastian/environment",
+            "version": "3.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/environment.git",
+                "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5",
+                "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^6.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.1.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Provides functionality to handle HHVM/PHP environments",
+            "homepage": "http://www.github.com/sebastianbergmann/environment",
+            "keywords": [
+                "Xdebug",
+                "environment",
+                "hhvm"
+            ],
+            "time": "2017-07-01T08:51:00+00:00"
+        },
+        {
+            "name": "sebastian/exporter",
+            "version": "3.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/exporter.git",
+                "reference": "234199f4528de6d12aaa58b612e98f7d36adb937"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937",
+                "reference": "234199f4528de6d12aaa58b612e98f7d36adb937",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0",
+                "sebastian/recursion-context": "^3.0"
+            },
+            "require-dev": {
+                "ext-mbstring": "*",
+                "phpunit/phpunit": "^6.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.1.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Jeff Welch",
+                    "email": "whatthejeff@gmail.com"
+                },
+                {
+                    "name": "Volker Dusch",
+                    "email": "github@wallbash.com"
+                },
+                {
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@2bepublished.at"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                },
+                {
+                    "name": "Adam Harvey",
+                    "email": "aharvey@php.net"
+                }
+            ],
+            "description": "Provides the functionality to export PHP variables for visualization",
+            "homepage": "http://www.github.com/sebastianbergmann/exporter",
+            "keywords": [
+                "export",
+                "exporter"
+            ],
+            "time": "2017-04-03T13:19:02+00:00"
+        },
+        {
+            "name": "sebastian/global-state",
+            "version": "2.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/global-state.git",
+                "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4",
+                "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^6.0"
+            },
+            "suggest": {
+                "ext-uopz": "*"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Snapshotting of global state",
+            "homepage": "http://www.github.com/sebastianbergmann/global-state",
+            "keywords": [
+                "global state"
+            ],
+            "time": "2017-04-27T15:39:26+00:00"
+        },
+        {
+            "name": "sebastian/object-enumerator",
+            "version": "3.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/object-enumerator.git",
+                "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5",
+                "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0",
+                "sebastian/object-reflector": "^1.1.1",
+                "sebastian/recursion-context": "^3.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^6.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.0.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Traverses array structures and object graphs to enumerate all referenced objects",
+            "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
+            "time": "2017-08-03T12:35:26+00:00"
+        },
+        {
+            "name": "sebastian/object-reflector",
+            "version": "1.1.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/object-reflector.git",
+                "reference": "773f97c67f28de00d397be301821b06708fca0be"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be",
+                "reference": "773f97c67f28de00d397be301821b06708fca0be",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^6.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Allows reflection of object attributes, including inherited and non-public ones",
+            "homepage": "https://github.com/sebastianbergmann/object-reflector/",
+            "time": "2017-03-29T09:07:27+00:00"
+        },
+        {
+            "name": "sebastian/recursion-context",
+            "version": "3.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/recursion-context.git",
+                "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8",
+                "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^6.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.0.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Jeff Welch",
+                    "email": "whatthejeff@gmail.com"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                },
+                {
+                    "name": "Adam Harvey",
+                    "email": "aharvey@php.net"
+                }
+            ],
+            "description": "Provides functionality to recursively process PHP variables",
+            "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
+            "time": "2017-03-03T06:23:57+00:00"
+        },
+        {
+            "name": "sebastian/resource-operations",
+            "version": "1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/resource-operations.git",
+                "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
+                "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.6.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Provides a list of PHP built-in functions that operate on resources",
+            "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
+            "time": "2015-07-28T20:34:47+00:00"
+        },
+        {
+            "name": "sebastian/version",
+            "version": "2.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/version.git",
+                "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019",
+                "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.6"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Library that helps with managing the version number of Git-hosted PHP projects",
+            "homepage": "https://github.com/sebastianbergmann/version",
+            "time": "2016-10-03T07:35:21+00:00"
+        },
+        {
+            "name": "symfony/finder",
+            "version": "v4.0.6",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/finder.git",
+                "reference": "44a796d2ecc2a16a5fc8f2956a34ee617934d55f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/finder/zipball/44a796d2ecc2a16a5fc8f2956a34ee617934d55f",
+                "reference": "44a796d2ecc2a16a5fc8f2956a34ee617934d55f",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.0-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Finder\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Finder Component",
+            "homepage": "https://symfony.com",
+            "time": "2018-03-05T18:28:26+00:00"
+        },
+        {
+            "name": "theseer/tokenizer",
+            "version": "1.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/theseer/tokenizer.git",
+                "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/theseer/tokenizer/zipball/cb2f008f3f05af2893a87208fe6a6c4985483f8b",
+                "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "ext-tokenizer": "*",
+                "ext-xmlwriter": "*",
+                "php": "^7.0"
+            },
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Arne Blankerts",
+                    "email": "arne@blankerts.de",
+                    "role": "Developer"
+                }
+            ],
+            "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
+            "time": "2017-04-07T12:08:54+00:00"
+        },
+        {
+            "name": "webmozart/assert",
+            "version": "1.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/webmozart/assert.git",
+                "reference": "0df1908962e7a3071564e857d86874dad1ef204a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a",
+                "reference": "0df1908962e7a3071564e857d86874dad1ef204a",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.3.3 || ^7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.6",
+                "sebastian/version": "^1.0.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.3-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Webmozart\\Assert\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@gmail.com"
+                }
+            ],
+            "description": "Assertions to validate method input/output with nice error messages.",
+            "keywords": [
+                "assert",
+                "check",
+                "validate"
+            ],
+            "time": "2018-01-29T19:49:41+00:00"
+        },
+        {
+            "name": "zircote/swagger-php",
+            "version": "2.0.13",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/zircote/swagger-php.git",
+                "reference": "8b42fdc3d8c5a5e0d1f8d344aa359822c9f085e0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/zircote/swagger-php/zipball/8b42fdc3d8c5a5e0d1f8d344aa359822c9f085e0",
+                "reference": "8b42fdc3d8c5a5e0d1f8d344aa359822c9f085e0",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/annotations": "*",
+                "php": ">=5.6",
+                "symfony/finder": ">=2.2"
+            },
+            "require-dev": {
+                "phpunit/phpunit": ">=4.8.35 <=5.6",
+                "squizlabs/php_codesniffer": ">=2.7",
+                "zendframework/zend-form": "<2.8"
+            },
+            "bin": [
+                "bin/swagger"
+            ],
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Swagger\\": "src"
+                },
+                "files": [
+                    "src/functions.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache2"
+            ],
+            "authors": [
+                {
+                    "name": "Robert Allen",
+                    "email": "zircote@gmail.com",
+                    "homepage": "http://www.zircote.com"
+                },
+                {
+                    "name": "Bob Fanger",
+                    "email": "bfanger@gmail.com",
+                    "homepage": "http://bfanger.nl"
+                }
+            ],
+            "description": "Swagger-PHP - Generate interactive documentation for your RESTful API using phpdoc annotations",
+            "homepage": "https://github.com/zircote/swagger-php/",
+            "keywords": [
+                "api",
+                "json",
+                "rest",
+                "service discovery"
+            ],
+            "time": "2017-12-01T09:22:05+00:00"
+        }
+    ],
+    "packages-dev": [],
+    "aliases": [],
+    "minimum-stability": "stable",
+    "stability-flags": [],
+    "prefer-stable": false,
+    "prefer-lowest": false,
+    "platform": [],
+    "platform-dev": []
+}


+ 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');

Неке датотеке нису приказане због велике количине промена