rabin преди 1 година
родител
ревизия
5de300a97d

+ 6 - 0
boot.php

@@ -269,4 +269,10 @@ class Dever
             return is_file($file);
         }
     }
+    public static function subdir($dir)
+    {
+        return array_filter(scandir($dir), function($file) use ($dir) {
+            return is_dir($dir . '/' . $file) && $file !== '.' && $file !== '..';
+        });
+    }
 }

+ 0 - 541
src/Ares333/Curl.php

@@ -1,541 +0,0 @@
-<?php /** @noinspection PhpUnused */
-
-namespace Ares333\Curl;
-
-/**
- * The best curl-multi library.
- *
- * @author Ares
- */
-class Curl
-{
-
-    public $maxThread = 10;
-
-    // Max try times on curl error
-    public $maxTry = 0;
-
-    // Global CURLOPT_*
-    public $opt = array();
-
-    // Global config.
-    public $cache = array(
-        'enable' => false,
-        /**
-         * The level of compression.
-         * Can be given as 0 for no compression up to 9 for maximum compression.
-         * 6 is a good choice.
-         */
-        'compress' => 0,
-        'dir' => null,
-        'expire' => 86400,
-        // Different post data different cache file when enabled.
-        'verifyPost' => false
-    );
-
-    // stack or queue
-    public $taskPoolType = 'queue';
-
-    // Emitted when new tasks are needed.
-    public $onTask;
-
-    // Emitted on IO event.At least 1 second interval.
-    public $onInfo;
-
-    // Emitted on curl error.
-    public $onFail;
-
-    // Emitted on IO event.
-    public $onEvent;
-
-    protected $_mh;
-
-    protected $_taskPool = array();
-
-    // Task pool with higher priority.
-    protected $_taskPoolAhead = array();
-
-    protected $_taskRunning = array();
-
-    // Failed tasks retrying.
-    protected $_taskFailed = array();
-
-    protected $_stop = false;
-
-    // Running info.
-    protected $_info = array(
-        'all' => array(
-            'downloadSpeed' => 0,
-            'bodySize' => 0,
-            'headerSize' => 0,
-            // Active requests count
-            'activeNum' => 0,
-            // Finished requests count on the queue
-            'queueNum' => 0,
-            // Finished tasks count including failed tasks and tasks using cache.
-            'finishNum' => 0,
-            // Cache used count
-            'cacheNum' => 0,
-            // Count of tasks failed after retry
-            'failNum' => 0,
-            // Count of tasks added to the poll
-            'taskNum' => 0,
-            // $this->taskRunning count.taskRunning >= active + queue
-            'taskRunningNum' => 0,
-            // $this->taskPool count
-            'taskPoolNum' => 0,
-            // $this->taskFail count
-            'taskFailNum' => 0
-        ),
-        'running' => array()
-    );
-
-    protected $_onInfoLastTime = 0;
-
-    protected $_downloadSpeedStartTime;
-
-    protected $_downloadSpeedTotalSize = 0;
-
-    protected $_downloadSpeedList = array();
-
-    /**
-     * Add a task to the taskPool
-     *
-     * @param array $item
-     *            array $item[opt] CURLOPT_* for current task
-     *            mixed $item[args] Args for callbacks
-     *            array $item[cache]
-     * @param mixed $onSuccess
-     *            Callback for response
-     * @param mixed $onFail
-     *            Callback for curl error
-     * @param bool $ahead
-     * @return self
-     */
-    public function add(array $item, $onSuccess = null, $onFail = null, $ahead = null)
-    {
-        if (! isset($ahead)) {
-            $ahead = false;
-        }
-        if (! isset($item['opt'])) {
-            $item['opt'] = array();
-        }
-        if (! isset($item['args'])) {
-            $item['args'] = null;
-        }
-        if (! isset($item['cache'])) {
-            $item['cache'] = array();
-        }
-        if (! isset($item['opt'][CURLOPT_URL])) {
-            $item['opt'][CURLOPT_URL] = '';
-        }
-        $item['opt'][CURLOPT_URL] = trim($item['opt'][CURLOPT_URL]);
-        // replace space with + to avoid some curl problems
-        $item['opt'][CURLOPT_URL] = str_replace(' ', '+', $item['opt'][CURLOPT_URL]);
-        $parse = parse_url($item['opt'][CURLOPT_URL]);
-        $keys = array(
-            'scheme',
-            'host',
-            'port',
-            'user',
-            'pass',
-            'path',
-            'query',
-            'fragment'
-        );
-        foreach ($keys as $v) {
-            if (! isset($parse[$v])) {
-                $parse[$v] = '';
-            }
-        }
-        if ('' !== $parse['user']) {
-            $parse['user'] .= ':';
-            $parse['pass'] .= '@';
-        }
-        if ('' !== $parse['port']) {
-            $parse['host'] .= ':';
-        }
-        if ('' !== $parse['query']) {
-            $parse['path'] .= '?';
-        }
-        $parse['path'] = preg_replace('/\/+/', '/', $parse['path']);
-        strtolower($parse['scheme']);
-        strtolower($parse['host']);
-        $item['opt'][CURLOPT_URL] = $parse['scheme'] . '://' . $parse['user'] . $parse['pass'] . $parse['host'] .
-            $parse['port'] . $parse['path'] . $parse['query'];
-        $task = array();
-        $task['args'] = $item['args'];
-        $task['opt'] = $item['opt'];
-        $task['cache'] = $item['cache'];
-        $task['onSuccess'] = $onSuccess;
-        $task['onFail'] = $onFail;
-        $task['tried'] = 0;
-        $task['ch'] = null;
-        // $task['fileMeta'] is used for download cache and __wakeup
-        if (isset($task['opt'][CURLOPT_FILE])) {
-            $task['fileMeta'] = stream_get_meta_data($task['opt'][CURLOPT_FILE]);
-        } else {
-            $task['fileMeta'] = array();
-        }
-        // add
-        if (true == $ahead) {
-            $this->_taskPoolAhead[] = $task;
-        } else {
-            $this->_taskPool[] = $task;
-        }
-        $this->_info['all']['taskNum'] ++;
-        return $this;
-    }
-
-    /**
-     *
-     * @return array[] tasks
-     */
-    public function stop()
-    {
-        $this->_stop = true;
-        $tasks = array();
-        foreach (array(
-            '_taskPoolAhead',
-            '_taskRunning',
-            '_taskPool'
-        ) as $v) {
-            foreach ($this->$v as $k1 => $v1) {
-                if (isset($v1['opt'][CURLOPT_FILE]) && is_resource($v1['opt'][CURLOPT_FILE])) {
-                    fclose($v1['opt'][CURLOPT_FILE]);
-                    if (is_file($v1['fileMeta']['uri'])) {
-                        unlink($v1['fileMeta']['uri']);
-                    }
-                }
-                if (is_resource($v1['ch'])) {
-                    curl_multi_remove_handle($this->_mh, $v1['ch']);
-                    curl_close($v1['ch']);
-                }
-                unset($this->{$v}[$k1]);
-                $tasks[] = $v1;
-            }
-        }
-        $this->_downloadSpeedStartTime = null;
-        $this->_onInfoLastTime = 0;
-        $this->_downloadSpeedTotalSize = 0;
-        $this->_downloadSpeedList = array();
-        $this->_info['all']['downloadSpeed'] = 0;
-        $this->_info['all']['activeNum'] = 0;
-        $this->_info['all']['queueNum'] = 0;
-        return $tasks;
-    }
-
-    public function start()
-    {
-        $this->_stop = false;
-        $this->_mh = curl_multi_init();
-        $this->runTask();
-        do {
-            $this->exec();
-            $this->onInfo();
-            curl_multi_select($this->_mh);
-            if (isset($this->onEvent)) {
-                call_user_func($this->onEvent, $this);
-            }
-            if ($this->_stop) {
-                break;
-            }
-            while (false != ($curlInfo = curl_multi_info_read($this->_mh, $this->_info['all']['queueNum']))) {
-                $ch = $curlInfo['handle'];
-                $task = $this->_taskRunning[(int) $ch];
-                $info = curl_getinfo($ch);
-                $this->_info['all']['bodySize'] += $info['size_download'];
-                $this->_info['all']['headerSize'] += $info['header_size'];
-                $param = array();
-                $param['info'] = $info;
-                $param['curl'] = $this;
-                if ($curlInfo['result'] == CURLE_OK) {
-                    if (! isset($task['opt'][CURLOPT_FILE])) {
-                        $param['body'] = curl_multi_getcontent($ch);
-                        if (isset($task['opt'][CURLOPT_HEADER])) {
-                            $param = array_merge($param, $this->parseResponse($param['body']));
-                        }
-                    }
-                }
-                $curlError = curl_error($ch);
-                curl_multi_remove_handle($this->_mh, $ch);
-                curl_close($ch);
-                if (isset($task['opt'][CURLOPT_FILE])) {
-                    fclose($task['opt'][CURLOPT_FILE]);
-                }
-                if ($curlInfo['result'] == CURLE_OK) {
-                    $this->onProcess($task, $param);
-                }
-                // Handle error
-                if ($curlInfo['result'] !== CURLE_OK) {
-                    if ($task['tried'] >= $this->maxTry) {
-                        $param['errorCode'] = $curlInfo['result'];
-                        $param['errorMsg'] = $curlError;
-                        if (isset($task['onFail'])) {
-                            call_user_func($task['onFail'], $param, $task['args']);
-                        } elseif (isset($this->onFail)) {
-                            call_user_func($this->onFail, $param, $task['args']);
-                        } else {
-                            user_error("Curl error($curlInfo[result]) $info[url]", E_USER_WARNING);
-                        }
-                        $this->_info['all']['failNum'] ++;
-                    } else {
-                        $task['tried'] ++;
-                        $this->_taskFailed[] = $task;
-                        $this->_info['all']['taskNum'] ++;
-                    }
-                }
-                unset($this->_taskRunning[(int) $ch]);
-                $this->_info['all']['finishNum'] ++;
-                $this->_downloadSpeedTotalSize += $info['size_download'] + $info['header_size'];
-                $this->runTask();
-                $this->exec();
-                $this->onInfo();
-                if (isset($this->onEvent)) {
-                    call_user_func($this->onEvent, $this);
-                }
-                if ($this->_stop) {
-                    break 2;
-                }
-            }
-        } while ($this->_info['all']['activeNum'] || $this->_info['all']['queueNum'] || ! empty($this->_taskFailed) ||
-            ! empty($this->_taskRunning) || ! empty($this->_taskPool));
-        $this->onInfo(true);
-        curl_multi_close($this->_mh);
-        $this->_mh = null;
-    }
-
-    public function parseResponse($response)
-    {
-        $res = array();
-        preg_match_all("/HTTP\/.+(?=\r\n\r\n)/Usm", $response, $res['header']);
-        $res['header'] = $res['header'][0];
-        $pos = 0;
-        foreach ($res['header'] as $v) {
-            $pos += strlen($v) + 4;
-        }
-        $res['body'] = substr($response, $pos);
-        return $res;
-    }
-
-    /**
-     * Call $this->onInfo
-     *
-     * @param bool $isLast
-     *            Is last output?
-     */
-    protected function onInfo($isLast = false)
-    {
-        $now = time();
-        if (! isset($this->_downloadSpeedStartTime)) {
-            $this->_downloadSpeedStartTime = $now;
-        }
-        if (($isLast || $now - $this->_onInfoLastTime > 0) && isset($this->onInfo)) {
-            $this->_info['all']['taskPoolNum'] = count($this->_taskPool);
-            $this->_info['all']['taskPoolNum'] += count($this->_taskPoolAhead);
-            $this->_info['all']['taskRunningNum'] = count($this->_taskRunning);
-            $this->_info['all']['taskFailNum'] = count($this->_taskFailed);
-            // running
-            $this->_info['running'] = array();
-            foreach ($this->_taskRunning as $k => $v) {
-                $this->_info['running'][$k] = curl_getinfo($v['ch']);
-            }
-            if ($now - $this->_downloadSpeedStartTime > 0) {
-                if (count($this->_downloadSpeedList) > 10) {
-                    array_shift($this->_downloadSpeedList);
-                }
-                $this->_downloadSpeedList[] = round(
-                    $this->_downloadSpeedTotalSize / ($now - $this->_downloadSpeedStartTime));
-                $this->_info['all']['downloadSpeed'] = round(
-                    array_sum($this->_downloadSpeedList) / count($this->_downloadSpeedList));
-            }
-            if ($now - $this->_downloadSpeedStartTime > 3) {
-                $this->_downloadSpeedTotalSize = 0;
-                $this->_downloadSpeedStartTime = $now;
-            }
-            call_user_func($this->onInfo, $this->_info, $this, $isLast);
-            $this->_onInfoLastTime = $now;
-        }
-    }
-
-    protected function exec()
-    {
-        while (curl_multi_exec($this->_mh, $this->_info['all']['activeNum']) === CURLM_CALL_MULTI_PERFORM) {
-            continue;
-        }
-    }
-
-    protected function runTask()
-    {
-        $c = $this->maxThread - count($this->_taskRunning);
-        while ($c > 0) {
-            $task = null;
-            // search failed first
-            if (! empty($this->_taskFailed)) {
-                $task = array_pop($this->_taskFailed);
-            } else {
-                // onTask
-                if (empty($this->_taskPool) && empty($this->_taskPoolAhead) && isset($this->onTask)) {
-                    call_user_func($this->onTask, $this);
-                }
-                if (! empty($this->_taskPoolAhead)) {
-                    $task = array_shift($this->_taskPoolAhead);
-                } elseif (! empty($this->_taskPool)) {
-                    if ($this->taskPoolType == 'stack') {
-                        $task = array_pop($this->_taskPool);
-                    } else {
-                        $task = array_shift($this->_taskPool);
-                    }
-                }
-            }
-            $cache = null;
-            if (isset($task)) {
-                $cache = $this->cache($task);
-                if (null !== $cache) {
-                    // download task
-                    if (isset($task['opt'][CURLOPT_FILE])) {
-                        if (flock($task['opt'][CURLOPT_FILE], LOCK_EX)) {
-                            fwrite($task['opt'][CURLOPT_FILE], $cache['body']);
-                            flock($task['opt'][CURLOPT_FILE], LOCK_UN);
-                        }
-                        fclose($task['opt'][CURLOPT_FILE]);
-                        unset($cache['body']);
-                    }
-                    $cache['curl'] = $this;
-                    $this->onProcess($task, $cache);
-                    $this->_info['all']['cacheNum'] ++;
-                    $this->_info['all']['finishNum'] ++;
-                    $this->onInfo();
-                    if (isset($this->onEvent)) {
-                        call_user_func($this->onEvent, $this);
-                    }
-                    if ($this->_stop) {
-                        break;
-                    }
-                } else {
-                    $task = $this->initTask($task);
-                    $this->_taskRunning[(int) $task['ch']] = $task;
-                    curl_multi_add_handle($this->_mh, $task['ch']);
-                }
-            } else {
-                break;
-            }
-            if (null == $cache) {
-                $c --;
-            }
-        }
-    }
-
-    /**
-     * Process response
-     *
-     * @param array $task
-     * @param array $param
-     */
-    protected function onProcess($task, $param)
-    {
-        $userRes = array();
-        if (isset($task['onSuccess'])) {
-            $userRes = call_user_func($task['onSuccess'], $param, $task['args']);
-        }
-        if (isset($userRes['cache'])) {
-            $task['cache'] = array_merge($task['cache'], $userRes['cache']);
-        }
-        // write cache
-        if (! isset($param['cache'])) {
-            $this->cache($task, $param);
-        }
-    }
-
-    /**
-     *
-     * @param string $url
-     * @param string|array $post
-     * @return string
-     */
-    public function getCacheFile($url, $post = null)
-    {
-        $suffix = '';
-        if (isset($post)) {
-            if (is_array($post)) {
-                ksort($post);
-                $post = http_build_query($post);
-            }
-            $suffix .= $post;
-        }
-        $key = md5($url . $suffix);
-        return substr($key, 0, 3) . '/' . substr($key, 3, 3) . '/' . substr($key, 6);
-    }
-
-    /**
-     * Set or get file cache.
-     *
-     * @param $task
-     * @param array|null $data
-     * @return mixed
-     */
-    protected function cache($task, $data = null)
-    {
-        $config = array_merge($this->cache, $task['cache']);
-        if (! $config['enable']) {
-            return null;
-        }
-        if (! isset($config['dir'])) {
-            user_error('cache dir is not defined', E_USER_WARNING);
-            return null;
-        }
-        $url = $task['opt'][CURLOPT_URL];
-        $post = null;
-        if (true == $config['verifyPost'] && ! empty($task['opt'][CURLOPT_POSTFIELDS])) {
-            $post = $task['opt'][CURLOPT_POSTFIELDS];
-        }
-        $file = rtrim($config['dir'], '/') . '/';
-        $file .= $this->getCacheFile($url, $post);
-        if (! isset($data)) {
-            if (file_exists($file)) {
-                $time = time();
-                $mtime = filemtime($file);
-                if ($time - $mtime < $config['expire']) {
-                    return unserialize(gzuncompress(file_get_contents($file)));
-                }
-            }
-        } else {
-            if (! isset($data['cache'])) {
-                $data['cache'] = array();
-            }
-            $data['cacheFile'] = $file;
-            unset($data['curl']);
-            $dir = dirname($file);
-            $dir1 = dirname($dir);
-            if (! is_dir($dir1)) {
-                mkdir($dir1);
-            }
-            if (! is_dir($dir)) {
-                mkdir($dir);
-            }
-            // Cache response from downloaded file.
-            if (isset($task['fileMeta']['uri'])) {
-                $data['body'] = file_get_contents($task['fileMeta']['uri']);
-            }
-            file_put_contents($file, gzcompress(serialize($data), $config['compress']), LOCK_EX);
-        }
-        return null;
-    }
-
-    /**
-     *
-     * @param array $task
-     * @return array
-     */
-    protected function initTask($task)
-    {
-        $task['ch'] = curl_init();
-        $opt = $this->opt;
-        foreach ($task['opt'] as $k => $v) {
-            $opt[$k] = $v;
-        }
-        curl_setopt_array($task['ch'], $opt);
-        $task['opt'] = $opt;
-        return $task;
-    }
-}

