文的環(huán)境是參考這篇《Spring Cloud OAuth2 實現(xiàn)用戶認證及單點登錄”》搭建的。
https://www.cnblogs.com/fengzheng/p/11724625.html
當前已經(jīng)獲取到了access_token,根據(jù)access_token請求自己的接口時報了401,token驗證有問題。
我自己的接口是:
/**
* 一個 RESTful 方法,只有當訪問用戶具有 ROLE_ADMIN 權(quán)限時才能訪問,否則返回 401 未授權(quán)。
*
* 通過 Authentication 參數(shù)或者 SecurityContextHolder.getContext().getAuthentication() 可以拿到授權(quán)信息進行查看。
* @param authentication
* @return
*/
@GetMapping("get")
@PreAuthorize("hasAnyRole('ROLE_ADMIN')")
public Object get(Authentication authentication){
authentication.getCredentials();
OAuth2AuthenticationDetails details=(OAuth2AuthenticationDetails) authentication.getDetails();
String token=details.getTokenValue();
return token;
}
org.springframework.security.oauth2.provider.token.RemoteTokenServices
我們將這個類復制出來,在java下創(chuàng)建相同的包名,類。
源碼圖
在源碼中,這里返回的active應該得為Boolean類型,但是這個map的值是個Object,返回直接返回了個String類型。
他使用了Boolean和String類型進行比較,所以我得到的返回結(jié)果永遠都是false了,就一直走了這個異常。(具體他為什么會這樣來比較,我真不太懂,歡迎大佬們在評論里指點一下)
修改后的圖
那么我的處理就比較簡單粗暴了,直接把這個類提出來,然后把這個Boolean.TRUE.toString()一下。類型一致,比較就正常了。
再次去請求,現(xiàn)在就沒有問題了,返回成功
本文只是個人在學習過程中的一次排查處理過程,如果有更好的見解歡迎大佬們指點。
者:redmed
Web 項目中經(jīng)常會遇到處理 URL 中 Query 的情況,來看下下面問題你有疑惑嗎?
于是梳理一下關(guān)于 URL Query 的相關(guān)知識點,用來去偽解惑。
首先介紹下 Query String 的基本概念,這是一切問題的開始。下面是 wiki[1] 的描述:
A query string is a part of a uniform resource locator[2] (URL) that assigns values to specified parameters.
通常的理解就是 URL 中問號(?)后面的部分,其設計最初是用做 HTML form 表單提交時的傳參。
基本結(jié)構(gòu)
下面我們看下 query 的基本結(jié)構(gòu) field1=value1&field2=value2&field3=value3...
包含了如下標準:
補充個冷知識:除了使用&分割每對數(shù)據(jù)外,W3C 曾在 1999 年建議所有 Web 服務器同時支持分號;分割符:
We recommend that HTTP server implementors, and in particular, CGI implementors support the use of ";" in place of "&" to save authors the trouble of escaping "&" characters in this manner.
但在 2014 年以來,就只建議使用 & 作為分隔符了。也就目前我們用到的方式。
例如:field=a&field=b時,field 的值應該是 a、b、['a', 'b']、'a, b' 并無任何權(quán)威解釋。
關(guān)于處理標準這點實在令人出乎意料。通常這類情況會按照數(shù)組的方式處理,即 field 值為 ['a', 'b'],但這僅是不同的框架的決定了如何實現(xiàn)而已。
關(guān)于這個問題可以前往 stackoverflow[3] 上查看。
數(shù)據(jù)編碼
前面定義好了整體結(jié)構(gòu),接下來我們看下數(shù)據(jù)是如何在 query 中傳輸?shù)摹?/span>
由于某些字符集(如中文)和在 URL 中有特殊含義的字符(如 空格、%、&、=、?、# 等)無法直接在 Query String 中使用,因此使用了一種叫做「百分號編碼[4] Percent-encoding[5]」的方式先將這類特殊字符進行編碼后,再進行傳輸。
其基本結(jié)構(gòu)就是 % + 2 個 16 進制數(shù)字(一個 Byte 的內(nèi)容),范圍 %00 - %FF。
具體規(guī)則如下:
! # $ & ' ( ) * + , / : ; = ? @ [ ] %21 %23 %24 %26 %27 %28 %29 %2A %2B %2C %2F %3A %3B %3D %3F %40 %5B %5D
其對應的就是這些字符的 ASCII 編碼的 16 進制格式;
注意,如果使用 from 表單 action 方式時,具體編碼會根據(jù) meta 頭的 charset 的選擇。
當然上述只是標準,實踐中 JavaScript 內(nèi)置了使用 UTF-8 編碼的 encodeURI/encodeURIComponent 函數(shù),大大簡化的編碼過程。
關(guān)于指定編碼,這里有個有趣的事情:
在使用百度時,你會發(fā)現(xiàn) URL 中有個 ie 參數(shù),其實含義就是 Input Encoding(對,不是 IE 瀏覽器),目的就是指定關(guān)鍵詞 wd 的編碼格式。曾默認是 GB2312(因為當時很多網(wǎng)站還使用 GB2312 編碼),當然現(xiàn)在已經(jīng)默認成 UTF-8 。(不過百度結(jié)果里依然有不少文章還在說 ie 的默認值是 GB2312 )
可以用 https://www.baidu.com/s?wd=%E4%B8%AD&ie=gb2312 和 https://www.baidu.com/s?wd=%E4%B8%AD 來感受下他們的差異吧~
這一節(jié)我們挑重點地對比下各類 Query String 的函數(shù)庫,了解老虎老鼠的差異,避免開發(fā)時傻傻分不清楚。
以下僅對常用 API 的部分用法做演示,更多用法可自行查找。
瑞士軍刀 qs
github[7]
A querystring parsing and stringifying library with some added security.
官方介紹很簡單:一個增加了安全性的 Query String 解析和序列化的函數(shù)庫。
.parse(string, [options])
qs.parse('a=c&b%201=d%26e');
// { a: 'c', 'b 1': 'd&e' }
注意 qs 不會忽略頭部的 ?,需要自行去掉,否則會當做 field 的一部分,例如:qs.parse('?a=b')會解析為 { '?a': 'b' }。
qs.parse('foo[bar]=baz');
// { foo: { bar: 'baz' } }
但默認子元素最多嵌套 5 層,需要通過 parse(string, [options]) 的 opinion.depth 來修改。
// defalut
qs.parse('a[b][c][d][e][f][g][h][i]=j');
// {a: {b: {c: {d: {e: {f: {'[g][h][i]': 'j'}}}}}}}
// set depth
qs.parse('a[b][c][d][e][f][g][h][i]=j', { depth: 1 });
// { a: { b: { '[c][d][e][f][g][h][i]': 'j' } } }
var delimited=qs.parse('a=b;c=d', { delimiter: ';' });
// { a: 'b', c: 'd' }
這點符合 W3C 對;支持的建議,但大部分情況應該不會用到。
var withArray=qs.parse('a[]=b&a[]=c');
// { a: ['b', 'c'] }
var withArray=qs.parse('a=b&a=c');
// { a: ['b', 'c'] }
同時也支持為數(shù)組指定索引順序。
var withIndexes=qs.parse('a[1]=c&a[0]=b');
// { a: ['b', 'c'] };
并行支持 allowSparse 獲取抽稀形式的數(shù)組。
var sparseArray=qs.parse('a[1]=2&a[3]=5', { allowSparse: true });
// { a: [, '2', , '5'] };
但默認指定的 index 最大值為 20,如果超過最大值,則按照 object 形式解析。使用 arrayLimit控制最大值。
var withMaxIndex=qs.parse('a[100]=b');
// { a: { '100': 'b' } }
var withArrayLimit=qs.parse('a[1]=b', { arrayLimit: 0 });
// { a: { '1': 'b' } }
.stringify(object, [options])
這里主要介紹下 array 類型的編碼。qs 默認會對 field 和 value 都進行編碼,同時會使用[]作為數(shù)據(jù)的標識(且默認對[]進行百分號編碼),需指定 encodeValuesOnly: true才僅對 value 編碼。
// defalut
qs.stringify({key: ['a', 'b']});
// key%5B0%5D=a&key%5B1%5D=b
//
qs.stringify({key: ['a', 'b']}, { encodeValuesOnly: true });
// key[0]=a&key[1]=b
去掉[]標識,可使用 { indices: false }。
qs.stringify({key: ['a', 'b']}, { indices: false });
// key=a&key=b
支持配置 charset
默認使用 UTF-8,內(nèi)置了 ISO-8859-1 模式,也可以支持 encoder 擴展。
而接下來的庫僅支持 UTF-8 的編碼方式。
簡潔專注 query-string
github[8]
Parse and stringify URL query strings[1]
For browser usage, this package targets the latest version of Chrome, Firefox, and Safari.
官方名字看起來,依舊是處理 Query String 的。
另外,官方還送上友(wei)情(xian)提示,各位同學不要看走眼。
Not npm install querystring !!!!!
.parse(string, [options])
不過,頭部的?和#的部分將被忽略,因此可以直接將 location.search 和 location.hash 傳入。
queryString.parse('a=c&b%201=d%26e');
// { a: 'c', 'b 1': 'd&e' }
This module intentionally doesn't support nesting as it's not spec'd and varies between implementations, which causes a lot of edge cases[10].
You're much better off just converting the object to a JSON string:
queryString.parse('key=a&key=b');
// { key: ['a', 'b'] };
queryString.parse('key[]=a&key[]=b');
// { 'key[]': ['a', 'b'] };
queryString.parse('key[]=a&key[]=b', { arrayFormat: 'bracket' });
// { key: ['a', 'b'] };
當然 query-string 也支持索引的方式標記的數(shù)組,{arrayFormat: 'index'}。
queryString.parse('foo[0]=1&foo[1]=2&foo[3]=3', {arrayFormat: 'index'});
{foo: ['1', '2', '3']}
.stringify(object, [options])
依然重點介紹 array 類型的編碼,默認不使用[]標識。
queryString.stringify({key: ['a', 'b']});
// key=a&key=b
需要[]的話,使用 {arrayFormat: 'bracket'}開啟,默認[]也不會被 encode。
queryString.stringify({key: ['a', 'b']}, {arrayFormat: 'bracket'});
// key[]=a&key[]=b
這點和 qs 是相反的,需要特別注意!
歷史產(chǎn)物 querystring
NodeJS 中解析 query 的模塊。
NodeJS 14.x[11] 中明確標記為 Legacy,官方推薦 URLSearchaParms 代替。
The querystring API is considered Legacy. New code should use the URLSearchParams[12] API instead.
但在 15.x 以及以后的版本又改為 Stable,但指出這是非標準 API。
querystring is more performant than ``[13] but is not a standardized API. Use <URLSearchParams> when performance is not critical or when compatibility with browser code is desirable.
功能類似 query-string,不支持嵌套對象的解析,這里不再贅述。
血統(tǒng)純正 URL / URLSearchParams
URL 和 URLSearchParams 是 URL API 規(guī)范[14] 中的兩個標準的接口。其提供了訪問、操作 URL 的 API。
其中,URL 定義了像域名、主機和 IP 地址等概念,URLSearchParams 定義了一些常用的方法來處理 Query String。我們重點介紹下后者。
URLSearchParams
兩種方式創(chuàng)建 URLSearchParams 對象,URLSearchParams構(gòu)造函數(shù)會忽略 search 中的?。
// 1. 通過 URL
const url=new URL('https://abc.com/path/v1?key=a&key=b%26c');
const search1=url.searchParams;
// 2. 直接構(gòu)造
const search2=new URLSearchParams(location.search);
.get(name)
該方法獲取的值會被自動 decode,如果 name 不存在返回 null,如果 value 不存在返回空字符串。
const search=new URLSearchParams('key=b%26c&key2');
search.get('key'); // b&c
search.get('key2'); // ''
search.get('key3'); // null
.getAll(name)
需要特別注意,如果有多個相同的 name,get() 只能獲取第一個值。獲取全部需要使用 getAll(),該函數(shù)返回數(shù)組(即便只有一個 value)。
const search=new URLSearchParams('key=a&key=b');
search.get('key'); // a
search.getAll('key'); // ['a', 'b']
.set(name, string) / .append(name, string)
向 URLSearchParams 中添加數(shù)據(jù),set() 會覆蓋原有值。如果需要添加重復的 name,需要使用 append()。
set() 和 append() 僅支持 string 類型的 value。同時 field 和 value 都會被 encode,無需額外處理。
const search=new URLSearchParams();
search.append('key', 'a');
search.append('key', 'b');
search.toString(); // key=a&key=b
.keys()
返回一個 IterableIterator迭代器,可以使用for...of遍歷。需要注意,重復的 key 會出現(xiàn)多次
const search=new URLSearchParams('key=a&key=b');
for (const key of search.keys()) {
console.log(key);
}
// key
// key
.toString()
獲取的 Query String,會被自動 encode 處理。空格轉(zhuǎn)成+。對于重復 field,使用了 field=v1&field=v2 的方式。
const search=new URLSearchParams();
search.set('key', '?&=')
search.set('key2', 'a b');
search.toString(); // key=%3F%26%3D&&key2=a+b
兼容
關(guān)于兼容,目前瀏覽器占比基本上沒有問題。實際開發(fā)中遇到 iOS10 以下不兼容的情況,使用 polyfill 即可。
總結(jié)對比
從上面的總結(jié)來看,我們發(fā)現(xiàn) qs 和 query-string / URLSearchParams 最大的差異在于對于多層嵌套對象(Nested object)的支持與否。
而當使用復雜的 JSON 數(shù)據(jù)結(jié)構(gòu)時,我們通常會使用JSON.stringify() 方法先將數(shù)據(jù)進行序列化(也稱字符串化),將復雜數(shù)據(jù)轉(zhuǎn)換成基本的字符串數(shù)據(jù)后,再進行傳輸。
因此通常情況下:
expressjs 的 body-parser 中,用戶可以自行選擇使用 qs 還是 querystring;
koajs的koa-body和bodyparser所依賴的 co-body,都選擇了qs。
當然了解了他們差異后,選擇哪種方式就要根據(jù)你的實際情況而定了。
整理資料過程中,引申出更多有趣的問題,也稍作整理。
空格編碼問題
還記得前面提到的編碼規(guī)則里, 空格的編碼可以是 + 或者%20,這里描述的就很模糊。
函數(shù)對比
我們先來看下上面不同 API 是如何處理的?
對+和%20的識別都沒問題(畢竟兼容還是能做到的),但是轉(zhuǎn)換空格URLSearchParams就有不同的邏輯了。至于為什么會有兩種編碼結(jié)果?
這里要特別說明的是URLSearchParams采用了application/x-www-form-urlencoded編碼模式,而這個編碼采用了一個非常早期(RFC 1738)的通用百分號編碼方法——就是將 空格轉(zhuǎn)換為+。至于為什么會采用這種方式,我猜想是因為要考慮到歷史兼容問題——生成的 URL 需要被那些舊的僅支持+的程序識別。
當然+已經(jīng)不推薦了,在 RFC 3986[15] 中已推薦使用%20。
特別說明
這里特別說明下 decodeURIComponent,是無法解析+為 空格的,因此實際業(yè)務中,如果無法保證傳入空格的編碼方式,還是使用URLSearchParams或者query-string來解析數(shù)據(jù)吧。
或者做一個簡單的兼容處理:
function decodeQueryParam(p) {
return decodeURIComponent(p.replace(/+/g, " "));
}
decodeQueryParam("search+query%20%28correct%29");
// 'search query (correct)'
擴展參考
URLSearchParams中 +的問題,具體細節(jié)可參考 whatwg 的描述:
As a URLSearchParams object uses the application/x-www-form-urlencoded format underneath there are some difference with how it encodes certain code points compared to a URL object (including href and search ). This can be especially surprising when using searchParams to operate on a URL[16]’s query[17].
URLSearchParams objects will percent-encode anything in the application/x-www-form-urlencoded percent-encode set, and will encode U+0020 SPACE as U+002B (+).
以及 whatwg 中關(guān)于 application/x-www-form-urlencoded 的描述:
Control names and values are escaped. Space characters are replaced by '+', and then reserved characters are escaped as described in [RFC1738][18], section 2.2: Non-alphanumeric characters are replaced by %HH, a percent sign and two hexadecimal digits representing the ASCII code of the character. Line breaks are represented as "CR LF" pairs (i.e., %0D%0A).
Content-type 中的 x-www-form-urlencoded
當我們在HTTP中使用 MIME 類型為x-www-form-urlencoded格式提交數(shù)據(jù)時,所使用的就是前文所介紹的編碼方式。
只是如果發(fā)送的是 GET 請求,數(shù)據(jù)會拼接在 Query 中;而發(fā)送 POST 請求則會將數(shù)據(jù)放置在消息體(body)中,通過Header中的Content-Type 來指定 MIME 類型。
當然并不是所有的數(shù)據(jù)都適合使用 x-www-form-urlencoded,通常有二進制數(shù)據(jù)時,urlencoded使用百分號%HH和UTF-8的編碼方式,會大大增加了數(shù)據(jù)的長度。為了節(jié)省傳輸數(shù)據(jù)的空間,會選擇form-data代替。
原生 from 表單的編碼
除了上面提到的各類函數(shù)外,原生 html 的 form 表單在提交數(shù)據(jù)時,本身也是可以進行編碼的。
<html>
<head>
<meta charset="UTF-8">
<!-- <meta charset="GBK">-->
</head>
<body>
<form action="/search" method="get" enctype="application/x-www-form-urlencoded">
<input type="text" name="name" required>
<input type="submit" value="提交">
</form>
</body>
</html>
當點擊提交時, 表單內(nèi)的 input 數(shù)據(jù)會進行百分號編碼。但要注意的是編碼的格式是按照 meta 中設定的 charset 進行的。例如當輸入「中」時,UTF-8 是 name=%E4%B8%AD,GBK 則是 name=%D6%D0。空格 則是+。
encodeURI(Component) 和 escape
前文提到過encodeURI(encodeURIComponent)使用 UTF-8 編碼,而 escape 是一個已經(jīng)被廢棄的非標準方式,其采用了 UTF-16 編碼,同時在碼點小于 255 的使用 %uXX 表示,碼點大于 255 的使用 %uXXXX 的方式。
同時要注意當decodeURI(decodeURIComponent)解析非法的 %HH 格式數(shù)據(jù)時(如不合規(guī)范的 UTF-8 數(shù)據(jù)、被截斷的%HH 字符等),會包拋出URIError異常。
try {
const a=decodeURIComponent("%E0%A4%A");
} catch (e) {
console.error(e);
}
// URIError: malformed URI sequence
因此如果無法保證數(shù)據(jù)的可用性,記得總是要 try...catch 一下比較保險。
或者更推薦使用類似`safe-decode-uri-component`[19]的三方庫,來避免這類麻煩。
至于 UTF-8 的合法格式是什么樣的,這就要涉及更多的編碼知識了。
[1] https://en.wikipedia.org/wiki/Query_string
[2] https://en.wikipedia.org/wiki/Uniform_resource_locator
[3] https://bytedance.feishu.cn/docx/D6LTd2zgHo2S5NxaFE6cnh9knHf#Oo6UdgOqSog2G2xsp3VcSPdIn6d
[4] https://zh.wikipedia.org/wiki/%E7%99%BE%E5%88%86%E5%8F%B7%E7%BC%96%E7%A0%81
[5] https://en.wikipedia.org/wiki/Percent-encoding
[6] https://datatracker.ietf.org/doc/html/rfc3986
[7] https://github.com/ljharb/qs
[8] https://www.npmjs.com/package/query-string
[9] https://www.baeldung.com/postman-form-data-raw-x-www-form-urlencoded
[10] https://github.com/visionmedia/node-querystring/issues
[11] https://nodejs.org/docs/latest-v14.x/api/querystring.html
[12] https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams
[13] https://nodejs.org/dist/latest-v19.x/docs/api/url.html#class-urlsearchparams
[14] https://url.spec.whatwg.org/#api
[15] https://datatracker.ietf.org/doc/html/rfc3986#section-2.1
[16] https://url.spec.whatwg.org/#concept-url
[17] https://url.spec.whatwg.org/#concept-url-query
[18] https://www.w3.org/TR/html4/references.html#ref-RFC1738
[19] https://github.com/jridgewell/safe-decode-uri-component
[20] https://stackoverflow.com/questions/29175465/body-parser-extended-option-qs-vs-querystring/29177740#29177740
[21] https://www.w3.org/TR/1999/REC-html401-19991224/appendix/notes.html#h-B.2.2
[22] https://stackoverflow.com/questions/1746507/authoritative-position-of-duplicate-http-get-query-keys
[23] https://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1
[24] https://url.spec.whatwg.org/#interface-urlsearchparams
[25] https://datatracker.ietf.org/doc/html/rfc1738
[26] https://www.w3.org/International/O-URL-code.html
關(guān)注「字節(jié)前端 ByteFE」公眾號,追更不迷路!
文末彩蛋 >>
碼上掘金正在舉辦第 2 期月賽,在這里邀請大家參與「互動抽獎」活動,參與互動即可抽獎哦~活動玩法請看這里 >>
https://juejin.cn/post/7213184860675571749/
家好,我是站長 polarisxu。
看到標題,大家應該知曉今天聊的主角是誰。是的,它就是 PHP。
PHP 曾經(jīng)很輝煌,現(xiàn)在怎么樣?不做過多評價,前幾天好未來不剛組織了一屆 PHP 大會嗎?!正因為曾經(jīng)很輝煌,很多現(xiàn)在的 Go 愛好者曾經(jīng)都是 PHPer,應該還有不少還在用著 PHP。我覺得完全沒必要非得貶低一門語言去抬高另外一門語言,自己喜歡就好。而且掌握多門語言是自己的優(yōu)勢。
為什么聊 PHP,因為我也寫了好幾年 PHP,而且現(xiàn)在也會關(guān)注 PHP 的一些動態(tài)。PHP 8 發(fā)布差不多半個月了,有些人可能根本不知曉,還停留在 PHP 5.x。沒想到吧,一眨眼,PHP 8 都發(fā)布了。
關(guān)于版本的那些事,這里不探討,主要看看 PHP 8 有哪些新特性。另外,本文只會講述新特性的一些關(guān)鍵點,因為官方文檔對它們已經(jīng)有更詳細的介紹,你應該認真閱讀官方文檔。
說明一點,從 PHP 7 開始,支持下面這樣的語法:
function sum(int $a, int $b): int {
return $a + $b;
}
是不是越來越強類型的感覺?雖然如此,但在非嚴格類型模式下(strict_types=0,這是默認值),你依然可以這么調(diào)用:
sum(1.2, 3);
但因為函數(shù)參數(shù)接收 int 類型(返回值也是 int 類型),因此上面結(jié)果是 4,而不是 4.2。如果是嚴格模式下,只允許傳遞 int 類型了。(sum('1.2', 3.0) 結(jié)果也是 4)
如果希望結(jié)果輸出 4.2,同時又保持類型約束,怎么辦?PHP 不支持方法重載。這就有了 PHP 8 的聯(lián)合類型。
聯(lián)合類型接受多個不同的類型做為參數(shù)。聲明聯(lián)合類型的語法為 T1|T2|...。
所以,上面代碼可以改為:
function sum(int|float $a, int|float $b): int|float{
return $a + $b;
}
這樣 sum(1.2, 3) 的結(jié)果就是 4.2 了。
一些注意事項:
還有其他一些細節(jié)點,詳情請訪問官方文檔查看:https://www.php.net/manual/zh/language.types.declarations.php。
不得不說,也許越來越意識到弱類型的問題,PHP 這是在做強類型的事情。然而,不少人要說了,搞這么費勁、這么復雜,還不如直接換強類型語言呢?!你覺得呢?
當然,你完全可以忽略聯(lián)合類型,繼續(xù)使用 5.x 的方式寫 PHP。
這個特性還是很棒的。了解 Python 的朋友應該對這個特性很熟悉。這樣一來,PHP 的函數(shù)支持不定參數(shù)、參數(shù)默認值、命名參數(shù)等。相對來說,Go 的函數(shù)還是弱很多。
比如 htmlspecialchars 函數(shù)簽名如下:
htmlspecialchars ( string $string [, int $flags = ENT_COMPAT | ENT_HTML401 [, string $encoding = ini_get("default_charset") [, bool $double_encode = TRUE ]]] ) : string
PHP 8 之前,如果想要最后一個參數(shù)傳遞 false,需要這么調(diào)用:
htmlspecialchars($string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);
而有了命名參數(shù)后(PHP 8),可以這么調(diào)用:
htmlspecialchars($string, double_encode: false);
簡單清晰。
總結(jié)一下就是:
命名參數(shù)確實帶來了不少便利。不過我覺得也有一些要注意的點:
實際中我們經(jīng)常通過 state 來表示各種狀態(tài),比如:0-待審核;1-上線;2-下線;3-刪除。因為數(shù)據(jù)庫中存的數(shù)字,但顯示希望是文字說明。這時一般有兩種做法:
switch ($state) {
case 0:
$stateDesc = '待審核';
break;
case 1:
$stateDesc = '上線';
break;
case 2:
$stateDesc = '下線';
break;
case 3:
$stateDesc = '刪除';
break;
}
echo $stateDesc;
我個人喜歡通過 map 來實現(xiàn):
$stateMap = [
0 => '待審核',
1 => '上線',
2 => '下線',
3 => '刪除',
];
echo $stateMap[$state];
PHP 8 針對這樣的場景提供了 match 表達式:
echo match($state) {
0 => '待審核',
1 => '上線',
2 => '下線',
3 => '刪除',
};
可見 match 類似于 switch 語句,有如下特點:
更多信息查看官方文檔:https://www.php.net/manual/zh/control-structures.match.php。
了解 Swift 之類的語言,應該知曉其中的可選型。PHP 8 新增的這個特性,我覺得多少有點可選型的意思。
在 PHP 7 中的如下代碼:
$country = null;
if ($session !== null) {
$user = $session->user;
if ($user !== null) {
$address = $user->getAddress();
if ($address !== null) {
$country = $address->country;
}
}
}
在 PHP 8 中簡化為:
$country = $session?->user?->getAddress()?->country;
PHP 8 起構(gòu)造器的參數(shù)可以提升為類的屬性。構(gòu)造器的參數(shù)賦值給類屬性的行為很普遍,否則無法操作。而構(gòu)造器提升的功能則為這種場景提供了便利。例如下面的代碼:
class Point {
public float $x;
public float $y;
public float $z;
public function __construct(
float $x = 0.0,
float $y = 0.0,
float $z = 0.0
) {
$this->x = $x;
$this->y = $y;
$this->z = $z;
}
}
改為 PHP 8 的方式:
class Point {
public function __construct(
public float $x = 0.0,
public float $y = 0.0,
public float $z = 0.0,
) {}
}
PHP 8 比較數(shù)字字符串(numeric string)時,會按數(shù)字進行比較。不是數(shù)字字符串時,將數(shù)字轉(zhuǎn)化為字符串,按字符串比較。
這一點要注意,之前這樣的代碼:
0 == 'foobar' // true
現(xiàn)在是 false:
0 == 'foobar' // fals
更多說明參見這里:https://wiki.php.net/rfc/string_to_number_comparison。
現(xiàn)在可以用 PHP 原生語法來使用結(jié)構(gòu)化的元數(shù)據(jù),而非 PHPDoc 聲明。
之前這么寫:
class PostsController{
/**
* @Route("/api/posts/{id}", methods={"GET"})
*/
public function get($id) { /* ... */ }
}
現(xiàn)在這么寫:
class PostsController{
#[Route("/api/posts/{id}", methods: ["GET"])]
public function get($id) { /* ... */ }
}
PHP 8 引入了兩個即時編譯引擎。Tracing JIT 在兩個中更有潛力,它在綜合基準測試中顯示了三倍的性能, 并在某些長時間運行的程序中顯示了 1.5-2 倍的性能改進。典型的應用性能則和 PHP 7.4 不相上下。
官方給了一個性能測試:
PHP 8 還有很多其他改動,在這里有詳細的說明:https://www.php.net/releases/8.0/zh.php。其中新增了 3 個函數(shù)實用的函數(shù):str_contains()、str_starts_with() 和 str_ends_with()。(Go 表示第一天就有了)
這里面的新特性,命名參數(shù)我個人還是比較喜歡。你呢?
*請認真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。