最近优化php项目,记录下经验,直接上干活。。。
php在公司项目中主要用于页面展现,前端有个view,view向后端的service请求数据,数据的传输格式是json。下面看优化前的service的代码:
[php]
require_once('../../../global.php');
require_once(INCLUDE_PATH . '/discache/CacherManager.php');
require_once(INCLUDE_PATH.'/oracle_oci.php');
require_once(INCLUDE_PATH.'/caihui/cwsd.php');
header('Content-type: text/plain; charset=utf-8');
$max_age = isset($_GET['max-age']) ? $_GET['max-age']*1 : 15*60;
if($max_age
$max_age = 30;
}
header('Cache-Control: max-age='.$max_age);
// 通过将url进行hash作为缓冲key
$url = $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
$url_hash = md5($url);
//echo "/finance/hs/marketdata/segment/${url_hash}.json";
if (!CacherManager::cachePageStart(CACHER_MONGO, "/finance/hs/marketdata/segment/${url_hash}.json", 60*60)) {
// 查询条件
$page = isset($_GET['page']) ? $_GET['page']*1 : 0;
$count = isset($_GET['count']) ? $_GET['count']*1 : 30;
$type = isset($_GET['type']) ? $_GET['type'] : 'query';
$sort = isset($_GET['sort']) ? $_GET['sort'] : 'symbol';
$order = isset($_GET['order']) ? $_GET['order'] : 'desc';
$callback = isset($_GET['callback']) ? $_GET['callback'] : null;
$fieldsstring = isset($_GET['fields']) ? $_GET['fields'] : null;
$querystring = isset($_GET['query']) ? $_GET['query'] : null;
$symbol=isset($_GET['symbol'])?$_GET['symbol']:'';
$date=isset($_GET['date'])?$_GET['date']:'';
if ($type == 'query') {
$queryObj = preg_split('/:|;/', $querystring, -1);
for($i=0; $i
if($queryObj[$i]=='symbol'){
$symbol = $queryObj[$i+1];
}
if($queryObj[$i]=='date'){
$date = $queryObj[$i+1];
}
}
}
// 查询列表
$oci = ntes_get_caihui_oci();
$stocklist = array();
$cwsd = new namespace\dao\caihui\Cwsd($oci);
$stockcurror = $cwsd->getCznlList($symbol,$date,$sort,$order,$count*($page),$count);
$sumrecords=$cwsd->getRecordCount($symbol,$date);
$i=0;
//var_dump($symbol,$date,$sort,$order,$count*($page),$count);
foreach($stockcurror as $item){
$item['RSMFRATIO1422']=isset($item['RSMFRATIO1422'])?number_format($item['RSMFRATIO1422'],2).'%':'--';
$item['RSMFRATIO1822']=isset($item['RSMFRATIO1822'])?number_format($item['RSMFRATIO1822'],2).'%':'--';
$item['RSMFRATIO22']=isset($item['RSMFRATIO22'])?number_format($item['RSMFRATIO22'],2).'%':'--';
$item['RSMFRATIO10']=isset($item['RSMFRATIO10'])?number_format($item['RSMFRATIO10'],2):'--';
$item['RSMFRATIO12']=isset($item['RSMFRATIO12'])?number_format($item['RSMFRATIO12'],2):'--';
$item['RSMFRATIO4']=isset($item['RSMFRATIO4'])?number_format($item['RSMFRATIO4'],2):'--';
$item['RSMFRATIO18']=isset($item['RSMFRATIO18'])?number_format($item['RSMFRATIO18'],2):'--';
$item['RSMFRATIO14']=isset($item['RSMFRATIO14'])?number_format($item['RSMFRATIO14'],2):'--';
$item['CODE']=$item['EXCHANGE'].$item['SYMBOL'];
//$item['REPORTDATE']=isset($item['REPORTDATE'])?$item['REPORTDATE']:'--';
$stocklist[$i] = $item;
$i=$i+1;
}
// 输出结果
$result = array();
// 页码page、每页数量count、结果总数total、分页数pagecount、结果列表list
$result['page'] = $page;
$result['count'] = $count;
$result['order'] = $order;
$result['total'] = $i;//$stockcurror->count();
$result['pagecount'] = ceil($sumrecords['SUMRECORD']/$count);
$result['time'] = date('Y-m-d H:i:s');
$result['list'] = $stocklist;
if(emptyempty($callback)){
echo json_encode($result);
}else{
echo $callback.'('.json_encode($result).');';
}
CacherManager::cachePageEnd();
}
?>
下面看一下这个service具体完成的功能:
1. 6-16行,准备缓存参数,开启缓存。
2. 19-41行,提取请求参数。
3. 44-49行,连接、查询数据库。
4. 50-67行,将数据库查询结果放入数组。
5. 71-84行,准备json数据。
6. 86-87行,关闭缓存。
如果只看这一个文件,存在的问题有:
1. 19-86行,没有缩进。
2. 44行,每次请求都会重新连接数据库。
3. 53-61行,重复的逻辑,可以提取为一个函数,然后通过迭代完成。
如果大部分后端Service都采用这个结构,那么问题就是所有的Service都需要经过:开启缓存,取参,获取数据,json转化,关闭缓存这一系列的过程。而在所有过程中,除了获取数据的逻辑,其他的过程都是一样的。在代码中存在着大量的重复逻辑,甚至给人一种“复制-粘贴”的感觉,这严重的违背了DRY原则(Don't Repeat Yourself)。所以,这里需要运用面向对象的思想对其重构。而在我重构的过程中,脑海中始终谨记着一个原则——封装变化原则。所谓封装变化,就是区分系统中不变的和可变的,将可变的进行封装,这样可以很好过应对变化。
通过上面的分析,只有获取数据的逻辑是变化的,其他的逻辑是不变的。所以需要对获取数据的逻辑进行封装,具体的封装方式可以采用继承或组合。我采用的是继承的方式,首先将service的处理过程抽象为:
service(){
startCache();
getParam();
getData(); // 抽象方法,由子类实现
toJson();
closeCache();
}
抽象出ServiceBase类,由子类继承,实现相应的获取数据的逻辑,子类不需要处理其他的取参、缓存等逻辑,这些都被ServiceBase类处理了。
[php]
abstract class ServiceBase {
public function __construct($cache_path, $cache_type, $max_age, $age_explore) {
// 获取请求参数
$this->page = $this->getQueryParamDefault('page', 0, INT);
// 省略其他的获取参数的逻辑
……
// 生成响应
$this->response();
}
/**
*
* 子类实现,返回数组格式的数据
*/
abstract protected function data();
/**
*
* 子类实现,返回所有数据的总数
*/
abstract protected function total();
private function cache() {
$url = $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
$url_hash = md5($url);
$key = $this->cache_path.$url_hash.'.json';
if(!CacherManager::cachePageStart($this->cache_type, $key, $this->age_cache)){
$this->no_cache();
CacherManager::cachePageEnd();
}
}
private function no_cache(){
$data = $this->data();
$total = $this->total();
$this->send_data($data, $total);
}
private function send_data($data, $total){
// 进行json转化,省略具体代码
}
private function response() {
header('Content-type: text/plain; charset=utf-8');
header('Cache-Control: max-age='.$this->age_explore);
if($this->cache_type == NONE || self::$enable_cache == false){
$this->no_cache();
}else{
$this->cache();
}
}
}
这就是各个service的抽象父类,有两个抽象方法data和total,data是返回数组格式的数据,tatol是由于分页加入的。具体的service只要继承ServiceBase并实现data和total方法即可,其他的逻辑都是复用的父类的。实际上,优化后的ServiceBase是使用了模板方法模式(Template Method),父类定义算法处理的流程(service的处理过程),子类实现某个具体变化的步骤(具体service的获取数据的逻辑)。通过使用模板方法模式可以保证步骤的变化对于客户端是透明的,并且可以复用父类中的逻辑。
下面是上述php采用ServiceBase后的代码:
[php]
class CWSDService extends ServiceBase{
function __construct(){
parent::__construct();
$oci = ntes_get_caihui_oci();
$this->$cwsd = new namespace\dao\caihui\Cwsd($oci);
}
public function data(){
$stocklist = array();
$stockcurror = $this->cwsd->getCznlList($this->query_obj['symbol'],
$this->query_obj['symbol'], $sort, $order, $count*($page), $count);
$filter_list = array('RSMFRATIO1422', 'RSMFRATIO1822', 'RSMFRATIO22',
'RSMFRATIO10', 'RSMFRATIO12', 'RSMFRATIO4', 'RSMFRATIO18',
'RSMFRATIO14');
$i=0;
foreach($stockcurror as $item){
foreach($filter_list as $k)
$this->filter($item, $k);
$item['CODE']=$item['EXCHANGE'].$item['SYMBOL'];
$stocklist[$i] = $item;
$i=$i+1;
}
return $stocklist;
}
public function total(){
return $sumrecords=$this->cwsd->getRecordCount($this->query_obj['symbol'],
$this->query_obj['symbol']);
}
private function filter($item, $k){
isset($item[$k])?number_format($item[$k],2).'%':'--';
}
}
new CWSDService('/finance/hs/realtimedata/market/ab', MONGO, 30, 30);
代码量从87减少到32行,是因为大部分的逻辑都由父类完成,具体service只需要关注自己的业务逻辑就可以了。通过上面代码可以看出继承可以实现代码复用,多个子类中的相同的逻辑可以提取到父类中达到复用的目的;同时,继承也增加了父类和子类之间的耦合性,这也就是组合由于继承的方面,如果这个例子采用组合来封装变化,则具体的实现就是策略模式,将具体获取数据的逻辑看成是策略,不同的service就是不同的策略,由于时间原因,不再赘述。。。
摘自 chosen0ne的专栏

phpsessionstrackuserdataacrossmultiplepagerequestsusingauniqueIdStoredInAcookie.here'showtomanageThemeffectionaly:1)startAsessionWithSessionWwithSession_start()和stordoredAtain $ _session.2)

在PHP中,遍歷會話數據可以通過以下步驟實現:1.使用session_start()啟動會話。 2.通過foreach循環遍歷$_SESSION數組中的所有鍵值對。 3.處理複雜數據結構時,使用is_array()或is_object()函數,並用print_r()輸出詳細信息。 4.優化遍歷時,可採用分頁處理,避免一次性處理大量數據。這將幫助你在實際項目中更有效地管理和使用PHP會話數據。

會話通過服務器端的狀態管理機制實現用戶認證。 1)會話創建並生成唯一ID,2)ID通過cookies傳遞,3)服務器存儲並通過ID訪問會話數據,4)實現用戶認證和狀態管理,提升應用安全性和用戶體驗。

