PSR-6 緩存接口規(guī)范

介紹

緩存是提升應(yīng)用性能的常用手段,為框架中最通用的功能,每個(gè)框架也都推出專屬的、功能多
樣的緩存庫(kù)。這些差別使得開發(fā)人員不得不學(xué)習(xí)多種系統(tǒng),而很多可能是他們并不需要的功能。
此外,緩存庫(kù)的開發(fā)者同樣面臨著一個(gè)窘境,是只支持有限數(shù)量的幾個(gè)框架還是創(chuàng)建一堆龐
大的適配器類。

一個(gè)通用的緩存系統(tǒng)接口可以解決掉這些問題。庫(kù)和框架的開發(fā)人員能夠知道緩存系統(tǒng)會(huì)按照他們所
預(yù)期的方式工作,緩存系統(tǒng)的開發(fā)人員只需要實(shí)現(xiàn)單一的接口,而不用去開發(fā)各種各樣的適配器。

目標(biāo)

本 PSR 的目標(biāo)是:創(chuàng)建一套通用的接口規(guī)范,能夠讓開發(fā)人員整合到現(xiàn)有框架和系統(tǒng),而不需要去
開發(fā)框架專屬的適配器類。

關(guān)于「能愿動(dòng)詞」的使用

為了避免歧義,文檔大量使用了「能愿動(dòng)詞」,對(duì)應(yīng)的解釋如下:

  • 必須 (MUST):絕對(duì),嚴(yán)格遵循,請(qǐng)照做,無條件遵守;
  • 一定不可 (MUST NOT):禁令,嚴(yán)令禁止;
  • 應(yīng)該 (SHOULD) :強(qiáng)烈建議這樣做,但是不強(qiáng)求;
  • 不該 (SHOULD NOT):強(qiáng)烈不建議這樣做,但是不強(qiáng)求;
  • 可以 (MAY)可選 (OPTIONAL) :選擇性高一點(diǎn),在這個(gè)文檔內(nèi),此詞語(yǔ)使用較少;

參見:RFC 2119

