天天看點

opencart 源碼解析

//通路index.php,安全過濾、加載配置檔案、核心啟動檔案、函數庫、類庫
//轉載請注明: http://blog.csdn.net/dabao1989/article/details/21223585
//new系統資料庫
$registry = new Registry();

//$registry裡儲存的config是根據目前店店鋪(`oc_store`)擷取`store_id`,然後到`oc_setting`裡取出該店的配置資訊,跟配置檔案config.php無關
$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 $setting) {
	if (!$setting['serialized']) {
		$config->set($setting['key'], $setting['value']);
	} else {
		$config->set($setting['key'], unserialize($setting['value']));
	}
}



//依次new核心類,壓入$registry,最後幾個類需要目前$registry作為參數,new之後再重新壓入$registry
$registry->set('customer', new Customer($registry));

//new Front();加載前置Action
$controller = new Front($registry);
$controller->addPreAction(new Action('common/seo_url'));	
$controller->addPreAction(new Action('common/maintenance'));
	
//根據目前URL,加載指定Action,預設加載Action('common/home');
if (isset($request->get['route'])) {
	$action = new Action($request->get['route']);
} else {
	$action = new Action('common/home');
}

//new Action()所進行的操作,根據URL路由,判斷是否是一個控制器檔案,如果是,break
//儲存此控制器檔案路徑、目前需要執行的方法、參數等,假如方法名為空則要執行的方法名儲存為index
function __construct($route, $args = array()) {
        $path = '';

        $parts = explode('/', str_replace('../', '', (string)$route));

        foreach ($parts as $part) { 
                $path .= $part;

                if (is_dir(DIR_APPLICATION . 'controller/' . $path)) {
                        $path .= '/';

                        array_shift($parts);

                        continue;
                }

                if (is_file(DIR_APPLICATION . 'controller/' . str_replace(array('../', '..\\', '..'), '', $path) . '.php')) {
                        $this->file = DIR_APPLICATION . 'controller/' . str_replace(array('../', '..\\', '..'), '', $path) . '.php';

                        $this->class = 'Controller' . preg_replace('/[^a-zA-Z0-9]/', '', $path);

                        array_shift($parts);

                        break;
                }
        }

        if ($args) {
                $this->args = $args;
        }

        $method = array_shift($parts);

        if ($method) {
                $this->method = $method;
        } else {
                $this->method = 'index';//預設index
        }
}

//Front執行控制器, 先執行之前壓入的前置控制器, 然後執行目前請求的控制器, 失敗則執行控制器error/not_found
//前置控制器有seo_url,maintenance,背景應該可配置SEO,可能會根據SEO建立新的控制器并傳回,覆寫,執行
//Front->dispatch()會調用$this->execute()執行控制器
//execute()内部通過調用is_callable()判斷控制器方法是否可以調用,是以可以在子Controller中将index()定義為protected,防止直接調用
//當直接調用時child Controller中的protected方法時,将會轉到error/not_found
$controller->dispatch($action, new Action('error/not_found'));
function dispatch($action, $error) {
        $this->error = $error;

        foreach ($this->pre_action as $pre_action) {
                $result = $this->execute($pre_action);

                if ($result) {
                        $action = $result;//重置

                        break;
                }
        }

        while ($action) {
                $action = $this->execute($action);
        }
}
function execute($action) {
        if (file_exists($action->getFile())) {
                require_once($action->getFile());

                $class = $action->getClass();

                $controller = new $class($this->registry);

                if (is_callable(array($controller, $action->getMethod()))) {
                        $action = call_user_func_array(array($controller, $action->getMethod()), $action->getArgs());
                } else {
                        $action = $this->error;//當不可用時,會調用$this->error

                        $this->error = '';
                }
        } else {
                $action = $this->error;

                $this->error = '';
        }

        return $action;
}

//假設執行的控制器是common/home, 沒有指定方法, 則執行index()
class ControllerCommonHome extends Controller {
	public function index() {
		$this->document->setTitle($this->config->get('config_title'));
		$this->document->setDescription($this->config->get('config_meta_description'));

		$this->data['heading_title'] = $this->config->get('config_title');
		
		if (file_exists(DIR_TEMPLATE . $this->config->get('config_template') . '/template/common/home.tpl')) {
			$this->template = $this->config->get('config_template') . '/template/common/home.tpl';
		} else {
			$this->template = 'default/template/common/home.tpl';
		}
		
		$this->children = array(
			'common/column_left',
			'common/column_right',
			'common/content_top',
			'common/content_bottom',
			'common/footer',
			'common/header'
		);
										
		$this->response->setOutput($this->render());
	}
}