Tostoreauser'snameinaPHPsession,startthesessionwithsession_start(),thenassignthenameto$_SESSION['username'].1)Usesession_start()toinitializethesession.2)Assigntheuser'snameto$_SESSION['username'].Thisallowsyoutoaccessthenameacrossmultiplepages,enhanc

PHPSession失效的原因包括配置錯誤、Cookie問題和Session過期。 1.配置錯誤:檢查並設置正確的session.save_path。 2.Cookie問題:確保Cookie設置正確。 3.Session過期:調整session.gc_maxlifetime值以延長會話時間。

在PHP中調試會話問題的方法包括:1.檢查會話是否正確啟動;2.驗證會話ID的傳遞;3.檢查會話數據的存儲和讀取;4.查看服務器配置。通過輸出會話ID和數據、查看會話文件內容等方法,可以有效診斷和解決會話相關的問題。

多次調用session_start()會導致警告信息和可能的數據覆蓋。 1)PHP會發出警告,提示session已啟動。 2)可能導致session數據意外覆蓋。 3)使用session_status()檢查session狀態,避免重複調用。

在PHP中配置會話生命週期可以通過設置session.gc_maxlifetime和session.cookie_lifetime來實現。 1)session.gc_maxlifetime控制服務器端會話數據的存活時間,2)session.cookie_lifetime控制客戶端cookie的生命週期,設置為0時cookie在瀏覽器關閉時過期。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

SecLists
SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

禪工作室 13.0.1
強大的PHP整合開發環境

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

記事本++7.3.1
好用且免費的程式碼編輯器