定義

  • 調(diào)用類庫(kù) (Calling Library) - 調(diào)用者,使用緩存服務(wù)的類庫(kù),這個(gè)類庫(kù)調(diào)用緩存服務(wù),調(diào)用的
    是此緩存接口規(guī)范的具體「實(shí)現(xiàn)類庫(kù)」,調(diào)用者不需要知道任何「緩存服務(wù)」的具體實(shí)現(xiàn)。

  • 實(shí)現(xiàn)類庫(kù) (Implementing Library) - 此類庫(kù)是對(duì)「緩存接口規(guī)范」的具體實(shí)現(xiàn),封裝起來的緩存服務(wù),供「調(diào)用類庫(kù)」使用。實(shí)現(xiàn)類庫(kù) 必須 提供 PHP 類來實(shí)現(xiàn)
    Cache\\CacheItemPoolInterfaceCache\\CacheItemInterface 接口。
    實(shí)現(xiàn)類庫(kù) 必須 支持最小的如下描述的 TTL 功能,秒級(jí)別的精準(zhǔn)度。

  • 生存時(shí)間值 (TTL - Time To Live) - 定義了緩存可以存活的時(shí)間,以秒為單位的整數(shù)值。

  • 過期時(shí)間 (Expiration) - 定義準(zhǔn)確的過期時(shí)間點(diǎn),一般為緩存存儲(chǔ)發(fā)生的時(shí)間點(diǎn)加上 TTL 時(shí)
    間值,也可以指定一個(gè) DateTime 對(duì)象。

    假如一個(gè)緩存項(xiàng)的 TTL 設(shè)置為 300 秒,保存于 1:30:00 ,那么緩存項(xiàng)的過期時(shí)間為 1:35:00。

    實(shí)現(xiàn)類庫(kù) 可以 讓緩存項(xiàng)提前過期,但是 必須 在到達(dá)過期時(shí)間時(shí)立即把緩存項(xiàng)標(biāo)示為
    過期。如果調(diào)用類庫(kù)在保存一個(gè)緩存項(xiàng)的時(shí)候未設(shè)置「過期時(shí)間」、或者設(shè)置了 null 作為過期
    時(shí)間(或者 TTL 設(shè)置為 null),實(shí)現(xiàn)類庫(kù) 可以 使用默認(rèn)自行配置的一個(gè)時(shí)間。如果沒
    有默認(rèn)時(shí)間,實(shí)現(xiàn)類庫(kù) 必須把存儲(chǔ)時(shí)間當(dāng)做 永久性 存儲(chǔ),或者按照底層驅(qū)動(dòng)能支持的
    最長(zhǎng)時(shí)間作為保持時(shí)間。

  • 鍵 (KEY) - 長(zhǎng)度大于 1 的字串,用作緩存項(xiàng)在緩存系統(tǒng)里的唯一標(biāo)識(shí)符。實(shí)現(xiàn)類庫(kù)
    必須 支持「鍵」規(guī)則 A-Z, a-z, 0-9, _, 和 . 任何順序的 UTF-8 編碼,長(zhǎng)度
    小于 64 位。實(shí)現(xiàn)類庫(kù) 可以 支持更多的編碼或者更長(zhǎng)的長(zhǎng)度,不過 必須 支持至少以上指定
    的編碼和長(zhǎng)度。實(shí)現(xiàn)類庫(kù)可自行實(shí)現(xiàn)對(duì)「鍵」的轉(zhuǎn)義,但是 必須 保證能夠無損的返回「鍵」字串。以下
    的字串作為系統(tǒng)保留: {}()/\\@:一定不可 作為「鍵」的命名支持。

  • 命中 (Hit) - 一個(gè)緩存的命中,指的是當(dāng)調(diào)用類庫(kù)使用「鍵」在請(qǐng)求一個(gè)緩存項(xiàng)的時(shí)候,在緩存
    池里能找到對(duì)應(yīng)的緩存項(xiàng),并且此緩存項(xiàng)還未過期,并且此數(shù)據(jù)不會(huì)因?yàn)槿魏卧虺霈F(xiàn)錯(cuò)誤。調(diào)用類
    庫(kù) 應(yīng)該 確保先驗(yàn)證下 isHit() 有命中后才調(diào)用 get() 獲取數(shù)據(jù)。

  • 未命中 (Miss) - 一個(gè)緩存未命中,是完全的上面描述的「命中」的相反。指的是當(dāng)調(diào)用類庫(kù)使用「鍵」在請(qǐng)求一個(gè)緩存項(xiàng)的時(shí)候,在緩存池里未能找到對(duì)應(yīng)的緩存項(xiàng),或者此緩存項(xiàng)已經(jīng)過期,或者此數(shù)據(jù)因?yàn)槿魏卧虺霈F(xiàn)錯(cuò)誤。一個(gè)過期的緩存項(xiàng),必須 被當(dāng)做 未命中 來對(duì)待。

  • 延遲 (Deferred) - 一個(gè)延遲的緩存,指的是這個(gè)緩存項(xiàng)可能不會(huì)立刻被存儲(chǔ)到物理緩存池里。一個(gè)
    緩存池對(duì)象 可以 對(duì)一個(gè)指定延遲的緩存項(xiàng)進(jìn)行延遲存儲(chǔ),這樣做的好處是可以利用一些緩存服務(wù)器提供
    的批量插入功能。緩存池 必須 能對(duì)所有延遲緩存最終能持久化,并且不會(huì)丟失。可以 在調(diào)用類庫(kù)還未發(fā)起保存請(qǐng)求之前就做持久化。當(dāng)調(diào)用類庫(kù)調(diào)用 commit() 方法時(shí),所有的延遲緩存都 必須
    做持久化。實(shí)現(xiàn)類庫(kù) 可以 自行決定使用什么邏輯來觸發(fā)數(shù)據(jù)持久化,如對(duì)象的 析構(gòu)方法 (destructor) 內(nèi)、調(diào)用 save() 時(shí)持久化、倒計(jì)時(shí)保存或者觸及最大數(shù)量時(shí)保存等。當(dāng)請(qǐng)求一個(gè)延遲
    緩存項(xiàng)時(shí),必須 返回一個(gè)延遲,未持久化的緩存項(xiàng)對(duì)象。

數(shù)據(jù)