+ 0 - 916
src/Ares333/Toolkit.php

@@ -1,916 +0,0 @@
-<?php /** @noinspection PhpUnused */
-
-namespace Ares333\Curl;
-
-/**
- * Toolkit for Curl
- */
-class Toolkit
-{
-
-    // Curl instance
-    protected $_curl;
-
-    function setCurl(Curl $curl = null)
-    {
-        $this->_curl = $curl;
-        if (! isset($this->_curl)) {
-            $this->_curl = new Curl();
-            $this->_curl->opt = array(
-                CURLINFO_HEADER_OUT => true,
-                CURLOPT_HEADER => true,
-                CURLOPT_CONNECTTIMEOUT => 10,
-                CURLOPT_TIMEOUT => 30,
-                CURLOPT_AUTOREFERER => true,
-                CURLOPT_USERAGENT => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36',
-                CURLOPT_RETURNTRANSFER => true,
-                CURLOPT_FOLLOWLOCATION => true,
-                CURLOPT_SSL_VERIFYHOST => false,
-                CURLOPT_SSL_VERIFYPEER => false,
-                CURLOPT_MAXREDIRS => 5
-            );
-            // default fail callback
-            $this->_curl->onFail = array(
-                $this,
-                'onFail'
-            );
-            // default info callback
-            $this->_curl->onInfo = array(
-                $this,
-                'onInfo'
-            );
-        }
-    }
-
-    /**
-     * Output curl error information
-     *
-     * @param array $error
-     */
-    function onFail($error)
-    {
-        $msg = "Curl error ($error[errorCode]). $error[errorMsg], url=" . $error['info']['url'];
-        if ($this->_curl->onInfo == array(
-            $this,
-            'onInfo'
-        )) {
-            $this->onInfo($msg . "\n");
-        } else {
-            echo "\n$msg\n\n";
-        }
-    }
-
-    /**
-     *
-     * Add delayed and formatted output or output with running information.
-     *
-     * @param array|string $info
-     *            array('all'=>array(),'running'=>array())
-     */
-    function onInfo($info)
-    {
-        $isLast = null;
-        if (func_num_args() == 3) {
-            $isLast = func_get_arg(2);
-        }
-        static $meta = array(
-            'downloadSpeed' => array(
-                0,
-                'SPD'
-            ),
-            'downloadSize' => array(
-                0,
-                'DWN'
-            ),
-            'finishNum' => array(
-                0,
-                'FNH'
-            ),
-            'cacheNum' => array(
-                0,
-                'CACHE'
-            ),
-            'taskRunningNum' => array(
-                0,
-                'RUN'
-            ),
-            'activeNum' => array(
-                0,
-                'ACTIVE'
-            ),
-            'taskPoolNum' => array(
-                0,
-                'POOL'
-            ),
-            'queueNum' => array(
-                0,
-                'QUEUE'
-            ),
-            'taskNum' => array(
-                0,
-                'TASK'
-            ),
-            'failNum' => array(
-                0,
-                'FAIL'
-            )
-        );
-        static $isFirst = true;
-        static $buffer = '';
-        if (is_string($info)) {
-            $buffer .= $info;
-            return;
-        }
-        $all = $info['all'];
-        $all['downloadSpeed'] = round($all['downloadSpeed'] / 1024) . 'KB';
-        $all['downloadSize'] = round(($all['headerSize'] + $all['bodySize']) / 1024 / 1024) . "MB";
-        // clean
-        foreach (array_keys($meta) as $v) {
-            if (! array_key_exists($v, $all)) {
-                unset($meta[$v]);
-            }
-        }
-        $content = '';
-        $lenPad = 2;
-        $caption = '';
-        foreach (array(
-            'meta'
-        ) as $name) {
-            foreach ($$name as $k => $v) {
-                if (! isset($all[$k])) {
-                    continue;
-                }
-                if (mb_strlen($all[$k]) > $v[0]) {
-                    $v[0] = mb_strlen($all[$k]);
-                }
-                if (PHP_OS == 'Linux') {
-                    if (mb_strlen($v[1]) > $v[0]) {
-                        $v[0] = mb_strlen($v[1]);
-                    }
-                    $caption .= sprintf('%-' . ($v[0] + $lenPad) . 's', $v[1]);
-                    $content .= sprintf('%-' . ($v[0] + $lenPad) . 's', $all[$k]);
-                } else {
-                    $format = '%-' . ($v[0] + strlen($v[1]) + 1 + $lenPad) . 's';
-                    $content .= sprintf($format, $v[1] . ':' . $all[$k]);
-                }
-                ${$name}[$k] = $v;
-            }
-        }
-        $str = '';
-        if (PHP_OS == 'Linux') {
-            if ($isFirst) {
-                $str .= "\n";
-                $isFirst = false;
-            }
-            $str .= "\33[A\r\33[K" . $caption . "\n\r\33[K" . rtrim($content);
-        } else {
-            $str .= "\r" . rtrim($content);
-        }
-        echo $str;
-        if ($isLast) {
-            echo "\n";
-        }
-        if ('' !== $buffer) {
-            if ($isLast) {
-                echo trim($buffer) . "\n";
-            } else {
-                echo "\n" . trim($buffer) . "\n\n";
-            }
-            $buffer = '';
-        }
-    }
-
-    /**
-     * Html encoding transform
-     *
-     * @param string $html
-     * @param string $in
-     *            detect automatically if not set
-     * @param string $out
-     *            default UTF-8
-     * @param string $mode
-     *            auto|iconv|mb_convert_encoding
-     * @return string
-     */
-    function htmlEncode($html, $in = null, $out = null, $mode = 'auto')
-    {
-        $valid = array(
-            'auto',
-            'iconv',
-            'mb_convert_encoding'
-        );
-        if (! isset($out)) {
-            $out = 'UTF-8';
-        }
-        if (! in_array($mode, $valid)) {
-            user_error('invalid mode, mode=' . $mode, E_USER_ERROR);
-        }
-        $if = function_exists('mb_convert_encoding');
-        $if = $if && ($mode == 'auto' || $mode == 'mb_convert_encoding');
-        $func = null;
-        if (function_exists('iconv') && ($mode == 'auto' || $mode == 'iconv')) {
-            $func = 'iconv';
-        } elseif ($if) {
-            $func = 'mb_convert_encoding';
-        } else {
-            user_error('encode failed, php extension not found', E_USER_ERROR);
-        }
-        $pattern = '/(<meta[^>]*?charset=(["\']?))([a-z\d_\-]*)(\2[^>]*?>)/is';
-        if (! isset($in)) {
-            $n = preg_match($pattern, $html, $in);
-            if ($n > 0) {
-                $in = $in[3];
-            } else {
-                if (function_exists('mb_detect_encoding')) {
-                    $in = mb_detect_encoding($html);
-                } else {
-                    $in = null;
-                }
-            }
-        }
-        if (isset($in)) {
-            $old = error_reporting(error_reporting() & ~ E_NOTICE);
-            $html = call_user_func($func, $in, $out . '//IGNORE', $html);
-            error_reporting($old);
-            $html = preg_replace($pattern, "\\1$out\\4", $html, 1);
-        } else {
-            user_error('source encoding is unknown', E_USER_ERROR);
-        }
-        return $html;
-    }
-
-    /**
-     * content between start and end
-     *
-     * @param string $str
-     * @param string $start
-     * @param string $end
-     * @param bool $greed
-     * @return string
-     */
-    function between($str, $start, $end = null, $greed = true)
-    {
-        if (isset($start)) {
-            $pos1 = strpos($str, $start);
-        } else {
-            $pos1 = 0;
-        }
-        if (isset($end)) {
-            if ($greed) {
-                $pos2 = strrpos($str, $end);
-            } else {
-                $pos2 = strpos($str, $end, $pos1);
-            }
-        } else {
-            $pos2 = strlen($str);
-        }
-        if (false === $pos1 || false === $pos2 || $pos2 < $pos1) {
-            return '';
-        }
-        $len = strlen($start);
-        return substr($str, $pos1 + $len, $pos2 - $pos1 - $len);
-    }
-
-    /**
-     *
-     * @param string $url
-     * @return boolean
-     */
-    function isUrl($url)
-    {
-        $url = ltrim($url);
-        return in_array(substr($url, 0, 7), array(
-            'http://',
-            'https:/'
-        ));
-    }
-
-    /**
-     * Clean up and format
-     *
-     * @param string $url
-     * @return string|null
-     */
-    function formatUrl($url)
-    {
-        if (! $this->isUrl($url)) {
-            return null;
-        }
-        $url = trim($url);
-        $url = str_replace(' ', '+', $url);
-        $parse = parse_url($url);
-        strtolower($parse['scheme']);
-        strtolower($parse['host']);
-        return $this->buildUrl($parse);
-    }
-
-    /**
-     *
-     * @param array $parse
-     * @return string
-     */
-    function buildUrl(array $parse)
-    {
-        $keys = array(
-            'scheme',
-            'host',
-            'port',
-            'user',
-            'pass',
-            'path',
-            'query',
-            'fragment'
-        );
-        foreach ($keys as $v) {
-            if (! isset($parse[$v])) {
-                $parse[$v] = '';
-            }
-        }
-        if ('' !== $parse['scheme']) {
-            $parse['scheme'] .= '://';
-        }
-        if ('' !== $parse['user']) {
-            $parse['user'] .= ':';
-            $parse['pass'] .= '@';
-        }
-        if ('' !== $parse['port']) {
-            $parse['host'] .= ':';
-        }
-        if ('' !== $parse['query']) {
-            $parse['path'] .= '?';
-            // sort
-            $query = array();
-            parse_str($parse['query'], $query);
-            asort($query);
-            $parse['query'] = http_build_query($query);
-        }
-        if ('' !== $parse['fragment']) {
-            $parse['query'] .= '#';
-        }
-        $parse['path'] = preg_replace('/\/+/', '/', $parse['path']);
-        return $parse['scheme'] . $parse['user'] . $parse['pass'] . $parse['host'] . $parse['port'] . $parse['path'] .
-            $parse['query'] . $parse['fragment'];
-    }
-
-    /**
-     *
-     * @param string $uri
-     * @param string $urlCurrent
-     *            Should be final url which was redirected by 3xx http code.
-     * @return string|null
-     */
-    function uri2url($uri, $urlCurrent)
-    {
-        if (empty($uri)) {
-            return $urlCurrent;
-        }
-        if ($this->isUrl($uri)) {
-            return $uri;
-        }
-        if (! $this->isUrl($urlCurrent)) {
-            return null;
-        }
-        // uri started with ?,#
-        if (0 === strpos($uri, '#') || 0 === strpos($uri, '?')) {
-            if (false !== ($pos = strpos($urlCurrent, '#'))) {
-                $urlCurrent = substr($urlCurrent, 0, $pos);
-            }
-            if (false !== ($pos = strpos($urlCurrent, '?'))) {
-                $urlCurrent = substr($urlCurrent, 0, $pos);
-            }
-            return $urlCurrent . $uri;
-        }
-        if (0 === strpos($uri, './')) {
-            $uri = substr($uri, 2);
-        }
-        $urlDir = $this->url2dir($urlCurrent);
-        if (0 === strpos($uri, '/')) {
-            $path = parse_url($urlDir, PHP_URL_PATH);
-            if (isset($path)) {
-                $len = 0 - strlen($path);
-            } else {
-                $len = strlen($urlDir);
-            }
-            return substr($urlDir, 0, $len) . $uri;
-        } else {
-            return $urlDir . $uri;
-        }
-    }
-
-    /**
-     *
-     * @param string $url
-     * @param string $urlCurrent
-     *            Should be final url which was redirected by 3xx http code.
-     * @return string|null
-     */
-    function url2uri($url, $urlCurrent)
-    {
-        if (! $this->isUrl($url)) {
-            return null;
-        }
-        $urlDir = $this->url2dir($urlCurrent);
-        $parse1 = parse_url($url);
-        $parse2 = parse_url($urlDir);
-        if (! array_key_exists('port', $parse1)) {
-            $parse1['port'] = null;
-        }
-        if (! array_key_exists('port', $parse2)) {
-            $parse2['port'] = null;
-        }
-        $eq = true;
-        foreach (array(
-            'scheme',
-            'host',
-            'port'
-        ) as $v) {
-            if (isset($parse1[$v]) && isset($parse2[$v])) {
-                if ($parse1[$v] != $parse2[$v]) {
-                    $eq = false;
-                    break;
-                }
-            }
-        }
-        $path = null;
-        if ($eq) {
-            $len = strlen($urlDir) - strlen(parse_url($urlDir, PHP_URL_PATH));
-            // relative path
-            $path1 = substr($url, $len + 1);
-            $path2 = substr($urlDir, $len + 1);
-            $arr1 = explode('/', $path1);
-            $arr2 = explode('/', $path2);
-            foreach ($arr1 as $k => $v) {
-                if (empty($v)) {
-                    continue;
-                }
-                if (array_key_exists($k, $arr2) && $v == $arr2[$k]) {
-                    unset($arr1[$k], $arr2[$k]);
-                } else {
-                    break;
-                }
-            }
-            $path = '';
-            foreach ($arr2 as $v) {
-                if (empty($v)) {
-                    continue;
-                }
-                $path .= '../';
-            }
-            $path .= implode('/', $arr1);
-        }
-        return $path;
-    }
-
-    /**
-     *
-     * @param string $url
-     *            Should be final url which was redirected by 3xx http code.
-     * @return string|null
-     */
-    function url2dir($url)
-    {
-        if (! $this->isUrl($url)) {
-            return null;
-        }
-        $parse = parse_url($url);
-        $urlDir = $url;
-        if (isset($parse['path'])) {
-            if ('/' != substr($urlDir, - 1)) {
-                $urlDir = dirname($urlDir) . '/';
-            }
-        } else {
-            if ('/' != substr($urlDir, - 1)) {
-                $urlDir .= '/';
-            }
-        }
-        return $urlDir;
-    }
-
-    /**
-     * Combine a base URL and a relative URL to produce a new
-     * absolute URL.
-     * The base URL is often the URL of a page,
-     * and the relative URL is a URL embedded on that page.
-     *
-     * This function implements the "absolutize" algorithm from
-     * the RFC3986 specification for URLs.
-     *
-     * This function supports multi-byte characters with the UTF-8 encoding,
-     * per the URL specification.
-     *
-     * Parameters:
-     * baseUrl the absolute base URL.
-     *
-     * url the relative URL to convert.
-     *
-     * Return values:
-     * An absolute URL that combines parts of the base and relative
-     * URLs, or FALSE if the base URL is not absolute or if either
-     * URL cannot be parsed.
-     *
-     *
-     * @param $url
-     * @param $urlCurrent
-     * @return bool|string
-     * @noinspection SpellCheckingInspection
-     */
-    function url2absolute($url, $urlCurrent)
-    {
-        // If relative URL has a scheme, clean path and return.
-        $r = $this->splitUrl($url);
-        if ($r === FALSE)
-            return FALSE;
-        if (! empty($r['scheme'])) {
-            if (! empty($r['path']) && $r['path'][0] == '/')
-                $r['path'] = $this->urlRemoveDotSegments($r['path']);
-            return $this->joinUrl($r);
-        }
-
-        // Make sure the base URL is absolute.
-        $b = $this->splitUrl($urlCurrent);
-        if ($b === FALSE || empty($b['scheme']) || empty($b['host']))
-            return FALSE;
-        $r['scheme'] = $b['scheme'];
-
-        // If relative URL has an authority, clean path and return.
-        if (isset($r['host'])) {
-            if (! empty($r['path']))
-                $r['path'] = $this->urlRemoveDotSegments($r['path']);
-            return $this->joinUrl($r);
-        }
-        unset($r['port']);
-        unset($r['user']);
-        unset($r['pass']);
-
-        // Copy base authority.
-        $r['host'] = $b['host'];
-        if (isset($b['port']))
-            $r['port'] = $b['port'];
-        if (isset($b['user']))
-            $r['user'] = $b['user'];
-        if (isset($b['pass']))
-            $r['pass'] = $b['pass'];
-
-        // If relative URL has no path, use base path
-        if (empty($r['path'])) {
-            if (! empty($b['path']))
-                $r['path'] = $b['path'];
-            if (! isset($r['query']) && isset($b['query']))
-                $r['query'] = $b['query'];
-            return $this->joinUrl($r);
-        }
-
-        // If relative URL path doesn't start with /, merge with base path
-        if ($r['path'][0] != '/') {
-            $base = mb_strrchr($b['path'], '/', TRUE, 'UTF-8');
-            if ($base === FALSE)
-                $base = '';
-            $r['path'] = $base . '/' . $r['path'];
-        }
-        $r['path'] = $this->urlRemoveDotSegments($r['path']);
-        return $this->joinUrl($r);
-    }
-
-    /**
-     * Filter out "." and ".." segments from a URL's path and return
-     * the result.
-     *
-     * This function implements the "remove_dot_segments" algorithm from
-     * the RFC3986 specification for URLs.
-     *
-     * This function supports multi-byte characters with the UTF-8 encoding,
-     * per the URL specification.
-     *
-     * Parameters:
-     * path the path to filter
-     *
-     * Return values:
-     * The filtered path with "." and ".." removed.
-     *
-     * @param $path
-     * @return string
-     */
-    function urlRemoveDotSegments($path)
-    {
-        // multi-byte character explode
-        $inSeg = preg_split('!/!u', $path);
-        $outSeg = array();
-        foreach ($inSeg as $seg) {
-            if ($seg == '' || $seg == '.')
-                continue;
-            if ($seg == '..')
-                array_pop($outSeg);
-            else
-                array_push($outSeg, $seg);
-        }
-        $outPath = implode('/', $outSeg);
-        if ($path[0] == '/')
-            $outPath = '/' . $outPath;
-        // compare last multi-byte character against '/'
-        if ($outPath != '/' && (mb_strlen($path) - 1) == mb_strrpos($path, '/', 'UTF-8'))
-            $outPath .= '/';
-        return $outPath;
-    }
-
-    /**
-     * This function parses an absolute or relative URL and splits it
-     * into individual components.
-     *
-     * RFC3986 specifies the components of a Uniform Resource Identifier (URI).
-     * A portion of the ABNFs are repeated here:
-     *
-     * URI-reference = URI
-     * / relative-ref
-     *
-     * URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
-     *
-     * relative-ref = relative-part [ "?" query ] [ "#" fragment ]
-     *
-     * hier-part = "//" authority path-abempty
-     * / path-absolute
-     * / path-rootless
-     * / path-empty
-     *
-     * relative-part = "//" authority path-abempty
-     * / path-absolute
-     * / path-noscheme
-     * / path-empty
-     *
-     * authority = [ userinfo "@" ] host [ ":" port ]
-     *
-     * So, a URL has the following major components:
-     *
-     * scheme
-     * The name of a method used to interpret the rest of
-     * the URL. Examples: "http", "https", "mailto", "file'.
-     *
-     * authority
-     * The name of the authority governing the URL's name
-     * space. Examples: "example.com", "user@example.com",
-     * "example.com:80", "user:password@example.com:80".
-     *
-     * The authority may include a host name, port number,
-     * user name, and password.
-     *
-     * The host may be a name, an IPv4 numeric address, or
-     * an IPv6 numeric address.
-     *
-     * path
-     * The hierarchical path to the URL's resource.
-     * Examples: "/index.htm", "/scripts/page.php".
-     *
-     * query
-     * The data for a query. Examples: "?search=google.com".
-     *
-     * fragment
-     * The name of a secondary resource relative to that named
-     * by the path. Examples: "#section1", "#header".
-     *
-     * An "absolute" URL must include a scheme and path. The authority, query,
-     * and fragment components are optional.
-     *
-     * A "relative" URL does not include a scheme and must include a path. The
-     * authority, query, and fragment components are optional.
-     *
-     * This function splits the $url argument into the following components
-     * and returns them in an associative array. Keys to that array include:
-     *
-     * "scheme" The scheme, such as "http".
-     * "host" The host name, IPv4, or IPv6 address.
-     * "port" The port number.
-     * "user" The user name.
-     * "pass" The user password.
-     * "path" The path, such as a file path for "http".
-     * "query" The query.
-     * "fragment" The fragment.
-     *
-     * One or more of these may not be present, depending upon the URL.
-     *
-     * Optionally, the "user", "pass", "host" (if a name, not an IP address),
-     * "path", "query", and "fragment" may have percent-encoded characters
-     * decoded. The "scheme" and "port" cannot include percent-encoded
-     * characters and are never decoded. Decoding occurs after the URL has
-     * been parsed.
-     *
-     * Parameters:
-     * url the URL to parse.
-     *
-     * decode an optional boolean flag selecting whether
-     * to decode percent encoding or not. Default = TRUE.
-     *
-     * Return values:
-     * the associative array of URL parts, or FALSE if the URL is
-     * too malformed to recognize any parts.
-     *
-     *
-     * @param $url
-     * @param bool $decode
-     * @return array|bool
-     * @noinspection SpellCheckingInspection
-     */
-    protected function splitUrl($url, $decode = TRUE)
-    {
-        // Character sets from RFC3986.
-        $xunressub = 'a-zA-Z\d\-._~\!$&\'()*+,;=';
-        $xpchar = $xunressub . ':@%';
-
-        // Scheme from RFC3986.
-        $xscheme = '([a-zA-Z][a-zA-Z\d+-.]*)';
-
-        // User info (user + password) from RFC3986.
-        $xuserinfo = '(([' . $xunressub . '%]*)' . '(:([' . $xunressub . ':%]*))?)';
-
-        // IPv4 from RFC3986 (without digit constraints).
-        $xipv4 = '(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})';
-
-        // IPv6 from RFC2732 (without digit and grouping constraints).
-        $xipv6 = '(\[([a-fA-F\d.:]+)\])';
-
-        // Host name from RFC1035. Technically, must start with a letter.
-        // Relax that restriction to better parse URL structure, then
-        // leave host name validation to application.
-        $xhost_name = '([a-zA-Z\d-.%]+)';
-
-        // Authority from RFC3986. Skip IP future.
-        $xhost = '(' . $xhost_name . '|' . $xipv4 . '|' . $xipv6 . ')';
-        $xport = '(\d*)';
-        $xauthority = '((' . $xuserinfo . '@)?' . $xhost . '?(:' . $xport . ')?)';
-
-        // Path from RFC3986. Blend absolute & relative for efficiency.
-        $xslash_seg = '(/[' . $xpchar . ']*)';
-        $xpath_authabs = '((//' . $xauthority . ')((/[' . $xpchar . ']*)*))';
-        $xpath_rel = '([' . $xpchar . ']+' . $xslash_seg . '*)';
-        $xpath_abs = '(/(' . $xpath_rel . ')?)';
-        $xapath = '(' . $xpath_authabs . '|' . $xpath_abs . '|' . $xpath_rel . ')';
-
-        // Query and fragment from RFC3986.
-        $xqueryfrag = '([' . $xpchar . '/?' . ']*)';
-
-        // URL.
-        $xurl = '^(' . $xscheme . ':)?' . $xapath . '?' . '(\?' . $xqueryfrag . ')?(#' . $xqueryfrag . ')?$';
-
-        // Split the URL into components.
-        $m = array();
-        if (! preg_match('!' . $xurl . '!', $url, $m))
-            return false;
-
-        $parts = array();
-        if (! empty($m[2]))
-            $parts['scheme'] = strtolower($m[2]);
-
-        if (! empty($m[7])) {
-            if (isset($m[9]))
-                $parts['user'] = $m[9];
-            else
-                $parts['user'] = '';
-        }
-        if (! empty($m[10]))
-            $parts['pass'] = $m[11];
-
-        if (! empty($m[13]))
-            $h = $parts['host'] = $m[13];
-        else if (! empty($m[14]))
-            $parts['host'] = $m[14];
-        else if (! empty($m[16]))
-            $parts['host'] = $m[16];
-        else if (! empty($m[5]))
-            $parts['host'] = '';
-        if (! empty($m[17]))
-            $parts['port'] = $m[18];
-
-        if (! empty($m[19]))
-            $parts['path'] = $m[19];
-        else if (! empty($m[21]))
-            $parts['path'] = $m[21];
-        else if (! empty($m[25]))
-            $parts['path'] = $m[25];
-
-        if (! empty($m[27]))
-            $parts['query'] = $m[28];
-        if (! empty($m[29]))
-            $parts['fragment'] = $m[30];
-
-        if (! $decode)
-            return $parts;
-        if (! empty($parts['user']))
-            $parts['user'] = rawurldecode($parts['user']);
-        if (! empty($parts['pass']))
-            $parts['pass'] = rawurldecode($parts['pass']);
-        if (! empty($parts['path']))
-            $parts['path'] = rawurldecode($parts['path']);
-        if (isset($h))
-            $parts['host'] = rawurldecode($parts['host']);
-        if (! empty($parts['query']))
-            $parts['query'] = rawurldecode($parts['query']);
-        if (! empty($parts['fragment']))
-            $parts['fragment'] = rawurldecode($parts['fragment']);
-        return $parts;
-    }
-
-    /**
-     * This function joins together URL components to form a complete URL.
-     *
-     * RFC3986 specifies the components of a Uniform Resource Identifier (URI).
-     * This function implements the specification's "component recomposition"
-     * algorithm for combining URI components into a full URI string.
-     *
-     * The $parts argument is an associative array containing zero or
-     * more of the following:
-     *
-     * "scheme" The scheme, such as "http".
-     * "host" The host name, IPv4, or IPv6 address.
-     * "port" The port number.
-     * "user" The user name.
-     * "pass" The user password.
-     * "path" The path, such as a file path for "http".
-     * "query" The query.
-     * "fragment" The fragment.
-     *
-     * The "port", "user", and "pass" values are only used when a "host"
-     * is present.
-     *
-     * The optional $encode argument indicates if appropriate URL components
-     * should be percent-encoded as they are assembled into the URL. Encoding
-     * is only applied to the "user", "pass", "host" (if a host name, not an
-     * IP address), "path", "query", and "fragment" components. The "scheme"
-     * and "port" are never encoded. When a "scheme" and "host" are both
-     * present, the "path" is presumed to be hierarchical and encoding
-     * processes each segment of the hierarchy separately (i.e., the slashes
-     * are left alone).
-     *
-     * The assembled URL string is returned.
-     *
-     * Parameters:
-     * parts an associative array of strings containing the
-     * individual parts of a URL.
-     *
-     * encode an optional boolean flag selecting whether
-     * to do percent encoding or not. Default = true.
-     *
-     * Return values:
-     * Returns the assembled URL string. The string is an absolute
-     * URL if a scheme is supplied, and a relative URL if not. An
-     * empty string is returned if the $parts array does not contain
-     * any of the needed values.
-     *
-     *
-     * @param $parts
-     * @param bool $encode
-     * @return string
-     * @noinspection SpellCheckingInspection
-     */
-    protected function joinUrl($parts, $encode = TRUE)
-    {
-        if ($encode) {
-            if (isset($parts['user']))
-                $parts['user'] = rawurlencode($parts['user']);
-            if (isset($parts['pass']))
-                $parts['pass'] = rawurlencode($parts['pass']);
-            if (isset($parts['host']) && ! preg_match('!^(\[[\da-f.:]+]])|([\da-f.:]+)$!ui', $parts['host']))
-                $parts['host'] = rawurlencode($parts['host']);
-            if (! empty($parts['path']))
-                $parts['path'] = preg_replace('!%2F!ui', '/', rawurlencode($parts['path']));
-            if (isset($parts['query']))
-                $parts['query'] = rawurlencode($parts['query']);
-            if (isset($parts['fragment']))
-                $parts['fragment'] = rawurlencode($parts['fragment']);
-        }
-
-        $url = '';
-        if (! empty($parts['scheme']))
-            $url .= $parts['scheme'] . ':';
-        if (isset($parts['host'])) {
-            $url .= '//';
-            if (isset($parts['user'])) {
-                $url .= $parts['user'];
-                if (isset($parts['pass']))
-                    $url .= ':' . $parts['pass'];
-                $url .= '@';
-            }
-            if (preg_match('!^[\da-f]*:[\da-f.:]+$!ui', $parts['host']))
-                $url .= '[' . $parts['host'] . ']'; // IPv6
-            else
-                $url .= $parts['host']; // IPv4 or name
-            if (isset($parts['port']))
-                $url .= ':' . $parts['port'];
-            if (! empty($parts['path']) && $parts['path'][0] != '/')
-                $url .= '/';
-        }
-        if (! empty($parts['path']))
-            $url .= $parts['path'];
-        if (isset($parts['query']))
-            $url .= '?' . $parts['query'];
-        if (isset($parts['fragment']))
-            $url .= '#' . $parts['fragment'];
-        return $url;
-    }
-
-    /**
-     *
-     * @return Curl
-     */
-    function getCurl()
-    {
-        return $this->_curl;
-    }
-}

+ 5 - 3
src/Dever/Helper/Cmd.php

@@ -4,7 +4,7 @@ class Cmd
 {
     public static function run($api, $app = false, $daemon = true)
     {
-        if (strpos($api, 'http://') !== false) {
+        if (strpos($api, 'http') !== false) {
             return self::shell('curl "' . $api . '"');
         }
         if (!$app) {
@@ -16,9 +16,11 @@ class Cmd
         } else {
             $php = Dever::config('setting')['php'] ?? 'php';
             $temp = explode('?', $api);
-            parse_str($temp[1], $param);
+            if (isset($temp[1])) {
+                parse_str($temp[1], $param);
+            }
             $param['l'] = $temp[0];
-            return self::shell($php . ' ' . $app['path'] . 'index.php ' . $param, $daemon);
+            return self::shell($php . ' ' . $app['path'] . 'index.php \'' . Dever::json_encode($param) . '\'', $daemon);
         }
     }
     public static function kill($command)

+ 44 - 36
src/Dever/Helper/Curl.php

@@ -134,7 +134,7 @@ class Curl
     {
         $header['Content-Type'] = 'multipart/form-data';
         if (!is_array($param)) {
-            $param = json_decode($param, $param);
+            $param = Dever::json_decode($param);
         }
         $this->setHeader($header);
         $this->setParam($param);
@@ -143,7 +143,10 @@ class Curl
     {
         $header['Content-Type'] = 'application/x-www-form-urlencoded';
         if (!is_array($param)) {
-            $param = json_decode($param, $param);
+            $array = Dever::json_decode($param);
+            if (json_last_error() === JSON_ERROR_NONE) {
+                $param = $array;
+            }
         }
         if (is_array($param)) {
             $param = http_build_query($param);
@@ -159,41 +162,46 @@ class Curl
     }
     public function result($setting = false)
     {
-        if ($setting) {
-            $this->setting($setting);
-        }
-        if ($this->header) {
-            curl_setopt($this->handle, CURLOPT_HTTPHEADER, $this->header);
-        }
-        curl_setopt($this->handle, CURLOPT_HEADER, false);
-        if ($this->result_header) {
-            $this->result_header = array();
-            curl_setopt($this->handle, CURLOPT_HEADERFUNCTION, array($this, 'headerHandler'));
-        }
-        if (Dever::shell('debug')) {
-            curl_setopt($this->handle, CURLINFO_HEADER_OUT, true);
-        }
-        curl_setopt($this->handle, CURLOPT_ACCEPT_ENCODING, 'gzip,deflate');
-        $result = curl_exec($this->handle);
-        $debug = array();
-        if (Dever::shell('debug')) {
-            $debug['request'] = curl_getinfo($this->handle, CURLINFO_HEADER_OUT);
-        } elseif ($this->get_info) {
-            $result = curl_getinfo($this->handle);
-        }
-        curl_close($this->handle);
-        $data = $result;
-        if (!Dever::shell('all') && is_array($data)) {
-            $data = count($data) . ' records';
-        }
-        $debug['url'] = $this->url;
-        $debug['param'] = $this->param;
-        $debug['result'] = $data;
-        if ($this->log) {
-            Dever::log($debug, 'curl');
+        try {
+            if ($setting) {
+                $this->setting($setting);
+            }
+            if ($this->header) {
+                curl_setopt($this->handle, CURLOPT_HTTPHEADER, $this->header);
+            }
+            curl_setopt($this->handle, CURLOPT_HEADER, false);
+            if ($this->result_header) {
+                $this->result_header = array();
+                curl_setopt($this->handle, CURLOPT_HEADERFUNCTION, array($this, 'headerHandler'));
+            }
+            if (Dever::shell('debug')) {
+                curl_setopt($this->handle, CURLINFO_HEADER_OUT, true);
+            }
+            curl_setopt($this->handle, CURLOPT_ACCEPT_ENCODING, 'gzip,deflate');
+            $result = curl_exec($this->handle);
+            $debug = array();
+            if (Dever::shell('debug')) {
+                $debug['request'] = curl_getinfo($this->handle, CURLINFO_HEADER_OUT);
+            } elseif ($this->get_info) {
+                $result = curl_getinfo($this->handle);
+            }
+            curl_close($this->handle);
+            $data = $result;
+            if (!Dever::shell('all') && is_array($data)) {
+                $data = count($data) . ' records';
+            }
+            $debug['url'] = $this->url;
+            $debug['param'] = $this->param;
+            $debug['result'] = $data;
+            if ($this->log) {
+                Dever::log($debug, 'curl');
+            }
+            Dever::debug($debug, 'curl');
+            return $result;
+        } catch (\Exception $e) {
+            curl_close($this->handle);
+            return 'error';
         }
-        Dever::debug($debug, 'curl');
-        return $result;
     }
     public function setting($setting = array())
     {

+ 17 - 5
src/Dever/Helper/Date.php

@@ -10,6 +10,11 @@ class Date
         if (is_numeric($v)) {
             return $v;
         }
+        $n = 0;
+        if (strstr($v, 'T')) {
+            $v = str_replace(array('T', 'Z'), ' ', $v);
+            $n = 8*3600;
+        }
         if (strstr($v, ' ')) {
             $t = explode(' ', $v);
             $v = $t[0];
@@ -40,7 +45,7 @@ class Date
         if (!isset($t[2])) {
             $t[2] = '01';
         }
-        $v = mktime($s[0], $s[1], $s[2], $t[1], $t[2], $t[0]);
+        $v = mktime($s[0], $s[1], $s[2], $t[1], $t[2], $t[0]) + $n;
         return $v;
     }
     # 获取日期
@@ -97,8 +102,8 @@ class Date
         } elseif (!$time) {
             $time = time();
         }
-        $start = self::maketime(date('Y-m-d 00:00:00', strtotime('this week Monday', $time)));
-        $end = self::maketime(date('Y-m-d 23:59:59', strtotime('this week Sunday', $time)));
+        $start = self::mktime(date('Y-m-d 00:00:00', strtotime('this week Monday', $time)));
+        $end = self::mktime(date('Y-m-d 23:59:59', strtotime('this week Sunday', $time)));
         return array($start, $end);
     }
 
@@ -128,8 +133,8 @@ class Date
             $time = time();
         }
         list($year, $month, $day) = explode('-', date('Y-m-d', $time));
-        $start = Dever::maketime($year . '-' . $month . '-' . $day . ' 00:00:00');
-        $end = Dever::maketime($year . '-' . $month . '-' . $day . ' 23:59:59');
+        $start = self::mktime($year . '-' . $month . '-' . $day . ' 00:00:00');
+        $end = self::mktime($year . '-' . $month . '-' . $day . ' 23:59:59');
         return array($start, $end);
     }
     # 获取毫秒
@@ -139,6 +144,13 @@ class Date
         $msectime = (float)sprintf('%.0f', (floatval($msec) + floatval($sec)) * 1000);
         return $msectime;
     }
+    # 获取微秒
+    public static function wtime()
+    {
+        list($usec, $sec) = explode(' ', microtime());
+        $timestamp = sprintf('%d%06d', $sec, $usec*1000000);
+        return $timestamp;
+    }
     # 获取第几周
     public static function getWeek($time)
     {

+ 17 - 6
src/Dever/Helper/Secure.php

@@ -2,6 +2,7 @@
 use Dever;
 class Secure
 {
+    private static $token = false;
     public static function login($uid, $extend = false)
     {
         $auth = '';
@@ -19,8 +20,11 @@ class Secure
         }
         return false;
     }
-    public static function get($request)
+    public static function get($request, $token = false)
     {
+        if ($token) {
+            self::$token = $token;
+        }
         $time = self::timestamp();
         $nonce = self::nonce();
         $signature = self::signature($time, $nonce, $request);
@@ -31,13 +35,16 @@ class Secure
         );
         return $request;
     }
-    public static function check($request = array(), $time = 300)
+    public static function check($request = array(), $time = 300, $token = false)
     {
+        if ($token) {
+            self::$token = $token;
+        }
         if (!$request) {
             $request = Dever::input();
         }
         if (empty($request['signature']) || empty($request['nonce'])) {
-            Dever::error('api signature not exists');
+            Dever::error('signature不存在');
         }
         if (isset($request['l'])) {
             unset($request['l']);
@@ -50,11 +57,11 @@ class Secure
         }
         self::repeat($request['signature'], $time);
         if (time() - $request['time'] > $time) {
-            Dever::error('api signature has expired');
+            Dever::error('signature已过期');
         }
         $signature = self::signature($request['time'], $request['nonce'], $request);
         if ($request['signature'] != $signature) {
-            Dever::error('invalid signature');
+            Dever::error('signature验签失败');
         }
         return $signature;
     }
@@ -79,6 +86,9 @@ class Secure
     }
     public static function token()
     {
+        if (self::$token) {
+            return self::$token;
+        }
         return Dever::config('setting')['token'];
     }
     public static function nonce()
@@ -91,8 +101,9 @@ class Secure
     }
     public static function repeat($value, $expire)
     {
+        return;
         if (isset(Dever::config('setting')['redis']) && !Redis::lock($value, 1, $expire)) {
-            Dever::error('api signature repeat');
+            Dever::error('signature不能重复使用');
         }
     }
     public static function encode($string, $key = '')

+ 2 - 2
src/Dever/Helper/Str.php

@@ -3,11 +3,11 @@ class Str
 {
     public static function encode($string, $key = '')
     {
-        return \Dever\Secure::encode($string, $key);
+        return Secure::encode($string, $key);
     }
     public static function decode($string, $key = '')
     {
-        return \Dever\Secure::decode($string, $key);
+        return Secure::decode($string, $key);
     }
     public static function hide($string, $start = 3, $len = 4, $hide = '****')
     {

+ 1 - 1
src/Dever/Import.php

@@ -41,7 +41,7 @@ class Import
                     if (method_exists($this->class, $token)) {
                         $key = $this->class->{$token}();
                     }
-                    \Dever\Helper\Secure::check($this->param, array(), $key);
+                    \Dever\Helper\Secure::check($this->param, 300, $key);
                 }
             }
             return $this->loadDevelopCommit();

+ 15 - 14
src/Dever/Library.php

@@ -1,40 +1,41 @@
 <?php namespace Dever;
 class Library
 {
-    protected static $file;
     protected static $class;
+    protected static $load;
     public static function autoload($class)
     {
-        $class = explode('\\', $class);
-        if (isset($class[3])) {
-            self::require(lcfirst($class[0]), lcfirst($class[1]) . DIRECTORY_SEPARATOR . $class[2] . DIRECTORY_SEPARATOR . $class[3]);
-        } elseif (isset($class[2])) {
-            self::require(lcfirst($class[0]), lcfirst($class[1]) . DIRECTORY_SEPARATOR . $class[2]);
+        if (empty(self::$load[$class])) {
+            $class = explode('\\', $class);
+            if (isset($class[3])) {
+                self::require(lcfirst($class[0]), lcfirst($class[1]) . DIRECTORY_SEPARATOR . $class[2] . DIRECTORY_SEPARATOR . $class[3]);
+            } elseif (isset($class[2])) {
+                self::require(lcfirst($class[0]), lcfirst($class[1]) . DIRECTORY_SEPARATOR . $class[2]);
+            }
         }
     }
     public static function require($app, $file)
     {
         $project = Project::load($app);
         if ($project) {
-            $file = $project['path'] . $file . '.php';
-            if (is_file($file) && empty(self::$file[$file])) {
-                self::$file[$file] = true;
-                require $file;
-            }
+            require $project['path'] . $file . '.php';
         }
     }
     public static function apply($file, $app, $path)
     {
         if (strpos($file, '/')) {
             $class = str_replace(' ', '/', ucwords(str_replace('/', ' ', $file)));
+            $file = $path . DIRECTORY_SEPARATOR . $class;
             $class = str_replace('/', '\\', $class);
-            $file = $path . DIRECTORY_SEPARATOR . $file;
         } else {
             $class = ucfirst($file);
-            $file = $path . DIRECTORY_SEPARATOR . $file;
+            $file = $path . DIRECTORY_SEPARATOR . ucfirst($file);
         }
         $class = ucfirst($app) . '\\' . ucfirst($path) . '\\' . $class;
-        self::require($app, $file);
+        if (empty(self::$load[$class])) {
+            self::$load[$class] = true;
+            self::require($app, $file);
+        }
         return $class;
     }
     public static function load($class, $app, $path)

+ 23 - 11
src/Dever/Model.php

@@ -16,9 +16,13 @@ class Model
                 if (isset($this->config['partition']) && empty($partition)) {
                     $partition = $this->config['partition'];
                 }
+                if (isset($this->config['store']) && $store == 'default') {
+                    $store = $this->config['store'];
+                }
                 $file = $app . DIRECTORY_SEPARATOR . $table . '.php';
                 $this->config['app'] = $app;
                 $this->config['table'] = DEVER_PROJECT . '_' . $app . '_' . $table;
+                $this->config['load'] = $app . '/' . $table;
                 $this->lang();
             } else {
                 $this->config['table'] = $table;
@@ -46,7 +50,7 @@ class Model
                 $t = Dever::session($k);
                 if ($t) {
                     $v = $t;
-                } else {
+                } elseif ($k == 'database' || $k == 'table') {
                     $e = '$v=' . $v . ';';
                     eval($e);
                 }
@@ -89,7 +93,9 @@ class Model
             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']);
+                    $e = '$v=' . $this->partition['field']['value'] . ';';
+                    eval($e);
+                    $this->partition['field']['value'] = \Dever\Helper\Date::mktime($v);
                 }
                 $data['field'] = 0;
             }
@@ -113,6 +119,10 @@ class Model
             }
         }
     }
+    public function load($param, $set = array(), $lock = false)
+    {
+        return $this->store->load($this->config['table'], $param, $set, $this->config['struct'], $lock);
+    }
     public function select($param, $set = array(), $lock = false)
     {
         if (isset($this->partition['where']) && $this->partition['where']) {
@@ -133,26 +143,28 @@ class Model
         }
         $result = $this->store->select($this->config['table'], $param, $set, $this->config['struct'], $lock);
         if (isset($set['num']) && empty($set['page'])) {
-            $result = $result->fetchAll();
             Paginator::status(empty($result));
         }
         return $result;
     }
-    public function count($param = array())
+    public function find($param, $set = array(), $lock = false)
     {
         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']);
+        return $this->store->find($this->config['table'], $param, $set, $this->config['struct'], $lock);
     }
-    public function find($param, $set = array(), $lock = false)
+    public function count($param = array())
     {
-        return $this->select($param, $set, $lock)->fetch();
+        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 kv($param, $set = array())
     {
         $result = array();
-        $data = $this->select($param, $set)->fetchAll();
+        $data = $this->select($param, $set);
         if ($data) {
             if (empty($set['kv'])) {
                 $set['kv'] = array('id', 'name');
@@ -264,7 +276,7 @@ class Model
                 if (is_string($option) && strpos($option, 'Dever') === 0) {
                     eval('$option=' . $option . ';');
                 } elseif (is_string($option)) {
-                    $option = Dever::db($option)->select([], ['col' => $col])->fetchAll();
+                    $option = Dever::db($option)->select([], ['col' => $col]);
                 } elseif (is_array($option) && !isset($option[0])) {
                     $temp = $option;
                     $option = array();
@@ -296,12 +308,12 @@ class Model
     public function tree($where, $config, $func = false, $set = array())
     {
         $where[$config[0]] = $config[1];
-        $data = $this->select($where, $set)->fetchAll();
+        $data = $this->select($where, $set);
         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);
+                $child = $this->tree($where, $config, $func, $set);
                 if ($child) {
                     $v['children'] = $child;
                 }

+ 49 - 9
src/Dever/Project.php

@@ -20,24 +20,64 @@ class Project
     {
         $file = self::init();
         if (empty(self::$content[DEVER_APP_NAME])) {
-            self::$content[DEVER_APP_NAME] = array('lang' => DEVER_APP_LANG,'path' => DEVER_APP_PATH,'url' => DEVER_APP_HOST);
+            $host = DEVER_APP_HOST;
+            if (strpos($host, '/src/' . DEVER_APP_NAME)) {
+                $host = explode('/src/' . DEVER_APP_NAME, $host)[0] . '/';
+            } elseif (strpos($host, '/package/' . DEVER_APP_NAME)) {
+                $host = explode('/package/' . DEVER_APP_NAME, $host)[0] . '/';
+            }
+            self::write($host, 'package');self::write($host, 'src');
+            self::$content[DEVER_APP_NAME]['url'] = DEVER_APP_HOST;
+            if (isset(self::$content['manage'])) {
+                $manage = self::$content['manage'];
+                unset(self::$content['manage']);
+                self::$content = array_merge(array('manage' => $manage), self::$content);
+            }
+            self::content($file);
+            if (isset(self::$content['manage'])) {
+                \Dever::load('menu', 'manage')->init();
+            }
+        }
+        if (empty(self::$content[DEVER_APP_NAME]['lang'])) {
+            self::$content[DEVER_APP_NAME]['lang'] = DEVER_APP_LANG;
             self::content($file);
         }
     }
-    public static function update($key, $index, $value)
+    public static function write($host, $name)
     {
-        $file = self::init();
-        if (isset(self::$content[$key])) {
-            self::$content[$key][$index] = $value;
-            self::content($file);
+        $dir = DEVER_PROJECT_PATH . $name . '/';
+        $data = scandir($dir);
+        foreach ($data as $v) {
+            if (empty(self::$content[$v]) && is_dir($dir . '/' . $v) && $v !== '.' && $v !== '..') {
+                if (is_file($dir . $v . '/index.php')) {
+                    $k = $name . '/' . $v . '/';
+                    self::$content[$v] = array();
+                    if (strstr($name, 'package')) {
+                        if (strstr($name, 'package/manage')) {
+                            unset(self::$content[$v]);
+                            $v = 'manage';
+                            self::$content[$v] = array();
+                            self::$content[$v]['path'] = DEVER_PATH . $name . '/';
+                        } elseif($v == 'manage') {
+                            self::$content[$v]['path'] = DEVER_PATH . $k;
+                            $k .= 'api/';
+                        } else {
+                            self::$content[$v]['path'] = DEVER_PATH . $k;
+                        }
+                        self::$content[$v]['setup'] = DEVER_PROJECT_PATH . $k;
+                    } else {
+                        self::$content[$v]['path'] = DEVER_PROJECT_PATH . $k;
+                    }
+                    self::$content[$v]['url'] = $host . $k;
+                } else {
+                    self::write($host, $name . '/' . $v);
+                }
+            }
         }
     }
     public static function content($file)
     {
         file_put_contents($file, '<?php $project = ' . var_export(self::$content, true) . ';');
-        if (isset(self::$content['manage'])) {
-            \Dever::load('menu', 'manage')->init();
-        }
     }
     public static function read()
     {

+ 1 - 1
src/Dever/Route.php

@@ -11,7 +11,7 @@ class Route
         if ($condition == 'set') {
             return self::$data[$key] = $lang;
         }
-        if (is_string($key) && isset(self::$data[$key]) && self::$data[$key]) {
+        if (is_string($key) && isset(self::$data[$key]) && self::$data[$key] && self::$data[$key] != 'false') {
             $value = self::$data[$key];
         }
         if ($condition) {

+ 46 - 15
src/Dever/Sql.php

@@ -34,7 +34,7 @@ class Sql
             $sql .= ' AUTO_INCREMENT = ' . $config['auto'];
         }
         if (isset($config['type'])) {
-            $sql .= ' ENGINE = ' . $config['type'] . ';';
+            $sql .= ' ENGINE = ' . $config['type'] . ' ';
         }
         return $sql . ' COMMENT="'.$config['name'].'"';
     }
@@ -111,14 +111,18 @@ class Sql
         }
         $alter = '';
         if ($state) {
-            $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'].') ';
+            $type = $partition['type'];
+            if ($partition['type'] == 'time') {
+                $type = 'range';
+            }
+            $alter = 'ALTER TABLE `' . $table . '` DROP PRIMARY KEY, ADD PRIMARY KEY (`id`, `'.$partition['field'].'`) USING BTREE;ALTER TABLE `' . $table . '` PARTITION BY '.strtoupper($type).' ('.$partition['field'].') ';
         } else {
             $alter = 'ALTER TABLE `' . $table . '` ADD PARTITION ';
         }
         if ($partition['type'] == 'range' || $partition['type'] == 'time') {
             $name = $partition['value'];
             if ($partition['type'] == 'time') {
-                $name = date('Ymd', $name);
+                $name = date('Ymd', $name - 86400);
             }
             $sql = 'PARTITION p'.$name.' VALUES LESS THAN ('.$partition['value'].')';
             return $alter . '('.$sql.')';
@@ -135,7 +139,7 @@ class Sql
             return self::desc($table);
         }
     }
-    public static function select($table, $param, &$bind, $set = array(), $field = array(), $lock = false)
+    public static function select($table, $param, &$bind, $set = array(), $field = array(), $lock = false, $type = '')
     {
         $col = '*';
         $rule = '';
@@ -154,23 +158,31 @@ class Sql
             $rule .= ' GROUP BY ' . $set['group'];
         }
         if (isset($set['order'])) {
+            if ($type == 'Influxdb') {
+                $set['order'] = ' time desc';
+            }
             $rule .= ' ORDER BY ' . $set['order'];
         }
         if (isset($set['limit'])) {
-            if (is_array($set['limit'])) {
+            if (is_array($set['limit']) && !$type) {
                 $table .= ' inner join (select id from ' . $table . self::where($param, $bind, $field) . $rule . ' limit ' . $set['limit'][0].','.$set['limit'][1].') as t on '.$table.'.id=t.id';
                 $rule = '';
                 $param = false;
             } else {
-                $rule .= ' LIMIT ' . $set['limit'];
+                if ($type == 'Influxdb' && strpos($set['limit'], ',')) {
+                    $temp = explode(',', $set['limit']);
+                    $rule .= ' LIMIT ' . $temp[1] . ' OFFSET ' . $temp[0];
+                } else {
+                    $rule .= ' LIMIT ' . $set['limit'];
+                }
             }
         }
         if ($lock) {
             $rule .= ' FOR UPDATE';
         }
-        return 'SELECT ' . $col . ' FROM ' . $table . self::where($param, $bind, $field) . $rule;
+        return 'SELECT ' . $col . ' FROM ' . $table . self::where($param, $bind, $field, $type) . $rule;
     }
-    public static function where($param, &$bind, $field = array())
+    public static function where($param, &$bind, $field = array(), $type = '')
     {
         if ($param) {
             $first = $second = '';
@@ -184,24 +196,28 @@ class Sql
                         $first_link = $second_link = '';
                         foreach ($v as $k1 => $v1) {
                             if (is_array($v1)) {
-                                self::field($second_link, $bind, $i, $k1, $v1[0], $v1[1], $field);
+                                self::field($second_link, $bind, $i, $k1, $v1[0], $v1[1], $field, $type);
                             } else {
-                                self::field($first_link, $bind, $i, $k1, '=', $v1, $field);
+                                self::field($first_link, $bind, $i, $k1, '=', $v1, $field, $type);
                             }
                         }
                         $first_link = ltrim($first_link, ' and ');
                         $second .= ' ' . $k . ' (' . $first_link . $second_link . ')';
                     } else {
                         if (is_array($v)) {
-                            self::field($second, $bind, $i, $k, $v[0], $v[1], $field);
+                            self::field($second, $bind, $i, $k, $v[0], $v[1], $field, $type);
                         } else {
-                            self::field($first, $bind, $i, $k, '=', $v, $field);
+                            self::field($first, $bind, $i, $k, '=', $v, $field, $type);
                         }
                     }
                 }
                 return ' WHERE ' . ltrim($first . $second, ' and ');
             } elseif (is_numeric($param)) {
-                return ' WHERE id = ' . $param;
+                if ($type == 'Influxdb') {
+                    return ' WHERE "id" = \'' . $param . '\'';
+                } else {
+                    return ' WHERE id = ' . intval($param);
+                }
             } else {
                 return ' WHERE ' . $param;
             }
@@ -209,14 +225,29 @@ class Sql
             return '';
         }
     }
-    private static function field(&$s, &$b, &$i, $k, $e, $v, $f)
+    private static function field(&$s, &$b, &$i, $k, $e, $v, $f, $t)
     {
+        $type = '';
+        if (is_string($b)) {
+            $type = $b;
+            $b = array();
+        }
         if ($f && empty($f[$k]) && strpos('id,cdate', $k) === false) {
             return;
         }
         $s .= ' and ';
         $p = ':'.$k.$i;
-        $k = '`'.$k.'`';
+        $x = '`';
+        if ($t == 'Influxdb') {
+            $e = '=';
+            if ($k == 'cdate') {
+                $k = 'time';
+                $v = date('Y-m-d H:i:s', $v-28800);
+            }
+            $x = '"';
+            $p = "'".$v."'";
+        }
+        $k = $x.$k.$x;
         if (strpos($e,'in') !== false) {
             if (!is_array($v)) {
                 $v = explode(',', $v);

+ 165 - 0
src/Dever/Store/Influxdb.php

@@ -0,0 +1,165 @@
+<?php namespace Dever\Store;
+use Dever;
+use Dever\Debug;
+use Dever\Output;
+use Dever\Sql;
+#https://docs.influxdata.com/influxdb/v2/api-guide/api_intro/
+class Influxdb extends Base
+{
+    public function __construct($setting)
+    {
+        $this->type = $setting['type'];
+        $this->read = $setting;
+    }
+    public function query($param = array(), $type = 1)
+    {
+        $header['Authorization'] = 'Token ' . $this->read['token'];
+        $header['Content-Type'] = 'text/plain; charset=utf-8';
+        $header['Accept'] = 'application/json';
+        if ($type == 1) {
+            $type = 'get';
+            $host = $this->read['host'] . '/query?db='.$this->read['name'];
+            $param = array('q' => $param);
+            $json = false;
+        } else {
+            $type = 'post';
+            $host = $this->read['host'] . '/api/v2/write?org='.$this->read['user'].'&bucket='.$this->read['name'].'&precision=' . $this->read['precision'];
+            $json = true;
+        }
+
+        /*
+        //$curl = 'curl --get '.$this->read['host'].'/query?db=api  --header "Authorization: '.$header['Authorization'].'" --data-urlencode "q='.$param['q'].'"';
+        //$curl = 'curl --request POST "'.$host.'" --header "Authorization: '.$header['Authorization'].'" --header "Content-Type: '.$header['Content-Type'].'" --header "Accept: '.$header['Accept'].'" --data-binary \''.$param.'\'';
+        //echo $curl;die;
+        */
+        $result = Dever::curl($host, $param, $type, $json, $header)->result();
+        if (Debug::$shell) {
+            $this->log(array('param' => $param, 'result' => $result));
+        }
+        return $result;
+    }
+    public function struct($config, $state = 0)
+    {
+        if (!$state) {
+            $this->query('DELETE FROM ' . $config['table']);
+        }
+    }
+    public function load($table, $param, $set, $field, $lock)
+    {
+        $bind = array();
+        $sql = Sql::select($table, $param, $bind, $set, $field, $lock, $this->type);
+        $data = $this->query($sql);
+        $data = Dever::json_decode($data);
+        if (isset($data['results'][0]['series'][0]['values'])) {
+            return $data['results'][0]['series'][0];
+        } else {
+            return array();
+        }
+    }
+    public function select($table, $param, $set, $field, $lock)
+    {
+        $data = $this->load($table, $param, $set, $field, $lock);
+        if ($data) {
+            $columns = $data['columns'];
+            $values = $data['values'];
+            $result = array();
+            foreach ($values as $k => $v) {
+                foreach ($columns as $k1 => $v1) {
+                    $result[$k][$v1] = $this->set($v[$k1], $v1);
+                }
+            }
+            return $result;
+        }
+        return $data;
+    }
+    public function find($table, $param, $set, $field, $lock)
+    {
+        $data = $this->load($table, $param, $set, $field, $lock);
+        if ($data) {
+            $columns = $data['columns'];
+            $values = $data['values'];
+            $result = array();
+            foreach ($columns as $k => $v) {
+                $result[$v] = $this->set($values[0][$k], $v);
+            }
+            return $result;
+        }
+        return $data;
+    }
+    public function count($table, $param, $field)
+    {
+        $result = $this->load($table, $param, array('col'=>'count(*)'), $field, false);
+        if ($result && isset($result['values'][0][1])) {
+            return $result['values'][0][1];
+        }
+        return 0;
+    }
+    public function insert($table, $data, $field)
+    {
+        $param = $table;
+        $time = $data['cdate'];
+        $tags = $fields = array();
+        if (isset($data['id'])) {
+            $tags[] = 'id=' . $data['id'];
+        }
+        foreach ($field as $k => $v) {
+            $value = $v['default'] ?? "null";
+            if (isset($data[$k])) {
+                $value = $data[$k];
+            }
+            if (!$value) {
+                $value = 'null';
+            }
+            if (!strstr($v['type'], 'char') && !strstr($v['type'], 'text') && $value == 'null') {
+                $value = 0;
+            }
+            if (isset($v['base64']) && $v['base64']) {
+                $value = 'base64_' . base64_encode($value);
+            }
+            if (isset($v['fields']) && $v['fields']) {
+                if (strstr($v['type'], 'char') || strstr($v['type'], 'text')) {
+                    $value = '"' . $value . '"';
+                }
+                $fields[] = $k . '=' . $value;
+            } else {
+                $tags[] = $k . '=' . $value;
+            }
+        }
+        if ($tags) {
+            $param .= ',' . implode(',', $tags);
+        } else {
+            Dever::error('influxdb tags not null');
+        }
+        if ($fields) {
+            $param .= ' ' . implode(',', $fields);
+        }
+        $param .= ' ' . $time;
+        $this->query($param, 2);
+        return $time;
+    }
+    public function update($table, $param, $data, $field)
+    {
+        $data = array_merge($param, $data);
+        return $this->insert($table, $data, $field);
+    }
+    private function set($k, &$v)
+    {
+        if ($v == 'time') {
+            $v = 'cdate';
+            $k = strtotime($k);
+        }
+        if (strstr($k, 'base64_')) {
+            $k = base64_decode(str_replace('base64_', '', $k));
+        }
+        if ($k == 'null') {
+            $k = '';
+        }
+        return $k;
+    }
+    public function delete($table, $param, $field){}
+    public function index($config, $state = 0){}
+    public function partition($config, $partition){}
+    public function begin(){}
+    public function commit(){}
+    public function rollback(){}
+}

+ 10 - 2
src/Dever/Store/Pdo.php

@@ -105,15 +105,23 @@ class Pdo extends Base
         }
         return $handle;
     }
-    public function select($table, $param, $set, $field, $lock)
+    public function load($table, $param, $set, $field, $lock)
     {
         $bind = array();
         $sql = Sql::select($table, $param, $bind, $set, $field, $lock);
         return $this->query($sql, $bind);
     }
+    public function select($table, $param, $set, $field, $lock)
+    {
+        return $this->load($table, $param, $set, $field, $lock)->fetchAll();
+    }
+    public function find($table, $param, $set, $field, $lock)
+    {
+        return $this->load($table, $param, $set, $field, $lock)->fetch();
+    }
     public function count($table, $param, $field)
     {
-        return $this->select($table, $param, array('col'=>'count(*)'), $field, false)->fetch(\PDO::FETCH_NUM)[0];
+        return $this->load($table, $param, array('col'=>'count(*)'), $field, false)->fetch(\PDO::FETCH_NUM)[0];
     }
     public function insert($table, $data, $field)
     {