rabin 4 months ago
parent
commit
ea065564f9

+ 1 - 1
LICENSE

@@ -1,5 +1,5 @@
 Apache License
-Copyright 2016-2017 Dever(dever.cc)
+Copyright 2023-2025 Dever2(dever.cc)
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.

+ 101 - 13
boot.php

@@ -4,9 +4,10 @@ Dever::run();
 class Dever
 {
     public static $data = array();
+    public static $app = array();
     public static function run()
     {
-        spl_autoload_register(array('Dever', 'loadClass'));
+        spl_autoload_register(array('Dever', 'autoload'));
         $route = Dever\Route::get();
         Dever\Debug::init();
         Dever\Project::register();
@@ -23,19 +24,46 @@ class Dever
                 }
             }
         }
-        $result = Dever\Output::success(self::load($route['l'], DEVER_APP_NAME, 'api'));
+        self::$app[] = DEVER_APP_NAME;
+        if ($route['l']) {
+            $result = Dever\Output::success(self::load($route['l'], DEVER_APP_NAME, 'api'));
+        } else {
+            $result = Dever\Output::success('ok');
+        }
         if (isset($expire)) {
             self::cache($key, $result, $expire);
         }
     }
-    public static function loadClass($class)
+    public static function autoload($class)
+    {
+        if (strpos($class, 'Dever') === 0) {
+            include DEVER_PATH . 'src/' . str_replace('\\', '/', $class) . '.php';
+        } else {
+            Dever\Library::autoload($class);
+        }
+    }
+    public static function call($class, $param = array(), $path = '')
     {
-        include DEVER_PATH . 'src/' . str_replace('\\', '/', $class) . '.php';
+        if (strpos($class, '.')) {
+            list($class, $method) = explode('.', $class);
+            $call = 'load';
+            if (!$path) $path = 'lib';
+        } elseif (strpos($class, '-')) {
+            list($class, $method) = explode('-', $class);
+            $call = 'db';
+            if (!$path) $path = 'default';
+        }
+        return self::$call($class, '', $path)->$method(...$param);
     }
     public static function load($class, $app = '', $path = 'lib')
     {
-        if (!$app) {
-            $app = DEVER_APP_NAME;
+        if (strpos($class, '/') && !$app) {
+            list($app, $class) = explode('/', $class);
+        }
+        if ($app) {
+            array_unshift(self::$app, $app);
+        } else {
+            $app = self::$app[0];
         }
         $index = $app . $path . $class;
         if (empty(self::$data[$index])) {
@@ -45,8 +73,13 @@ class Dever
     }
     public static function db($table, $app = '', $store = 'default', $partition = false, $path = 'table')
     {
-        if (!$app) {
-            $app = DEVER_APP_NAME;
+        if (strpos($table, '/') && !$app) {
+            list($app, $table) = explode('/', $table);
+        }
+        if ($app) {
+            array_unshift(self::$app, $app);
+        } else {
+            $app = self::$app[0];
         }
         $index = $app . $path . $table;
         if (empty(self::$data[$index])) {
@@ -54,13 +87,19 @@ class Dever
         }
         return self::$data[$index];
     }
+    public static function reset()
+    {
+        if (count(self::$app) > 1) {
+            array_shift(self::$app);
+        }
+    }
     public static function config()
     {
         return Dever\Config::get(...func_get_args());
     }
     public static function apply()
     {
-        return Library::get()->apply(...func_get_args());
+        return Dever\Library::apply(...func_get_args());
     }
     public static function success()
     {
@@ -82,6 +121,10 @@ class Dever
     {
         return Dever\Route::url(...func_get_args());
     }
+    public static function host()
+    {
+        return Dever\Route::host(...func_get_args());
+    }
     public static function project()
     {
         return Dever\Project::load(...func_get_args());
@@ -102,14 +145,26 @@ class Dever
     {
         return Dever\View::show(...func_get_args());
     }
-    public static function path()
+    public static function file()
+    {
+        return Dever\File::get(...func_get_args());
+    }
+    public static function data()
+    {
+        return Dever\File::data();
+    }
+    public static function page()
     {
-        return Dever\Path::get(...func_get_args());
+        return Dever\Paginator::get(...func_get_args());
+    }
+    public static function rule()
+    {
+        return Dever\Helper\Rule::get(...func_get_args());
     }
     public static function curl()
     {
         $curl = new Dever\Helper\Curl();
-        return $curl->log(true)->load(...func_get_args());
+        return $curl->load(...func_get_args());
     }
     public static function cache($key, $value = false)
     {
@@ -135,6 +190,27 @@ class Dever
         $class = 'Dever\\Store\\' . $setting['type'];
         return $class::getInstance($store, $setting, $partition);
     }
+    public static function in_array($array, $value, $key = 'id', $show = 'name')
+    {
+        $column = array_column($array, $key);
+        if ($column) {
+            $index = array_search($value, $column);
+            if ($index >= 0) {
+                return $array[$index][$show];
+            }
+        }
+        return false;
+    }
+    public static function isset($input, $value = false)
+    {
+        if (isset($input[$value])) {
+            if (is_string($input[$value]) && !strlen($input[$value])) {
+                return false;
+            }
+            return $input[$value];
+        }
+        return false;
+    }
     public static function check($var, $find)
     {
         if (is_array($var)) {
@@ -146,7 +222,10 @@ class Dever
     }
     public static function json_encode($value)
     {
-        $value = json_encode($value, JSON_UNESCAPED_UNICODE);
+        $value = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_NUMERIC_CHECK);
+        if (strpos($value, '<')) {
+            $value = Dever\Helper\Secure::xss($value);
+        }
         return $value;
     }
     public static function json_decode($value)
@@ -181,4 +260,13 @@ class Dever
         $charid = strtoupper(md5(uniqid(mt_rand(), true)));
         return substr($charid, 0, 8) . substr($charid, 8, 4) . substr($charid, 12, 4) . substr($charid, 16, 4) . substr($charid, 20, 12);
     }
+    public static function is_file($file)
+    {
+        if (strtolower(substr($file, 0, 4)) == 'http') {
+            $header = get_headers($file, true);
+            return isset($header[0]) && (strpos($header[0], '200') || strpos($header[0], '304'));
+        } else {
+            return is_file($file);
+        }
+    }
 }

+ 4 - 1
src/Dever/Config.php

@@ -2,7 +2,7 @@
 class Config
 {
     protected static $data = array();
-    public static function get($key)
+    public static function get($key = 'setting', $data = false)
     {
         if (empty(self::$data[$key])) {
             $path = DEVER_PROJECT_PATH . 'config' . DIRECTORY_SEPARATOR;
@@ -13,6 +13,9 @@ class Config
                 self::$data[$key] = include($file);
             }
         }
+        if ($data) {
+            self::$data[$key] = array_merge(self::$data[$key], $data);
+        }
         return self::$data[$key];
     }
     protected static function env($path)

+ 29 - 0
src/Dever/File.php

@@ -0,0 +1,29 @@
+<?php namespace Dever;
+class File
+{
+    public static function id($id, $path)
+    {
+        $id = abs(intval($id));
+        $sid = sprintf("%09d", $id);
+        $dir1 = substr($sid, 0, 3);
+        $dir2 = substr($sid, 3, 2);
+        $dir3 = substr($sid, 5, 2);
+        return self::get($path . DIRECTORY_SEPARATOR . $dir1 . DIRECTORY_SEPARATOR . $dir2 . DIRECTORY_SEPARATOR . $dir3 . DIRECTORY_SEPARATOR . $id . '.jpg');
+    }
+    public static function get($file, $path = '')
+    {
+        $file = self::data() . DEVER_PROJECT . DIRECTORY_SEPARATOR . $file;
+        $path = dirname($file);
+        if (!is_dir($path)) {
+            mkdir($path, 0777, true);
+        }
+        return $file;
+    }
+    public static function data()
+    {
+        if (isset(Config::get('setting')['data'])) {
+            return Config::get('setting')['data'];
+        }
+        return DEVER_PROJECT_PATH . 'data' . DIRECTORY_SEPARATOR;
+    }
+}

+ 2 - 1
src/Dever/Helper/Curl.php

@@ -53,9 +53,10 @@ class Curl
         $this->setUrl($url);
         return $this;
     }
-    public function setLog($log)
+    public function log($log)
     {
         $this->log = $log;
+        return $this;
     }
     private function init()
     {

+ 0 - 42
src/Dever/Helper/Path.php

@@ -1,42 +0,0 @@
-<?php namespace Dever\Helper;
-use Dever;
-class Path
-{
-    public static function id($id, $path = '')
-    {
-        $id = abs(intval($id));
-        $sid = sprintf("%09d", $id);
-        $dir1 = substr($sid, 0, 3);
-        $dir2 = substr($sid, 3, 2);
-        $dir3 = substr($sid, 5, 2);
-        if ($path) {
-            $path .= DIRECTORY_SEPARATOR;
-        }
-        $path = Dever::path($dir1 . DIRECTORY_SEPARATOR . $dir2 . DIRECTORY_SEPARATOR . $dir3 . DIRECTORY_SEPARATOR);
-        return $path;
-    }
-    public static function month($path, $time = false)
-    {
-        if (!$time) {
-            $time = time();
-        }
-        if (is_numeric($time)) {
-            $time = date('Y_m_d', $time);
-        }
-        $date = explode('_', $time);
-        $path = Dever::path($path . DIRECTORY_SEPARATOR . $date[0] . DIRECTORY_SEPARATOR . $date[1] . DIRECTORY_SEPARATOR);
-        return $path;
-    }
-    public static function day($path, $time = false)
-    {
-        if (!$time) {
-            $time = time();
-        }
-        if (is_numeric($time)) {
-            $time = date('Y_m_d', $time);
-        }
-        $date = explode('_', $time);
-        $path = Dever::path($path . DIRECTORY_SEPARATOR . $date[0] . DIRECTORY_SEPARATOR . $date[1] . DIRECTORY_SEPARATOR . $date[2] . DIRECTORY_SEPARATOR);
-        return $path;
-    }
-}

+ 26 - 1
src/Dever/Helper/Secure.php

@@ -8,7 +8,7 @@ class Secure
         $data = Dever::json_encode(array($uid, time(), $extend));
         return self::encode($data);
     }
-    public static function checkLogin($signature, $time = 300)
+    public static function checkLogin($signature, $time = 604800)
     {
         self::repeat($signature, $time);
         $auth = Dever::json_decode(self::decode($signature));
@@ -198,4 +198,29 @@ class Secure
         $decodestr = base64_decode($string);
         return $decodestr;
     }
+    public static function xss($data)
+    {
+        if (!is_string($data)) {
+            return $data;
+        }
+        $data = htmlspecialchars_decode($data);
+        $data = str_replace(array('&amp;', '&lt;', '&gt;'), array('&amp;amp;', '&amp;lt;', '&amp;gt;'), $data);
+        $data = preg_replace('/(&#*\w+)[\x00-\x20]+;/u', '$1;', $data);
+        $data = preg_replace('/(&#x*[0-9A-F]+);*/iu', '$1;', $data);
+        $data = html_entity_decode($data, ENT_COMPAT, 'UTF-8');
+        $data = preg_replace('#(<[^>]+?[\x00-\x20"\'])(?:on|xmlns)[^>]*+>#iu', '$1>', $data);
+        $data = preg_replace('#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([`\'"]*)[\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2nojavascript...', $data);
+        $data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2novbscript...', $data);
+        $data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u', '$1=$2nomozbinding...', $data);
+        $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?expression[\x00-\x20]*\([^>]*+>#i', '$1>', $data);
+        $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?behaviour[\x00-\x20]*\([^>]*+>#i', '$1>', $data);
+        $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*+>#iu', '$1>', $data);
+        $data = preg_replace('#</*\w+:\w[^>]*+>#i', '', $data);
+
+        do {
+            $old_data = $data;
+            $data = preg_replace('#</*(?:applet|b(?:ase|gsound|link)|frame(?:set)?|i(?:frame|layer)|l(?:ayer|ink)|meta|object|s(?:cript|tyle)|title|xml)[^>]*+>#i', '', $data);
+        } while ($old_data !== $data);
+        return $data;
+    }
 }

+ 2 - 1
src/Dever/Import.php

@@ -34,7 +34,7 @@ class Import
                 return $this->loadDevelopServer();
             }
             if ($this->path == 'api') {
-                $this->param = Route::input();
+                if (!$this->param) $this->param = Route::input();
                 if (method_exists($this->class, $this->method . '_secure')) {
                     $key = false;
                     $token = $this->method . '_token';
@@ -77,6 +77,7 @@ class Import
         $data = $this->class->{$this->method}(...$param);
         Debug::lib($this->class, $this->method);
         Dever::reset();
+        $this->method = '';
         return $data;
     }
     private function loadDevelopParam()

+ 11 - 7
src/Dever/Library.php

@@ -6,7 +6,11 @@ class Library
     public static function autoload($class)
     {
         $class = explode('\\', $class);
-        self::require(lcfirst($class[0]), lcfirst($class[1]) . DIRECTORY_SEPARATOR . $class[2]);
+        if (isset($class[3])) {
+            self::require(lcfirst($class[0]), lcfirst($class[1]) . DIRECTORY_SEPARATOR . $class[2] . DIRECTORY_SEPARATOR . $class[3]);
+        } else {
+            self::require(lcfirst($class[0]), lcfirst($class[1]) . DIRECTORY_SEPARATOR . $class[2]);
+        }
     }
     public static function require($app, $file)
     {
@@ -17,15 +21,15 @@ class Library
             require $file;
         }
     }
-    public static function apply($class, $app, $path)
+    public static function apply($file, $app, $path)
     {
-        if (strpos($class, '/')) {
-            $class = str_replace(' ', '/', ucwords(str_replace('/', ' ', $class)));
-            $file = $path . DIRECTORY_SEPARATOR . $class;
+        if (strpos($file, '/')) {
+            $class = str_replace(' ', '/', ucwords(str_replace('/', ' ', $file)));
             $class = str_replace('/', '\\', $class);
+            $file = $path . DIRECTORY_SEPARATOR . $file;
         } else {
-            $class = ucfirst($class);
-            $file = $path . DIRECTORY_SEPARATOR . $class;
+            $class = ucfirst($file);
+            $file = $path . DIRECTORY_SEPARATOR . $file;
         }
         $class = ucfirst($app) . '\\' . ucfirst($path) . '\\' . $class;
         self::require($app, $file);

+ 14 - 18
src/Dever/Log.php

@@ -48,8 +48,10 @@ class Log
     }
     private static function push_file($log, $config)
     {
-        $log = date('Y-m-d H:i:s') . ' ' . DEVER_PROJECT . ' ' . DEVER_APP_NAME . ' ' . $log . "\r\n";
-        $file = self::getFile($config['level']);
+        $day = date('Y/m/d');
+        $time = date('H:i:s');
+        $log = $day . ' ' . $time . ' ' . DEVER_PROJECT . ' ' . DEVER_APP_NAME . ' ' . $log . "\r\n";
+        $file = self::file($config['level'], $day, substr($time, 0, 2));
         $size = 5242880;
         if (isset($config['size'])) {
             $size = $config['size'];
@@ -59,7 +61,7 @@ class Log
             $exists = true;
         }
         if ($exists && $size <= filesize($file)) {
-            rename($file, $file . '.' . date('H_i_s') . '.bak');
+            rename($file, $file . '.' . str_replace(':', '_', $time) . '.bak');
             $exists = false;
         }
         $state = error_log($log, 3, $file);
@@ -116,26 +118,20 @@ class Log
         }
         return $string;
     }
-    private static function file($level, $file = '')
+    private static function file($level, $day, $hour = '')
     {
-        if (!$file) {
-            $file = date('Y_m_d_H');
-        }
-        if (strpos($file, '-')) {
-            $file = str_replace('-', '_', $file);
-        }
-        $date = explode('_', $file);
-        $path = Path::get('logs' . DIRECTORY_SEPARATOR . $date[0] . DIRECTORY_SEPARATOR . $date[1] . DIRECTORY_SEPARATOR . $date[2] . DIRECTORY_SEPARATOR);
-        $root = \Dever\Helper\Path::day('logs', $file);
         if ($level == 1) {
-            $prefix = 'debug';
+            $file = 'debug';
         } elseif ($level == 2) {
-            $prefix = 'notice';
+            $file = 'notice';
         } elseif ($level == 3) {
-            $prefix = 'info';
+            $file = 'info';
         } else {
-            $prefix = $level;
+            $file = $level;
+        }
+        if ($hour) {
+            $file .= '_' . $hour;
         }
-        return $path . $prefix . '_' . $file . '.log';
+        return File::get('logs/' . $day . DIRECTORY_SEPARATOR . $file);
     }
 }

+ 72 - 24
src/Dever/Model.php

@@ -10,14 +10,13 @@ class Model
     {
         $project = Project::load($app);
         if ($table) {
-            $file = $path . DIRECTORY_SEPARATOR . $table . '.php';
-            $base = $project['path'] . $file;
+            $base = $project['path'] . $path . DIRECTORY_SEPARATOR . $table . '.php';
             if (is_file($base)) {
                 $this->config = include $base;
                 if (isset($this->config['partition']) && empty($partition)) {
                     $partition = $this->config['partition'];
                 }
-                $file = $path . DIRECTORY_SEPARATOR . $app . DIRECTORY_SEPARATOR . $table . '.php';
+                $file = $app . DIRECTORY_SEPARATOR . $table . '.php';
                 $this->config['app'] = $app;
                 $this->config['table'] = DEVER_PROJECT . '_' . $app . '_' . $table;
                 $this->lang();
@@ -31,25 +30,30 @@ class Model
         }
         $this->store = Dever::store($store, $this->partition);
         if (isset($file)) {
-            $this->init($file);
+            $this->init($path, $store, $file);
         }
         Dever::reset();
     }
-    private function partition($partition)
+    public function partition($partition)
     {
         if (is_array($partition)) {
             $this->partition = $partition;
         } else {
             $this->partition = Dever::config('setting')['database']['partition'] ?? false;
         }
-        if ($this->partition['database']) {
+        if ($this->partition) {
             foreach ($this->partition as $k => &$v) {
                 $t = Dever::session($k);
-                if (!$t) {
+                if ($t) {
+                    $v = $t;
+                } else {
                     $e = '$v=' . $v . ';';
                     eval($e);
                 }
             }
+            if (empty($this->partition['create'])) {
+                $this->partition['create'] = true;
+            }
         }
     }
     private function lang()
@@ -66,24 +70,31 @@ class Model
             }
         }
     }
-    private function init($file)
+    private function init($path, $store, $file)
     {
+        $path .= DIRECTORY_SEPARATOR . $store;
         $data['index'] = $data['struct'] = 0;
         if ($this->partition) {
-            if (isset($this->partition['table'])) {
+            if (isset($this->partition['database']) && $this->partition['database']) {
+                if (!$this->partition['create']) {
+                    $this->config['table'] .= '_' . $this->partition['database'];
+                }
+                $path .= '_' . $this->partition['database'] . DIRECTORY_SEPARATOR;
+            }
+            if (isset($this->partition['table']) && $this->partition['table']) {
                 $this->config['table'] .= '_' . $this->partition['table'];
                 $file = rtrim($file, '.php') . '_';
-                if (isset($this->partition['database'])) {
-                    $file .= $this->partition['database'] . '_';
-                }
                 $file .= $this->partition['table'] . '.php';
             }
-            if (isset($this->partition['field'])) {
-                $this->partition['field'] = Dever::$date::maketime($this->partition['field']);
+            if (isset($this->partition['field']) && $this->partition['field']) {
+                if (strstr($this->partition['field']['value'], 'date(')) {
+                    $this->partition['field']['type'] = 'time';
+                    $this->partition['field']['value'] = Dever::$date::maketime($this->partition['field']['value']);
+                }
                 $data['field'] = 0;
             }
         }
-        $file = Path::get($file);
+        $file = File::get($path . DIRECTORY_SEPARATOR . $file);
         if (is_file($file)) {
             $data = include $file;
         }
@@ -95,15 +106,18 @@ class Model
                     $data[$k] = $num;
                     file_put_contents($file, '<?php return ' . var_export($data, true) . ';');
                 }
-            } elseif ($k == 'field' && $v != $this->partition['field']) {
+            } elseif ($k == 'field' && $v != $this->partition['field']['value']) {
                 $this->store->partition($this->config, $this->partition['field']);
-                $data['field'] = $this->partition['field'];
+                $data['field'] = $this->partition['field']['value'];
                 file_put_contents($file, '<?php return ' . var_export($data, true) . ';');
             }
         }
     }
     public function select($param, $set = array(), $lock = false)
     {
+        if (isset($this->partition['where']) && $this->partition['where']) {
+            $param = array_merge($this->partition['where'], $param);
+        }
         if (empty($set['order'])) {
             if (isset($this->config['order'])) {
                 $set['order'] = $this->config['order'] . ',id desc';
@@ -126,6 +140,9 @@ class Model
     }
     public function count($param = array())
     {
+        if (isset($this->partition['where']) && $this->partition['where']) {
+            $param = array_merge($this->partition['where'], $param);
+        }
         return $this->store->count($this->config['table'], $param, $this->config['struct']);
     }
     public function find($param, $set = array(), $lock = false)
@@ -150,13 +167,13 @@ class Model
     {
         $info = $this->find($param, array(), $lock);
         if ($info) {
-            $state = Dever::db('menu')->update($info['id'], $data);
+            $state = $this->update($info['id'], $data);
             if ($state) {
                 return $info['id'];
             }
             return false;
         } else {
-            return Dever::db('menu')->insert($data);
+            return $this->insert($data);
         }
     }
     public function insert($data)
@@ -164,6 +181,9 @@ class Model
         if (empty($data['cdate'])) {
             $data['cdate'] = DEVER_TIME;
         }
+        if (isset($this->partition['where']) && $this->partition['where']) {
+            $data = array_merge($this->partition['where'], $data);
+        }
         return $this->store->insert($this->config['table'], $data, $this->config['struct']);
     }
     public function update($param, $data, $lock = false)
@@ -237,13 +257,12 @@ class Model
     }
     public function value($key, $value = false, $col = 'id,name')
     {
-        if (isset($this->config['struct']) && $option = Dever::isset($this->config['struct'][$key], 'value')) {
+        if (isset($this->config['struct'][$key]) && $option = Dever::isset($this->config['struct'][$key], 'value')) {
             if (isset($this->config['struct'][$key]['option'])) {
                 $option = $this->config['struct'][$key]['option'];
             } else {
-                if (strpos($option, 'Dever') === 0) {
+                if (is_string($option) && strpos($option, 'Dever') === 0) {
                     eval('$option=' . $option . ';');
-                    $value['option'] = $option;
                 } elseif (is_string($option)) {
                     $option = Dever::db($option)->select([], ['col' => $col])->fetchAll();
                 } elseif (is_array($option) && !isset($option[0])) {
@@ -251,7 +270,7 @@ class Model
                     $option = array();
                     $col = explode(',', $col);
                     foreach ($temp as $k => $v) {
-                        $option[] = array($col[0] => $k, $col[1] => $name);
+                        $option[] = array($col[0] => $k, $col[1] => $v);
                     }
                 }
                 $this->config['struct'][$key]['option'] = $option;
@@ -261,7 +280,10 @@ class Model
                     $temp = explode(',', $value);
                     $result = array();
                     foreach ($temp as $v) {
-                        $result[] = Dever::in_array($option, $v);
+                        $state = Dever::in_array($option, $v);
+                        if ($state) {
+                            $result[] = $state;
+                        }
                     }
                     return implode('、', $result);
                 }
@@ -271,6 +293,32 @@ class Model
         }
         return false;
     }
+    public function tree($where, $config, $func = false, $set = array())
+    {
+        $where[$config[0]] = $config[1];
+        $data = $this->select($where, $set)->fetchAll();
+        if ($data) {
+            foreach ($data as &$v) {
+                if ($func) $v = call_user_func($func, $v);
+                $config[1] = $v[$config[2]];
+                $child = $this->tree(array(), $config, $func, $set);
+                if ($child) {
+                    $v['children'] = $child;
+                }
+            }
+        }
+        return $data;
+    }
+    public function show($where, $field = 'name', $str = '、')
+    {
+        $result = array();
+        $data = $this->select($where);
+        foreach ($data as $k => $v) {
+            $result[] = $v[$field];
+        }
+        $result = implode($str, $result);
+        return $result;
+    }
     public function __call($method, $data)
     {
         if (isset($this->config['request'][$method])) {

+ 12 - 0
src/Dever/Output.php

@@ -51,6 +51,18 @@ class Output
     }
     public static function handle(&$result)
     {
+        if (isset(Dever::config('setting')['output'])) {
+            foreach (Dever::config('setting')['output'] as $k => $v) {
+                if (isset($result[$k])) {
+                    if (is_array($v)) {
+                        $result[$v[0]] = $v[1][$result[$k]] ?? $result[$k];
+                    } else {
+                        $result[$v] = $result[$k];
+                    }
+                    unset($result[$k]);
+                }
+            }
+        }
         Debug::out($result);
         self::json($result);
         self::callback($result);

+ 0 - 20
src/Dever/Path.php

@@ -1,20 +0,0 @@
-<?php namespace Dever;
-class Path
-{
-    public static function get($path)
-    {
-        $path = self::data() . DEVER_PROJECT . DIRECTORY_SEPARATOR . $path;
-        $dir = dirname($path);
-        if (!is_dir($dir)) {
-            mkdir($dir, 0777, true);
-        }
-        return $path;
-    }
-    public static function data()
-    {
-        if (isset(Config::get('setting')['data'])) {
-            return Config::get('setting')['data'];
-        }
-        return DEVER_PROJECT_PATH . 'data' . DIRECTORY_SEPARATOR;
-    }
-}

+ 1 - 1
src/Dever/Project.php

@@ -4,7 +4,7 @@ class Project
     protected static $content = array();
     public static function init()
     {
-        $file = Path::get('app.php');
+        $file = File::get('app.php');
         if (!self::$content) {
             if (is_file($file)) {
                 require $file;

+ 17 - 7
src/Dever/Route.php

@@ -51,14 +51,12 @@ class Route
         }
         if (strpos($uri, '/')) {
             $temp = explode('/', $uri);
-            $project_name = $temp[0];
+            $app = $temp[0];
+            $uri = str_replace($app . '/', '', $uri);
         } else {
-            $project_name = DEVER_APP_NAME;
-        }
-        $project = Project::load($project_name);
-        if (!$project) {
-            Output::error('project not exists:' . $project_name);
+            $app = DEVER_APP_NAME;
         }
+        $project = Project::load($app);
         if (!$uri) {
             return $project['url'];
         }
@@ -89,6 +87,18 @@ class Route
         }
         return $project['url'] . self::$type . $uri;
     }
+    public static function host()
+    {
+        $temp = explode('/', DEVER_PROJECT_PATH);
+        $name = '/' . $temp[count($temp)-2] . '/';
+        $host = DEVER_PROTO . '://' . $_SERVER['HTTP_HOST'];
+        if (strpos($_SERVER['REQUEST_URI'], $name) === 0) {
+            $host .= $name;
+        } else {
+            $host .= '/';
+        }
+        return $host;
+    }
     public static function get()
     {
         if (!self::command()) {
@@ -142,7 +152,7 @@ class Route
     public static function match()
     {
         if (!isset(self::$data['l'])) {
-            Output::error('route not exists');
+            self::$data['l'] = '';
         }
         if ($route = Config::get('setting')['route']) {
             $value = self::$data['l'];

+ 1 - 2
src/Dever/Session.php

@@ -1,5 +1,4 @@
 <?php namespace Dever;
-use Dever;
 use Dever\Helper\Secure;
 class Session
 {
@@ -130,7 +129,7 @@ class Session
     private function _initFile()
     {
         $this->id = md5($this->key);
-        $this->file = Dever::path('session') . $this->id;
+        $this->file = File::get('session/' . $this->id);
         if (is_file($this->file)) {
             $this->data = unserialize(file_get_contents($this->file));
             return;

+ 42 - 10
src/Dever/Sql.php

@@ -43,7 +43,7 @@ class Sql
         $field = '`' . $name . '` ' . strtoupper($set['type']);
         if ($name == 'id') {
             $field .= 'UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL';
-        } elseif (isset($set['default']) && $set['default']) {
+        } elseif (isset($set['default'])) {
             $field .= ' NOT NULL DEFAULT "' . $set['default'] . '"';
         } elseif (strpos($set['type'], 'int') !== false || strpos($set['type'], 'float') !== false || strpos($set['type'], 'decimal') !== false || strpos($set['type'], 'double') !== false) {
             $field .= ' NOT NULL DEFAULT 0';
@@ -61,11 +61,11 @@ class Sql
         $alter = 'ALTER TABLE `' . $table . '` ';
         foreach ($data as $v) {
             $field = $v['Field'];
-            if ($field != 'id') {
+            if ($field != 'id' && $field != 'cdate') {
                 $set = $struct[$field] ?? false;
                 if ($set) {
                     if ($set['type'] != $v['Type'] || (isset($set['default']) && $set['default'] != $v['Default'])) {
-                        $sql[] = $alter . ' CHANGE `' . $field . '` `' . $field . '` ' . self::createField($field, $set);
+                        $sql[] = $alter . ' CHANGE `' . $field . '` ' . self::createField($field, $set);
                     } else {
                         unset($struct[$field]);
                     }
@@ -105,15 +105,34 @@ class Sql
     {
         $state = true;
         foreach ($index as $k => $v) {
-            if ($v['Key_name'] == 'PRIMARY' && $v['Column_name'] == 'cdate') {
+            if ($v['Key_name'] == 'PRIMARY' && $v['Column_name'] == $partition['field']) {
                 $state = false;
             }
         }
-        $sql = '(PARTITION p'.date('Ymd',$partition).' VALUES LESS THAN ('.$partition.'))';
+        $alter = '';
         if ($state) {
-            return 'ALTER TABLE `' . $table . '` DROP PRIMARY KEY, ADD PRIMARY KEY (`id`, `cdate`) USING BTREE;ALTER TABLE `' . $table . '` PARTITION BY RANGE (cdate) ' . $sql;
+            $alter = 'ALTER TABLE `' . $table . '` DROP PRIMARY KEY, ADD PRIMARY KEY (`id`, `'.$partition['field'].'`) USING BTREE;ALTER TABLE `' . $table . '` PARTITION BY '.strtoupper($partition['type']).' ('.$partition['field'].') ';
         } else {
-            return 'ALTER TABLE `' . $table . '` ADD PARTITION ' . $sql;
+            $alter = 'ALTER TABLE `' . $table . '` ADD PARTITION ';
+        }
+        if ($partition['type'] == 'range' || $partition['type'] == 'time') {
+            $name = $partition['value'];
+            if ($partition['type'] == 'time') {
+                $name = date('Ymd', $name);
+            }
+            $sql = 'PARTITION p'.$name.' VALUES LESS THAN ('.$partition['value'].')';
+            return $alter . '('.$sql.')';
+        } elseif ($partition['type'] == 'list') {
+            $sql = '';
+            foreach ($partition['value'] as $k => $v) {
+                $sql = 'PARTITION p'.$v['name'].' VALUES IN ('.$v['value'].')';
+            }
+            return $alter . '('.$sql.')';
+        } elseif ($partition['type'] == 'hash' || $partition['type'] == 'key') {
+            if ($state) {
+                return $alter . 'PARTITIONS ' . $partition['value'];
+            }
+            return self::desc($table);
         }
     }
     public static function select($table, $param, &$bind, $set = array(), $field = array(), $lock = false)
@@ -199,7 +218,21 @@ class Sql
         $p = ':'.$k.$i;
         $k = '`'.$k.'`';
         if (strpos($e,'in') !== false) {
-            $s .= $k.$e.'('.$p.')';
+            if (!is_array($v)) {
+                $v = explode(',', $v);
+            }
+            $t = '';
+            foreach ($v as $ti => $tj) {
+                if ($ti > 0) {
+                    $ti = $p.'_'.$ti;
+                    $t .= ','.$ti;
+                    $b[$ti] = $tj;
+                } else {
+                    $v = $tj;
+                    $t .= $p;
+                }
+            }
+            $s .= $k.' ' .$e.' ('.$t.')';
         } elseif ($e == 'like') {
             $s .= 'instr('.$k.','.$p.')'.' > 0';
         } elseif ($e == 'group') {
@@ -225,8 +258,7 @@ class Sql
             $sql .= '`' . $k . '`=:' . $k . ',';
             $bind[':'.$k] = $v;
         }
-        $sql = rtrim($sql, ',');
-        return $sql;
+        return rtrim($sql, ',');
     }
     public static function update($table, $param, $data, &$bind, $field)
     {

+ 9 - 5
src/Dever/Store/Base.php

@@ -8,18 +8,22 @@ class Base
     protected static $instance;
     public static function getInstance($key, $setting, $partition)
     {
+        if (isset($partition['create']) && $partition['create'] && isset($partition['database']) && $partition['database']) {
+            $key .= $partition['database'];
+            $setting['name'] .= '_' . $partition['database'];
+        }
         if (empty(static::$instance[$key])) {
-            static::$instance[$key] = new static($setting, $partition);
+            static::$instance[$key] = new static($setting);
         }
         return static::$instance[$key];
     }
-    public function __construct($setting, $partition)
+    public function __construct($setting)
     {
         if (isset($setting[0])) {
-            $this->read = $this->connect($setting[0], $partition);
-            $this->update = $this->connect($setting[1], $partition);
+            $this->read = $this->connect($setting[0]);
+            $this->update = $this->connect($setting[1]);
         } else {
-            $this->read = $this->connect($setting, $partition);
+            $this->read = $this->connect($setting);
             $this->update =&$this->read;
         }
     }

+ 3 - 6
src/Dever/Store/Pdo.php

@@ -4,11 +4,8 @@ use Dever\Output;
 use Dever\Sql;
 class Pdo extends Base
 {
-    protected function connect($setting, $partition)
+    protected function connect($setting)
     {
-        if (isset($partition['database'])) {
-            $setting['name'] .= '_' . $partition['database'];
-        }
         $this->type = $setting['type'];
         $handle = false;
         if (strpos($setting['host'], ':') !== false) {
@@ -39,7 +36,7 @@ class Pdo extends Base
         } catch (\PDOException $e) {
             if (strstr($e->getMessage(), 'Unknown database')) {
                 $this->create($setting);
-                $this->connect($setting, $partition);
+                $this->connect($setting);
             } else {
                 Output::error($e->getMessage());
             }
@@ -100,7 +97,7 @@ class Pdo extends Base
                 $handle = $this->$method->query($sql);
             }
         } catch (\PDOException $exception) {
-            $this->error($exception->getMessage(), $sql);
+            $this->error(array('sql' => $sql, 'msg' => $exception->getMessage()));
         }
         if (Debug::$shell) {
             $this->sql($sql, $bind);

+ 1 - 1
src/Dever/View.php

@@ -10,7 +10,7 @@ class View
         $template = self::template();
         $path = $path . DIRECTORY_SEPARATOR . $template . DIRECTORY_SEPARATOR;
         $host = $project['url'] . $path;
-        $compile = Path::get('compile' . DIRECTORY_SEPARATOR . $app . DIRECTORY_SEPARATOR . $template . DIRECTORY_SEPARATOR . $file . '.php');
+        $compile = File::get('compile' . DIRECTORY_SEPARATOR . $app . DIRECTORY_SEPARATOR . $template . DIRECTORY_SEPARATOR . $file . '.php');
         Debug::add($compile, 'template');
         if (Debug::$shell) {
             return $data;