2012-12-27 58 views
0

我剛寫了這個PHP跟蹤代碼,將包括在主題中,有一些簡單的統計數據。現在,由於它會處理很多請求,它需要堅實而快速。我的PHP水平不是那麼好,所以我正在尋找幫助/最佳實踐來優化這個壞男孩。我如何優化我的PHP函數和mysql請求

我寫了重定向所有請求到index.php的簡單的.htaccess文件,所以我可以處理與PHP請求URI:$_SERVER['REQUEST_URI'], /this-is-the-theme-slug /user-name

<?php 
/** MySQL Database Settings */ 
require dirname(__FILE__) . '/inc/database.php'; 

/** Klogger Log Framework*/ 
require dirname(__FILE__) . '/lib/KLogger.php'; 
$log = KLogger::instance(dirname(__FILE__).'/log/', KLogger::DEBUG); 

/** Process Request String **/ 
$request = $_SERVER['REQUEST_URI']; 
$ipaddr = $_SERVER['REMOTE_ADDR']; 

$uri = explode('/',$request); 
$slug = $uri[1]; 
$user = $uri[2]; 

/** Global Variables **/ 
$theme_id = NULL; 
$account_id = NULL; 


function process_request(){ 
    global $log, $slug, $user; 
    if(!empty($slug) && !empty($user)){ 

     $the_slug = validate_slug($slug); 
     $the_user = account_exists($user); 

     if($the_slug){ // if the slug is valid 
      if($the_user){ // and the user exists 
       // update entry 
       update_entry($user); 
      }else{ 
       // create new entry 
       create_entry($user); 
      } 
     } 

     return true; 
    }else{ 
     $log->logError('process_request:: Bad request'); 
     return false; 
    } 
} 

function validate_slug($slug){ 
    global $log, $theme_id; 

    $con = mysql_connect(DB_HOST,DB_USER,DB_PASSWORD); 
    if(!$con){ 
     $log->logError('validate_slug:: Database connection failed'); 
     return false; 
    }else{ 
     $select = mysql_select_db(DB_NAME, $con); 
     $query = sprintf("SELECT id FROM ".DB_PREFIX."themes WHERE slug='%s'", mysql_real_escape_string($slug)); 
     $result = mysql_query($query); 
     if(mysql_num_rows($result)==0){ 
      $log->logNotice('validate_slug:: Slug not found'); 
      return false; 
     }else{ 
      $theme_id = mysql_result($result,0); 
      return true; 
     } 
     mysql_close($con); 
    } 
} 

function account_exists($user){ 
    global $log, $account_id; 

    $con = mysql_connect(DB_HOST,DB_USER,DB_PASSWORD); 
    if(!$con){ 
     $log->logError('account_exists:: Database connection failed'); 
     return false; 
    }else{ 
     $select = mysql_select_db(DB_NAME, $con); 
     $query = sprintf("SELECT id FROM ".DB_PREFIX."stats WHERE account='%s'", mysql_real_escape_string($user)); 
     $result = mysql_query($query); 
     if(mysql_num_rows($result)==0){ 
      $log->logNotice('account_exists:: Account not found'); 
      return false; 
     }else{ 
      $account_id = mysql_result($result,0); 
      return true; 
     } 
     mysql_close($con); 
    } 
} 

function create_entry($user){ 
    global $log, $ipaddr, $theme_id; 

    $con = mysql_connect(DB_HOST,DB_USER,DB_PASSWORD); 
    if(!$con){ 
     $log->logError('create_entry:: Database connection failed'); 
     return false; 
    }else{ 
     $select = mysql_select_db(DB_NAME, $con); 
     $query = sprintf("INSERT INTO ".DB_PREFIX."stats (id,active,account,date,ip,hits,theme) VALUES ('','1','%s',NOW(),'".$ipaddr."','1','".$theme_id."')", mysql_real_escape_string($user)); 
     $result = mysql_query($query); 

     $log->logNotice('create_entry:: Account created with id '.mysql_insert_id()); 
     return true; 
    } 
    mysql_close($con); 
} 


