最近在對接微信支付的時候,需要在退款處用到證書,由於我們是SAAS平台,要支持多方多渠道支付,如果把所有證書文件保存在應用程序伺服器會受到SLB的影響,會導致某台機器檔案不同步而阻礙退款流程,但把檔案存在OSS的話,後端又要從OSS下載到應用程式伺服器來保證一致性。思來想去,最後決定將證書內容保存在資料庫,不同客戶各對應一份證書文件,無論幾台機器做集群都能保證文件的一致性,同時也避免了多餘的下載步驟。
但剛做就遇到了問題,PHP的CURL憑證並不支援字串的傳輸,只能填入憑證路徑(以下是官方的說法)
#Client certificates must be specified by a path expression to a certificate store.
我第一個想到的就是創建空白文件,將證書內容寫進去,等憑證使用完畢後再將檔案刪除,但是建立實體檔案再刪除的操作消耗效能不說,還非常麻煩,有沒有建立暫存檔案的方法呢?有,tmpfile()
函數就可以幫我們建立臨時檔案並拿到檔案路徑,於是我寫了一個獲取臨時檔案路徑的方法
<?php public function getTmpPathByContent($content) { $tmpFile = tmpfile(); fwrite($tmpFile, $content); $tempPemPath = stream_get_meta_data($tmpFile); return $tempPemPath['uri']; ///tmp/phpXZCtAO } ?>
比較悲哀的是,透過這個方法返回的路徑根本讀不到內容,甚至一度以為是不是被騙了
file_get_contents(/tmp/phpyyiOZv): failed to open stream: No such file or directory
看了官方文檔才找到原因,如果tmpfile()
返回的句柄引用計數為0的話就會將臨時文件回收,臨時路徑自然也就失效了,顯然方法getTmpPathByContent()
執行完後,局部變數$tmpFile
的生命週期就結束了(官方文檔如下)
The file is automatically removed when closed (for example, by calling fclose(), or when there are no remaining references to the file handle returned by tmpfile()), or when the script ends.
確認了根源,那我們現在亟需找到一個生命週期隨行程結束而終止的變數型別來保存句柄,什麼型別能滿足條件呢?靜態變數。靜態變數與局部變數不同的是,在PHP生命週期開始時便會為其分配記憶體空間,並會把它儲存在全域變數區域,而全域變數是在模組關閉階段銷毀的,這樣的話,聲明靜態變數就可以使$tmpFile
引用計數持續保持大於0的狀態,那麼我們的程式碼就可以做出如下處理
<?php public function getTmpPathByContent($content) { static $tmpFile = null; $tmpFile = tmpfile(); fwrite($tmpFile, $content); $tempPemPath = stream_get_meta_data($tmpFile); return $tempPemPath['uri']; } ?>
再執行一次就成功讀取了臨時檔案的內容
-----BEGIN CERTIFICATE----- MIIEbDCCA9WgAwIBAgIEAWJKHDANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMC Q04xEjAQBgNVBAgTCUd1YW5nZG9uZzERMA8GA1UEBxMIU2hlbnpoZW4xEDAOBgNV BAoTB1RlbmNlbnQxDDAKBgNVBAsTA1dYRzETMBEGA1UEAxMKTW1wYXltY2hDQTEf MB0GCSqGSIb3DQEJARYQbW1wYXltY2hAdGVuY2VudDAeFw0xNzA4MDcwOTIxNDda Fw0yNzA4MDUwOTIxNDdaMIGbMQswCQYDVQQGEwJDTjESMBAGA1UECBMJR3Vhbmdk b25nMREwDwYDVQQHEwhTaGVuemhlbjEQMA4GA1UEChMHVGVuY2VudDEOMAwGA1UE CxMFTU1QYXkxMDAuBgNVBAMUJ+a3seWcs+W4guaYjua6kOi9r+S7tuiCoeS7veac iemZkOWFrOWPuDERMA8GA1UEBBMIMTAyNTkyODEwggEiMA0GCSqGSIb3DQEBAQUA A4IBDwAwggEKAoIBAQDg2D3++uOxY/yMGQPBnROvyYimnCsfGE0dnqdGUTCykqBh yfv82zE1/St/4DQX2QDiIvLif+sMGcYwF4bkzdY+HgitYLI0k5o/5LCNZOMctuio kdYC2bNdWHq2y9S5UWLQR1Zvq+6QyPBVBVY9yq9xtQhIlUTsZnICAp3iQLfQUR3l aEdH9IERoRUIkbyb8oX5ONQz4P9jOeE9C5iwx0QrH4s01NFhkhr8JHlugRLpo9vA xGgi/48fOlONj6wWal5Gt0OvvEbIwgQwya15KBX2YeGnZvYBQa+lQMeXEqZSFie3 G+wGvbtlONczQEtp+JDxLZLUS/FT7U0TQN/t8JDvAgMBAAGjggFGMIIBQjAJBgNV HRMEAjAAMCwGCWCGSAGG+EIBDQQfFh0iQ0VTLUNBIEdlbmVyYXRlIENlcnRpZmlj YXRlIjAdBgNVHQ4EFgQUjDJ75bu3Roog7XOH6uFAdZ6kpcIwgb8GA1UdIwSBtzCB tIAUPgUm9iJitBVbiM1kfrDUYqflhnShgZCkgY0wgYoxCzAJBgNVBAYTAkNOMRIw EAYDVQQIEwlHdWFuZ2RvbmcxETAPBgNVBAcTCFNoZW56aGVuMRAwDgYDVQQKEwdU ZW5jZW50MQwwCgYDVQQLEwNXWEcxEzARBgNVBAMTCk1tcGF5bWNoQ0ExHzAdBgkq hkiG9w0BCQEWEG1tcGF5bWNoQHRlbmNlbnSCCQC7VJcrvADoVzAOBgNVHQ8BAf8E BAMCBsAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwIwDQYJKoZIhvcNAQEFBQADgYEA ucJLJkkHxlqQCEapZOWmySutqNVZxFbqyG//UXxxpA/1yG4e+KmufKZWv+c+MtYI 8i0KDDCv/UE+kkFIrHYDDKsdLRpxrYOUHGoqq0c7yBJ6Dimgy6m8U8FsEv3HtUR2 8g5xrg2Tc5MPWEp9ncEw575hGk0CXLDGOkI1nU+pGqk= -----END CERTIFICATE-----
下面就可以把產生的臨時檔案位址設定到CURLOPT_SSLCERT
了
<?php $sslCertPath = getTmpPathByContent($content); curl_setopt($ch,CURLOPT_SSLCERT, $sslCertPath); //...... ?>
【推薦課程:PHP影片教學】
以上是PHP如何支援CURL字串憑證傳輸(詳細解決過程)的詳細內容。更多資訊請關注PHP中文網其他相關文章!