//Controller中的render()方法
//會判斷children屬性是否指派,有的話會逐個new child Controller,child Controller仍可以$this->render(),但調用$this->render()必須設定$this->template
//$this->data數組存儲的值可在模闆中使用
//$this->data[basename[$child]]儲存的是子模闆,common/home.tpl可以通過調用echo $column_left、$column_right、$content_top、$content_bottom輸出
//應注意$this->data數組中的鍵值不要和子模闆名重複!
function render() {
        foreach ($this->children as $child) {
                $this->data[basename($child)] = $this->getChild($child);
        }

        if (file_exists(DIR_TEMPLATE . $this->template)) {
                extract($this->data);//模闆中使用

        ob_start();

                require(DIR_TEMPLATE . $this->template);

                $this->output = ob_get_contents();

        ob_end_clean();

                return $this->output;
} else {
                trigger_error('Error: Could not load template ' . DIR_TEMPLATE . $this->template . '!');
                exit();				
}
}

//controller中的getChild()方法
//隻有在child Controller中執行了$this->render(),父Controller在$this->data[basename($child)]才能捕獲到子模闆,否則捕獲的是空字元串
//雖然child Controller中的index()被定義為protected, 但是可以被父Controller調用, 因為他們都繼承自Controller
function getChild($child, $args = array()) {
        $action = new Action($child, $args);

        if (file_exists($action->getFile())) {
                require_once($action->getFile());

                $class = $action->getClass();

                $controller = new $class($this->registry);

                $controller->{$action->getMethod()}($action->getArgs());

                return $controller->output;
        } else {
                trigger_error('Error: Could not load controller ' . $child . '!');
                exit();					
        }		
}

//在主Controlle中,this->render()傳回的模闆需要傳送給$this->response->setOutput()才能輸出
$this->response->setOutput($this->render());

//Controller中的語言包使用,$this->language儲存在$registry中
$this->language->load('common/footer');
$this->data['text_information'] = $this->language->get('text_information');
$this->data['text_service'] = $this->language->get('text_service');

//Controller中session使用,$this->session儲存在$registry中
$this->session->data[$key];
$this->session->data[$key] = 'hello';

//Controller中的$this->request數組
$this->get = $_GET;
$this->post = $_POST;
$this->request = $_REQUEST;
$this->cookie = $_COOKIE;
$this->files = $_FILES;
$this->server = $_SERVER;
$this->request->get['route'];//擷取方式

//Controller中的$this->response使用
$this->response->setOutput($this->render());
$this->response->setOutput(json_encode($json));
$this->response->addHeader('Content-Type: application/xml');

//Controller中的$this->customer
$this->customer->isLogged();//判斷登入狀态
$this->customer->logout();//退出操作
$this->customer->login();//登入操作
$this->getId();//擷取使用者ID,存儲在customer表中的customer_id

//Controller中的頁面跳轉,$url->link()用于生成控制器的連結,如下
$this->redirect($this->url->link('catalog/information', 'token=' . $this->session->data['token'] . $url, 'SSL'));

//Controller中的forward(),用于new 新的控制器,并傳回
function forward($route, $args = array()) {
        return new Action($route, $args);
}

//Controller中的模型加載
$this->load->model('catalog/category');
$this->model_catalog_category;//使用格式

//cache使用(原始使用檔案緩存)
$this->cache->get($key);
$this->cache->set($key, $value);
$this->cache->delete($key);

//js,css等靜态檔案加載,通過Document對象加載額外的JS,CSS連結,在common子模闆中會自動輸出
$registry->document;
$registry->document->addLink($href, $rel);
$registry->addStyle($href, $rel = 'stylesheet', $media = 'screen');
$registry->addScript($script);

//币種


//模型機制
//在index.php建立資料庫連接配接,并儲存在$registry,根據DB_DRIVER不同,調用system/database/下指定的資料庫引擎
//擁有4個方法:query(),escape(),countAffected(),getLastId()
//當驗證外部資料時要用escape()進行安全過濾
$db = new DB(DB_DRIVER, DB_HOSTNAME, DB_USERNAME, DB_PASSWORD, DB_DATABASE);
$registry->set('db', $db);

//資料表解析
//oc_setting 配置
//oc_url_alias 配合apache rewrite,進行商品及分類URL優化