function update_entry($user){ 
    global $log, $ipaddr, $account_id; 
    $con = mysql_connect(DB_HOST,DB_USER,DB_PASSWORD); 
    if (!$con) { 
     $log->logError('update_entry:: Database connection failed'); 
     return false; 
    } else { 
     $select = mysql_select_db(DB_NAME, $con); 
     $query = sprintf("UPDATE ".DB_PREFIX."stats SET date=NOW(),ip='".$ipaddr."',hits=hits+1 WHERE id='".$account_id."'"); 
     $result = mysql_query($query); 

     $log->logNotice('update_entry:: Entry with id '.$account_id.' is updated.'); 
     return true; 
    } 
    mysql_close($con); 
} 


process_request(); 

編輯 我已經成功創建了工人階級的我的跟蹤器,請參閱下面的代碼。如果您的速度有所提升,請告訴我!我嘗試連接mysqlli和pdo,但它不起作用,顯然我的託管不支持它?

<?php 
    require_once(__DIR__ . '/inc/database.php'); 
    require_once(__DIR__ . '/lib/KLogger.php'); 

    Class ThemeStatsTracker 
    { 
     public $log; 

     private $theme_name; 
     private $theme_user; 
     private $theme_name_db_id; 
     private $theme_user_db_id; 

     public function __construct() 
     { 
      // Setup Log 
      $this->log = KLogger::instance(dirname(__FILE__).'/log/', KLogger::DEBUG); 

      // Process URI variables 
      $uri = explode('/',$_SERVER['REQUEST_URI']);  
      $this->theme_name = (!empty($uri[1])) ? $uri[1] : NULL; 
      $this->theme_user = (!empty($uri[2])) ? $uri[2] : NULL; 

      // Handle the Request 
      $this->database_connect(); 
      if($this->validate_theme()){ 
       if($this->user_entry_exists()){ 
        $this->user_entry_update(); 
       }else{ 
        $this->user_entry_create(); 
       } 
      } 

      // Always serve an image as response 
      $this->serve_image(); 
     } 

     private function validate_theme() 
     { 
      $query = sprintf("SELECT id FROM ".DB_PREFIX."themes WHERE slug='%s' LIMIT 1", mysql_real_escape_string($this->theme_name)); 
      $result = mysql_query($query); 
      if (!$result){ 
       $this->log->logError(__FUNCTION__ . ' FAIL: ' . $query . ' BECAUSE: ' . mysql_error()); 
       return FALSE; 
      } 
      if(mysql_num_rows($result)==0){ 
       $this->log->logError(__FUNCTION__ . ' Theme name ' . $this->theme_name . ' NOT found'); 
       return FALSE; 
      }else{ 
       $this->log->logInfo(__FUNCTION__ . ' Theme name ' . $this->theme_name . ' found'); 
       $this->theme_name_db_id = mysql_result($result,0); 
       return TRUE; 
      } 
     } 

     private function user_entry_exists() 
     { 
      $query = sprintf("SELECT id FROM ".DB_PREFIX."stats WHERE account='%s' LIMIT 1", mysql_real_escape_string($this->theme_user)); 
      $result = mysql_query($query); 
      if (!$result){ 
       $this->log->logError(__FUNCTION__ . ' FAIL: ' . $query . ' BECAUSE: ' . mysql_error()); 
       return FALSE; 
      } 
      if(mysql_num_rows($result)==0){ 
       $this->log->logInfo(__FUNCTION__ . ' New user ' . $this->theme_user); 
       return FALSE; 
      }else{ 
       $this->log->logInfo(__FUNCTION__ . ' Existing user ' . $this->theme_user); 
       $this->theme_user_db_id = mysql_result($result,0); 
       return TRUE; 
      } 
     } 

     private function user_entry_create() 
     { 
      $query = sprintf("INSERT INTO ".DB_PREFIX."stats (id,active,account,date,ip,hits,theme) VALUES ('','1','%s',NOW(),'".$_SERVER['REMOTE_ADDR']."','1','".$this->theme_name_db_id."')", mysql_real_escape_string($this->theme_user)); 
      $result = mysql_query($query); 
      if (!$result){ 
       $this->log->logError(__FUNCTION__ . ' FAIL: ' . $query . ' BECAUSE: ' . mysql_error()); 
       return FALSE; 
      } 
      $this->log->logNotice(__FUNCTION__ . ' New user created with id ' . mysql_insert_id()); 
      return TRUE; 
     } 

     private function user_entry_update() 
     { 
      $query = sprintf("UPDATE ".DB_PREFIX."stats SET date=NOW(),ip='".$_SERVER['REMOTE_ADDR']."',hits=hits+1 WHERE id='".$this->theme_user_db_id."' LIMIT 1"); 
      $result = mysql_query($query); 
      if (!$result){ 
       $this->log->logError(__FUNCTION__ . ' FAIL: ' . $query . ' BECAUSE: ' . mysql_error()); 
       return FALSE; 
      } 
      $this->log->logNotice(__FUNCTION__ . ' User with id ' . $this->theme_user_db_id . 'updated'); 
      return TRUE; 
     } 

     private function serve_image() 
     { 
      header("Content-type: image/gif"); 
      header("Content-length: 43"); 
      $fp = fopen("php://output","wb"); 
      fwrite($fp,"GIF89a\x01\x00\x01\x00\x80\x00\x00\xFF\xFF",15); 
      fwrite($fp,"\xFF\x00\x00\x00\x21\xF9\x04\x01\x00\x00\x00\x00",12); 
      fwrite($fp,"\x2C\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02",12); 
      fwrite($fp,"\x44\x01\x00\x3B",4); 
      fclose($fp); 
     } 

     private function database_connect() 
     { 
      $con = mysql_connect(DB_HOST,DB_USER,DB_PASSWORD); 
      if(!$con){ 
       $this->log->logError(__FUNCTION__ . ' FAIL: ' . $query . ' BECAUSE: ' . mysql_error()); 
       return FALSE; 
      } 
      $select = mysql_select_db(DB_NAME, $con); 
      if (!$select){ 
       $this->log->logError(__FUNCTION__ . ' FAIL: ' . $query . ' BECAUSE: ' . mysql_error()); 
       return FALSE; 
      } 
     }  
    } 

    $stats = new ThemeStatsTracker(); 
