背景
不得不說的是,昨天其實已經是基本上寫完了整個工具了的(Linux上那塊的shell指令碼沒往上添加罷了)。最後整理的時候,犯了個超級大的愚蠢的錯誤。
那就是忘了反選了,呵呵。一下子把原始碼給刪了。WTF!!!後來也使用了一些資料恢複軟體,也沒能成功找回。
於是今天不得不又重寫了一遍,而且僅僅完成了Windows平台上的適配。Linux上的拓展管理,就先不寫了,有時間再進行完善。
原理
在Windows上安裝php的拓展是非常的簡單,而且容易的一件事。
下載拓展對應的dll動態連結程式庫, 然後修改php.ini檔案,最後重啟Apache伺服器。
是的,就是這麼的簡單啦。
下面先說一下這個工具的作用:
爬取目標拓展的連結,做完過濾處理後羅列可以下載得到的拓展庫串連。
解壓下載的zip壓縮包,返回動態連結程式庫存在的位置。
尋找本機php環境變數,找到拓展檔案夾的位置,拷貝動態連結程式庫並修改php.ini檔案內容。
重啟Apache伺服器。即可生效(這個沒有添加自動處理,因為我覺得手動方式會更加理智一點)。
全程我們需要做的就是指定一下要安裝的拓展的名稱,手動的進行選擇要安裝的版本,就是這麼的簡單啦。接下來就一步步地進行分析吧。
下載
這個工具依託的是php官網提供的拓展目錄,
當然也可以使用手動下載安裝的方式,這個工具就是把這個流程自動化了而已。
擷取網頁內容
擷取網頁內容的時候,為了防止網站做了防爬蟲處理,我們採用類比瀏覽器的方式進行。
def urlOpener(self, targeturl): headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36' } return urllib2.urlopen(urllib2.Request(url=targeturl, headers=headers)).read()
正則與過濾下載連結
下載了一張網頁,內容還是很多的。而我們需要的僅僅是網頁中預期的下載連結罷了。所以要進行正則匹配,來過濾出我們想要的資料。
而正則有一定的靈活性,所以我們要根據具體情況進行集體的分析,於是我封裝了一個函數。
# 擷取指定拓展的所有能用的版本 def getLinks(self, url, regpattern): content = self.urlOpener(targeturl=url) reg = re.compile(regpattern) return re.findall(reg, content)
def getDownloadLinks(self): versionLinks = self.getLinks("http://windows.php.net/downloads/pecl/releases/{}".format(self.extensionname), '<A HREF="(/downloads/pecl/releases/{}/.*?)">'.format(self.extensionname)) for index, item in enumerate(versionLinks): print "{} : {}".format(index, item) choice = int(raw_input('Please choose the special version you want by number!\n')) return versionLinks[choice] def getTargetUrl(self): userChoice = "http://windows.php.net"+str(self.getDownloadLinks()) print userChoice regpattern = '<A HREF="(/.*?/php_.*?\.zip)">.*?<\/A>' targetUrls = self.getLinks(url=userChoice, regpattern=regpattern) # 由於正則匹配度寫的不好,第一個連結不能正常匹配,因此採用折斷方式,去除第一個無效連結 return targetUrls[1:]
代碼中很多東西寫得不是通用的,因為這個工具本身就是要依賴於這個網站而是用的。所以沒有做具體的重構處理, 而這已然是足夠的了。
下載預期目標
從上面即可擷取到經由使用者選擇的下載連結,然後我們就可以據此來進行特定版本的拓展包下載了。
def folderMaker(self): if not os.path.exists(r'./packages/{}'.format(self.extensionname)): os.mkdir(r'./packages/{}'.format(self.extensionname)) def download(self): choices = self.getTargetUrl() for index, item in enumerate(choices): print "{} : {}".format(index, item) choice = int(raw_input('Please choose the special version which suitable for your operation system you want by number!\n')) userChoice = choices[choice] # 對外提供友好的使用者提示資訊,最佳化的時候可通過添加進度條形式展現 print 'Downloading, please wait...' # 開啟下載模式,進行代碼最佳化的時候可以使用多線程來進行加速 data = self.urlOpener(targeturl="http://windows.php.net"+str(userChoice)) # 將下載的資源存放到 本地資產庫(先進行檔案夾存在與否判斷) filename = userChoice.split('/')[-1] self.folderMaker() with open(r'./packages/{}/{}'.format(self.extensionname, filename), 'wb') as file: file.write(data) print '{} downloaded!'.format(filename)
解壓
第一個步驟就是下載,下載的結果就是一個zip壓縮包,所以我們還需要對這個壓縮包進行處理,才能為我們所用。
檔案路徑問題
由於是針對Windows而製作,所以檔案路徑分隔字元就按照windows上的來吧(為了代碼更加優雅,還可以使用os.sep來相容不同的作業系統)。
解壓
def folderMaker(self, foldername): if not os.path.exists(r'./packages/{}/{}'.format(self.extensionname, foldername)): os.mkdir(r'./packages/{}/{}'.format(self.extensionname, foldername)) print 'Created folder {} succeed!'.format(foldername) def unzip(self): filelists = [item for item in os.listdir(r'./packages/{}/'.format(self.extensionname)) if item.endswith('.zip')] filezip = zipfile.ZipFile(r'./packages/{}/{}'.format(self.extensionname, filelists[0])) foldername = filelists[0].split('.')[0] self.folderMaker(foldername=foldername) print 'Uncompressing files, please wait...' for file in filezip.namelist(): filezip.extract(file, r'./packages/{}/{}/{}'.format(self.extensionname, foldername, file)) filezip.close() print 'Uncompress files succeed!'
安裝與配置
安裝與配置其實是兩個話題了。
安裝
首先是要將下載好的拓展的dll檔案放置到php安裝目錄下的拓展檔案夾,這個過程就涉及到了尋找php拓展目錄的問題。然後是檔案拷貝的問題。
def getPhpExtPath(self): # 預設系統中僅有一個php版本 rawpath = [item for item in os.getenv('path').split(';') if item.__contains__('php')][0] self.phppath = rawpath return rawpath+str('ext\\') def getExtensionDllPath(self): for root, dirs, files in os.walk(r'./packages/{}/'.format(self.extensionname)): extensionfolder = root.split('\\')[-1] if extensionfolder.__contains__('dll'): return root.split('\\')[0] + '/' + extensionfolder+'/'+extensionfolder
代碼未完,下面會把拷貝的那段放上去的。
配置
配置就是需要在php.ini檔案中進行聲明,也就是添加下面的這樣一條語句。(在Dynamic Extension塊下面即可)
extension=XX.dll
# 針對php.ini檔案中的相關的拓展選項進行針對性的添加.採用的具體方式是使用臨時檔案替換的方法 def iniAppend(self): inipath = self.phppath+str('php.ini') tmpinipath = self.phppath+str('php-tmp.ini') # 要進行替換的新的檔案內容 newcontent = '; Windows Extensions\nextension={}.dll'.format(self.extensionname) open(tmpinipath, 'w').write( re.sub(r'; Windows Extensions', newcontent, open(inipath).read())) # 進行更名操作 os.rename(inipath, self.phppath+str('php.bak.ini')) os.rename(tmpinipath, self.phppath+str('php.ini')) print 'Rename Succeed!' def configure(self): # 列印php拓展目錄路徑 extpath = self.getPhpExtPath()+str('php_{}.dll'.format(self.extensionname)) print extpath # 擷取到拓展動態連結程式庫及其路徑 extensiondllpath = self.getExtensionDllPath() # 將拓展檔案添加到php拓展目錄中 shutil.copyfile(extensiondllpath, extpath) # 在php.ini檔案中添加對應的拓展選項 self.iniAppend() print '{} 拓展已添加,拓展服務將在重啟Apache伺服器後生效!'.format(self.extensionname)
最後讓Apache重啟就可以生效啦。
示範
初始狀態
安裝與配置完畢狀態
啟用狀態
總結
最後來總結一下,這個工具主要使用到了Python語言,方便,優雅,快捷。
完成了對PHP拓展的安裝與配置,大大簡化了操作量。
其實,我覺得使用代碼來完成安裝與配置工作不是很恰當。然而在Windows上也就罷了,畢竟批命令處理起來也不是很好用(PowerShell我沒怎麼用過,所以我沒有發言權。),而要是在Linux上就不一樣了,做一些這樣的任務,使用Shell來寫是最合適不過的了, 而且還可以寫的很靈活。