秒杀系统开发(一)

本文部分内容来自 【PHP秒杀系统 高并发高性能的极致挑战】

目标

  1. 如何设计和实现秒杀系统?
  2. 实现系统的高并发、高性能
  3. 实现系统的安全可靠

特点

  1. 人多商品少
  2. 时间短流量高
  3. 外挂机器

技术分析

  1. 瞬时高并发的处理能力
  2. 多层次的分布式处理能力
  3. 人机交互与对抗

系统设计

基本功能

  1. 后台
    1. 活动管理
    2. 商品管理
    3. 订单管理
    4. 日志管理
  2. 前台
    1. 商品展示
    2. 秒杀
    3. 购物车
    4. 我的订单
  3. 安全
    1. 验证码
    2. 问答

流程

  1. 秒杀流程
    1. 验证用户登录状态
    2. 问答验证、问答信息
    3. 库存验证
    4. 生成订单

系统环境搭建

技术选型

  1. 基础服务: Linux + Nginx + PHP + Mysql + redis
  2. CDN
  3. 负载均衡LVS

开发环境准备

  1. 开发工具:PHPStorm
  2. 部署开发环境: Linux + Nginx + PHP + Mysql + redis
  3. 代码管理器:Git

创建项目

  1. 创建miaosha项目
秒杀系统开发(一)_第1张图片
image.png
  1. 创建app,app/Mysql,config, Common目录
秒杀系统开发(一)_第2张图片
image.png

调试封装类

  1. 封装资源类的调用,比如MySQL,Redis,curl等

  2. 功能及其作用如下:

    1 time 性能探针,计算运行的步骤以及每一步的执行效率
    2 log 日志记录,把每一个日志信息记录下来
    3 http 接口调用的记录以及耗时的汇总统计
    4 redis redis调用的记录以及耗时的汇总统计
    5 mysql mysql调用的记录以及耗时的汇总统计
    6 cache memcache调用的记录以及耗时的汇总统计
    
  3. 在Common目录下创建DebugLog.php

    logId = microtime();
            }
        }
    
    
        /**
         * 记录时间,方便调试程序执行逻辑和每一步的执行效率
         * @param $label
         * @param bool $handler
         */
        public static function _time($label, $handler=false)
        {
            if (self::$instance === false) return;
    
            self::$instance->timeList[] = array($label, microtime(), $handler);
        }
    
    
        /**
         * 记录运行时的调试信息,分为 DEBUG_LOG_INFO 和 DEBUG_LOG_ERROR,DEBUG_LOG_INFO 只有在全量输出调试信息的时候才会输出
         * @param $label
         * @param $info
         * @param string $level
         * @param bool $handler
         */
        public static function _log($label, $info, $level=DEBUG_LOG_INFO, $handler=false)
        {
            if (self::$instance === false || (DEBUG_LEVEL < 2 && $level == DEBUG_LOG_INFO)) return;
    
            self::$instance->logList[] = array($label, $info![image.png](https://upload-images.jianshu.io/upload_images/14506913-f2e6fa97c79834b8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    

, handler);
}

    /**
     * 记录运行时的http请求
     * @param $label
     * @param $params
     * @param $config
     * @param $mtime1
     * @param $mtime2
     * @param null $data
     * @param bool $handler
     */
    public static function _http($label, $params, $config, $mtime1, $mtime2, $data=null, $handler=false)
    {
        if (self::$instance === false) return;

        if (DEBUG_LEVEL === 1) {
            self::$instance->httpList[] = array($label, json_encode($params), json_encode($config), $mtime1, $mtime2, null, $handler);
        } else {
            self::$instance->httpList[] = array($label, json_encode($params), json_encode($config), $mtime1, $mtime2, $data, $handler);
        }
    }

    /**
     * 记录运行时的redis请求
     * @param $label
     * @param $params
     * @param $config
     * @param $mtime1
     * @param $mtime2
     * @param null $data
     * @param bool $handler
     */
    public static function _redis($label, $params, $config, $mtime1, $mtime2, $data = null, $handler = false)
    {
        if (self::$instance === false) return;

        if (DEBUG_LEVEL === 1) {
            if ('setex' == $label) {
                // 过滤掉内容块,避免日志太多
                $params[2] = null;
            }
            self::$instance->redisList[] = array($label, json_encode($params), json_encode($config), $mtime1, $mtime2, null, $handler);
        } else {
            self::$instance->redisList[] = array($label, json_encode($params), json_encode($config), $mtime1, $mtime2, $data, $handler);
        }
    }

    /**
     * 记录运行时的mysql请求
     * @param $label
     * @param $params
     * @param $config
     * @param $mtime1
     * @param $mtime2
     * @param null $data
     * @param bool $handler
     */
    public static function _mysql($label, $params, $config, $mtime1, $mtime2, $data = null, $handler = false)
    {
        if (self::$instance === false) return;

        if (DEBUG_LEVEL === 1) {
            self::$instance->mysqlList[] = array($label, json_encode($params), json_encode($config), $mtime1, $mtime2, null, $handler);
        } else {
            self::$instance->mysqlList[] = array($label, json_encode($params), json_encode($config), $mtime1, $mtime2, $data, $handler);
        }
    }

    /**
     * 记录运行时的memcache请求
     * @param $label
     * @param $params
     * @param $config
     * @param $mtime1
     * @param $mtime2
     * @param null $data
     * @param bool $handler
     */
    public static function _cache($label, $params, $config, $mtime1, $mtime2, $data = null, $handler = false)
    {
        if (self::$instance === false) return;

        if (DEBUG_LEVEL === 1) {
            self::$instance->cacheList[] = array($label, json_encode($params), json_encode($config), $mtime1, $mtime2, null, $handler);
        } else {
            self::$instance->cacheList[] = array($label, json_encode($params), json_encode($config), $mtime1, $mtime2, $data, $handler);
        }
    }

    /**
     * 输出日志
     */
    public static function _show()
    {
        if (self::$instance === false) return;

        if (isset($_SERVER['HTTP_USER_AGENT'])) {
            // 界面上可视化模式输出内容
            self::$instance->showViews();
        } else {
            self::$instance->writeLogs();
        }
    }

    /**
     * 是否有可视化界面输出,HTML代码直接返回到浏览器
     * @return bool
     */
    public static function _is_show_view()
    {
        if (self::$instance && isset($_SERVER['HTTP_USER_AGENT'])) return true;

        return false;
    }

    /**
     * 将microtime的时间字符串转换为float型的毫秒时间
     * @param $mt mixed
     * @return float|int
     */
    private function _floatMicrotime($mt)
    {
        if (strpos($mt, ' ')) {
            list($ms, $m) = explode(' ', $mt);
            return ($m + $ms) * 1000;
        }

        return floatval($mt) * 1000;
    }

    /**
     * 计算两个microtime时间的间隔时间
     * @param $m1 mixed 开始时间
     * @param $m2 mixed 结束时间
     * @param int $round 保留小数位
     * @return float
     */
    private function _intervalTime($m1, $m2, $round = 3) {
        return round(($this->_floatMicrotime($m2) - $this->_floatMicrotime($m1)), $round);
    }

    /**
     * 将调试信息生成可视化的HTML代码
     */
    private function showViews()
    {
        $showTime = microtime();
        $output = array();
        $output[] = "\n";
        $output[] = '
    '; $output[] = '
  • DebugLog showViews.total process time is ' . $this->_intervalTime($this->logId, $showTime) . 'ms
  • '; if ($this->timeList) { $total_num = count($this->timeList); $output[] = '
  • TimeList total count is ' . count($this->timeList) . ', log time is ' . $this->_intervalTime($this->logId, $this->timeList[$total_num - 1][1]) . '
  • '; $lasttime = $this->logId; $output[] = '
  • 0.000 : start debug log ' . $lasttime . '
  • '; foreach ($this->timeList as $info) { $lasttime2 = $info[1]; $output[] = '
  • '. $this->_intervalTime($lasttime, $lasttime2) . ' : ' . implode("\t", $info) . '
  • '; $lasttime = $lasttime2; } } if ($this->logList) { $output[] = '
  • LogList total count is ' . count($this->logList) . '
  • '; foreach ($this->logList as $info) { $output[] = '
  • ' . implode("\t", $info) . '
  • '; } } if ($this->httpList) { $current = count($output); $total_time = 0; $output[] = null; $max_num = array(); $multi_num = array(); foreach ($this->httpList as $info) { $intval = $this->_intervalTime($info[3], $info[4]); $multi_flag = @json_decode($info[2],true); if(isset($multi_flag) && isset($multi_flag['is_multi']) && $multi_flag['is_multi']==1) { $multi_str = strval($multi_flag['multi_num']); if($intval > $max_num[$multi_str]) { $max_num[$multi_str] = $intval; if(!in_array($multi_str, $multi_num)) { $multi_num[] = $multi_str; } } } else { $total_time += $intval; } if ($info[5] && is_array($info[5])) { $info[5] = json_encode($info[5]); } $output[] = '
  • '. $intval .' : ' . implode("\t", $info) . '
  • '; } if(!empty($multi_num )) { foreach($multi_num as $val) { $total_time += $max_num[$val]; } } $output[$current] = '
  • HttpList total count is ' . count($this->httpList) . ', total time is ' . $total_time . '
  • '; } if ($this->redisList) { $current = count($output); $total_time = 0; $output[] = null; foreach ($this->redisList as $info) { $intval = $this->_intervalTime($info[3], $info[4]); $total_time += $intval; if ($info[5] && is_array($info[5])) { $info[5] = json_encode($info[5]); } $output[] = '
  • '. $intval .' : ' . implode("\t", $info) . '
  • '; } $output[$current] = '
  • RedisList total count is ' . count($this->redisList) . ', total time is ' . $total_time . '
  • '; } if ($this->mysqlList) { $current = count($output); $total_time = 0; $output[] = null; foreach ($this->mysqlList as $info) { $intval = $this->_intervalTime($info[3], $info[4]); $total_time += $intval; if ($info[5] && is_array($info[5])) { $info[5] = json_encode($info[5]); } elseif (!$info[5]) { $info[5] = ''; } $output[] = '
  • '. $intval .' : ' . implode("\t", $info) . '
  • '; } $output[$current] = '
  • MysqlList total count is ' . count($this->mysqlList) . ', total time is ' . $total_time . '
  • '; } if ($this->cacheList) { $current = count($output); $total_time = 0; $output[] = null; foreach ($this->cacheList as $info) { $intval = $this->_intervalTime($info[3], $info[4]); $total_time += $intval; if ($info[5] && is_array($info[5])) { $info[5] = json_encode($info[5]); } $output[] = '
  • '. $intval .' : ' . implode("\t", $info) . '
  • '; } $output[$current] = '
  • CacheList total count is ' . count($this->cacheList) . ', total time is ' . $total_time . '
  • '; } $output[] = '
'; echo implode("\n", $output); } /** * 将调试日志写入到本地文件中,使用JSON格式保存为一行 */ public function writeLogs() { $showTime = microtime(); if (!defined('DEBUG_LOG_PATH')) { define('DEBUG_LOG_PATH', '/var/log/'); } $serverList = array( 'SCRIPT_NAME' => $_SERVER['SCRIPT_NAME'], 'REQUEST_URI' => $_SERVER['REQUEST_URI'], 'REMOTE_ADDR:PORT' => $_SERVER['REMOTE_ADDR'] . ':' . $_SERVER['REMOTE_PORT'], ); $datalist = array( 'logId'=>$this->logId, 'logTime'=>$showTime, 'timeList'=>$this->timeList, 'logList'=>$this->logList, 'httpList'=>$this->httpList, 'redisList'=>$this->redisList, 'mysqlList'=>$this->mysqlList, 'server'=>$serverList, ); $str = json_encode($datalist); $str = str_replace("\n", ' ', $str); $str .= "\n"; $file_path = DEBUG_LOG_PATH . 'discuz_debug.log'; if($fd = @fopen($file_path, 'a')) { fputs($fd, $str); fclose($fd); } } /** * 将消息输出到指定的文件 * 默认 define('DEBUG_LOG_PATH', '~/log/php/today/') * @param $msg * @param string $file */ public static function writeDebugLog($msg, $file='discuz_php.log') { $dtime = date('Y-m-d H:i:s'); if (!defined('DEBUG_LOG_PATH')) { $default_path = '/var/log/'; if (file_exists($default_path)) { define('DEBUG_LOG_PATH', $default_path); } else { define('DEBUG_LOG_PATH', ''); } } $str_cookie = 'no cookie'; $str_server = json_encode(array($_SERVER['HTTP_X_FORWARDED_FOR'], $_SERVER['REMOTE_ADDR'], $_SERVER['HTTP_HOST'], $_SERVER['REQUEST_URI'])); $str = "[$dtime]||$msg||$str_cookie||$str_server\n"; $file_path = DEBUG_LOG_PATH . $file; if($fd = @fopen($file_path, 'a')) { fputs($fd, $str); fclose($fd); } } /** * 通过PHP的 debug_backtrace 可以详细的查看到方法调用的细节情况 * @param int $deep * @param bool $all */ public static function writeBacktrace($deep=3, $all=false) { $result = array(); $trace = debug_backtrace(); unset($trace[0]); if ($deep < count($trace)) { for ($i = 1; $i <= $deep; $i++) { $info = $trace[$i]; if (isset($info['object']) && $all === false) { unset($info['object']); } $result[] = $info; } } elseif ($all === false) { foreach ($trace as $info) { if (isset($info['object'])) { unset($info['object']); } $result[] = $info; } } else { $result = $trace; } self::writeDebugLog(json_encode($result), 'backtrace.log'); } } ```

Rdis封装类

  1. 创建配置文件config/redis.ini.php

     array(
            'host' => '127.0.0.1',
            'port' => '6379',
            'timeout' => 5,
            'pconnect' => 1,
            'password' => ''
        )
    );
    
    
    $config['redis']['instance2'] = array(
        'default' => array(
            'host' => '127.0.0.1',
            'port' => '6379',
            'timeout' => 5,
            'pconnect' => 1,
            'password' => ''
        )
    );
    
  2. 创建Helpers目录和Helpers/RedisHelper.php

    _config_name = $config_name;
                $this->_redis_config = $redis_config;
                $this->_server_region = $server_region;
                $this->timeout = isset($this->_redis_config[$server_region]['timeout']) ?
                    $this->_redis_config[$server_region]['timeout'] : $this->timeout;
    
                try {
                    $this->_redis = new \Redis();
                    $this->_redis->connect($this->_redis_config[$server_region]['host'],
                        $this->_redis_config[$server_region]['port'], $this->timeout);
                    $password = $this->_redis_config[$server_region]['password'];
                    if ($password && !$this->_redis->auth($password)) {
                        $this->_redis = null;
                    }
                } catch (\Exception $exception) {
                    $this->_redis = null;
                }
    
            } else {
                $this->_redis = null;
            }
        }
    
        /**
         * Redis实例公开方法
         * @param $config_name string 配置名称
         * @param $redis_config array 配置信息
         * @param $server_region string 作用域
         * @return bool|mixed
         */
        public static function instance($config_name, $redis_config, $server_region)
        {
            if (!$config_name || !$redis_config) {
                return false;
            }
    
            // 判断实例是否存在,
            //
            $starttime = microtime();
            $only_key = $config_name . ':' . $server_region;
            if (!isset(self::$instances[$only_key])) {
                try {
                    self::$instances[$only_key] = new RedisHelper($config_name, $redis_config, $server_region);
                    self::$connect_error = 0;
                } catch (\Exception $exception) {
                    if (self::$connect_error < 2) {
                        self::$connect_error += 1;
                        return self::instance($config_name, $redis_config, $server_region);
                    } else {
                        self::$connect_error = 0;
                        self::$instances[$only_key] = new RedisHelper(false, false, false);
                    }
                }
            }
    
            $redis_config_info = array();
            if ($redis_config && isset($redis_config[$server_region]) && isset($redis_config[$server_region]['password']))
            {
                $redis_config_info = $redis_config[$server_region];
                unset($redis_config_info['password']);
            }
    
            DebugLog::_redis('redis_instance', $config_name, $redis_config_info, $starttime, microtime(), null);
            self::$connect_error = 0;
            return self::$instances[$only_key];
        }
    
        /**
         * 魔术方法, 没有定义的方法都会走这边
         * @param $name
         * @param $arguments
         * @return array|bool|mixed
         */
        public function __call($name, $arguments)
        {
            if (!$this->_redis) {
                return false;
            }
    
            $starttime = microtime();
            try {
                if ('scan' == $name) {
                    $data = $this->_redis->scan($arguments[0]);
                } else {
                    $data = call_user_func_array(array($this->_redis, $name), $arguments);
                }
            } catch (\Exception $exception) {
                if ($this->call_error < 2) {
                    $this->call_error++;
                    return call_user_func_array(array($this->_redis, $name), $arguments);
                } else {
                    $this->call_error = 0;
                }
                $data = false;
            }
            $this->call_error = 0;
            $redis_config = $this->_redis_config[$this->_server_region];
            if ($redis_config && isset($redis_config['password'])) {
                unset($redis_config['password']);
            }
    
            DebugLog::_redis($name, $arguments, $redis_config, $starttime, microtime(),
                (is_string($data) || is_array($data)) ? $data : null);
    
            return $data;
        }
    
        /**
         * 销毁
         */
        public function __destruct()
        {
            if ($this->_redis != NULL) {
                $this->_redis->close();
            }
        }
        
    }
    
  3. 封装RedisHelper,创建Common/Datasource.php

MySQL封装类

  1. 创建配置文件config/db.ini.php

     '127.0.0.1:3306',
        'dbname' => 'miaosha',
        'user' => 'root',
        'password' => '',
    );
    
    // 从库
    $config['db']['slave'] = array(
        'host' => '127.0.0.1:3306',
        'dbname' => 'miaosha',
        'user' => 'root',
        'password' => '',
    );
    
  2. 在app/Mysql目录下创建Db.php

```
Connect($name);
        $this->parameters = array();
    }

    /**
     *  This method makes connection to the database.
     *
     *  1. Reads the database settings from a ini file.
     *  2. Puts  the ini content into the settings array.
     *  3. Tries to connect to the database.
     *  4. If connection failed, exception is displayed and a log file gets created.
     */
    private function Connect($name = 'master')
    {
        // 全局配置信息
        global $config;
        $mtime1 = microtime();
        $this->settings = $config['db'][$name];
        $dsn            = 'mysql:dbname=' . $this->settings["dbname"] . ';host=' . $this->settings["host"] . '';
        try {
            # Read settings from INI file, set UTF8
            $this->pdo = new PDO($dsn, $this->settings["user"], $this->settings["password"], array(
                PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"
            ));

            # We can now log any exceptions on Fatal error.
            $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

            # Disable emulation of prepared statements, use REAL prepared statements instead.
            $this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);

            # Connection succeeded, set the boolean to true.
            $this->bConnected = true;
        }
        catch (PDOException $e) {
            # Write into log
            print_r($e);
            echo $this->ExceptionLog($e->getMessage());
            die();
        }

        $mtime2 = microtime();
        // 输出日志
        DebugLog::_mysql('connect', null, array('host' => $this->settings['host'],
            'dbname' => $this->settings['dbname']), $mtime1, $mtime2, null);
    }

    /*
     *   You can use this little method if you want to close the PDO connection
     *
     */
    public function CloseConnection()
    {
        # Set the PDO object to null to close the connection
        # http://www.php.net/manual/en/pdo.connections.php
        $this->pdo = null;
    }

    /**
     *  Every method which needs to execute a SQL query uses this method.
     *
     *  1. If not connected, connect to the database.
     *  2. Prepare Query.
     *  3. Parameterize Query.
     *  4. Execute Query.
     *  5. On exception : Write Exception into the log + SQL query.
     *  6. Reset the Parameters.
     */
    private function Init($query, $parameters = "")
    {
        # Connect to database
        if (!$this->bConnected) {
            $this->Connect();
        }
        try {
            # Prepare query
            $this->sQuery = $this->pdo->prepare($query);

            # Add parameters to the parameter array
            if ($parameters && isset($parameters[0])) {
                // ? 占位符形式
                # Execute SQL
                $this->sQuery->execute($this->parameters);
            } else {
                // : fieldname 字段名形式
                $this->bindMore($parameters);

                # Bind parameters
                if (!empty($this->parameters)) {
                    foreach ($this->parameters as $param) {

                        $parameters = explode("\x7F", $param);
                        $this->sQuery->bindParam($parameters[0], $parameters[1]);

//                        if(is_int($value[1])) {
//                            $type = PDO::PARAM_INT;
//                        } else if(is_bool($value[1])) {
//                            $type = PDO::PARAM_BOOL;
//                        } else if(is_null($value[1])) {
//                            $type = PDO::PARAM_NULL;
//                        } else {
//                            $type = PDO::PARAM_STR;
//                        }
//                        // Add type when binding the values to the column
//                        $this->sQuery->bindValue($value[0], $value[1], $type);
                    }
                }

                # Execute SQL
                $this->sQuery->execute();
            }

        }
        catch (PDOException $e) {
            # Write into log and display Exception
            echo $this->ExceptionLog($e->getMessage(), $query);
            die();
        }

        # Reset the parameters
        $this->parameters = array();
    }

    /**
     *  @void
     *
     *  Add the parameter to the parameter array
     *  @param string $para
     *  @param string $value
     */
    public function bind($para, $value)
    {
        if (is_array($para)) {
            $para = json_encode($para);
        }

        if (is_array($value)) {
            $value = json_encode($value);
        }

        $this->parameters[sizeof($this->parameters)] = ":" . $para . "\x7F" . $value;

//        $this->parameters[sizeof($this->parameters)] = [":" . $para , $value];
    }
    /**
     *  @void
     *
     *  Add more parameters to the parameter array
     *  @param array $parray
     */
    public function bindMore($parray)
    {
        if (empty($this->parameters) && is_array($parray)) {
            $columns = array_keys($parray);
            foreach ($columns as $i => &$column) {
                $this->bind($column, $parray[$column]);
            }
        }
    }
    /**
     *  If the SQL query  contains a SELECT or SHOW statement it returns an array containing all of the result set row
     *  If the SQL statement is a DELETE, INSERT, or UPDATE statement it returns the number of affected rows
     *
     *      @param  string $query
     *  @param  array  $params
     *  @param  int    $fetchmode
     *  @return mixed
     */
    public function query($query, $params = null, $fetchmode = PDO::FETCH_ASSOC)
    {
        $mtime1 = microtime();
        $query = trim($query);
//        $query = trim(str_replace("\r", " ", $query));

        $this->Init($query, $params);

        $rawStatement = explode(" ", $query);
//        $rawStatement = explode(" ", preg_replace("/\s+|\t+|\n+/", " ", $query));

        # Which SQL statement is used
        $statement = strtolower($rawStatement[0]);

        $result = NULL;
        if ($statement === 'select' || $statement === 'show') {
            $result = $this->sQuery->fetchAll($fetchmode);
        } elseif ($statement === 'insert' || $statement === 'update' || $statement === 'delete') {
            $result = $this->sQuery->rowCount();
        }

        $mtime2 = microtime();
        DebugLog::_mysql('query: ' . $query, $params, array('host' => $this->settings['host'],
            'dbname' => $this->settings['dbname']), $mtime1, $mtime2, $result);

        return $result;
    }

    /**
     *  Returns the last inserted id.
     *  @return string
     */
    public function lastInsertId()
    {
        return $this->pdo->lastInsertId();
    }

    /**
     * Starts the transaction
     * @return boolean, true on success or false on failure
     */
    public function beginTransaction()
    {
        return $this->pdo->beginTransaction();
    }

    /**
     *  Execute Transaction
     *  @return boolean, true on success or false on failure
     */
    public function executeTransaction()
    {
        return $this->pdo->commit();
    }

    /**
     *  Rollback of Transaction
     *  @return boolean, true on success or false on failure
     */
    public function rollBack()
    {
        return $this->pdo->rollBack();
    }

    /**
     *  Returns an array which represents a column from the result set
     *
     *  @param  string $query
     *  @param  array  $params
     *  @return array
     */
    public function column($query, $params = null)
    {
        $mtime1 = microtime();
        $this->Init($query, $params);
        $Columns = $this->sQuery->fetchAll(PDO::FETCH_NUM);

        $column = null;

        foreach ($Columns as $cells) {
            $column[] = $cells[0];
        }

        $mtime2 = microtime();
        DebugLog::_mysql('column: ' . $query, $params, array('host' => $this->settings['host'],
            'dbname' => $this->settings['dbname']), $mtime1, $mtime2, $column);

        return $column;

    }
    /**
     *  Returns an array which represents a row from the result set
     *
     *  @param  string $query
     *  @param  array  $params
     *      @param  int    $fetchmode
     *  @return array
     */
    public function row($query, $params = null, $fetchmode = PDO::FETCH_ASSOC)
    {
        $mtime1 = microtime();
        $this->Init($query, $params);
        $result = $this->sQuery->fetch($fetchmode);
//        $this->sQuery->closeCursor(); // Frees up the connection to the server so that other SQL statements may be issued,
        $mtime2 = microtime();
        DebugLog::_mysql('row: ' . $query, $params, array('host' => $this->settings['host'],
            'dbname' => $this->settings['dbname']), $mtime1, $mtime2, $result);

        return $result;
    }
    /**
     *  Returns the value of one single field/column
     *
     *  @param  string $query
     *  @param  array  $params
     *  @return string
     */
    public function single($query, $params = null)
    {
        $mtime1 = microtime();
        $this->Init($query, $params);
        $result = $this->sQuery->fetchColumn();
        $mtime2 = microtime();

        DebugLog::_mysql('single: ' . $query, $params, array('host' => $this->settings['host'],
            'dbname' => $this->settings['dbname']), $mtime1, $mtime2, $result);

//        $this->sQuery->closeCursor(); // Frees up the connection to the server so that other SQL statements may be issued
        return $result;
    }

    /**
     * Writes the log and returns the exception
     *
     * @param  string $message
     * @param  string $sql
     * @return string
     */
    private function ExceptionLog($message, $sql = "")
    {
        $exception = 'Unhandled Exception. 
'; if (!empty($sql)) { # Add the Raw SQL to the Log $message .= "\r\nRaw SQL : " . $sql; $exception .= $message; $exception .= "
You can find the error back in the log."; return $exception; } # Write into log // $this->log->write($message); return ''; } } ?> ```
  1. 在app/Mysql目录下创建Crud.php

    fields && $data) {
                foreach ($data as $k => $d) {
                    if (!in_array($k, $this->fields)) {
                        unset($data[$k]);
                    }
                }
            }
            $this->variables  = $data;
        }
    
        public function setDb($db) {
            $this->db = $db;
        }
    
        public function getDb() {
            if (!$this->db) {
                $this->db = DB::getInstance('master');
            }
            return $this->db;
        }
    
        public function __set($name,$value){
            if(strtolower($name) === $this->pk) {
                $this->variables[$this->pk] = $value;
            }
            else {
                if (!$this->fields || in_array($name, $this->fields)) {
                    $this->variables[$name] = $value;
                }
            }
        }
    
        public function __get($name)
        {
            if(is_array($this->variables)) {
                if(array_key_exists($name,$this->variables)) {
                    return $this->variables[$name];
                }
            }
    
            $trace = debug_backtrace();
            trigger_error(
                'Undefined property via __get(): ' . $name .
                ' in ' . $trace[0]['file'] .
                ' on line ' . $trace[0]['line'],
                E_USER_NOTICE);
            return null;
        }
    
        public function save($id = "0") {
            $this->variables[$this->pk] = $id ? $id : $this->variables[$this->pk];
    
            $fieldsvals = '';
            $columns = array_keys($this->variables);
    
            foreach($columns as $column)
            {
                if($column !== $this->pk)
                    $fieldsvals .= "`{$column}` = :". $column . ",";
            }
    
            $fieldsvals = substr_replace($fieldsvals , '', -1);
    
            if(count($columns) > 1 ) {
                $sql = "UPDATE `" . $this->table .  "` SET " . $fieldsvals . " WHERE `" . $this->pk . "`= :" . $this->pk;
                return $this->getDb()->query($sql,$this->variables);
            }
        }
    
        public function create() {
            $bindings       = $this->variables;
    
            if(!empty($bindings)) {
                $fields     =  array_keys($bindings);
                $fieldsvals =  array('`' . implode("`,`",$fields) . '`', ":" . implode(",:",$fields));
                $sql        = "INSERT INTO `".$this->table."` (".$fieldsvals[0].") VALUES (".$fieldsvals[1].")";
            }
            else {
                $sql        = "INSERT INTO `".$this->table."` () VALUES ()";
            }
    
            $ok = $this->getDb()->query($sql,$bindings);
            if ($ok) {
                return $this->getDB()->lastInsertId();
            } else {
                return $ok;
            }
        }
    
        public function delete($id = "") {
            $id = (empty($this->variables[$this->pk])) ? $id : $this->variables[$this->pk];
    
            if(!empty($id)) {
                $sql = "DELETE FROM `" . $this->table . "` WHERE `" . $this->pk . "`= :" . $this->pk. " LIMIT 1" ;
                return $this->getDb()->query($sql,array($this->pk=>$id));
            }
        }
    
        public function get($id = "") {
            $id = $id ? $id : $this->variables[$this->pk];
    
            if(!empty($id)) {
                $sql = "SELECT * FROM `" . $this->table ."` WHERE `" . $this->pk . "`= :" . $this->pk . " LIMIT 1";
                $this->variables = $this->getDb()->row($sql,array($this->pk=>$id));
            }
            return $this->variables;
        }
    
        public function all(){
            return $this->getDb()->query("SELECT * FROM `" . $this->table . '`');
        }
    
        public function count(){
            return $this->getDb()->query("SELECT COUNT(1) FROM `" . $this->table . '`');
        }
    
    //  public function min($field)  {
    //      if($field)
    //      return $this->getDb()->single("SELECT min(" . $field . ")" . " FROM " . $this->table);
    //  }
    //
    //  public function max($field)  {
    //      if($field)
    //      return $this->getDb()->single("SELECT max(" . $field . ")" . " FROM " . $this->table);
    //  }
    //
    //  public function avg($field)  {
    //      if($field)
    //      return $this->getDb()->single("SELECT avg(" . $field . ")" . " FROM " . $this->table);
    //  }
    //
    //  public function sum($field)  {
    //      if($field)
    //      return $this->getDb()->single("SELECT sum(" . $field . ")" . " FROM " . $this->table);
    //  }
    
    }
    

你可能感兴趣的:(秒杀系统开发(一))