+0

過早的優化是所有罪惡的根源。去搞清楚。 –

+0

除非您遇到需要解決方案的特定問題,否則[Code Review SE](http://codereview.stackexchange.com/)將更適合此問題。 (編輯:當然,使用'mysql_'函數是我的第一個紅旗。) – DCoder

+0

如果它的工作原理沒有中斷 –

回答

0

每當有潛在的性能問題,你可以打賭,它是在I/O子系統和現代Web應用程序,通常是指數據的基礎上,所以這是你去哪裏找到並修復性能問題。這是我簡短的經驗法則。

  1. 避免SELECT *,而是隻選擇實際需要的列。
  2. 在每個複雜查詢中使用EXPLAIN SELECT。 「複雜」我的意思是任何查詢具有多個或兩個WHERE子句元素,或使用多個表。
  3. 有在何處,使用JOIN每列索引,ORDER或GROUP
  4. 使用的每個執行查詢LIMIT子句不絕對需要一個完整的表掃描,這包括UPDATE查詢。

這些是可以應用於查詢語句的一般原則。

要在上面的代碼中的細節,讓您的服務器連接分貝在腳本的開始選擇一次。連接和選擇在範圍內是有效的。刪除關閉連接語句。 PHP垃圾回收器會爲你做到這一點。

您可能想要閱讀query()函數。大多數數據庫函數返回值,通常是資源或失敗時爲FALSE。你的腳本應該測試這些值並處理行爲。 MySQL不是黑匣子;它會因爲你不能控制的原因而失敗。發生這種情況時,您需要記錄錯誤並提出警報。

你可能會考慮用面向對象的表示法來重寫它,使用受保護的類屬性來處理像$ slug和$ log之類的東西。這將有助於避免使用全局聲明,這是一個確定的混淆路徑,因爲它破壞了封裝。

HTH,雷〜

+0

謝謝爲快速回復。 mysql選擇提示和代碼的總體評論是一個很大的幫助。至於PDO或MySQLi方法。我認爲他們是提升我的方式。你碰巧知道一個很好的資源,用於瞭解更多關於PHP中OOP基礎的知識。我的意思是,這是非常基本的東西,所以我真的很感興趣如何從這些功能創建一個好班級。乾杯。 – CKDT

+0

我推薦這本書:http://www.amazon.com/PHP-Object-Oriented-Solutions-David-Powers/dp/1430210117並在更高級的一點,這本書:http://www.amazon。 com/Objects-Patterns-Practice-Experts-Source/dp/143022925X/ –

+0

此手冊頁很實用http://php.net/manual/en/language.constants.predefined.php –

0

出於比較的目的,這是我會如何使用OOP符號寫的跟蹤器。我從來沒有測試過這個,但看起來非常接近正確。此外,PHP具有內置函數來記錄錯誤和通知。http://php.net/manual/en/function.error-log.php

<?php // RAY_temp_ckdt.php 
error_reporting(E_ALL); 

/** MySQL Database CONSTANT DEFINITIONS */ 
require_once(__DIR__ . '/inc/database.php'); 

/** Klogger Log Framework*/ 
require_once(__DIR__ . '/lib/KLogger.php'); 
$log = KLogger::instance(dirname(__FILE__).'/log/', KLogger::DEBUG); 



Class Tracker 
{ 
    protected $return, $slug, $user, $theme_id, $account_id; 
    public function __construct($log) 
    { 
     $this->return = TRUE; 
     $this->log = $log; 
     $con = mysql_connect(DB_HOST,DB_USER,DB_PASSWORD); 
     if(!$con){ 
      $this->log->logError('DB FAIL: ' . mysql_error()); 
     } 
     $select = mysql_select_db(DB_NAME, $con); 
     if (!$select){ 
      $this->log->logError('DB FAIL: ' . mysql_error()); 
      return FALSE; 
     } 
     $uri = explode(DIRECTORY_SEPARATOR, $_SERVER['REQUEST_URI']); 
     $this->slug = (!empty($uri[1])) ? $uri[1] : NULL; 
     $this->user = (!empty($uri[2])) ? $uri[2] : NULL; 

     if(empty($this->slug) || !empty($this->user)){ 
      $this->log->logError(__FUNCTION__ . ": Bad request"); 
      return FALSE; 
     } 
     $the_slug = validate_slug($this->slug); 
     $the_user = account_exists($this->user); 

     if($the_slug){ 
      if($the_user){ 
       update_entry(); 
      }else{ 
       create_entry(); 
      } 
     } 
     return $this->return; 
    } 

    protected function validate_slug(){ 
     $query = sprintf("SELECT id FROM ".DB_PREFIX."themes WHERE slug='%s' LIMIT 1", mysql_real_escape_string($this->slug)); 
     $result = mysql_query($query); 
     if (!$result){ 
      $this->log->logError(__FUNCTION__ . "FAIL: $query BECAUSE: " . mysql_error()); 
      $this->return = FALSE; 
      return FALSE; 
     } 
     if(mysql_num_rows($result)==0){ 
      $this->log->logNotice(__FUNCTION__ . ": Slug $this->slug not found"); 
      $this->return = FALSE; 
      return FALSE; 
     }else{ 
      $this->theme_id = mysql_result($result,0); 
      $this->return = TRUE; 
      return TRUE; 
     } 
    } 

    protected function account_exists(){ 
     $query = sprintf("SELECT id FROM ".DB_PREFIX."stats WHERE account='%s' LIMIT 1", mysql_real_escape_string($this->user)); 
     $result = mysql_query($query); 
     if (!$result){ 
      $this->log->logError(__FUNCTION__ . "FAIL: $query BECAUSE: " . mysql_error()); 
      $this->return = FALSE; 
      return FALSE; 
     } 
     if(mysql_num_rows($result)==0){ 
      $this->log->logNotice(__FUNCTION__ . ": Account $this->user not found"); 
      $this->return = FALSE; 
      return FALSE; 
     }else{ 
      $this->account_id = mysql_result($result,0); 
      $this->return = TRUE; 
      return TRUE; 
     } 
    } 

    protected function create_entry(){ 
     $query = sprintf("INSERT INTO ".DB_PREFIX."stats (id,active,account,date,ip,hits,theme) VALUES ('','1','%s',NOW(),'".$_SERVER['REMOTE_ADDR']."','1','".$theme_id."')", mysql_real_escape_string($this->user)); 
     $result = mysql_query($query); 
     if (!$result){ 
      $this->log->logError(__FUNCTION__ . "FAIL: $query BECAUSE: " . mysql_error()); 
      $this->return = FALSE; 
      return FALSE; 
     } 

     $this->log->logNotice(__FUNCTION__ . ': Account created with id '.mysql_insert_id()); 
     $this->return = TRUE; 
     return TRUE; 
    } 

    public function update_entry(){ 
     $query = sprintf("UPDATE ".DB_PREFIX."stats SET date=NOW(),ip='".$_SERVER['REMOTE_ADDR']."',hits=hits+1 WHERE id='".$this->account_id."' LIMIT 1"); 
     $result = mysql_query($query); 
     if (!$result){ 
      $this->log->logError(__FUNCTION__ . "FAIL: $query BECAUSE: " . mysql_error()); 
      $this->return = FALSE; 
      return FALSE; 
     } 

     $this->log->logNotice(__FUNCTION__ . ': Entry with id '.$account_id.' is updated.'); 
     $this->return = TRUE; 
     return TRUE; 
    } 
} // END CLASS TRACKER 

// TRACK THIS REQUEST 
$x = new Tracker($log); 
+0

謝謝你的比較課,真的很有幫助。我知道PHP的錯誤報告類。但我需要將錯誤記錄到文件中,因爲代碼應該最終返回一個圖像頭。所以我不能在屏幕上打印錯誤。 – CKDT

0

優化俱樂部的規則:

  1. 優化俱樂部第一個規則是,你不優化
  2. 優化俱樂部的第二條規則是,,如果不測量,則不進行優化。
  3. 如果您的應用運行速度比底層傳輸協議快,則優化結束。
  4. 一次一個因素。
  5. 沒有marketroids,沒有marketroid時間表。
  6. 只要需要,測試將繼續進行。
  7. 如果這是您在Optimization Club的第一晚,您必須編寫一個測試用例。

最重要的規則是#1。如果你不知道你的代碼很慢,那麼不要試圖加速它。然後,對於#2,如果你認爲你需要加快速度,你必須測量它的速度慢。這可能是低技術的,比如在整個代碼中調用print microtime()調用,以便您可以看到單個代碼塊需要運行多長時間,或者您可以使用諸如XDebug的分析工具來準確發現哪些代碼行需要運行多長時間。

無論你做什麼,不要拿工作代碼,只是開始重擊它試圖使其更快。

(而且,無關你的問題,你應該使用安全性和易用性的參數化查詢:http://bobby-tables.com/php.html有例子)