實(shí)現(xiàn)類庫(kù) 必須 支持所有的可序列化的 PHP 數(shù)據(jù)類型,包含:

  • 字符串 - 任何大小的 PHP 兼容字符串
  • 整數(shù) - PHP 支持的低于 64 位的有符號(hào)整數(shù)值
  • 浮點(diǎn)數(shù) - 所有的有符號(hào)浮點(diǎn)數(shù)
  • 布爾 - true 和 false.
  • Null - null
  • 數(shù)組 - 各種形式的 PHP 數(shù)組
  • 對(duì)象(Object) - 所有的支持無損序列化和反序列化的對(duì)象,如:$o == unserialize(serialize($o)) 。對(duì)象 可以
    使用 PHP 的 Serializable 接口,__sleep() 或者 __wakeup() 魔術(shù)方法,或者在合適的情況下,使用其他類似的語(yǔ)言特性。

所有存進(jìn)實(shí)現(xiàn)類庫(kù)的數(shù)據(jù),都 必須 能做到原封不動(dòng)的取出。連類型也 必須 是完全一致,如果
存進(jìn)緩存的是字符串 5,取出來的卻是整數(shù)值 5 的話,可以算作嚴(yán)重的錯(cuò)誤。實(shí)現(xiàn)類庫(kù) 可以 使用 PHP 的「serialize()/unserialize() 方法」作為底層實(shí)現(xiàn),不過不強(qiáng)迫這樣做。對(duì)于他們的兼容性,以能支持所有數(shù)據(jù)類型作為基準(zhǔn)線。

實(shí)在無法「完整取出」存入的數(shù)據(jù)的話,實(shí)現(xiàn)類庫(kù) 必須 把「緩存丟失」標(biāo)示作為返回,而不是損壞了的數(shù)據(jù)。

主要概念

緩存池 Pool

緩存池包含緩存系統(tǒng)里所有緩存數(shù)據(jù)的集合。緩存池邏輯上是所有緩存項(xiàng)存儲(chǔ)的倉(cāng)庫(kù),所有存儲(chǔ)進(jìn)去的數(shù)據(jù),
都能從緩存池里取出來,所有的對(duì)緩存的操作,都發(fā)生在緩存池子里。

緩存項(xiàng) Items

一條緩存項(xiàng)在緩存池里代表了一對(duì)「鍵/值」對(duì)應(yīng)的數(shù)據(jù),「鍵」被視為每一個(gè)緩存項(xiàng)主鍵,是緩存項(xiàng)的
唯一標(biāo)識(shí)符,必須 是不可變更的,當(dāng)然,「值」可以 任意變更。

錯(cuò)誤處理

緩存對(duì)應(yīng)用性能起著至關(guān)重要的作用,但是,無論在任何情況下,緩存 一定不可 作為應(yīng)用程序不
可或缺的核心功能。

緩存系統(tǒng)里的錯(cuò)誤 一定不可 導(dǎo)致應(yīng)用程序故障,所以,實(shí)現(xiàn)類庫(kù) 一定不可 拋出任何除了
此接口規(guī)范定義的以外的異常,并且 必須 捕捉包括底層存儲(chǔ)驅(qū)動(dòng)拋出的異常,不讓其冒泡至超
出緩存系統(tǒng)內(nèi)。

實(shí)現(xiàn)類庫(kù) 應(yīng)該 對(duì)此類錯(cuò)誤進(jìn)行記錄,或者以任何形式通知管理員。

調(diào)用類庫(kù)發(fā)起刪除緩存項(xiàng)的請(qǐng)求,或者清空整個(gè)緩沖池子的請(qǐng)求,「鍵」不存在的話 必須 不能
當(dāng)成是有錯(cuò)誤發(fā)生。后置條件是一樣的,如果取數(shù)據(jù)時(shí),「鍵」不存在的話 必須 不能當(dāng)成是有錯(cuò)誤發(fā)生

接口

CacheItemInterface

CacheItemInterface 定義了緩存系統(tǒng)里的一個(gè)緩存項(xiàng)。每一個(gè)緩存項(xiàng) 必須 有一個(gè)「鍵」與之相
關(guān)聯(lián),此「鍵」通常是通過 Cache\CacheItemPoolInterface 來設(shè)置。

