Zhu Jinhui 7 жил өмнө
parent
commit
0a31b33423
100 өөрчлөгдсөн 14069 нэмэгдсэн , 1 устгасан
  1. 3 0
      .idea/copyright/profiles_settings.xml
  2. 8 0
      .idea/modules.xml
  3. 8 0
      .idea/psr2.iml
  4. 555 0
      .idea/workspace.xml
  5. 275 1
      README.md
  6. 302 0
      _cli.php
  7. 12 0
      demo/private/Bootstrap.php
  8. 108 0
      demo/private/configure/app.ini
  9. 25 0
      demo/private/configure/db.ini
  10. 5 0
      demo/private/configure/router.config.php
  11. 36 0
      demo/private/controller/index.php
  12. 10 0
      demo/private/controller/test.php
  13. 1 0
      demo/private/controller/view/index.tpl
  14. 39 0
      demo/private/plugins/loger.php
  15. 1 0
      demo/private/view/index.tpl
  16. 13 0
      demo/public/.htaccess
  17. 13 0
      demo/public/index.php
  18. 394 0
      src/Application.php
  19. 49 0
      src/Autoloader/Factory.php
  20. 67 0
      src/Autoloader/Helper.php
  21. 175 0
      src/Autoloader/Import.php
  22. 71 0
      src/Autoloader/Instance.php
  23. 67 0
      src/Autoloader/Loader.php
  24. 413 0
      src/Autoloader/Psr4.php
  25. 16 0
      src/Base/Action.php
  26. 10 0
      src/Base/Bootstrap.php
  27. 296 0
      src/Base/Controller.php
  28. 91 0
      src/Base/Dispatcher.php
  29. 623 0
      src/Base/Request.php
  30. 306 0
      src/Base/Response.php
  31. 343 0
      src/Base/Rules.php
  32. 159 0
      src/Cache/File.php
  33. 21 0
      src/Cache/Intf.php
  34. 44 0
      src/Cache/Loader.php
  35. 153 0
      src/Cache/Memcached.php
  36. 109 0
      src/Cache/Redis.php
  37. 1372 0
      src/Cache/Redis/Client.php
  38. 340 0
      src/Cache/Redis/Cluster.php
  39. 346 0
      src/Cache/Redis/Sentinel.php
  40. 81 0
      src/Cache/XCache.php
  41. 207 0
      src/Config/Arrays.php
  42. 37 0
      src/Config/Consts.php
  43. 339 0
      src/Config/Register.php
  44. 94 0
      src/Config/Setting.php
  45. 901 0
      src/Driver/Base.php
  46. 63 0
      src/Driver/ConnBase.php
  47. 30 0
      src/Driver/ConnIntf.php
  48. 514 0
      src/Driver/Easy.php
  49. 92 0
      src/Driver/Fields.php
  50. 45 0
      src/Driver/Intf.php
  51. 234 0
      src/Driver/Model.php
  52. 54 0
      src/Driver/Mysql/Connection.php
  53. 264 0
      src/Driver/Mysql/Driver.php
  54. 57 0
      src/Driver/Mysqli/Connection.php
  55. 275 0
      src/Driver/Mysqli/Driver.php
  56. 90 0
      src/Driver/Observer.php
  57. 74 0
      src/Driver/Pdo/Connection.php
  58. 317 0
      src/Driver/Pdo/Driver.php
  59. 332 0
      src/Driver/Response.php
  60. 232 0
      src/Driver/Rules.php
  61. 17 0
      src/Exceptions/AccessDenied.php
  62. 17 0
      src/Exceptions/Cache.php
  63. 17 0
      src/Exceptions/CallUndefinedClass.php
  64. 17 0
      src/Exceptions/ClassInstanceof.php
  65. 17 0
      src/Exceptions/ClassNotFound.php
  66. 147 0
      src/Exceptions/Error.php
  67. 169 0
      src/Exceptions/Errors.php
  68. 17 0
      src/Exceptions/FileNotFound.php
  69. 17 0
      src/Exceptions/FolderDoesNotExist.php
  70. 17 0
      src/Exceptions/InvalidFormat.php
  71. 16 0
      src/Exceptions/MethodNotFound.php
  72. 17 0
      src/Exceptions/NotAllowed.php
  73. 17 0
      src/Exceptions/Overwrite.php
  74. 17 0
      src/Exceptions/Response.php
  75. 17 0
      src/Exceptions/TableException.php
  76. 17 0
      src/Exceptions/Unsupport.php
  77. 17 0
      src/Exceptions/Variable.php
  78. 1 0
      src/Exceptions/View/404.php
  79. 31 0
      src/Exceptions/View/error.php
  80. 372 0
      src/Exceptions/View/example.php
  81. 0 0
      src/Exceptions/View/footer.php
  82. 30 0
      src/Exceptions/View/message.php
  83. 67 0
      src/Exceptions/View/style.php
  84. 122 0
      src/Functions/Funcs.php
  85. 136 0
      src/Language/Loader.php
  86. 101 0
      src/Language/i18n/CN/error.php
  87. 17 0
      src/Language/i18n/CN/exception.php
  88. 15 0
      src/Language/i18n/CN/resource.php
  89. 99 0
      src/Language/i18n/EN/error.php
  90. 17 0
      src/Language/i18n/EN/exception.php
  91. 15 0
      src/Language/i18n/EN/resource.php
  92. 2 0
      src/Language/i18n/language.php
  93. 158 0
      src/Library/Arrays.php
  94. 61 0
      src/Library/Cookie.php
  95. 131 0
      src/Library/Crypt.php
  96. 191 0
      src/Library/Device.php
  97. 98 0
      src/Library/Download.php
  98. 378 0
      src/Library/Flexihash.php
  99. 29 0
      src/Library/Http.php
  100. 217 0
      src/Library/IP.php

+ 3 - 0
.idea/copyright/profiles_settings.xml

@@ -0,0 +1,3 @@
+<component name="CopyrightManager">
+  <settings default="" />
+</component>

+ 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/psr2.iml" filepath="$PROJECT_DIR$/.idea/psr2.iml" />
+    </modules>
+  </component>
+</project>

+ 8 - 0
.idea/psr2.iml

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

+ 555 - 0
.idea/workspace.xml

@@ -0,0 +1,555 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ChangeListManager">
+    <list default="true" id="8980901e-c655-4565-aa2f-fdeae75ef973" name="Default" comment="" />
+    <ignored path="psr2.iws" />
+    <ignored path=".idea/workspace.xml" />
+    <ignored path=".idea/dataSources.local.xml" />
+    <option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
+    <option name="TRACKING_ENABLED" 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="CreatePatchCommitExecutor">
+    <option name="PATCH_PATH" value="" />
+  </component>
+  <component name="ExecutionTargetManager" SELECTED_TARGET="default_target" />
+  <component name="FavoritesManager">
+    <favorites_list name="psr2" />
+  </component>
+  <component name="FileEditorManager">
+    <leaf>
+      <file leaf-file-name="Application.php" pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/Qii/Application.php">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="272">
+              <caret line="79" column="0" selection-start-line="79" selection-start-column="0" selection-end-line="79" selection-end-column="0" />
+              <folding />
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file leaf-file-name="Factory.php" pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/Qii/Autoloader/Factory.php">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="221">
+              <caret line="13" column="22" selection-start-line="13" selection-start-column="20" selection-end-line="13" selection-end-column="22" />
+              <folding />
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file leaf-file-name="Psr.php" pinned="false" current-in-tab="true">
+        <entry file="file://$PROJECT_DIR$/Qii/Autoloader/Psr.php">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="946">
+              <caret line="392" column="7" selection-start-line="392" selection-start-column="7" selection-end-line="392" selection-end-column="7" />
+              <folding />
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file leaf-file-name="Import.php" pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/Qii/Autoloader/Import.php">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="0">
+              <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
+              <folding />
+            </state>
+          </provider>
+        </entry>
+      </file>
+    </leaf>
+  </component>
+  <component name="IdeDocumentHistory">
+    <option name="CHANGED_PATHS">
+      <list>
+        <option value="$PROJECT_DIR$/Qii/Functions/Funcs.php" />
+        <option value="$PROJECT_DIR$/Qii/Autoloader/Factory.php" />
+        <option value="$PROJECT_DIR$/Qii/Autoloader/Autoloader.php" />
+        <option value="$PROJECT_DIR$/Qii/Config/Abstract.php" />
+        <option value="$PROJECT_DIR$/Qii/Config/Setting.php" />
+        <option value="$PROJECT_DIR$/Qii/Exceptions/AccessDenied.php" />
+        <option value="$PROJECT_DIR$/Qii/Exceptions/CacheException.php" />
+        <option value="$PROJECT_DIR$/Qii/Exceptions/CallUndefinedClass.php" />
+        <option value="$PROJECT_DIR$/Qii/Exceptions/ClassInstanceof.php" />
+        <option value="$PROJECT_DIR$/Qii/Exceptions/ClassNotFound.php" />
+        <option value="$PROJECT_DIR$/Qii/Exceptions/Error.php" />
+        <option value="$PROJECT_DIR$/Qii/Exceptions/Errors.php" />
+        <option value="$PROJECT_DIR$/Qii/Exceptions/FileNotFound.php" />
+        <option value="$PROJECT_DIR$/Qii/Exceptions/FolderDoesNotExist.php" />
+        <option value="$PROJECT_DIR$/Qii/Exceptions/InvalidFormat.php" />
+        <option value="$PROJECT_DIR$/Qii/Exceptions/MethodNotFound.php" />
+        <option value="$PROJECT_DIR$/Qii/Exceptions/NotAllowed.php" />
+        <option value="$PROJECT_DIR$/Qii/Exceptions/Overwrite.php" />
+        <option value="$PROJECT_DIR$/Qii/Exceptions/Response.php" />
+        <option value="$PROJECT_DIR$/Qii/Exceptions/TableException.php" />
+        <option value="$PROJECT_DIR$/Qii/Exceptions/Unsupport.php" />
+        <option value="$PROJECT_DIR$/Qii/Exceptions/Variable.php" />
+        <option value="$PROJECT_DIR$/Qii/Exceptions/Cache.php" />
+        <option value="$PROJECT_DIR$/Qii/Application.php" />
+        <option value="$PROJECT_DIR$/Qii/Const/Config.php" />
+        <option value="$PROJECT_DIR$/Qii/Config/Config.php" />
+        <option value="$PROJECT_DIR$/Qii/Consts/Config.php" />
+        <option value="$PROJECT_DIR$/Qii/Config/Arrays.php" />
+        <option value="$PROJECT_DIR$/Qii/Config/Register.php" />
+        <option value="$PROJECT_DIR$/Qii/Autoloader/Psr.php" />
+      </list>
+    </option>
+  </component>
+  <component name="JsBuildToolGruntFileManager" detection-done="true" sorting="DEFINITION_ORDER" />
+  <component name="JsBuildToolPackageJson" detection-done="true" sorting="DEFINITION_ORDER" />
+  <component name="JsGulpfileManager">
+    <detection-done>true</detection-done>
+    <sorting>DEFINITION_ORDER</sorting>
+  </component>
+  <component name="PhpWorkspaceProjectConfiguration" backward_compatibility_performed="true" />
+  <component name="ProjectFrameBounds">
+    <option name="x" value="-9" />
+    <option name="y" value="-3" />
+    <option name="width" value="1216" />
+    <option name="height" value="734" />
+  </component>
+  <component name="ProjectLevelVcsManager" settingsEditedManually="false">
+    <OptionsSetting value="true" id="Add" />
+    <OptionsSetting value="true" id="Remove" />
+    <OptionsSetting value="true" id="Checkout" />
+    <OptionsSetting value="true" id="Update" />
+    <OptionsSetting value="true" id="Status" />
+    <OptionsSetting value="true" id="Edit" />
+    <ConfirmationsSetting value="0" id="Add" />
+    <ConfirmationsSetting value="0" id="Remove" />
+  </component>
+  <component name="ProjectView">
+    <navigator currentView="ProjectPane" proportions="" version="1">
+      <flattenPackages />
+      <showMembers />
+      <showModules />
+      <showLibraryContents />
+      <hideEmptyPackages />
+      <abbreviatePackageNames />
+      <autoscrollToSource />
+      <autoscrollFromSource />
+      <sortByType />
+      <manualOrder />
+      <foldersAlwaysOnTop value="true" />
+    </navigator>
+    <panes>
+      <pane id="Scratches" />
+      <pane id="ProjectPane">
+        <subPane>
+          <PATH>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="psr2" />
+              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
+            </PATH_ELEMENT>
+          </PATH>
+          <PATH>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="psr2" />
+              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
+            </PATH_ELEMENT>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="psr2" />
+              <option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
+            </PATH_ELEMENT>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="Qii" />
+              <option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
+            </PATH_ELEMENT>
+          </PATH>
+          <PATH>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="psr2" />
+              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
+            </PATH_ELEMENT>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="psr2" />
+              <option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
+            </PATH_ELEMENT>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="public" />
+              <option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
+            </PATH_ELEMENT>
+          </PATH>
+          <PATH>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="psr2" />
+              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
+            </PATH_ELEMENT>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="psr2" />
+              <option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
+            </PATH_ELEMENT>
+          </PATH>
+        </subPane>
+      </pane>
+      <pane id="Scope" />
+    </panes>
+  </component>
+  <component name="PropertiesComponent">
+    <property name="WebServerToolWindowFactoryState" value="false" />
+    <property name="js-jscs-nodeInterpreter" value="D:\Program Files\nodejs\node.exe" />
+  </component>
+  <component name="RecentsManager">
+    <key name="MoveFile.RECENT_KEYS">
+      <recent name="E:\WebRoot\psr2\Qii\Consts" />
+      <recent name="E:\WebRoot\psr2\Qii\Config" />
+    </key>
+  </component>
+  <component name="RunManager">
+    <configuration default="true" type="GoApplicationRunConfiguration" factoryName="Go Application">
+      <module name="" />
+      <working_directory value="$PROJECT_DIR$" />
+      <filePath value="$PROJECT_DIR$" />
+      <kind value="FILE" />
+      <method />
+    </configuration>
+    <configuration default="true" type="GoRunFileConfiguration" factoryName="Go Single File">
+      <module name="" />
+      <working_directory value="$PROJECT_DIR$" />
+      <filePath value="$PROJECT_DIR$" />
+      <method />
+    </configuration>
+    <configuration default="true" type="GoTestRunConfiguration" factoryName="Go Test">
+      <module name="" />
+      <working_directory value="$PROJECT_DIR$" />
+      <framework value="gotest" />
+      <kind value="DIRECTORY" />
+      <method />
+    </configuration>
+    <configuration default="true" type="JavascriptDebugType" factoryName="JavaScript Debug">
+      <method />
+    </configuration>
+    <configuration default="true" type="NodeJSConfigurationType" factoryName="Node.js" path-to-node="project" working-dir="">
+      <method />
+    </configuration>
+    <configuration default="true" type="PHPUnitRunConfigurationType" factoryName="PHPUnit">
+      <TestRunner />
+      <method />
+    </configuration>
+    <configuration default="true" type="PhpBehatConfigurationType" factoryName="Behat">
+      <BehatRunner />
+      <method />
+    </configuration>
+    <configuration default="true" type="PhpLocalRunConfigurationType" factoryName="PHP Console">
+      <method />
+    </configuration>
+    <configuration default="true" type="js.build_tools.gulp" factoryName="Gulp.js">
+      <method />
+    </configuration>
+    <configuration default="true" type="js.build_tools.npm" factoryName="npm">
+      <command value="run-script" />
+      <scripts />
+      <node-interpreter value="project" />
+      <envs />
+      <method />
+    </configuration>
+    <configuration default="true" type="mocha-javascript-test-runner" factoryName="Mocha">
+      <node-interpreter>project</node-interpreter>
+      <node-options />
+      <working-directory />
+      <pass-parent-env>true</pass-parent-env>
+      <envs />
+      <ui />
+      <extra-mocha-options />
+      <test-kind>DIRECTORY</test-kind>
+      <test-directory />
+      <recursive>false</recursive>
+      <method />
+    </configuration>
+  </component>
+  <component name="ShelveChangesManager" show_recycled="false">
+    <option name="remove_strategy" value="false" />
+  </component>
+  <component name="TaskManager">
+    <task active="true" id="Default" summary="Default task">
+      <changelist id="8980901e-c655-4565-aa2f-fdeae75ef973" name="Default" comment="" />
+      <created>1498461896023</created>
+      <option name="number" value="Default" />
+      <option name="presentableId" value="Default" />
+      <updated>1498461896023</updated>
+      <workItem from="1498461897322" duration="6167000" />
+    </task>
+    <servers />
+  </component>
+  <component name="TimeTrackingManager">
+    <option name="totallyTimeSpent" value="6167000" />
+  </component>
+  <component name="ToolWindowManager">
+    <frame x="-9" y="-3" width="1216" height="734" extended-state="1" />
+    <editor active="true" />
+    <layout>
+      <window_info id="Project" active="true" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.2855903" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" />
+      <window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
+      <window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="true" content_ui="tabs" />
+      <window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
+      <window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
+      <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
+      <window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
+      <window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="true" content_ui="tabs" />
+      <window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
+      <window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="combo" />
+      <window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
+      <window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
+      <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
+      <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
+      <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
+      <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
+      <window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
+    </layout>
+  </component>
+  <component name="Vcs.Log.UiProperties">
+    <option name="RECENTLY_FILTERED_USER_GROUPS">
+      <collection />
+    </option>
+    <option name="RECENTLY_FILTERED_BRANCH_GROUPS">
+      <collection />
+    </option>
+  </component>
+  <component name="VcsContentAnnotationSettings">
+    <option name="myLimit" value="2678400000" />
+  </component>
+  <component name="XDebuggerManager">
+    <breakpoint-manager />
+    <watches-manager />
+  </component>
+  <component name="editorHistoryManager">
+    <entry file="file://$PROJECT_DIR$/public/index.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="17">
+          <caret line="1" column="12" selection-start-line="1" selection-start-column="12" selection-end-line="1" selection-end-column="12" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Qii/Functions/Funcs.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="68">
+          <caret line="4" column="44" selection-start-line="4" selection-start-column="44" selection-end-line="4" selection-end-column="44" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Qii/Config/Abstract.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="34">
+          <caret line="2" column="15" selection-start-line="2" selection-start-column="15" selection-end-line="2" selection-end-column="15" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Qii/Config/Setting.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="357">
+          <caret line="21" column="39" selection-start-line="21" selection-start-column="39" selection-end-line="21" selection-end-column="39" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Qii/Exceptions/ClassInstanceof.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="51">
+          <caret line="3" column="30" selection-start-line="3" selection-start-column="30" selection-end-line="3" selection-end-column="30" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Qii/Exceptions/ClassNotFound.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="51">
+          <caret line="3" column="28" selection-start-line="3" selection-start-column="28" selection-end-line="3" selection-end-column="28" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Qii/Exceptions/Error.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="34">
+          <caret line="2" column="0" selection-start-line="2" selection-start-column="0" selection-end-line="2" selection-end-column="0" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Qii/Exceptions/Errors.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="51">
+          <caret line="3" column="6" selection-start-line="3" selection-start-column="6" selection-end-line="3" selection-end-column="6" />
+          <folding>
+            <marker date="1498465002838" expanded="true" signature="4154:4171" ph="..." />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Qii/Exceptions/FileNotFound.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="51">
+          <caret line="3" column="27" selection-start-line="3" selection-start-column="27" selection-end-line="3" selection-end-column="27" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Qii/Exceptions/FolderDoesNotExist.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="34">
+          <caret line="2" column="0" selection-start-line="2" selection-start-column="0" selection-end-line="2" selection-end-column="0" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Qii/Exceptions/InvalidFormat.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="51">
+          <caret line="3" column="28" selection-start-line="3" selection-start-column="28" selection-end-line="3" selection-end-column="28" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Qii/Exceptions/MethodNotFound.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="51">
+          <caret line="3" column="29" selection-start-line="3" selection-start-column="29" selection-end-line="3" selection-end-column="29" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Qii/Exceptions/NotAllowed.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="51">
+          <caret line="3" column="25" selection-start-line="3" selection-start-column="25" selection-end-line="3" selection-end-column="25" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Qii/Exceptions/Overwrite.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="17">
+          <caret line="1" column="26" selection-start-line="1" selection-start-column="26" selection-end-line="1" selection-end-column="26" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Qii/Exceptions/Response.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="51">
+          <caret line="3" column="23" selection-start-line="3" selection-start-column="23" selection-end-line="3" selection-end-column="23" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Qii/Exceptions/TableException.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="51">
+          <caret line="3" column="29" selection-start-line="3" selection-start-column="29" selection-end-line="3" selection-end-column="29" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Qii/Exceptions/Variable.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="85">
+          <caret line="5" column="26" selection-start-line="5" selection-start-column="26" selection-end-line="5" selection-end-column="26" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Qii/Exceptions/Unsupport.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="238">
+          <caret line="14" column="0" selection-start-line="14" selection-start-column="0" selection-end-line="14" selection-end-column="0" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="jar://$APPLICATION_HOME_DIR$/plugins/php/lib/php.jar!/com/jetbrains/php/lang/psi/stubs/data/standard_defines.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="192">
+          <caret line="724" column="9" selection-start-line="724" selection-start-column="9" selection-end-line="724" selection-end-column="9" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Qii/Autoloader/Factory.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="221">
+          <caret line="13" column="22" selection-start-line="13" selection-start-column="20" selection-end-line="13" selection-end-column="22" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Qii/Consts/Config.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="187">
+          <caret line="11" column="43" selection-start-line="11" selection-start-column="43" selection-end-line="11" selection-end-column="43" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Qii/Exceptions/AccessDenied.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="272">
+          <caret line="16" column="1" selection-start-line="16" selection-start-column="1" selection-end-line="16" selection-end-column="1" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Qii/Autoloader/Import.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="0">
+          <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Qii/Config/Arrays.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="-314">
+          <caret line="26" column="14" selection-start-line="26" selection-start-column="14" selection-end-line="26" selection-end-column="14" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Qii/Config/Register.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="1658">
+          <caret line="324" column="20" selection-start-line="324" selection-start-column="20" selection-end-line="324" selection-end-column="20" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Qii/Exceptions/CallUndefinedClass.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="68">
+          <caret line="4" column="1" selection-start-line="4" selection-start-column="1" selection-end-line="4" selection-end-column="1" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Qii/Exceptions/Cache.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="153">
+          <caret line="9" column="6" selection-start-line="9" selection-start-column="6" selection-end-line="9" selection-end-column="6" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Qii/Application.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="272">
+          <caret line="79" column="0" selection-start-line="79" selection-start-column="0" selection-end-line="79" selection-end-column="0" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/Qii/Autoloader/Psr.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="946">
+          <caret line="392" column="7" selection-start-line="392" selection-start-column="7" selection-end-line="392" selection-end-column="7" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+  </component>
+</project>

+ 275 - 1
README.md

@@ -1,3 +1,277 @@
 # Qii
 
-新版Qii
+<<<<<<< HEAD
+新版Qii
+=======
+使用方法:
+
+1、创建项目
+   通过命令行进入当前目录,并执行:php -q _cli.php create=yes workspace=../project cache=tmp useDB=1
+	Command line usage:
+	>php -q _cli.php create=yes workspace=../project cache=tmp useDB=1
+	* create: is auto create default:yes;
+	* workspace: workspace
+	* cache : cache dir
+	* useDB : use db or not 
+	程序将自动创建工作目录,并生成首页及配置相关文件。设置好Web网站目录,开启.htaccess即可直接访问。
+	相关的配置文件见 configure/app.ini及configure/db.ini文件
+
+2、框架的使用
+	1) 命令行运行程序
+		仅支持GET方法
+		1. normal模式
+		php -q index.php control=index/action=home/id=100
+		php -q index.php control=plugins/action=index/page=2
+
+		2. middle模式
+		php -q index.php control/index/action/home/id/100
+		php -q index.php control/plugins/action/index/page/2
+
+		3. short模式
+		php -q index.php index/home/100
+		php -q index.php plugins/page/2.html
+	2) 自动加载类
+		new Controller\User(); === require("Controller<?=DS?>User.php"); new Controller\User();
+		new Model\User(); === require("Model<?=DS?>User.php"); new Model\User();
+		new Model\Test\User(); === require("Model<?=DS?>Test<?=DS?>User.php"); new Model\Test\User();
+
+	3) Qii 基本功能:
+		Qii::Instance(className, param1, param2, param3[,...]); === ($class = new className(param1, param2, param3[,...]));
+		Qii::load(className)->method(); === $class->method();
+		Qii::import(fileName); 如果指定的文件无法找到则会在get_include_path()和站点配置文件的[path]的目录中去搜索,直到搜索到一个则停止。
+		Qii::includes(fileName); == include(fileName);
+		Qii::setPrivate($key, $value);保存到私有变量 $_global[$key]中, $value可以为数组,如果是数组的话会将数组合并到已经存在$key的数组中去。
+		Qii::getPrivate($key, $index);获取$_global[$key][$index]的值
+		Qii::setError($condition, $code, $argvs); 检查$condition是否成立,成立就没有错,返回false,否则有错,返回true并将错误信息,详细代码错误$code详情见<?php echo Qii::getPrivate('qii_sys_language');?>。
+	4) 多域名支持:
+		开启多域名支持,不同域名可以访问不同目录中的controller,在app.ini中的common下添加以下内容,注意:hosts中的内容会覆盖网站中对应的配置
+        hosts[0.domain] = test.xxx.wang
+        hosts[0.ext] = test
+
+        hosts[1.domain] = admin.xxx.wang
+        hosts[1.ext] = admin
+	5) Module用法示例:
+		第一步,创建一个user的model
+		class user_model extends Model
+		{
+			public function __construct()
+			{
+				parent::__construct();
+			}
+			public function userInfo($uid)
+			{
+				return $this->getRow("SELECT * FROM user WHERE uid = '{$uid}'");
+			}
+		}
+		第二步,使用user_model
+		class user_controler extends \Qii\Controller\Abstract
+		{
+			public function __construct()
+			{
+					$this->Qii('Model');
+					$userClass = new user_module();
+					$userInfo = $userClass->userInfo(10);
+
+					或
+					$this->Qii("user_module")->userInfo(10);
+					或
+					$this->Qii("user_module");
+					$this->user_module->userInfo(10);
+			}
+		}
+	6) ORM的使用示例:
+		第一步,创建表对应的ORM模型
+		/**
+		 * User ORM 模型
+		 * @author Zhu Jinhui 2015-02-13 11:26
+		 *
+		 */
+		class User extends Tables
+		{
+			public function getTableName()//返回User对应的数据表
+			{
+				return 'istudy_user';
+			}
+			public function getRelationMap()//返回alias对应的字段名
+			{
+				return array('id' => 'uid', 'email' => 'email', 'nick' => 'nickname');
+			}
+			public function getValidateSaveFields()//保存数据需要验证的字段
+			{
+				return array('email', 'password');
+			}
+			public function getValidateRules()//验证规则
+			{
+				return array('email' => array('email' => true), 'password' => array('password' => true, 'length' => 32));
+			}
+			public function getInvalidMessage()//验证不通过返回的消息内容
+			{
+				return array();
+			}
+		}
+		第二步,创建User Model:
+		class user_model extends Model
+		{
+		    public function __construct()
+		    {
+		        parent::__construct();
+		    }
+		    /**
+		     * 注册
+		     * @param String $email
+		     * @param String $password
+		     * @return Array
+		     */
+		    public function register($email, $password)
+		    {
+		    	$data = array();
+		    	if(!$email || !$password)
+		    	{
+		    	    $data['code'] = 1;
+		    	    $data['error'] = array('result' => '参数不正确');
+		    		return $data;
+		    	}
+		    	$user = new User();
+		    	$user->email = $email;
+		    	$user->password = md5($password);
+		    	$user->active_code = substr(md5(uniqid(rand(), TRUE)), -6);
+		    	$user->add_time = time();
+		    	$user->update_time = time();
+		    	
+		    	$user->setPrivateKey('email');
+		    	$isExists = $user->isExits();
+		    	
+		    	if($isExists)
+		    	{
+		    	    $data['code'] = 10001;
+		    	    $data['data'] = $isExists;
+		    	}
+		    	else
+		    	{
+		    	   $data['code'] = 0;
+		    	   $data['uid'] = $user->execSave();
+		    	   if($user->getTablesError())
+		    	   {
+		    	       $data['error'] = $user->getTablesError();
+		    	   }
+		    	}
+		    	return $data;
+		    }
+		    
+		    public function login($email, $password)
+		    {
+		    	$data = array();
+		    	if(!$email || !$password)
+		    	{
+		    		return $data;
+		    	}
+		    	$user = new User();
+		    	$user->email = $email;
+		    	//$user->password = md5($password);
+		    	$userInfo = $user->isExits();
+		    	if($userInfo['uid'])
+		    	{
+		    	    if($userInfo['password'] != md5($password))
+		    	    {
+		    	        $data['code'] = 1;
+		    	        $data['msg'] = '密码不正确';
+		    	    }
+		    	    else
+		    	    {
+		    	       $data['code'] = 0;
+		    	       $cookie['uid'] = $userInfo['uid'];
+		    	       $cookie['email'] = $userInfo['email'];
+					   $data['cookie'] = $cookie;
+		    	    } 
+		    	}
+		    	return $data;
+		    }
+		}
+		第三步,使用user_model:
+		
+		class index_controller extends \Qii\Controller\Abstract
+		{
+			public function __construct()
+			{
+				$user = new user_model();
+				$status = $user->login('email@test.com', '119328118');
+				if($status['code'] === 0) echo '登录成功';
+			}
+		}
+	7) View的支持
+		view支持smarty及php
+		class index_controller extends \Qii\Controller\Abstract
+		{
+			public function __construct()
+			{
+				$this->enableView();//默认是app.ini中的view[engine],你可以在此处选择是用include或者require,只要将参数默认传给enableView即可
+				$this->view->display('tpl'); //$this->view即为使用的模板引擎
+			}
+		}
+	8) Controller的使用
+	class test_controller extends \Qii\Controller\Abstract
+	{
+		//为了避免Controller逻辑过于复杂,可以将Action拆到单独的文件
+		//当在调用dummy方法的时候会自动执行actions/dummy_action.php中的execute方法
+		public $actions = array(
+				"dummy" => "actions/dummy_action.php",
+		);
+		public function __construct()
+		{
+			parent::__construct();
+		}
+	}
+	9) Cache支持
+	Controller中使用Cache
+	class cache_controller extends Controller
+	{
+		public function __construct()
+		{
+			parent::__construct();
+		}
+		public function cacheTest()
+		{
+			$cache_id = 'cache_test';
+			//文件缓存
+			$this->setCache('file', array('path' => 'tmp'));
+			//Memcache缓存
+			$this->setCache('memcache', array('servers' => array( array('host'=>'127.0.0.1','port'=>11211) ) ,'life_time'=>600));
+			//xcache
+			$this->setCache('xcache', array('life_time'=>600));
+			//缓存内容
+			$this->cache($cache_id, 'cache内容');
+			//redis缓存
+			$this->setCache('redis', array('servers' => array('127.0.0.1.6379')));
+			$this->cache($cache_id, array('cache' => 'cache 内容'));
+			//获取缓存内容
+			$this->getCache($cache_id);
+			//移除缓存
+			$this->cache->remove($cache_id);
+		}
+	}
+	
+	Model中使用
+	class cache_model extends Model
+	{
+		public function __construct()
+		{
+			parent::__construct();
+		}
+		public function cacheTest()
+		{
+			$cache_id = 'cache_test';
+			//文件缓存
+			$this->setCache('file', array('path' => 'tmp'));
+			//Memcache缓存
+			$this->setCache('memcache', array('servers' => array( array('host'=>'127.0.0.1','port'=>11211) ) ,'life_time'=>600));
+			//xcache
+			$this->setCache('xcache', array('life_time'=>600));
+			//缓存内容
+			$this->cache($cache_id, 'cache内容');
+			//获取缓存内容
+			$this->getCache($cache_id);
+			//移除缓存
+			$this->cache->remove($cache_id);
+		}
+	}
+>>>>>>> c51722c... 调整文件的目录结构

+ 302 - 0
_cli.php

@@ -0,0 +1,302 @@
+<?php
+/**
+ * @author Jinhui Zhu  <jinhui.zhu@live.cn>
+ * 通过命令行直接生成项目目录
+ */
+ini_set("display_errors", "On");
+/**
+ * 错误模式
+ */
+error_reporting(E_ALL ^ E_NOTICE ^ E_WARNING);
+
+/**
+ * 自动生成目标目录
+ *
+ * php -q _cli.php create=yes workspace=../project cache=tmp useDB=1
+ * create: 是否自动创建
+ * workspace: 程序存放的目录
+ * cache: 缓存目录
+ * useDB: 是否使用数据库,值 0/1
+ * 目前已经将配置文件写在db.ini中,配置内容将不在创建项目的时候提供,可以自行在目录中修改
+ */
+class cmd
+{
+    const VERSION = '1.2';
+    public $dir = array('configure', 'controller', 'model', 'view', 'plugins', 'tmp');
+
+    public function __construct($args)
+    {
+        $param = $this->parseArgvs($args);
+        if (sizeof($param) < 1 || ($param['dbHost'] != '' && sizeof($param) < 7)) {
+            $this->stdout("命令行使用如下:\n
+>php -q _cli.php create=yes workspace=../project cache=tmp dbHost=localhost dbName=test dbUser=root dbPassword=test\n
+ * create: 是否自动创建:yes; \n
+ * workspace: 工作目录\n
+ * cache : 缓存目录\n
+ * dbHost : 数据库服务器IP\n
+ * dbName : 数据库名称 : \n
+ * dbUser : 数据库用户名\n
+ * dbPassword : 数据库密码 : \n
+ ");
+            $this->stdout("是否自动创建 yes/no:");
+            $param['create'] = trim(fgets(\STDIN));
+            $this->stdout("工作目录:");
+            $param['workspace'] = trim(fgets(\STDIN));
+            $this->stdout("缓存目录:");
+            $param['cache'] = trim(fgets(\STDIN));
+            
+            $this->stdout('数据库服务器IP:');
+            $param['dbHost'] = trim(fgets(\STDIN));
+            if(!$param['dbHost']) $param['dbHost'] = 'localhost';
+            $this->stdout("数据库名称:");
+            $param['dbName'] = trim(fgets(\STDIN));
+
+            $this->stdout('请输入数据库用户名:');
+            $param['dbUser'] = trim(fgets(\STDIN));
+
+            $this->stdout('请输入数据库密码:');
+            $param['dbPassword'] = trim(fgets(\STDIN));
+
+            $this->stdout('将要在'. $param['workspace'] .'创建项目,确认请输入yes,取消请输入no:');
+
+            $param['create'] = trim(fgets(\STDIN));
+        }
+        $param['useDB'] = 0;
+        if($param['dbName'] != '')
+        {
+            $param['useDB'] = 1;
+        }
+        if ($param['create'] == 'yes') {
+            if ($this->workspace($param['workspace'])) {
+                $cache = $param['cache'];
+                if (empty($param['cache'])) $cache = 'tmp';
+                $this->dir[5] = $cache;
+                //创建目录工作区目录
+                if(!is_dir($param['workspace'] . '/public'))
+                {
+                    mkdir($param['workspace'] . '/public', 0777);
+                }
+                foreach ($this->dir AS $d) {
+                    $path = $param['workspace'] . '/private/' . $d;
+                    if (!is_dir($path)) {
+                        $date = date('Y-m-d H:i:s');
+                        echo "create path {$path} success.\n";
+                        mkdir($path, 0777, true);
+                        //写入.htaccess文件到包含的目录,不允许通过Apache浏览
+                        $htaccess = array();
+                        $htaccess[] = "##";
+                        $htaccess[] = "#";
+                        $htaccess[] = "#	\$Id: .htaccess 268 {$date}Z Jinhui.Zhu $";
+                        $htaccess[] = "#";
+                        $htaccess[] = "#	Copyright (C) 2010-2012 All Rights Reserved.";
+                        $htaccess[] = "#";
+                        $htaccess[] = "##";
+                        $htaccess[] = "";
+                        $htaccess[] = "Options Includes";
+                        file_put_contents($path . '/.htaccess', join("\n", $htaccess));
+                    } else {
+                        $this->stdout("{$path} 已经存在.". PHP_EOL);
+                    }
+                }
+                //拷贝网站配置文件site.xml到项目目录
+                if($cache != 'tmp'){
+                    $appIni = file_get_contents('_cli/app.ini');
+                    $appIni = str_replace('tmp/compile', $cache . '/compile', $appIni);
+                    $appIni = str_replace('tmp/cache', $cache . '/cache', $appIni);
+                    file_put_contents($param['workspace'] . '/private/configure/app.ini', $appIni);
+                }else if (!copy("_cli/app.ini", $param['workspace'] . '/private/configure/app.ini')) {
+                     $this->stdout('拷贝 app.ini 到 ' . $param['workspace'] . '/private/configure/app.ini失败, 拒绝访问.');
+                    
+                }
+                if (!copy("_cli/router.config.php", $param['workspace'] . '/private/configure/router.config.php')) {
+                    $this->stdout('拷贝 router.config.php 到' . $param['workspace'] . '/private/configure/router.config.php 失败, 拒绝访问.');
+                }
+                if ($param['useDB'] != 'no') {
+                    $dbIni = file_get_contents('_cli/db.ini');
+                    $dbIni = str_replace('DB_NAME', $param['dbName'], $dbIni);
+                    $dbIni = str_replace('DB_HOST', $param['dbHost'], $dbIni);
+                    $dbIni = str_replace('DB_USER', $param['dbUser'], $dbIni);
+                    $dbIni = str_replace('DB_PASSWORD', $param['dbPassword'], $dbIni);
+                    
+                    file_put_contents($param['workspace'] . '/private/configure/db.ini', $dbIni);
+                }
+
+                //生成数据库文件
+                //--生成首页文件
+                //--获取文件的相对路径
+                $realPath = $this->getRealPath($param['workspace']);
+                $this->stdout("真实路径 " . $realPath . "\n");
+                $QiiPath = $this->getRelatePath($realPath . "/index.php", dirname(__FILE__) . "/src/Qii.php");
+                $this->stdout("Qii 路径 " . $QiiPath . "\n");
+                $date = date("Y/m/d H:i:s");
+                $indexPage = array();
+                $indexPage[] = "<?php";
+                $indexPage[] = "/**";
+                $indexPage[] = " * This is index page auto create by Qii, don't delete";
+                $indexPage[] = " * ";
+                $indexPage[] = " * @author Jinhui.zhu	<jinhui.zhu@live.cn>";
+                $indexPage[] = " * @version  \$Id: index.php,v 1.1 {$date} Jinhui.Zhu Exp $";
+                $indexPage[] = " */";
+                $indexPage[] = 'require("../'.$QiiPath.'");';
+                $indexPage[] = '$app = \\Qii::getInstance();';
+                $indexPage[] = '//如需更改网站源代码存储路径,请修改此路径';
+                $indexPage[] = '$app->setWorkspace(\'../private\');';
+                $indexPage[] = '$env = getenv(\'WEB_ENVIRONMENT\') ? getenv(\'WEB_ENVIRONMENT\') : \'product\';';
+                $indexPage[] = '$app->setEnv($env);';
+                $indexPage[] = '$app->setCachePath(\''.$cache.'\');';
+                $indexPage[] = '$app->setAppConfigure(\'../private/configure/app.ini\');';
+                if ($param['useDB']) $indexPage[] = '$app->setDB(\'../private/configure/db.ini\');';
+                $indexPage[] = '$app->setRouter(\'../private/configure/router.config.php\')';
+                $indexPage[] = '->run();';
+                if (!file_exists($realPath . "/public/index.php")) {
+                    //如果文件不存在就写入
+                    file_put_contents($realPath . "/public/index.php", join("\n", $indexPage));
+                }
+                //写入首页controller
+                if (!file_exists($realPath . "/private/controller/index.php")) {
+                    $indexContents = array();
+                    $indexContents[] = "<?php";
+                    $indexContents[] = 'namespace controller;' . PHP_EOL;
+                    $indexContents[] = 'use \Qii\Base\Controller;' . PHP_EOL;
+                    $indexContents[] = "class index extends Controller";
+                    $indexContents[] = "{";
+                    $indexContents[] = "\tpublic \$enableView = true;";
+                    $indexContents[] = "\tpublic function __construct()\n\t{";
+                    $indexContents[] = "\t\tparent::__construct();";
+                    $indexContents[] = "\t}";
+                    $indexContents[] = "\tpublic function indexAction()\n\t{";
+                    $indexContents[] = "\t\t return new \Qii\Base\Response(array('format' => 'html', 'body' => '请重写 '. __FILE__ . ' 中的 indexAction 方法, 第 ' . __LINE__ . ' 行'));";
+                    $indexContents[] = "\t}";
+                    $indexContents[] = "}";
+                    file_put_contents($realPath . "/private/controller/index.php", join("\n", $indexContents));
+                }
+                //apache rewrite file
+                $htaccessFile = $param['workspace'] . "/public/.htaccess";
+                if(!file_exists($htaccessFile)){
+                    if(!copy('_cli/.htaccess', $htaccessFile)){
+                        $this->stdout($this->stdout("拷贝 .htaccess 到 ". $htaccessFile . ' 失败, 拒绝访问') . PHP_EOL);
+                    }
+                }
+            }
+        } else if($param['create'] == 'no'){
+            $this->stdout('您已经取消');
+        }
+    }
+
+    /**
+     * 获取文件相对路径
+     *
+     * @param String $cur 路径1
+     * @param String $absp 路径2
+     * @return String 路径2相对于路径1的路径
+     */
+    public function getRelatePath($cur, $absp)
+    {
+        $cur = str_replace('\\', '/', $cur);
+        $absp = str_replace('\\', '/', $absp);
+        $sabsp = explode('/', $absp);
+        $scur = explode('/', $cur);
+        $la = count($sabsp) - 1;
+        $lb = count($scur) - 1;
+        $l = max($la, $lb);
+
+        for ($i = 0; $i <= $l; $i++) {
+            if ($sabsp[$i] != $scur[$i])
+                break;
+        }
+        $k = $i - 1;
+        $path = "";
+        for ($i = 1; $i <= ($lb - $k - 1); $i++)
+            $path .= "../";
+        for ($i = $k + 1; $i <= ($la - 1); $i++)
+            $path .= $sabsp[$i] . "/";
+        $path .= $sabsp[$la];
+        return $path;
+    }
+
+    /**
+     * 获取相对目录
+     *
+     * @param String $workspace 目标路径
+     * @return String 目标路径相对于当期路径的相对路径
+     */
+    public function getRealPath($workspace)
+    {
+        $currentDir = str_replace("\\", "/", dirname(__FILE__));
+        $workspace = str_replace("\\", "/", $workspace);
+
+        if ($workspace[0] == '/') {
+            return $workspace;
+        } else {
+            $workspaceArray = explode("/", $workspace);
+            $currentDirArray = explode("/", $currentDir);
+
+            $work = array();
+            foreach ($workspaceArray AS $k) {
+                if ($k == '..') {
+                    array_pop($currentDirArray);
+                } elseif ($k == '.') {
+
+                } else {
+                    $work[] = $k;
+                }
+            }
+            if (!empty($currentDirArray)) {
+                return join("/", $currentDirArray) . "/" . join("/", $work);
+            } else {
+                return false;
+            }
+        }
+    }
+
+    /**
+     * 创建工作区目录
+     *
+     * @param String $dir
+     * @return Bool
+     */
+    public function workspace($dir)
+    {
+        return true;
+        if (!empty($dir)) {
+            if (!is_dir($dir)) {
+                return mkdir($dir, 0777, true);
+            } else {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 匹配命令行参数
+     *
+     * @param String $argvs
+     * @return Array 返回参数对应的值
+     */
+    public function parseArgvs($argvs)
+    {
+        $keyValue = array();
+        foreach ($argvs AS $value) {
+            $valueArray = explode("=", $value);
+            $k = $valueArray[0];
+            $v = stripslashes($valueArray[1]);
+            $keyValue[$k] = $v;
+        }
+        return $keyValue;
+    }
+    /**
+     * 在windows cmd 情况下的中文输出乱码问题
+     * @param string $string
+     * @return bool|int
+     */
+    public function stdout($string)
+    {
+        if(strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') $string = iconv('utf-8', 'gbk', $string);
+        fwrite(\STDOUT, $string);
+    }
+}
+
+array_shift($argv);
+new cmd($argv);
+?>

+ 12 - 0
demo/private/Bootstrap.php

@@ -0,0 +1,12 @@
+<?php
+class Bootstrap implements \Qii\Base\Bootstrap
+{
+	public function __construct()
+	{
+		
+	}
+
+	public function _initialize()
+	{
+	}
+}

+ 108 - 0
demo/private/configure/app.ini

@@ -0,0 +1,108 @@
+;通用配置,如果没有指定环境将使用通用配置,指定了环境,指定环境的配置将覆盖默认配置中的配置信息
+[author]
+name = 朱金辉
+email = jinhui.zhu@live.cn
+qq = 119328118
+
+[common]
+;程序是否使用命名空间
+namespace['use'] = true
+namespace[list.controller] = true
+namespace[list.model] = true
+namespace[list.library] = true
+namespace[list.action] = true
+;rewrite 方法, 此定义不能省略
+rewriteMethod = Short
+;rewrite匹配规则,可选
+rewriteRules = Normal
+;hosts中的属性将会覆盖掉默认的属性,当在不同域名上使用不同配置上很有用处
+;hosts[0.domain] = test.istudy.wang
+;hosts[0.path] = test
+;hosts[0.password] = test
+
+;用于检查系统环境的时候用到的密码,如果不填写就可以直接通过url进入
+admin[user] = admin
+admin[password] = 119328118
+;是否开启调试模式,调试模式下,所有的错误都将抛出来,并终止运行
+debug = 1
+;错误页面
+errorPage = Error:Index
+;时间区域
+timezone = Asia/Shanghai
+;文档类型
+contentType = text/html
+;文档编码
+charset = UTF-8
+;模板引擎
+view[engine] = smarty
+view[path] = view
+;smarty 引擎的相关配置
+view[smarty.view] = view
+view[smarty.path] = view
+view[smarty.ldelimiter] = {#
+view[smarty.rdelimiter] = #}
+view[smarty.compile] = tmp/compile
+view[smarty.cache] = tmp/cache
+view[smarty.lifetime] = 300
+;缓存类型
+cache = memcache
+
+;是否开启安全验证,enable:是否开启安全验证;key:POST数据的时候安全字符串加密用到的key
+security[enable] = true
+security[name] = security_sid
+security[expired] = 3600
+security[key] = 4cd780a986d5c30e03bdcb67d16c8320
+
+;memcache配置,多个服务器IP和端口以;隔开
+memcache[servers] = 127.0.0.1
+memcache[ports] = 11211
+
+
+;搜索文件路径
+xpath[] = controller
+xpath[] = model
+xpath[] = class
+xpath[] = plugin
+
+;map:为参数的顺序,当启用短URI标签的情况下会按照这个顺序去遍历参数;
+query[] = controller
+query[] = action
+query[] = param
+
+;controller、action配置  name:参数名 ext:后缀;default:默认方法 
+controller[name] = controller
+controller[prefix] = controller
+controller[default] = index
+
+action[name] = action
+action[suffix] = Action
+action[default] = index
+
+;url相关配置,用于生成链接用
+[uri]
+
+;url模式,短链接模式
+mode = short
+controllerName = controller
+actionName = action
+
+normal[mode] = normal
+normal[trim] = 0
+normal[symbol] = "&"
+normal[extenstion] = .html
+
+middle[mode] = middle
+middle[trim] = 1
+middle[symbol] = "/"
+middle[extenstion] = .html
+
+short[mode] = short
+short[trim] = 1
+short[symbol] = "/"
+short[extenstion] = .html
+;以下这种写法将会继承:后边的section,如果是"."开头的话就放在当前key下边
+[dev:common:.uri]
+password = 12345
+[product:common:.uri]
+password = 119328118
+

+ 25 - 0
demo/private/configure/db.ini

@@ -0,0 +1,25 @@
+;数据库配置
+[common]
+;是否读写分离
+readOrWriteSeparation = 0
+driver = pdo
+debug = true
+;使用的数据库类型
+use_db_driver = mysql
+master[db] = wecv
+master[host] = 127.0.0.1
+master[user] = root
+master[password] = A119328118a
+
+slave[0.db] = istudy
+slave[0.host] = 127.0.0.1
+slave[0.user] = wecv
+slave[0.password] = A119328118a
+
+
+slave[1.db] = istudy
+slave[1.host] = 127.0.0.1
+slave[1.user] = wecv
+slave[1.password] = A119328118a
+
+[product:common]

+ 5 - 0
demo/private/configure/router.config.php

@@ -0,0 +1,5 @@
+<?php
+return [
+    'home:index' => 'index:index',
+    'manage:*' => 'manage\{1}:*'
+];

+ 36 - 0
demo/private/controller/index.php

@@ -0,0 +1,36 @@
+<?php
+namespace controller;
+
+class index extends \Qii\Base\Controller
+{
+    public $enableDB = true;
+    public $enableView = true;
+    public function indexAction()
+    {
+        return $this->setResponse(new \Qii\Base\Response(array('format' => 'html', 'body' => 'This is html')));
+        return;
+        $data = array();
+        $data['lists'][] = $this->db->getRow('SELECT * FROM ipAddress ORDER BY id DESC LIMIT 1');
+        $data['lists'][] = $this->db->getRow('SELECT * FROM ipAddress ORDER BY id ASC LIMIT 1');
+        $data['querySeconds'] = $this->db->querySeconds;
+        
+        return new \Qii\Base\Response(array('format' => 'json', 'body' => $data));
+    }
+    
+    public function dispatchAction()
+    {
+        $this->dispatch('test', 'index');
+    }
+    
+    public function forwardAction()
+    {
+        $this->setForward('test', 'index');
+    }
+
+    public function displayAction()
+    {
+        //可以从这里设置加载模板的路径
+        $this->view->setTemplateDir(__DIR__ . "/view/");
+        echo $this->view->fetch('index.tpl');
+    }
+}

+ 10 - 0
demo/private/controller/test.php

@@ -0,0 +1,10 @@
+<?php
+namespace controller;
+
+class test extends \Qii\Base\Controller
+{
+    public function indexAction()
+    {
+        echo __CLASS__;
+    }
+}

+ 1 - 0
demo/private/controller/view/index.tpl

@@ -0,0 +1 @@
+This is tpl from controller dir

+ 39 - 0
demo/private/plugins/loger.php

@@ -0,0 +1,39 @@
+<?php
+namespace plugins;
+
+class loger implements \Qii\Loger\Writer
+{
+	public $logerPath = 'tmp';
+	public $fileName = 'loger.log';
+
+	public function __construct()
+	{
+		$this->logerPath = dirname(dirname(__FILE__)) .DS. $this->logerPath;
+		$this->fileName = $this->logerPath . DS . date('Y-m-d') .'.'. $this->fileName;
+		return $this;
+	}
+
+	public function setFileName($fileName)
+	{
+		$this->fileName = $this->logerPath . DS . date('Y-m-d') .'.'. $fileName . '.log';
+		return $this;
+	}
+	protected function trimSpace($text)
+	{
+	    return  str_replace(array("\r\n", "\r", "\n", "\t", "\s", ' ', chr(32)), "", $text);
+	}
+
+	public function formatLog($log)
+	{
+		if(!is_array($log)) return $log;
+		return json_encode($log);
+		return $this->trimSpace(print_r($log, true));
+	}
+
+	public function writeLog($loger)
+	{
+		if(is_array($loger)) $loger = $this->formatLog($loger);
+
+		file_put_contents($this->fileName, date('Y-m-d H:i:s') ."\n\t". $loger . "\n", FILE_APPEND);
+	}
+}

+ 1 - 0
demo/private/view/index.tpl

@@ -0,0 +1 @@
+index test page

+ 13 - 0
demo/public/.htaccess

@@ -0,0 +1,13 @@
+##
+#
+#	$Id: .htaccess 268 2009-12-16 06:18:19Z Jinhui.Zhu $
+#
+#	Copyright (C) 2008-2009 www.qiiframework.com. All Rights Reserved.
+#  
+##
+RewriteEngine On
+RewriteCond %{REQUEST_FILENAME} !-d
+RewriteCond %{REQUEST_FILENAME} !-f
+
+RewriteCond $1 !^(index.php|images|robots.txt)
+RewriteRule (.*)$ index.php/$1 [L,QSA,PT]

+ 13 - 0
demo/public/index.php

@@ -0,0 +1,13 @@
+<?php
+require_once('../../src/Qii.php');
+
+$app = \Qii::getInstance();
+$app->setWorkspace('../private')
+->setCachePath('tmp')
+->setAppConfigure('configure/app.ini')
+->setUseNamespace('Bootstrap', false)
+->setLoger('plugins\loger')
+->setDB('configure/db.ini')
+->setRouter('configure/router.config.php')
+->setBootstrap()
+->run();

+ 394 - 0
src/Application.php

@@ -0,0 +1,394 @@
+<?php
+namespace Qii;
+
+use \Qii\Base\Dispatcher;
+
+use \Qii\Autoloader\Factory;
+use \Qii\Autoloader\Import;
+use \Qii\Autoloader\Psr4;
+use \Qii\Autoloader\Instance;
+
+use \Qii\Config\Register;
+use \Qii\Config\Consts;
+use \Qii\Config\Setting;
+
+class Application 
+{
+    /**
+     * 存储网站配置文件内容
+     *
+     * @var array $config 配置内容
+     */
+    protected static $config = [];
+    /**
+     * @var object $logerWriter 写日志工具
+     */
+    public $logerWriter = null;
+    /**
+     * @var string $workspace 工作目录
+     */
+    private static $workspace = './';/**
+     * @var string $env 环境变量
+     */
+    public static $env = 'product';
+    /**
+     * @var array $paths 网站使用的路径
+     */
+    public static $paths = array('configure', 'controller', 'model', 'view', 'plugins', 'tmp');
+
+    /**
+     * Qii\Request\Url
+     */
+    public $request;
+    /**
+     * 分发器
+     */
+    public $dispatcher = null;
+
+	public function __construct()
+	{
+        $this->helper = Psr4::getInstance()->loadClass('\Qii\Autoloader\Helper');
+	}
+
+    /**
+     * 初始化本实例对象
+     *
+     * @return object
+     */
+	public static function getInstance()
+	{
+        $args = func_get_args();
+        if (count($args) > 0) {
+            $className = array_shift($args);
+            return Psr4::getInstance($className);
+        }
+	    return Factory::getInstance('\Qii\Application');
+	}
+
+    /**
+     * 设置网站运行环境
+     *
+     * @param string $env 网站环境
+     * @return $this
+     */
+    public function setEnv($env)
+    {
+        self::$env = $env;
+        return $this;
+    }
+    /**
+     * 设置缓存文件路径
+     * @param string $path 缓存路径
+     */
+    public function setCachePath($path)
+    {
+        Register::set(Consts::APP_CACHE_PATH, $this->getCachePath($path));
+        return $this;
+    }
+
+    /**
+     * 保存网站的配置文件
+     *
+     * @param $iniFile
+     */
+    public function setAppIniFile($iniFile)
+    {
+        Register::set(Consts::APP_INI_FILE, $iniFile);
+        return $this;
+    }
+
+    /**
+     * 设置网站的工作目录,可以通过此方法将网站的重要文件指向到其他目录
+     *
+     * @param string $workspace 工作目录
+     * @return $this
+     */
+    public function setWorkspace($workspace = './')
+    {
+        //此处转换成真实路径,防止workspace中引入的文件出错
+        if (!is_dir($workspace)) {
+            throw new \Qii\Exceptions\FolderDoesNotExist(\Qii::i(1045, $workspace), __LINE__);
+        }
+        $workspace = Psr4::getInstance()->realpath($workspace);
+        Psr4::getInstance()->removeNamespace('workspace', self::$workspace);
+        //如果配置了使用namespace就走namespace
+        self::$workspace = $workspace;
+        Psr4::getInstance()->addNamespace('workspace', $workspace, true);
+        foreach (self::$paths AS $path) {
+            Psr4::getInstance()->addNamespace($path, $workspace . '\\' . $path);
+        }
+
+        return $this;
+    }
+    /**
+     * 获取指定路径的缓存绝对路径
+     * @param string $path 路径
+     * @return string 绝对路径
+     */
+    public function getCachePath($path)
+    {
+        if (self::$workspace != '') return self::$workspace . DS . $path;
+        $dir = '';
+        $workspace = sr4::getInstance()->getNamespace('workspace');
+        foreach ($workspace AS $dir) {
+            if (is_dir($dir)) $dir = $dir;
+        }
+        return $dir . DS . $path;
+    }
+    /**
+     * 获取网站运行环境
+     *
+     * @return string
+     */
+    public function getEnv()
+    {
+        return self::$env;
+    }
+    /**
+     * 获取当前工作目录
+     */
+    public function getWorkspace()
+    {
+        return self::$workspace;
+    }
+    /**
+     * 获取网站的配置文件
+     * @return Mix
+     */
+    public function getAppIniFile()
+    {
+        return Register::get(Consts::APP_INI_FILE);
+        return $this;
+    }
+
+    /**
+     * 设置网站配置文件
+     * @param string $ini 配置文件路径
+     * @param string $env 环境
+     *
+     */
+    public function setAppConfigure($ini, $env = '')
+    {
+        if ($env == '') $env = $this->getEnv();
+        $ini = Psr4::getInstance()->getFileByPrefix($ini);
+        $this->setAppIniFile($ini);
+        if (!Register::setAppConfigure(
+            $ini,
+            $env
+        )
+        ) throw new \Qii\Exceptions\FileNotFound(\Qii::i(1405, $ini), __LINE__);
+        //载入request方法
+        $this->request = Psr4::getInstance()->loadClass('\Qii\Request\Http');
+        Setting::getInstance()->setDefaultTimeZone();
+        Setting::getInstance()->setDefaultControllerAction();
+        Setting::getInstance()->setDefaultNamespace();
+        Setting::getInstance()->setDefaultLanguage();
+        return $this;
+    }
+
+    /**
+     * 合并ini文件生成的数组
+     * @param String $iniFile ini文件名
+     * @param Array $array
+     */
+    public function mergeAppConfigure($iniFile, $array)
+    {
+        if (!is_array($array)) return;
+        Register::mergeAppConfigure($iniFile, $array);
+        return $this;
+    }
+
+    /**
+     * 覆盖/添加ini文件的key对应的值
+     * @param String $iniFile ini文件名
+     * @param String $key
+     * @param String $val
+     */
+    public function rewriteAppConfigure($iniFile, $key, $val)
+    {
+        Register::rewriteConfig($iniFile, $key, $val);
+        return $this;
+    }
+    
+    /**
+     * 设置指定的前缀是否使用命名空间
+     * @param string $prefix 前缀
+     * @param bool $useNamespace 是否使用
+     * @return $this
+     */
+    public function setUseNamespace($prefix, $useNamespace = true)
+    {
+        Psr4::getInstance()->setUseNamespace($prefix, $useNamespace);
+        return $this;
+    }
+
+    /**
+     * 添加命名空间对应的网站目录
+     * @param string $prefix 前缀
+     * @param string $baseDir 对应的路径
+     * @param bool $prepend 是否追加
+     * @return $this
+     */
+    public function addNamespace($prefix, $baseDir, $prepend = false)
+    {
+        if (!is_dir($baseDir)) {
+            throw new \Qii\Exceptions\FolderDoesNotExist(\Qii::i(1009, $baseDir), __LINE__);
+        }
+        $baseDir = Psr4::getInstance()->realpath($baseDir);
+        Psr4::getInstance()->addNamespace($prefix, $baseDir, $prepend);
+        return $this;
+    }
+
+    /**
+     * 设置启动前执行的方法
+     *
+     * @return $this
+     * @throws Exception
+     */
+    public function setBootstrap()
+    {
+        Psr4::getInstance()->loadFileByClass('Bootstrap');
+        if (!class_exists('Bootstrap', false)) throw new \Qii\Exceptions\ClassNotFound(\Qii::i(1405, 'Bootstrap'), __LINE__);;
+        $bootstrap = Psr4::getInstance()->instance('Bootstrap');
+        if (!$bootstrap instanceof \Qii\Base\Bootstrap) {
+            throw new \Qii\Exceptions\ClassInstanceof(Qii::i(1107, 'Bootstrap', 'Qii\Bootstrap'), __LINE__);;
+        }
+        $refectionClass = new \ReflectionClass('Bootstrap');
+        $methods = $refectionClass->getMethods();
+        //自动执行以init开头的公共方法
+        foreach ($methods as $method) {
+            $name = $method->getName();
+            if (substr($name, 0, 4) == 'init' && $method->isPublic()) $bootstrap->$name();
+        }
+        return $this;
+    }
+
+    /**
+     * 设置写loger的类
+     *
+     * @param LogerWriter $logerCls 日志记录类
+     */
+    public function setLoger($logerCls)
+    {
+        $this->logerWriter = Instance::instance(
+            '\Qii\Loger\Instance',
+            Instance::instance($logerCls)
+        );
+        return $this;
+    }
+
+    /**
+     * 设置数据库使用的文件
+     *
+     * @param $iniFile
+     * @throws \Qii_Exceptions_Overwrite
+     */
+    public function setDBIniFile($iniFile)
+    {
+        Register::set(Consts::APP_DB, $iniFile);
+    }
+
+    /**
+     * 获取当前数据库文件
+     *
+     * @return \Qii_Mix
+     * @throws \Qii_Exceptions_Variable
+     */
+    public function getDBIniFile()
+    {
+        return Register::get(Consts::APP_DB);
+    }
+
+    /**
+     * 设置数据库配置文件
+     * @param string $ini 配置文件路径
+     * @param string $env 环境
+     */
+    public function setDB($ini, $env = '')
+    {
+        if ($env == '') $env = $this->getEnv();
+        $this->setDBIniFile($ini);
+        if (!Register::setAppConfigure(
+            Psr4::getInstance()->getFileByPrefix($ini),
+            $env
+        )
+        ) {
+            throw new \Qii\Exceptions\FileNotFound(\Qii::i(1405, $ini), __LINE__);
+        }
+        return $this;
+    }
+
+    /**
+     * 设置路由规则
+     *
+     * @param string $router 路由配置文件位置
+     * @return $this
+     */
+    public function setRouter($router)
+    {
+        Register::set(
+            Consts::APP_SITE_ROUTER,
+            Import::includes(
+                Psr4::realpath(Psr4::getInstance()->getFileByPrefix($router)
+                )
+            )
+        );
+        //载入rewrite规则后重写request
+        $rewrite = Psr4::loadStatic(
+            '\Qii\Router\Parse',
+            'get',
+            \Qii\Request\Url::getPathInfo(),
+            $this->request->controller,
+            $this->request->action,
+            $this->request->url->get(2)
+        );
+        $rewrite['controller'] = $rewrite['controller'] ? $rewrite['controller'] : $this->request->defaultController();
+        $rewrite['action'] = $rewrite['action'] ? $rewrite['action'] : $this->request->defaultAction();
+        //是否已经rewrite,如果和url中的不一样就是已经rewrite
+        if ($this->request->controller != $rewrite['controller'] || $this->request->action != $rewrite['action']) {
+            $this->request->setRouted(true);
+        }
+        $this->request->setControllerName($rewrite['controller']);
+        $this->request->setActionName($rewrite['action']);
+        return $this;
+    }
+    /**
+     * sprintf 格式化语言错误信息内容
+     *
+     * Qii::e($message, $argv1, $argv2, ..., $line);
+     * $message = sprintf($message, $argv1, $argv2, ...);
+     * throw new \Qii\Exceptions\Error($message, $line);
+     */
+    public function showError()
+    {
+        return call_user_func_array(array('\Qii\Exceptions\Errors', 'e'), func_get_args());
+    }
+	
+	public function run()
+	{
+        $this->helper->load(self::$workspace);
+        $this->dispatcher = Psr4::getInstance()->loadClass('\Qii\Base\Dispatcher');
+        if (!$this->dispatcher instanceof \Qii\Base\Dispatcher) {
+            throw new \Exception('Dispatcher must instance of Qii\Base\Dispatcher', __LINE__);
+        }
+        //如果设置了host的话,看host对应的controller路径
+        $hosts = $this->appConfigure('hosts');
+        if (count($hosts) > 0) {
+            foreach ($hosts AS $host) {
+                if ($host['domain'] == $this->request->host) {
+                    Register::set(
+                        Consts::APP_DEFAULT_CONTROLLER_PREFIX,
+                        ($host['path'] ? $host['path'] : $host['domain'])
+                    );
+                    break;
+                }
+            }
+        }
+        $this->request->setDispatcher($this->dispatcher);
+        //rewrite规则
+        $this->dispatcher->setRequest($this->request);
+        $this->dispatcher->dispatch();
+        $this->request->setDispatched(true);
+        return $this;
+	}
+}

+ 49 - 0
src/Autoloader/Factory.php

@@ -0,0 +1,49 @@
+<?php
+namespace Qii\Autoloader;
+
+use \Qii\Exceptions;
+
+class Factory
+{
+    /**
+     * @param array $_instance 实例化对象的存储池
+     */
+    protected static $instance = [];
+    /**
+     * 以 new \Qii\Autoloader\Factory($className)的方式实例化对象
+     */
+    public function __construct($className)
+    {
+        return Factory::getInstance($className);
+    }
+    /**
+     * 实例化对象
+     * @param string $className 类名
+     */
+    public static function getInstance($className)
+    {
+        if(!$className)
+        {
+            return \Qii::e('CLASS_NAME_IS_NULL', $className);
+        }
+        if(isset(Factory::$instance[$className]) &&
+            Factory::$instance[$className] != null
+        ){
+            return Factory::$instance[$className];
+        }
+
+        $args = func_get_args();
+        array_shift($args);
+        
+        if(!class_exists($className, false))
+        {
+            $className = Psr4::getInstance()->getClassName($className);
+        }
+        $refClass = new \ReflectionClass($className);
+        $instance = $refClass->newInstanceArgs($args);
+        if ($refClass->hasMethod('_initialize')) {
+            call_user_func_array(array($instance, '_initialize'), $args);
+        }
+        return Factory::$instance[$className] = $instance;
+    }
+}

+ 67 - 0
src/Autoloader/Helper.php

@@ -0,0 +1,67 @@
+<?php
+namespace Qii\Autoloader;
+
+/**
+ * Helper 将自动注册到系统 Helper中,直接调用即可
+ *
+ * @author Jinhui Zhu<jinhui.zhu@live.cn>2015-10-24 23:18
+ * @version 1.2
+ */
+class Helper
+{
+    const VERSION = '1.2';
+    /**
+     * @var $helpers helper类
+     */
+    public static $helpers = array();
+
+    public function __construct()
+    {
+        self::$helpers = array();
+    }
+
+    /**
+     * 使用属性返回helper类
+     * @param string $name 不带helper\的类名
+     * @return object
+     */
+    public function __get($name)
+    {
+        if (substr($name, 0, 7) == 'helper\\') return $this->get($name);
+        return $this->get('helper\\' . $name);
+    }
+
+    /**
+     * 获取helper类,如果没有实例化就抛出异常
+     * @param string $helper
+     * @return object
+     */
+    public function get($helper)
+    {
+        if (isset(self::$helpers[$helper])) return self::$helpers[$helper];
+        throw new \Qii\Exceptions\CallUndefinedClass(\Qii::i('1105', $helper), __LINE__);
+    }
+
+    /*
+     * 自动加载Helper目录中文件,支持自动实例化对象
+     * @param string $appPath 自动加载指定目录中文件
+     */
+    public function load($appPath = '')
+    {
+        if ($appPath == '') {
+            return;
+        }
+        if (!is_dir($appPath . DS . 'helper')) {
+            return;
+        }
+        foreach (glob(str_replace("//", DS, $appPath . DS . 'helper' . DS . '*.php'), GLOB_BRACE) AS $file) {
+            if(\Qii\Autoloader\Import::requires($file)){
+                //如果里边包含class的话就将class注册到Qii::instance('class');
+                $className = 'helper\\'. pathinfo($file)['filename'];
+                if (!isset(self::$helpers[$className]) && class_exists($className, false)) {
+                    self::$helpers[$className] = \Qii\Autoloader\Psr4::getInstance()->instance($className);
+                }
+            }
+        }
+    }
+}

+ 175 - 0
src/Autoloader/Import.php

@@ -0,0 +1,175 @@
+<?php
+namespace Qii\Autoloader;
+
+class Import
+{
+    const VERSION = '1.3';
+    private static $loadedFiles = array();
+    private static $includeFiles = array();
+
+    /**
+     * require文件
+     *
+     * @param string $file 需要require的文件
+     * @return array|bool|void
+     */
+    public static function requires($file)
+    {
+        if (is_array($file)) {
+            return array_map(function ($n) {
+                return self::requires($n);
+            }, $file);
+        }
+        $file = str_replace(DS . DS , DS, str_replace(array('\\', '/'), DS, $file));
+        if (self::getFileLoaded($file)) return true;
+        if (file_exists($file)) {
+            self::setFileLoaded($file);
+            require $file;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * 包含文件
+     * @param string $file 文件路径
+     * @return mix
+     */
+    public static function includes($file)
+    {
+        if (is_array($file)) {
+            return array_map(function ($n) {
+                return self::includes($n);
+            }, $file);
+        }
+        $file = str_replace(array('\\', '/'), DS, $file);
+        if (self::getIncludeFiles($file) !== null) self::getIncludeFiles($file);
+        if (file_exists($file)) {
+            $configure = include($file);
+            self::setIncludeFiles($file, $configure);
+            return $configure;
+        }else{
+            //print_r(debug_backtrace());
+            throw new \Qii\Exceptions\FileNotFound(\Qii::i(1405, $file), __LINE__);
+        }
+        return false;
+    }
+
+    /**
+     * 根据类名载入文件
+     *
+     * @param string $className 类名
+     * @return string
+     */
+    public static function requireByClass($className)
+    {
+        return \Qii\Autoloader\Psr4::getInstance()->loadFileByClass($className);
+    }
+
+    /**
+     * 载入文件夹中所有文件
+     *
+     * @param string $dir 目录
+     * @throws Qii\Exceptions\FileNotFound
+     */
+    public static function requireByDir($dir)
+    {
+        if (!is_dir($dir)) throw new \Qii\Exceptions\FileNotFound(\Qii::i(1405, $dir), __LINE__);
+        $files = self::findFiles($dir, array('php'));
+        if (isset($files['php'])) self::requires($files['php']);
+    }
+
+    /**
+     * 设置文件到加载列表
+     *
+     * @param string $file 文件
+     */
+    public static function setFileLoaded($file)
+    {
+        self::$loadedFiles[$file] = true;
+    }
+
+    /**
+     * 获取指定文件是否已经加载
+     * @param string $file 文件路径
+     * @return null
+     */
+    public static function getFileLoaded($file)
+    {
+        if (isset(self::$loadedFiles[$file])) return self::$loadedFiles[$file];
+        return false;
+    }
+
+    /**
+     * 设置include的文件到已经加载列表
+     *
+     * @param string $file 文件路径
+     */
+    public static function setIncludeFiles($file, $config)
+    {
+        self::$includeFiles[$file] = $config;
+    }
+
+    /**
+     * 获取指定文件是否已经加载
+     *
+     * @param string $file 文件路径
+     * @return bool
+     */
+    public static function getIncludeFiles($file)
+    {
+        if (isset(self::$includeFiles[$file])) {
+            return self::$includeFiles[$file];
+        }
+        return false;
+    }
+
+    /**
+     * 获取已经require及include的文件列表
+     *
+     * @return array
+     */
+    public static function getLoadedFile()
+    {
+        return array('include' => self::$includeFiles, 'requires' => self::$loadedFiles);
+    }
+
+    /**
+     * 遍历目录中的路径
+     * @param string $directory 目录
+     * @param array $directories 目录下所有的路径
+     */
+    public static function globRecursive($directory, &$directories = array())
+    {
+        foreach (glob($directory, GLOB_ONLYDIR | GLOB_NOSORT) as $folder) {
+            $directories[] = $folder;
+            self::globRecursive("{$folder}/*", $directories);
+        }
+    }
+
+    /**
+     * 返回指定目录中的文件
+     * @param string $directory 目录
+     * @param array $extensions 需要过滤的后缀名
+     * @return array
+     */
+    public static function findFiles($directory, $extensions = array())
+    {
+        self::globRecursive($directory, $directories);
+        $files = array();
+        foreach ($directories as $directory) {
+            if (count($extensions) == 0) {
+                foreach (glob("{$directory}/*.*") as $file) {
+                    $files[] = $file;
+                }
+            } else {
+                foreach ($extensions as $extension) {
+                    foreach (glob("{$directory}/*.{$extension}") as $file) {
+                        $files[$extension][] = $file;
+                    }
+                }
+            }
+        }
+        return $files;
+    }
+}

+ 71 - 0
src/Autoloader/Instance.php

@@ -0,0 +1,71 @@
+<?php
+namespace Qii\Autoloader;
+/**
+ * Instance类
+ * @author Jinhui Zhu<jinhui.zhu@live.cn>2015-10-22 19:45
+ * @version 1.3
+ *
+ * 使用方法:
+ *
+ * $class = Qii\Instance::init('className');
+ * 再次使用就可以直接通过Qii\Instance::getInstance('className');
+ */
+class Instance
+{
+    const VERSION = '1.2';
+    /**
+     * @var APP_LOAD_PREFIX 保存类到以APP_LOAD_PREFIX开头的key中
+     */
+    const APP_LOAD_PREFIX = '__qii_instance';
+    /**
+     * @var $loadedClass 保存加载过的类
+     */
+    protected static $loadedClass = array();
+
+    /**
+     * 获取初始化的类
+     * @param string $className 类名
+     * @reutrn object
+     */
+    public static function getInstance($className)
+    {
+        if (isset(self::$loadedClass[self::APP_LOAD_PREFIX . $className])) return self::$loadedClass[self::APP_LOAD_PREFIX . $className];
+        throw new \Qii\Exceptions\CallUndefinedClass(\Qii::i('1105', $className), __LINE__);
+    }
+
+    /**
+     * 初始化类保存到_loadedClass中并返回
+     */
+    public static function initialize()
+    {
+        $args = func_get_args();
+        $className = array_shift($args);
+        if (!class_exists($className, false)) throw new \Qii\Exceptions\CallUndefinedClass(\Qii::i('1105', $className), __LINE__);
+        if (isset(self::$loadedClass[self::APP_LOAD_PREFIX . $className])
+            && self::$loadedClass[self::APP_LOAD_PREFIX . $className]
+        ) return self::$loadedClass[self::APP_LOAD_PREFIX . $className];
+        $loader = new \ReflectionClass($className);
+        try {
+            $instance = $loader->newInstanceArgs($args);
+            self::$loadedClass[self::APP_LOAD_PREFIX . $className] = $instance;
+            //如果有_initialize方法就自动调用_initialize方法,并将参数传递给_initialize方法
+            if ($loader->hasMethod('_initialize')) {
+                call_user_func_array(array($instance, '_initialize'), $args);
+            }
+            return self::$loadedClass[self::APP_LOAD_PREFIX . $className];
+        } catch (\Exception $e) {
+            throw new \Exception($e->getMessage(), __LINE__);
+        }
+    }
+
+    /**
+     * 加载文件后再初始化
+     */
+    public static function instance()
+    {
+        $args = func_get_args();
+        $className = array_shift($args);
+        \Qii\Autoloader\Psr4::getInstance()->loadFileByClass($className);
+        return call_user_func_array(array('\Qii\Autoloader\Instance', 'initialize'), array_merge(array($className), $args));
+    }
+}

+ 67 - 0
src/Autoloader/Loader.php

@@ -0,0 +1,67 @@
+<?php
+namespace Qii\Autoloader;
+
+/**
+ * Loader类
+ */
+class Loader
+{
+
+    const VERSION = '1.2';
+    /**
+     * @var $loaded 保存已经加载过的类实例
+     */
+    private $loaded;
+    /**
+     * @var $lastCall 最后一次访问的方法
+     */
+    private $lastCall;
+
+    public function __construct()
+    {
+        $this->lastCall = null;
+        return $this;
+    }
+
+    public function __clone()
+    {
+        $this->lastCall = null;
+    }
+
+    public function __get($name)
+    {
+        $this->lastCall = $name;
+        return $this;
+    }
+
+    /**
+     * 返回load对象
+     *
+     * @return mixed
+     */
+    public static function Instance()
+    {
+        return \Qii\Autoloader\Instance::instance('\Qii\Autoloader\Loader');
+    }
+
+    /**
+     * 实现自动加载
+     *
+     * @param $method
+     * @param $argvs
+     * @return object
+     */
+    public function __call($method, $args)
+    {
+        $class = array_shift($args);
+        if ($this->lastCall) {
+            $className = $this->lastCall . '\\' . $method;
+            \Qii\Autoloader\Import::requireByClass($className);
+        } else {
+            $className = $method . '\\' . $class;
+        }
+        $this->lastCall = null;
+        if (isset($this->loaded[$className])) return $this->loaded[$className];
+        return $this->loaded[$className] = call_user_func_array(array('\Qii\Autoloader\Instance', 'Instance'), array_merge(array($className), $args));
+    }
+}

+ 413 - 0
src/Autoloader/Psr4.php

@@ -0,0 +1,413 @@
+<?php
+namespace Qii\Autoloader;
+
+/**
+ * Psr4 规范
+ *
+ */
+class Psr4
+{
+    /**
+     * 将查找过的文件放入缓存
+     */
+    protected static $cachedFiles = array();
+    /**
+     * 是否使用namespace
+     */
+    protected $useNamespace = array();
+    /**
+     * 添加namespace前缀对应的目录,只要是以这个前缀开头的文件都在指定目录中去查找
+     * 前缀可以对应多个目录,找的时候会去遍历数组
+     * @var array
+     */
+    protected $prefixes = array();
+
+    /**
+     * 当前class的初始化
+     */
+    private static $_instance = null;
+
+    /**
+     * @var APP_LOAD_PREFIX 保存类到以APP_LOAD_PREFIX开头的key中
+     */
+    const APP_LOAD_PREFIX = '__qii_psr4_instance';
+    /**
+     * @var $_loadedClass 保存加载过的类
+     */
+    protected static $_loadedClass = array();
+
+    /**
+     * @var $_realpath 将转换后的路径存放到此变量中
+     */
+    protected static $_realpath = array();
+
+    /**
+     * 最后一次没有加载到文件的错误路径
+     * @var array $lastErrorLoadedFile
+     */
+    protected static $lastErrorLoadedFile = array();
+
+    /**
+     * 注册自动加载类
+     *
+     */
+    private function __construct()
+    {
+    }
+
+    /**
+     * 单例模式
+     */
+    public static function getInstance()
+    {
+        if (self::$_instance == null) {
+            self::$_instance = new self();
+        }
+        return self::$_instance;
+    }
+
+    /**
+     * 注册自动加载类
+     *
+     * @return void
+     */
+    public function register()
+    {
+        spl_autoload_register(array($this, 'loadFileByClass'));
+        return $this;
+    }
+
+    /**
+     * Setting is use namespace for class
+     *
+     * @param $prefix 以prefix前缀开头的使用namespace
+     * @param bool $useNamespace
+     * @return object $this
+     */
+    public function setUseNamespace($prefix, $useNamespace = true)
+    {
+        $this->useNamespace[$prefix] = $useNamespace;
+        return $this;
+    }
+
+    /**
+     * Adds a base directory for a namespace prefix.
+     *
+     * @param string $prefix The namespace prefix.
+     * @param string $baseDir A base directory for class files in the
+     * namespace.
+     * @param bool $prepend If true, prepend the base directory to the stack
+     * instead of appending it; this causes it to be searched first rather
+     * than last.
+     * @return void
+     */
+    public function addNamespace($prefix, $baseDir, $prepend = false)
+    {
+        // normalize namespace prefix
+        $prefix = trim($prefix, '\\') . '\\';
+
+        // normalize the base directory with a trailing separator
+        $baseDir = rtrim($baseDir, '/') . DIRECTORY_SEPARATOR;
+        $baseDir = rtrim($baseDir, DIRECTORY_SEPARATOR) . '/';
+
+        // initialize the namespace prefix array
+        if (isset($this->prefixes[$prefix]) === false) {
+            $this->prefixes[$prefix] = array();
+        }
+        if (in_array($baseDir, $this->prefixes[$prefix])) {
+            return $this;
+        }
+        // retain the base directory for the namespace prefix
+        if ($prepend) {
+            array_unshift($this->prefixes[$prefix], $baseDir);
+        } else {
+            array_push($this->prefixes[$prefix], $baseDir);
+        }
+        return $this;
+    }
+
+    /**
+     * 移除某一个namespace下的指定路径
+     * @param string $prefix 前缀
+     * @param string $baseDir 路径
+     * @return array
+     */
+    public function removeNameSpace($prefix, $baseDir)
+    {
+        // normalize namespace prefix
+        $prefix = trim($prefix, '\\') . '\\';
+
+        // normalize the base directory with a trailing separator
+        $baseDir = rtrim($baseDir, '/') . DIRECTORY_SEPARATOR;
+        $baseDir = rtrim($baseDir, DIRECTORY_SEPARATOR) . '/';
+        // initialize the namespace prefix array
+        if (isset($this->prefixes[$prefix]) === false) {
+            return false;
+        }
+        foreach ($this->prefixes[$prefix] AS $key => $dir) {
+            if ($dir == $baseDir) unset($this->prefixes[$prefix][$key]);
+        }
+        return $this->prefixes;
+    }
+
+    /**
+     * 返回namespace路径
+     */
+    public function getNamespace($prefix)
+    {
+        // normalize namespace prefix
+        $prefix = trim($prefix, '\\') . '\\';
+        if (isset($this->prefixes[$prefix])) return $this->prefixes[$prefix];
+        return '';
+    }
+
+    /**
+     * 通过文件名返回路径
+     * @param string $fileName 文件名
+     * @return string
+     */
+    public function getFileByPrefix($fileName)
+    {
+        $fileName = str_replace(array('/', '\\'), DS, $fileName);
+        $prefixes = explode(DS, $fileName, 2);
+        $dirs = isset($this->prefixes['workspace\\']) ? $this->prefixes['workspace\\'] : array();
+        if (count($prefixes) == 2) {
+            if (isset($this->prefixes[$prefixes[0]])) $dirs = $this->prefixes[$prefixes[0]];
+        }
+        foreach ($dirs as $baseDir) {
+            if (is_file($baseDir . DS . $fileName)) {
+                return $baseDir . DS . $fileName;
+            }
+        }
+        return $fileName;
+    }
+
+    /**
+     * 获取指定文件夹路径
+     * @param string $folder 路径
+     * @return string 路径
+     */
+    public function getFolderByPrefix($folder)
+    {
+        $fileName = str_replace(array('/', '\\'), DS, $folder);
+        $prefixes = explode(DS, $fileName, 2);
+        $dirs = isset($this->prefixes['workspace\\']) ? $this->prefixes['workspace\\'] : array();
+        if (count($prefixes) == 2) {
+            if (isset($this->prefixes[$prefixes[0]])) $dirs = $this->prefixes[$prefixes[0]];
+        }
+        foreach ($dirs as $baseDir) {
+            return $baseDir . DS . $folder;
+        }
+        return $folder;
+    }
+    /**
+     * 从Map中获取文件
+     */
+    public function searchMappedFile($class)
+    {
+        $prefix = $class;
+        // work backwards through the namespace names of the fully-qualified
+        // class name to find a mapped file name
+        while (false !== $pos = strrpos($prefix, '\\')) {
+            // retain the trailing namespace separator in the prefix
+            $prefix = substr($class, 0, $pos + 1);
+
+            // the rest is the relative class name
+            $relativeClass = substr($class, $pos + 1);
+
+            // try to load a mapped file for the prefix and relative class
+            $mappedFile = $this->loadMappedFile($prefix, $relativeClass);
+            if ($mappedFile) {
+                return $mappedFile;
+            }
+            $prefix = rtrim($prefix, '\\');
+        };
+        //如果没有找到就在workspace中去找对应的文件 额外添加的方法
+        $mappedFile = $this->loadMappedFile('workspace\\', $class);
+        if ($mappedFile) {
+            return $mappedFile;
+        }
+        return false;
+    }
+    /**
+     * 通过类名加载文件
+     * @param string $class 类名
+     * @return string 文件路径
+     */
+    public function loadFileByClass($class)
+    {
+        // the current namespace prefix
+        //replace "_" to "\" use common method to load class
+        $class = str_replace("_", "\\", $class);
+        if(!$this->searchMappedFile($class))
+        {
+            $notLoaded = isset(self::$lastErrorLoadedFile[$class]) ? self::$lastErrorLoadedFile[$class] : self::getClassName($class);
+            throw new \Qii\Exceptions\FileNotFound(\Qii::i(1405, $notLoaded), __LINE__);
+        }
+    }
+
+    /**
+     * loadClass返回真正的类名
+     *
+     * @param string $class 类名
+     */
+    public function getClassName($class)
+    {
+        // the current namespace prefix
+        //replace "_" to "\" use common method to load class
+        $class = str_replace("_", "\\", $class);
+        if($this->searchMappedFile($class))
+        {
+            return $class;
+        }
+        return  str_replace('\\', '_', $class);
+    }
+
+    /**
+     * Loads the class file for a given class name.
+     *
+     * @param string $class The fully-qualified class name.
+     * @return mixed The mapped file name on success, or boolean false on
+     * failure.
+     */
+    public function loadClass($class)
+    {
+        $args = func_get_args();
+        //去掉第一个斜杠
+        $class = array_shift($args);
+        $class = preg_replace("/^\\\\/", "", $class);
+        $class = $this->getClassName($class);
+        array_unshift($args, $class);
+
+        if (class_exists($class, false)) {
+            return call_user_func_array(array($this, 'instance'), $args);
+        }
+        if ($this->loadFileByClass($class)) {
+            return call_user_func_array(array($this, 'instance'), $args);
+        }
+        throw new \Qii\Exceptions\ClassNotFound(\Qii::i(1103, $class), __LINE__);
+    }
+
+    /**
+     * 调用静态的方法
+     * @param string $class 类名
+     * @param string $method 方法名
+     * @return mixed
+     */
+    public static function loadStatic($class, $method)
+    {
+        $args = func_get_args();
+        $class = \Qii\Autoloader\Psr4::getInstance()->getClassName(array_shift($args));
+        $method = array_shift($args);
+        return call_user_func_array(array($class, $method), $args);
+    }
+
+    /**
+     * 获取文件的绝对路径
+     * @param string $path
+     * @param bool $exists 是否使用realpath
+     * @return string  真实路径
+     */
+    public static function realpath($path)
+    {
+        if (isset(self::$_realpath[$path])) return self::$_realpath[$path];
+        $drive = '';
+        if (OS === 'WIN') {
+            $path = preg_replace('/[\\\\\/]/', DIRECTORY_SEPARATOR, $path);
+            if (preg_match('/(phar\:\\\\|[a-zA-Z]\:)(.*)/', $path, $matches)) {
+                list(, $drive, $path) = $matches;
+            } else {
+                $cwd = getcwd();
+                $drive = substr($cwd, 0, 2);
+                if (substr($path, 0, 1) != DIRECTORY_SEPARATOR) {
+                    $path = substr($cwd, 3) . DIRECTORY_SEPARATOR . $path;
+                }
+            }
+        } elseif (substr($path, 0, 1) != DIRECTORY_SEPARATOR) {
+            $path = getcwd() . DIRECTORY_SEPARATOR . $path;
+        }
+        $stack = array();
+        $parts = explode(DIRECTORY_SEPARATOR, $path);
+        foreach ($parts as $dir) {
+            if (strlen($dir) && $dir !== '.') {
+                if ($dir == '..') {
+                    array_pop($stack);
+                } else {
+                    array_push($stack, $dir);
+                }
+            }
+        }
+        $realPath = str_replace(DIRECTORY_SEPARATOR, '/', $drive . DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $stack));
+        self::$_realpath[$path] = $realPath;
+        return $realPath;
+    }
+
+    /**
+     * Load the mapped file for a namespace prefix and relative class.
+     *
+     * @param string $prefix The namespace prefix.
+     * @param string $relativeClass The relative class name.
+     * @return mixed Boolean false if no mapped file can be loaded, or the
+     * name of the mapped file that was loaded.
+     */
+    protected function loadMappedFile($prefix, $relativeClass)
+    {
+        if (isset(self::$cachedFiles[$prefix . '_' . $relativeClass])) return self::$cachedFiles[$prefix . '_' . $relativeClass];
+        // are there any base directories for this namespace prefix?
+        if (isset($this->prefixes[$prefix]) === false) {
+            //if there any base directories , add self to prefix
+            $this->addNamespace($prefix, $prefix);
+            //return false;
+        }
+        $prefix = trim($prefix, '\\') . '\\';
+        $file = '';
+        // look through base directories for this namespace prefix
+        foreach ($this->prefixes[$prefix] as $baseDir) {
+            $file = str_replace("/", DS, $baseDir
+                . str_replace('\\', DS, $relativeClass)
+                . '.php');
+            // if the mapped file exists, require it
+            if ($this->requireFile($file)) {
+                self::$cachedFiles[$prefix . '_' . $relativeClass] = $file;
+                return $file;
+            }
+        }
+        self::$lastErrorLoadedFile[$relativeClass] = $file;
+        // never found it
+        return false;
+    }
+
+    /**
+     * If a file exists, require it from the file system.
+     *
+     * @param string $file The file to require.
+     * @return bool True if the file exists, false if not.
+     */
+    protected function requireFile($file)
+    {
+        return \Qii\Autoloader\Import::requires($file);
+    }
+
+    /**
+     * instance class
+     * @param string $class
+     * @return object
+     */
+    public function instance()
+    {
+        $args = func_get_args();
+        $class = array_shift($args);
+        $className = $this->getClassName($class);
+        if (isset(self::$_loadedClass[$className])) return self::$_loadedClass[$className];
+        if (!class_exists($className, false)) {
+            throw new \Qii\Exceptions\CallUndefinedClass(\Qii::i('1105', $className), __LINE__);
+        }
+        $refClass = new \ReflectionClass($className);
+        self::$_loadedClass[$className] = $instance = $refClass->newInstanceArgs($args);
+        //如果有_initialize方法就自动调用_initialize方法,并将参数传递给_initialize方法
+        if ($refClass->hasMethod('_initialize')) {
+            call_user_func_array(array($instance, '_initialize'), $args);
+        }
+        return $instance;
+    }
+}

+ 16 - 0
src/Base/Action.php

@@ -0,0 +1,16 @@
+<?php
+/**
+ * Action 
+ */
+namespace Qii\Base;
+
+class Action extends Controller
+{
+    public $controllerId;
+    public $actionId;
+    public $response;
+    public function __construct()
+    {
+
+    }
+}

+ 10 - 0
src/Base/Bootstrap.php

@@ -0,0 +1,10 @@
+<?php
+/**
+ * Bootstrap 启动的时候默认加载
+ */
+namespace Qii\Base;
+
+interface Bootstrap 
+{
+	public function _initialize();
+}

+ 296 - 0
src/Base/Controller.php

@@ -0,0 +1,296 @@
+<?php
+/**
+ * 控制器基类
+ */
+namespace Qii\Base;
+
+use \Qii\Autoloader\Psr4;
+
+use \Qii\Config\Register;
+use \Qii\Config\Consts;
+
+/**
+ * Qii_Controller_Abstract class
+ * @author Zhu Jinhui
+ */
+abstract class Controller
+{
+    /**
+     * @var array $actions action对应的方法列表,设置了的就转发
+     */
+    public $actions = array();
+    /**
+     * @var \Qii\Autoloader\Psr4::getInstance() $load
+     */
+    public $load;
+    /**
+     * @var \Qii\Language\Loader $language
+     */
+    public $language;
+    /**
+     * @var Qii\Controller\Base $controller
+     */
+    public $controller;
+    /**
+     * @var string $controllerId controller名
+     */
+    public $controllerId = 'index';
+    /**
+     * @var string $actionId action名
+     */
+    public $actionId = 'index';
+    /**
+     * @var Qii\Request\Base $request
+     */
+    public $request;
+    /**
+     * @var \Qii\Base\Response $response
+     */
+    public $response;
+    /**
+     * @var Qii_Driver_xxx_Connection
+     */
+    public $db;
+    /**
+     * @var mixed
+     */
+    public $view;
+    /**
+     * @var Qii_Cache_Abslute $cache
+     */
+    public $cache;
+    /**
+     * 是否启用view模块
+     * @var bool
+     */
+    public $enableView = false;
+
+    /**
+     * 是否启用Model
+     * @var bool
+     */
+    public $enableDB = false;
+
+    public function __construct()
+    {
+        $this->load = Psr4::getInstance()->loadClass('\Qii\Autoloader\Loader');
+        $this->request = Psr4::getInstance()->loadClass('\Qii\Request\Http');
+        $this->controllerId = $this->request->controller;
+        $this->actionId = $this->request->action;
+        $this->language = \Qii\Autoloader\Factory::getInstance('\Qii\Language\Loader');
+        $this->response = \Qii\Autoloader\Factory::getInstance('\Qii\Base\Response');
+        $this->cache = new \stdClass();
+        //载入model
+        if ($this->enableDB) {
+            $this->enableDB();
+        }
+        //载入view
+        if ($this->enableView) {
+            $this->view = $this->setView();
+        }
+
+        if (!$this->beforeRun()) {
+            exit();
+        }
+    }
+
+    /**
+     * 启用view后调用初始化View方法
+     */
+    protected function initView()
+    {
+    }
+
+    /**
+     * 设置view
+     *
+     * @param string $engine
+     * @param array $policy
+     * @return mixed
+     */
+    public function setView($engine = 'smarty', $policy = array())
+    {
+        $viewConfigure = \Qii::appConfigure('view');
+        //如果之前实例化过相同的就不再实例化
+        if (!$engine) $engine = $viewConfigure['engine'];
+        $policy = (array)$policy;
+        if (!$policy) {
+            $policy = array_merge($policy, $viewConfigure[$engine]);
+        }
+        $viewEngine = Psr4::getInstance()->loadClass('\Qii\View\Loader');
+        $viewEngine->setView($engine, $policy);
+        $this->view = $viewEngine->getView();
+        if(method_exists($this, 'initView'))
+        {
+            $this->initView();
+        }
+        $this->response->setRender($this->view);
+        return $this->view ;
+    }
+
+    /**
+     * 设置缓存
+     *
+     * @param string $engine 缓存方法
+     * @param array $policy 缓存策略
+     */
+    public function setCache($engine = '', $policy = array())
+    {
+        $engine = $engine == '' ? \Qii::appConfigure('cache') : $engine;
+        $basicPolicy = array(
+            'servers' => $this->getCachePolicy($engine),
+        );
+        if ($basicPolicy['servers']) {
+            $policy = array_merge($basicPolicy, $policy);
+        }
+        $loader = new \Qii\Cache\Loader($engine);
+        return $this->cache->$engine = $loader->initialization($policy);
+    }
+
+    /**
+     * 获取缓存的策略
+     * @param String $cache 缓存的内容
+     * @return multitype:multitype:Ambigous <>
+     */
+    final public function getCachePolicy($cache)
+    {
+        $data = array();
+        if (!$cache) return $data;
+        $cacheInfo = Register::getAppConfigure(Register::get(Consts::APP_INI_FILE), $cache);
+        if (!$cacheInfo) return $data;
+
+        $servers = explode(";", $cacheInfo['servers']);
+        $ports = explode(";", $cacheInfo['ports']);
+        for ($i = 0; $i < count($servers); $i++) {
+            $data[] = array('host' => $servers[$i], 'port' => $ports[$i]);
+        }
+        return $data;
+    }
+    /**
+     * 开启数据库操作
+     */
+    final public function enableDB()
+    {
+        return $this->db = Psr4::getInstance()->loadClass('\Qii\Driver\Model');
+    }
+
+    /**
+     * 获取view
+     *
+     * @return mixed
+     */
+    public function getView()
+    {
+        return $this->view;
+    }
+
+    /**
+     * 设置 response
+     * @param $request
+     */
+    public function setResponse(\Qii\Base\Response $response)
+    {
+        return $this->response = $response;
+    }
+    /**
+     * 设置request
+     * @param $request
+     */
+    public function setRequest(\Qii\Base\Request $request)
+    {
+        return $this->request = $request;
+    }
+
+
+    /**
+     * 只要继承的方法调用parent::__construct()就开始执行
+     * 此方法如果返回false,将不再往下继续执行
+     */
+    protected function beforeRun()
+    {
+        return true;
+    }
+
+    /**
+     * 执行完dispatch后调用
+     */
+    protected function afterRun()
+    {
+        if(!$this->response || !is_object($this->response))
+        {
+            return;
+        }
+        if($this->response instanceof \Qii\Base\Response)
+        {
+            $this->response->response();
+        }
+    }
+
+    /**
+     * 转发
+     * @param String $controller
+     * @param String $action
+     * @return mixed
+     */
+    final public function dispatch($controller, $action)
+    {
+        $this->request->setControllerName($controller);
+        $this->request->setActionName($action);
+        \Qii::getInstance()->dispatcher->setRequest($this->request);
+        return call_user_func_array(array(\Qii::getInstance()->dispatcher, 'dispatch'), func_get_args());
+    }
+
+    /**
+     * 获取当前使用的controller
+     *
+     * @return string
+     */
+    final public function getCurrentController()
+    {
+        return get_called_class();
+    }
+
+    /**
+     * 获取 request 方法
+     * @return Qii_Request_Http
+     */
+    final public function getRequest()
+    {
+        return $this->request;
+    }
+
+    /**
+     * 获取response类
+     * @return mixed
+     */
+    final public function getResponse()
+    {
+        return $this->response;
+    }
+
+    /**
+     * 设置forward
+     * @param string $controller controller名
+     * @param string $action action名
+     */
+    public function setForward($controller, $action)
+    {
+        $this->request->setControllerName($controller);
+        $this->request->setActionName($action);
+        $this->request->setForward(true);
+    }
+
+    /**
+     * afterRun 和 forward 执行
+     */
+    public function __destruct()
+    {
+        $this->afterRun($this->controllerId, $this->actionId);
+        if ($this->request && $this->request->isForward()) {
+            $this->request->setForward(false);
+            \Qii::getInstance()->dispatcher->setRequest($this->request);
+            \Qii::getInstance()->dispatcher->dispatch();
+            $this->request->setDispatched(true);
+        }
+    }
+}

+ 91 - 0
src/Base/Dispatcher.php

@@ -0,0 +1,91 @@
+<?php
+namespace Qii\Base;
+
+use \Qii\Config\Register;
+use \Qii\Config\Consts;
+
+class Dispatcher
+{
+    public $request;
+
+    public $controllerCls = null;
+
+    public $actionCls = null;
+
+    public function __construct()
+    {
+
+    }
+    /**
+     * 设置请求
+     * @param \Qii\Request\Http $request 当前请求
+     */
+    public function setRequest(\Qii\Request\Http $request)
+    {
+        $this->request = $request;
+        return $this;
+    }
+
+    /**
+     * 转发
+     * @param string $controller
+     * @param string $action
+     * @return mixed
+     */
+    public function dispatch($controller = '', $action = '')
+    {
+        $args = func_get_args();
+        $controller = $controller != '' ? $controller : $this->request->getControllerName();
+        $action = $action != '' ? $action : $this->request->getActionName();
+
+        $controllerName = Register::get(Consts::APP_DEFAULT_CONTROLLER_PREFIX) . '_' . $controller;
+        $funcArgs = array();
+        if (count($args) > 2) {
+            $funcArgs = array_slice($args, 2);
+        }
+        array_unshift($funcArgs, $controllerName);
+        $psr4 = \Qii\Autoloader\Psr4::getInstance();
+        $controllerCls = call_user_func_array(array($psr4, 'loadClass'), $funcArgs);
+        $this->controllerCls = $controllerCls;
+        $this->controllerCls->setRequest($this->request);
+        $this->controllerCls->controller = $controllerCls;
+        $this->controllerCls->controllerId = $controller;
+        $this->controllerCls->actionId = $action;
+        $response = null;
+        //查看是否设置了当前action的对应关系,如果设置了就走对应关系里边的,否则走当前类中的
+        if ($this->controllerCls->actions && isset($this->controllerCls->actions[$action]) && $this->controllerCls->actions[$action]) {
+            $actionArgs = array();
+            $actionArgs[] = $this->controllerCls->actions[$action];
+            $actionCls = call_user_func_array(array($psr4, 'loadClass'), $actionArgs);
+            $this->actionCls = $actionCls;
+            $this->actionCls->setRequest($this->request);
+            $this->actionCls->controller = $this->controllerCls;
+            $this->actionCls->actionId = $action;
+            $this->actionCls->controllerId = $this->controllerCls->controllerId;
+            //支持多个action对应到同一个文件,如果对应的文件中存在指定的方法就直接调用
+            if (method_exists($this->actionCls, $action . Register::get(Consts::APP_DEFAULT_ACTION_SUFFIX))) {
+                $this->actionCls->response = $response = call_user_func_array(array($this->actionCls, $action. Register::get(Consts::APP_DEFAULT_ACTION_SUFFIX)), $funcArgs);
+            }
+            if(method_exists($this->actionCls, 'initialization'))
+            {
+                call_user_func_array(array($this->actionCls, 'initialization'), array($this->actionCls));
+            }
+            if (!method_exists($this->actionCls, 'run')) {
+                throw new \Qii\Exceptions\MethodNotFound(\Qii::i(1101, $this->controllerCls->actions[$action] . '->run'), __LINE__);
+            }
+            $response = call_user_func_array(array($this->actionCls, 'run'), $funcArgs);
+        } else {
+            if(method_exists($this->controllerCls, 'initialization'))
+            {
+                call_user_func_array(array($this->controllerCls, 'initialization'), array($this->controllerCls));
+            }
+            array_shift($funcArgs);
+            $actionName = $action . Register::get(Consts::APP_DEFAULT_ACTION_SUFFIX);
+            if (!method_exists($this->controllerCls, $actionName) && !method_exists($this->controllerCls, '__call')) {
+                throw new \Qii\Exceptions\MethodNotFound(\Qii::i(1101, $controller . '->' . $actionName), __LINE__);
+            }
+            $this->controllerCls->response = $response = call_user_func_array(array($this->controllerCls, $actionName), $funcArgs);
+        }
+        return $response;
+    }
+}

+ 623 - 0
src/Base/Request.php

@@ -0,0 +1,623 @@
+<?php
+namespace Qii\Base;
+
+abstract class Request
+{
+    const SCHEME_HTTP = 'http';
+    const SCHEME_HTTPS = 'https';
+    public $host = '';
+    public $module;
+    public $controller;
+    public $action;
+    public $method;
+
+    public $dispatcher;
+
+
+    protected $params;
+
+    protected $language;
+
+    protected $_exception;
+
+    protected $_baseUri = '';
+
+    protected $uri = '';
+
+    protected $dispatched = false;
+
+    protected $routed = false;
+
+    protected $forward = false;
+    /**
+     * @var Qii_Request_Url url
+     */
+    public $url;
+    /**
+     * 初始化参数,获取链接中对应的参数并存放到$this->params中
+     *
+     */
+    public function __construct()
+    {
+        foreach ($_GET AS $key => $val) {
+            $this->setParam($key, $val);
+        }
+        $rewriteRule = \Qii::getInstance()->appConfigure(\Qii\Config\Consts::APP_SITE_METHOD);
+        $this->url = new \Qii\Request\Url($rewriteRule);
+        $this->host = IS_CLI ? '' : $_SERVER['HTTP_HOST'];
+        $params = (array)$this->url->getParams();
+        if(count($params) > 0) $this->params = array_merge($this->params, $params);
+        //处理url中的数据
+        if(ucwords($rewriteRule) == 'Short'){
+            $this->setControllerName(
+                    isset($this->params[0]) && $this->params[0] != '' ?
+                        $this->params[0] :
+                        $this->defaultController());
+            $this->setActionName(
+                    isset($this->params[1]) && $this->params[1] != '' ?
+                        $this->params[1] :
+                        $this->defaultAction());
+        }
+        return $this;
+    }
+    /**
+     * 获取POST数据
+     */
+    public function post($name = null, $default = null)
+    {
+        return call_user_func_array(array($this->url, 'post'), func_get_args());
+    }
+    /**
+     * 默认controller
+     */
+    public function defaultController()
+    {
+        return \Qii\Config\Register::get(\Qii\Config\Consts::APP_DEFAULT_CONTROLLER, 'index');
+    }
+    /**
+     * 默认action
+     */
+    public function defaultAction()
+    {
+        return \Qii\Config\Register::get(\Qii\Config\Consts::APP_DEFAULT_ACTION, 'index');
+    }
+
+    /**
+     * 获取当前数据处理
+     */
+    public function uri()
+    {
+        return $this->uri;
+    }
+
+    public function redirect($url)
+    {
+        ob_clean();
+        header('Location:'. $url);
+    }
+
+    /**
+     * isGet
+     *
+     * @param void
+     * @return boolean
+     */
+    public function isGet()
+    {
+        return (strtoupper($this->method) == 'GET');
+    }
+
+    /**
+     * isPost
+     *
+     * @param void
+     * @return boolean
+     */
+    public function isPost()
+    {
+        return (strtoupper($this->method) == 'POST');
+    }
+
+    /**
+     * isPut
+     *
+     * @param void
+     * @return boolean
+     */
+    public function isPut()
+    {
+        return (strtoupper($this->method) == 'PUT');
+    }
+
+    /**
+     * isHead
+     *
+     * @param void
+     * @return boolean
+     */
+    public function isHead()
+    {
+        return (strtoupper($this->method) == 'HEAD');
+    }
+
+    /**
+     * isOptions
+     *
+     * @param void
+     * @return boolean
+     */
+    public function isOptions()
+    {
+        return (strtoupper($this->method) == 'OPTIONS');
+    }
+
+    /**
+     * isCli
+     *
+     * @param void
+     * @return boolean
+     */
+    public function isCli()
+    {
+        return (strtoupper($this->method) == 'CLI');
+    }
+
+    /**
+     * isXmlHttpRequest
+     *
+     * @param void
+     * @return boolean
+     */
+    public function isXmlHttpRequest()
+    {
+        return false;
+    }
+
+    /**
+     * getServer
+     *
+     * @param string $name
+     * @param mixed $default
+     * @return mixed
+     */
+    public function getServer($name = null, $default = null)
+    {
+        if (is_null($name)) {
+            return $_SERVER;
+        } elseif (isset($_SERVER[$name])) {
+            return $_SERVER[$name];
+        }
+        return $default;
+    }
+
+    /**
+     * getEnv
+     *
+     * @param string $name
+     * @param mixed $default
+     * @return mixed
+     */
+    public function getEnv($name = null, $default = null)
+    {
+        if (is_null($name)) {
+            return $_ENV;
+        } elseif (isset($_ENV[$name])) {
+            return $_ENV[$name];
+        }
+        return $default;
+    }
+
+    /**
+     * setParam
+     *
+     * @param mixed $name
+     * @param mixed $value
+     * @return boolean | Qii_Request_Abstract
+     */
+    public function setParam($name, $value = null)
+    {
+        if (is_null($value)) {
+            if (is_array($name)) {
+                $this->params = array_merge($this->params, $name);
+                return $this;
+            }
+        } elseif (is_string($name)) {
+            $this->params[$name] = $value;
+            return $this;
+        }
+        return false;
+    }
+
+    /**
+     * getParam
+     *
+     * @param string $name
+     * @param mixed $default
+     * @return mixed
+     */
+    public function getParam($name, $dafault = null)
+    {
+        if (isset($this->params[$name])) {
+            return $this->params[$name];
+        }
+        return $dafault;
+    }
+
+    /**
+     * setParams
+     *
+     * @param array
+     * @return boolean | Qii_Request_Abstract
+     */
+    public function setParams($params)
+    {
+        if (is_array($params)) {
+            $this->params = $params;
+            return $this;
+        }
+        return false;
+    }
+
+    /**
+     * getParams
+     *
+     * @param void
+     * @return array
+     */
+    public function getParams()
+    {
+        return $this->params;
+    }
+
+    /**
+     * setException
+     *
+     * @param Exception $exception
+     * @return boolean | Qii_Request_Abstract
+     */
+    public function setException($exception)
+    {
+        if (is_object($exception)
+            && ($exception instanceof Exception)
+        ) {
+            $this->_exception = $exception;
+            return $this;
+        }
+        return false;
+    }
+
+    /**
+     * getException
+     *
+     * @param void
+     * @return Exception
+     */
+    public function getException()
+    {
+        if (is_object($this->_exception)
+            && ($this->_exception instanceof Exception)
+        ) {
+            return $this->_exception;
+        }
+        return null;
+    }
+
+
+    /**
+     * getModuleName
+     *
+     * @param void
+     * @return string
+     */
+    public function getModuleName()
+    {
+        return $this->module;
+    }
+
+    /**
+     * getControllerName
+     *
+     * @param void
+     * @return string
+     */
+    public function getControllerName()
+    {
+        return $this->controller;
+    }
+
+    /**
+     * getActionName
+     *
+     * @param void
+     * @return string
+     */
+    public function getActionName()
+    {
+        return $this->action;
+    }
+
+    /**
+     * setModuleName
+     *
+     * @param string $name
+     * @return boolean | Qii_Request_Abstract
+     */
+    public function setModuleName($name)
+    {
+        if (!is_string($name)) {
+            trigger_error('Expect a string module name', E_USER_WARNING);
+            return false;
+        }
+        $this->module = $name;
+        return $this;
+    }
+
+    /**
+     * setControllerName
+     *
+     * @param string $name
+     * @return boolean | Qii_Request_Abstract
+     */
+    public function setControllerName($name)
+    {
+        if (!is_string($name)) {
+            trigger_error('Expect a string controller name', E_USER_WARNING);
+            return $this;
+        }
+        $this->controller = $name;
+        return $this;
+    }
+
+    /**
+     * setActionName
+     *
+     * @param string $name
+     * @return boolean | Qii_Request_Abstract
+     */
+    public function setActionName($name)
+    {
+        if (!is_string($name)) {
+            trigger_error('Expect a string action name', E_USER_WARNING);
+            return false;
+        }
+        $this->action = $name;
+        return $this;
+    }
+
+    /**
+     * getMethod
+     *
+     * @param void
+     * @return string
+     */
+    public function getMethod()
+    {
+        return $this->method;
+    }
+
+    /**
+     * getLanguage
+     *
+     * @param void
+     * @return string
+     */
+    public function getLanguage()
+    {
+        return $this->language;
+    }
+
+    /**
+     * setBaseUri
+     *
+     * @param string $baseUri
+     * @return boolean | Qii_Request_Abstract
+     */
+    public function setBaseUri($baseUri)
+    {
+        if ($baseUri && is_string($baseUri)) {
+            $this->_baseUri = $baseUri;
+            return $this;
+        }
+        return false;
+    }
+
+    /**
+     * getBaseUri
+     *
+     * @param void
+     * @return string
+     */
+    public function getBaseUri()
+    {
+        return $this->_baseUri;
+    }
+
+    /**
+     * setRequestUri
+     *
+     * @param string $uri
+     * @return boolean | Qii_Request_Abstract
+     */
+    public function setRequestUri($uri)
+    {
+        if (is_string($uri)) {
+            $this->uri = $uri;
+            return $this;
+        }
+        return false;
+    }
+
+    /**
+     * getRequestUri
+     *
+     * @param void
+     * @return string
+     */
+    public function getRequestUri()
+    {
+        return $this->uri;
+    }
+
+    /**
+     * isDispatched
+     *
+     * @param void
+     * @return boolean
+     */
+    public function isDispatched()
+    {
+        return (boolean)$this->dispatched;
+    }
+
+    /**
+     * setDispatched
+     *
+     * @param boolean $flag
+     * @return boolean | Qii_Request_Abstract
+     */
+    public function setDispatched($flag = true)
+    {
+        if (is_bool($flag)) {
+            $this->dispatched = $flag;
+            return $this;
+        }
+        return false;
+    }
+
+    /**
+     * 设置dispatcher
+     *
+     * @param Qii_Controller_Dispatcher $dispatcher
+     */
+    public function setDispatcher(\Qii\Base\Dispatcher $dispatcher)
+    {
+        $this->dispatcher = $dispatcher;
+    }
+
+    /**
+     * isRouted
+     *
+     * @param void
+     * @return boolean
+     */
+    public function isRouted()
+    {
+        return $this->routed;
+    }
+
+    /**
+     * setRouted
+     *
+     * @param boolean $flag
+     * @return boolean | Qii_Request_Abstract
+     */
+    public function setRouted($flag = true)
+    {
+        if (is_bool($flag)) {
+            $this->routed = $flag;
+            return $this;
+        }
+        return false;
+    }
+    /**
+     * 是否需要转发
+     */
+    public function isForward()
+    {
+        return $this->forward;
+    }
+    /**
+     * 设置是否需要转发
+     * @param bool $flag
+     * @return $this
+     */
+    public function setForward($flag = true)
+    {
+        if (is_bool($flag)) {
+            $this->forward = $flag;
+            return $this;
+        }
+        return $this;
+    }
+
+    /**
+     * __setbaseUri
+     *
+     * @param string $baseUri
+     * @param string $request_uri
+     * @return boolean
+     */
+    protected function _setbaseUri($baseUri, $request_uri = null)
+    {
+        if ($baseUri && is_string($baseUri)) {
+            $this->_baseUri = $baseUri;
+
+            return true;
+        } elseif ($request_uri && is_string($request_uri)) {
+            $scriptFileName = $this->getServer('SCRIPT_FILENAME');
+
+            do {
+                if ($scriptFileName && is_string($scriptFileName)) {
+                    $fileName = basename($scriptFileName, \Qii::getInstance()->appConfigure('ext', '.php'));
+                    $fileNameLen = strlen($fileName);
+
+                    $script_name = $this->getServer('SCRIPT_NAME');
+                    if ($script_name && is_string($script_name)) {
+                        $script = basename($script_name);
+
+                        if (strncmp($fileName, $script, $fileNameLen) == 0) {
+                            $basename = $script_name;
+                            break;
+                        }
+                    }
+
+                    $phpself_name = $this->getServer('PHP_SELF');
+                    if ($phpself_name && is_string($phpself_name)) {
+                        $phpself = basename($phpself_name);
+                        if (strncmp($fileName, $phpself, $fileNameLen) == 0) {
+                            $basename = $phpself_name;
+                            break;
+                        }
+                    }
+
+                    $orig_name = $this->getServer('ORIG_SCRIPT_NAME');
+                    if ($orig_name && is_string($orig_name)) {
+                        $orig = basename($orig_name);
+                        if (strncmp($fileName, $orig, $fileNameLen) == 0) {
+                            $basename = $orig_name;
+                            break;
+                        }
+                    }
+                }
+            } while (0);
+
+            if ($basename && strstr($request_uri, $basename) == $request_uri) {
+                $this->_baseUri = rtrim($basename, '/');
+
+                return true;
+            } elseif ($basename) {
+                $dirname = rtrim(dirname($basename), '/');
+                if ($dirname) {
+                    if (strstr($request_uri, $dirname) == $request_uri) {
+                        $this->_baseUri = $dirname;
+
+                        return true;
+                    }
+                }
+            }
+
+            $this->_baseUri = '';
+
+            return true;
+        }
+
+        return false;
+    }
+    /**
+     * 对于不存在的方法默认调用url中的方法
+     */
+    public function __call($method, $args)
+    {
+        return call_user_func_array(array($this->url, $method), $args);
+    }
+}

+ 306 - 0
src/Base/Response.php

@@ -0,0 +1,306 @@
+<?php
+namespace Qii\Base;
+
+class Response
+{
+    /**
+     * Default body name
+     */
+    const DEFAULT_BODY = 'html';
+    const FORMAT_JSON = 'json';
+    const FORMAT_HTML = 'html';
+
+    public static $render = null;
+
+    /**
+     * Body content
+     * @var array
+     */
+    protected $body = array();
+    
+    /**
+     * data 
+     * @param array $data
+     */
+    protected $data = array();
+    /**
+     * Array of headers. Each header is an array with keys 'name' and 'value'
+     * @var array
+     */
+    protected $headers = array();
+
+    /**
+     * Determine to send the headers or not
+     * @var unknown_type
+     */
+    protected $_sendHeader = false;
+	
+    
+	public function __construct($data = array())
+	{
+        $this->format = isset($data['format']) ? isset($data['format']) : self::FORMAT_HTML;
+		$this->data = $data;
+	}
+    /**
+     * 设置页面渲染类
+     * @param \Qii\View\Intf $render 渲染类
+     */
+    public function setRender(\Qii\View\Intf $render)
+    {
+        \Qii\Base\Response::$render = $render;
+    }
+
+    /**
+     * Append content to the body content
+     *
+     * @param string $content
+     * @param string $key
+     * @return Qii_Response_Abstract
+     */
+    public function appendBody($body, $key = NULL)
+    {
+        if (!strlen($key)) {
+            $key = self::DEFAULT_BODY;
+        }
+        if (!isset($this->body[$key])) {
+            $this->body[$key] = '';
+        }
+        $this->body[$key] .= (string) $body;
+        return $this;
+    }
+
+    /**
+     * Clear the entire body
+     *
+     * @param string $key
+     * @return boolean
+     */
+    public function clearBody($key = NULL)
+    {
+        if (strlen($key)) {
+            if (array_key_exists($key, $this->body)) {
+                unset($this->body[$key]);
+            }
+        } else {
+            $this->body = array();
+        }
+        return true;
+    }
+
+    /**
+     * Clear headers
+     *
+     * @return Qii\Response\Abstract
+     */
+    public function clearHeaders()
+    {
+        $this->headers = array();
+        return $this;
+    }
+
+    /**
+     * Return the body content
+     *
+     * @param string $key
+     * @return string
+     */
+    public function getBody($key = NULL)
+    {
+        if (!strlen($key)) {
+            $key = self::DEFAULT_BODY;
+        }
+        return array_key_exists($key, $this->body) ? $this->body[$key] : null;
+    }
+
+    /**
+     * Return array of headers; see {@link $headers} for format
+     *
+     * @return array
+     */
+    public function getHeader()
+    {
+        return $this->headers;
+    }
+
+    /**
+     * Prepend content the body
+     *
+     * @param string $body
+     * @param string $key
+     * @return Qii_Response_Abstract
+     */
+    public function prependBody($body, $key = null)
+    {
+        if (!strlen($key)) {
+            $key = self::DEFAULT_BODY;
+        }
+        if (!isset($this->body[$key])) {
+            $this->body[$key] = '';
+        }
+        $this->body[$key] = $body . $this->body[$key];
+        return $this;
+    }
+
+    /**
+     * Send the response, including all headers
+     *
+     * @return void
+     */
+    public function response()
+    {
+        if($this->data && isset($this->data['body']))
+        {
+            switch($this->data['format'])
+            {
+                case self::FORMAT_JSON:
+                    $this->setHeader('Content-Type', 'text/json');
+                    $this->sendHeaders();
+                    echo json_encode($this->data['body'], JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP | JSON_UNESCAPED_UNICODE);
+                break;
+                default:
+					$body = $this->data['body'];
+					if(is_array($this->data['body'])) {
+						$body = '';
+						if(isset($this->data['body']['render']) && $this->data['body']['render'] instanceof  \Qii\View\Intf)
+						{
+                            \Qii\Base\Response::$render = $this->data['body']['render'];
+						}
+
+                        if(\Qii\Base\Response::$render != null && \Qii\Base\Response::$render instanceof  \Qii\View\Intf)
+                        {
+                            $tplData = isset($this->data['body']['tplData']) ? $this->data['body']['tplData'] : [];
+                            \Qii\Base\Response::$render->assign($tplData);
+                            $body = \Qii\Base\Response::$render->fetch($this->data['body']['tpl']);
+                        }
+					}
+                    echo (IS_CLI ? (new \Qii\Response\Cli())->stdout($body) : $body);
+                break;
+            }
+            return;
+        }
+        if ($this->_sendHeader == true) {
+            $this->sendHeaders();
+        }
+        foreach ($this->body as $key => $body) {
+            echo IS_CLI ? new \Qii\Response\Cli($body) : $body;
+        }
+    }
+
+    public function setAllHeaders()
+    {
+        return false;
+    }
+    /**
+     * Set body content
+     *
+     * @param string $body
+     * @param string $key
+     * @return Qii_Response_Abstract
+     */
+    public function setBody($body, $key = NULL)
+    {
+        if (!strlen($key)) {
+            $key = self::DEFAULT_BODY;
+        }
+        $this->body[$key] = (string) $body;
+        return $this;
+    }
+
+    /**
+     * Set a header
+     *
+     * If $replace is true, replaces any headers already defined with that
+     * $name.
+     *
+     * @param string $name
+     * @param string $value
+     * @param boolean $replace
+     * @return Qii_Response_Abstract
+     */
+    public function setHeader($name, $value, $replace = false)
+    {
+        $name  = $this->_normalizeHeader($name);
+        $value = (string) $value;
+
+        if ($replace) {
+            foreach ($this->headers as $key => $header) {
+                if ($name == $header['name']) {
+                    unset($this->headers[$key]);
+                }
+            }
+        }
+
+        $this->headers[] = array(
+            'name'    => $name,
+            'value'   => $value,
+            'replace' => $replace
+        );
+
+        return $this;
+    }
+
+    /**
+     * Set redirect URL
+     *
+     * Sets Location header. Forces replacement of any prior redirects.
+     *
+     * @param string $url
+     * @return Qii_Response_Abstract
+     */
+    public function setRedirect($url)
+    {
+        $this->setHeader('Location', $url, true);
+        return $this;
+    }
+
+    /**
+     * Magic __toString functionality
+     *
+     * Returns response value as string
+     * using output buffering.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        ob_start();
+        $this->response();
+        return ob_get_clean();
+    }
+
+    /**
+     * Normalize a header name
+     *
+     * Normalizes a header name to X-Capitalized-Names
+     *
+     * @param  string $name
+     * @return string
+     */
+    protected function _normalizeHeader($name)
+    {
+        $filtered = str_replace(array('-', '_'), ' ', (string) $name);
+        $filtered = ucwords(strtolower($filtered));
+        $filtered = str_replace(' ', '-', $filtered);
+        return $filtered;
+    }
+
+    /**
+     * Send all headers
+     *
+     * Sends any headers specified.
+     * If an {@link setHttpResponseCode() HTTP response code}
+     * has been specified, it is sent with the first header.
+     *
+     * @return Qii_Response_Abstract
+     */
+    protected function sendHeaders()
+    {
+        foreach ($this->headers as $header) {
+            header(
+                $header['name'] . ': ' . $header['value'],
+                $header['replace']
+            );
+        }
+        return $this;
+    }
+}

+ 343 - 0
src/Base/Rules.php

@@ -0,0 +1,343 @@
+<?php
+namespace Qii\Base;
+class Rules 
+{
+    /**
+     * @var array $rules 验证规则
+     */
+    private $rules = array();
+    /**
+     * @var array $message 验证消息
+     */
+    private $message = array();
+    /**
+     * @var array $data 待验证的数据
+     */
+    private $data = array();
+    /**
+     * 需要验证的字段
+     */
+    private $forceValidKey = array();
+    /**
+     * 选择其中一个去验证
+     */
+    private $optionValidKey = array();
+    /**
+     * 字段名称
+     */
+    private $files = array();
+
+    public function __construct()
+    {
+        $this->constants();
+        $this->clean();
+    }
+
+    public function getDefaultValues()
+    {
+        $data = array();
+        foreach($this->fields() AS $key => $val)
+        {
+            $data[$val] = '';
+        }
+        return $data;
+    }
+
+    /**
+     * 返回数据表中的字段名
+     *
+     * @return array
+     */
+    public function fields()
+    {
+        return $this->fields;
+    }
+    /**
+     * 添加字段
+     * @param string | array $fields 字段名
+     */
+    public function addFields($fields)
+    {
+        if(empty($fields)) return;
+        if(is_array($fields))
+        {
+            return array_map(function ($n) {
+                return $this->addFields($n);
+            }, $fields);
+        }
+        $this->fields[] = $fields;
+    }
+    /**
+     * 定义规则
+     */
+    public function constants()
+    {
+        return array();
+    }
+
+    /**
+     * 获取所有字段中的数据
+     *
+     * @return array
+     */
+    public function getValues()
+    {
+        return $this->data;
+    }
+
+    /**
+     * 获取制定字段数据
+     *
+     * @param string $field 字段名
+     * @return mixed|null
+     */
+    public function getValue($field)
+    {
+        if(isset($this->data[$field])) return $this->data[$field];
+        return null;
+    }
+    /**
+     * 清空数据
+     */
+    public function clean()
+    {
+        $this->data = array();
+        $this->fields = array();
+        $this->forceValidKey = array();
+    }
+    /**
+     * 获取指定字段的规则配置
+     * @param string $field
+     * @return array
+     */
+    public function get($field)
+    {
+        $data = array();
+        if(isset($this->rules[$field]))
+        {
+            $data['rules'] = $this->rules[$field];
+            $data['message'] = isset($this->message[$field]) ? $this->message[$field] : '未设置';
+        }
+        return $data;
+    }
+    /**
+     * 自动获取rules中定义的规则
+     * @param string $method 方法名
+     */
+    public function autoForceValidKeyForMethod($method)
+    {
+        if(!$method) throw new Exception(__METHOD__ . ' parameter error.', 1);
+        if(method_exists($this, $method))
+        {
+            $this->$method();
+        }
+        if(!$method) throw new Exception(__METHOD__ . ' undefined.', 1);
+    }
+    /**
+     * 添加规则
+     */
+    public function addRules($field, $key, $isValid, $message)
+    {
+        if(!$field || !$key || $isValid === null || $isValid === '') return;
+        if(!$this->isAllow($key)) return;
+        $this->rules[$field][$key] = $isValid;
+        $this->message[$field][$key] = $message;
+    }
+    /**
+     * 移除规则
+     * @param string $fields 字段名
+     */
+    public function removeRules($fields)
+    {
+        if(!$fields) return;
+        if(is_array($fields))
+        {
+            return array_map(function ($n) {
+                return $this->removeRules($n);
+            }, $fields);
+        }
+        if(isset($this->rules[$fields])) 
+        {
+            unset($this->rules[$fields]);
+            unset($this->message[$fields]);
+        }
+    }
+    /**
+     * 添加必须验证用的字段
+     * @param string $key 字段名
+     */
+    public function addForceValidKey($key)
+    {
+        if(!$key) return;
+        if(is_array($key))
+        {
+            foreach ($key as $k => $value) 
+            {
+                $this->addForceValidKey($value);
+            }
+        }
+        else
+        {
+            if(!in_array($key, $this->forceValidKey))
+            {
+                $this->forceValidKey[] = $key;
+            }
+        }
+    }
+
+    public function removeForceValidKey($key)
+    {
+        foreach ($this->forceValidKey as $key => $value) 
+        {
+            if($value == $key)
+            {
+                unset($this->forceValidKey);
+            }
+        }
+    }
+    /**
+     * 添加必须其中某一个字段,选择不为空的字段去验证
+     * 
+     * @param string $key 字段名
+     */
+    public function addOptionValidKey($key)
+    {
+        if(!$key) return;
+        if(is_array($key))
+        {
+            foreach ($key as $k => $value) 
+            {
+                $this->addOptionValidKey($value);
+            }
+        }
+        else
+        {
+            if(!in_array($key, $this->optionValidKey))
+            {
+                $this->optionValidKey[] = $key;
+            }
+        }
+    }
+    /**
+     * 给数据添加属性
+     * @param string $field 字段
+     * @param mix $val 值
+     */
+    public function addValue($field, $val)
+    {
+        if(in_array($field, $this->fields())){
+            $this->data[$field] = $val;
+        }
+    }
+    /**
+     * 添加数据
+     * @param array $data 数据
+     */
+    public function addValues($data)
+    {
+        foreach ($data AS $field => $value){
+            $this->addValue($field, $value);
+        }
+    }
+    /**
+     * 验证数据,验证将返回数据及验证结果
+     * @return bool
+     */
+    public function verify()
+    {
+        $data = array();
+        $data['data'] = $this->data;
+        $data['code'] = 0;
+        $data['valid'] = true;
+        $data['msg'] = '';
+        if(empty($this->forceValidKey))
+        {
+            return $data;
+        }
+        $valid = \_loadClass('\Qii\Library\Validate');
+        //将optionValidKey中不为空的字段添加到必须验证的字段中去
+        //如果选择验证的都没数据就提示参数错误
+        $options = array();
+        foreach($this->optionValidKey AS $key)
+        {
+            if($this->data[$key] && $this->data[$key] != '')
+            {
+                $options[] = $key;
+                $this->addForceValidKey($key);
+            }
+        }
+        if(count($this->optionValidKey) > 0 && count($options) == 0)
+        {
+                $data['valid'] = false;
+                $data['code'] = 20000;
+                $data['errorInfo'][] = join(',', $this->optionValidKey) . ' 字段必须填写一个';
+                $data['msg'] = _i($data['code']);
+                return $data;
+        }
+        foreach($this->forceValidKey AS $key)
+        {
+            $rule = $this->get($key);
+            if(!$rule)
+            {
+                $rule['rules'] = array('required' => true);
+                $rule['message'] = array('required' => $key .'不能为空');
+            }
+            $result = $valid->verify(
+                array($key => isset($this->data[$key]) ? $this->data[$key] : ''),
+                array($key => $rule['rules']),
+                array($key => $rule['message'])
+            );
+            if($result !== true){
+                $data['valid'] = false;
+                $data['code'] = 20000;
+                $data['errorInfo'][$key] = $result;
+            }
+        }
+        if($data['code'] > 0)
+        {
+            $data['msg'] = _i($data['code']);
+        }
+        return $data;
+    }
+    /**
+     * 验证数据,验证将返回数据及验证结果
+     * @return bool
+     */
+     /*
+    public function verify()
+    {
+        $data = array();
+        $data['data'] = array();
+        $valid = _loadClass('Qii_Library_Validate');
+        foreach($this->data AS $key => $val)
+        {
+            $rule = $this->get($key);
+            $data['data'][$key] = $val;
+            if(empty($rule))
+            {
+                continue;
+            }
+            $result = $valid->verify(array($key => $val), array($key => $rule['rules']), array($key => $rule['message']));
+            if($result !== true){
+                $data['valid'][$key] = $result;
+            }
+        }
+        return $data;
+    }*/
+    /**
+     * 是否在允许的规则内
+     * @param string $key 规则名称
+     * @return bool
+     */
+    public function isAllow($key)
+    {
+        $allow = array(
+            'required', 'email', 'idcode', 'http',
+            'qq', 'postcode', 'ip', 'phone', 'telephone',
+            'mobile', 'en', 'cn', 'account', 'number', 'date',
+            'safe', 'password', 'maxlength', 'minlength', 'length',
+            'rangeof', 'string', 'sets', 'setsArray'
+        );
+        return in_array($key, $allow);
+    }
+}

+ 159 - 0
src/Cache/File.php

@@ -0,0 +1,159 @@
+<?php
+/**
+ * @author Jinhui Zhu<jinhui.zhu@live.cn> 2015-10-26 21:44
+ *
+ * Useage:
+ * 在control.php中使用
+ * $this->setCache('file', array('path' => 'tmp'));
+ * $this->cache->set(id, data, policy);
+ * $this->cache->get(id);
+ * $this->cache->remove(id);
+ */
+class Qii_Cache_File implements Qii_Cache_Intf
+{
+    const VERSION = '1.2';
+    public $policy = array('path' => 'tmp', 'life_time' => 3600, 'prefix' => 'file');//设置目录、过期时间、文件前缀
+    public $exclude = array();
+
+    public function __construct(array $policy = null)
+    {
+        if (!empty($policy)) {
+            $this->policy = array_merge($this->policy, $policy);
+        }
+        $this->exclude = array();
+        $this->exclude[] = Qii_DIR;
+    }
+
+    /**
+     * 初始化 将规则合并
+     *
+     * @param array|null $policy
+     */
+    public function initialization(array $policy = null)
+    {
+        if (!empty($policy)) {
+            $this->policy = array_merge($this->policy, $policy);
+        }
+    }
+
+    /**
+     * 检查是否可以保存到指定的目录
+     *
+     */
+    public function checkIsSave()
+    {
+        if (!is_dir($this->policy['path'])) mkdir($this->policy['path'], 0777);
+        if (is_dir($this->policy['path'])) {
+            $this->policy['path'] = \Qii_Autoloader_Psr4::realpath($this->policy['path']);
+        } else {
+            \Qii::setError(false, __LINE__, 1401, $this->policy['path']);
+        }
+        //如果在系统目录就不让保存
+        if (in_array($this->policy['path'], $this->exclude)) {
+            throw  new \Qii_Execptions_AccessDenied($this->policy['path']);
+        }
+    }
+
+    /**
+     * 获取文件名称
+     *
+     * @param String $id
+     * @return String
+     */
+    public function getFileName($id)
+    {
+        $fileName = $this->policy['path'] . '/' . $this->policy['prefix'] . '.' . $id . '.' . (time() + $this->policy['life_time']);
+        $fileName = $fileName . '.' . md5($fileName);
+        return $fileName;
+    }
+
+    /**
+     * 检查文件是否存在
+     *
+     * @param Int $id
+     */
+    public function scanFile($id)
+    {
+        $fileArray = glob($this->policy['path'] . '/' . $this->policy['prefix'] . '.' . $id . '.*');
+        return $fileArray;
+    }
+
+    /**
+     * 缓存数据
+     *
+     * @param $id
+     * @param $data
+     * @param array|null $policy
+     * @return bool|int|void
+     * @throws AccessDeniedExecption
+     */
+    public function set($id, $data, array $policy = null)//设置
+    {
+        if (!empty($policy)) {
+            $this->policy = array_merge($this->policy, $policy);
+        }
+        $this->checkIsSave();
+        $fileName = $this->getFileName($id);
+        //检查文件是否存在,存在就先删除再保存
+        $this->remove($id);
+        return file_put_contents($fileName, serialize($data), LOCK_EX);
+    }
+
+    /**
+     * 获取指定key的缓存
+     *
+     * @param $id
+     * @return mixed|void
+     * @throws AccessDeniedExecption
+     */
+    public function get($id)
+    {
+        $this->checkIsSave();
+        $fileArray = glob($this->policy['path'] . '/' . $this->policy['prefix'] . '.' . $id . '.*');
+        //检查文件是否存在
+        if (count($fileArray) == 0) {
+            return;
+        }
+        $fileName = $fileArray[0];
+        //检查文件是否过期,如果过期就返回空
+        $fileInfo = explode(".", $fileName);
+        $time = $fileInfo[count($fileInfo) - 2];
+        if ($this->policy['life_time'] > 0 && $time < time()) {
+            return;
+        }
+        return unserialize(file_get_contents($fileName));
+    }
+
+    /**
+     * 移除指定key的缓存
+     *
+     * @param $key
+     */
+    public function remove($key)
+    {
+        $fileArray = $this->scanFile($key);//检查文件是否存在,存在就先删除再保存
+        foreach ($fileArray AS $file) {
+            unlink($file);
+        }
+    }
+
+    /**
+     * 清除所有缓存
+     *
+     * @throws AccessDeniedExecption
+     */
+    public function clean()
+    {
+        $this->checkIsSave();
+        //禁止清除Qii目录文件
+        $handle = opendir($this->policy['path']);
+        if ($handle) {
+            while ($file = readdir($handle)) {
+                if (is_file($this->policy['path'] . '/' . $file)) {
+                    unlink($this->policy['path'] . '/' . $file);
+                }
+            }
+        }
+        closedir($handle);
+    }
+}

+ 21 - 0
src/Cache/Intf.php

@@ -0,0 +1,21 @@
+<?php
+/**
+ * 缓存类接口文件,统一所有缓存类的方法
+ *
+ * @author Jinhui.zhu    <jinhui.zhu@live.cn>
+ *
+ */
+namespace Qii\Cache;
+
+interface Intf
+{
+    public function __construct(array $policy = null);
+
+    public function set($id, $data, array $policy = null);//设置
+
+    public function get($id);//获取指定key的缓存
+
+    public function remove($key);//移除指定key的缓存
+
+    public function clean();//清除所有缓存
+}

+ 44 - 0
src/Cache/Loader.php

@@ -0,0 +1,44 @@
+<?php
+/**
+ * 缓存类
+ *
+ * @author Zhu Jinhui<zhujinhui@zhangyue.com>2015-10-31 22:35
+ */
+namespace Qii\Cache;
+
+class Loader
+{
+    const VERSION = '1.2';
+    private $cache;
+
+    public function __construct($cache)
+    {
+        $this->setCache($cache);
+    }
+
+    /**
+     * 初始化缓存类
+     *
+     * @param Array $policy
+     * @return Object
+     */
+    public function initialization($policy)
+    {
+        return \Qii\Autoloader\Psr4::getInstance()->loadClass('Qii\Cache\\' . ucwords($this->cache), $policy);
+    }
+
+    /**
+     * 设置用于缓存的类
+     *
+     * @param String $cache
+     */
+    public function setCache($cache)
+    {
+        $this->cache = $cache;
+        $cacheFile = dirname(__FILE__) . DS . DS . ucwords($cache) . '.php';
+        if (!is_file($cacheFile)) {
+            throw new \Exception('Unsupport cache class '. $cacheFile);
+        }
+        \Qii\Autoloader\Import::requires($cacheFile);
+    }
+}

+ 153 - 0
src/Cache/Memcached.php

@@ -0,0 +1,153 @@
+<?php
+/**
+ * Memcache缓存
+ * @author Jinhui Zhu<jinhui.zhu@live.cn>2015-10-25 21:41
+ *
+ */
+namespace Qii\Cache;
+
+class Memcached implements Intf
+{
+    const VERSION = '1.2';
+    /**
+     * memcached连接句柄
+     *
+     * @var resource
+     */
+    protected $_conn;
+    /**
+     * 默认的缓存策略
+     *
+     * @var array
+     */
+    protected $_default_policy = array(
+        /**
+         * 缓存服务器配置,参看$_default_server
+         * 允许多个缓存服务器
+         */
+        'servers' => array(),
+
+        /**
+         * 是否压缩缓存数据
+         */
+        'compressed' => false,
+
+        /**
+         * 缓存有效时间
+         *
+         * 如果设置为 0 表示缓存永不过期
+         */
+        'life_time' => 900,
+
+        /**
+         * 是否使用持久连接
+         */
+        'persistent' => true,
+    );
+
+    /**
+     * 默认的缓存服务器
+     *
+     * @var array
+     */
+    protected $_default_server = array(
+        /**
+         * 缓存服务器地址或主机名
+         */
+        'host' => '127.0.0.1',
+
+        /**
+         * 缓存服务器端口
+         */
+        'port' => '11211',
+    );
+
+    public function __construct(array $policy = null)
+    {
+        if (!extension_loaded('memcached') && !extension_loaded('memcache')) {
+            return \Qii::setError(false, __LINE__, 1004);
+        }
+        if (is_array($policy)) {
+            $this->_default_policy = array_merge($this->_default_policy, $policy);
+        }
+        if (empty($this->_default_policy['servers'])) {
+            $this->_default_policy['servers'][] = $this->_default_server;
+        }
+        if (!isset($this->_default_policy['persistent'])) $this->_default_policy['persistent'] = '';
+        if(extension_loaded('memcached'))
+        {
+	        $this->_conn = new \Memcached();
+        }
+        else
+        {
+	        $this->_conn = new \Memcache();
+        }
+        foreach ($this->_default_policy['servers'] as $server) {
+            $result = $this->_conn->addServer($server['host'], $server['port'], $this->_default_policy['persistent']);
+            if (!$result) {
+                return \Qii::setError(false, __LINE__, 1005, $server['host'], $server['port']);
+            }
+        }
+    }
+
+    /**
+     * 写入缓存
+     *
+     * @param string $id
+     * @param mixed $data
+     * @param array $policy
+     * @return boolean
+     */
+    public function set($id, $data, array $policy = null)
+    {
+        if (!isset($policy['life_time'])) $policy['life_time'] = $this->_default_policy['life_time'];
+        if($this->_conn instanceof \Memcache){
+            $data = serialize($data);
+            return $this->_conn->set($id, $data, MEMCACHE_COMPRESSED, $policy['life_time']);
+        }
+        return $this->_conn->set($id, $data, $policy['life_time']);
+    }
+
+    /**
+     * 读取缓存,失败或缓存撒失效时返回 false
+     *
+     * @param string $id
+     *
+     * @return mixed
+     */
+    public function get($id)
+    {
+        $data = $this->_conn->get($id);
+        if($this->_conn instanceof \Memcache){
+            $data = unserialize($data);
+        }
+        return $data;
+    }
+
+    /**
+     * 删除指定的缓存
+     *
+     * @param string $id
+     * @return boolean
+     */
+    public function remove($id)
+    {
+        return $this->_conn->delete($id);
+    }
+
+    /**
+     * 清除所有的缓存数据
+     *
+     * @return boolean
+     */
+    public function clean()
+    {
+        return $this->_conn->flush();
+    }
+    public function __call($method, $args)
+    {
+        return call_user_func_array(array($this->_conn, $method), $args);
+    }
+}
+
+?>

+ 109 - 0
src/Cache/Redis.php

@@ -0,0 +1,109 @@
+<?php
+namespace Qii\Cache;
+
+\Qii\Autoloader\Import::requires(array(dirname(__FILE__) . DS . 'Redis/Client.php', dirname(__FILE__) . DS . 'Redis/Cluster.php'));
+
+/**
+ * PHP 操作 redis
+ * @author Jinhui.Zhu
+ *
+ */
+
+class Redis implements Intf
+{
+    const VERSION = '1.2';
+
+    public $redis;
+    protected $policy = array(
+        /**
+         * 缓存服务器配置,参看$_default_server
+         * 允许多个缓存服务器
+         */
+        'servers' => array(['host' => '127.0.0.1', 'port' => '6379']),
+
+        /**
+         * 缓存有效时间
+         *
+         * 如果设置为 0 表示缓存永不过期
+         */
+        'life_time' => 900
+    );
+
+    public function __construct(array $policy = null)
+    {
+        if (!extension_loaded('redis')) {
+            throw new \Qii\Exceptions\MethodNotFound(\Qii::i(1006), __LINE__);
+        }
+
+
+        if (!empty($policy)) {
+            $this->policy = array_merge($this->policy, $policy);
+        }
+
+        $redisServer = array();
+        foreach ($this->policy['servers'] AS $value) {
+            $redisServer[] = array('host' => $value['host'], 'port' => $value['port']);
+        }
+        $this->redis = new \Qii\Cache\Redis\Cluster($redisServer, 128);
+    }
+
+    /**
+     * 保存指定key的数据
+     */
+    public function set($id, $data, array $policy = null)
+    {
+        if (!isset($policy['life_time'])) $policy['life_time'] = $this->policy['life_time'];
+        try {
+            $this->redis->hMset($id, $data);
+            if (isset($policy['life_time']) && $policy['life_time'] > 0) {
+                $this->redis->setTimeout($id, $policy['life_time']);
+            }
+        } catch (\CredisException $e) {
+            throw new \Qii\Exceptions\Errors(\Qii::i(-1, $e->getMessage()), __LINE__);
+        }
+    }
+
+    /**
+     * 获取指定key的数据
+     */
+    public function hGet($id)
+    {
+        if ($this->redis->exists($id)) {
+            return $this->redis->hGetAll($id);
+        }
+        return null;
+    }
+    /**
+     * 获取指定key的数据
+     */
+    public function get($id)
+    {
+        if ($this->redis->exists($id)) {
+            return $this->redis->get($id);
+        }
+        return null;
+    }
+
+    /**
+     * 删除指定key的数据
+     */
+    public function remove($id)
+    {
+        if ($this->redis->exists($id)) {
+            return $this->redis->delete($id);
+        }
+    }
+
+    /**
+     * 清除当前db的所有数据
+     */
+    public function clean()
+    {
+        $this->redis->flushdb();
+    }
+    
+    public function __call($method, $args)
+    {
+        return call_user_func_array(array($this->redis, $method), $args);
+    }
+}

+ 1372 - 0
src/Cache/Redis/Client.php

@@ -0,0 +1,1372 @@
+<?php
+namespace Qii\Cache\Redis;
+/**
+ * Credis_Client (a fork of Redisent)
+ *
+ * Most commands are compatible with phpredis library:
+ *   - use "pipeline()" to start a pipeline of commands instead of multi(Redis::PIPELINE)
+ *   - any arrays passed as arguments will be flattened automatically
+ *   - setOption and getOption are not supported in standalone mode
+ *   - order of arguments follows redis-cli instead of phpredis where they differ (lrem)
+ *
+ * - Uses phpredis library if extension is installed for better performance.
+ * - Establishes connection lazily.
+ * - Supports tcp and unix sockets.
+ * - Reconnects automatically unless a watch or transaction is in progress.
+ * - Can set automatic retry connection attempts for iffy Redis connections.
+ *
+ * @author Colin Mollenhour <colin@mollenhour.com>
+ * @copyright 2011 Colin Mollenhour <colin@mollenhour.com>
+ * @license http://www.opensource.org/licenses/mit-license.php The MIT License
+ * @package Credis_Client
+ */
+
+if( ! defined('CRLF')) define('CRLF', sprintf('%s%s', chr(13), chr(10)));
+
+/**
+ * Credis-specific errors, wraps native Redis errors
+ */
+class CredisException extends \Exception
+{
+
+    const CODE_TIMED_OUT = 1;
+    const CODE_DISCONNECTED = 2;
+
+    public function __construct($message, $code = 0, $exception = NULL)
+    {
+        if ($exception && get_class($exception) == 'RedisException' && $message == 'read error on connection') {
+            $code = CredisException::CODE_DISCONNECTED;
+        }
+        parent::__construct($message, $code, $exception);
+    }
+
+}
+
+/**
+ * Credis_Client, a lightweight Redis PHP standalone client and phpredis wrapper
+ *
+ * Server/Connection:
+ * @method Credis_Client pipeline()
+ * @method Credis_Client multi()
+ * @method array         exec()
+ * @method string        flushAll()
+ * @method string        flushDb()
+ * @method array         info(string $section)
+ * @method bool|array    config(string $setGet, string $key, string $value = null)
+ * @method array         role()
+ * @method array         time()
+ *
+ * Keys:
+ * @method int           del(string $key)
+ * @method int           exists(string $key)
+ * @method int           expire(string $key, int $seconds)
+ * @method int           expireAt(string $key, int $timestamp)
+ * @method array         keys(string $key)
+ * @method int           persist(string $key)
+ * @method bool          rename(string $key, string $newKey)
+ * @method bool          renameNx(string $key, string $newKey)
+ * @method array         sort(string $key, string $arg1, string $valueN = null)
+ * @method int           ttl(string $key)
+ * @method string        type(string $key)
+ *
+ * Scalars:
+ * @method int           append(string $key, string $value)
+ * @method int           decr(string $key)
+ * @method int           decrBy(string $key, int $decrement)
+ * @method bool|string   get(string $key)
+ * @method int           getBit(string $key, int $offset)
+ * @method string        getRange(string $key, int $start, int $end)
+ * @method string        getSet(string $key, string $value)
+ * @method int           incr(string $key)
+ * @method int           incrBy(string $key, int $decrement)
+ * @method array         mGet(array $keys)
+ * @method bool          mSet(array $keysValues)
+ * @method int           mSetNx(array $keysValues)
+ * @method bool          set(string $key, string $value)
+ * @method int           setBit(string $key, int $offset, int $value)
+ * @method bool          setEx(string $key, int $seconds, string $value)
+ * @method int           setNx(string $key, string $value)
+ * @method int           setRange(string $key, int $offset, int $value)
+ * @method int           strLen(string $key)
+ *
+ * Sets:
+ * @method int           sAdd(string $key, mixed $value, string $valueN = null)
+ * @method int           sRem(string $key, mixed $value, string $valueN = null)
+ * @method array         sMembers(string $key)
+ * @method array         sUnion(mixed $keyOrArray, string $valueN = null)
+ * @method array         sInter(mixed $keyOrArray, string $valueN = null)
+ * @method array         sDiff(mixed $keyOrArray, string $valueN = null)
+ * @method string        sPop(string $key)
+ * @method int           sCard(string $key)
+ * @method int           sIsMember(string $key, string $member)
+ * @method int           sMove(string $source, string $dest, string $member)
+ * @method string|array  sRandMember(string $key, int $count = null)
+ * @method int           sUnionStore(string $dest, string $key1, string $key2 = null)
+ * @method int           sInterStore(string $dest, string $key1, string $key2 = null)
+ * @method int           sDiffStore(string $dest, string $key1, string $key2 = null)
+ *
+ * Hashes:
+ * @method bool|int      hSet(string $key, string $field, string $value)
+ * @method bool          hSetNx(string $key, string $field, string $value)
+ * @method bool|string   hGet(string $key, string $field)
+ * @method bool|int      hLen(string $key)
+ * @method bool          hDel(string $key, string $field)
+ * @method array         hKeys(string $key, string $field)
+ * @method array         hVals(string $key)
+ * @method array         hGetAll(string $key)
+ * @method bool          hExists(string $key, string $field)
+ * @method int           hIncrBy(string $key, string $field, int $value)
+ * @method bool          hMSet(string $key, array $keysValues)
+ * @method array         hMGet(string $key, array $fields)
+ *
+ * Lists:
+ * @method array|null    blPop(string $keyN, int $timeout)
+ * @method array|null    brPop(string $keyN, int $timeout)
+ * @method array|null    brPoplPush(string $source, string $destination, int $timeout)
+ * @method string|null   lIndex(string $key, int $index)
+ * @method int           lInsert(string $key, string $beforeAfter, string $pivot, string $value)
+ * @method int           lLen(string $key)
+ * @method string|null   lPop(string $key)
+ * @method int           lPush(string $key, mixed $value, mixed $valueN = null)
+ * @method int           lPushX(string $key, mixed $value)
+ * @method array         lRange(string $key, int $start, int $stop)
+ * @method int           lRem(string $key, int $count, mixed $value)
+ * @method bool          lSet(string $key, int $index, mixed $value)
+ * @method bool          lTrim(string $key, int $start, int $stop)
+ * @method string|null   rPop(string $key)
+ * @method string|null   rPoplPush(string $source, string $destination)
+ * @method int           rPush(string $key, mixed $value, mixed $valueN = null)
+ * @method int           rPushX(string $key, mixed $value)
+ *
+ * Sorted Sets:
+ * @method int           zCard(string $key)
+ * @method array         zRangeByScore(string $key, mixed $start, mixed $stop, array $args = null)
+ * @method array         zRevRangeByScore(string $key, mixed $start, mixed $stop, array $args = null)
+ * @method int           zRemRangeByScore(string $key, mixed $start, mixed $stop)
+ * @method array         zRange(string $key, mixed $start, mixed $stop, array $args = null)
+ * @method array         zRevRange(string $key, mixed $start, mixed $stop, array $args = null)
+ * TODO
+ *
+ * Pub/Sub
+ * @method int           publish(string $channel, string $message)
+ * @method int|array     pubsub(string $subCommand, $arg = NULL)
+ *
+ * Scripting:
+ * @method string|int    script(string $command, string $arg1 = null)
+ * @method string|int|array|bool eval(string $script, array $keys = NULL, array $args = NULL)
+ * @method string|int|array|bool evalSha(string $script, array $keys = NULL, array $args = NULL)
+ */
+class Client {
+
+    const TYPE_STRING      = 'string';
+    const TYPE_LIST        = 'list';
+    const TYPE_SET         = 'set';
+    const TYPE_ZSET        = 'zset';
+    const TYPE_HASH        = 'hash';
+    const TYPE_NONE        = 'none';
+    const FREAD_BLOCK_SIZE = 8192;
+
+    /**
+     * Socket connection to the Redis server or Redis library instance
+     * @var resource|Redis
+     */
+    protected $redis;
+    protected $redisMulti;
+
+    /**
+     * Host of the Redis server
+     * @var string
+     */
+    protected $host;
+    
+    /**
+     * Port on which the Redis server is running
+     * @var integer
+     */
+    protected $port;
+
+    /**
+     * Timeout for connecting to Redis server
+     * @var float
+     */
+    protected $timeout;
+
+    /**
+     * Timeout for reading response from Redis server
+     * @var float
+     */
+    protected $readTimeout;
+
+    /**
+     * Unique identifier for persistent connections
+     * @var string
+     */
+    protected $persistent;
+
+    /**
+     * @var bool
+     */
+    protected $closeOnDestruct = TRUE;
+
+    /**
+     * @var bool
+     */
+    protected $connected = FALSE;
+
+    /**
+     * @var bool
+     */
+    protected $standalone;
+
+    /**
+     * @var int
+     */
+    protected $maxConnectRetries = 0;
+
+    /**
+     * @var int
+     */
+    protected $connectFailures = 0;
+
+    /**
+     * @var bool
+     */
+    protected $usePipeline = FALSE;
+
+    /**
+     * @var array
+     */
+    protected $commandNames;
+
+    /**
+     * @var string
+     */
+    protected $commands;
+
+    /**
+     * @var bool
+     */
+    protected $isMulti = FALSE;
+
+    /**
+     * @var bool
+     */
+    protected $isWatching = FALSE;
+
+    /**
+     * @var string
+     */
+    protected $authPassword;
+
+    /**
+     * @var int
+     */
+    protected $selectedDb = 0;
+
+    /**
+     * Aliases for backwards compatibility with phpredis
+     * @var array
+     */
+    protected $wrapperMethods = array('delete' => 'del', 'getkeys' => 'keys', 'sremove' => 'srem');
+
+    /**
+     * @var array
+     */
+    protected $renamedCommands;
+
+    /**
+     * @var int
+     */
+    protected $requests = 0;
+    
+    /**
+     * @var bool
+     */
+    protected $subscribed = false;
+    
+
+    /**
+     * Creates a Redisent connection to the Redis server on host {@link $host} and port {@link $port}.
+     * $host may also be a path to a unix socket or a string in the form of tcp://[hostname]:[port] or unix://[path]
+     *
+     * @param string $host The hostname of the Redis server
+     * @param integer $port The port number of the Redis server
+     * @param float $timeout  Timeout period in seconds
+     * @param string $persistent  Flag to establish persistent connection
+     * @param int $db The selected datbase of the Redis server
+     * @param string $password The authentication password of the Redis server
+     */
+    public function __construct($host = '127.0.0.1', $port = 6379, $timeout = null, $persistent = '', $db = 0, $password = null)
+    {
+        $this->host = (string) $host;
+        $this->port = (int) $port;
+        $this->timeout = $timeout;
+        $this->persistent = (string) $persistent;
+        $this->standalone = ! extension_loaded('redis');
+        $this->authPassword = $password;
+        $this->selectedDb = (int)$db;
+        $this->convertHost();
+    }
+
+    public function __destruct()
+    {
+        if ($this->closeOnDestruct) {
+            $this->close();
+        }
+    }
+    
+    /**
+     * @return bool
+     */
+    public function isSubscribed()
+    {
+    	return $this->subscribed;
+    }
+    
+    /**
+     * Return the host of the Redis instance
+     * @return string
+     */
+    public function getHost()
+    {
+        return $this->host;
+    }
+    /**
+     * Return the port of the Redis instance
+     * @return int
+     */
+    public function getPort()
+    {
+        return $this->port;
+    }
+
+    /**
+     * Return the selected database
+     * @return int
+     */
+    public function getSelectedDb()
+    {
+        return $this->selectedDb;
+    }
+    /**
+     * @return string
+     */
+    public function getPersistence()
+    {
+        return $this->persistent;
+    }
+    /**
+     * @throws CredisException
+     * @return Credis_Client
+     */
+    public function forceStandalone()
+    {
+        if ($this->standalone) {
+            return $this;
+        }
+        if($this->connected) {
+            throw new CredisException('Cannot force Credis_Client to use standalone PHP driver after a connection has already been established.');
+        }
+        $this->standalone = TRUE;
+        return $this;
+    }
+
+    /**
+     * @param int $retries
+     * @return Credis_Client
+     */
+    public function setMaxConnectRetries($retries)
+    {
+        $this->maxConnectRetries = $retries;
+        return $this;
+    }
+
+    /**
+     * @param bool $flag
+     * @return Credis_Client
+     */
+    public function setCloseOnDestruct($flag)
+    {
+        $this->closeOnDestruct = $flag;
+        return $this;
+    }
+    protected function convertHost()
+    {
+        if (preg_match('#^(tcp|unix)://(.*)$#', $this->host, $matches)) {
+            if($matches[1] == 'tcp') {
+                if ( ! preg_match('#^([^:]+)(:([0-9]+))?(/(.+))?$#', $matches[2], $matches)) {
+                    throw new CredisException('Invalid host format; expected tcp://host[:port][/persistence_identifier]');
+                }
+                $this->host = $matches[1];
+                $this->port = (int) (isset($matches[3]) ? $matches[3] : 6379);
+                $this->persistent = isset($matches[5]) ? $matches[5] : '';
+            } else {
+                $this->host = $matches[2];
+                $this->port = NULL;
+                if (substr($this->host,0,1) != '/') {
+                    throw new CredisException('Invalid unix socket format; expected unix:///path/to/redis.sock');
+                }
+            }
+        }
+        if ($this->port !== NULL && substr($this->host,0,1) == '/') {
+            $this->port = NULL;
+        }
+    }
+    /**
+     * @throws CredisException
+     * @return Credis_Client
+     */
+    public function connect()
+    {
+        if ($this->connected) {
+            return $this;
+        }
+        if ($this->standalone) {
+            $flags = STREAM_CLIENT_CONNECT;
+            $remote_socket = $this->port === NULL
+                ? 'unix://'.$this->host
+                : 'tcp://'.$this->host.':'.$this->port;
+            if ($this->persistent && $this->port !== NULL) {
+                // Persistent connections to UNIX sockets are not supported
+                $remote_socket .= '/'.$this->persistent;
+                $flags = $flags | STREAM_CLIENT_PERSISTENT;
+            }
+            $result = $this->redis = @stream_socket_client($remote_socket, $errno, $errstr, $this->timeout !== null ? $this->timeout : 2.5, $flags);
+        }
+        else {
+            if ( ! $this->redis) {
+                $this->redis = new \Redis;
+            }
+            try
+            {
+                $socketTimeout = $this->timeout ? $this->timeout : 0.0;
+                $result = $this->persistent
+                    ? $this->redis->pconnect($this->host, $this->port, $socketTimeout, $this->persistent)
+                    : $this->redis->connect($this->host, $this->port, $socketTimeout);
+            }
+            catch(Exception $e)
+            {
+                // Some applications will capture the php error that phpredis can sometimes generate and throw it as an Exception
+                $result = false;
+                $errno = 1;
+                $errstr = $e->getMessage();
+            }
+        }
+
+        // Use recursion for connection retries
+        if ( ! $result) {
+            $this->connectFailures++;
+            if ($this->connectFailures <= $this->maxConnectRetries) {
+                return $this->connect();
+            }
+            $failures = $this->connectFailures;
+            $this->connectFailures = 0;
+            throw new CredisException("Connection to Redis {$this->host}:{$this->port} failed after $failures failures." . (isset($errno) && isset($errstr) ? "Last Error : ({$errno}) {$errstr}" : ""));
+        }
+
+        $this->connectFailures = 0;
+        $this->connected = TRUE;
+
+        // Set read timeout
+        if ($this->readTimeout) {
+            $this->setReadTimeout($this->readTimeout);
+        }
+
+        if($this->authPassword) {
+            $this->auth($this->authPassword);
+        }
+        if($this->selectedDb !== 0) {
+            $this->select($this->selectedDb);
+        }
+        return $this;
+    }
+    /**
+     * @return bool
+     */
+    public function isConnected()
+    {
+        return $this->connected;
+    }
+    /**
+     * Set the read timeout for the connection. Use 0 to disable timeouts entirely (or use a very long timeout
+     * if not supported).
+     *
+     * @param int $timeout 0 (or -1) for no timeout, otherwise number of seconds
+     * @throws CredisException
+     * @return Credis_Client
+     */
+    public function setReadTimeout($timeout)
+    {
+        if ($timeout < -1) {
+            throw new CredisException('Timeout values less than -1 are not accepted.');
+        }
+        $this->readTimeout = $timeout;
+        if ($this->connected) {
+            if ($this->standalone) {
+                $timeout = $timeout <= 0 ? 315360000 : $timeout; // Ten-year timeout
+                stream_set_blocking($this->redis, TRUE);
+                stream_set_timeout($this->redis, (int) floor($timeout), ($timeout - floor($timeout)) * 1000000);
+            } else if (defined('Redis::OPT_READ_TIMEOUT')) {
+                // supported in phpredis 2.2.3
+                // a timeout value of -1 means reads will not timeout
+                $timeout = $timeout == 0 ? -1 : $timeout;
+                $this->redis->setOption(Redis::OPT_READ_TIMEOUT, $timeout);
+            }
+        }
+        return $this;
+    }
+
+    /**
+     * @return bool
+     */
+    public function close()
+    {
+        $result = TRUE;
+        if ($this->connected && ! $this->persistent) {
+            try {
+                $result = $this->standalone ? fclose($this->redis) : $this->redis->close();
+                $this->connected = FALSE;
+            } catch (Exception $e) {
+                ; // Ignore exceptions on close
+            }
+        }
+        return $result;
+    }
+
+    /**
+     * Enabled command renaming and provide mapping method. Supported methods are:
+     *
+     * 1. renameCommand('foo') // Salted md5 hash for all commands -> md5('foo'.$command)
+     * 2. renameCommand(function($command){ return 'my'.$command; }); // Callable
+     * 3. renameCommand('get', 'foo') // Single command -> alias
+     * 4. renameCommand(['get' => 'foo', 'set' => 'bar']) // Full map of [command -> alias]
+     *
+     * @param string|callable|array $command
+     * @param string|null $alias
+     * @return $this
+     */
+    public function renameCommand($command, $alias = NULL)
+    {
+        if ( ! $this->standalone) {
+            $this->forceStandalone();
+        }
+        if ($alias === NULL) {
+            $this->renamedCommands = $command;
+        } else {
+            if ( ! $this->renamedCommands) {
+                $this->renamedCommands = array();
+            }
+            $this->renamedCommands[$command] = $alias;
+        }
+        return $this;
+    }
+
+    /**
+     * @param $command
+     */
+    public function getRenamedCommand($command)
+    {
+        static $map;
+
+        // Command renaming not enabled
+        if ($this->renamedCommands === NULL) {
+            return $command;
+        }
+
+        // Initialize command map
+        if ($map === NULL) {
+            if (is_array($this->renamedCommands)) {
+                $map = $this->renamedCommands;
+            } else {
+                $map = array();
+            }
+        }
+
+        // Generate and return cached result
+        if ( ! isset($map[$command])) {
+            // String means all commands are hashed with salted md5
+            if (is_string($this->renamedCommands)) {
+                $map[$command] = md5($this->renamedCommands.$command);
+            }
+            // Would already be set in $map if it was intended to be renamed
+            else if (is_array($this->renamedCommands)) {
+                return $command;
+            }
+            // User-supplied function
+            else if (is_callable($this->renamedCommands)) {
+                $map[$command] = call_user_func($this->renamedCommands, $command);
+            }
+        }
+        return $map[$command];
+    }
+
+    /**
+     * @param string $password
+     * @return bool
+     */
+    public function auth($password)
+    {
+        $response = $this->__call('auth', array($password));
+        $this->authPassword = $password;
+        return $response;
+    }
+
+    /**
+     * @param int $index
+     * @return bool
+     */
+    public function select($index)
+    {
+        $response = $this->__call('select', array($index));
+        $this->selectedDb = (int) $index;
+        return $response;
+    }
+    
+    /**
+     * @param string|array $pattern
+     * @return array
+     */
+    public function pUnsubscribe()
+    {
+    	list($command, $channel, $subscribedChannels) = $this->__call('punsubscribe', func_get_args());
+    	$this->subscribed = $subscribedChannels > 0;
+    	return array($command, $channel, $subscribedChannels);
+    }
+
+    /**
+     * @param int $Iterator
+     * @param string $pattern
+     * @param int $count
+     * @return bool | Array
+     */    
+    public function scan(&$Iterator, $pattern = null, $count = null)
+    {
+        return $this->__call('scan', array(&$Iterator, $pattern, $count));
+    }
+
+    /**
+	 * @param int $Iterator
+	 * @param string $field
+	 * @param string $pattern
+	 * @param int $count
+	 * @return bool | Array
+	 */
+	public function hscan(&$Iterator, $field, $pattern = null, $count = null)
+	{
+		return $this->__call('hscan', array($field, &$Iterator, $pattern, $count));
+	}
+    
+    /**
+     * @param int $Iterator
+     * @param string $field
+     * @param string $pattern
+     * @param int $Iterator
+     * @return bool | Array
+     */    
+    public function sscan(&$Iterator, $field, $pattern = null, $count = null)
+    {
+        return $this->__call('sscan', array($field, &$Iterator, $pattern, $count));
+    }
+    
+    /**
+     * @param int $Iterator
+     * @param string $field
+     * @param string $pattern
+     * @param int $Iterator
+     * @return bool | Array
+     */    
+    public function zscan(&$Iterator, $field, $pattern = null, $count = null)
+    {
+        return $this->__call('zscan', array($field, &$Iterator, $pattern, $count));
+    }
+
+    /**
+     * @param string|array $patterns
+     * @param $callback
+     * @return $this|array|bool|Credis_Client|mixed|null|string
+     * @throws CredisException
+     */
+    public function pSubscribe($patterns, $callback)
+    {
+        if ( ! $this->standalone) {
+            return $this->__call('pSubscribe', array((array)$patterns, $callback));
+        }
+
+        // Standalone mode: use infinite loop to subscribe until timeout
+        $patternCount = is_array($patterns) ? count($patterns) : 1;
+        while ($patternCount--) {
+            if (isset($status)) {
+                list($command, $pattern, $status) = $this->read_reply();
+            } else {
+                list($command, $pattern, $status) = $this->__call('psubscribe', array($patterns));
+            }
+            $this->subscribed = $status > 0;
+            if ( ! $status) {
+                throw new CredisException('Invalid pSubscribe response.');
+            }
+        }
+        try {
+            while ($this->subscribed) {
+                list($type, $pattern, $channel, $message) = $this->read_reply();
+                if ($type != 'pmessage') {
+                    throw new CredisException('Received non-pmessage reply.');
+                }
+                $callback($this, $pattern, $channel, $message);
+            }
+        } catch (CredisException $e) {
+            if ($e->getCode() == CredisException::CODE_TIMED_OUT) {
+                try {
+                    list($command, $pattern, $status) = $this->pUnsubscribe($patterns);
+                    while ($status !== 0) {
+                        list($command, $pattern, $status) = $this->read_reply();
+                    }
+                } catch (CredisException $e2) {
+                    throw $e2;
+                }
+            }
+            throw $e;
+        }
+    }
+
+    /**
+     * @param string|array $pattern
+     * @return array
+     */
+    public function unsubscribe()
+    {
+    	list($command, $channel, $subscribedChannels) = $this->__call('unsubscribe', func_get_args());
+    	$this->subscribed = $subscribedChannels > 0;
+    	return array($command, $channel, $subscribedChannels);
+    }
+
+    /**
+     * @param string|array $channels
+     * @param $callback
+     * @throws CredisException
+     * @return $this|array|bool|Credis_Client|mixed|null|string
+     */
+    public function subscribe($channels, $callback)
+    {
+        if ( ! $this->standalone) {
+            return $this->__call('subscribe', array((array)$channels, $callback));
+        }
+
+        // Standalone mode: use infinite loop to subscribe until timeout
+        $channelCount = is_array($channels) ? count($channels) : 1;
+        while ($channelCount--) {
+            if (isset($status)) {
+                list($command, $channel, $status) = $this->read_reply();
+            } else {
+                list($command, $channel, $status) = $this->__call('subscribe', array($channels));
+            }
+            $this->subscribed = $status > 0;
+            if ( ! $status) {
+                throw new CredisException('Invalid subscribe response.');
+            }
+        }
+        try {
+            while ($this->subscribed) {
+                list($type, $channel, $message) = $this->read_reply();
+                if ($type != 'message') {
+                    throw new CredisException('Received non-message reply.');
+                }
+                $callback($this, $channel, $message);
+            }
+        } catch (CredisException $e) {
+            if ($e->getCode() == CredisException::CODE_TIMED_OUT) {
+                try {
+                    list($command, $channel, $status) = $this->unsubscribe($channels);
+                    while ($status !== 0) {
+                        list($command, $channel, $status) = $this->read_reply();
+                    }
+                } catch (CredisException $e2) {
+                    throw $e2;
+                }
+            }
+            throw $e;
+        }
+    }
+
+    public function __call($name, $args)
+    {
+        // Lazy connection
+        $this->connect();
+
+        $name = strtolower($name);
+
+        // Send request via native PHP
+        if($this->standalone)
+        {
+            switch ($name) {
+                case 'eval':
+                case 'evalsha':
+                    $script = array_shift($args);
+                    $keys = (array) array_shift($args);
+                    $eArgs = (array) array_shift($args);
+                    $args = array($script, count($keys), $keys, $eArgs);
+                    break;
+                case 'zunionstore':
+                    $dest = array_shift($args);
+                    $keys = (array) array_shift($args);
+                    $weights = array_shift($args);
+                    $aggregate = array_shift($args);
+                    $args = array($dest, count($keys), $keys);
+                    if ($weights) {
+                        $args[] = (array) $weights;
+                    }
+                    if ($aggregate) {
+                        $args[] = $aggregate;
+                    }
+                    break;
+                case 'set':
+                    // The php redis module has different behaviour with ttl
+                    // https://github.com/phpredis/phpredis#set
+                    if (count($args) === 3 && is_int($args[2])) { 
+                        $args = array($args[0], $args[1], array('EX', $args[2]));
+                    } elseif (count($args) === 3 && is_array($args[2])) {
+                        $tmp_args = $args;
+                        $args = array($tmp_args[0], $tmp_args[1]);
+                        foreach ($tmp_args[2] as $k=>$v) {
+                            if (is_string($k)) {
+                                $args[] = array($k,$v);
+                            } elseif (is_int($k)) {
+                                $args[] = $v;
+                            }
+                        }
+                        unset($tmp_args);
+                    }
+                    break;
+                case 'scan':
+                    $ref =& $args[0];
+                    if (empty($ref))
+                    {
+                        $ref = 0;
+                    }
+                    $eArgs = array($ref);
+                    if (!empty($args[1]))
+                    {
+                        $eArgs[] = 'MATCH';
+                        $eArgs[] = $args[1];
+                    }
+                    if (!empty($args[2]))
+                    {
+                        $eArgs[] = 'COUNT';
+                        $eArgs[] = $args[2];
+                    }
+                    $args = $eArgs;
+                    break;
+                case 'sscan':
+                case 'zscan':
+                case 'hscan':
+					$ref =& $args[1];
+					if (empty($ref))
+					{
+						$ref = 0;
+					}
+					$eArgs = array($args[0],$ref);
+					if (!empty($args[2]))
+					{
+						$eArgs[] = 'MATCH';
+						$eArgs[] = $args[2];
+					}
+					if (!empty($args[3]))
+					{
+						$eArgs[] = 'COUNT';
+						$eArgs[] = $args[3];
+					}
+					$args = $eArgs;
+					break;
+                case 'zrangebyscore':
+                case 'zrevrangebyscore':
+                case 'zrange':
+                case 'zrevrange':
+                    if (isset($args[3]) && is_array($args[3])) {
+                        // map options
+                        $cArgs = array();
+                        if (!empty($args[3]['withscores'])) {
+                            $cArgs[] = 'withscores';
+                        }
+                        if (($name == 'zrangebyscore' || $name == 'zrevrangebyscore') && array_key_exists('limit', $args[3])) {
+                            $cArgs[] = array('limit' => $args[3]['limit']);
+                        }
+                        $args[3] = $cArgs;
+                    }
+                    break;
+                case 'mget':
+                    if (isset($args[0]) && is_array($args[0])) 
+                    {
+                        $args = array_values($args[0]);
+                    }
+                    break;
+            }
+            // Flatten arguments
+            $args = self::_flattenArguments($args);
+
+            // In pipeline mode
+            if($this->usePipeline)
+            {
+                if($name == 'pipeline') {
+                    throw new CredisException('A pipeline is already in use and only one pipeline is supported.');
+                }
+                else if($name == 'exec') {
+                    if($this->isMulti) {
+                        $this->commandNames[] = $name;
+                        $this->commands .= self::_prepare_command(array($this->getRenamedCommand($name)));
+                    }
+
+                    // Write request
+                    if($this->commands) {
+                        $this->write_command($this->commands);
+                    }
+                    $this->commands = NULL;
+
+                    // Read response
+                    $response = array();
+                    foreach($this->commandNames as $command) {
+                        $response[] = $this->read_reply($command);
+                    }
+                    $this->commandNames = NULL;
+
+                    if($this->isMulti) {
+                        $response = array_pop($response);
+                    }
+                    $this->usePipeline = $this->isMulti = FALSE;
+                    return $response;
+                }
+                else {
+                    if($name == 'multi') {
+                        $this->isMulti = TRUE;
+                    }
+                    array_unshift($args, $this->getRenamedCommand($name));
+                    $this->commandNames[] = $name;
+                    $this->commands .= self::_prepare_command($args);
+                    return $this;
+                }
+            }
+
+            // Start pipeline mode
+            if($name == 'pipeline')
+            {
+                $this->usePipeline = TRUE;
+                $this->commandNames = array();
+                $this->commands = '';
+                return $this;
+            }
+
+            // If unwatching, allow reconnect with no error thrown
+            if($name == 'unwatch') {
+                $this->isWatching = FALSE;
+            }
+
+            // Non-pipeline mode
+            array_unshift($args, $this->getRenamedCommand($name));
+            $command = self::_prepare_command($args);
+            $this->write_command($command);
+            $response = $this->read_reply($name);
+
+            switch($name)
+            {
+                case 'scan':
+                case 'sscan':
+                    $ref = array_shift($response);
+                    $response = empty($response[0]) ? array() : $response[0];
+                    break;
+                case 'hscan':
+                case 'zscan':
+                    $ref = array_shift($response);
+                    $response = empty($response[0]) ? array() : $response[0];
+                    if (!empty($response) && is_array($response))
+                    {
+                        $count  = count($response);
+                        $out    = array();
+                        for($i  = 0;$i < $count;$i+=2){
+                            $out[$response[$i]] = $response[$i+1];
+                        }
+                        $response = $out;
+                    }
+					break;
+                case 'zrangebyscore':
+                case 'zrevrangebyscore':
+                    if (in_array('withscores', $args, true)) {
+                        // Map array of values into key=>score list like phpRedis does
+                        $item = null;
+                        $out = array();
+                        foreach ($response as $value) {
+                            if ($item == null) {
+                                $item = $value;
+                            } else {
+                                // 2nd value is the score
+                                $out[$item] = (float) $value;
+                                $item = null;
+                            }
+                        }
+                        $response = $out;
+                    }
+                    break;
+            }
+
+            // Watch mode disables reconnect so error is thrown
+            if($name == 'watch') {
+                $this->isWatching = TRUE;
+            }
+            // Transaction mode
+            else if($this->isMulti && ($name == 'exec' || $name == 'discard')) {
+                $this->isMulti = FALSE;
+            }
+            // Started transaction
+            else if($this->isMulti || $name == 'multi') {
+                $this->isMulti = TRUE;
+                $response = $this;
+            }
+        }
+
+        // Send request via phpredis client
+        else
+        {
+            // Tweak arguments
+            switch($name) {
+                case 'get':   // optimize common cases
+                case 'set':
+                case 'hget':
+                case 'hset':
+                case 'setex':
+                case 'mset':
+                case 'msetnx':
+                case 'hmset':
+                case 'hmget':
+                case 'del':
+                case 'zrangebyscore':
+                case 'zrevrangebyscore':
+                case 'zrange':
+                case 'zrevrange':
+                   break;
+                case 'zunionstore':
+                    $cArgs = array();
+                    $cArgs[] = array_shift($args); // destination
+                    $cArgs[] = array_shift($args); // keys
+                    if(isset($args[0]) and isset($args[0]['weights'])) {
+                        $cArgs[] = (array) $args[0]['weights'];
+                    } else {
+                        $cArgs[] = null;
+                    }
+                    if(isset($args[0]) and isset($args[0]['aggregate'])) {
+                        $cArgs[] = strtoupper($args[0]['aggregate']);
+                    }
+                    $args = $cArgs;
+                    break;
+                case 'mget':
+                    if(isset($args[0]) && ! is_array($args[0])) {
+                        $args = array($args);
+                    }
+                    break;
+                case 'lrem':
+                    $args = array($args[0], $args[2], $args[1]);
+                    break;
+                case 'eval':
+                case 'evalsha':
+                    if (isset($args[1]) && is_array($args[1])) {
+                        $cKeys = $args[1];
+                    } elseif (isset($args[1]) && is_string($args[1])) {
+                        $cKeys = array($args[1]);
+                    } else {
+                        $cKeys = array();
+                    }
+                    if (isset($args[2]) && is_array($args[2])) {
+                        $cArgs = $args[2];
+                    } elseif (isset($args[2]) && is_string($args[2])) {
+                        $cArgs = array($args[2]);
+                    } else {
+                        $cArgs = array();
+                    }
+                    $args = array($args[0], array_merge($cKeys, $cArgs), count($cKeys));
+                    break;
+                case 'subscribe':
+                case 'psubscribe':
+                    break;
+                case 'scan':
+                case 'sscan':
+                case 'hscan':
+                case 'zscan':
+                    // allow phpredis to see the caller's reference
+                    //$param_ref =& $args[0];
+                    break;
+                default:
+                    // Flatten arguments
+                    $args = self::_flattenArguments($args);
+            }
+
+            try {
+                // Proxy pipeline mode to the phpredis library
+                if($name == 'pipeline' || $name == 'multi') {
+                    if($this->isMulti) {
+                        return $this;
+                    } else {
+                        $this->isMulti = TRUE;
+                        $this->redisMulti = call_user_func_array(array($this->redis, $name), $args);
+                        return $this;
+                    }
+                }
+                else if($name == 'exec' || $name == 'discard') {
+                    $this->isMulti = FALSE;
+                    $response = $this->redisMulti->$name();
+                    $this->redisMulti = NULL;
+                    #echo "> $name : ".substr(print_r($response, TRUE),0,100)."\n";
+                    return $response;
+                }
+
+                // Use aliases to be compatible with phpredis wrapper
+                if(isset($this->wrapperMethods[$name])) {
+                    $name = $this->wrapperMethods[$name];
+                }
+
+                // Multi and pipeline return self for chaining
+                if($this->isMulti) {
+                    call_user_func_array(array($this->redisMulti, $name), $args);
+                    return $this;
+                }
+
+                // Send request, retry one time when using persistent connections on the first request only
+                $this->requests++;
+                try {
+                    $response = call_user_func_array(array($this->redis, $name), $args);
+                } catch (RedisException $e) {
+                    if ($this->persistent && $this->requests == 1 && $e->getMessage() == 'read error on connection') {
+                        $this->connected = FALSE;
+                        $this->connect();
+                        $response = call_user_func_array(array($this->redis, $name), $args);
+                    } else {
+                        throw $e;
+                    }
+                }
+            }
+            // Wrap exceptions
+            catch(RedisException $e) {
+                $code = 0;
+                if ( ! ($result = $this->redis->IsConnected())) {
+                    $this->connected = FALSE;
+                    $code = CredisException::CODE_DISCONNECTED;
+                }
+                throw new CredisException($e->getMessage(), $code, $e);
+            }
+
+            #echo "> $name : ".substr(print_r($response, TRUE),0,100)."\n";
+
+            // change return values where it is too difficult to minim in standalone mode
+            switch($name)
+            {
+                case 'hmget':
+                    $response = array_values($response);
+                    break;
+
+                case 'type':
+                    $typeMap = array(
+                      self::TYPE_NONE,
+                      self::TYPE_STRING,
+                      self::TYPE_SET,
+                      self::TYPE_LIST,
+                      self::TYPE_ZSET,
+                      self::TYPE_HASH,
+                    );
+                    $response = $typeMap[$response];
+                    break;
+
+                // Handle scripting errors
+                case 'eval':
+                case 'evalsha':
+                case 'script':
+                    $error = $this->redis->getLastError();
+                    $this->redis->clearLastError();
+                    if ($error && substr($error,0,8) == 'NOSCRIPT') {
+                        $response = NULL;
+                    } else if ($error) {
+                        throw new CredisException($error);
+                    }
+                    break;
+                default:
+                    $error = $this->redis->getLastError();
+                    $this->redis->clearLastError();
+                    if ($error) {
+                        throw new CredisException($error);
+                    }
+                    break;
+            }
+        }
+
+        return $response;
+    }
+
+    protected function write_command($command)
+    {
+        // Reconnect on lost connection (Redis server "timeout" exceeded since last command)
+        if(feof($this->redis)) {
+            $this->close();
+            // If a watch or transaction was in progress and connection was lost, throw error rather than reconnect
+            // since transaction/watch state will be lost.
+            if(($this->isMulti && ! $this->usePipeline) || $this->isWatching) {
+                $this->isMulti = $this->isWatching = FALSE;
+                throw new CredisException('Lost connection to Redis server during watch or transaction.');
+            }
+            $this->connected = FALSE;
+            $this->connect();
+            if($this->authPassword) {
+                $this->auth($this->authPassword);
+            }
+            if($this->selectedDb != 0) {
+                $this->select($this->selectedDb);
+            }
+        }
+
+        $commandLen = strlen($command);
+        $lastFailed = FALSE;
+        for ($written = 0; $written < $commandLen; $written += $fwrite) {
+            $fwrite = fwrite($this->redis, substr($command, $written));
+            if ($fwrite === FALSE || ($fwrite == 0 && $lastFailed)) {
+                $this->connected = FALSE;
+                throw new CredisException('Failed to write entire command to stream');
+            }
+            $lastFailed = $fwrite == 0;
+        }
+    }
+
+    protected function read_reply($name = '')
+    {
+        $reply = fgets($this->redis);
+        if($reply === FALSE) {
+            $info = stream_get_meta_data($this->redis);
+            if ($info['timed_out']) {
+                throw new CredisException('Read operation timed out.', CredisException::CODE_TIMED_OUT);
+            } else {
+                $this->connected = FALSE;
+                throw new CredisException('Lost connection to Redis server.', CredisException::CODE_DISCONNECTED);
+            }
+        }
+        $reply = rtrim($reply, CRLF);
+        #echo "> $name: $reply\n";
+        $replyType = substr($reply, 0, 1);
+        switch ($replyType) {
+            /* Error reply */
+            case '-':
+                if($this->isMulti || $this->usePipeline) {
+                    $response = FALSE;
+                } else if ($name == 'evalsha' && substr($reply,0,9) == '-NOSCRIPT') {
+                    $response = NULL;
+                } else {
+                    throw new CredisException(substr($reply,0,4) == '-ERR' ? substr($reply, 5) : substr($reply,1));
+                }
+                break;
+            /* Inline reply */
+            case '+':
+                $response = substr($reply, 1);
+                if($response == 'OK' || $response == 'QUEUED') {
+                  return TRUE;
+                }
+                break;
+            /* Bulk reply */
+            case '$':
+                if ($reply == '$-1') return FALSE;
+                $size = (int) substr($reply, 1);
+                $response = stream_get_contents($this->redis, $size + 2);
+                if( ! $response) {
+                    $this->connected = FALSE;
+                    throw new CredisException('Error reading reply.');
+                }
+                $response = substr($response, 0, $size);
+                break;
+            /* Multi-bulk reply */
+            case '*':
+                $count = substr($reply, 1);
+                if ($count == '-1') return FALSE;
+
+                $response = array();
+                for ($i = 0; $i < $count; $i++) {
+                        $response[] = $this->read_reply();
+                }
+                break;
+            /* Integer reply */
+            case ':':
+                $response = intval(substr($reply, 1));
+                break;
+            default:
+                throw new CredisException('Invalid response: '.print_r($reply, TRUE));
+                break;
+        }
+
+        // Smooth over differences between phpredis and standalone response
+        switch($name)
+        {
+            case '': // Minor optimization for multi-bulk replies
+                break;
+            case 'config':
+            case 'hgetall':
+                $keys = $values = array();
+                while($response) {
+                    $keys[] = array_shift($response);
+                    $values[] = array_shift($response);
+                }
+                $response = count($keys) ? array_combine($keys, $values) : array();
+                break;
+            case 'info':
+                $lines = explode(CRLF, trim($response,CRLF));
+                $response = array();
+                foreach($lines as $line) {
+                    if ( ! $line || substr($line, 0, 1) == '#') {
+                      continue;
+                    }
+                    list($key, $value) = explode(':', $line, 2);
+                    $response[$key] = $value;
+                }
+                break;
+            case 'ttl':
+                if($response === -1) {
+                    $response = FALSE;
+                }
+                break;
+        }
+
+        return $response;
+    }
+
+    /**
+     * Build the Redis unified protocol command
+     *
+     * @param array $args
+     * @return string
+     */
+    private static function _prepare_command($args)
+    {
+        return sprintf('*%d%s%s%s', count($args), CRLF, implode(array_map(array('self', '_map'), $args), CRLF), CRLF);
+    }
+
+    private static function _map($arg)
+    {
+        return sprintf('$%d%s%s', strlen($arg), CRLF, $arg);
+    }
+
+    /**
+     * Flatten arguments
+     *
+     * If an argument is an array, the key is inserted as argument followed by the array values
+     *  array('zrangebyscore', '-inf', 123, array('limit' => array('0', '1')))
+     * becomes
+     *  array('zrangebyscore', '-inf', 123, 'limit', '0', '1')
+     *
+     * @param array $in
+     * @return array
+     */
+    private static function _flattenArguments(array $arguments, &$out = array())
+    {
+        foreach ($arguments as $key => $arg) {
+            if (!is_int($key)) {
+                $out[] = $key;
+            }
+            
+            if (is_array($arg)) {
+                self::_flattenArguments($arg, $out);
+            } else {
+                $out[] = $arg;
+            }
+        }
+
+        return $out;
+    }
+}

+ 340 - 0
src/Cache/Redis/Cluster.php

@@ -0,0 +1,340 @@
+<?php
+namespace Qii\Cache\Redis;
+/**
+ * Credis, a Redis interface for the modest
+ *
+ * @author Justin Poliey <jdp34@njit.edu>
+ * @copyright 2009 Justin Poliey <jdp34@njit.edu>
+ * @license http://www.opensource.org/licenses/mit-license.php The MIT License
+ * @package Credis
+ */
+
+/**
+ * A generalized \Qii\Cache\Redis\Client interface for a cluster of Redis servers
+ *
+ * @deprecated
+ */
+class Cluster
+{
+  /**
+   * Collection of \Qii\Cache\Redis\Client objects attached to Redis servers
+   * @var \Qii\Cache\Redis\Client[]
+   */
+  protected $clients;
+  /**
+   * If a server is set as master, all write commands go to that one
+   * @var \Qii\Cache\Redis\Client
+   */
+  protected $masterClient;
+  /**
+   * Aliases of \Qii\Cache\Redis\Client objects attached to Redis servers, used to route commands to specific servers
+   * @see Credis_Cluster::to
+   * @var array
+   */
+  protected $aliases;
+  
+  /**
+   * Hash ring of Redis server nodes
+   * @var array
+   */
+  protected $ring;
+  
+  /**
+   * Individual nodes of pointers to Redis servers on the hash ring
+   * @var array
+   */
+  protected $nodes;
+  
+  /**
+   * The commands that are not subject to hashing
+   * @var array
+   * @access protected
+   */
+  protected $dont_hash;
+
+  /**
+   * Currently working cluster-wide database number.
+   * @var int
+   */
+  protected $selectedDb = 0;
+
+  /**
+   * Creates an interface to a cluster of Redis servers
+   * Each server should be in the format:
+   *  array(
+   *   'host' => hostname,
+   *   'port' => port,
+   *   'db' => db,
+   *   'password' => password,
+   *   'timeout' => timeout,
+   *   'alias' => alias,
+   *   'persistent' => persistence_identifier,
+   *   'master' => master
+   *   'write_only'=> true/false
+   * )
+   *
+   * @param array $servers The Redis servers in the cluster.
+   * @param int $replicas
+   * @param bool $standAlone
+   * @throws CredisException
+   */
+  public function __construct($servers, $replicas = 128, $standAlone = false)
+  {
+    $this->clients = array();
+    $this->masterClient = null;
+    $this->aliases = array();
+    $this->ring = array();
+    $this->replicas = (int)$replicas;
+    $client = null;
+    foreach ($servers as $server)
+    {
+      if(is_array($server)){
+          $client = new \Qii\Cache\Redis\Client(
+            $server['host'],
+            $server['port'],
+            isset($server['timeout']) ? $server['timeout'] : 2.5,
+            isset($server['persistent']) ? $server['persistent'] : '',
+            isset($server['db']) ? $server['db'] : 0,
+            isset($server['password']) ? $server['password'] : null
+          );
+          if (isset($server['alias'])) {
+            $this->aliases[$server['alias']] = $client;
+          }
+          if(isset($server['master']) && $server['master'] === true){
+            $this->masterClient = $client;
+            if(isset($server['write_only']) && $server['write_only'] === true){
+                continue;
+            }
+          }
+      } elseif($server instanceof \Qii\Cache\Redis\Client){
+        $client = $server;
+      } else {
+          throw new CredisException('Server should either be an array or an instance of \Qii\Cache\Redis\Client');
+      }
+      if($standAlone) {
+          $client->forceStandalone();
+      }
+      $this->clients[] = $client;
+      for ($replica = 0; $replica <= $this->replicas; $replica++) {
+          $md5num = hexdec(substr(md5($client->getHost().':'.$client->getPort().'-'.$replica),0,7));
+          $this->ring[$md5num] = count($this->clients)-1;
+      }
+    }
+    ksort($this->ring, SORT_NUMERIC);
+    $this->nodes = array_keys($this->ring);
+    $this->dont_hash = array_flip(array(
+      'RANDOMKEY', 'DBSIZE', 'PIPELINE', 'EXEC',
+      'SELECT',    'MOVE',    'FLUSHDB',  'FLUSHALL',
+      'SAVE',      'BGSAVE',  'LASTSAVE', 'SHUTDOWN',
+      'INFO',      'MONITOR', 'SLAVEOF'
+    ));
+    if($this->masterClient !== null && count($this->clients()) == 0){
+        $this->clients[] = $this->masterClient;
+        for ($replica = 0; $replica <= $this->replicas; $replica++) {
+            $md5num = hexdec(substr(md5($this->masterClient->getHost().':'.$this->masterClient->getHost().'-'.$replica),0,7));
+            $this->ring[$md5num] = count($this->clients)-1;
+        }
+        $this->nodes = array_keys($this->ring);
+    }
+  }
+
+  /**
+   * @param \Qii\Cache\Redis\Client $masterClient
+   * @param bool $writeOnly
+   * @return Credis_Cluster
+   */
+  public function setMasterClient(\Qii\Cache\Redis\Client $masterClient, $writeOnly=false)
+  {
+    if(!$masterClient instanceof \Qii\Cache\Redis\Client){
+        throw new CredisException('Master client should be an instance of \Qii\Cache\Redis\Client');
+    }
+    $this->masterClient = $masterClient;
+    if (!isset($this->aliases['master'])) {
+        $this->aliases['master'] = $masterClient;
+    }
+    if(!$writeOnly){
+        $this->clients[] = $this->masterClient;
+        for ($replica = 0; $replica <= $this->replicas; $replica++) {
+            $md5num = hexdec(substr(md5($this->masterClient->getHost().':'.$this->masterClient->getHost().'-'.$replica),0,7));
+            $this->ring[$md5num] = count($this->clients)-1;
+        }
+        $this->nodes = array_keys($this->ring);
+    }
+    return $this;
+  }
+  /**
+   * Get a client by index or alias.
+   *
+   * @param string|int $alias
+   * @throws CredisException
+   * @return \Qii\Cache\Redis\Client
+   */
+  public function client($alias)
+  {
+    if (is_int($alias) && isset($this->clients[$alias])) {
+      return $this->clients[$alias];
+    }
+    else if (isset($this->aliases[$alias])) {
+      return $this->aliases[$alias];
+    }
+    throw new CredisException("Client $alias does not exist.");
+  }
+
+  /**
+   * Get an array of all clients
+   *
+   * @return array|\Qii\Cache\Redis\Client[]
+   */
+  public function clients()
+  {
+    return $this->clients;
+  }
+
+  /**
+   * Execute a command on all clients
+   *
+   * @return array
+   */
+  public function all()
+  {
+    $args = func_get_args();
+    $name = array_shift($args);
+    $results = array();
+    foreach($this->clients as $client) {
+      $results[] = call_user_func_array([$client, $name], $args);
+    }
+    return $results;
+  }
+
+  /**
+   * Get the client that the key would hash to.
+   *
+   * @param string $key
+   * @return \\Qii\Cache\Redis\Client
+   */
+  public function byHash($key)
+  {
+    return $this->clients[$this->hash($key)];
+  }
+
+  /**
+   * @param int $index
+   * @return void
+   */
+  public function select($index)
+  {
+      $this->selectedDb = (int) $index;
+  }
+
+  /**
+   * Execute a Redis command on the cluster with automatic consistent hashing and read/write splitting
+   *
+   * @param string $name
+   * @param array $args
+   * @return mixed
+   */
+  public function __call($name, $args)
+  {
+    if($this->masterClient !== null && !$this->isReadOnlyCommand($name)){
+        $client = $this->masterClient;
+    }elseif (count($this->clients()) == 1 || isset($this->dont_hash[strtoupper($name)]) || !isset($args[0])) {
+      $client = $this->clients[0];
+    }
+    else {
+      $client = $this->byHash($args[0]);
+    }
+    // Ensure that current client is working on the same database as expected.
+    if ($client->getSelectedDb() != $this->selectedDb) {
+      $client->select($this->selectedDb);
+    }
+    return call_user_func_array([$client, $name], $args);
+  }
+
+  /**
+   * Get client index for a key by searching ring with binary search
+   *
+   * @param string $key The key to hash
+   * @return int The index of the client object associated with the hash of the key
+   */
+  public function hash($key)
+  {
+    $needle = hexdec(substr(md5($key),0,7));
+    $server = $min = 0;
+    $max = count($this->nodes) - 1;
+    while ($max >= $min) {
+      $position = (int) (($min + $max) / 2);
+      $server = $this->nodes[$position];
+      if ($needle < $server) {
+        $max = $position - 1;
+      }
+      else if ($needle > $server) {
+        $min = $position + 1;
+      }
+      else {
+        break;
+      }
+    }
+    return $this->ring[$server];
+  }
+
+  public function isReadOnlyCommand($command)
+  {
+      $readOnlyCommands = array(
+          'DBSIZE',
+          'INFO',
+          'MONITOR',
+          'EXISTS',
+          'TYPE',
+          'KEYS',
+          'SCAN',
+          'RANDOMKEY',
+          'TTL',
+          'GET',
+          'MGET',
+          'SUBSTR',
+          'STRLEN',
+          'GETRANGE',
+          'GETBIT',
+          'LLEN',
+          'LRANGE',
+          'LINDEX',
+          'SCARD',
+          'SISMEMBER',
+          'SINTER',
+          'SUNION',
+          'SDIFF',
+          'SMEMBERS',
+          'SSCAN',
+          'SRANDMEMBER',
+          'ZRANGE',
+          'ZREVRANGE',
+          'ZRANGEBYSCORE',
+          'ZREVRANGEBYSCORE',
+          'ZCARD',
+          'ZSCORE',
+          'ZCOUNT',
+          'ZRANK',
+          'ZREVRANK',
+          'ZSCAN',
+          'HGET',
+          'HMGET',
+          'HEXISTS',
+          'HLEN',
+          'HKEYS',
+          'HVALS',
+          'HGETALL',
+          'HSCAN',
+          'PING',
+          'AUTH',
+          'SELECT',
+          'ECHO',
+          'QUIT',
+          'OBJECT',
+          'BITCOUNT',
+          'TIME',
+          'SORT'
+      );
+      return in_array(strtoupper($command),$readOnlyCommands);
+  }
+}
+

+ 346 - 0
src/Cache/Redis/Sentinel.php

@@ -0,0 +1,346 @@
+<?php
+namespace Qii\Cache\Redis;
+/**
+ * Credis_Sentinel
+ *
+ * Implements the Sentinel API as mentioned on http://redis.io/topics/sentinel.
+ * Sentinel is aware of master and slave nodes in a cluster and returns instances of Credis_Client accordingly.
+ *
+ * The complexity of read/write splitting can also be abstract by calling the createCluster() method which returns a
+ * Credis_Cluster object that contains both the master server and a random slave. Credis_Cluster takes care of the
+ * read/write splitting
+ *
+ * @author Thijs Feryn <thijs@feryn.eu>
+ * @license http://www.opensource.org/licenses/mit-license.php The MIT License
+ * @package Credis_Sentinel
+ */
+class Sentinel
+{
+    /**
+     * Contains a client that connects to a Sentinel node.
+     * Sentinel uses the same protocol as Redis which makes using Credis_Client convenient.
+     * @var Credis_Client
+     */
+    protected $_client;
+
+    /**
+     * Contains an active instance of Credis_Cluster per master pool
+     * @var array
+     */
+    protected $_cluster = array();
+
+    /**
+     * Contains an active instance of Credis_Client representing a master
+     * @var array
+     */
+    protected $_master = array();
+
+    /**
+     * Contains an array Credis_Client objects representing all slaves per master pool
+     * @var array
+     */
+    protected $_slaves = array();
+
+    /**
+     * Use the phpredis extension or the standalone implementation
+     * @var bool
+     * @deprecated
+     */
+    protected $_standAlone = false;
+
+    /**
+     * Store the AUTH password used by Credis_Client instances
+     * @var string
+     */
+    protected $_password = '';
+
+    /**
+     * Connect with a Sentinel node. Sentinel will do the master and slave discovery
+     *
+     * @param Credis_Client $client
+     * @param string $password (deprecated - use setClientPassword)
+     * @throws CredisException
+     */
+    public function __construct(Credis_Client $client, $password = NULL)
+    {
+        if(!$client instanceof \Qii\Cache\Redis\Client){
+            throw new CredisException('Sentinel client should be an instance of \Qii\Cache\Redis\Client');
+        }
+        $client->forceStandalone(); // SENTINEL command not currently supported by phpredis
+        $this->_client     = $client;
+        $this->_password   = $password;
+        $this->_timeout    = NULL;
+        $this->_persistent = '';
+        $this->_db         = 0;
+    }
+
+    /**
+     * Clean up client on destruct
+     */
+    public function __destruct()
+    {
+        $this->_client->close();
+    }
+
+    /**
+     * @param float $timeout
+     * @return $this
+     */
+    public function setClientTimeout($timeout)
+    {
+        $this->_timeout = $timeout;
+        return $this;
+    }
+
+    /**
+     * @param string $persistent
+     * @return $this
+     */
+    public function setClientPersistent($persistent)
+    {
+        $this->_persistent = $persistent;
+        return $this;
+    }
+
+    /**
+     * @param int $db
+     * @return $this
+     */
+    public function setClientDatabase($db)
+    {
+        $this->_db = $db;
+        return $this;
+    }
+
+    /**
+     * @param null|string $password
+     * @return $this
+     */
+    public function setClientPassword($password)
+    {
+        $this->_password = $password;
+        return $this;
+    }
+
+    /**
+     * @return Credis_Sentinel
+     * @deprecated
+     */
+    public function forceStandalone()
+    {
+        $this->_standAlone = true;
+        return $this;
+    }
+
+    /**
+     * Discover the master node automatically and return an instance of Credis_Client that connects to the master
+     *
+     * @param string $name
+     * @return Credis_Client
+     * @throws CredisException
+     */
+    public function createMasterClient($name)
+    {
+        $master = $this->getMasterAddressByName($name);
+        if(!isset($master[0]) || !isset($master[1])){
+            throw new CredisException('Master not found');
+        }
+        return new Credis_Client($master[0], $master[1], $this->_timeout, $this->_persistent, $this->_db, $this->_password);
+    }
+
+    /**
+     * If a Credis_Client object exists for a master, return it. Otherwise create one and return it
+     * @param string $name
+     * @return Credis_Client
+     */
+    public function getMasterClient($name)
+    {
+        if(!isset($this->_master[$name])){
+            $this->_master[$name] = $this->createMasterClient($name);
+        }
+        return $this->_master[$name];
+    }
+
+    /**
+     * Discover the slave nodes automatically and return an array of Credis_Client objects
+     *
+     * @param string $name
+     * @return Credis_Client[]
+     * @throws CredisException
+     */
+    public function createSlaveClients($name)
+    {
+        $slaves = $this->slaves($name);
+        $workingSlaves = array();
+        foreach($slaves as $slave) {
+            if(!isset($slave[9])){
+                throw new CredisException('Can\' retrieve slave status');
+            }
+            if(!strstr($slave[9],'s_down') && !strstr($slave[9],'disconnected')) {
+                $workingSlaves[] = new Credis_Client($slave[3], $slave[5], $this->_timeout, $this->_persistent, $this->_db, $this->_password);
+            }
+        }
+        return $workingSlaves;
+    }
+
+    /**
+     * If an array of Credis_Client objects exist for a set of slaves, return them. Otherwise create and return them
+     * @param string $name
+     * @return Credis_Client[]
+     */
+    public function getSlaveClients($name)
+    {
+        if(!isset($this->_slaves[$name])){
+            $this->_slaves[$name] = $this->createSlaveClients($name);
+        }
+        return $this->_slaves[$name];
+    }
+
+    /**
+     * Returns a Redis cluster object containing a random slave and the master
+     * When $selectRandomSlave is true, only one random slave is passed.
+     * When $selectRandomSlave is false, all clients are passed and hashing is applied in Credis_Cluster
+     * When $writeOnly is false, the master server will also be used for read commands.
+     *
+     * @param string $name
+     * @param int $db
+     * @param int $replicas
+     * @param bool $selectRandomSlave
+     * @param bool $writeOnly
+     * @return Credis_Cluster
+     * @throws CredisException
+     * @deprecated
+     */
+    public function createCluster($name, $db=0, $replicas=128, $selectRandomSlave=true, $writeOnly=false)
+    {
+        $clients = array();
+        $workingClients = array();
+        $master = $this->master($name);
+        if(strstr($master[9],'s_down') || strstr($master[9],'disconnected')) {
+            throw new CredisException('The master is down');
+        }
+        $slaves = $this->slaves($name);
+        foreach($slaves as $slave){
+            if(!strstr($slave[9],'s_down') && !strstr($slave[9],'disconnected')) {
+                $workingClients[] =  array('host'=>$slave[3],'port'=>$slave[5],'master'=>false,'db'=>$db,'password'=>$this->_password);
+            }
+        }
+        if(count($workingClients)>0){
+            if($selectRandomSlave){
+                if(!$writeOnly){
+                    $workingClients[] = array('host'=>$master[3],'port'=>$master[5],'master'=>false,'db'=>$db,'password'=>$this->_password);
+                }
+                $clients[] = $workingClients[rand(0,count($workingClients)-1)];
+            } else {
+                $clients = $workingClients;
+            }
+        }
+        $clients[] = array('host'=>$master[3],'port'=>$master[5], 'db'=>$db ,'master'=>true,'write_only'=>$writeOnly,'password'=>$this->_password);
+        return new Credis_Cluster($clients,$replicas,$this->_standAlone);
+    }
+
+    /**
+     * If a Credis_Cluster object exists, return it. Otherwise create one and return it.
+     * @param string $name
+     * @param int $db
+     * @param int $replicas
+     * @param bool $selectRandomSlave
+     * @param bool $writeOnly
+     * @return Credis_Cluster
+     * @deprecated
+     */
+    public function getCluster($name, $db=0, $replicas=128, $selectRandomSlave=true, $writeOnly=false)
+    {
+        if(!isset($this->_cluster[$name])){
+            $this->_cluster[$name] = $this->createCluster($name, $db, $replicas, $selectRandomSlave, $writeOnly);
+        }
+        return $this->_cluster[$name];
+    }
+
+    /**
+     * Catch-all method
+     * @param string $name
+     * @param array $args
+     * @return mixed
+     */
+    public function __call($name, $args)
+    {
+        array_unshift($args,$name);
+        return call_user_func(array($this->_client,'sentinel'),$args);
+    }
+
+    /**
+     * Return information about all registered master servers
+     * @return mixed
+     */
+    public function masters()
+    {
+        return $this->_client->sentinel('masters');
+    }
+
+    /**
+     * Return all information for slaves that are associated with a single master
+     * @param string $name
+     * @return mixed
+     */
+    public function slaves($name)
+    {
+        return $this->_client->sentinel('slaves',$name);
+    }
+
+    /**
+     * Get the information for a specific master
+     * @param string $name
+     * @return mixed
+     */
+    public function master($name)
+    {
+        return $this->_client->sentinel('master',$name);
+    }
+
+    /**
+     * Get the hostname and port for a specific master
+     * @param string $name
+     * @return mixed
+     */
+    public function getMasterAddressByName($name)
+    {
+        return $this->_client->sentinel('get-master-addr-by-name',$name);
+    }
+
+    /**
+     * Check if the Sentinel is still responding
+     * @param string $name
+     * @return mixed
+     */
+    public function ping()
+    {
+        return $this->_client->ping();
+    }
+
+    /**
+     * Perform an auto-failover which will re-elect another master and make the current master a slave
+     * @param string $name
+     * @return mixed
+     */
+    public function failover($name)
+    {
+        return $this->_client->sentinel('failover',$name);
+    }
+
+    /**
+     * @return string
+     */
+    public function getHost()
+    {
+        return $this->_client->getHost();
+    }
+
+    /**
+     * @return int
+     */
+    public function getPort()
+    {
+        return $this->_client->getPort();
+    }
+}

+ 81 - 0
src/Cache/XCache.php

@@ -0,0 +1,81 @@
+<?php
+namespace Qii\Cache;
+
+/**
+ * XCache
+ *
+ * @author Jinhui Zhu<jinhui.zhu@live.cn> 2015-10-26 21:51
+ *
+ */
+class XCache implements Intf
+{
+    const VERSION = '1.2';
+    /**
+     * 默认的缓存策略
+     *
+     * @var array
+     */
+    protected $_default_policy = array(
+        /**
+         * 缓存有效时间
+         *
+         * 如果设置为 0 表示缓存总是失效,设置为 null 则表示不检查缓存有效期。
+         */
+        'life_time' => 900,
+    );
+
+    /**
+     * 构造函数
+     *
+     * @param 默认的缓存策略 $default_policy
+     */
+    public function __construct(array $default_policy = null)
+    {
+        if (isset($default_policy['life_time'])) {
+            $this->_default_policy['life_time'] = (int)$default_policy['life_time'];
+        }
+    }
+
+    /**
+     * 写入缓存
+     *
+     * @param string $id
+     * @param mixed $data
+     * @param array $policy
+     */
+    public function set($id, $data, array $policy = null)
+    {
+        $life_time = !isset($policy['life_time']) ? (int)$policy['life_time'] : $this->_default_policy['life_time'];
+        xcache_set($id, $data, $life_time);
+    }
+
+    /**
+     * 读取缓存,失败或缓存撒失效时返回 false
+     *
+     * @param string $id
+     *
+     * @return mixed
+     */
+    public function get($id)
+    {
+        if (xcache_isset($id)) {
+            return xcache_get($id);
+        }
+        return false;
+    }
+
+    /**
+     * 删除指定的缓存
+     *
+     * @param string $id
+     */
+    public function remove($id)
+    {
+        xcache_unset($id);
+    }
+
+    public function clean()
+    {
+
+    }
+}

+ 207 - 0
src/Config/Arrays.php

@@ -0,0 +1,207 @@
+<?php
+namespace Qii\Config;
+/**
+ * 实现PHP中数组功能
+ *
+ * 用法:
+ *
+ * $array = new \Qii\Config\Arrays();
+ *
+ * $data = array();
+ * $data['common']['one'] = '1';
+ * $data['common']['two'] = '1';
+ * $data['common']['three'] = '1';
+ *
+ * $array->getValueFromArray($data, '[common][three]');
+ * $array->setPrivate('string', 'string');
+ * $array->setPrivate('array[val][]', array(1, 2));
+ * $array->setPrivate('array[val][]', array(3, 4));
+ *
+ * $array->getPrivate('string');
+ *
+ * $array->getPrivate('array[val]');
+ * $array->set('string', 'string');
+ * $array->set('array[val][]', array(1, 2));
+ * $array->set('array[val][]', array(3, 4));
+ *
+ * $array->get('string');
+ *
+ * $array->get('array[val]');
+ *
+ * $array->get('array[val][0]');
+ *
+ * $array->get('array[val][1]');
+ *
+ *
+ */
+
+class Arrays
+{
+	const VERSION = '1.2';
+	protected $_private = array();
+
+	public function __construct()
+	{
+
+	}
+
+	public function __toString()
+	{
+		return self::VERSION;
+	}
+
+	/**
+	 * 直接从数组中获取指定key的值
+	 *
+	 * @param Array $data
+	 * @param String $key
+	 * @return Mix
+	 */
+	public function getValueFromArray($data, $key)
+	{
+		if (preg_match('/^\s*$/', $key)) {
+			return $data;
+		}
+		preg_match_all("/(.*?)\[(.*?)\]/", $key, $match);
+
+		$name = $match[1][0];
+		$keys = $match[2];
+
+		if ($name == '') {
+			return isset($data[$key]) ? $data[$key] : '';
+		}
+		if (!isset($data[$name])) {
+			return '';
+		}
+		$value = $data[$name];
+		foreach ($keys AS $key) {
+			if ($key == '') {
+				$value = $value;
+			} else {
+				$value = $value[$key];
+			}
+		}
+		return $value;
+	}
+
+	/**
+	 * 实现PHP数组赋值
+	 *
+	 * @param String $key
+	 * @param Mix $value
+	 * @return Array
+	 */
+	public function set($key, $value)
+	{
+		preg_match_all("/(.*?)\[(.*?)\]/", $key, $match);
+		$name = '';
+		if (isset($match[1]) && isset($match[1][0])) {
+			$name = $match[1][0];
+		}
+		$keys = $match[2];
+		if ($name == '') {
+			$name = $key;
+		}
+		if (empty($keys)) {
+			$this->_private[$key] = $value;
+			return $this->_private;
+		}
+		$private = array();
+		$private = array_merge($private, $keys);
+		$privates = null;
+		if (is_array($value) || is_object($value)) {
+			$array = str_replace('[\'\']', '[]', '$privates[\'' . join("']['", $private) . '\']=$value;');
+		} else {
+			$array = str_replace('[\'\']', '[]', '$privates[\'' . join("']['", $private) . '\']=\'' . $value . '\';');
+		}
+		eval($array);
+		if (isset($this->_private[$name])) {
+			if (!is_array($this->_private[$name])) {
+				unset($this->_private[$name]);
+				$this->_private[$name] = $privates;
+			} else {
+				$this->_private[$name] = array_merge_recursive($this->_private[$name], $privates);
+			}
+		} else {
+			$this->_private[$name] = $privates;
+		}
+		return $this->_private;
+	}
+	/**
+	 * 实现PHP数组赋值
+	 *
+	 * @param String $key
+	 * @param Mix $value
+	 * @return Array
+	 */
+	public function setPrivate($key, $value)
+	{
+		preg_match_all("/(.*?)\[(.*?)\]/", $key, $match);
+		$name = '';
+		if (isset($match[1]) && isset($match[1][0])) {
+			$name = $match[1][0];
+		}
+		$keys = $match[2];
+		if ($name == '') {
+			$name = $key;
+		}
+		if (empty($keys)) {
+			$this->_private[$key] = $value;
+			return $this->_private;
+		}
+		$private = array();
+		$private = array_merge($private, $keys);
+		$privates = null;
+		if (is_array($value) || is_object($value)) {
+			$array = str_replace('[\'\']', '[]', '$privates[\'' . join("']['", $private) . '\']=$value;');
+		} else {
+			$array = str_replace('[\'\']', '[]', '$privates[\'' . join("']['", $private) . '\']=\'' . $value . '\';');
+		}
+		eval($array);
+		if (isset($this->_private[$name])) {
+			if (!is_array($this->_private[$name])) {
+				unset($this->_private[$name]);
+				$this->_private[$name] = $privates;
+			} else {
+				$this->_private[$name] = array_merge_recursive($this->_private[$name], $privates);
+			}
+		} else {
+			$this->_private[$name] = $privates;
+		}
+		return $this->_private;
+	}
+
+	/**
+	 * 获取通过setPrivate key对应的值
+	 *
+	 * @param String $key
+	 * @return Mix
+	 */
+	public function get($key)
+	{
+		if (preg_match('/^\s*$/', $key)) {
+			return $this->_private;
+		}
+		preg_match_all("/(.*?)\[(.*?)\]/", $key, $match);
+		$name = '';
+		if (isset($match[1]) && isset($match[1][0])) {
+			$name = $match[1][0];
+		}
+		$keys = $match[2];
+		if ($name == '') {
+			return isset($this->_private[$key]) ? $this->_private[$key] : '';
+		}
+		if (!isset($this->_private[$name])) {
+			return '';
+		}
+		$value = $this->_private[$name];
+		foreach ($keys AS $key) {
+			if ($key == '') {
+				$value = $value;
+			} else {
+				$value = $value[$key];
+			}
+		}
+		return $value;
+	}
+}

+ 37 - 0
src/Config/Consts.php

@@ -0,0 +1,37 @@
+<?php
+namespace Qii\Config;
+
+/**
+ * App系统配置
+ * @author Jinhui.Zhu <jinhui.zhu@live.cn> 2015-09-21 11:31
+ *
+ */
+class Consts
+{
+	const VERSION = '1.2';
+	const APP_SYS = 'Qii';//框架名,用户保存框架实例化对象
+	const APP_ENVIRONS = 'QII_ENVIRONS';//配置文件使用环境
+	const APP_ENVIRON = 'QII_ENVIRON';//当前系统使用的环境
+	const APP_DEFAULT_ENVIRON = 'product';//默认系统环境
+	const APP_DB = 'QII_APP_DB';//数据库配置
+	const APP_CONFIGURE = 'AppConfigure';//系统配置,不能修改为其他值,默认会调用此方法
+	const APP_LOADED_FILE = 'QII_APP_LOADED_FILE';//加载的文件列表
+	const APP_INI_FILE = 'QII_APP_INI_FILE';//网站配置文件列表
+	const APP_INCLUDES_FILE = 'QII_APP_INCLUDES_FILE';//保存includes过的文件列表
+	const APP_SITE_METHOD = 'rewriteMethod';//网站路由
+	const APP_SITE_ROUTER = 'QII_APP_SITE_ROUTER';//网站路由
+	const APP_DISPATCHER = 'Qii_Dispatcher';
+	const APP_SYS_DISPATCHER = 'QII_APP_SYS_DISPATCHER';
+	const APP_LOAD_PREFIX = 'Qii_Load_';//不能随意修改
+	const APP_LOAD_SYS_PREFIX = 'Qii_Load_Qii_';//不能随意修改
+	const APP_CACHE_PATH = 'QII_CACHE_PATH';//App缓存目录
+	const APP_CACHE_SYSTEM_PATH = 'QII_CACHE_SYSTEM_PATH';//框架缓存目录
+	const APP_INI = 'app.ini';//网站配置文件
+	const APP_LANGUAGE_CONFIG = 'QII_LANGUAGE_CONFIG';//语言包
+	const APP_OUTPUT_CHARSET = 'utf-8';
+	const APP_VIEW = 'QII_VIEW';//controller->_view
+	const APP_DEFAULT_CONTROLLER = 'APP_DEFAULT_CONTROLLER';//默认controller
+	const APP_DEFAULT_CONTROLLER_PREFIX = 'APP_DEFAULT_CONTROLLER_PREFIX';//默认controller
+	const APP_DEFAULT_ACTION = 'APP_DEFAULT_ACTION';//默认Action
+	const APP_DEFAULT_ACTION_SUFFIX = 'APP_DEFAULT_ACTION_SUFFIX';//默认Action后缀
+}

+ 339 - 0
src/Config/Register.php

@@ -0,0 +1,339 @@
+<?php
+/**
+ * 将键值保存到\Qii\Config\Register::$config中
+ * 用于保存系统相关设置,保存项目相关的配置,请注意不出现key相互覆盖的情况,可以考虑加前缀
+ *
+ * @author Jinhui Zhu
+ * @version 1.3
+ *
+ * Usage:
+ *    保存值:
+ *        \Qii\Config\Register::set($key, $val);
+ *    读取值:
+ *        \Qii\Config\Register::get($key, $default);
+ *        \Qii\Config\Register::$key($default);
+ *
+ *
+ */
+namespace Qii\Config;
+
+use \Qii\Application;
+use \Qii\Autoloader\Psr4;
+
+use \Qii\Config\Register;
+use \Qii\Config\Consts;
+
+use \Qii\Exceptions\Variable;
+
+class Register
+{
+	const VERSION = '1.3';
+	/**
+	 * 存储键值的变量
+	 *
+	 * @var Array
+	 */
+	public static $_cache;
+
+	/**
+	 * 设置键值
+	 *
+	 * @param String $key
+	 * @param String $val
+	 * @param Bool overwrite 是否覆盖之前保存的值,如果之前保存了值,要想再保存需要额外设置它为true,否则不让保存
+	 */
+	public static function set($key, $val, $overwrite = true)
+	{
+		if (!Register::isValid($key, $overwrite)) \Qii\Application::_e('Overwrite', $key, __LINE__);
+		Register::$_cache[$key] = $val;
+	}
+
+	/**
+	 * 设置键
+	 *
+	 * @param String $index
+	 * @param String $key
+	 * @param String $val
+	 */
+	public static function add($index, $key, $val)
+	{
+		$added = Register::get(Consts::APP_LOADED_FILE, array());
+		$added[$index][$key] = $val;
+		Register::$_cache[$index] = $added;
+	}
+
+	/**
+	 * 移除某一个key
+	 * @param String $key
+	 */
+	public static function remove($key)
+	{
+		if (!isset(Register::$_cache[$key])) return;
+		unset(Register::$_cache[$key]);
+	}
+
+	/**
+	 * 获取保存的值
+	 *
+	 * @param String $key
+	 * @param String $index
+	 * @param Mix $default
+	 * @return Mix
+	 */
+	public static function get($key, $default = null)
+	{
+		if (!$key) throw new \Qii\Exceptions\Variable(\Qii::i(1003), __LINE__);
+		//优先调用存在的get方法
+		$method = 'get' . $key;
+		if (method_exists('\Qii\Config\Register', $method)) return Register::$method();
+
+		if (isset(Register::$_cache[$key])) {
+			return Register::$_cache[$key];
+		}
+		return $default;
+	}
+
+	/**
+	 * 通过\Qii\Config\Register::$key($defaultVal)来获取内容
+	 *
+	 * @param String $method
+	 * @param Array $argvs
+	 * @return Mix
+	 */
+	public static function __callStatic($method, $argvs)
+	{
+		$default = array_shift($argvs);
+		return Register::get($method, $default);
+	}
+
+	/**
+	 * 整理数组,将0.key 最后会整理到 [0][key]中
+	 * @param Array $array
+	 * @return multitype:
+	 */
+	public static function feval($array)
+	{
+		$data = array();
+		foreach ($array AS $key => $value) {
+			$keys = explode('.', $key);
+			if (is_array($value)) {
+				$string = "\$data['" . join("']['", $keys) . "']=" . var_export(Register::feval($value), true) . ";";
+			} else {
+				$string = "\$data['" . join("']['", $keys) . "']='" . $value . "';";
+			}
+			eval($string);
+		}
+		return $data;
+	}
+
+	/**
+	 * 读取ini配置文件
+	 *
+	 * @param String $fileName
+	 * @return Array
+	 */
+	public static function ini($fileName)
+	{
+		if (!$fileName) throw new \Qii\Exceptions\Variable(\Qii::i(1408), __LINE__);
+		$ini = parse_ini_file($fileName, true);
+		if (!$ini) throw new \Qii\Exceptions\InvalidFormat($fileName, __LINE__);
+		$config = array();
+		foreach ($ini AS $namespace => $properties) {
+			$properties = Register::feval($properties);
+			$extends = '';
+			$name = $namespace;
+			$namespaces = array();
+			if (stristr($namespace, ':')) {
+				$namespaces = explode(':', $namespace);
+				$name = array_shift($namespaces);
+			}
+			$name = trim($name);
+			$config[$name] = $properties;
+			if (count($namespaces) > 0) {
+				foreach ($namespaces AS $space) {
+					//如果space以“.”开头,与key的方式放在当前key下边如[dev:.space],那么生成后的数据就是这样的[dev][space]否则是[space+dev]
+					if (substr($space, 0, 1) == '.') {
+						$space = substr($space, 1);
+						if (isset($config[$space])) $config[$name][$space] = $config[$space];
+						continue;
+					}
+					if (isset($config[$space])) $config[$name] = array_merge($config[$space], $config[$name]);
+				}
+			}
+		}
+		return $config;
+	}
+
+	/**
+	 * 返回cache的名称
+	 * @param String $iniFile
+	 * @return String
+	 */
+	public static function getCacheName($iniFile)
+	{
+		$cacheName = basename($iniFile);
+		$environs = Register::get(Consts::APP_ENVIRONS, array());
+		if (isset($environs[$cacheName])) {
+			$environ = $environs[$cacheName];
+			$cacheName = $environ . '.' . $cacheName;
+		}
+		return $cacheName;
+	}
+
+	/**
+	 * 覆盖/添加ini文件的key对应的值
+	 * @param String $iniFile ini文件名
+	 * @param String $key 需覆盖的key
+	 * @param String $val key对应的值
+	 */
+	public static function rewriteConfig($iniFile, $key, $val)
+	{
+		$config = Register::getIniConfigure($iniFile);
+		$cacheName = Register::getCacheName($iniFile);
+		$config[$key] = $val;
+		Register::set($cacheName, $config);
+	}
+	/**
+	 * 删除ini配置文件中对应的key
+	 * @param string $iniFile ini配置我呢见
+	 * @param string $key 陪删除的key
+	 */
+	public static function removeAppConfigure($iniFile, $key)
+	{
+		$config = Register::getIniConfigure($iniFile);
+		$cacheName = Register::getCacheName($iniFile);
+		unset($config[$key]);
+		Register::set($cacheName, $config);
+	}
+
+	/**
+	 * 合并ini文件生成的数组
+	 * @param String $iniFile ini文件名
+	 * @param Array $array
+	 */
+	public static function mergeAppConfigure($iniFile, $array)
+	{
+		if (!is_array($array)) return;
+		$config = Register::getIniConfigure($iniFile);
+
+		$environs = Register::get(Consts::APP_ENVIRONS, array());
+
+		$cacheName = basename($iniFile);
+		if (isset($environs[$cacheName])) {
+			$environ = $environs[$cacheName];
+			$cacheName = $environ . '.' . $cacheName;
+		}
+		$config = array_merge($config, $array);
+		Register::set($cacheName, $config);
+	}
+
+	/**
+	 * 获取配置ini文件
+	 * @param String $iniFile
+	 * @param String $environ
+	 * @return boolean
+	 */
+	public static function setConfig($iniFile, $environ = 'product')
+	{
+		$cacheName = basename($iniFile);
+		$environs = Register::get(Consts::APP_ENVIRONS, array());
+		$environs[$cacheName] = $environ;
+		Register::set(Consts::APP_ENVIRONS, $environs);
+
+		$cacheName = $environ . '.' . $cacheName;
+		if (!is_file($iniFile)) return false;
+		$cacheFile = Psr4::getInstance()->getFileByPrefix(Register::get(Consts::APP_CACHE_PATH) . DS . $cacheName . '.php');
+		if (Register::get(Consts::APP_CACHE_PATH)) {
+			if (is_file($cacheFile)) {
+				if (filemtime($cacheFile) == filemtime($iniFile)) {
+					$common = include($cacheFile);
+					Register::set($cacheName, $common);
+					return $common;
+				}
+			}
+		}
+		$array = Register::ini($iniFile);
+		if (!$array) return false;
+
+		$common = $array['common'];
+		if (isset($array[$environ])) {
+			$environConfig = $array[$environ];
+			$common = array_merge($common, $environConfig);
+		}
+		//如果文件不可写,touch就有问题,就不写缓存文件
+		if (is_writeable($iniFile)) {
+			file_put_contents($cacheFile, "<?php \n return " . var_export($common, true) . "\n?>", LOCK_EX);
+			touch($iniFile);
+		}
+		Register::set($cacheName, $common);
+		return $common;
+	}
+
+	/**
+	 * 设置网站的配置文件
+	 *
+	 * @param String $iniFile 配置我呢见
+	 * @param string $environ 环境变量
+	 * @return Object self
+	 */
+	public static function setAppConfigure($iniFile, $environ = 'product')
+	{
+		return Register::setConfig($iniFile, $environ);
+	}
+
+	/**
+	 * 获取配置ini文件相关信息
+	 * @param String $fileName 文件名
+	 * @return Ambigous <Mix, multitype:>
+	 */
+	public static function getIniConfigure($fileName)
+	{
+		$cacheName = basename($fileName);
+		$environs = Register::get(Consts::APP_ENVIRONS, array());
+		if (isset($environs[$cacheName])) {
+			$cacheName = $environs[$cacheName] . '.' . $cacheName;
+		}
+		return Register::get($cacheName);
+	}
+
+	/**
+	 * 获取网站的配置信息
+	 *
+	 * @return Array
+	 */
+	public static function getAppConfigure($iniFile = Consts::APP_INI, $key = NULL)
+	{
+		$appConfigure = Register::getIniConfigure($iniFile);
+		if ($key == null) return $appConfigure;
+		return isset($appConfigure[$key]) ? $appConfigure[$key] : NULL;
+	}
+
+	/**
+	 * 验证是否之前已经保存过这个属性,如果保存过,不覆盖属性对应的值就不保存
+	 *
+	 * @param String $key
+	 * @param Bool $overwrite
+	 * @return Bool
+	 */
+	public static function isValid($key, $overwrite = false)
+	{
+		if ($overwrite) return true;
+		if (isset(Register::$_cache[$key])) return false;
+		return true;
+	}
+
+	/**
+	 * 获取当前系统环境
+	 * @return Ambigous <Mix, multitype:>
+	 */
+	public static function getAppEnviron()
+	{
+		return isset(Register::$_cache[Consts::APP_ENVIRON]) ?
+                    Register::$_cache[Consts::APP_ENVIRON]
+                    : Consts::APP_DEFAULT_ENVIRON;
+	}
+
+	public function __call($method, $argvs)
+	{
+	}
+}

+ 94 - 0
src/Config/Setting.php

@@ -0,0 +1,94 @@
+<?php
+/**
+ * 系统设置
+ *
+ */
+namespace Qii\Config;
+
+use \Qii\Autoloader\Psr4;
+
+use \Qii\Config\Register;
+use \Qii\Config\Consts;
+
+class Setting
+{
+    protected static $instance;
+    public $language;
+
+    private function __construct()
+    {
+
+    }
+
+    /**
+     * 返回Qii初始化类
+     */
+    public static function getInstance()
+    {
+        if (self::$instance == null) {
+            self::$instance = new self();
+        }
+        return self::$instance;
+    }
+
+    /**
+     * 设置默认语言
+     *
+     * @return $this
+     * @throws Exception
+     */
+    public function setDefaultLanguage()
+    {
+        $this->language = Psr4::getInstance()->loadClass('\Qii\Language\Loader');
+        //加载语言包
+        $this->language->load('error', Qii_DIR);
+        $this->language->load('exception', Qii_DIR);
+        return $this;
+    }
+
+    /**
+     * 设置区域
+     *
+     * @return $this
+     */
+    public function setDefaultTimeZone()
+    {
+        //设置时区
+        $timezone = \Qii::getInstance()->appConfigure('timezone');
+        if ($timezone) date_default_timezone_set($timezone);
+        return $this;
+    }
+
+    /**
+     * 设置默认的controller和action
+     */
+    public function setDefaultControllerAction()
+    {
+        //设置默认controller及controller方法前缀
+        Register::set(Consts::APP_DEFAULT_CONTROLLER, \Qii::getInstance()->appConfigure('controller')['default']);
+        Register::set(Consts::APP_DEFAULT_CONTROLLER_PREFIX, \Qii::getInstance()->appConfigure('controller')['prefix']);
+
+        //设置默认action及方法名后缀
+        Register::set(Consts::APP_DEFAULT_ACTION, \Qii::getInstance()->appConfigure('action')['default']);
+        Register::set(Consts::APP_DEFAULT_ACTION_SUFFIX, \Qii::getInstance()->appConfigure('action')['suffix']);
+        return $this;
+    }
+
+    /**
+     * 设置默认的namespace
+     */
+    public function setDefaultNamespace()
+    {
+        //配置文件中如果设置了使用namespace就将指定的前缀添加到namespace中
+        $namespaces = \Qii::appConfigure('namespace');
+        if (is_array($namespaces) && isset($namespaces['use'])
+            && $namespaces['use'] && isset($namespaces['list'])
+            && is_array($namespaces['list'])
+        ) {
+            foreach ($namespaces['list'] AS $namespace => $val) {
+                \Qii::getInstance()->setUseNamespace($namespace, $val);
+            }
+        }
+        return $this;
+    }
+}

+ 901 - 0
src/Driver/Base.php

@@ -0,0 +1,901 @@
+<?php
+namespace Qii\Driver;
+
+class Base
+{
+	const VERSION = '1.2';
+	public $_cache;
+	public $language;
+	protected $_query = array(
+		"INSERT" => "INSERT INTO %s(%s) VALUES('%s')",
+		"REPLACE" => "REPLACE %s (%s) VALUES('%s')",
+		"SELECT" => "SELECT %s FROM %s",
+		"UPDATE" => "UPDATE %s SET ",
+		"DELETE" => "DELETE FROM %s %s",
+		"WHERE" => " WHERE %s",
+		"ORDER" => " ORDER BY %s %s",
+		"GROUP" => " GROUP BY %s",
+		"LIMIT" => " LIMIT %d, %d"
+	);
+	private $query;
+	private $setArray = array();
+	public $modelSQL = "";
+	protected $fields;
+	protected $where;
+	protected $groupBy;
+	protected $limit;
+	protected $orderBy;
+	public $load;
+	/**
+	 * @var string $_response Response对象
+	 */
+	protected $_response;
+
+	//方法对应的别名
+	protected $_modelAlias = array('selectRows' => 'selectAll', 'select' => 'selectRow', 'getOne' => 'selectOne', 'getRow' => 'selectRow', 'getAll' => 'selectAll', 'remove' => 'deleteRows');
+
+	public function __construct()
+	{
+		$this->language = \Qii\Autoloader\Psr4::getInstance()->loadClass('Qii\Language\Loader');
+		$this->load = \Qii\Autoloader\Psr4::getInstance()->loadClass('\Qii\Autoloader\Loader');
+		$this->_response = new \Qii\Driver\Response();
+	}
+	/**
+	 * 获取数据库中所有的数据表
+	 * @return array
+	 */
+	public function getAllDatabases()
+	{
+		$sql = "SHOW DATABASES";
+		$rs = $this->setQuery($sql);
+		
+		$database = array();
+		while($row = $rs->fetch())
+		{
+			$database[] = $row['Database'];
+		}
+		return $database;
+	}
+	/**
+	 * 数据库中是否包含指定库
+	 * @param string $database 数据库名
+	 * @return bool
+	 */
+	public function hasDatabase($database)
+	{
+		if(!$database) return false;
+		$sql = "SHOW DATABASES LIKE '". $database ."'";
+		$rs = $this->setQuery($sql);
+		while($row = $rs->fetch())
+		{
+			$val = array_values($row);
+			if(in_array($database, $val))
+			{
+				return true;
+			}
+		}
+		return false;
+	}
+	/**
+	 * 获取当前数据库中所有的表
+	 * @param null|string $database 数据库
+	 * @return array
+	 */
+	public function getAllTables($database = null)
+	{
+		if($database)
+		{
+			$this->setQuery('USE '. $database);
+		}
+		$sql = "SHOW TABLES";
+		$rs = $this->setQuery($sql);
+		$tables = array();
+		while($row = $rs->fetch())
+		{
+            if(is_array($row)) {
+                foreach($row AS $val) {
+                    $tables[] = $val;
+                }
+            }
+		}
+		return $tables;
+	}
+	/**
+	 * 获取指定数据表的所有字段
+	 * @param string $table 表名
+	 * @param string $database 数据库名 
+	 * @return array
+	 */
+	public function getTableInfo($table, $database = null)
+	{
+		if(!$database) $database = $this->currentDB;
+		$sql = "SELECT * from information_schema.COLUMNS where table_name = '".$table."' and table_schema = '".$database."'";
+		$data = ['fields' => [], 
+					'rules' => [
+						'pri' => [], 'required' => []
+					]
+				];
+		
+		$rs = $this->setQuery($sql);
+		while($row = $rs->fetch())
+		{
+			$data['fields'][] = $row['COLUMN_NAME'];
+			if($row['EXTRA'])
+			{
+				$data['rules']['extra'][$row['EXTRA']][] = $row['COLUMN_NAME'];
+			}
+			if($row['COLUMN_KEY'] == 'PRI') 
+			{
+				$data['rules']['pri'][] = $row['COLUMN_NAME'];
+				$data['rules']['required'][] = $row['COLUMN_NAME'];
+			}
+			if($row['IS_NULLABLE'] == 'NO')
+			{
+				$data['rules']['required'][] = $row['COLUMN_NAME'];
+			}
+			if(in_array($row['DATA_TYPE'], ['varchar', 'char']))
+			{
+				$data['rules']['maxlength'][$row['COLUMN_NAME']] = $row['CHARACTER_MAXIMUM_LENGTH'];
+			}
+			if(in_array($row['DATA_TYPE'], ['mediumtext', 'TINYTEXT', 'text', 'longtext']))
+			{
+				$data['rules']['text'][] = $row['COLUMN_NAME'];
+			}
+			if(in_array($row['DATA_TYPE'], ['bigint', 'int', 'smallint', 'tinyint', 'integer']))
+			{
+				preg_match('/[\d]{1,}/', $row['COLUMN_TYPE'], $matches);
+				$data['rules']['int'][$row['COLUMN_NAME']] = $matches[0];
+				$data['rules']['number'][] = $row['COLUMN_NAME'];
+			}
+			if(in_array($row['DATA_TYPE'], ['float', 'double', 'decimal']))
+			{
+				$data['rules']['float'][$row['COLUMN_NAME']] = $this->getValueFromBrackets($row['COLUMN_TYPE']);
+			}
+			if(in_array($row['DATA_TYPE'], ['timestamp', 'datatime']))
+			{
+				$data['rules']['timestamp'][] = $row['COLUMN_NAME'];
+			}
+			if(in_array($row['DATA_TYPE'], ['enum', 'set']))
+			{
+				$data['rules']['sets'][$row['COLUMN_NAME']] = $this->getValueFromBrackets($row['COLUMN_TYPE']);
+			}
+			if(isset($row['COLUMN_DEFAULT']))
+			{
+				$data['rules']['default'][$row['COLUMN_NAME']] = $row['COLUMN_DEFAULT'];
+			}
+		}
+		$data['sql'] = $this->getTableSQL($table, $database);
+		return $data;
+	}
+	/**
+	 * 从括号中获取指定的值
+	 * @param string $str 需要获取的内容
+	 * @return array
+	 */
+	public function getValueFromBrackets($str)
+	{
+		preg_match("/(?:\()(.*)(?:\))/i", $str, $matches);
+		$str = $matches[1];
+		$a = explode(",", $str);
+		for($i=0; $i<count($a); $i++)
+		{
+			$this->removeQuote($a[$i]);//从字符串中去除单引号
+		}
+		return $a;
+	}
+	/**
+	 * 去除双引号
+	 * @param string $str 去除双引号 
+	 */
+	public function removeQuote(&$str) 
+	{
+		if(preg_match("/^\'/",$str))
+		{
+			$str = substr($str, 1, strlen($str)-1);
+		}
+		if(preg_match("/\'$/",$str))
+		{
+			$str = substr($str, 0, strlen($str)-1);
+		}
+		return $str;
+	}
+	/**
+	 * 查询数据库中是否有指定的表
+	 * @param string $tableName 表名
+	 * @param null|string $database 数据库
+	 * @return bool
+	 */
+	public function hasTable($tableName, $database = null)
+	{
+		if($database)
+		{
+			$this->setQuery('USE '. $database);
+		}
+		$sql = "SHOW TABLES LIKE '".$tableName."'";
+		$rs = $this->setQuery($sql);
+
+		$tables = array();
+		while($row = $this->fetch($rs))
+		{
+            if(is_array($row)) {
+                foreach($row AS $val) {
+                    if($val == $tableName) return true;
+                }
+            }
+		}
+		return false;
+	}
+	/**
+	 * 获取创建表的SQL语句
+	 * @param string $tableName 表名
+	 * @param null|string $database 数据库
+	 * @param int|null $autoIncr 自增长的值,null的话就不使用
+	 * @return string
+	 */
+	public function getTableSQL($tableName, $database = null, $autoIncr = null)
+	{
+		if($database)
+		{
+			$this->setQuery('USE '. $database);
+		}
+		$row = $this->getRow("SHOW CREATE TABLE `".$tableName."`");
+		if(!$row) {
+			throw new \Exception('数据表不存在', __LINE__);
+		}
+		$sql = $row['Create Table'];
+		if($autoIncr === null) {
+			return $sql;
+		}
+		return preg_replace("/AUTO_INCREMENT=[\d]{1,}/", "AUTO_INCREMENT=". intval($autoIncr), $sql);
+	}
+	/**
+	 * 通过数据表名称获取规则
+	 * @param string $table 表名
+	 * @param string $database 数据库名 
+	 */
+	public function buildRulesForTable($table, $database = null)
+	{
+		if(!$database) $database = $this->currentDB;
+		$tableInfo = $this->getTableInfo($table, $database);
+		$rules = new \Qii\Base\Rules();
+		$rules->addFields($tableInfo['fields']);
+		if($tableInfo['rules']['required'])
+		{
+			$rules->addForceValidKey($tableInfo['rules']['required']);
+		}
+		if(isset($tableInfo['rules']['number']))
+		{
+			foreach ($tableInfo['rules']['number'] as $key => $value) 
+			{
+				$rules->addRules($value, 'number', true, $value . '字段必须是数字');
+			}
+		}
+		if(isset($tableInfo['rules']['maxlength']))
+		{
+			foreach ($tableInfo['rules']['maxlength'] as $key => $value) 
+			{
+				$rules->addRules($key, 'maxlength', $value, $key . '字段内容长度不能大于'. $value .'个字符');
+			}
+		}
+		if(isset($tableInfo['rules']['timestamp']))
+		{
+			foreach ($tableInfo['rules']['timestamp'] as $key => $value) 
+			{
+				$rules->addRules($value, 'datetime', true, $value . '字段必须为日期格式');
+			}
+		}
+		return $rules;
+	}
+	
+	final public function getAlias($alias)
+	{
+		return isset($this->_modelAlias[$alias]) ? $this->_modelAlias[$alias] : null;
+	}
+
+	/**
+	 * 设置Cache
+	 *
+	 * @param String $cache
+	 * @param Array $policy
+	 */
+	final public function setCache($cache, $policy)
+	{
+		\Qii\Autoloader\Import::requires(Qii_DIR . DS . 'Qii' . DS . 'Cache.php');
+		$this->_cache = \Qii\Autoloader\Psr4::loadClass('\Qii\Cache', $cache)->initialization($policy);//载入cache类文件
+	}
+
+	/**
+	 * 缓存内容
+	 *
+	 * @param String $id
+	 * @param Array $value
+	 * @return Bool
+	 */
+	final public function cache($id, $value)
+	{
+		return $this->_cache->set($id, $value);
+	}
+
+	/**
+	 * 获取缓存的类
+	 */
+	final public function getCache()
+	{
+		return $this->_cache;
+	}
+
+	/**
+	 * 获取缓存内容
+	 *
+	 * @param String $id
+	 * @return Array
+	 */
+	final public function getCacheData($id)
+	{
+		return $this->_cache->get($id);
+	}
+
+	/**
+	 * 获取表的名称
+	 * @param String $table
+	 * @return String
+	 */
+	public function getTable($table)
+	{
+		return $table;
+	}
+
+	public function setLanguage()
+	{
+		$this->language = \Qii\Autoloader\Psr4::loadClass('Qii_Language_Loader');
+	}
+
+	/**
+	 *
+	 * Insert Object
+	 * @param String $table
+	 * @param Array|Object $dataArray
+	 */
+	final function insertObject($table, $dataArray)
+	{
+		if (empty($table)) {
+			return -1;
+		}
+		if (sizeof($dataArray) > 0 || (is_object($dataArray) && get_object_vars($dataArray)) > 0) {
+			$keys = array();
+			$values = array();
+			foreach ($dataArray AS $key => $value) {
+				$keys[] = $key;
+				if(is_array($value))
+				{
+					throw new \Qii\Exceptions\InvalidFormat(_i('Invalid %s format', $key), __LINE__);
+				}
+				$values[] = $this->setQuote($value);
+			}
+			$this->modelSQL = $sql = "INSERT INTO `{$table}`(`" . join("`, `", $keys) . "`) VALUES('" . join("', '", $values) . "')";
+			$this->setQuery($sql);
+			$this->cleanData();
+			$this->setError();
+			return $this->lastInsertId();
+		}
+		return -2;
+	}
+
+	/**
+	 *
+	 * Replace Object
+	 * @param String $table
+	 * @param Array|Object $dataArray
+	 */
+	final function replaceObject($table, $dataArray)
+	{
+		if (empty($table)) {
+			return -1;
+		}
+		if (sizeof($dataArray) > 0 || get_object_vars($dataArray) > 0) {
+			$keys = array();
+			$values = array();
+			foreach ($dataArray AS $key => $value) {
+				$keys[] = $key;
+				if(is_array($value))
+				{
+					throw new \Qii\Exceptions\InvalidFormat(_i('Invalid %s format', $key), __LINE__);
+				}
+				$values[] = $this->setQuote($value);
+			}
+			$this->modelSQL = $sql = "REPLACE INTO `{$table}`(`" . join("`, `", $keys) . "`) VALUES('" . join("', '", $values) . "')";
+			$rs = $this->setQuery($sql);
+			$this->cleanData();
+			$this->setError();
+			return $this->AffectedRows($rs);
+		}
+		return -2;
+	}
+
+	/**
+	 *
+	 * Update data
+	 * @param String $table
+	 * @param Array|Objct $dataArray
+	 * @param Array $keys
+	 */
+	final function updateObject($table, $dataArray, $keys = array())
+	{
+		if (empty($table) || !is_array($keys)) {
+			return -1;
+		}
+		if (sizeof($dataArray) > 0 || get_object_vars($dataArray) > 0) {
+			$values = array();
+			$where = array();
+			foreach ($dataArray AS $key => $value) {
+				if(is_array($value))
+				{
+					throw new \Qii\Exceptions\InvalidFormat(_i('Invalid %s format', $key), __LINE__);
+				}
+				$value = $this->setQuote($value);
+				if (in_array($key, $keys)) {
+					$where[] = "`{$key}` = '" . $value . "'";
+				} else {
+					$values[] = "`{$key}` = '" . $value . "'";
+				}
+			}
+			//$keys为key => value的方式,就直接用keys
+			if (empty($where) && count($keys) > 0) {
+				foreach ($keys as $key => $value) {
+					$value = $this->setQuote($value);
+					$where[] = "`{$key}` = '" . $value . "'";
+				}
+			}
+			$this->modelSQL = $sql = "UPDATE `{$table}` SET " . join(", ", $values) . (sizeof($where) > 0 ? " WHERE " . join(" AND ", $where) : '');
+			$rs = $this->setQuery($sql);
+			$this->cleanData();
+			$this->setError();
+			return $this->AffectedRows($rs);
+		}
+		return 0;
+	}
+
+	/**
+	 *
+	 * 删除数据
+	 * @param String $table
+	 * @param Array $keys
+	 */
+	final function deleteObject($table, $keys = array())
+	{
+		if (empty($table)) {
+			return -1;
+		}
+		$where = array();
+		if (sizeof($keys) > 0 || get_object_vars($keys)) {
+			foreach ($keys AS $k => $v) {
+				$where[] = "`{$k}` = '" . $this->setQuote($v) . "'";
+			}
+		}
+		$this->modelSQL = $sql = "DELETE FROM `{$table}`" . (sizeof($where) > 0 ? " WHERE " . join(" AND ", $where) : '');
+		$rs = $this->query($sql);
+		$this->cleanData();
+		$this->setError();
+		return $this->AffectedRows($rs);
+	}
+
+	/**
+	 * 需要清除的数据
+	 *
+	 * @return Array
+	 */
+	final function cleanOptions()
+	{
+		return array('fields', 'where', 'groupBy', 'orderBy', 'limit', 'setArray');
+	}
+
+	/**
+	 * 清除数据
+	 *
+	 */
+	final function cleanData()
+	{
+		$array = $this->cleanOptions();
+		foreach ($array AS $k) {
+			if (is_array($this->$k)) {
+				$this->$k = array();
+			} else {
+				$this->$k = null;
+			}
+		}
+	}
+
+	/**
+	 *
+	 * 查询的字段
+	 * @param String $fileds
+	 */
+	final function fields($fileds = "*")
+	{
+		$this->fields = null;
+		if (empty($fileds)) $fileds = "*";
+		if (is_array($fileds)) $fileds = join(',', $fileds);
+		$this->fields = $fileds;
+		return $this;
+	}
+
+	/**
+	 *
+	 * GROUP BY方法
+	 * @param String $fields
+	 */
+	final function groupBy($fields)
+	{
+		$this->groupBy = null;
+		if (!empty($fields)) {
+			$this->groupBy = sprintf($this->_query['GROUP'], $fields);
+		}
+		return $this;
+	}
+
+	/**
+	 *
+	 * 插入数据用
+	 * @param Array $array
+	 */
+	final function dataArray($array)
+	{
+		$this->fileds = null;
+		if (is_array($array)) {
+			$tmpArray = array();
+			foreach ($array AS $k => $v) {
+				$tmpArray['k'][] = $k;
+				$tmpArray['v'][] = $this->setQuote($v);
+			}
+			$this->fileds = $tmpArray;
+		}
+		return $this;
+	}
+
+	/**
+	 *
+	 * Order By函数
+	 * @param String $field
+	 * @param String $orderBy
+	 */
+	final function orderBy($field, $orderBy)
+	{
+		if(!empty($field)) return $this;
+
+		if($this->orderBy != '') {
+			$this->orderBy .= $field .' '. $orderBy;
+		}else{
+			$this->orderBy = sprintf($this->_query['ORDER'], $field, $orderBy);
+		}
+	}
+	
+	final function orderByArr($map)
+	{
+		if(empty($map)) return $this;
+		foreach($map AS $val)
+		{
+			$this->orderBy($val['field'], $val['orderBy']);
+		}
+		return $this;
+	}
+
+	final function orderByStr($orderBy)
+	{
+		if(!$orderBy) return $this;
+		$this->orderBy = $orderBy;
+		return $this;
+	}
+
+	/**
+	 *
+	 * Limit函数,如果省略第二个参数则第一个为0,第二个参数值取第一个
+	 * @param Int $limit
+	 * @param Int $offset
+	 */
+	final function limit($limit, $offset = 0)
+	{
+		$this->limit = null;
+		if ($limit !== '') {
+			if (!$offset) {
+				$this->limit = sprintf($this->_query["LIMIT"], 0, $limit);
+			} else {
+				$this->limit = sprintf($this->_query["LIMIT"], $limit, $offset);
+			}
+		}
+		return $this;
+	}
+
+	/**
+	 * 传的条件为数组
+	 * @param  Array $where 条件
+	 * @return Object       对象本身
+	 */
+	final function whereArray($where)
+	{
+		$this->where = null;
+		if (!empty($where)) {
+			$whereArray = array();
+			foreach ($where AS $k => $v) {
+				$whereArray[] = " `{$k}` = '{$v}'";
+			}
+			if (sizeof($whereArray) > 0) {
+				$whereSQL = join(" AND ", $whereArray);
+				$this->where = sprintf($this->_query["WHERE"], $whereSQL);
+			}
+		}
+		return $this;
+	}
+
+	/**
+	 *
+	 * WHERE 子句
+	 * @param String $where
+	 */
+	final function where($where)
+	{
+		if (is_array($where)) return $this->whereArray($where);
+		$this->where = null;
+		if (!empty($where)) {
+			$this->where = sprintf($this->_query["WHERE"], $where);
+		}
+		return $this;
+	}
+
+	/**
+	 *
+	 * 插入数据到指定表中
+	 * @param String $table
+	 */
+	final function insertRow($table)
+	{
+		$this->modelSQL = $sql = sprintf($this->_query['INSERT'], $table, join(",", $this->fileds['k']), join("', '", $this->fileds['v']));
+		$this->cleanData();
+		$this->setQuery($sql, '');
+		return $this->lastInsertId();
+	}
+
+	/**
+	 *
+	 * Replace方法
+	 * @param String $table
+	 */
+	final function replaceRow($table)
+	{
+		$this->modelSQL = $sql = sprintf($this->_query['REPLACE'], $table, join(",", $this->fileds['k']), join("', '", $this->fileds['v']));
+		$this->cleanData();
+		return $this->exec($sql);
+	}
+
+	/**
+	 *
+	 * 查询一行
+	 * @param String $table
+	 */
+	final function selectRow($table)
+	{
+		$this->modelSQL = $sql = sprintf($this->_query['SELECT'], ((trim($this->fields) != '') ? $this->fields : "*"), $table) . $this->where . $this->groupBy . $this->orderBy . $this->limit;
+		$this->cleanData();
+		return $this->getRow($sql);
+	}
+
+	/**
+	 *
+	 * 查询一行
+	 * @param String $table
+	 */
+	final function selectOne($table)
+	{
+		$this->modelSQL = $sql = sprintf($this->_query['SELECT'], ((trim($this->fields) != '') ? $this->fields : "*"), $table) . $this->where . $this->groupBy . $this->orderBy . $this->limit;
+		$this->cleanData();
+		return $this->getOne($sql);
+	}
+	/**
+	 * 创建SQL
+	 */
+	final function createSQL($table)
+	{
+		$sql = sprintf($this->_query['SELECT'], ((trim($this->fields) != '') ? $this->fields : "*"), $table) . $this->where . $this->groupBy . $this->orderBy . $this->limit;
+		$this->cleanData();
+		return $sql;
+	}
+	/**
+	 *
+	 * 查询所有
+	 * @param String $table
+	 * @return Array
+	 */
+	final function selectAll($table)
+	{
+		$this->modelSQL = $sql = sprintf($this->_query['SELECT'], ((trim($this->fields) != '') ? $this->fields : "*"), $table) . $this->where . $this->groupBy . $this->orderBy . $this->limit;
+		$this->cleanData();
+		return $this->getAll($sql);
+	}
+
+	/**
+	 * 将指定字段减指定值
+	 *
+	 * @param $data
+	 * @return $this
+	 */
+	final function downsetCounter($data)
+	{
+		if (is_array($data)) {
+			foreach ($data AS $k => $value) {
+				$this->setArray[] = $k . "=" . $k . '-' . $value;
+			}
+		}
+		$this->set(null);
+		return $this;
+	}
+
+	/**
+	 * 将指定字段加指定值
+	 *
+	 * @param $data
+	 * @return $this
+	 */
+	final function upsetCounter($data)
+	{
+		if (is_array($data)) {
+			foreach ($data AS $k => $value) {
+				$this->setArray[] = $k . "=" . $k . '+' . $value;
+			}
+		}
+		$this->set(null);
+		return $this;
+	}
+
+	/**
+	 * 更新数据时候用,方法同setData
+	 * @param Array $data
+	 */
+	final function set($data)
+	{
+		return $this->setData($data);
+	}
+
+	/**
+	 *
+	 * 更新数据时候用
+	 * @param Array $data
+	 * @return $this
+	 */
+	final function setData($data)
+	{
+		if (is_array($data)) {
+			$set = array();
+			foreach ($data AS $k => $value) {
+				$set[] = $k . "='" . $this->setQuote($value) . "'";
+			}
+			if (sizeof($this->setArray) > 0) {
+				$this->set = " " . join(", ", $set) . ", " . join(",", $this->setArray);
+			} else {
+				$this->set = " " . join(", ", $set);
+			}
+		} else {
+			if (sizeof($this->setArray) > 0) {
+				$this->set = join(",", $this->setArray);
+			} else {
+				$this->set = "";
+			}
+		}
+		return $this;
+	}
+	/**
+	 * 执行更新操作,updateRows的alias
+	 * @param String $table
+	 * @return number
+	 */
+	/*
+	final function update($table){
+		return $this->updateRows($table);
+	}
+	*/
+	/**
+	 *
+	 * 执行更新操作
+	 * @param $table
+	 * @return Int 返回影响的行数
+	 */
+	final function updateRows($table)
+	{
+		$this->modelSQL = $sql = sprintf($this->_query['UPDATE'], $table) . $this->set . $this->where . $this->limit;
+		$this->cleanData();
+		return $this->exec($sql);
+	}
+
+	/**
+	 *
+	 * 执行删除操作
+	 * @param String $table
+	 */
+	final function deleteRows($table)
+	{
+		$this->modelSQL = $sql = sprintf($this->_query['DELETE'], $table, $this->where) . $this->limit;
+		$this->cleanData();
+		return $this->exec($sql);
+	}
+
+	/**
+	 * 执行Model过程中保存的相关信息
+	 *
+	 * @param String $option
+	 * @return Mix
+	 */
+	final function querySQL($option = '')
+	{
+		$allow = array('_queryTimes', '_querySeconds', '_errorInfo', '_exeSQL');
+		if (in_array($option, $allow)) {
+			return $this->{$option};
+		}
+		return 0;
+	}
+
+	/**
+	 * 将结果编码一下
+	 * @param String $word
+	 * @return String|multitype:
+	 */
+	public function setQuote($word)//过滤sql字符
+	{
+		if (ini_get("magic_quotes_gpc")) {
+			return $word;
+		}
+		return is_array($word) ? array_map('addslashes', $word) : addslashes($word);
+	}
+	/**
+	 * 获取错误码
+	 */
+	public function getCode()
+	{
+		return $this->_response->getCode();
+	}
+	/**
+	 * 获取错误信息
+	 */
+	public function getMessage()
+	{
+		if($this->_response->isError())
+		{
+			return $this->_response->getMessage();
+		}
+	}
+	/**
+	 * 返回response对象
+	 *
+	 * @return Bool
+	 */
+	public function getResponse()
+	{
+		return $this->_response;
+	}
+
+	public function iconv($str)
+	{
+		if (is_array($str)) {
+            return array_map(function ($n) {
+                return iconv('GB2312', 'UTF-8', $n);
+            }, $str);
+        }
+
+		return iconv('GB2312', 'UTF-8', $str);
+	}
+	/**
+	 * 如果不存在指定的方法则调用提示错误
+	 *
+	 * @param String $name
+	 * @param Mix $args
+	 * @return Mix
+	 */
+	public function __call($method, $argvs)
+	{
+		if (isset($this->_modelAlias[$method])) {
+			if (method_exists($this, $this->_modelAlias[$method])) {
+				return call_user_func_array(array($this, $this->_modelAlias[$method]), $argvs);
+			}
+			\Qii::setError(false, __LINE__, 1506, 'Alias ' . get_called_class() . '->' . $method . '()');
+		}
+
+		\Qii::setError(false, __LINE__, 1506, get_called_class() . '->' . $method . '()');
+	}
+}

+ 63 - 0
src/Driver/ConnBase.php

@@ -0,0 +1,63 @@
+<?php
+namespace Qii\Driver;
+
+/**
+ * 数据库连接基类
+ *
+ * @author Jinhui Zhu<jinhui.zhu@live.cn> 2015-12-10 14:34
+ */
+class ConnBase
+{
+	const VERSION = '1.2';
+	/**
+	 * @var $allowExec  读写对应的数据库配置文件
+	 */
+	public $allowExec = array("WRITE" => 'master', 'READ' => 'slave');
+
+	/**
+	 * 获取数据库配置中指定的key的值,不指定则获取全部
+	 * @param string key 数据库配置中指定的key
+	 */
+	public function getDBInfo($key = null)
+	{
+		if ($key != null) return isset($this->_dbInfo[$key]) ? $this->_dbInfo[$key] : false;
+		return $this->_dbInfo;
+	}
+
+	/**
+	 * 通过sql语句判断是读还是写操作
+	 * @param $sql  读/写的sql语句
+	 */
+	protected function prepare($sql)
+	{
+		$default = "READ";
+		$readMode = "/^SELECT\s/u";
+		$writeMode = "/^(UPDATE)|(REPLACE)|(DELETE)\s/u";
+		$isRead = preg_match($readMode, $sql);
+		$isWrite = preg_match($writeMode, $sql);
+		if ($isWrite) $default = "WRITE";
+		if (!isset($this->allowExec[$default])) $default = 'WRITE';
+		return $default;
+	}
+
+	/**
+	 * 通过sql获取连接资源
+	 *
+	 * @param String $sql 通过sql语句获取读/写操作对应的res
+	 * @return res
+	 */
+	public function getConnectionBySQL($sql)
+	{
+		$default = $this->prepare($sql);
+		if (isset($this->_connections[$default])) return $this->_connections[$default];
+		switch ($default) {
+			case 'READ':
+				return $this->_connections[$default] = $this->getReadConnection();
+				break;
+			default:
+				return $this->_connections['WRITE'] = $this->getWriteConnection();
+				break;
+		}
+		throw new \Exception('Call undefined driver', __LINE__);
+	}
+}

+ 30 - 0
src/Driver/ConnIntf.php

@@ -0,0 +1,30 @@
+<?php
+namespace Qii\Driver;
+/**
+ * 数据库读写资源接口文件
+ *
+ * @author Jinhui Zhu<jinhui.zhu@live.cn> 2015-10-25 21:54
+ */
+
+interface ConnIntf
+{
+	public function __construct();
+
+	/**
+	 * 通过sql获取连接资源
+	 *
+	 * @param String $sql
+	 */
+	public function getConnectionBySQL($sql);
+
+	/**
+	 * 获取读数据的连接资源
+	 */
+	public function getReadConnection();
+
+	/**
+	 * 获取取数据的连接资源
+	 *
+	 */
+	public function getWriteConnection();
+}

+ 514 - 0
src/Driver/Easy.php

@@ -0,0 +1,514 @@
+<?php
+/**
+ * 简易数据库操作类
+ *
+ * @author Jinhui Zhu<jinhui.zhu@live.cn>2015-12-10 14:35
+ *
+ * usage:
+ * $user = (new \Qii\Driver\Easy())->_initialize();
+ * $user->setRules(new \Qii\Driver\Rules($rules));
+ * $user->setPrivateKey(array());
+ * $user->uid = 1;
+ * $user->email = 'antsnet@163.com';
+ * $user->nick = 'Jinhui Zhu';
+ * $user->add_time = time();
+ * $user->update_time = time();
+ * 保存数据
+ * $user->_save();
+ * 检查指定数据是否存在,以privateKey为准
+ * $user->_exist();
+ * 删除指定数据,以privateKey为准
+ * $user->_remove()
+ * 更新数据,以privateKey为准
+ * $user->_update();
+ * if($user->isError())
+ * {
+ *     //todo 如果执行操作有问题,错误详情 $user->getErrors()
+ * }
+ * else
+ * {
+ *     //todo 对返回结果进行处理
+ * }
+ *
+ * //获取所有
+ * $user->_getRowsByEmail('antsnet@163.com');
+ * $user->_getRowByFileds($this->_privateKey);
+ * $user->_getRowsByFileds($this->_privateKey);
+ * $user->getEmailByUid(1);
+ * $user->_getAllByUid(1);
+ */
+namespace Qii\Driver;
+
+\Qii\Autoloader\Import::requires(dirname(__FILE__) . DS . 'Response.php');
+
+class Easy
+{
+	const VERSION = '1.2';
+	/**
+	 * @var Fields $fields
+	 */
+	private $fields = null;
+	/**
+	 * 数据表主键
+	 * @var array $privateKeys
+	 */
+	private $privateKeys = array();
+	/**
+	 * 保存或更新的时候验证的规则
+	 * @var array $rules
+	 */
+	private $easyRules = array();
+	/**
+	 * @var string $databaseName 数据库名称
+	 * 如果没有指定数据库名称将使用当前连接的数据库
+	 */
+	private $databaseName;
+	/**
+	 * @var string $tableName 表名,
+	 * 如果要指定操作的数据库名需要将数据库名和表名合在一起,如:database.table
+	 */
+	private $tableName = '';
+	/**
+	 * @var book $isInstance 是否实例化对象了
+	 */
+	private $isInstance = false;
+	/**
+	 * @var array $_response 操作出错保存数据到此数组
+	 */
+	private $_response;
+	/**
+	 * @var bool  是否需要重置privateKeys
+	 */
+	private $needResetPrivatekey = true;
+	
+	private $db;
+
+	public function __construct()
+	{
+		$this->db = \Qii\Autoloader\Psr4::loadClass('Qii\Model')->db;
+		$this->clean();
+		return $this;
+	}
+
+	/**
+	 * 初始化数据库对象
+	 *
+	 */
+	final public function _initialize()
+	{
+		$this->isInstance = true;
+		$this->clean();
+		return $this;
+	}
+
+	/**
+	 * 设置表字段
+	 *
+	 * @param array $fields
+	 * @return $this
+	 */
+	final public function setFields(array $fields)
+	{
+		$this->fields = new Fields($fields);
+		return $this;
+	}
+
+	/**
+	 * 设置主键
+	 *
+	 * @param array|string $privateKeys
+	 */
+	final public function setPrivateKey(array $privateKeys)
+	{
+		if(count($privateKeys) == 0) return $this;
+		$this->needResetPrivatekey = false;
+		$this->privateKeys = $privateKeys;
+		return $this;
+	}
+
+	/**
+	 * 重置主键,主要用于查询、更新、删除操作
+	 *
+	 * @param string | null $opt
+	 * @return array
+	 */
+	final public function resetPrivateKey($opt = null)
+	{
+		if ($this->needResetPrivatekey && $opt) {
+			$valid = $this->easyRules->getOperateValidFields($opt);
+			if (!empty($valid)) $this->privateKeys = array_values($valid);
+		}
+		return $this->privateKeys;
+	}
+
+	/**
+	 * 设置规则
+	 * @param array $rules {_save:{}, _update:{}, _remove:{}}
+	 */
+	final public function setRules(\Qii\Driver\Rules $rules)
+	{
+		$this->setFields($rules->getFields());
+		$this->tableName = $rules->getTableName();
+		$this->databaseName = $rules->getDatabase();
+		if (!$this->privateKeys) $this->privateKeys = $rules->getPrivateKey();
+		$this->easyRules = $rules;
+		return $this;
+	}
+
+	/**
+	 * 设置表字段
+	 *
+	 * @param $name
+	 * @param $val
+	 * @return $this
+	 */
+	public function __set($name, $val)
+	{
+		$this->fields->$name = $val;
+		return $this;
+	}
+
+	/**
+	 * 批量设置字段的值
+	 * @param array $data
+	 * @return $this
+	 */
+	public function setFieldsVal(array $data)
+	{
+		foreach ($data AS $name => $val) {
+			$this->fields->$name = $val;
+		}
+		return $this;
+	}
+
+	/**
+	 * 获取当前使用的rules的字段
+	 *
+	 * @return mixed
+	 */
+	public function getFields()
+	{
+		return $this->easyRules->getFields();
+	}
+	/**
+	 * 获取当前使用的fields的值
+	 *
+	 * @return mixed
+	 */
+	public function getValues()
+	{
+		return $this->fields->getValues();
+	}
+
+	/**
+	 * 如果没有调用parent::__construct()方法就报错
+	 *
+	 */
+	public function checkInstance()
+	{
+		if (!$this->isInstance) {
+			$this->_response = Response::Fail('checkInstance', array('msg' => \Qii::i(1507, 'parent::__construct()'), 'code' => __LINE__));
+			throw new \Qii\Exceptions\TableException(\Qii::i(1507, 'parent::__construct()'), __LINE__);
+		}
+		return $this;
+	}
+
+	/**
+	 * 重置Fields及相关条件
+	 *
+	 */
+	final protected function clean()
+	{
+		$this->fields = null;
+		$this->privateKeys = array();
+		$this->easyRules = array();
+		$this->tableName = '';
+		$this->_response = new Response();
+		return $this;
+	}
+
+	/**
+	 * 验证保存的数据
+	 *
+	 * @param $rules
+	 * @return bool
+	 */
+	final protected function validateFields($rules)
+	{
+		if (empty($rules)) return true;
+		$validateCls = _loadClass('Qii_Library_Validate');
+		$result = $validateCls->verify($this->fields->getValues(), $rules, $this->easyRules->getInvalidMessage());
+		if ($result === true) {
+			return true;
+		}
+		$error = $validateCls->getErrors();
+		$this->_response = Response::FailValidate('validate', array('_result' => $error['msg'], 'fields' => array('field' => $error['field'], 'message' => $error['msg'])));
+		return false;
+	}
+
+	/**
+	 * 获取用户表
+	 */
+	final public function getTableName()
+	{
+		if (!$this->tableName) throw new \Qii\Exceptions\Errors(\Qii::i(1510), true);
+		return $this->databaseName ? $this->databaseName . '.' . $this->tableName : $this->tableName;
+	}
+
+	/**
+	 * 对指定字段执行指定方法 同时校验值的有效性
+	 *
+	 * @param String $func
+	 * @param String $field
+	 * @param String $val
+	 * @return Object
+	 */
+	final public function func($func, $key, $val)
+	{
+		$rule = $this->getValidateField($key);
+		//如果字段需要用到函数编码字符,如果没有通过验证就不将值放入fields中
+
+		$validateCls = _loadClass('Qii\Library\Validate');
+		$result = $validateCls->verify(array($key => $val), array($key => $rule), $this->easyRules->getInvalidMessage());
+		if ($result === true) {
+			$this->fields->$key = $func($val);
+			return $this;
+		}
+		$error = $validateCls->getErrors();
+		$this->_response = Response::FailValidate('validate', array('_result' => $error['msg'], 'fields' => array('field' => $error['field'], 'message' => $error['msg'])));
+		return $this;
+	}
+
+	/**
+	 * 检查数据是否已经存在,并返回一行,只能根据主键查询
+	 *
+	 * @return array
+	 */
+	final public function _exist()
+	{
+		$this->checkInstance();
+		if (!$this->privateKeys) {
+			$this->_response = Response::FAIL('privateKey', \Qii::i(1513));
+			return $this->_response;
+		}
+		$where = array();
+		foreach ($this->privateKeys AS $key) {
+			$rule = $this->getValidateField($key);
+			if (count($rule) > 0 && !$this->validateFields(array($key => $rule))) {
+				return $this->_response;
+			}
+			if ($this->fields->isField($key)) $where[] = "`{$key}` = '" . $this->db->setQuote($this->fields->getField($key)) . "'";
+		}
+		$result = (array)$this->db->limit(1)->where(join(' AND ', $where))->select($this->getTableName());
+		if ($this->db->isError()) {
+			$this->_response = $this->db->getResponse();
+		}
+		return $this->_response = Response::Success('_exist', array('_result' => $result));
+	}
+
+	/**
+	 * 获取主键对应的值
+	 * @return array
+	 */
+	final protected function getPrivateValue()
+	{
+		$data = array();
+		foreach ($this->privateKeys AS $key) {
+			$data[$key] = $this->fields->getField($key);
+		}
+		return $data;
+	}
+
+	/**
+	 * 保存数据
+	 *
+	 * @return string 如果是自动增长的行返回插入数据的id
+	 */
+	final public function _save()
+	{
+		$this->checkInstance();
+		if (!$this->validateFields($this->easyRules->getRulesByOperate('save'))) return $this->_response;
+		$this->resetPrivateKey('save');
+		if ($this->privateKeys && count($this->_exist()->getResult()) > 0) {
+			$this->_response = Response::Exist('_save', array('_result' => \Qii::i(1511, join(',', $this->getPrivateValue()))));
+			return $this->_response;
+		}
+		$result = $this->db->insertObject($this->getTableName(), $this->fields->getValues());
+		if ($this->db->isError()) {
+			return $this->db->getResponse();
+		}
+		return $this->_response = Response::Success('_save', array_merge($this->fields->getValueAsArray(), array('_result' => $result)));
+	}
+
+	/**
+	 * 更新数据
+	 *
+	 * @return int  更新数据影响的行数
+	 */
+	final public function _update()
+	{
+		$this->checkInstance();
+		if (!$this->validateFields($this->easyRules->getRulesByOperate('update'))) return $this->_response;
+		$this->resetPrivateKey('update');
+		if (count($this->_exist()) == 0) {
+			return $this->_response = Response::NotExist('_update', \Qii::i(1512, join(',', $this->getPrivateValue())));
+		}
+		$result = $this->db->updateObject($this->getTableName(), $this->fields->getValues(), $this->privateKeys);
+		if ($this->db->isError()) {
+			return $this->_response = $this->db->getResponse();
+		}
+		return $this->_response = Response::Success('_update', array('_result' => $result));
+	}
+
+	/**
+	 * 删除数据
+	 * @return int 删除数据影响的行数
+	 */
+	final public function _remove()
+	{
+		$this->checkInstance();
+		if (!$this->validateFields($this->easyRules->getRulesByOperate('remove'))) return $this->_response;
+		$this->resetPrivateKey('remove');
+		if (count($this->_exist()) == 0) {
+			return $this->_response = Response::NotExist('_remove', \Qii::i(1512, join(',', $this->getPrivateValue())));
+		}
+		$result = $this->db->deleteObject($this->getTableName(), $this->fields->getValues());
+		if ($this->db->isError()) {
+			return $this->_response = $this->db->getResponse();
+		}
+		return $this->_response = Response::Success('_remove', array('_result' => $result));
+	}
+
+	/**
+	 * 获取操作数据返回的错误
+	 */
+	final public function getErrors()
+	{
+		if ($this->_response) {
+			if (!$this->_response->isError()) return false;
+			return $this->_response;
+		}
+		return $this->db->getError();
+	}
+
+	/**
+	 * 是否有错误
+	 */
+	final public function isError()
+	{
+		if ($this->_response) {
+			return $this->_response->isError();
+		}
+		return $this->db->isError();
+	}
+
+	/**
+	 * 获取response对象
+	 */
+	final public function getResponse()
+	{
+		return $this->_response;
+	}
+
+	/**
+	 * 使用此方法用于查询某一条数据的某一个字段
+	 * @useage getXxxByXxx
+	 *     getNameById:通过id获取name的值; getEmailById:通过id获取email;
+	 *     _getRowById:返回所有字段; _getRowsById:通过id获取所有匹配的数据
+	 * 备注:以_开头的才会去走getRow, getRows方法去取所有字段,目前仅支持All, Row, Rows这几个方法
+	 * @param  String $method [description]
+	 * @param  Mix $args 请求的参数
+	 * @return Mix         [description]
+	 */
+	final public function __call($method, $args)
+	{
+		$this->checkInstance();
+		$selectType = 'normal';
+		if (substr($method, 0, 1) == '_') {
+			$selectType = 'system';
+			$method = substr($method, 1);
+		}
+		preg_match('/^(get)(.*)(By)(.*)/', $method, $matches);
+
+		if ($matches && count($matches) == 5 && $matches[1] == 'get') {
+			//大写字母匹配下划线,字段中统一用小写字母,在查询的时候使用驼峰结构
+			//如:getEmailAddressByUserId 通过user_id查询email_address
+			$field = strtolower(preg_replace('/(?<!^)([A-Z])/', '_$1', $matches[2]));
+
+			if (isset($this->_relationMap[$field])) $field = $this->_relationMap[$field];
+			$method = 'selectRow';
+			if ($field == 'rows' && $selectType == 'system') $method = 'selectRows';
+			if ($field == 'row' && $selectType == 'system') $method = 'selectRow';
+			if (in_array($field, array('all', 'row', 'rows')) && $selectType == 'system') {
+				$field = '*';
+			}
+			$value = $this->db->setQuote(array_shift($args));
+			$name = strtolower(strtolower(preg_replace('/(?<!^)([A-Z])/', '_$1', $matches[4])));
+			$whereArray = array();
+			if ($selectType == 'system' && $name == 'fields') {
+				foreach ($value AS $val) {
+					$whereArray[$val] = $this->fields->isField($val) ? $this->fields->getField($val) : '';
+				}
+			} else {
+				if (!$this->fields->isField($name)) $this->fields->$name = $value;
+				$whereArray[$name] = $this->fields->getField($name);
+			}
+			foreach ($whereArray AS $key => $val) {
+				$rule = $this->getValidateField($key);
+				if (count($rule) > 0 && !$this->validateFields(array($key => $rule))) {
+					return $this->_response;
+				}
+			}
+			$result = $this->db->fields($field)->whereArray($whereArray)->$method($this->getTableName());
+			if ($this->db->isError()) {
+				return $this->_response = $this->db->getResponse();
+			}
+			return $this->_response = Response::Success($method, array('_result' => $result));
+		}
+		//exec{$method}
+		preg_match('/^(exec)(.*)/', $method, $matches);
+
+		if ($matches && count($matches) == 3) {
+			$alias = lcfirst($matches[2]);
+			if (method_exists($this->db, $alias)) {
+				$result = $this->db->{$matches[2]}($this->getTableName(), $this->getFields());
+				if ($this->db->isError()) {
+					return $this->_response = $this->db->getResponse();
+				}
+				return $this->_response = Response::Success($matches[2], array('_result' => $result));
+			}
+			if ($this->db->getAlias($method) && method_exists($this->db, $this->db->getAlias($method))) {
+				$this->db->whereArray($this->getFields());
+				$result = $this->db->{$this->db->getAlias($method)}($this->getTableName());
+				if ($this->db->isError()) {
+					return $this->_response = $this->db->getResponse();
+				}
+				return $this->_response = Response::Success($this->db->getAlias($method), array('_result' => $result));
+			}
+		}
+		//访问方法的别名
+		if ($this->db->getAlias($method)) {
+			if (method_exists($this->db, $this->db->getAlias($method))) {
+				$result = call_user_func_array(array($this->db, $this->db->getAlias($method)), $args);
+				if ($this->db->isError()) {
+					return $this->_response = $this->db->getResponse();
+				}
+				return $this->_response = Response::Success($this->db->getAlias($method), array('_result' => $result));
+			}
+			$this->_response = Response::UndefinedMethod('__call', $this->db->getAlias($method));
+			\Qii::setError(false, __LINE__, 1106, 'Model', 'Alias ' . $this->db->getAlias($method) . ' does not exist.', print_r($args, true));
+		}
+		$this->_response = Response::UndefinedMethod('__call', $method);
+		\Qii::setError(false, __LINE__, 1106, 'Model', $method, print_r($args, true));
+	}
+
+	/**
+	 * 获取单个字段的验证规则
+	 * @param  String $fieldName 字段名
+	 * @return Array
+	 */
+	protected function getValidateField($fieldName)
+	{
+		return $this->easyRules->getRulesByField($fieldName);
+	}
+}

+ 92 - 0
src/Driver/Fields.php

@@ -0,0 +1,92 @@
+<?php
+/**
+ * 存储表的相关数据
+ * @author Jinhui Zhu<jinhui.zhu@live.cn>2015-09-21 16:52
+ *
+ * 用法
+ *
+ */
+namespace Qii\Driver;
+
+final class Fields
+{
+	const VERSION = '1.2';
+	protected $keys;
+	protected $fields;
+
+	/**
+	 * 初始化数据表结构
+	 *
+	 * @param $fields
+	 *
+	 */
+	public function __construct($fields)
+	{
+		if (!is_array($fields) && count($fields) == 0) throw new \Exception(\Qii::i(1508), __LINE__);
+		$this->keys = new \stdClass();
+		$this->fields = $fields;
+		return $this;
+	}
+
+	/**
+	 * 设置数据表字段值,仅在列表中的才保存到对应的字段中
+	 *
+	 * @param $name
+	 * @param $val
+	 * @return $this
+	 */
+	public function __set($name, $val)
+	{
+		if (in_array($name, $this->fields)) $this->keys->$name = $val;
+		return $this;
+	}
+
+	/**
+	 * 判断是否存在相关键值
+	 *
+	 * @param $field
+	 * @return bool
+	 */
+	public function isField($field)
+	{
+		if (isset($this->keys->$field)) return true;
+		return false;
+	}
+
+	/**
+	 * 获取相关的键值
+	 *
+	 * @param $field
+	 * @return null
+	 */
+	public function getField($field)
+	{
+		if (isset($this->keys->$field)) return $this->keys->$field;
+		return null;
+	}
+
+	/**
+	 * 获取字段及值
+	 *
+	 * @return stdClass
+	 */
+	public function getValues()
+	{
+		return $this->keys;
+	}
+
+	/**
+	 * 以array的形式返回字段及值
+	 *
+	 * @return array
+	 */
+	public function getValueAsArray()
+	{
+		return (array)$this->keys;
+	}
+
+	public function __call($method, $argvs)
+	{
+		throw new \Qii\Exceptions\MethodNotFound(\Qii::i(1101, $method . ' Not found'), __LINE__);
+	}
+}

+ 45 - 0
src/Driver/Intf.php

@@ -0,0 +1,45 @@
+<?php
+/**
+ * 数据库接口文件
+ * @author Jinhui Zhu<zhujinhui@zhangyue.com>2015-10-25 21:54
+ */
+namespace Qii\Driver;
+
+interface Intf
+{
+	public function __construct(\Qii\Driver\ConnIntf $connection);
+
+	/**
+	 * 执行SQL前检查是读/写
+	 *
+	 * @param String $sql
+	 * @return String READ/WRITE
+	 */
+	public function setQuery($sql);//查询预处理
+
+	public function query($sql);//查询
+
+	public function exec($sql);//执行查询并返回影响的行数
+
+	public function fetch($rs);//获取一行,在while循环中可使用
+
+	public function getRow($sql);//获取一行
+
+	public function getOne($sql);//获取一列
+
+	public function getAll($sql);//获取所有的行
+
+	public function transaction();//事务处理开始
+
+	public function commit();//事务提交
+
+	public function rollback();//事务回滚
+
+	public function affectedRows();//返回影响的行数
+
+	public function lastInsertId();//返回自增长ID
+
+	public function getError($key = '');//获取错误
+
+	public function setError();//设置错误
+}

+ 234 - 0
src/Driver/Model.php

@@ -0,0 +1,234 @@
+<?php
+/**
+ * 数据库分发器
+ * @author Jinhui Zhu <jinhui.zhu@live.cn>2016-01-19 18:31
+ * 使用方法:
+ * 1.
+ * namespace Model;
+ *
+ * use \Qii\Model;
+ * class comments extends Model
+ * {
+ *        public function __construct()
+ *        {
+ *            parent::__construct();
+ *            $this->setRules(new \Qii\Driver\Rules(\Qii\Autoloader\Import::includes('configure/table/adcycle.comments.config.php')));
+ *        }
+ * }
+ * 2.
+ * $test = _M(new \Qii\Driver\Rules(\Qii\Autoloader\Import::includes('configure/table/test.config.php')));
+ * $fields = array('id' => 1, 'name' => 'test');
+ * $test->save($fields);
+ */
+namespace Qii\Driver;
+
+class Model
+{
+    const VERSION = '1.2';
+    /**
+     * @var $_allow 允许使用的数据库驱动类新
+     */
+    protected $_allow = array('pdo', 'mysql', 'mysqli');
+    /**
+     * @var $db 数据库实例
+     */
+    public $db = null;
+    /**
+     * 数据库配置文件
+     */
+    protected $_dbInfo;
+    /**
+     * 数据库驱动
+     */
+    protected $_driver = 'pdo';
+    /**
+     * @var $_load 加载类
+     */
+    public $_load;
+
+    /**
+     * @var $_language 语言包
+     */
+    public $_language;
+    /**
+     * @var array $rules 数据表规则
+     */
+    private $rules = null;
+    /**
+     * @var \Qii_Driver_Easy $model
+     */
+    private $model = array();
+    /**
+     * @var Qii_Request_Abstract $_request 请求类
+     */
+    protected $_request;
+    /**
+     * @var $_helper helper类
+     */
+    protected $_helper;
+
+    public function __construct()
+    {
+        $this->_load = \Qii\Autoloader\Psr4::getInstance()->loadClass('\Qii\Autoloader\Loader');
+        $this->_language = \Qii\Autoloader\Psr4::getInstance()->loadClass('\Qii\Language\Loader');
+        $this->_request = \Qii\Autoloader\Psr4::getInstance()->loadClass('Qii\Request\Http');
+        $this->_helper = \Qii\Autoloader\Psr4::getInstance()->loadClass('Qii\Autoloader\Helper');
+        $this->_dbInfo = \Qii\Config\Register::getAppConfigure(\Qii\Config\Register::get(\Qii\Config\Consts::APP_DB));
+        if (isset($this->_dbInfo['driver'])) {
+            $this->_driver = $this->_dbInfo['driver'];
+        }
+        if (!in_array($this->_driver, $this->_allow)) {
+            $this->_driver = array_shift($this->_allow);
+        }
+        \Qii\Autoloader\Import::requires(array(
+            Qii_DIR . DS . 'Qii' . DS . 'Driver' . DS . 'Base.php',
+            Qii_DIR . DS . 'Qii' . DS . 'Driver' . DS . 'ConnBase.php',
+            Qii_DIR . DS . 'Qii' . DS . 'Driver' . DS . 'ConnIntf.php',
+            Qii_DIR . DS . 'Qii' . DS . 'Driver' . DS . ucWords($this->_driver) . DS . 'Connection.php',
+            Qii_DIR . DS . 'Qii' . DS . 'Driver' . DS . ucWords($this->_driver) . DS . 'Driver.php',
+        ));
+        $this->db = \Qii\Autoloader\Psr4::getInstance()->loadClass(
+            '\Qii\Driver\\' . ucWords($this->_driver) . '\Driver',
+            \Qii\Autoloader\Psr4::getInstance()->loadClass(
+                '\Qii\Driver\\' . ucWords($this->_driver) . '\Connection'
+            )
+        );
+        $this->db->_debugSQL = isset($this->_dbInfo['debug']) ? $this->_dbInfo['debug'] : false;
+        return $this;
+    }
+
+    /**
+     * 设置属性
+     *
+     * @param string $name 属性名
+     * @param mix $val 值
+     */
+    public function __set($name, $val)
+    {
+        $this->db->$name = $val;
+    }
+
+    /**
+     * 将属性转到DB类中去
+     */
+    public function __get($attr)
+    {
+        if ($this->db) {
+            return $this->db->$attr;
+        }
+        return null;
+    }
+
+    /**
+     * 获取当前使用的数据库
+     */
+    public function getCurrentDB()
+    {
+        return $this->db->currentDB;
+    }
+
+    /**
+     * 设置规则
+     * @param array $rules
+     */
+    public function setRules(\Qii\Driver\Rules $rules)
+    {
+        if (empty($rules)) throw new \Exception(\Qii::i('Please set rules first'), __LINE__);
+        $this->rules = $rules;
+        return $this;
+    }
+
+    /**
+     * 生成数据库结构
+     */
+    public function tableStruct()
+    {
+        $this->checkRulesInstance();
+        $struct = array_flip($this->rules->getFields());
+        foreach ($struct AS $key => $val) {
+            $struct[$key] = '';
+        }
+        return $struct;
+    }
+
+    /**
+     * 检查是否已经设置规则
+     */
+    final public function checkRulesInstance()
+    {
+        if ($this->rules == null) throw new \Exception(\Qii::i('Please set rules first'), __LINE__);
+    }
+
+    /**
+     * 获取当前初始化的model
+     * @return \Qii_Driver_Easy
+     */
+    final public function getInstance()
+    {
+        $this->checkRulesInstance();
+        $tableName = $this->rules->getTableName();
+        if (!isset($this->model[$tableName])) $this->model[$tableName] = _DBDriver($this->rules);
+        return $this->model[$tableName];
+    }
+
+    /**
+     * 设置主键
+     * @param array $privateKey 设置主键
+     * @return Object
+     */
+    final public function setPrivateKey($privateKey = array())
+    {
+        $this->getInstance()->setPrivateKey($privateKey);
+        return $this;
+    }
+
+    /**
+     * 检查数据是否存在
+     * @param array $fields 数据
+     * @return \Qii\Driver\Response
+     */
+    final public function _exist($fields, $privateKey = array())
+    {
+        return $this->getInstance()->setPrivateKey($privateKey)->setFieldsVal($fields)->_exist();
+    }
+
+    /**
+     * 保存数据
+     * @param array $fields 数据
+     * @return \Qii\Driver\Response
+     */
+    final public function _save($fields, $privateKey = array())
+    {
+        return $this->getInstance()->setPrivateKey($privateKey)->setFieldsVal($fields)->_save();
+    }
+
+    /**
+     * 更新数据
+     * @param array $fields 数据
+     * @return \Qii\Driver\Response
+     */
+    final public function _update($fields, $privateKey = array())
+    {
+        return $this->getInstance()->setPrivateKey($privateKey)->setFieldsVal($fields)->_update();
+    }
+
+    /**
+     * 删除数据
+     * @param array $fields 数据
+     * @return \Qii_Response
+     */
+    final public function _remove($fields, $privateKey = array())
+    {
+        return $this->getInstance()->setPrivateKey($privateKey)->setFieldsVal($fields)->_remove();
+    }
+
+    /**
+     * 方法不存在的时候调用$this->db下的方法
+     * @param string $method 方法名
+     * @param mix $args 参数
+     */
+    public function __call($method, $args)
+    {
+        if ($this->db) return call_user_func_array(array($this->db, $method), $args);
+    }
+}

+ 54 - 0
src/Driver/Mysql/Connection.php

@@ -0,0 +1,54 @@
+<?php
+namespace Qii\Driver\mysql;
+
+class Connection extends \Qii\Driver\ConnBase implements \Qii\Driver\ConnIntf
+{
+	const VERSION = '1.2';
+	protected $_dbInfo;
+
+	public function __construct()
+	{
+		$this->_dbInfo = \Qii\Config\Register::getAppConfigure(\Qii\Config\Register::get(\Qii\Config\Consts::APP_DB));
+	}
+
+	/**
+	 * 获取读数据的连接资源
+	 */
+	public function getReadConnection()
+	{
+		$dbInfo = $this->_dbInfo['master'];
+		$useSlave = false;
+
+		if ($this->_dbInfo['readOrWriteSeparation'] && $this->_dbInfo['slave']) {
+			$i = rand(0, count($this->_dbInfo['slave']) - 1);
+			$dbInfo = $this->_dbInfo['slave'][$i];
+			$useSlave = true;
+		}
+
+		if ($useSlave) {
+			try {
+				$connection = mysql_connect($dbInfo['host'], $dbInfo['user'], $dbInfo['password'], $dbInfo['db']);
+				if (!$connection) throw new \Qii\Exceptions\Errors(\Qii::i(1501, iconv("GBK", "UTF-8//TRANSLIT", mysql_error())), true);
+				return $connection;
+			} catch (Exception  $e) {
+				return $this->getWriteConnection();
+			}
+		}
+		return $this->getWriteConnection();
+	}
+
+	/**
+	 * 获取写数据的连接资源
+	 *
+	 */
+	public function getWriteConnection()
+	{
+		try {
+			$connection = mysql_connect($dbInfo['host'], $dbInfo['user'], $dbInfo['password'], $dbInfo['db']);
+			if (!$connection) throw new \Qii\Exceptions\Errors(\Qii::i(1501, iconv("GBK", "UTF-8//TRANSLIT", mysql_error())), true);
+			return $connection;
+		} catch (Exception  $e) {
+			throw new \Qii\Exceptions\Errors(\Qii::i(1500, $dbInfo['host'], $dbInfo['user'], $dbInfo['password'], $dbInfo['db'], $e->getMessage()));
+		}
+	}
+}

+ 264 - 0
src/Driver/Mysql/Driver.php

@@ -0,0 +1,264 @@
+<?php
+namespace Qii\Driver\Mysql;
+
+\Qii\Autoloader\Import::requires(dirname(dirname(__FILE__)) . DS . 'Response.php');
+
+class Driver extends \Qii\Driver\Base implements \Qii\Driver\Intf
+{
+	const VERSION = '1.2';
+	private static $_instance;
+	protected $connection;
+	private $sysConfigure;
+	private $rs;
+	public $db;
+	/**
+	 * 是否开启调试
+	 *
+	 * @var BOOL
+	 */
+	public $_debugSQL = true;
+	/**
+	 * 执行SQL的列表
+	 *
+	 * @var Array
+	 */
+	public $_exeSQL = array();
+	/**
+	 * 查询次数
+	 *
+	 * @var int $queryTimes
+	 */
+	public $queryTimes = 0;
+	/**
+	 * 查询耗时
+	 *
+	 * @var array $querySeconds
+	 */
+	public $querySeconds = array();
+
+	/**
+	 * 最后一次执行的SQL
+	 *
+	 * @var unknown_type
+	 */
+	private $sql;
+	/**
+	 * 是否开启执行SQL的时间
+	 *
+	 * @var BOOL
+	 */
+	public $_debugTime = false;
+	public $_errorInfo = array();
+	/**
+	 * 保存未定义变量
+	 *
+	 * @var Array
+	 */
+	private $_undefined;
+	/**
+	 * @var string $charset 数据库默认编码
+	 */
+	public $charset = 'UTF8';
+	/**
+	 * @var array $currentDB 当前数据库信息
+	 */
+	public $currentDB;
+	/**
+	 * @var string $markKey 用于保存数据库执行相关信息
+	 */
+	public $markKey = '__model';
+	/**
+	 * @var string $response Response对象
+	 */
+	protected $response;
+
+	public function __construct(\Qii\Driver\ConnIntf $connection)
+	{
+		parent::__construct();
+		$this->connection = $connection;
+		$this->sysConfigure = $this->connection->getDBInfo();
+		$this->currentDB = $this->sysConfigure['master']['db'];
+		$this->response = new \Qii\Driver\Response();
+	}
+
+	/**
+	 * 用户直接输出这个实例化的类后会输出当前类的名称
+	 *
+	 * @return String
+	 */
+	public function __toString()
+	{
+		return get_class($this);
+	}
+
+	public function setQuery($sql)//查询预处理
+	{
+		return $this->query($sql);
+	}
+
+	public function query($sql)//查询
+	{
+		/**
+		 * 如果调试SQL的话就启用时间的记录
+		 */
+		if ($this->_debugSQL) {
+			$startTime = microtime(true);
+		}
+		$this->sql = $sql;
+		$this->db['CURRENT'] = $this->connection->getConnectionBySQL($this->sql);
+		if (!empty($this->sysConfigure['charset'])) {
+			\mysql_query($this->db['CURRENT'], "SET CHARACTER SET {$this->sysConfigure['charset']}");
+		} else {
+			\mysql_query($this->db['CURRENT'], "SET CHARACTER SET UTF8");
+		}
+
+		$this->rs = $rs = \mysql_query($this->db['CURRENT'], $sql);
+		$this->setError();
+		if (!$rs) {
+			$error = $this->getError('error');
+			return \Qii::setError(false, __LINE__, 1509, $sql, $error[2] == '' ? 'NULL' : $error[2]);
+		}
+		$this->queryTimes++;
+		/**
+		 * 如果调试SQL的话就启用时间的记录
+		 */
+		if ($this->_debugSQL) {
+			$endTime = microtime(true);
+			$costTime = sprintf('%.4f', ($endTime - $startTime));
+			$this->querySeconds[$this->queryTimes]['sql'] = $sql;
+			$this->querySeconds[$this->queryTimes]['costTime'] = $costTime;
+			$this->querySeconds[$this->queryTimes]['startTime'] = $startTime;
+			$this->querySeconds[$this->queryTimes]['endTime'] = $endTime;
+		}
+		return $rs;
+	}
+
+	/**
+	 * 执行SQL
+	 *
+	 * @param String $sql
+	 * @return Int
+	 */
+	public function exec($sql)
+	{
+		$this->setQuery($sql);
+		return $this->affectedRows();
+	}
+
+	public function getRow($sql)//获取一行
+	{
+		if (!preg_match("/LIMIT(\s){1,}(\d){1,},(\s){0,}(\d){1,}/u", $sql) && !preg_match("/LIMIT(\s){1,}(\d){1,}/u", $sql)) {
+			$sql = $sql . " LIMIT 1";
+		}
+		$rs = $this->query($sql);
+		return \mysql_fetch_assoc($rs);
+	}
+
+	public function getOne($sql)//获取一列
+	{
+		$data = $this->getRow($sql);
+		return array_shift($data);
+	}
+
+	public function getAll($sql)//获取所有的行
+	{
+		$data = array();
+		$rs = $this->query($sql);
+		while ($row = \mysql_fetch_assoc($rs)) {
+			$data[] = $row;
+		}
+		return $data;
+	}
+
+	/**
+	 * 获取一行
+	 *
+	 * @param Resource $rs
+	 * @return Array
+	 */
+	public function fetch($rs = null)
+	{
+		if (!$rs) return \mysql_fetch_assoc($this->rs);
+		return \mysql_fetch_assoc($rs);
+	}
+
+	public function transaction()//事务处理
+	{
+		\mysql_query('begin');
+	}
+
+	public function commit()//事务提交
+	{
+		\mysql_query('commit');
+	}
+
+	public function rollback()//事务回滚
+	{
+		\mysql_query('rollback');
+	}
+
+	public function affectedRows()//返回影响的行数
+	{
+		return \mysql_affected_rows($this->db['CURRENT']);
+	}
+
+	public function lastInsertId()//返回自增长ID
+	{
+		return \mysql_insert_id($this->db['CURRENT']);
+	}
+
+	/**
+	 * 获取最后一次出错的信息
+	 *
+	 * @return Array
+	 */
+	public function getError($key = '')
+	{
+		$errorInfo = array_pop($this->_errorInfo);
+		if ($errorInfo) {
+			//将错误加回来
+			array_push($this->_errorInfo, $errorInfo);
+			if (!empty($key)) {
+				return $errorInfo[$key];
+			}
+			return $errorInfo;
+		}
+		return false;
+	}
+
+	/**
+	 * 是否有错,有错误的话存储错误
+	 *
+	 */
+	public function setError()//设置错误
+	{
+		if (\mysql_errno($this->db['CURRENT'])) {
+			$this->_errorInfo[$this->queryTimes]['sql'] = $this->sql;
+			$this->_errorInfo[$this->queryTimes]['error'][2] = $this->iconv(\mysql_error($this->db['CURRENT']));
+			$this->response = \Qii\Driver\Response::Fail('mysql.error', $this->_errorInfo);
+		}
+	}
+
+	/**
+	 * 是否执行出错
+	 *
+	 * @return Bool
+	 */
+	public function isError()
+	{
+		if ($this->getError()) {
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * 返回response对象
+	 *
+	 * @return Bool
+	 */
+	public function getResponse()
+	{
+		return $this->response;
+	}
+}

+ 57 - 0
src/Driver/Mysqli/Connection.php

@@ -0,0 +1,57 @@
+<?php
+namespace Qii\Driver\Mysqli;
+
+class Connection extends \Qii\Driver\ConnBase implements \Qii\Driver\ConnIntf
+{
+	const VERSION = '1.2';
+	protected $_dbInfo;
+
+	public function __construct()
+	{
+		$this->_dbInfo = \Qii\Config\Register::getAppConfigure(\Qii\Config\Register::get(\Qii\Config\Consts::APP_DB));
+	}
+
+	/**
+	 * 获取读数据的连接资源
+	 */
+	public function getReadConnection()
+	{
+		$dbInfo = $this->_dbInfo['master'];
+		$useSlave = false;
+
+		if ($this->_dbInfo['readOrWriteSeparation'] && $this->_dbInfo['slave']) {
+			$i = rand(0, count($this->_dbInfo['slave']) - 1);
+			$dbInfo = $this->_dbInfo['slave'][$i];
+			$useSlave = true;
+		}
+
+		if ($useSlave) {
+			try {
+				$connection = mysqli_connect($dbInfo['host'], $dbInfo['user'], $dbInfo['password'], $dbInfo['db']);
+				if (!$connection) throw new \Qii\Exceptions\Errors(\Qii::i(1501, iconv("GBK", "UTF-8//TRANSLIT", mysqli_connect_error())), true);
+				mysqli_select_db($connection, $dbInfo['db']);
+				return $connection;
+			} catch (Exception  $e) {
+				return $this->getWriteConnection();
+			}
+		}
+		return $this->getWriteConnection();
+	}
+
+	/**
+	 * 获取写数据的连接资源
+	 *
+	 */
+	public function getWriteConnection()
+	{
+		$dbInfo = $this->_dbInfo['master'];
+		try {
+			$connection = @mysqli_connect($dbInfo['host'], $dbInfo['user'], $dbInfo['password'], $dbInfo['db']);
+			if (!$connection) throw new \Qii\Exceptions\Errors(\Qii::i(1501, iconv("GBK", "UTF-8//TRANSLIT", mysqli_connect_error())), true);
+			mysqli_select_db($connection, $dbInfo['db']);
+			return $connection;
+		} catch (Exception  $e) {
+			throw new \Qii\Exceptions\Errors(\Qii::i(1500, $dbInfo['host'], $dbInfo['user'], $dbInfo['password'], $dbInfo['db'], $e->getMessage()), __LINE__);
+		}
+	}
+}

+ 275 - 0
src/Driver/Mysqli/Driver.php

@@ -0,0 +1,275 @@
+<?php
+namespace Qii\Driver\Mysqli;
+
+\Qii\Autoloader\Import::requires(dirname(dirname(__FILE__)) . DS . 'Response.php');
+
+class Driver extends \Qii\Driver\Base implements \Qii\Driver\Intf
+{
+	const VERSION = '1.2';
+	private static $_instance;
+	protected $connection;
+	private $sysConfigure;
+	private $rs;
+	public $db;
+	/**
+	 * 是否开启调试
+	 *
+	 * @var BOOL
+	 */
+	public $_debugSQL = true;
+	/**
+	 * 执行SQL的列表
+	 *
+	 * @var Array
+	 */
+	public $_exeSQL = array();
+	/**
+	 * 查询次数
+	 *
+	 * @var unknown_type
+	 */
+	public $queryTimes = 0;
+	/**
+	 * 查询耗时
+	 *
+	 * @var INT
+	 */
+	public $querySeconds = array();
+
+	/**
+	 * 最后一次执行的SQL
+	 *
+	 * @var unknown_type
+	 */
+	private $sql;
+	/**
+	 * 是否开启执行SQL的时间
+	 *
+	 * @var BOOL
+	 */
+	public $_debugTime = false;
+	public $_errorInfo = array();
+	/**
+	 * 保存未定义变量
+	 *
+	 * @var Array
+	 */
+	private $_undefined;
+	/**
+	 * @var string $charset 数据库默认编码
+	 */
+	public $charset = 'UTF8';
+	/**
+	 * @var array $currentDB 当前数据库信息
+	 */
+	public $currentDB;
+	/**
+	 * @var string $markKey 用于保存数据库执行相关信息
+	 */
+	public $markKey = '__model';
+	/**
+	 * @var string $response Response对象
+	 */
+	protected $response;
+
+	public function __construct(\Qii\Driver\ConnIntf $connection)
+	{
+		parent::__construct();
+		$this->connection = $connection;
+		$this->sysConfigure = $this->connection->getDBInfo();
+		$this->currentDB = $this->sysConfigure['master']['db'];
+		$this->response = new \Qii\Driver\Response();
+	}
+
+	/**
+	 * 用户直接输出这个实例化的类后会输出当前类的名称
+	 *
+	 * @return String
+	 */
+	public function __toString()
+	{
+		return get_class($this);
+	}
+
+	/**
+	 * 查询预处理
+	 */
+	public function setQuery($sql)
+	{
+		return $this->query($sql);
+	}
+
+	/**
+	 * 执行查询
+	 */
+	public function query($sql)
+	{
+		/**
+		 * 如果调试SQL的话就启用时间的记录
+		 */
+		if ($this->_debugSQL) {
+			$startTime = microtime(true);
+		}
+		$this->sql = $sql;
+		$this->db['CURRENT'] = $this->connection->getConnectionBySQL($this->sql);
+		if (!empty($this->sysConfigure['charset'])) {
+			\mysqli_query($this->db['CURRENT'], "SET CHARACTER SET {$this->sysConfigure['charset']}");
+		} else {
+			\mysqli_query($this->db['CURRENT'], "SET CHARACTER SET UTF8");
+		}
+
+		$this->rs = $rs = \mysqli_query($this->db['CURRENT'], $sql);
+		$this->setError();
+		if (!$rs) {
+			$error = $this->getError('error');
+			return \Qii::setError(false, __LINE__, 1509, $sql, $error[2] == '' ? 'NULL' : $error[2]);
+		}
+		/**
+		 * 如果调试SQL的话就启用时间的记录
+		 */
+		if ($this->_debugSQL) {
+			$endTime = microtime(true);
+			$costTime = sprintf('%.4f', ($endTime - $startTime));
+			$this->querySeconds[$this->queryTimes]['sql'] = $sql;
+			$this->querySeconds[$this->queryTimes]['costTime'] = $costTime;
+			$this->querySeconds[$this->queryTimes]['startTime'] = $startTime;
+			$this->querySeconds[$this->queryTimes]['endTime'] = $endTime;
+		}
+		$this->queryTimes++;
+		return $rs;
+	}
+
+	/**
+	 * 执行SQL
+	 *
+	 * @param String $sql
+	 * @return Int
+	 */
+	public function exec($sql)
+	{
+		$this->setQuery($sql);
+		return $this->affectedRows();
+	}
+
+	public function getRow($sql)//获取一行
+	{
+		if (!preg_match("/LIMIT(\s){1,}(\d){1,},(\s){0,}(\d){1,}/u", $sql) && !preg_match("/LIMIT(\s){1,}(\d){1,}/u", $sql)) {
+			$sql = $sql . " LIMIT 1";
+		}
+		$rs = $this->query($sql);
+		return \mysqli_fetch_assoc($rs);
+	}
+
+	public function getOne($sql)//获取一列
+	{
+		$data = $this->getRow($sql);
+		return array_shift($data);
+	}
+
+	public function getAll($sql)//获取所有的行
+	{
+		$data = array();
+		$rs = $this->query($sql);
+		while ($row = \mysqli_fetch_assoc($rs)) {
+			$data[] = $row;
+		}
+		return $data;
+	}
+
+	/**
+	 * 获取一行
+	 *
+	 * @param Resource $rs
+	 * @return Array
+	 */
+	public function fetch($rs = null)
+	{
+		if (!$rs) return \mysqli_fetch_assoc($this->rs);
+		return \mysqli_fetch_assoc($rs);
+	}
+
+	/**
+	 * 事务处理
+	 */
+	public function transaction()
+	{
+		\mysqli_query('begin');
+	}
+
+	/**
+	 * 事务提交
+	 */
+	public function commit()
+	{
+		\mysqli_query('commit');
+	}
+
+	/**
+	 * 事务回滚
+	 */
+	public function rollback()
+	{
+		\mysqli_query('rollback');
+	}
+
+	/**
+	 * 返回影响的行数
+	 */
+	public function affectedRows()
+	{
+		return \mysqli_affected_rows($this->db['CURRENT']);
+	}
+
+	/**
+	 * 返回自增长ID
+	 */
+	public function lastInsertId()
+	{
+		return \mysqli_insert_id($this->db['CURRENT']);
+	}
+
+	/**
+	 * 获取最后一次出错的信息
+	 *
+	 * @return Array
+	 */
+	public function getError($key = '')
+	{
+		$errorInfo = array_pop($this->_errorInfo);
+		if ($errorInfo) {
+			//将错误加回来
+			array_push($this->_errorInfo, $errorInfo);
+			if (!empty($key)) {
+				return $errorInfo[$key];
+			}
+			return $errorInfo;
+		}
+		return false;
+	}
+
+	/**
+	 * 是否有错,有错误的话存储错误
+	 *
+	 */
+	public function setError()//设置错误
+	{
+		if (\mysqli_errno($this->db['CURRENT'])) {
+			$this->_errorInfo[$this->queryTimes]['sql'] = $this->sql;
+			$this->_errorInfo[$this->queryTimes]['error'][2] = $this->iconv(\mysqli_error($this->db['CURRENT']));
+			$this->response = \Qii\Driver\Response::Fail('mysqli.error', $this->_errorInfo);
+		}
+	}
+
+	/**
+	 * 是否执行出错
+	 *
+	 * @return Bool
+	 */
+	public function isError()
+	{
+		if ($this->getError()) {
+			return true;
+		}
+		return false;
+	}
+}

+ 90 - 0
src/Driver/Observer.php

@@ -0,0 +1,90 @@
+<?php
+/**
+ * 观察者
+ * @author Jinhui Zhu
+ *
+ * 用法:
+ * class User
+ * {
+ *        public $observer;
+ *        public function __construct()
+ *        {
+ *            $this->observer = new \Qii_Driver_Observer($this);
+ *    }
+ *        public function signup($email, $password)
+ *        {
+ *            //todo
+ *            //执行notify通知观察者
+ *            $this->observer->notify($email, $password);
+ *        }
+ * }
+ * class emailObserver implements \SplObserver
+ * {
+ *        public function update(SplSubject $subject)
+ *        {
+ *            //todo
+ *            $email = func_get_arg(1);
+ *            $password = func_get_arg(2);
+ *            echo '发送邮件到'. $email . ', 你的密码是'. $password . '请妥善保管';
+ *        }
+ * }
+ * $user = new User();
+ * $user->observer->attach($emailObserver);
+ * $user->signup('email@test.com', '123456');
+ */
+namespace Qii\Driver;
+
+use SplSubject;
+use SplObjectStorage;
+use SplObserver;
+
+class Observer implements SplSubject
+{
+	private $observers = NULL;
+	//上下文
+	public $context;
+
+	/**
+	 * Observer constructor.
+	 * @param $context 调用此方法的类
+	 */
+	public function __construct($context)
+	{
+		if (!isset($context) || !$context || !is_object($context)) throw new \Exception(\Qii::i(1003), __LINE__);
+		
+		$this->context = $context;
+		$this->observers = new \SplObjectStorage();
+	}
+
+	/**
+	 * 添加观察者
+	 * @param SplObserver $observer
+	 */
+	public function attach(\SplObserver $observer)
+	{
+		$this->observers->attach($observer);
+	}
+
+	/**
+	 * 移除观察者
+	 * @param SplObserver $observer
+	 */
+	public function detach(\SplObserver $observer)
+	{
+		$this->observers->detach($observer);
+	}
+
+	/**
+	 * 发送通知 调用此方法需要传递一个参数
+	 */
+	public function notify()
+	{
+		$result = array();
+		$args = func_get_args();
+		array_unshift($args, $this);
+		foreach ($this->observers as $observer) {
+			$result[] = call_user_func_array(array($observer, 'update'), $args);
+		}
+		return $result;
+	}
+}

+ 74 - 0
src/Driver/Pdo/Connection.php

@@ -0,0 +1,74 @@
+<?php
+/**
+ * 数据库连接类
+ *
+ * @author Jinhui Zhu<jinhui.zhu@live.cn>2015-12-10 14:42
+ */
+namespace Qii\Driver\Pdo;
+
+class Connection extends \Qii\Driver\ConnBase implements \Qii\Driver\ConnIntf
+{
+	const VERSION = '1.2';
+	/**
+	 * @var array $_dbInfo 数据库配置
+	 */
+	protected $_dbInfo;
+	/**
+	 * @var res $_instanceConnection 数据库连接
+	 */
+	protected $_instanceConnection;
+
+	public function __construct()
+	{
+		$this->_dbInfo = \Qii\Config\Register::getAppConfigure(\Qii\Config\Register::get(\Qii\Config\Consts::APP_DB));
+		if(!isset($this->_dbInfo['use_db_driver'])) $this->_dbInfo['use_db_driver'] = 'mysql';
+	}
+
+	/**
+	 * 获取读数据的连接资源
+	 * @return res
+	 */
+	public function getReadConnection()
+	{
+		$dbInfo = $this->_dbInfo['master'];
+		$useSlave = false;
+
+		if ($this->_dbInfo['readOrWriteSeparation'] && $this->_dbInfo['slave']) {
+			$i = rand(0, count($this->_dbInfo['slave']) - 1);
+			$dbInfo = $this->_dbInfo['slave'][$i];
+			$useSlave = true;
+		}
+		if ($useSlave) {
+			try {
+				if ($this->_dbInfo['use_db_driver'] == 'mssql') {
+					$dsn = 'odbc:Driver={SQL Server};Server=' . $dbInfo['host'] . ';Database=' . $dbInfo['db'] . ';';
+				} else {
+					$dsn = $this->_dbInfo['use_db_driver'] . ":host=" . $dbInfo['host'] . ";dbname=" . $dbInfo['db'];
+				}
+				return new \PDO($dsn, $dbInfo['user'], $dbInfo['password']);
+			} catch (Exception  $e) {
+				return $this->getWriteConnection();
+			}
+		}
+		return $this->getWriteConnection();
+	}
+
+	/**
+	 * 获取写数据的连接资源
+	 * @return res
+	 */
+	public function getWriteConnection()
+	{
+		$dbInfo = $this->_dbInfo['master'];
+		try {
+			if ($this->_dbInfo['use_db_driver'] == 'mssql') {
+				$dsn = 'odbc:Driver={SQL Server};Server=' . $dbInfo['host'] . ';Database=' . $dbInfo['db'] . ';';
+			} else {
+				$dsn = $this->_dbInfo['use_db_driver'] . ":host=" . $dbInfo['host'] . ";dbname=" . $dbInfo['db'];
+			}
+			return new \PDO($dsn, $dbInfo['user'], $dbInfo['password']);
+		} catch (Exception  $e) {
+			throw new \Qii\Exceptions\Errors(\Qii::i(1500, $dbInfo['host'], $dbInfo['user'], $dbInfo['password'], $dbInfo['db'], iconv('GB2312', 'UTF-8', $e->getMessage())), __LINE__);
+		}
+	}
+}

+ 317 - 0
src/Driver/Pdo/Driver.php

@@ -0,0 +1,317 @@
+<?php
+namespace Qii\Driver\Pdo;
+
+\Qii\Autoloader\Import::requires(dirname(dirname(__FILE__)) . DS . 'Response.php');
+
+class Driver extends \Qii\Driver\Base implements \Qii\Driver\Intf
+{
+    const VERSION = '1.2';
+    private static $_instance;
+    protected $connection;
+    private $sysConfigure;
+    private $rs;
+    public $db;
+    /**
+     * 是否开启调试
+     *
+     * @var BOOL
+     */
+    public $_debugSQL = true;
+    /**
+     * 执行SQL的列表
+     *
+     * @var Array
+     */
+    public $_exeSQL = array();
+    /**
+     * 查询次数
+     *
+     * @var unknown_type
+     */
+    public $queryTimes = 0;
+    /**
+     * 查询耗时
+     *
+     * @var INT
+     */
+    public $querySeconds = array();
+
+    /**
+     * 最后一次执行的SQL
+     *
+     * @var unknown_type
+     */
+    private $sql;
+    /**
+     * 是否开启执行SQL的时间
+     *
+     * @var BOOL
+     */
+    public $_debugTime = false;
+    public $_errorInfo = array();
+    /**
+     * @var string $charset 数据库默认编码
+     */
+    public $charset = 'UTF8';
+    /**
+     * 当前使用的db信息
+     */
+    public $currentDB;
+    /**
+     * @var string $markKey debug信息保存用的key
+     */
+    public $markKey = '__model';
+    /**
+     * @var string $response Response对象
+     */
+    public $response;
+
+    /**
+     * 初始化
+     * @param \Qii_Driver_ConnIntf $connection 数据库连接
+     */
+    public function __construct(\Qii\Driver\ConnIntf $connection)
+    {
+        parent::__construct();
+        $this->connection = $connection;
+        $this->sysConfigure = $this->connection->getDBInfo();
+        $this->currentDB = $this->sysConfigure['master']['db'];
+        $this->response = new \Qii\Driver\Response();
+    }
+
+    /**
+     * 用户直接输出这个实例化的类后会输出当前类的名称
+     *
+     * @return String
+     */
+    public function __toString()
+    {
+        return get_class($this);
+    }
+
+    /**
+     * 查询预处理
+     * @var string $sql 执行的sql语句
+     */
+    public function setQuery($sql)
+    {
+        $this->rs = $rs = $this->query($sql);
+        return $rs;
+    }
+
+    /**
+     * 查询
+     * @var string $sql 执行的sql语句
+     */
+    public function query($sql)
+    {
+        /**
+         * 如果调试SQL的话就启用时间的记录
+         */
+        if ($this->_debugSQL) {
+            $startTime = microtime(true);
+        }
+        $this->sql = $sql;
+        $this->db['CURRENT'] = $this->connection->getConnectionBySQL($sql);
+        $this->db['CURRENT']->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
+        $this->db['CURRENT']->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_WARNING);
+        $this->db['CURRENT']->query('set names utf8');
+        $rs = $this->db['CURRENT']->query($sql);
+        $this->setError();
+        if (!$rs) {
+            $error = $this->getError('error');
+            return \Qii::setError(false, __LINE__, 1509, $sql, $error[2] == '' ? 'NULL' : $error[2]);
+        }
+        $rs->setFetchMode(\PDO::FETCH_ASSOC);
+        /**
+         * 如果调试SQL的话就启用时间的记录
+         */
+        if ($this->_debugSQL) {
+            $endTime = microtime(true);
+            $costTime = sprintf('%.4f', ($endTime - $startTime));
+            $this->querySeconds[$this->queryTimes]['sql'] = $sql;
+            $this->querySeconds[$this->queryTimes]['costTime'] = $costTime;
+            $this->querySeconds[$this->queryTimes]['startTime'] = $startTime;
+            $this->querySeconds[$this->queryTimes]['endTime'] = $endTime;
+        }
+        $this->queryTimes++;
+        return $rs;
+    }
+
+    /**
+     * 获取一行
+     *
+     * @param Resource $rs
+     * @return Array
+     */
+    public function fetch($rs = null)
+    {
+        if (!$rs) return $this->rs->rech();
+        return $rs->fetch();
+    }
+
+    /**
+     * 执行SQL
+     *
+     * @param String $sql
+     * @return Int
+     */
+    public function exec($sql)
+    {
+        $this->rs = $this->query($sql);
+        return $this->affectedRows();
+    }
+
+    /**
+     * 设置获取数据的类型
+     *
+     */
+    public function setFetchMode()
+    {
+        $this->rs->setFetchMode(PDO::FETCH_ASSOC);
+    }
+
+    /**
+     * 获取一行
+     * @var string $sql 获取一行
+     * @return array 返回数据
+     */
+    public function getRow($sql)
+    {
+        if (!$this->sysConfigure['driver'] == 'mssql' && !preg_match("/LIMIT(\s){1,}(\d){1,},(\s){0,}(\d){1,}/ui", $sql) && !preg_match("/LIMIT(\s){1,}(\d){1,}/ui", $sql)) {
+            $sql = $sql . " LIMIT 1";
+        } else if ($this->sysConfigure['driver'] == 'mssql' && !preg_match("/^SELECT(\s)TOP(\s)(\d){1,}/i", $sql)) {
+            $sql = preg_replace("/^SELECT(\s)/i", "SELECT TOP 1 ", $sql);
+        }
+        $this->rs = $rs = $this->setQuery($sql);
+        return $rs->fetch();
+    }
+
+    /**
+     * 获取一列
+     * @var string $sql 需要获取一列的sql语句
+     * @return strin | bool 返回其中一列或者是false
+     */
+    public function getOne($sql)
+    {
+        $rs = $this->setQuery($sql);
+        if ($rs) {
+            return $rs->fetchColumn();
+        }
+        return false;
+    }
+
+    /**
+     * 获取所有的行
+     * @var string $sql 需要获取所有行的sql语句
+     * @return array
+     */
+    public function getAll($sql)
+    {
+        $this->rs = $rs = $this->setQuery($sql);
+        return $rs->fetchAll();
+    }
+
+    /**
+     * 事务处理
+     */
+    public function transaction()
+    {
+        $this->db['CURRENT']->beginTransaction();
+    }
+
+    /**
+     * 事务提交
+     */
+    public function commit()
+    {
+        $this->db['CURRENT']->commit();
+    }
+
+    /**
+     * 事务回滚
+     */
+    public function rollback()
+    {
+        $this->db['CURRENT']->rollBack();
+    }
+
+    /**
+     * 影响的行数
+     *
+     * @return Int
+     */
+    public function affectedRows()
+    {
+        if (!$this->rs) return false;
+        return $this->rs->rowCount();
+    }
+
+    /**
+     * 最后插入到数据库的自增长ID
+     *
+     * @return Int
+     */
+    public function lastInsertId()
+    {
+        return $this->db['CURRENT']->lastInsertId();
+    }
+
+    /**
+     * 获取最后一次出错的信息
+     *
+     * @return Array
+     */
+    public function getError($key = '')
+    {
+        $errorInfo = array_pop($this->_errorInfo);
+        if ($errorInfo) {
+            //将错误加回来
+            array_push($this->_errorInfo, $errorInfo);
+            if (!empty($key)) {
+                return $errorInfo[$key];
+            }
+            return $errorInfo;
+        }
+        return null;
+    }
+
+    /**
+     * 是否有错,有错误的话存储错误
+     *
+     */
+    public function setError()
+    {
+        if ($this->connection->getConnectionBySQL($this->sql)->errorCode() != '00000') {
+            $this->_errorInfo[$this->queryTimes]['sql'] = $this->sql;
+            $this->_errorInfo[$this->queryTimes]['error'] = $this->iconv($this->connection->getConnectionBySQL($this->sql)->errorInfo());
+            $this->response = \Qii\Driver\Response::Fail('pdo.error', $this->_errorInfo);
+        }
+    }
+
+    /**
+     * 是否执行出错
+     *
+     * @return Bool
+     */
+    public function isError()
+    {
+        if (!$this->rs) {
+            return true;
+        }
+        if ($this->connection->getConnectionBySQL($this->sql)->errorCode() != '00000') {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * 返回response对象
+     *
+     * @return Bool
+     */
+    public function getResponse()
+    {
+        return $this->response;
+    }
+}

+ 332 - 0
src/Driver/Response.php

@@ -0,0 +1,332 @@
+<?php
+/**
+ * 数据库操作Response类,返回response对象{code:0, body:{}}
+ * @author Zhu Jinhui<jinhui.zhu@live.cn> 2015-12-31 17:49
+ * 用法:
+ * $response = array();
+ *
+ * $response[] = new \Qii\Driver\Response(array(
+ *        'code' => \Qii\Driver\Response::DO_SUCCESS,
+ *        'body' => array(
+ *            'operate' => 'save',
+ *            'result' => 10
+ *        )
+ * ));
+ * $response[] = \Qii\Driver\Response::Success('save', 10);
+ * $response[] = new Response(array(
+ *        'code' => \Qii\Driver\Response::DO_FAIL,
+ *        'body' => array(
+ *            'operate' => 'save',
+ *            'result' => '操作失败'
+ *        )
+ * ));
+ * $response[] = Qii\Driver\Response::Fail('save', '操作失败');
+ * $response[] = new Response(array(
+ *        'code' => Qii\Driver\Response::DOES_EXISTS,
+ *        'body' => array(
+ *            'operate' => 'save',
+ *            'result' => array(
+ *                    'uid' => 1,
+ *                    'email' => 'antsnet@163.com'
+ *            )
+ *        )
+ * ));
+ * $response[] = \Qii\Driver\Response::Exist('save', array('uid' => 10, 'email' => 'antsnet@163.com'));
+ * $response[] = new Response(array(
+ *        'code' => \Qii\Driver\Response::DOES_NOT_EXISTS,
+ *        'body' => array(
+ *            'operate' => 'save',
+ *            'result' => array()
+ *        )
+ * ));
+ * $response[] = \Qii\Driver\Response::NotExist('save', 10);
+ * $response[] = new Response(array(
+ *        'code' => \Qii\Driver\Response::FAIL_FOR_VALIDATE,
+ *        'body' => array(
+ *            'operate' => 'save',
+ *            'result' => array(
+ *                'fields' => array(
+ *                    array(
+ *                        'name' => 'email',
+ *                        'msg' => '验证失败/格式不正确'
+ *                    )
+ *                )
+ *            )
+ *        )
+ * ));
+ * $response[] = \Qii\Driver\Response::FailValidate('save', array('fields' => array('name' => 'email', 'msg' => '验证失败/格式不正确')));
+ *
+ * $response[] = new Response(array(
+ *        'code' => \Qii\Driver\Response::FAIL_FOR_SAVE,
+ *        'body' => array(
+ *            'operate' => 'save',
+ *            'result' => '连接错误'
+ *        )
+ * ));
+ * $result[] = \Qii\Driver\Response::FailSave('save', '链接错误');
+ * foreach($response AS $res)
+ * {
+ *    if($res->isError())
+ *    {
+ *        echo "<pre>". $res->getCode() .'&nbsp;'. print_r($res->getError(), true) ."</pre>";
+ *    }
+ *    else
+ *    {
+ *        echo '操作成功' . print_r($res->getResult(), 1);
+ *    }
+ * }
+ */
+namespace Qii\Driver;
+
+class Response
+{
+	const VERSION = '1.2';
+	//成功
+	const DO_SUCCESS = 0;
+	//失败
+	const DO_FAIL = 1;
+	//数据已经存在
+	const DOES_EXIST = 100;
+	//数据不存在
+	const DOES_NOT_EXIST = 101;
+	//验证失败
+	const FAIL_FOR_VALIDATE = 102;
+	//保存失败
+	const FAIL_FOR_SAVE = 103;
+	//更新失败
+	const FAIL_FOR_UPDATE = 104;
+	//删除失败
+	const FAIL_FOR_REMOVE = 105;
+	//类为定义
+	const UNDEFINED_CLASS = 106;
+	//方法为定义
+	const UNDEFINED_METHOD = 107;
+	//是否有错误
+	public $isError = false;
+	//状态码,对应上边的常量
+	public $code;
+	//返回的内容
+	public $body;
+
+	/**
+	 * 实例化response对象
+	 */
+	public function __construct()
+	{
+		$this->code = 0;
+		$this->body = array();
+		return $this;
+	}
+
+	/**
+	 * 设置response的信息
+	 */
+	public function set(array $data)
+	{
+		if (!isset($data['code']) || !isset($data['body'])
+			|| !isset($data['body']['operate']) || !isset($data['body']['result'])
+		) {
+			throw new \Exception('Response muse have code , body , body[operate] and body[result] element');
+		}
+		foreach ($data AS $key => $val) {
+			$this->$key = $val;
+		}
+		$this->isError = $this->isError();
+		return $this;
+	}
+
+	/**
+	 * 成功
+	 * @param string $operate 操作类型
+	 * @param mix $result 结果
+	 * @return Qii\Driver\Response
+	 */
+	public static function Success($operate, $result)
+	{
+		return self::Instance(self::DO_SUCCESS, $operate, $result);
+	}
+
+	/**
+	 * 失败
+	 * @param string $operate 操作类型
+	 * @param mix $result 结果
+	 * @return Qii\Driver\Response
+	 */
+	public static function Fail($operate, $result)
+	{
+		return self::Instance(self::DO_FAIL, $operate, $result);
+	}
+
+	/**
+	 * 记录已存在
+	 * @param string $operate 操作类型
+	 * @param mix $result 结果
+	 * @return Qii\Driver\Response
+	 */
+	public static function Exist($operate, $result)
+	{
+		return self::Instance(self::DOES_EXIST, $operate, $result);
+	}
+
+	/**
+	 * 记录不存在
+	 * @param string $operate 操作类型
+	 * @param mix $result 结果
+	 * @return Qii\Driver\Response
+	 */
+	public static function NotExist($operate, $result)
+	{
+		return self::Instance(self::DOES_NOT_EXIST, $operate, $result);
+	}
+
+	/**
+	 * 验证失败
+	 * @param string $operate 操作类型
+	 * @param mix $result 结果
+	 * @return Qii\Driver\Response
+	 */
+	public static function FailValidate($operate, $result)
+	{
+		return self::Instance(self::FAIL_FOR_VALIDATE, $operate, $result);
+	}
+
+	/**
+	 * 保存失败
+	 * @param string $operate 操作类型
+	 * @param mix $result 结果
+	 * @return Qii\Driver\Response
+	 */
+	public static function FailSave($operate, $result)
+	{
+		return self::Instance(self::FAIL_FOR_SAVE, $operate, $result);
+	}
+
+	/**
+	 * 更新失败
+	 * @param string $operate 操作类型
+	 * @param mix $result 结果
+	 * @return Qii\Driver\Response
+	 */
+	public static function FailUpdate($operate, $result)
+	{
+		return self::Instance(self::FAIL_FOR_UPDATE, $operate, $result);
+	}
+
+	/**
+	 * 删除失败
+	 * @param string $operate 操作类型
+	 * @param mix $result 结果
+	 * @return Qii\Driver\Response
+	 */
+	public static function FailRemove($operate, $result)
+	{
+		return self::Instance(self::FAIL_FOR_REMOVE, $operate, $result);
+	}
+
+	/**
+	 * 方法未定义
+	 * @param string $operate 操作类型
+	 * @param mix $result 结果
+	 * @return Qii\Driver\Response
+	 */
+	public static function UndefinedMethod($operate, $result)
+	{
+		return self::Instance(self::UNDEFINED_METHOD, $operate, $result);
+	}
+
+	/**
+	 * 类失败
+	 * @param string $operate 操作类型
+	 * @param mix $result 结果
+	 * @return Qii\Driver\Response
+	 */
+	public static function UndefinedClass($operate, $result)
+	{
+		return self::Instance(self::UNDEFINED_CLASS, $operate, $result);
+	}
+
+	/**
+	 * 直接初始化Qii\Driver\Response 对象
+	 */
+	public static function Instance($code, $operate, $result)
+	{
+		$data = array('code' => $code, 'body' => array('operate' => $operate, 'result' => $result));
+		return (new \Qii\Driver\Response())->set($data);
+	}
+
+	/**
+	 * 获取操作类型
+	 */
+	public function getOperate()
+	{
+		if (isset($this->body['operate'])) return $this->body['operate'];
+		throw new \Exception('Call undefined operate');
+	}
+
+	/**
+	 * 返回body中的result,默认返回_result字段
+	 * @param string $key 返回字段
+	 * @return mix
+	 */
+	public function getResult($key = '_result')
+	{
+		if ($key) {
+			return isset($this->body['result']) && isset($this->body['result'][$key]) ? $this->body['result'][$key] : null;
+		}
+		return $this->body['result'];
+	}
+
+	/**
+	 * 返回错误信息
+	 */
+	public function getMessage()
+	{
+		$message = array(
+			0 => '成功',
+			1 => '失败',
+			100 => '数据已经存在',
+			101 => '数据不存在',
+			102 => '验证失败',
+			103 => '保存失败',
+			104 => '更新失败',
+			105 => '删除失败',
+			106 => '类未定义',
+			107 => '方法未定义'
+		);
+		$code = $this->getCode();
+		if (isset($message[$code])) return $message[$code] . $this->getResult();
+		return $this->getResult('message');
+	}
+
+	/**
+	 * 返回错误信息,如果无错误即返回false
+	 * @return array
+	 */
+	public function getErrors()
+	{
+		$code = $this->code;
+		if ($code == self::DO_SUCCESS) return false;
+		return $this->getResult();
+	}
+
+	/**
+	 * 是否包含错误信息
+	 */
+	public function isError()
+	{
+		$code = $this->code;
+		return $code == self::DO_SUCCESS ? false : true;
+	}
+
+	/**
+	 * 当调用get...的时候使用
+	 */
+	public function __call($method, $args)
+	{
+		if (substr($method, 0, 3) == 'get') {
+			$propertty = strtolower(substr($method, 3));
+			if (property_exists($this, $propertty)) return $this->$propertty;
+		}
+		throw new \Qii\Exceptions\MethodNotFound(\Qii::i(1101, $method), __LINE__);
+	}
+}

+ 232 - 0
src/Driver/Rules.php

@@ -0,0 +1,232 @@
+<?php
+/**
+ * 数据保存规则
+ * @author Jinhui Zhu<jinhui.zhu@live.cn> 2015-12-16
+ */
+namespace Qii\Driver;
+
+class Rules
+{
+	const VERSION = '1.2';
+	/**
+	 * @var  $rules
+	 */
+	private $rules;
+
+	/**
+	 * @var $cacheRules
+	 */
+	static $cacheRules;
+
+	static $invalidMessage;
+
+	public function __construct($rules)
+	{
+		$this->rules = $rules;
+	}
+
+	/**
+	 * 获取使用的数据表名
+	 *
+	 * @return mixed
+	 * @throws \Exception 未定义database的时候就抛出异常
+	 */
+	public function getDatabase()
+	{
+		if (!isset($this->rules['database'])) {
+			throw new \Exception(\Qii::i(5001, 'database'), __LINE__);
+		}
+		return $this->rules['database'];
+	}
+
+	/**
+	 * 获取数据表名称
+	 *
+	 * @return mixed
+	 * @throws \Exception 未定义table的时候就抛出异常
+	 * @return string
+	 */
+	public function getTableName()
+	{
+		if (!isset($this->rules['tableName'])) {
+			throw new \Exception(\Qii::i(5001, 'tableName'), __LINE__);
+		}
+		return $this->rules['tableName'];
+	}
+
+	/**
+	 * 获取数据表中的字段列表
+	 *
+	 * @return mixed
+	 * @throws \Exception
+	 */
+	public function getFields()
+	{
+		if (!isset($this->rules['rules']['fields'])) {
+			throw new \Exception(\Qii::i(5002, 'fields'), __LINE__);
+		}
+		return $this->rules['rules']['fields'];
+	}
+
+	/**
+	 * 获取验证失败的时候提示信息
+	 * 优先使用规则里表中的验证提示信息,如果未指定,再使用自动生成的规则信息
+	 * @return array  提示信息
+	 */
+	public function getInvalidMessage()
+	{
+		if (isset($this->rules['rules']['invalidMessage'])) return $this->rules['rules']['invalidMessage'];
+		if (isset(self::$invalidMessage[$this->rules['database'] . $this->rules['tableName']])) return self::$invalidMessage[$this->rules['database'] . $this->rules['tableName']];
+		return array();
+	}
+
+	/**
+	 * 获取数据表保存\更新\的验证规则
+	 * @return array  获取验证规则
+	 */
+	public function getOriginalRules()
+	{
+		if (!isset($this->rules['rules'])) {
+			return array();
+		}
+		return $this->rules['rules'];
+	}
+
+	/**
+	 * 重置privateKeys使用
+	 *
+	 * @return array
+	 */
+	public function getOperateValidFields($opt = null)
+	{
+		$fields = array();
+		//检查是否存在数据使用字段
+		if (isset($this->rules['rules']['existValid'])) {
+			$fields['exist'] = array_keys($this->rules['rules']['existValid']);
+		}
+		//检查是保存使用字段
+		if (isset($this->rules['rules']['saveValid'])) {
+			$fields['save'] = array_keys($this->rules['rules']['saveValid']);
+		}
+		//更新使用字段
+		if (isset($this->rules['rules']['updateValid'])) {
+			$fields['update'] = array_keys($this->rules['rules']['updateValid']);
+		}
+		//删除使用字段
+		if (isset($this->rules['rules']['removeValid'])) {
+			$fields['remove'] = array_keys($this->rules['rules']['removeValid']);
+		}
+		if (!$opt) return $fields;
+		return isset($fields[$opt]) ? $fields[$opt] : array();
+	}
+
+	/**
+	 * 获取唯一字段,如果不设置privatekey就是用此字段作为privatekey
+	 */
+	public function getPrivateKey()
+	{
+		if (isset($this->rules['rules']['uni']) && count($this->rules['rules']['uni']) > 0) return $this->rules['rules']['uni'];
+	}
+
+	/**
+	 * 根据字段获取验证规则
+	 * @param string $field 需要验证的字段
+	 * @return array 验证规则
+	 */
+	public function getRulesByField($field)
+	{
+		$rules = $this->buildRules();
+		return isset($rules[$field]) ? $rules[$field] : array();
+	}
+
+	/**
+	 * 根据配置生成所有验证需要的规则
+	 *
+	 * @return array 生成规则
+	 */
+	public function buildRules()
+	{
+		$rules = array();
+		if (!isset($this->rules['rules']['validate']) && count($this->rules['rules']['validate']) == 0) {
+			return $rules;
+		}
+		if (isset(self::$cacheRules[$this->rules['database'] . $this->rules['tableName']]['rules'])) return self::$cacheRules[$this->rules['database'] . $this->rules['tableName']]['rules'];
+		foreach ($this->rules['rules']['validate'] AS $key => $validate) {
+			$fieldRules = array();
+			$fieldRules['required'] = false;
+			foreach ($validate AS $rule) {
+				if ($rule == 'minlength') {
+					$fieldRules[$rule] = 1;
+					continue;
+				}
+				if ($rule == 'maxlength') {
+					$fieldRules[$rule] = $this->rules['rules']['length'][$key];
+					continue;
+				}
+				if ($rule == 'sets') {
+					if (isset($this->rules['rules']['sets'][$key])) $fieldRules[$rule] = $this->rules['rules']['sets'][$key];
+					continue;
+				}
+				$fieldRules[$rule] = true;
+				//根据验证类型自动生成规则验证错误信息
+				$alias = isset($this->rules['rules']['alias']) && isset($this->rules['rules']['alias'][$key]) ? $this->rules['rules']['alias'][$key] : $key;
+				self::$invalidMessage[$this->rules['database'] . $this->rules['tableName']][$key][$rule] = $rule == 'required' ? \Qii::i(5003, $alias) : \Qii::i(5004, $alias);
+			}
+			$rules[$key] = $fieldRules;
+		}
+		self::$cacheRules[$this->rules['database'] . $this->rules['tableName']]['rules'] = $rules;
+		return $rules;
+	}
+
+	/**
+	 * 通过数据库操作类型获取对应的规则
+	 * @param string $opt 操作类型
+	 * @return array 规则
+	 */
+	public function getRulesByOperate($opt = 'save')
+	{
+		$rules = array();
+		$allowOpt = array('save', 'update', 'remove');
+		if (!in_array($opt, $allowOpt)) {
+			throw new \Qii\Exceptions\NotAllowed(\Qii::i(5002, $opt), __LINE__);
+		}
+		if (isset(self::$cacheRules[$this->rules['database'] . $this->rules['tableName']][$opt])) return self::$cacheRules[$this->rules['database'] . $this->rules['tableName']][$opt];
+		$buildRules = $this->buildRules();
+		//获取字段的验证类型
+		$fieldsValidate = $this->rules['rules']['validate'];
+		//获取操作需要验证的字段
+		if (!isset($this->rules['rules'][$opt]) || count($this->rules['rules'][$opt]) == 0) return $rules;
+		foreach ($this->rules['rules'][$opt] AS $key => $val) {
+			//如果操作需要验证字段没有设定规则,就验证字段必须有值
+			if (isset($buildRules[$key])) {
+				$rules[$key] = $buildRules[$key];
+			} else {
+				$rules[$key] = array('required' => true);
+			}
+		}
+		self::$cacheRules[$this->rules['database'] . $this->rules['tableName']][$opt] = $rules;
+		return $rules;
+	}
+
+	/**
+	 * 通过 $this->$val来获取 $this->$val();的返回内容
+	 * @param string $name 属性名称
+	 */
+	public function __get($name)
+	{
+		if (method_exists($this, $name)) {
+			return call_user_func_array(array($this, $name), array());
+		}
+		throw new \Qii\Exceptions\MethodNotFound(\Qii::i(1101, $name), __LINE__);
+	}
+
+	/**
+	 * 调用不存在的方法抛出方法不存在的异常
+	 * @param string $method
+	 * @param mix $args
+	 */
+	public function __call($method, $args)
+	{
+		throw new \Qii\Exceptions\MethodNotFound(\Qii::i(1101, $method), __LINE__);
+	}
+}

+ 17 - 0
src/Exceptions/AccessDenied.php

@@ -0,0 +1,17 @@
+<?php
+namespace Qii\Exceptions;
+
+class AccessDenied extends Errors
+{
+	const VERSION = '1.2';
+
+	/**
+	 * 显示错误
+	 *
+	 * @param Object $e Exception
+	 */
+	public static function getError($e)
+	{
+
+	}
+}

+ 17 - 0
src/Exceptions/Cache.php

@@ -0,0 +1,17 @@
+<?php
+namespace Qii\Exceptions;
+
+class Cache extends Errors
+{
+	const VERSION = '1.2';
+
+	/**
+	 * 显示错误
+	 *
+	 * @param Object $e Exception
+	 */
+	public static function getError($e)
+	{
+
+	}
+}

+ 17 - 0
src/Exceptions/CallUndefinedClass.php

@@ -0,0 +1,17 @@
+<?php
+namespace Qii\Exceptions;
+
+class CallUndefinedClass extends Errors
+{
+	const VERSION = '1.2';
+
+	/**
+	 * 显示错误
+	 *
+	 * @param Object $e Exception
+	 */
+	public static function getError($e)
+	{
+
+	}
+}

+ 17 - 0
src/Exceptions/ClassInstanceof.php

@@ -0,0 +1,17 @@
+<?php
+namespace Qii\Exceptions;
+
+class ClassInstanceof extends Errors
+{
+	const VERSION = '1.2';
+
+	/**
+	 * 显示错误
+	 *
+	 * @param Object $e Exception
+	 */
+	public static function getError($e)
+	{
+
+	}
+}

+ 17 - 0
src/Exceptions/ClassNotFound.php

@@ -0,0 +1,17 @@
+<?php
+namespace Qii\Exceptions;
+
+class ClassNotFound extends Errors
+{
+	const VERSION = '1.2';
+
+	/**
+	 * 显示错误
+	 *
+	 * @param Object $e Exception
+	 */
+	public static function getError($e)
+	{
+
+	}
+}

+ 147 - 0
src/Exceptions/Error.php

@@ -0,0 +1,147 @@
+<?php
+
+namespace Qii\Exceptions;
+
+/**
+ *
+ * Class Qii_Exceptions_Error
+ * @package Qii
+ */
+class Error
+{
+    const VERSION = '1.2';
+    
+    public function __construct()
+    {
+    
+    }
+    
+    public static function index()
+    {
+        $args = func_get_args();
+        echo \Qii::i('1108', $args[0], $args[1]);
+    }
+    
+    /**
+     * 返回程序的调用栈
+     */
+    static public function getTrace()
+    {
+        return debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
+    }
+    
+    /**
+     * 程序的调用栈生成字符串
+     */
+    static public function getTraceAsString()
+    {
+        $e = new \Exception();
+        $trace = explode("\n", $e->getTraceAsString());
+        $trace = array_reverse($trace);
+        array_shift($trace);
+        array_pop($trace);
+        $length = count($trace);
+        $result = array();
+        
+        for ($i = 0; $i < $length; $i++) {
+            $result[] = ($i + 1) . ')' . substr($trace[$i], strpos($trace[$i], ' ')); // replace '#someNum' with '$i)', set the right ordering
+        }
+        
+        return "\t" . implode("\n\t", $result);
+    }
+    
+    /**
+     * 错误描述
+     * @return string
+     */
+    static public function getMessage()
+    {
+        $e = new \Exception();
+        return $e->getMessage();
+    }
+    
+    /**
+     * 错误码
+     * @return int
+     */
+    static public function getCode()
+    {
+        return __LINE__;
+    }
+    
+    /**
+     * 错误设置,如果满足给定的条件就直接返回false,否则在设置了错误页面的情况下返回true
+     * 如果出错后要终止的话,需要自行处理,此方法不错停止不执行
+     *
+     * @param bool $condition
+     * @param int $line 出错的行数,这样以便轻松定位错误
+     * @param string $msg
+     * @param int|string $code
+     * @param string $args ...
+     * @return bool
+     */
+    public static function setError($condition, $line = 0, $code, $args = null, $msg = null)
+    {
+        if ($condition) {
+            return false;
+        }
+        $appConfigure = \Qii::appConfigure();
+        //如果是调试模式就直接抛出异常
+        $isDebug = $appConfigure['debug'];
+        $message = array();
+        $message[] = explode("\n", self::getTraceAsString());
+        if (\Qii::getInstance()->logerWriter != null) {
+            $message[] = 'Referer:' . (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : \Qii::getInstance()->request->url->getCurrentURL());
+            \Qii::getInstance()->logerWriter->writeLog($message);
+        }
+        if ($isDebug) {
+            $args = array_slice(func_get_args(), 2);
+            throw new \Exception(call_user_func_array(array('\Qii', 'i'), $args), $line);
+        }
+        $errorPage = $appConfigure['errorPage'];
+        $env = \Qii\Config\Register::get(\Qii\Config\Consts::APP_ENVIRON, 'dev');
+        if ($env != 'product' && $errorPage != null) {
+            list($controller, $action) = explode(':', $appConfigure['errorPage']);
+            $controllerCls = \Qii\Config\Register::get(\Qii\Config\Consts::APP_DEFAULT_CONTROLLER_PREFIX) . '\\' . $controller;
+            $action = preg_replace('/(Action)$/i', "", $action);
+            $filePath = \Qii\Autoloader\Psr4::getInstance()->searchMappedFile($controllerCls);
+            if (!is_file($filePath)) {
+                if ($env == 'product') return '';
+                \Qii\Autoloader\Import::requires(Qii_DIR . DS . 'Exceptions' . DS . 'Error.php');
+                call_user_func_array(array('\Qii\Exceptions\Error', 'index'), array($controller, $action));
+                die();
+            } else {
+                \Qii::getInstance()->request->setControllerName($controller);
+                \Qii::getInstance()->request->setActionName($action);
+                \Qii::getInstance()->dispatcher->setRequest(\Qii::getInstance()->request);
+                \Qii::getInstance()->dispatcher->dispatch($controller, $action, new \Exception($msg ? $msg : self::getTraceAsString(), $code));
+                die();
+            }
+            return;
+        }
+        return true;
+    }
+    
+    /**
+     * 显示错误信息
+     * @param Array $message
+     */
+    public static function showError($message)
+    {
+        include(join(DS, array(Qii_DIR, 'Exceptions', 'view', 'error.php')));
+    }
+    
+    /**
+     * 显示错误信息
+     * @param Array $message
+     */
+    public static function showMessage($message)
+    {
+        include(join(DS, array(Qii_DIR, 'Exceptions', 'view', 'message.php')));
+    }
+    
+    public function __call($method, $args)
+    {
+        if (method_exists(self, $method)) return call_user_func_array(array('Qii\Exceptions\Error', $method), $args);
+    }
+}

+ 169 - 0
src/Exceptions/Errors.php

@@ -0,0 +1,169 @@
+<?php
+
+namespace Qii\Exceptions;
+
+class Errors extends \Exception
+{
+    const VERSION = '1.2';
+    
+    /**
+     * 获取两个文件的相对路径
+     * @param String $cur
+     * @param String $absp
+     * @return String
+     */
+    public static function getRelatePath($cur, $absp)
+    {
+        $cur = str_replace('\\', '/', $cur);
+        $absp = str_replace('\\', '/', $absp);
+        $sabsp = explode('/', $absp);
+        $scur = explode('/', $cur);
+        $la = count($sabsp) - 1;
+        $lb = count($scur) - 1;
+        $l = max($la, $lb);
+        
+        for ($i = 0; $i <= $l; $i++) {
+            if ($sabsp[$i] != $scur[$i])
+                break;
+        }
+        $k = $i - 1;
+        $path = "";
+        for ($i = 1; $i <= ($lb - $k - 1); $i++)
+            $path .= "../";
+        for ($i = $k + 1; $i <= ($la - 1); $i++)
+            $path .= $sabsp[$i] . "/";
+        $path .= $sabsp[$la];
+        return $path;
+    }
+    
+    /**
+     * 显示错误
+     *
+     * @param Object $e Exception
+     */
+    public static function getError($e)
+    {
+        $message = array();
+        if (isset($_GET['isAjax']) && $_GET['isAjax'] == 1) {
+            $code = $e->getCode();
+            if ($code == 0) $code = 1;
+            echo json_encode(array('code' => $code, 'line' => $e->getFile() . ' line :' . $e->getLine(), 'msg' => strip_tags($e->getMessage())), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP | JSON_UNESCAPED_UNICODE);
+            return;
+        }
+        $message[] = (IS_CLI ? QII_EOL : '') . \Qii::i('Error file', self::getRelatePath($_SERVER['SCRIPT_FILENAME'], $e->getFile())) . (IS_CLI ? QII_EOL : '');
+        $message[] = \Qii::i('Error code', $e->getCode()) . (IS_CLI ? QII_EOL : '');
+        $message[] = \Qii::i('Error description', $e->getMessage()) . (IS_CLI ? QII_EOL : '');
+        $message[] = \Qii::i('Error line', $e->getLine() . ' on ' . self::getLineMessage($e->getFile(), $e->getLine())) . (IS_CLI ? QII_EOL : '');
+        $traceString = \Qii::i('Trace as below') . QII_EOL;
+        $traces = explode("\n", $e->getTraceAsString());
+        foreach ($traces AS $trance) {
+            $traceString .= str_repeat(QII_SPACE, 4) . $trance . QII_EOL;
+        }
+        $message[] = $traceString;
+        if (\Qii::getInstance()->logerWriter != null) {
+            $message[] = 'Source URL:' . \Qii::getInstance()->request->url->getCurrentURL();
+            $message[] = 'Referer URL:' . (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : \Qii::getInstance()->request->url->getCurrentURL());
+            \Qii::getInstance()->logerWriter->writeLog($message);
+        }
+        $appConfigure = \Qii::appConfigure();
+        
+        $env = \Qii\Config\Register::get(\Qii\Config\Consts::APP_ENVIRON, 'dev');
+        if ($env == 'product' || ($appConfigure['errorPage'] && (isset($appConfigure['debug']) && $appConfigure['debug'] == 0))) {
+            list($controller, $action) = explode(':', $appConfigure['errorPage']);
+            $controllerCls = \Qii\Config\Register::get(\Qii\Config\Consts::APP_DEFAULT_CONTROLLER_PREFIX) . '\\' . $controller;
+            $action = preg_replace('/(Action)$/i', "", $action);
+            $filePath = \Qii\Autoloader\Psr4::getInstance()->searchMappedFile($controllerCls);
+            if (!is_file($filePath)) {
+                if ($env == 'product') return '';
+                \Qii\Autoloader\Import::requires(Qii_DIR . DS . 'Exceptions' . DS . 'Error.php');
+                call_user_func_array(array('\Qii\Exceptions\Error', 'index'), array($controller, $action));
+                die();
+            } else {
+                \Qii::getInstance()->request->setControllerName($controller);
+                \Qii::getInstance()->request->setActionName($action);
+                \Qii::getInstance()->dispatcher->setRequest(\Qii::getInstance()->request);
+                \Qii::getInstance()->dispatcher->dispatch($controller, $action, $e);
+                die();
+            }
+        }
+        ob_start();
+        include(join(DS, array(Qii_DIR, 'Exceptions', 'View', 'error.php')));
+        $html = ob_get_contents();
+        ob_clean();
+        
+        if (!IS_CLI) {
+            echo $html;
+            return;
+        }
+        return (new \Qii\Response\Cli())->stdout(
+            str_replace("&nbsp;"
+                , " "
+                , strip_tags(join(PHP_EOL, preg_replace("/[\n|\r\n]/", PHP_EOL, $message)))
+            )
+        );
+    }
+    
+    /**
+     * 获取指定文件的指定行内容
+     *
+     * @param String $fileName 文件名
+     * @param Int $line 行号
+     * @return String
+     */
+    public static function getLineMessage($fileName, $line)
+    {
+        $seekline = max(0, $line - 1);
+        $spl = new \SplFileObject($fileName);
+        $code = array();
+        if ($line > 1) {
+            $maxLine = 10;
+            $firstLine = max(0, $seekline - $maxLine);
+            
+            $spl->seek($firstLine);
+            $min = $seekline - $maxLine;
+            $max = $seekline + $maxLine + 1;
+            
+            for ($i = $min; $i < $max; $i++) {
+                $currentLine = $i + ($min < 0 ? abs($min) : 0) + 1;
+                $color = $currentLine == $line ? ' color="red"' : '';
+                if ($spl->eof()) break;
+                if (IS_CLI) {
+                    $code[] = $currentLine . ($color != '' ? ' 行发生错误' : '') . rtrim($spl->current());
+                } else {
+                    $code[] = '<font ' . $color . '>' . $currentLine . ':</font>' . "\t" . '<font ' . $color . '>' . htmlspecialchars(rtrim($spl->current())) . '</font>';
+                }
+                $spl->next();
+            }
+        } else {
+            $spl->seek($seekline);
+            if (IS_CLI) {
+                $code[] = rtrim($spl->current());
+            } else {
+                $code[] = htmlspecialchars(rtrim($spl->current()));
+            }
+            
+        }
+        return IS_CLI ? PHP_EOL . join(PHP_EOL, $code) : '<pre style="font-weight:bold;">' . join("<br />", $code) . '</pre>';
+    }
+    
+    /**
+     * sprintf 格式化语言错误信息内容
+     *
+     *
+     * Qii::e($message, $argv1, $argv2, ..., $line);
+     * $message = sprintf($message, $argv1, $argv2, ...);
+     * throw new \Qii_Exceptions_Errors($message, $line);
+     */
+    public static function e()
+    {
+        $argvs = func_get_args();
+        $count = count($argvs);
+        $message = array_shift($argvs);
+        $line = (int)array_pop($argvs);
+        if ($count == 2) {
+            throw new \Qii\Exceptions\Errors($message, $line);
+        }
+        $message = vsprintf($message, $argvs);
+        throw new \Qii\Exceptions\Errors($message, $line);
+    }
+}

+ 17 - 0
src/Exceptions/FileNotFound.php

@@ -0,0 +1,17 @@
+<?php
+namespace Qii\Exceptions;
+
+class FileNotFound extends Errors
+{
+	const VERSION = '1.2';
+
+	/**
+	 * 显示错误
+	 *
+	 * @param Object $e Exception
+	 */
+	public static function getError($e)
+	{
+		
+	}
+}

+ 17 - 0
src/Exceptions/FolderDoesNotExist.php

@@ -0,0 +1,17 @@
+<?php
+namespace Qii\Exceptions;
+
+class FolderDoesNotExist extends Errors
+{
+    const VERSION = '1.2';
+
+    /**
+     * 显示错误
+     *
+     * @param Object $e Exception
+     */
+    public static function getError($e)
+    {
+
+    }
+}

+ 17 - 0
src/Exceptions/InvalidFormat.php

@@ -0,0 +1,17 @@
+<?php
+namespace Qii\Exceptions;
+
+class InvalidFormat extends Errors
+{
+	const VERSION = '1.2';
+
+	/**
+	 * 显示错误
+	 *
+	 * @param Object $e Exception
+	 */
+	public static function getError($e)
+	{
+
+	}
+}

+ 16 - 0
src/Exceptions/MethodNotFound.php

@@ -0,0 +1,16 @@
+<?php
+namespace Qii\Exceptions;
+
+class MethodNotFound extends Errors
+{
+	const VERSION = '1.2';
+
+	/**
+	 * 显示错误
+	 *
+	 * @param Object $e Exception
+	 */
+	public static function getError($e)
+	{
+	}
+}

+ 17 - 0
src/Exceptions/NotAllowed.php

@@ -0,0 +1,17 @@
+<?php
+namespace Qii\Exceptions;
+
+class NotAllowed extends Errors
+{
+	const VERSION = '1.2';
+
+	/**
+	 * 显示错误
+	 *
+	 * @param Object $e Exception
+	 */
+	public static function getError($e)
+	{
+
+	}
+}

+ 17 - 0
src/Exceptions/Overwrite.php

@@ -0,0 +1,17 @@
+<?php
+namespace Qii\Exceptions;
+
+class Overwrite extends Errors
+{
+	const VERSION = '1.2';
+
+	/**
+	 * 显示错误
+	 *
+	 * @param Object $e Exception
+	 */
+	public static function getError($e)
+	{
+
+	}
+}

+ 17 - 0
src/Exceptions/Response.php

@@ -0,0 +1,17 @@
+<?php
+namespace Qii\Exceptions;
+
+class Response extends Errors
+{
+	const VERSION = '1.2';
+
+	/**
+	 * 显示错误
+	 *
+	 * @param Object $e Exception
+	 */
+	public static function getError($e)
+	{
+
+	}
+}

+ 17 - 0
src/Exceptions/TableException.php

@@ -0,0 +1,17 @@
+<?php
+namespace Qii\Exceptions;
+
+class TableException extends Errors
+{
+	const VERSION = '1.2';
+
+	/**
+	 * 显示错误
+	 *
+	 * @param Object $e Exception
+	 */
+	public static function getError($e)
+	{
+
+	}
+}

+ 17 - 0
src/Exceptions/Unsupport.php

@@ -0,0 +1,17 @@
+<?php
+namespace Qii\Exceptions;
+
+class Unsupport extends Errors
+{
+	const VERSION = '1.2';
+
+	/**
+	 * 显示错误
+	 *
+	 * @param Object $e Exception
+	 */
+	public static function getError($e)
+	{
+
+	}
+}

+ 17 - 0
src/Exceptions/Variable.php

@@ -0,0 +1,17 @@
+<?php
+namespace Qii\Exceptions;
+
+class Variable extends Errors
+{
+	const VERSION = '1.2';
+
+	/**
+	 * 显示错误
+	 *
+	 * @param Object $e Exception
+	 */
+	public static function getError($e)
+	{
+
+	}
+}

+ 1 - 0
src/Exceptions/View/404.php

@@ -0,0 +1 @@
+404 Not found.

+ 31 - 0
src/Exceptions/View/error.php

@@ -0,0 +1,31 @@
+<html>
+<head>
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <meta http-equiv="content-type" content="text/html;charset=utf-8">
+    <title><?php echo Qii::i('Throw Exception'); ?></title>
+    <?php
+    include(dirname(__FILE__) . DS . 'style.php');
+    ?>
+</head>
+<body>
+
+<h1><font color="red"><?php echo Qii::i('Throw Exception'); ?></font></h1>
+
+<p style="text-align:right;"><?php echo Qii::i('The page you are looking at is being generated dynamically by Qii'); ?></p>
+
+<ul><?php echo Qii::i('Error information'); ?>
+    <?php
+    //$messageArray = Qii::getPrivate('error');
+    foreach ($message AS $error) {
+        ?>
+        <li><code><?= $error; ?></code></li>
+        <?php
+    }
+    ?>
+</ul>
+<?php
+include(dirname(__FILE__) . DS . 'footer.php');
+?>
+
+</body>
+</html>

+ 372 - 0
src/Exceptions/View/example.php

@@ -0,0 +1,372 @@
+<html xmlns="http://www.w3.org/1999/html">
+<head>
+    <title>使用方法 - Qii Framework</title>
+    <meta name="keywords" content="Qii框架,PHP开发框架">
+	<meta name="description" content="Qii 框架,以最简单的方式创建你的Web应用。" />
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <meta http-equiv="content-type" content="text/html;charset=utf-8">
+    <?php include(Qii_DIR . '/view/style.php'); ?>
+</head>
+<body>
+<h1>使用方法:</h1>
+<ul>
+    <li><a href="#createProject">创建项目</a></li>
+    <li><a href="#env">设置环境</a></li>
+    <li><a href="#useFramework">框架的使用</a>
+        <ul>
+            <li><a href="#useCommandLine">1)命令行运行程序</a></li>
+            <li><a href="#autoload">2) 自动加载类</a></li>
+            <li><a href="#basicMethod">3) Qii 基本功能:</a></li>
+            <li><a href="#supportMultDomain"> 4) 多域名支持:</a></li>
+            <li><a href="#modle"> 5) 数据库使用示例:</a></li>
+            <li><a href="#view"> 6) View的支持</a></li>
+            <li><a href="#controller"> 7) Controller的使用</a></li>
+            <li><a href="#cache"> 8) Cache支持</a></li>
+        </ul>
+    </li>
+    <li><a href="#download">下载</a></li>
+</ul>
+<pre>
+<a id="createProject"> 1、创建项目</a>
+<code>
+    通过命令行进入当前目录,并执行:php -q _cli.php create=yes workspace=../project cache=tmp useDB=1
+    Command line usage:
+    >php -q _cli.php create=yes workspace=../project cache=tmp useDB=1
+    * create: is auto create default:yes;
+    * workspace: workspace
+    * cache : cache dir
+    * useDB : use db or not
+    程序将自动创建工作目录,并生成首页及配置相关文件。设置好Web网站目录,开启.htaccess即可直接访问。
+    相关的配置文件见 configure/app.ini及configure/db.ini文件
+</code>
+<a id="env">2、设置环境</a>
+<code>
+    1、安装服务器软件(详情google或百度)
+    2、设置服务器的Rewrite规则:
+    Apache:
+    RewriteEngine On
+    RewriteCond %{REQUEST_FILENAME} !-d
+    RewriteCond %{REQUEST_FILENAME} !-f
+    RewriteCond $1 !^(index.php|images|robots.txt)
+    RewriteRule (.*)$ index.php/$1 [L,QSA,PT]
+    RewriteRule ^(.*)\.phar$ - [F]
+    RewriteRule ^(.*)\.ini$ - [F]
+    Nginx:
+    if (!-f $request_filename){
+    rewrite (.*) /index.php;
+    }
+    location ~* /.(ini|phar){
+    deny all;
+    }
+</code>
+<a id="useFramework">3、框架的使用</a>
+<code>
+    <a id="useCommandLine"> 1) 命令行运行程序</a>
+    <code>
+        仅支持GET方法
+        1. normal模式
+        php -q index.php controller=index/action=home/id=100
+        php -q index.php controller=plugins/action=index/page=2
+
+        2. middle模式
+        php -q index.php controller/index/action/home/id/100
+        php -q index.php controller/plugins/action/index/page/2
+
+        3. short模式
+        php -q index.php index/home/100
+        php -q index.php plugins/page/2.html
+    </code>
+    <a id="autoload">2) 自动加载类</a>
+    <code>
+        new \controller\user(); === require("controller<?= DS ?>user.php"); new \controller\user();
+        new \model\user(); === require("model<?= DS ?>user.php"); new \model\user();
+        new \model\test\user(); === require("model<?= DS ?>test<?= DS ?>user.php"); new \model\test\user();
+    </code>
+    <a id="basicMethod">3) Qii 基本功能:</a>
+    <code>
+        Qii\Instance::Instance(className, param1, param2, param3[,...]); === ($class = new className(param1, param2,
+        param3[,...]));
+        Qii\Load::load(className)->method(); === $class->method();
+        Qii\Import::requires(fileName); 如果指定的文件无法找到则会在get_include_path()和站点配置文件的[path]的目录中去搜索,直到搜索到一个则停止。
+        Qii\Import::includes(fileName); == include(fileName);
+        Qii::setPrivate($key, $value);保存到私有变量 $_global[$key]中, $value可以为数组,如果是数组的话会将数组合并到已经存在$key的数组中去。
+        Qii::getPrivate($key, $index);获取$_global[$key][$index]的值
+        Qii::setError($condition, $code, $argv1, $argv2, ...);
+        检查$condition是否成立,成立就没有错,返回false,否则有错,返回true并将错误信息,详细代码错误$code详情见<?php echo Qii::getPrivate('qii_sys_language'); ?>
+        。
+    </code>
+    <a id="supportMultDomain"> 4) 多域名支持:</a>
+    <code>
+        开启多域名支持,不同域名可以访问不同目录中的controller,在app.ini中的common下添加以下内容,注意:hosts中的内容会覆盖网站中对应的配置
+        hosts[0.domain] = test.xxx.wang
+        hosts[0.ext] = test
+
+        hosts[1.domain] = admin.xxx.wang
+        hosts[1.ext] = admin
+    </code>
+    <a id="modle"> 5) 数据库使用示例:</a>
+    <code>
+        1、创建配置文件:configure/istudy.istudy_user.config.php,你可以设置好数据库配置文件或者在这里<a
+                href="<?= $this->_request->getFullUrl('main') . '?url=' . urlencode($this->_request->getFullUrl('database/creator')); ?>"><b>生成</b></a>配置文件并下载保存到本地目录。
+        <?php highlight_file('configure/istudy.istudy_user.config.php') ?>
+        namespace Model;
+        use \Qii\Model;
+
+        class user extends Model
+        {
+        public function __construct()
+        {
+        parent::__construct();
+        }
+
+        public function saveUserInfo()
+        {
+        $user = (new \Qii\Driver\Easy())->_initialize();
+        //设置数据表的主键,保存的时候会自动验证指定数据是否已经存在
+        $user->setPrivateKey(array('email'));
+        //设置规则
+        $user->setRules(new \Qii\Driver\Rules(\Qii\Import::includes('configure/istudy.istudy_user.config.php')));
+        $user->nickname = 'antsnet';
+        $user->sex = 1;
+        $user->email = 'antsnet4@163.com';
+        $user->password = 'A123456a';
+        $user->add_time = time();
+        $user->update_time = time();
+        $user->status = 0;
+        //此处返回response对象
+        $response = $user->_save();
+
+        if($response->isError())
+        {
+        return $response->getErrors();
+        }
+        return array('uid' => $response->getResult('_result'));
+        }
+
+        /**
+        * 获取用户信息
+        */
+        public function userInfo($email)
+        {
+        return $this->db->getRow('SELECT * FROM istudy_user WHERE email = "'.$this->db->setQuote($email).'"');
+        //或者
+        return $this->db->where(array('email' => $email))->selectOne('istudy_user');
+        //或者使用以下代码
+        $user = (new \Qii\Driver\Easy('istudy_user'))->_initialize();
+        //设置规则
+        $user->setRules(new \Qii\Driver\Rules(\Qii\Import::includes('configure/istudy.istudy_user.config.php')));
+        //操作的时候会根据规则自动验证对应字段是否符合指定规则,不符合就不做查询操作
+        $response = $user->_getRowByEmail('antsnet@163.com');
+        if($response->isError())
+        {
+        return array('users' => array(), 'error' => $user->getErrors());
+        }
+        return array('users' => $response->getResult('_result'));
+        }
+        /**
+        * 登录
+        */
+        public function login($email, $password)
+        {
+        $user = (new \Qii\Driver\Easy('istudy_user'))->_initialize();
+        //设置规则
+        $user->setRules(new \Qii\Driver\Rules(\Qii\Import::includes('configure/istudy.istudy_user.config.php')));
+        $user->setPrivateKey(array('email'));
+        $user->email = $email;
+        $response = $user->_exist();
+        if($response->isError())
+        {
+        return array('login' => false, 'error' => $response->getErrors());
+        }
+        $data = $response->getResult();
+        if($data['password'] != md5($password))
+        {
+        return array('login' => false, 'error' => 'invalid password');
+        }
+        return array('login' => true, 'res' => $data);
+        }
+
+        public function update($email, $password)
+        {
+        $user = (new \Qii\Driver\Easy('istudy_user'))->_initialize();
+        //设置规则
+        $user->setRules(new \Qii\Driver\Rules(\Qii\Import::includes('configure/istudy.istudy_user.config.php')));
+        $user->setPrivateKey(array('email'));
+        $user->email = $email;
+        $user->func('md5', 'password', $password);
+
+        $response = $user->_update();
+        if($response->isError())
+        {
+        return array('update' => false, 'msg' => $response->getErrors());
+        }
+        return array('update' => true);
+        }
+        public function remove($email)
+        {
+        $user = (new \Qii\Driver\Easy('istudy_user'))->_initialize();
+        //设置规则
+        $user->setRules(new \Qii\Driver\Rules(\Qii\Import::includes('configure/istudy.istudy_user.config.php')));
+        $user->setPrivateKey(array('email'));
+        $user->email = $email;
+
+        $response = $user->_remove();
+        if($response->isError())
+        {
+        return array('remove' => false, 'msg' => $response->getErrors());
+        }
+        return array('remove' => true);
+        }
+        }
+        使用方法:
+        namespace controller;
+        use \Qii\Controller_Abstract;
+        class test extends Controller_Abstract
+        {
+        //启用模板引擎支持
+        protected $enableView = true;
+        //启用数据库支持
+        protected $enableModel = true;
+        //修改默认模板引擎
+        protected $viewType = array('engine' => 'include');
+        public function __construct()
+        {
+        parent::__construct();
+        }
+
+        public function indexAction()
+        {
+        $this->_model->getRow('SELECT * FROM user WHERE email = "antsnet@163.com"');
+        print_r($this->_load->model('user')->saveUserInfo());
+        print_r($this->_load->model('user')->userInfo('antsnet@163.com'));
+        print_r($this->_load->model('user')->login('antsnet@163.com', 'A123456a'));
+        print_r($this->_load->model('user')->update('antsnet@163.com', 'A123456a'));
+        print_r($this->_load->model('user')->remove('antsnet@163.com'));
+        }
+        }
+    </code>
+    <a id="view"> 6) View的支持</a>
+    <code>
+        view支持smarty及php
+        use \Qii\Controller_Abstract;
+        class index extends Controller_Abstract
+        {
+        //启用view,在调用parent::__construct()时自动初始化view
+        protected $enableView = true;
+        //启用database在调用parent::__construct()时自动初始化database
+        protected $enableModel = true;
+        //修改默认模板引擎
+        protected $viewType = array('engine' => 'include');
+        public function __construct()
+        {
+        parent::__construct();//默认是app.ini中的view[engine],你可以在此处选择是用include或者require,只要将参数默认传给enableView即可
+        $this->_view->display('tpl'); //$this->view即为使用的模板引擎
+        }
+        }
+    </code>
+    <a id="controller"> 7) Controller的使用</a>
+    <code>
+        namespace controller;
+        use \Qii\Controller_Abstract;
+        class test extends Controller_Abstract
+        {
+        //为了避免Controller逻辑过于复杂,可以将Action拆到单独的文件
+        //当在调用dummy方法的时候会自动执行actions\dummy中的execute方法
+        public $actions = array(
+        "dummy" => "actions\dummy",
+        );
+        public function __construct()
+        {
+        parent::__construct();
+        }
+        public function indexAction()
+        {
+        //转发到\controller\test的indexAction方法上
+        $this->dispatch('test', 'index');
+        //执行结束后再转发
+        $this->forward('test', 'index');
+        }
+        //在执行parent::__construct()的时候会自动调用,如果return false即直接退出程序
+        protected function beforeRun()
+        {
+        return true;
+        }
+        /**
+        * 执行完dispatch后调用
+        */
+        protected function afterRun()
+        {
+        }
+        }
+    </code>
+    <a id="cache"> 8) Cache支持</a>
+    <code>
+        Controller中使用Cache
+        namespace controller;
+        use \Qii\Controller_Abstract;
+        class cache extends Controller_Abstract
+        {
+        public function __construct()
+        {
+        parent::__construct();
+        }
+        public function cacheTest()
+        {
+        $cache_id = 'cache_test';
+        //文件缓存
+        $this->setCache('file', array('path' => 'tmp'));
+        //Memcache缓存
+        $this->setCache('memcache', array('servers' => array( array('host'=>'127.0.0.1','port'=>11211) )
+        ,'life_time'=>600));
+        //xcache
+        $this->setCache('xcache', array('life_time'=>600));
+        //缓存内容
+        $this->_cache->set($cache_id, 'cache内容');
+        //redis缓存
+        $this->setCache('redis', array('servers' => array('127.0.0.1.6379')));
+        $this->_cache->set($cache_id, array('cache' => 'cache 内容'));
+        //获取缓存内容
+        $this->_cache->get($cache_id);
+        //移除缓存
+        $this->_cache->remove($cache_id);
+        }
+        }
+
+        Model中使用
+        namespace Model;
+        use \Qii\Model;
+        class cache extends Model
+        {
+        public function __construct()
+        {
+        parent::__construct();
+        }
+        public function cacheTest()
+        {
+        $cache_id = 'cache_test';
+        //文件缓存
+        $this->setCache('file', array('path' => 'tmp'));
+        //Memcache缓存
+        $this->setCache('memcache', array('servers' => array( array('host'=>'127.0.0.1','port'=>11211) )
+        ,'life_time'=>600));
+        //xcache
+        $this->setCache('xcache', array('life_time'=>600));
+        //缓存内容
+        $this->_cache->set($cache_id, 'cache内容');
+        //获取缓存内容
+        $this->_cache->get($cache_id);
+        //移除缓存
+        $this->cache->remove($cache_id);
+        }
+        }
+    </code>
+</code>
+<a id="download">下载</a>
+<code>
+    测试阶段暂时不提供下载
+
+</code>
+</pre>
+<?php include(Qii_DIR . '/view/footer.php'); ?>
+</body>
+</html>

+ 0 - 0
src/Exceptions/View/footer.php


+ 30 - 0
src/Exceptions/View/message.php

@@ -0,0 +1,30 @@
+<html>
+<head>
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <meta http-equiv="content-type" content="text/html;charset=utf-8">
+    <title><?php echo Qii::i('Show Message'); ?></title>
+    <?php
+    include(dirname(__FILE__) . DS . 'style.php');
+    ?>
+</head>
+<body>
+
+<h1><font color="red"><?php echo Qii::i('Show Message'); ?></font></h1>
+
+<p><?php echo Qii::i('The page you are looking at is being generated dynamically by Qii'); ?></p>
+
+<ul>Messages:
+    <?php
+    foreach ($message AS $error) {
+        ?>
+        <li><code><?= $error; ?></code></li>
+        <?php
+    }
+    ?>
+</ul>
+<?php
+include(dirname(__FILE__) . DS . 'footer.php');
+?>
+
+</body>
+</html>

+ 67 - 0
src/Exceptions/View/style.php

@@ -0,0 +1,67 @@
+<style type="text/css">
+
+    body {
+        background-color: #fff;
+        margin: 40px;
+        font-family: Lucida Grande, Verdana, Sans-serif;
+        font-size: 14px;
+        color: #4F5155;
+    }
+
+    a {
+        color: #003399;
+        background-color: transparent;
+        font-weight: normal;
+    }
+
+    h1 {
+        color: #444;
+        background-color: transparent;
+        border-bottom: 1px solid #D0D0D0;
+        font-size: 16px;
+        font-weight: bold;
+        margin: 24px 0 2px 0;
+        padding: 5px 0 6px 0;
+    }
+
+    code {
+        width: 98%;
+        font-family: Monaco, Verdana, Sans-serif;
+        word-wrap: break-word;
+        word-break: break-all;
+        font-size: 12px;
+        color: #002166;
+        display: block;
+        line-height: 20px;
+        padding: 10px 20px;
+        margin: 15px 0;
+        border: 1px solid rgba(0, 0, 0, 0.2);
+        border-radius: 5px;
+        background-color: #eee;
+        position: relative;
+    }
+
+    pre {
+        font-size: 14px;
+        white-space: pre-wrap; /* css3.0 */
+        white-space: -moz-pre-wrap; /* Firefox */
+        white-space: -pre-wrap; /* Opera 4-6 */
+        white-space: -o-pre-wrap; /* Opera 7 */
+        word-wrap: break-word; /* Internet Explorer 5.5+ */
+    }
+
+    ul {
+        list-style: lower-roman;
+        line-height: 18px;
+        padding: 0px 0px;
+        margin: 0px 0px;
+    }
+
+    ul ul {
+        padding-left: 20px;
+    }
+
+    li {
+        line-height: 25px;
+    }
+</style>

+ 122 - 0
src/Functions/Funcs.php

@@ -0,0 +1,122 @@
+<?php
+/**
+ * Qii ...
+ * @return null|Qii|Qii\Autoloader\Psr4
+ */
+function _Qii()
+{
+	return \Qii::getInstance();
+}
+
+/**
+ * \Qii::i(.., ...)
+ * @return mixed
+ */
+function _i()
+{
+    return call_user_func_array('\Qii::i', func_get_args());
+}
+/**
+ * throw new Exception
+ */
+function _e()
+{
+    return call_user_func_array('\Qii::e', func_get_args());
+}
+/**
+ * 加载语言包
+ * @param string $language 语言包
+ */
+function _language($language)
+{
+	\Qii\Language\Loader::getInstance()->load($language);
+}
+
+/**
+ * \Qii_Config_Register:: get or set
+ * @param $key
+ * @param null $val
+ * @return Mix|void
+ */
+function _config($key, $val = null)
+{
+	if($val === null)
+	{
+		return \Qii_Config_Register::get($key);
+	}
+	return \Qii_Config_Register::set($key, $val);
+}
+/**
+ * Adds a base directory for a namespace prefix.
+ *
+ * @param string $prefix The namespace prefix.
+ * @param string $baseDir A base directory for class files in the
+ * namespace.
+ * @param bool $prepend If true, prepend the base directory to the stack
+ * instead of appending it; this causes it to be searched first rather
+ * than last.
+ * @return void
+ */
+function _addNamespace($prefix, $baseDir, $prepend = false)
+{
+	_qii()->addNamespace($prefix, $baseDir, $prepend);
+}
+/**
+ * 加载loader 可以直接加载指定类
+ */
+function _loader($class = null)
+{
+	$args = func_get_args();
+	if($class != null){
+		return call_user_func_array(array(\Qii\Autoloader\Psr4::getInstance(), 'loadClass'), $args);
+	}
+	return \Qii\Autoloader\Psr4::getInstance();
+}
+/**
+ * 简便的loadClass方法
+ * Qii\Autoloader\Psr4::getInstance()->loadClass(.., ..);
+ */
+function _loadClass()
+{
+	$args = func_get_args();
+	return call_user_func_array(array(\_loader(), 'loadClass'), $args);
+}
+
+/**
+ * 根据文件前缀获取文件路径
+ *
+ * @param string $file 文件名
+ */
+function _getFileByPrefix($file)
+{
+	return \_loader()->getFileByPrefix($file);
+}
+
+/**
+ * 数据库操作类
+ *
+ * @param Qii_Driver_Rules $rule 规则
+ * @param array|null $privateKey 主键
+ * @param array|null $fieldsVal 值
+ * @return mixed
+ */
+function _DBDriver(\Qii_Driver_Rules $rule, $privateKey = null, $fieldsVal = null)
+{
+    $rules = _loadClass('Qii\Driver\Easy')->_initialize();
+    if ($privateKey) $rules->setPrivateKey($privateKey);
+    $rules->setRules($rule);
+    if ($fieldsVal) $rules->setFieldsVal($fieldsVal);
+    return $rules;
+}
+/**
+ * _include include文件
+ */
+function _include($files){
+	return \Qii\Autoloader\Import::includes($files);
+}
+
+function _require($files)
+{
+	return \Qii\Autoloader\Import::requires($files);
+}
+

+ 136 - 0
src/Language/Loader.php

@@ -0,0 +1,136 @@
+<?php
+/**
+ * 加载语言包
+ * @Author Jinhui Zhu
+ * @version 1.2
+ *
+ * Usage:
+ *    Qii::Qii_Language_Loader('load', 'error', Qii_DIR); 加载系统目录中的语言
+ *    Qii::Qii_Language_Loader('load', 'error'); 加载程序目录中的语言
+ * OR
+ *    Qii::Qii_Language_Loader()->load('error', Qii_DIR); 加载系统目录中的语言
+ *    Qii::Qii_Language_Loader()->load('error'); 加载程序目录中的语言
+ */
+namespace Qii\Language;
+
+use \Qii\Config\Register;
+use \Qii\Config\Consts;
+
+class Loader
+{
+	const VERSION = 1.3;
+	/**
+	 * @var $loaded ;
+	 */
+	private $loaded;
+
+	protected static $_instance;
+
+	public function __construct()
+	{
+		return $this;
+	}
+	/**
+	 * 单例模式
+	 */
+	public static function getInstance()
+	{
+
+	    return \Qii\Autoloader\Factory::getInstance('\Qii\Language\Loader');
+	}
+
+	/**
+	 * 加载系统语言包
+	 * @param  string $package 语言包名称
+	 * @param string $dir 语言包路径 默认为当前目录
+	 * @return  Array 语言包内容
+	 */
+	public function load($package, $dir = '')
+	{
+		if(!$dir)
+		{
+			$dir = \Qii::getInstance()->getWorkspace() . DS;
+		}
+		else if ($dir == Qii_DIR)
+		{
+			$dir = Qii_DIR . DS . 'Language' . DS;
+		}
+		else
+		{
+			$dir = $dir . DS;
+		}
+
+		//先获取语言配置信息
+		$language = \Qii\Autoloader\Import::includes($dir . 'i18n' . DS . 'language.php');
+		//如果是cli模式就使用英文
+		if(IS_CLI) $language = "EN";
+		$fileName = $dir . 'i18n' . DS . $language . DS . $package . '.php';
+		if (isset($this->loaded[$fileName])) return;
+		$this->loaded[$fileName] = true;
+		if (is_file($fileName)) {
+			return $this->merge($fileName);
+		}
+		throw new \Qii\Exceptions\FileNotFound(\Qii::i(1405, $fileName), __LINE__);
+	}
+
+	/**
+	 * 将语言包内容保存到系统Language中
+	 * @param string $fileName 文件名
+	 */
+	protected function merge($fileName)
+	{
+		$data = Register::get(Consts::APP_LANGUAGE_CONFIG);
+		if (!is_file($fileName)) throw new Exceptions(\Qii::i(1405, $fileName));
+		$merge = (array) \Qii\Autoloader\Import::includes($fileName);
+		
+		if ($data) $merge = $data + $merge;
+		Register::set(Consts::APP_LANGUAGE_CONFIG, $merge);
+	}
+
+	/**
+	 * 获取语言内容
+	 * @param Mix $code
+	 * @return String
+	 */
+	public function get($code)
+	{
+		$data = Register::get(Consts::APP_LANGUAGE_CONFIG, array());
+		if (isset($data) && isset($data[$code])) {
+			return $data[$code];
+		}
+		return $code;
+	}
+
+	/**
+	 * sprintf 格式化语言信息内容
+	 * Qii::i(key, '格式化语言信息内容');
+	 * @return String
+	 */
+	public function i()
+	{
+		$args = func_get_args();
+		$message = array_shift($args);
+		$message = $this->get($message);
+		$vmessage = vsprintf($message, $args);
+
+		if ($vmessage == $message && is_array($args) && count($args) > 0 && !(count($args) == 1 && $args[0] == '')) {
+			return ' ['. $message .'] ['.join("\t", $args) . ']';
+		}
+		return $vmessage;
+	}
+
+	/**
+	 * 获取语言内容,支持vsprintf
+	 *
+	 * @param String $words
+	 * @param String $code
+	 * @param argvs vsprintf的格式化参数
+	 * @return String
+	 */
+	public function gettext($code, $argvs = null)
+	{
+		if ($argvs == null) return $this->get($code);
+		return vsprintf($this->get($code), $argvs);
+	}
+
+}

+ 101 - 0
src/Language/i18n/CN/error.php

@@ -0,0 +1,101 @@
+<?php
+/**
+ *
+ * 400 Invalid syntax. 语法问题
+ * 401 Access denied. 访问拒绝
+ * 402 Payment required. 必须完整
+ * 403 Request forbidden. 请求被禁止
+ * 404 Object not found. 对象没有找到
+ * 405 Method is not allowed. 方法不允许
+ * 406 No response acceptable to client found. 客户端没有响应
+ * 407 Proxy authentication required. 代理需要验证
+ * 408 Server timed out waiting for request. 等等请求时服务器断开连接
+ * 409 User should resubmit with more info. 有冲突用户应该进行检查
+ * 410 Resource is no longer available. 资源不可用
+ * 411 Server refused to accept request without a length. 服务器拒绝接受没有长度的请求
+ * 412 Precondition given in request failed. 放弃请求失败的条件
+ * 413 Request entity was too large. 请求太大
+ * 414 Request Uniform Resource Identifier (URI) too long. 请求的URI 太长
+ * 415 Unsupported media type. 不支持MEDIA类型
+ * 449 Retry after doing the appropriate action. 在作了适当动作后重试
+ * 500 Internal server error. 服务器内部错误
+ * 501 Server does not support the functionality required to fulfill the request. 服务器不支持请求的功能
+ * 502 Error response received from gateway. 从网关收到错误应答
+ * 503 Temporarily overloaded. 过载
+ * 504 Timed out waiting for gateway. 等待网关时请求断开
+ * 505 HTTP version not supported. 不支持HTTP的版本
+ */
+return array(
+    //系统错误
+    -1 => '%s',
+    0 => '未知错误%d',
+    //网络相关
+    400 => '400 语法问题',
+    401 => '401 访问拒绝',
+    403 => '403 请求被禁止',
+    404 => '404 对象没有找到',
+    405 => '405 方法不允许',
+    406 => '406 客户端没有响应',
+    407 => '407 代理需要验证',
+    408 => '408 等等请求时服务器断开连接',
+    409 => '409 有冲突用户应该进行检查',
+    410 => '410 资源不可用',
+    412 => '412 放弃请求失败的条件',
+    413 => '413 请求太大',
+    414 => '414 请求的URI 太长',
+    415 => '415 不支持MEDIA类型',
+    449 => '449 在作了适当动作后重试',
+    500 => '500 服务器内部错误',
+    501 => '501 服务器不支持请求的功能',
+    502 => '502 从网关收到错误应答',
+    503 => '503 过载',
+    504 => '504 等待网关时请求断开',
+    505 => '505 不支持HTTP的版本',
+    //系统错误
+    1000 => '文件 <font color="red">%s</font>格式不正确',
+    1001 => '错误页面<font color="red">%s</font>不存在',
+    1002 => '安全校验失败',
+    1003 => '参数未定义',
+    1004 => 'memcached扩展没有加载',
+    1005 => '连接服务器[%s:%s] 失败',
+    1006 => 'redis扩展没有加载',
+    1007 => '未定义缓存策略',
+    1008 => '%s扩展没有加载',
+    1009 => '文件夹%s不存在',
+    //类相关
+    1100 => '%s参数太多,此方法只接受<font color="red">%d</font>个参数,传递了<font color="red">%d</font>个。',
+    1101 => '方法<font color="red">%s</font>未定义',
+    1103 => '未找到类<font color="red">%s</font>',
+    1104 => '类名不能为空',
+    1105 => '类<font color="red">%s</font>未被实例化',
+    1106 => '调用不存在的方法:<font color="red">%s::%s</font> 参数:<font color="red">%s</font>"',
+    1107 => '%s必须扩展或继承%s类',
+    1108 => '请在你的项目下添加此控制器<font color="red">%s</font>, 并添加此方法<font color="red">%s</font>',
+    1109 => '请在你的项目下添加<font color="red">%s</font>类, 并添加此方法<font color="red">%s</font>',
+    //文件相关
+    1400 => '网站配置文件%s错误',
+    1401 => '目录%s不存在',
+    1402 => '未指定数据库配置文件',
+    1403 => '未配置数据库',
+    1404 => '在<font color="red">%s</font>目录下未找到文件<font color="red">%s</font>',
+    1405 => '文件<font color="red">%s</font>不存在',
+    1406 => '配置信息不能为空',
+    //model相关
+    1500 => '连接数据库失败, 数据库服务器 %s, 用户名 %s, 密码 %s, 数据库 %s, 错误信息:%s',
+    1501 => '数据库连接失败, %s',
+    1506 => '数据库方法%s不存在',
+    1507 => '请先调用%s方法',
+    1508 => '数据表必须包含字段 %s',
+    1509 => '执行SQL:<font color="red">%s</font>出错, 错误描述 <font color="red">%s</font>',
+    1510 => '未指定数据表名称',
+    1511 => '数据%s已存在',
+    1512 => '数据%s不存在',
+    1513 => '未设置主键',
+
+    5001 => '%s变量未定义',
+    5002 => '%s 操作不允许',
+    5003 => '%s不能为空',
+    5004 => '%s格式不正确',
+    5005 => '%s验证规则不存在',
+
+);

+ 17 - 0
src/Language/i18n/CN/exception.php

@@ -0,0 +1,17 @@
+<?php
+return array(
+	'Welcome to Qii!' => '欢迎使用Qii',
+	'Show Message' => '消息列表',
+	'Throw Exception' => '出错啦',
+	'The page you are looking at is being generated dynamically by Qii' => '<small style="color:#9c9ea1;">此页面由Qii自动生成</small>',
+	'Error information' => '错误信息',
+	'Error file' => '错误文件 :%s',
+	'Error code' => '错误代码 :%s',
+	'Error line' => '错误行 :%s',
+	'Error description' => '错误描述 :%s',
+	'Trace as below' => '错误追踪如下:',
+	'qii execute footer' => '<p style="text-align:center;color:#9c9ea1;"><small>服务器软件:%s , 页面执行 %s seconds, 内存使用量 %s. 作者: %s 邮箱地址 : %s</small></p>',
+	'Please set rules first' => '请先设置规则',
+	'Invalid %s format' => '%s 格式错误',
+	'Unsupport method' => '不支持%s方法',
+);

+ 15 - 0
src/Language/i18n/CN/resource.php

@@ -0,0 +1,15 @@
+<?php
+return array(
+    '10001' => '两个值不相同',
+    '100000' => '参数错误',
+    '100001' => '未设置资源文件',
+    '100002' => '未设置标题',
+    '100003' => '未设之资源',
+    '100004' => '参数指定错误',
+    '100005' => '文件移动失败',
+    '100006' => '文件夹不存在',
+    '100007' => '创建文件夹失败',
+    '100008' => '文件上传失败',
+    '100009' => '验证码错误',
+	'100010' => '错误的日志类型 %s ',
+);

+ 99 - 0
src/Language/i18n/EN/error.php

@@ -0,0 +1,99 @@
+<?php
+/**
+ *
+ * 400 Invalid syntax. 语法问题
+ * 401 Access denied. 访问拒绝
+ * 402 Payment required. 必须完整
+ * 403 Request forbidden. 请求被禁止
+ * 404 Object not found. 对象没有找到
+ * 405 Method is not allowed. 方法不允许
+ * 406 No response acceptable to client found. 客户端没有响应
+ * 407 Proxy authentication required. 代理需要验证
+ * 408 Server timed out waiting for request. 等等请求时服务器断开连接
+ * 409 User should resubmit with more info. 有冲突用户应该进行检查
+ * 410 Resource is no longer available. 资源不可用
+ * 411 Server refused to accept request without a length. 服务器拒绝接受没有长度的请求
+ * 412 Precondition given in request failed. 放弃请求失败的条件
+ * 413 Request entity was too large. 请求太大
+ * 414 Request Uniform Resource Identifier (URI) too long. 请求的URI 太长
+ * 415 Unsupported media type. 不支持MEDIA类型
+ * 449 Retry after doing the appropriate action. 在作了适当动作后重试
+ * 500 Internal server error. 服务器内部错误
+ * 501 Server does not support the functionality required to fulfill the request. 服务器不支持请求的功能
+ * 502 Error response received from gateway. 从网关收到错误应答
+ * 503 Temporarily overloaded. 过载
+ * 504 Timed out waiting for gateway. 等待网关时请求断开
+ * 505 HTTP version not supported. 不支持HTTP的版本
+ */
+return array(
+	-1 => '%s',
+	0 => 'Unknow error %d',
+	//network
+	400 => '400 Bad request.',
+	401 => '401 Access denied.',
+	403 => '403 Request forbidden.',
+	404 => '404 Not found.',
+	405 => '405 Method is not allowed.',
+	406 => '406 No response acceptable to client found.',
+	407 => '407 Proxy authentication required.',
+	408 => '408 Server timed out waiting for request.',
+	409 => '409 User should resubmit with more info.',
+	410 => '410 Resource is no longer available. ',
+	412 => '412 Precondition given in request failed.',
+	413 => '413 Request entity was too large.',
+	414 => '414 Request Uniform Resource Identifier (URI) too long.',
+	415 => '415 Unsupported media type.',
+	449 => '449 Retry after doing the appropriate action.',
+	500 => '500 Internal server error ',
+	501 => '501 Server does not support the functionality required to fulfill the request',
+	502 => '502 Error response received from gateway',
+	503 => '503 Temporarily overloaded.',
+	504 => '504 Timed out waiting for gateway.',
+	505 => '505 HTTP version not supported.',
+	//system relate
+	1000 => 'The file <font color="red">%s</font> format is wrong',
+	1001 => 'Error page <font color="red">%s</font> does not exist',
+	1002 => 'Security check failure',
+	1003 => 'Undefined variable',
+	1004 => 'The memcached extension must be loaded before use',
+	1005 => 'Connect memcached server [%s:%s] failed',
+	1006 => 'The redis extension must be loaded before use',
+	1007 => 'Undefined cache policy',
+	1008 => 'The %s extension must be loaded before use',
+    1009 => 'Folder "%s" does not exist',
+	//class relate
+	1100 => 'Two many argements in %s , this method need %d parameter, %d given',
+	1101 => 'Call undefined method <font color="red">%s</font>',
+	1102 => 'Class <font color="red">%s</font> does not exist',
+	1103 => 'Class name couldn\'t be NULL',
+	1104 => 'Class <font color="red">%s</font> didn\'t instance',
+	1105 => 'Call undefined method <font color="red">%s::%s</font>" with args "<font color="red">%s</font>"',
+	1107 => 'Class %s must be the extends/implements of %s',
+	1108 => 'Please write this controller<font color="red">%s</font> controller in your project, and add this method <font color="red">%s</font>',
+	1109 => 'Please write this <font color="red">%s</font> class in your project, and add this method <font color="red">%s</font>',
+	//file and others
+	1400 => 'Website configure file <font color="red">%s</font> error',
+	1401 => 'Dir %s does not exist',
+	1402 => 'No database configure file',
+	1403 => 'No database configure',
+	1404 => 'Directory <font color="red">%s</font> not included file <font color="red">%s</font>',
+	1405 => 'File %s not found',
+	1406 => 'Configure file is empty',
+	//model
+	1500 => 'Connect Database fail, host:%s, use user:%s, password:%s, database:%s error : %s',
+	1501 => 'Connect Database fail, %s',
+	1506 => 'Model\'s method %s does not exist',
+	1507 => 'Please call %s method first',
+	1508 => 'Table must have some fields',
+	1509 => 'Execute Query error with sql : <font color="red">%s</font>, Error description: <font color="red">%s</font>',
+	1510 => 'unknow table name',
+	1511 => '%s does exist',
+	1512 => '%s does not exist',
+	1513 => 'private key is not set',
+
+	5001 => '%s undefined',
+	5002 => '%s not allowed',
+	5003 => '%s is null',
+	5004 => '%s is invalid',
+	5005 => '%s regular does not exist'
+);

+ 17 - 0
src/Language/i18n/EN/exception.php

@@ -0,0 +1,17 @@
+<?php
+return array(
+	'Welcome to Qii!' => 'Welcome to Qii!',
+	'Show Message' => 'Show Message',
+	'Throw Exception' => 'Throw Exception',
+	'The page you are looking at is being generated dynamically by Qii' => '<small style="color:#9c9ea1;">The page you are looking at is being generated dynamically by Qii</small>',
+	'Error information' => 'Error information',
+	'Error file' => 'Error file :%s',
+	'Error code' => 'Error code:%s',
+	'Error line' => 'Error line :%s',
+	'Error description' => 'Error description :%s',
+	'Trace as below' => 'Trace as below :',
+	'qii execute footer' => '<p style="text-align:center;color:#9c9ea1;"><small>%s , Page rendered in %s seconds, Use memory %s. Author: %s Email : %s</small></p>',
+	'Please set rules first' => 'Please set rules first',
+	'Invalid %s format' => 'Invalid %s format',
+	'Unsupport method' => 'Unsupport %s method',
+);

+ 15 - 0
src/Language/i18n/EN/resource.php

@@ -0,0 +1,15 @@
+<?php
+return array(
+    '10001' => '两个值不相同',
+    '100000' => '参数错误',
+    '100001' => '未设置资源文件',
+    '100002' => '未设置标题',
+    '100003' => '未设之资源',
+    '100004' => '参数指定错误',
+    '100005' => '文件移动失败',
+    '100006' => '文件夹不存在',
+    '100007' => '创建文件夹失败',
+    '100008' => '文件上传失败',
+    '100009' => '验证码错误',
+	'100010' => '错误的日志类型 %s ',
+);

+ 2 - 0
src/Language/i18n/language.php

@@ -0,0 +1,2 @@
+<?php
+return 'CN';

+ 158 - 0
src/Library/Arrays.php

@@ -0,0 +1,158 @@
+<?php
+namespace Qii\Library;
+/**
+ * 实现PHP中数组功能
+ *
+ * 用法:
+ *
+ * $array = new \Qii\Library\Arrays();
+ *
+ * $data = array();
+ * $data['common']['one'] = '1';
+ * $data['common']['two'] = '1';
+ * $data['common']['three'] = '1';
+ *
+ * $array->getValueFromArray($data, '[common][three]');
+ *
+ * $array->setPrivate('string', 'string');
+ * $array->setPrivate('array[val][]', array(1, 2));
+ * $array->setPrivate('array[val][]', array(3, 4));
+ *
+ * $array->getPrivate('string');
+ *
+ * $array->getPrivate('array[val]');
+ *
+ * $array->getPrivate('array[val][0]');
+ *
+ * $array->getPrivate('array[val][1]');
+ *
+ *
+ */
+
+class Arrays
+{
+	const VERSION = '1.2';
+	protected $_private = array();
+
+	public function __construct()
+	{
+
+	}
+
+	public function __toString()
+	{
+		return self::VERSION;
+	}
+
+	/**
+	 * 直接从数组中获取指定key的值
+	 *
+	 * @param Array $data
+	 * @param String $key
+	 * @return Mix
+	 */
+	public function getValueFromArray($data, $key)
+	{
+		if (preg_match('/^\s*$/', $key)) {
+			return $data;
+		}
+		preg_match_all("/(.*?)\[(.*?)\]/", $key, $match);
+
+		$name = $match[1][0];
+		$keys = $match[2];
+
+		if ($name == '') {
+			return isset($data[$key]) ? $data[$key] : '';
+		}
+		if (!isset($data[$name])) {
+			return '';
+		}
+		$value = $data[$name];
+		foreach ($keys AS $key) {
+			if ($key == '') {
+				$value = $value;
+			} else {
+				$value = $value[$key];
+			}
+		}
+		return $value;
+	}
+
+	/**
+	 * 实现PHP数组赋值
+	 *
+	 * @param String $key
+	 * @param Mix $value
+	 * @return Array
+	 */
+	public function setPrivate($key, $value)
+	{
+		preg_match_all("/(.*?)\[(.*?)\]/", $key, $match);
+		$name = '';
+		if (isset($match[1]) && isset($match[1][0])) {
+			$name = $match[1][0];
+		}
+		$keys = $match[2];
+		if ($name == '') {
+			$name = $key;
+		}
+		if (empty($keys)) {
+			$this->_private[$key] = $value;
+			return $this->_private;
+		}
+		$private = array();
+		$private = array_merge($private, $keys);
+		$privates = null;
+		if (is_array($value) || is_object($value)) {
+			$array = str_replace('[\'\']', '[]', '$privates[\'' . join("']['", $private) . '\']=$value;');
+		} else {
+			$array = str_replace('[\'\']', '[]', '$privates[\'' . join("']['", $private) . '\']=\'' . $value . '\';');
+		}
+		eval($array);
+		if (isset($this->_private[$name])) {
+			if (!is_array($this->_private[$name])) {
+				unset($this->_private[$name]);
+				$this->_private[$name] = $privates;
+			} else {
+				$this->_private[$name] = array_merge_recursive($this->_private[$name], $privates);
+			}
+		} else {
+			$this->_private[$name] = $privates;
+		}
+		return $this->_private;
+	}
+
+	/**
+	 * 获取通过setPrivate key对应的值
+	 *
+	 * @param String $key
+	 * @return Mix
+	 */
+	public function getPrivate($key)
+	{
+		if (preg_match('/^\s*$/', $key)) {
+			return $this->_private;
+		}
+		preg_match_all("/(.*?)\[(.*?)\]/", $key, $match);
+		$name = '';
+		if (isset($match[1]) && isset($match[1][0])) {
+			$name = $match[1][0];
+		}
+		$keys = $match[2];
+		if ($name == '') {
+			return isset($this->_private[$key]) ? $this->_private[$key] : '';
+		}
+		if (!isset($this->_private[$name])) {
+			return '';
+		}
+		$value = $this->_private[$name];
+		foreach ($keys AS $key) {
+			if ($key == '') {
+				$value = $value;
+			} else {
+				$value = $value[$key];
+			}
+		}
+		return $value;
+	}
+}

+ 61 - 0
src/Library/Cookie.php

@@ -0,0 +1,61 @@
+<?php
+namespace Qii\Library;
+/**
+ * 设置Cookie,cookie内容将会被加密
+ */
+class Cookie
+{
+	const VERSION = '1.2';
+	/**
+	 * @var string $prefix cookie保存的前缀
+	 */
+	private $prefix = '_';
+	/**
+	 * cookie的过期时间
+	 */
+	private $expire = 86400;
+
+	private $securityKey = 'qii.v.1.3';
+
+	public function __construct()
+	{
+		return $this;
+	}
+
+	/**
+	 * 设置用于加密解码的密匙
+	 * @param $key
+	 */
+	public function setSecurityKey($key)
+	{
+		$this->securityKey = $key;
+		return $this;
+	}
+
+	/**
+	 * 设置cookie
+	 * @param string $name cookie名
+	 * @param string $val cookie值
+	 * @param int $expire 过期时间,默认为一天
+	 */
+	public function set($name, $val, $expire = 0)
+	{
+		if ($expire <= 0) $expire = $this->expire;
+		$crypt = new \Qii\Library\Crypt();
+		$crypt->setSecurityKey($this->securityKey);
+		$val = trim($crypt->encrypt(urlencode($val)));
+		setcookie($this->prefix . $name, $val, time() + $expire, '/');
+	}
+
+	/**
+	 * 获取cookie
+	 */
+	public function get($name)
+	{
+		$val = isset($_COOKIE[$this->prefix . $name]) ? $_COOKIE[$this->prefix . $name] : '';
+		if (!$val) return '';
+		$crypt = new \Qii\Library\Crypt();
+		$crypt->setSecurityKey($this->securityKey);
+		return trim(urldecode($crypt->decrypt($val)));
+	}
+}

+ 131 - 0
src/Library/Crypt.php

@@ -0,0 +1,131 @@
+<?php
+namespace Qii\Library;
+/**
+ * \Qii\Library\Crypt
+ * @author Jinhui Zhu<zhujinhui@zhangyue.com>2015-11-09 16:07
+ *
+ * 加密类
+ * 用法:
+ * $crypt = new \Qii\Library\Crypt();
+ * 设置密钥
+ * $crypt->setSecurityKey('密钥');
+ * 加密字符串
+ * echo $crypt->encrypt('加密字符串');
+ * 解密字符串
+ * echo $crypt->decrypt('解密字符串');
+ */
+class Crypt
+{
+	const VERSION = '1.2';
+	//密匙
+	private $securityKey = 'qii.v.1.3';
+	private $keyLength = 4;
+	private $iv = 'sdEKw2wJCnctEG09';
+
+	public function __construct()
+	{
+		if (!function_exists('openssl_encrypt')) {
+			throw new \Exception(\Qii::i(1008, 'openssl_encrypt'), __LINE__);
+		}
+		$this->setSecurityKey($this->securityKey);
+		return $this;
+	}
+
+	/**
+	 * 设置用于加密解码的密匙
+	 * @param $key
+	 */
+	public function setSecurityKey($key)
+	{
+		if(!$key) return;
+		$len = strlen($key);
+		if ($len < 16) $key = str_pad($key, 16, '.');
+		if($len > 16) $key = substr($key, 0, 16);
+		$this->securityKey = $key;
+		return $this;
+	}
+
+	/**
+	 * 设置iv字符串
+	 */
+	public function setIv($iv)
+	{
+		if(strlen($iv) > 16) $iv = substr($iv, 0, 16);
+		if(strlen($iv) < 16) $iv = str_pad($iv, 16, '.');
+		$this->iv = $iv;
+	}
+	/**
+	 * 获取iv字符串
+	 */
+	public function getIv()
+	{
+		$this->iv = $this->iv;
+		if(!$this->iv < 16) $this->iv = str_pad($this->iv, 16, '.');
+		if(strlen($this->iv) == 16) return $this->iv;
+		return substr($this->iv, 0, 16);
+	}
+
+
+	/**
+	 * 加密字符
+	 * @param $string
+	 * @return string
+	 */
+	public function encrypt($string)
+	{
+		$string = time() . $string;
+		$passcrypt = openssl_encrypt($string, 'aes-256-cbc', $this->securityKey, OPENSSL_RAW_DATA, $this->getIv());
+		return $this->getVerifyString(base64_encode($passcrypt));
+	}
+
+	/**
+	 * 解密字符
+	 *
+	 * @param $string
+	 * @return string
+	 */
+	public function decrypt($string)
+	{
+		$string = str_replace(' ', '+', $string);
+		$string = base64_decode($this->verifyString($string));
+
+		$passcrypt = openssl_decrypt($string, 'aes-256-cbc', $this->securityKey, OPENSSL_RAW_DATA, $this->getIv());
+		return substr($passcrypt, 10);
+	}
+
+	/**
+	 * 将字符串做数字签名
+	 *
+	 * @param $string
+	 * @return string
+	 */
+	public function getVerifyCode($string)
+	{
+		return substr(md5($string), -1 * $this->keyLength);
+	}
+
+	/**
+	 * 生成签名字符串并返回 签名+字符串
+	 *
+	 * @param $string
+	 * @return string
+	 */
+	public function getVerifyString($string)
+	{
+		return $this->getVerifyCode($string) . $string;
+	}
+
+	/**
+	 * 验证字符创的数字签名,如果没有通过就返回空字符,否则返回去掉签名的字符
+	 *
+	 * @param $string
+	 * @param $code
+	 * @return bool
+	 */
+	public function verifyString($string)
+	{
+		$verifyCode = substr($string, 0, $this->keyLength);
+		if ($this->getVerifyCode(substr($string, $this->keyLength)) != $verifyCode) return '';
+		return substr($string, $this->keyLength);
+	}
+}

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 191 - 0
src/Library/Device.php


+ 98 - 0
src/Library/Download.php

@@ -0,0 +1,98 @@
+<?php
+namespace Qii\Library;
+
+ignore_user_abort(true);
+class Download
+{
+	const VERSION = 1.0;
+	public function __construct()
+	{
+		
+	}
+	/**
+	 * 以字符串的形式下载文件
+	 *
+	 * @param String $fileName 保存的文件名
+	 * @param String $string 下载的内容
+	 */
+	public function downloadByString($fileName, $string)
+	{
+		header('Cache-Control: max-age=2592000');
+		header('Last-Modified: '.gmdate('D, d M Y H:i:s') . ' GMT');
+		header('Cache-Control: no-store, no-cache, must-revalidate');
+		header('Cache-Control: pre-check=0, post-check=0, max-age=0');
+		header("Content-type: application/octet-stream");
+		header("Accept-Length: ".  sizeof($string));
+		$fileName = iconv("UTF-8", "GB2312//TRANSLIT", $fileName);
+		header("Content-Disposition: attachment; filename=". $fileName);
+		echo $string;
+	}
+	/**
+	 * 指定文件路径下载指定文件
+	 *
+	 * @param String $filePath 文件路径
+	 * @param String $fileName 文件名
+	 * @param String $mime 文件类型
+	 * @param String $view 下载/打开文件
+	 */
+	public function download($filePath, $fileName = '', $mime = '', $view = 'download')
+	{
+		//设定不限制时间
+		ignore_user_abort(false);
+		set_time_limit(0);
+		if(file_exists($filePath))
+		{
+			if($fileName == '')
+			{
+				$fileName = basename($filePath);
+			}
+			$file = fopen($filePath, "r"); // 打开文件
+			// 输入文件标签
+			header('Cache-Control: max-age=2592000');
+			header('Last-Modified: '.gmdate('D, d M Y H:i:s') . ' GMT');
+			header('Cache-Control: no-store, no-cache, must-revalidate');
+			header('Cache-Control: pre-check=0, post-check=0, max-age=0');
+			if($mime != '' && $mime != 'unlink')
+			{
+				header("Content-type: {$mime}");
+			}
+			else
+			{
+				header("Content-type: application/octet-stream");
+			}
+			header('Content-Encoding: none');
+			header('Content-Transfer-Encoding: binary');
+			header("Accept-Ranges: bytes");
+			header("Accept-Length: ".  filesize($filePath));
+			$fileName = iconv("UTF-8", "GB2312//TRANSLIT", $fileName);
+			if($view == 'download')
+			{
+				header("Content-Disposition: attachment; filename=". $fileName);
+			}
+			else
+			{
+				header('Content-Disposition: inline;filename="'.$fileName.'"');
+			}
+			//输出固定长度的文件避免文件过大导致无法下载
+			$chunk = 16384;
+			$speed = 1000;
+			//#$speed = 0;
+			$sleep = $speed ? floor(( $chunk / ($speed*1024))*1000000) : 0;
+			do
+			{
+				$buf = fread($file, $chunk);
+				$sent += strlen($buf);
+				echo $buf;
+				ob_flush();
+				flush();
+				usleep($sleep);
+				if(strlen($buf) ==0)
+				{
+					break;
+				}
+			}while(true);
+			fclose($file);
+			exit();
+		}
+	}
+}

+ 378 - 0
src/Library/Flexihash.php

@@ -0,0 +1,378 @@
+<?php
+namespace Qii\Library;
+/**
+ * Flexihash - A simple consistent hashing implementation for PHP.
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2008 Paul Annesley
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author Paul Annesley
+ * @link http://paul.annesley.cc/
+ * @copyright Paul Annesley, 2008
+ * @comment by MyZ (http://blog.csdn.net/mayongzhan)
+ */
+
+/**
+ * A simple consistent hashing implementation with pluggable hash algorithms.
+ *
+ * @author Paul Annesley
+ * @package Flexihash
+ * @licence http://www.opensource.org/licenses/mit-license.php
+ *
+ * 使用方法:
+ *
+ * $hash = new \Qii\Library\Flexihash(null, 64);
+ * $hash->addTarget('Node1');
+ * $hash->addTarget('Node2');
+ * $hash->addTarget('Node3');
+ * $hash->addTarget('Node4');
+ * print_r($hash->getAllTargets());
+ * echo PHP_EOL;
+ * print_r($hash->lookup('www.baidu.com'));
+ *
+ * echo PHP_EOL;
+ * $hash->removeTarget('Node1');
+ * $hash->removeTarget('Node4');
+ * print_r($hash->lookup('www.baidu.com'));
+ */
+class Flexihash
+{
+
+	/**
+	 * The number of positions to hash each target to.
+	 *
+	 * @var int
+	 * @comment 虚拟节点数,解决节点分布不均的问题
+	 */
+	private $_replicas = 64;
+
+	/**
+	 * The hash algorithm, encapsulated in a Flexihash_Hasher implementation.
+	 * @var object Flexihash_Hasher
+	 * @comment 使用的hash方法 : md5,crc32
+	 */
+	private $_hasher;
+
+	/**
+	 * Internal counter for current number of targets.
+	 * @var int
+	 * @comment 节点记数器
+	 */
+	private $_targetCount = 0;
+
+	/**
+	 * Internal map of positions (hash outputs) to targets
+	 * @var array { position => target, ... }
+	 * @comment 位置对应节点,用于lookup中根据位置确定要访问的节点
+	 */
+	private $_positionToTarget = array();
+
+	/**
+	 * Internal map of targets to lists of positions that target is hashed to.
+	 * @var array { target => [ position, position, ... ], ... }
+	 * @comment 节点对应位置,用于删除节点
+	 */
+	private $_targetToPositions = array();
+
+	/**
+	 * Whether the internal map of positions to targets is already sorted.
+	 * @var boolean
+	 * @comment 是否已排序
+	 */
+	private $_positionToTargetSorted = false;
+
+	/**
+	 * Constructor
+	 * @param object $hasher Flexihash_Hasher
+	 * @param int $replicas Amount of positions to hash each target to.
+	 * @comment 构造函数,确定要使用的hash方法和虚拟节点数,虚拟节点数越多,分布越均匀,但程序的分布式运算越慢
+	 */
+	public function __construct(Flexihash_Hasher $hasher = null, $replicas = null)
+	{
+		$this->_hasher = $hasher ? $hasher : new Flexihash_Crc32Hasher();
+		if (!empty($replicas)) $this->_replicas = $replicas;
+	}
+
+	/**
+	 * Add a target.
+	 * @param string $target
+	 * @chainable
+	 * @comment 添加节点,根据虚拟节点数,将节点分布到多个虚拟位置上
+	 */
+	public function addTarget($target)
+	{
+		if (isset($this->_targetToPositions[$target])) {
+			throw new \Flexihash_Exception("Target '$target' already exists.");
+		}
+
+		$this->_targetToPositions[$target] = array();
+
+		// hash the target into multiple positions
+		for ($i = 0; $i < $this->_replicas; $i++) {
+			$position = $this->_hasher->hash($target . $i);
+			$this->_positionToTarget[$position] = $target; // lookup
+			$this->_targetToPositions[$target] [] = $position; // target removal
+		}
+
+		$this->_positionToTargetSorted = false;
+		$this->_targetCount++;
+
+		return $this;
+	}
+
+	/**
+	 * Add a list of targets.
+	 * @param array $targets
+	 * @chainable
+	 */
+	public function addTargets($targets)
+	{
+		foreach ($targets as $target) {
+			$this->addTarget($target);
+		}
+
+		return $this;
+	}
+
+	/**
+	 * Remove a target.
+	 * @param string $target
+	 * @chainable
+	 */
+	public function removeTarget($target)
+	{
+		if (!isset($this->_targetToPositions[$target])) {
+			throw new \Flexihash_Exception("Target '$target' does not exist.");
+		}
+
+		foreach ($this->_targetToPositions[$target] as $position) {
+			unset($this->_positionToTarget[$position]);
+		}
+
+		unset($this->_targetToPositions[$target]);
+
+		$this->_targetCount--;
+
+		return $this;
+	}
+
+	/**
+	 * A list of all potential targets
+	 * @return array
+	 */
+	public function getAllTargets()
+	{
+		return array_keys($this->_targetToPositions);
+	}
+
+	/**
+	 * Looks up the target for the given resource.
+	 * @param string $resource
+	 * @return string
+	 */
+	public function lookup($resource)
+	{
+		$targets = $this->lookupList($resource, 1);
+		if (empty($targets)) throw new \Flexihash_Exception('No targets exist');
+		return $targets[0];
+	}
+
+	/**
+	 * Get a list of targets for the resource, in order of precedence.
+	 * Up to $requestedCount targets are returned, less if there are fewer in total.
+	 *
+	 * @param string $resource
+	 * @param int $requestedCount The length of the list to return
+	 * @return array List of targets
+	 * @comment 查找当前的资源对应的节点,
+	 *          节点为空则返回空,节点只有一个则返回该节点,
+	 *          对当前资源进行hash,对所有的位置进行排序,在有序的位置列上寻找当前资源的位置
+	 *          当全部没有找到的时候,将资源的位置确定为有序位置的第一个(形成一个环)
+	 *          返回所找到的节点
+	 */
+	public function lookupList($resource, $requestedCount)
+	{
+		if (!$requestedCount)
+			throw new \Flexihash_Exception('Invalid count requested');
+
+		// handle no targets
+		if (empty($this->_positionToTarget))
+			return array();
+
+		// optimize single target
+		if ($this->_targetCount == 1)
+			return array_unique(array_values($this->_positionToTarget));
+
+		// hash resource to a position
+		$resourcePosition = $this->_hasher->hash($resource);
+
+		$results = array();
+		$collect = false;
+
+		$this->_sortPositionTargets();
+
+		// search values above the resourcePosition
+		foreach ($this->_positionToTarget as $key => $value) {
+			// start collecting targets after passing resource position
+			if (!$collect && $key > $resourcePosition) {
+				$collect = true;
+			}
+
+			// only collect the first instance of any target
+			if ($collect && !in_array($value, $results)) {
+				$results [] = $value;
+			}
+
+			// return when enough results, or list exhausted
+			if (count($results) == $requestedCount || count($results) == $this->_targetCount) {
+				return $results;
+			}
+		}
+
+		// loop to start - search values below the resourcePosition
+		foreach ($this->_positionToTarget as $key => $value) {
+			if (!in_array($value, $results)) {
+				$results [] = $value;
+			}
+
+			// return when enough results, or list exhausted
+			if (count($results) == $requestedCount || count($results) == $this->_targetCount) {
+				return $results;
+			}
+		}
+
+		// return results after iterating through both "parts"
+		return $results;
+	}
+
+	public function __toString()
+	{
+		return sprintf(
+			'%s{targets:[%s]}',
+			get_class($this),
+			implode(',', $this->getAllTargets())
+		);
+	}
+
+	// ----------------------------------------
+	// private methods
+
+	/**
+	 * Sorts the internal mapping (positions to targets) by position
+	 */
+	private function _sortPositionTargets()
+	{
+		// sort by key (position) if not already
+		if (!$this->_positionToTargetSorted) {
+			ksort($this->_positionToTarget, SORT_REGULAR);
+			$this->_positionToTargetSorted = true;
+		}
+	}
+
+}
+
+
+/**
+ * Hashes given values into a sortable fixed size address space.
+ *
+ * @author Paul Annesley
+ * @package Flexihash
+ * @licence http://www.opensource.org/licenses/mit-license.php
+ */
+interface Flexihash_Hasher
+{
+
+	/**
+	 * Hashes the given string into a 32bit address space.
+	 *
+	 * Note that the output may be more than 32bits of raw data, for example
+	 * hexidecimal characters representing a 32bit value.
+	 *
+	 * The data must have 0xFFFFFFFF possible values, and be sortable by
+	 * PHP sort functions using SORT_REGULAR.
+	 *
+	 * @param string
+	 * @return mixed A sortable format with 0xFFFFFFFF possible values
+	 */
+	public function hash($string);
+
+}
+
+
+/**
+ * Uses CRC32 to hash a value into a signed 32bit int address space.
+ * Under 32bit PHP this (safely) overflows into negatives ints.
+ *
+ * @author Paul Annesley
+ * @package Flexihash
+ * @licence http://www.opensource.org/licenses/mit-license.php
+ */
+class Flexihash_Crc32Hasher
+	implements Flexihash_Hasher
+{
+
+	/* (non-phpdoc)
+	* @see Flexihash_Hasher::hash()
+	*/
+	public function hash($string)
+	{
+		return crc32($string);
+	}
+
+}
+
+
+/**
+ * Uses CRC32 to hash a value into a 32bit binary string data address space.
+ *
+ * @author Paul Annesley
+ * @package Flexihash
+ * @licence http://www.opensource.org/licenses/mit-license.php
+ */
+class Flexihash_Md5Hasher
+	implements Flexihash_Hasher
+{
+
+	/* (non-phpdoc)
+	* @see Flexihash_Hasher::hash()
+	*/
+	public function hash($string)
+	{
+		return substr(md5($string), 0, 8); // 8 hexits = 32bit
+
+		// 4 bytes of binary md5 data could also be used, but
+		// performance seems to be the same.
+	}
+
+}
+
+
+/**
+ * An exception thrown by Flexihash.
+ *
+ * @author Paul Annesley
+ * @package Flexihash
+ * @licence http://www.opensource.org/licenses/mit-license.php
+ */
+class Flexihash_Exception extends Exception
+{
+}

+ 29 - 0
src/Library/Http.php

@@ -0,0 +1,29 @@
+<?php
+ namespace Qii\Library;
+/**
+ * A parallel HTTP client written in pure PHP
+ *
+ * This file just for non-composer user, require this file directly.
+ *
+ * @author hightman <hightman@twomice.net>
+ * @link http://hightman.cn
+ * @copyright Copyright (c) 2015 Twomice Studio.
+ */
+
+_require(array(
+		 __DIR__ . '/Third/hightman/ParseInterface.php',
+		__DIR__ . '/Third/hightman/HeaderTrait.php',
+		__DIR__ . '/Third/hightman/Client.php',
+		__DIR__ . '/Third/hightman/Connection.php',
+		__DIR__ . '/Third/hightman/Response.php',
+		__DIR__ . '/Third/hightman/Request.php',
+		__DIR__ . '/Third/hightman/Processor.php'
+	)
+);
+use hightman\http\Client;
+use hightman\http\Request;
+use hightman\http\Response;
+class Http extends Client
+{
+	
+}

+ 217 - 0
src/Library/IP.php

@@ -0,0 +1,217 @@
+<?php
+ namespace Qii\Library;
+/**
+ * Class ip
+ *
+ * @package Library
+ */
+class IP
+{
+	var $StartIP = 0;
+	var $EndIP = 0;
+	var $Country = '';
+	var $Local = '';
+
+	var $CountryFlag = 0; // 标识 Country位置
+	// 0x01,随后3字节为Country偏移,没有Local
+	// 0x02,随后3字节为Country偏移,接着是Local
+	// 其他,Country,Local,Local有类似的压缩。可能多重引用。
+
+	var $fp;
+
+	var $FirstStartIp = 0;
+	var $LastStartIp = 0;
+	var $EndIpOff = 0;
+
+	/**
+	 * 获取访问者的ip地址
+	 *
+	 * @return mixed
+	 */
+	public function getIP()
+	{
+		if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
+			$ip = $_SERVER['HTTP_CLIENT_IP'];
+		} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
+			$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
+		} else {
+			$ip = $_SERVER['REMOTE_ADDR'];
+		}
+		return $ip;
+	}
+
+	public function getStartIp($RecNo)
+	{
+		$offset = $this->FirstStartIp + $RecNo * 7;
+		@fseek($this->fp, $offset, SEEK_SET);
+		$buf = fread($this->fp, 7);
+		$this->EndIpOff = ord($buf[4]) + (ord($buf[5]) * 256) + (ord($buf[6]) * 256 * 256);
+		$this->StartIp = ord($buf[0]) + (ord($buf[1]) * 256) + (ord($buf[2]) * 256 * 256) + (ord($buf[3]) * 256 * 256 * 256);
+		return $this->StartIp;
+	}
+
+	public function getEndIp()
+	{
+		@fseek($this->fp, $this->EndIpOff, SEEK_SET);
+		$buf = fread($this->fp, 5);
+		$this->EndIp = ord($buf[0]) + (ord($buf[1]) * 256) + (ord($buf[2]) * 256 * 256) + (ord($buf[3]) * 256 * 256 * 256);
+		$this->CountryFlag = ord($buf[4]);
+		return $this->EndIp;
+	}
+
+	public function getCountry()
+	{
+		switch ($this->CountryFlag) {
+			case 1:
+			case 2:
+				$this->Country = $this->getFlagStr($this->EndIpOff + 4);
+				$this->Local = (1 == $this->CountryFlag) ? '' : $this->getFlagStr($this->EndIpOff + 8);
+				break;
+			default:
+				$this->Country = $this->getFlagStr($this->EndIpOff + 4);
+				$this->Local = $this->getFlagStr(ftell($this->fp));
+		}
+	}
+
+	public function getFlagStr($offset)
+	{
+		$flag = 0;
+
+		while (1) {
+			@fseek($this->fp, $offset, SEEK_SET);
+			$flag = ord(fgetc($this->fp));
+
+			if ($flag == 1 || $flag == 2) {
+				$buf = fread($this->fp, 3);
+
+				if ($flag == 2) {
+					$this->CountryFlag = 2;
+					$this->EndIpOff = $offset - 4;
+				}
+
+				$offset = ord($buf[0]) + (ord($buf[1]) * 256) + (ord($buf[2]) * 256 * 256);
+			} else
+				break;
+		}
+
+		if ($offset < 12) return '';
+
+		@fseek($this->fp, $offset, SEEK_SET);
+
+		return $this->getStr();
+	}
+
+	public function getStr()
+	{
+		$str = '';
+
+		while (1) {
+			$c = fgetc($this->fp);
+
+			if (ord($c[0]) == 0) break;
+
+			$str .= $c;
+		}
+
+		return $str;
+	}
+
+	public function QQwry($dotip = '')
+	{
+		if (!$dotip) return;
+
+		if (preg_match("/^(127)/", $dotip)) {
+			$this->Country = '本地网络';
+			return $this;
+		} else if (preg_match("/^(192)/", $dotip)) {
+			$this->Country = '局域网';
+			return $this;
+		}
+		$ip = $this->IpToInt($dotip);
+		$this->fp = fopen(__QQWRY__, "rb");
+
+		if ($this->fp == NULL) {
+			$szLocal = "OpenFileError";
+			return 1;
+		}
+
+		@fseek($this->fp, 0, SEEK_SET);
+		$buf = fread($this->fp, 8);
+		$this->FirstStartIp = ord($buf[0]) + (ord($buf[1]) * 256) + (ord($buf[2]) * 256 * 256) + (ord($buf[3]) * 256 * 256 * 256);
+		$this->LastStartIp = ord($buf[4]) + (ord($buf[5]) * 256) + (ord($buf[6]) * 256 * 256) + (ord($buf[7]) * 256 * 256 * 256);
+
+		$RecordCount = floor(($this->LastStartIp - $this->FirstStartIp) / 7);
+
+		if ($RecordCount <= 1) {
+			$this->Country = "FileDataError";
+			fclose($this->fp);
+			return 2;
+		}
+
+		$RangB = 0;
+		$RangE = $RecordCount;
+
+		// Match ...
+		while ($RangB < $RangE - 1) {
+			$RecNo = floor(($RangB + $RangE) / 2);
+			$this->getStartIp($RecNo);
+
+			if ($ip == $this->StartIp) {
+				$RangB = $RecNo;
+				break;
+			}
+
+			if ($ip > $this->StartIp) $RangB = $RecNo;
+			else $RangE = $RecNo;
+		}
+
+		$this->getStartIp($RangB);
+		$this->getEndIp();
+
+		if (($this->StartIp <= $ip) && ($this->EndIp >= $ip)) {
+			$this->getCountry();
+		} else {
+			$this->Country = '未知';
+			$this->Local = '';
+		}
+
+		fclose($this->fp);
+              return $this;
+	}
+
+	public function IpToInt($Ip)
+	{
+		$array = explode('.', $Ip, 4);
+		$Int = ($array[0] * 256 * 256 * 256) + ($array[1] * 256 * 256) + ($array[2] * 256) + $array[3];
+
+		return $Int;
+	}
+
+	/**
+	 * 将ip转换成整形
+	 *
+	 * @param string $ip
+	 * @return string
+	 */
+	public function ip2Long($ip = null)
+	{
+		if (!$ip) $ip = $this->getIP();
+		return sprintf("%u", ip2long($ip));
+	}
+
+	/**
+	 * 将长整形的数转换成ip地址
+	 *
+	 * @param $longIP
+	 * @return string
+	 */
+	public function long2Ip($longIP)
+	{
+		if(!$longIP || intval($longIP) == 0) return 'Unknow';
+		$ip1 = ($longIP >> 24) & 0xff; // 跟0xff做与运算的目的是取低8位
+		$ip2 = ($longIP >> 16) & 0xff;
+		$ip3 = ($longIP >> 8) & 0xff;
+		$ip4 = $longIP & 0xff;
+		return $ip1 . '.' . $ip2 . '.' . $ip3 . '.' . $ip4;
+	}
+}

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно