zoukankan      html  css  js  c++  java
  • opencart 单入口文件简单分析

    opencart 单入口文件简单分析

    opencart是基于mvcl的商城系统,据说是一个外国有人单独开发。比较牛叉。但是又不大符合国人习惯,目前国内opencart社区也是不少。

    简单分析了下单入口,感觉在国内商家用起来略微臃肿,需要再改进去除部分代码。

    因为就在index.php上写了注释,在此贴出来,和大家一起学习。

    <?php
    // Version
    define('VERSION', '2.1.0.1');
    
    // 引入system初始化的配置文件,包括常量和数据库连接设置
    if (is_file('config.php')) {
        require_once('config.php');
    }
    
    // 如果没有设置系统所需常量,需要先安装
    if (!defined('DIR_APPLICATION')) {
        header('Location: install/index.php');
        exit;
    }
    
    // Startup引入start
    require_once(DIR_SYSTEM . 'startup.php');
    /**
     * system/startup.php做了如下事情:
     *
     * 1.对比php版本,magic_quotes_gpc问题,时区,判断https (ssl)问题,一般可以略过。
     *
     * 2.是否修正modifycation  override,目前都没有修改,
         直接返回system/engine下的各类。
     *   注册两个自动加载自定义函数,一个就是library下的N多个类文件,
         另一个是vendor下的scss(less 和scss是css的预处理器,不得不说php语言强大。。)
     *
     * 3.加载system/engine下的action controller event front 
          loader model registry类,注意传递$register的实例做为参数的类主要有
          loader event customer front,目的是做一个备份,
          以便处理其他controller时候处理不会丢失,
     *
     * 4.最后又require了helper下的几个函数
       (包括生成token,处理json,utf8字符串,欧盟增值税税率扒拉扒拉,
        就是那个vat文件,应该是value added tax)
     */
    
    
    /**
     * 接上:简单写下system/engine中各个类的作用
     * 1.action.php ,在构造函数中接受路由和传递参数,
         解析为controller目录下的类,excute方法new这个类并调用对应函数,
         作为具体执行
     * 
     * 2.controller 和modle为抽象类,供具体的controller和model继承使用,
          注意他们都涉及魔术方法
     *    __get和__set,用以方便调用一个不存在的方法后调用$register里的方法
         (看一下engine下的controller即可,比较简单),构造函数接受$register类。
     *
     * 3.loader.php 为final类,不能被继承和覆盖,
       主要是做controller文件夹 model文件夹, view helper文件夹
       (strup.php引入了helper啊??) 
       language config的一些加载,并且通过 view方法输出extract后的$data,
     *
     * 4.front.php 也是final类,在加载执行action之前执行
     * 5.registery.php,实现简单get set,主要目的是当作参数传递给以上几个类。
     * 6.event.php,还没看到具体用法,感觉像钩子,具有自定义的排序事件
     */
    
    
    // Registry,全局注册类,作为参数不断的往里压入
    $registry = new Registry();
    
    // Loader,不一定需要先加载loader,需要用到$this->loader的时候会用到此类。
    $loader = new Loader($registry);
    
    $registry->set('load', $loader);//此时register类里就有一个load指向了new 的loader类
    
    // Config 这个是关键,以下会从表里把各种值都循环塞进去
    $config = new Config();// 此处new的是/system/library/config.php,和register类似,但是他里面也有个load方法,load的是system/config下的文件(默认为空)
    $registry->set('config', $config);// 已经有两个了!load&config
    
    // Database new 的是library下的db,(都是因为spl_autoload_register('libaray')起作用的)DB的常量是根下config的常量
    $db = new DB(DB_DRIVER, DB_HOSTNAME, DB_USERNAME, DB_PASSWORD, DB_DATABASE, DB_PORT);
    $registry->set('db', $db);
    
    // Store 表一 store表 判断替换 ssl和url两个字段
    if (isset($_SERVER['HTTPS']) && (($_SERVER['HTTPS'] == 'on') || ($_SERVER['HTTPS'] == '1'))) {
        $store_query = $db->query("SELECT * FROM " . DB_PREFIX . "store WHERE REPLACE(`ssl`, 'www.', '') = '" . $db->escape('https://' . str_replace('www.', '', $_SERVER['HTTP_HOST']) . rtrim(dirname($_SERVER['PHP_SELF']), '/.\') . '/') . "'");
    } else {// 没有https,一般走下面
        $store_query = $db->query("SELECT * FROM " . DB_PREFIX . "store WHERE REPLACE(`url`, 'www.', '') = '" . $db->escape('http://' . str_replace('www.', '', $_SERVER['HTTP_HOST']) . rtrim(dirname($_SERVER['PHP_SELF']), '/.\') . '/') . "'");
    }
    
    if ($store_query->num_rows) {
        $config->set('config_store_id', $store_query->row['store_id']);
    } else {
    
        $config->set('config_store_id', 0);
    }
    
    // Settings 表2 setting表,读取setting表的信息,
    //循环所有的配置项,放在$config中,包括json格式的,
    $query = $db->query("SELECT * FROM `" . DB_PREFIX . "setting` WHERE store_id = '0' OR store_id = '" . (int)$config->get('config_store_id') . "' ORDER BY store_id ASC");
    
    foreach ($query->rows as $result) {
        if (!$result['serialized']) {
    // 开塞了 config
            $config->set($result['key'], $result['value']);
        } else {
            $config->set($result['key'], json_decode($result['value'], true));
        }
    }
    
    if (!$store_query->num_rows) {
        $config->set('config_url', HTTP_SERVER);
        $config->set('config_ssl', HTTPS_SERVER);
    }
    
    // Url library下的url,构造url,根据上面的config,我们常访问的?index.php?rount=a/b/c通过此类生成,传一个domain,是否ssl
    $url = new Url($config->get('config_url'), $config->get('config_secure') ? $config->get('config_ssl') : $config->get('config_url'));
    
    $registry->set('url', $url);
    
    // Log 读取的setting表中的config_error_filename配置字段,默认为storage/logs/error.log
    $log = new Log($config->get('config_error_filename'));
    $registry->set('log', $log);
    // 自定义错误函数
    function error_handler($code, $message, $file, $line) {
        global $log, $config;
    
    // error suppressed with @
        if (error_reporting() === 0) {
            return false;
        }
    
        switch ($code) {
            case E_NOTICE:
            case E_USER_NOTICE:
                $error = 'Notice';
                break;
            case E_WARNING:
            case E_USER_WARNING:
                $error = 'Warning';
                break;
            case E_ERROR:
            case E_USER_ERROR:
                $error = 'Fatal Error';
                break;
            default:
                $error = 'Unknown';
                break;
        }
    
        if ($config->get('config_error_display')) {
            echo '<b>' . $error . '</b>: ' . $message . ' in <b>' . $file . '</b> on line <b>' . $line . '</b>';
        }
    
        if ($config->get('config_error_log')) {
            $log->write('PHP ' . $error . ':  ' . $message . ' in ' . $file . ' on line ' . $line);
        }
    
        return true;
    }
    
    // Error Handler
    set_error_handler('error_handler');
    
    // Request 请求对象,把get post server cookie 等过滤,保留clean方法对外
    $request = new Request();
    $registry->set('request', $request);
    
    // Response 处理header头,压缩输出,定义跳转函数,setoutput 输出数据
    $response = new Response();
    $response->addHeader('Content-Type: text/html; charset=utf-8');
    $response->setCompression($config->get('config_compression'));
    $registry->set('response', $response);
    

    接上:貌似贴太多不能高亮~

    // Cache 默认有file cache,apc cache memcache ,不过要注意,5.4以下使用apc,5.5以上使用opcache吧
    // 可以参考 https://phphub.org/topics/301
    $cache = new Cache('file');
    $registry->set('cache', $cache);
    
    // Session 访问api接口时候,在后台系统设置-管理员-api中有体现,这个还不大明白
    if (isset($request->get['token']) && isset($request->get['route']) && substr($request->get['route'], 0, 4) == 'api/') {
        $db->query("DELETE FROM `" . DB_PREFIX . "api_session` WHERE TIMESTAMPADD(HOUR, 1, date_modified) < NOW()");
    
        $query = $db->query("SELECT DISTINCT * FROM `" . DB_PREFIX . "api` `a` LEFT JOIN `" . DB_PREFIX . "api_session` `as` ON (a.api_id = as.api_id) LEFT JOIN " . DB_PREFIX . "api_ip `ai` ON (as.api_id = ai.api_id) WHERE a.status = '1' AND as.token = '" . $db->escape($request->get['token']) . "' AND ai.ip = '" . $db->escape($request->server['REMOTE_ADDR']) . "'");
    
        if ($query->num_rows) {
            // Does not seem PHP is able to handle sessions as objects properly so so wrote my own class
            $session = new Session($query->row['session_id'], $query->row['session_name']);
            $registry->set('session', $session);
    
            // keep the session alive
            $db->query("UPDATE `" . DB_PREFIX . "api_session` SET date_modified = NOW() WHERE api_session_id = '" . $query->row['api_session_id'] . "'");
        }
    } else {
        $session = new Session();
        $registry->set('session', $session);
    }
    
    // Language Detection 表四 language 多语言控制
    $languages = array();
    
    $query = $db->query("SELECT * FROM `" . DB_PREFIX . "language` WHERE status = '1'");
    
    foreach ($query->rows as $result) {
        $languages[$result['code']] = $result;
    }
    
    if (isset($session->data['language']) && array_key_exists($session->data['language'], $languages)) {
        $code = $session->data['language'];
    } elseif (isset($request->cookie['language']) && array_key_exists($request->cookie['language'], $languages)) {
        $code = $request->cookie['language'];
    } else {
    // detect的意思是查明,发现o(∩_∩)o
        $detect = '';
    
    // 查看请求头中有没有设置语言,
        if (isset($request->server['HTTP_ACCEPT_LANGUAGE']) && $request->server['HTTP_ACCEPT_LANGUAGE']) {
            $browser_languages = explode(',', $request->server['HTTP_ACCEPT_LANGUAGE']);
    
            foreach ($browser_languages as $browser_language) {
                foreach ($languages as $key => $value) {
                    if ($value['status']) {
                        $locale = explode(',', $value['locale']);
    
                        if (in_array($browser_language, $locale)) {
                            $detect = $key;
                            break 2;
                        }
                    }
                }
            }
        }
    
        $code = $detect ? $detect : $config->get('config_language');
    }
    
    if (!isset($session->data['language']) || $session->data['language'] != $code) {
        $session->data['language'] = $code;
    }
    
    if (!isset($request->cookie['language']) || $request->cookie['language'] != $code) {
        setcookie('language', $code, time() + 60 * 60 * 24 * 30, '/', $request->server['HTTP_HOST']);
    }
    
    $config->set('config_language_id', $languages[$code]['language_id']);
    $config->set('config_language', $languages[$code]['code']);
    
    // Language
    $language = new Language($languages[$code]['directory']);
    $language->load($languages[$code]['directory']);
    $registry->set('language', $language);
    
    // Document seo优化和获取 资源
    $registry->set('document', new Document());
    
    // Customer 获取用户信息,登录,注销等
    $customer = new Customer($registry);
    $registry->set('customer', $customer);
    
    // Customer Group 用户分类
    if ($customer->isLogged()) {
        $config->set('config_customer_group_id', $customer->getGroupId());
    } elseif (isset($session->data['customer']) && isset($session->data['customer']['customer_group_id'])) {
        // For API calls 批量赛
        $config->set('config_customer_group_id', $session->data['customer']['customer_group_id']);
    } elseif (isset($session->data['guest']) && isset($session->data['guest']['customer_group_id'])) {
        $config->set('config_customer_group_id', $session->data['guest']['customer_group_id']);
    }
    
    // Tracking Code
    if (isset($request->get['tracking'])) {
        setcookie('tracking', $request->get['tracking'], time() + 3600 * 24 * 1000, '/');
    
        $db->query("UPDATE `" . DB_PREFIX . "marketing` SET clicks = (clicks + 1) WHERE code = '" . $db->escape($request->get['tracking']) . "'");
    }
    
    // 以下new的这些都依托于 startup.php中的spl_autoload_register('library');
    // Affiliate 隶属于成员,也是属于用户的信息获取,登录,注销等
    $registry->set('affiliate', new Affiliate($registry));
    
    // Currency // 货币
    $registry->set('currency', new Currency($registry));
    
    // Tax
    $registry->set('tax', new Tax($registry));
    
    // Weight
    $registry->set('weight', new Weight($registry));
    
    // Length
    $registry->set('length', new Length($registry));
    
    // Cart
    $registry->set('cart', new Cart($registry));
    
    // Encryption 加密,编码
    $registry->set('encryption', new Encryption($config->get('config_encryption')));
    
    // OpenBay Pro
    $registry->set('openbay', new Openbay($registry));
    
    // Event
    $event = new Event($registry);
    $registry->set('event', $event);
    
    $query = $db->query("SELECT * FROM " . DB_PREFIX . "event");
    
    foreach ($query->rows as $result) {
        $event->register($result['trigger'], $result['action']);
    }
    
    // Front new 了个front,会先执行addPreAcion,(可理解为先压入的前置控制器,),然后在dispatch中具体实现,dispatch接受第二个参数为错误控制器,error/not_found.php
    $controller = new Front($registry);
    
    /*new Action的作用:根据route判断是否为可用action,不可用默认为最后一个,
    如果最后一个也不可用,为index 。
    以下的两个addPre就是上面说的的前置控制器,可以理解为钩子吧,一个是seo,
    另外的应该是(还不知道,maintenance为保养的意思,用到再说),
    就是在dispatch时候,先执行前置的,在依次执行传进来的action
    (通过is_callable和call_user_function )
    */
    
    $controller->addPreAction(new Action('common/maintenance'));
    
    // SEO URL's
    $controller->addPreAction(new Action('common/seo_url'));
    
    // Router 通过request对象,接受route参数,如果没有(默认首页),action为common/home就是商城首页
    if (isset($request->get['route'])) {
        $action = new Action($request->get['route']);
    } else {
        echo 'default action';
        $action = new Action('common/hoe');
    }
    
    /*Dispatch 派遣,调度,分发,传入一个action实例,
    然后根据aciton类的构造函数得知method,然后在execute每个action
    */
    
    $controller->dispatch($action, new Action('error/not_found'));
    
    // 在每个具体的controller最后都会执行 setoutput()方法,
    //注意setoutput只接受一个参数,只不过在此是调用的load->view,通过ob得到数据
    
    // var_dump($response->getOutput());
    // 是一个string类型,最后输出
    
    $response->output();
  • 相关阅读:
    USACO2018 DEC(Platinum) (树上乱搞,期望+凸包)
    USACO2018 DEC (Gold) (dp,容斥+哈希,最短路)
    《信息学奥赛一本通》题库 1034 计算三角形面积——基础
    UNR#3 Day1——[ 堆+ST表+复杂度分析 ][ 结论 ][ 线段树合并 ]
    bzoj 4298 [ONTAK2015]Bajtocja——哈希+启发式合并
    玲珑杯#20 C 漆黑的太阳——莫队
    链表写法
    传址函数写法
    bzoj 4650 & 洛谷 P1117 优秀的拆分 —— 枚举关键点+后缀数组
    bzoj 2119 股市的预测 —— 枚举关键点+后缀数组
  • 原文地址:https://www.cnblogs.com/honeynm/p/5266812.html
Copyright © 2011-2022 走看看