Cache\CacheItemInterface 對(duì)象把緩存項(xiàng)的存儲(chǔ)進(jìn)行了封裝,每一個(gè) Cache\CacheItemInterface 由一個(gè) Cache\CacheItemPoolInterface 對(duì)象生成,CacheItemPoolInterface 負(fù)責(zé)一些必須的設(shè)置,并且給對(duì)象設(shè)置具有 唯一性 的「鍵」。

Cache\CacheItemInterface 對(duì)象 必須 能夠存儲(chǔ)和取出任何類型的,在「數(shù)據(jù)」章節(jié)定義的 PHP 數(shù)值。

調(diào)用類庫(kù) 一定不可 擅自初始化「CacheItemInterface」對(duì)象,「緩存項(xiàng)」只能使用「CacheItemPoolInterface」對(duì)象的 getItem() 方法來獲取。調(diào)用類庫(kù) 一定不可 假設(shè)
由一個(gè)實(shí)現(xiàn)類庫(kù)創(chuàng)建的「緩存項(xiàng)」能被另一個(gè)實(shí)現(xiàn)類庫(kù)完全兼容。

namespace Psr\\Cache;

/**
 * CacheItemInterface 定了緩存系統(tǒng)里對(duì)緩存項(xiàng)操作的接口
 */
interface CacheItemInterface
{
    /**
     * 返回當(dāng)前緩存項(xiàng)的「鍵」
     * 
     * 「鍵」由實(shí)現(xiàn)類庫(kù)來加載,并且高層的調(diào)用者(如:CacheItemPoolInterface)
     *  **應(yīng)該** 能使用此方法來獲取到「鍵」的信息。
     *
     * @return string
     *   當(dāng)前緩存項(xiàng)的「鍵」
     */
    public function getKey();

    /**
     * 憑借此緩存項(xiàng)的「鍵」從緩存系統(tǒng)里面取出緩存項(xiàng)。
     *
     * 取出的數(shù)據(jù) **必須** 跟使用 `set()` 存進(jìn)去的數(shù)據(jù)是一模一樣的。
     *
     * 如果 `isHit()` 返回 false 的話,此方法必須返回 `null`,需要注意的是 `null` 
     * 本來就是一個(gè)合法的緩存數(shù)據(jù),所以你 **應(yīng)該** 使用 `isHit()` 方法來辨別到底是
     * "返回 null 數(shù)據(jù)" 還是 "緩存里沒有此數(shù)據(jù)"。
     *
     * @return mixed
     *   此緩存項(xiàng)的「鍵」對(duì)應(yīng)的「值」,如果找不到的話,返回 `null`
     */
    public function get();

    /**
     * 確認(rèn)緩存項(xiàng)的檢查是否命中。
     * 
     * 注意: 調(diào)用此方法和調(diào)用 `get()` 時(shí) **一定不可** 有先后順序之分。
     *
     * @return bool
     *   如果緩沖池里有命中的話,返回 `true`,反之返回 `false`
     */
    public function isHit();

    /**
     * 為此緩存項(xiàng)設(shè)置「值」。
     *
     * 參數(shù) $value 可以是所有能被 PHP 序列化的數(shù)據(jù),序列化的邏輯
     * 需要在實(shí)現(xiàn)類庫(kù)里書寫。
     *
     * @param mixed $value
     *   將被存儲(chǔ)的可序列化的數(shù)據(jù)。
     *
     * @return static
     *   返回當(dāng)前對(duì)象。
     */
    public function set($value);

    /**
     * 設(shè)置緩存項(xiàng)的準(zhǔn)確過期時(shí)間點(diǎn)。
     *
     * @param \\DateTimeInterface $expiration
     * 
     *   過期的準(zhǔn)確時(shí)間點(diǎn),過了這個(gè)時(shí)間點(diǎn)后,緩存項(xiàng)就 **必須** 被認(rèn)為是過期了的。
     *   如果明確的傳參 `null` 的話,**可以** 使用一個(gè)默認(rèn)的時(shí)間。
     *   如果沒有設(shè)置的話,緩存 **應(yīng)該** 存儲(chǔ)到底層實(shí)現(xiàn)的最大允許時(shí)間。
     *
     * @return static
     *   返回當(dāng)前對(duì)象。
     */
    public function expiresAt($expiration);

    /**
     * 設(shè)置緩存項(xiàng)的過期時(shí)間。
     *
     * @param int|\\DateInterval $time
     *   以秒為單位的過期時(shí)長(zhǎng),過了這段時(shí)間后,緩存項(xiàng)就 **必須** 被認(rèn)為是過期了的。
     *   如果明確的傳參 `null` 的話,**可以** 使用一個(gè)默認(rèn)的時(shí)間。
     *   如果沒有設(shè)置的話,緩存 **應(yīng)該** 存儲(chǔ)到底層實(shí)現(xiàn)的最大允許時(shí)間。
     *
     * @return static
     *   返回當(dāng)前對(duì)象
     */
    public function expiresAfter($time);

}

CacheItemPoolInterface

Cache\CacheItemPoolInterface 的主要目的是從調(diào)用類庫(kù)接收「鍵」,然后返回對(duì)應(yīng)的 Cache\CacheItemInterface 對(duì)象。

此接口也是作為主要的,與整個(gè)緩存集合交互的方式。所有的配置和初始化由實(shí)現(xiàn)類庫(kù)自行實(shí)現(xiàn)。

namespace Psr\\Cache;

/**
 * CacheItemPoolInterface 生成 CacheItemInterface 對(duì)象
 */
interface CacheItemPoolInterface
{
    /**
     * 返回「鍵」對(duì)應(yīng)的一個(gè)緩存項(xiàng)。
     *
     * 此方法 **必須** 返回一個(gè) CacheItemInterface 對(duì)象,即使是找不到對(duì)應(yīng)的緩存項(xiàng)
     * 也 **一定不可** 返回 `null`。
     *
     * @param string $key
     *   用來搜索緩存項(xiàng)的「鍵」。
     *
     * @throws InvalidArgumentException
     *   如果 $key 不是合法的值,\\Psr\\Cache\\InvalidArgumentException 異常會(huì)被拋出。
     *
     * @return CacheItemInterface
     *   對(duì)應(yīng)的緩存項(xiàng)。
     */
    public function getItem($key);

    /**
     * 返回一個(gè)可供遍歷的緩存項(xiàng)集合。
     *
     * @param array $keys
     *   由一個(gè)或者多個(gè)「鍵」組成的數(shù)組。
     *
     * @throws InvalidArgumentException
     *   如果 $keys 里面有哪個(gè)「鍵」不是合法,\\Psr\\Cache\\InvalidArgumentException 異常
     *   會(huì)被拋出。
     *   
     * @return array|\\Traversable
     *   返回一個(gè)可供遍歷的緩存項(xiàng)集合,集合里每個(gè)元素的標(biāo)識(shí)符由「鍵」組成,即使即使是找不到對(duì)
     *   的緩存項(xiàng),也要返回一個(gè)「CacheItemInterface」對(duì)象到對(duì)應(yīng)的「鍵」中。
     *   如果傳參的數(shù)組為空,也需要返回一個(gè)空的可遍歷的集合。
     */
    public function getItems(array $keys = array());

    /**
     * 檢查緩存系統(tǒng)中是否有「鍵」對(duì)應(yīng)的緩存項(xiàng)。
     *
     * 注意: 此方法應(yīng)該調(diào)用 `CacheItemInterface::isHit()` 來做檢查操作,而不是
     * `CacheItemInterface::get()`
     *
     * @param string $key
     *   用來搜索緩存項(xiàng)的「鍵」。
     *
     * @throws InvalidArgumentException
     *   如果 $key 不是合法的值,\\Psr\\Cache\\InvalidArgumentException 異常會(huì)被拋出。
     *
     * @return bool
     *   如果存在「鍵」對(duì)應(yīng)的緩存項(xiàng)即返回 true,否則 false
     */
    public function hasItem($key);

    /**
     * 清空緩沖池
     *
     * @return bool
     *   成功返回 true,有錯(cuò)誤發(fā)生返回 false
     */
    public function clear();

    /**
     * 從緩沖池里移除某個(gè)緩存項(xiàng)
     *
     * @param string $key
     *   用來搜索緩存項(xiàng)的「鍵」。
     *
     * @throws InvalidArgumentException
     *   如果 $key 不是合法的值,\\Psr\\Cache\\InvalidArgumentException 異常會(huì)被拋出。
     *
     * @return bool
     *   成功返回 true,有錯(cuò)誤發(fā)生返回 false
     */
    public function deleteItem($key);

    /**
     * 從緩沖池里移除多個(gè)緩存項(xiàng)
     *
     * @param array $keys
     *   由一個(gè)或者多個(gè)「鍵」組成的數(shù)組。
     *   
     * @throws InvalidArgumentException
     *   如果 $keys 里面有哪個(gè)「鍵」不是合法,\\Psr\\Cache\\InvalidArgumentException 異常
     *   會(huì)被拋出。
     *
     * @return bool
     *   成功返回 true,有錯(cuò)誤發(fā)生返回 false
     */
    public function deleteItems(array $keys);

    /**
     * 立刻為「CacheItemInterface」對(duì)象做數(shù)據(jù)持久化。
     *
     * @param CacheItemInterface $item
     *   將要被存儲(chǔ)的緩存項(xiàng)
     *
     * @return bool
     *   成功返回 true,有錯(cuò)誤發(fā)生返回 false
     */
    public function save(CacheItemInterface $item);

    /**
     * 稍后為「CacheItemInterface」對(duì)象做數(shù)據(jù)持久化。
     *
     * @param CacheItemInterface $item
     *   將要被存儲(chǔ)的緩存項(xiàng)
     *
     * @return bool
     *   成功返回 true,有錯(cuò)誤發(fā)生返回 false
     */
    public function saveDeferred(CacheItemInterface $item);

    /**
     * 提交所有的正在隊(duì)列里等待的請(qǐng)求到數(shù)據(jù)持久層,配合 `saveDeferred()` 使用
     *
     * @return bool
     *  成功返回 true,有錯(cuò)誤發(fā)生返回 false
     */
    public function commit();
}

CacheException

此異常用于緩存系統(tǒng)發(fā)生的所有嚴(yán)重錯(cuò)誤,包括但不限制于 緩存系統(tǒng)配置,如連接到緩存服務(wù)器出錯(cuò)、錯(cuò)
誤的用戶身份認(rèn)證等。

所有的實(shí)現(xiàn)類庫(kù)拋出的異常都 必須 實(shí)現(xiàn)此接口。

namespace Psr\\Cache;

/**
 * 被所有的實(shí)現(xiàn)類庫(kù)拋出的異常繼承的「異常接口」
 */
interface CacheException
{
}

InvalidArgumentException

namespace Psr\\Cache;

/**
 * 傳參錯(cuò)誤拋出的異常接口
 *
 * 當(dāng)一個(gè)錯(cuò)誤或者非法的傳參發(fā)生時(shí),**必須** 拋出一個(gè)繼承了
 * Psr\\Cache\\InvalidArgumentException 的異常
 */
interface InvalidArgumentException extends CacheException
{
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 介紹緩存是提升應(yīng)用性能的常用手段,為框架中最通用的功能,每個(gè)框架也都推出專屬的、功能多 樣的緩存庫(kù)。這些差別使得開...
    L匿名君閱讀 368評(píng)論 0 0
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,201評(píng)論 4 61
  • 第一日 這世上最困難的行動(dòng)應(yīng)該就是集合,車子承載著“滿腹牢騷”和“歡聲笑語(yǔ)”終于駛出了學(xué)校。一路的昏昏睡睡,一路的...
    旭啊旭閱讀 647評(píng)論 0 2
  • 印象.停留 來到云安巷的第十天,找到了鐘點(diǎn)工的工作。在一家咖啡店。 咖啡店叫“印象”,就在巷子盡頭的右拐彎處,它的...
    哎呀媛媛媛閱讀 1,374評(píng)論 1 5
  • 2016年2月24號(hào)結(jié)束在Accra的職前適應(yīng),上午九點(diǎn)鐘左右從Accra出發(fā)往Kimasi……加納第二大城市位于...
    存敬閱讀 460評(píng)論 0 2

友情鏈接更多精彩內(nèi)容