ホームページ >バックエンド開発 >PHPチュートリアル >PHP の PSR キャッシュ インターフェイス

PHP の PSR キャッシュ インターフェイス

DDD
DDDオリジナル
2025-01-11 16:05:43979ブラウズ

PSR-Caching Interface in PHP

皆さん、こんにちは!

データベース クエリが繰り返されるため、アプリケーションの実行が遅くなっていませんか?または、異なるキャッシュ ライブラリ間の切り替えに問題がありますか? PHP でのキャッシュを予測可能かつ交換可能にする標準である PSR-6 について詳しく見ていきましょう。

この記事は、PHP PSR 標準シリーズの一部です。初めての方は、PSR-1 の基本から始めることをお勧めします。

PSR-6 はどのような問題を解決しますか? (2分)

PSR-6 より前は、各キャッシュ ライブラリは独自の動作方法を持っていました。 Memcached から Redis に切り替えたいですか?コードを書き直します。あるフレームワークから別のフレームワークに移行しますか?新しいキャッシュ API について学習します。 PSR-6 は、すべてのキャッシュ ライブラリが実装できる共通のインターフェイスを提供することで、この問題を解決します。

コアインターフェイス (5 分)

2 人の主なプレーヤーを見てみましょう:

1. CacheItemPoolInterface

これはキャッシュマネージャーです。アイテムを保管したり取り出したりできる倉庫と考えてください:

<code class="language-php"><?php namespace Psr\Cache;

interface CacheItemPoolInterface
{
    public function getItem($key);
    public function getItems(array $keys = array());
    public function hasItem($key);
    public function clear();
    public function deleteItem($key);
    public function deleteItems(array $keys);
    public function save(CacheItemInterface $item);
    public function saveDeferred(CacheItemInterface $item);
    public function commit();
}

?></code>

2.CacheItemInterface

これはキャッシュ内の 1 つの項目を表します:

<code class="language-php"><?php namespace Psr\Cache;

interface CacheItemInterface
{
    public function getKey();
    public function get();
    public function isHit();
    public function set($value);
    public function expiresAt($expiration);
    public function expiresAfter($time);
}

?></code>

実践的な応用 (10 分)

これは私たちのコードベースからの実践的な例です:

1. キャッシュアイテムの実装

<code class="language-php"><?php namespace JonesRussell\PhpFigGuide\PSR6;

use Psr\Cache\CacheItemInterface;
use DateTimeInterface;
use DateInterval;
use DateTime;

class CacheItem implements CacheItemInterface
{
    private $key;
    private $value;
    private $isHit;
    private $expiration;

    public function __construct(string $key)
    {
        $this->key = $key;
        $this->isHit = false;
    }

    public function getKey(): string
    {
        return $this->key;
    }

    public function get()
    {
        return $this->value;
    }

    public function isHit(): bool
    {
        return $this->isHit;
    }

    public function set($value): self
    {
        $this->value = $value;
        return $this;
    }

    public function expiresAt(?DateTimeInterface $expiration): self
    {
        $this->expiration = $expiration;
        return $this;
    }

    public function expiresAfter($time): self
    {
        if ($time instanceof DateInterval) {
            $this->expiration = (new DateTime())->add($time);
        } elseif (is_int($time)) {
            $this->expiration = (new DateTime())->add(new DateInterval("PT{$time}S"));
        } else {
            $this->expiration = null;
        }
        return $this;
    }

    // Helper method for our implementation
    public function getExpiration(): ?DateTimeInterface
    {
        return $this->expiration;
    }

    // Helper method for our implementation
    public function setIsHit(bool $hit): void
    {
        $this->isHit = $hit;
    }
}</code>

2. キャッシュプールの実装

<code class="language-php"><?php namespace JonesRussell\PhpFigGuide\PSR6;

use Psr\Cache\CacheItemPoolInterface;
use Psr\Cache\CacheItemInterface;
use RuntimeException;

class FileCachePool implements CacheItemPoolInterface
{
    private $directory;
    private $deferred = [];

    public function __construct(string $directory)
    {
        if (!is_dir($directory) && !mkdir($directory, 0777, true)) {
            throw new RuntimeException("Cannot create cache directory: {$directory}");
        }
        $this->directory = $directory;
    }

    public function getItem($key): CacheItemInterface
    {
        $this->validateKey($key);

        if (isset($this->deferred[$key])) {
            return $this->deferred[$key];
        }

        $item = new CacheItem($key);
        $path = $this->getPath($key);

        if (file_exists($path)) {
            try {
                $data = unserialize(file_get_contents($path));
                if (!$data['expiration'] || $data['expiration'] > new DateTime()) {
                    $item->set($data['value']);
                    $item->setIsHit(true);
                    return $item;
                }
                unlink($path);
            } catch (\Exception $e) {
                // Log error and continue with cache miss
            }
        }

        return $item;
    }

    public function getItems(array $keys = []): iterable
    {
        $items = [];
        foreach ($keys as $key) {
            $items[$key] = $this->getItem($key);
        }
        return $items;
    }

    public function hasItem($key): bool
    {
        return $this->getItem($key)->isHit();
    }

    public function clear(): bool
    {
        $this->deferred = [];
        $files = glob($this->directory . '/*.cache');

        if ($files === false) {
            return false;
        }

        $success = true;
        foreach ($files as $file) {
            if (!unlink($file)) {
                $success = false;
            }
        }
        return $success;
    }

    public function deleteItem($key): bool
    {
        $this->validateKey($key);
        unset($this->deferred[$key]);

        $path = $this->getPath($key);
        if (file_exists($path)) {
            return unlink($path);
        }
        return true;
    }

    public function deleteItems(array $keys): bool
    {
        $success = true;
        foreach ($keys as $key) {
            if (!$this->deleteItem($key)) {
                $success = false;
            }
        }
        return $success;
    }

    public function save(CacheItemInterface $item): bool
    {
        $path = $this->getPath($item->getKey());
        $data = [
            'value' => $item->get(),
            'expiration' => $item->getExpiration()
        ];

        try {
            if (file_put_contents($path, serialize($data)) === false) {
                return false;
            }
            return true;
        } catch (\Exception $e) {
            return false;
        }
    }

    public function saveDeferred(CacheItemInterface $item): bool
    {
        $this->deferred[$item->getKey()] = $item;
        return true;
    }

    public function commit(): bool
    {
        $success = true;
        foreach ($this->deferred as $item) {
            if (!$this->save($item)) {
                $success = false;
            }
        }
        $this->deferred = [];
        return $success;
    }

    private function getPath(string $key): string
    {
        return $this->directory . '/' . sha1($key) . '.cache';
    }

    private function validateKey(string $key): void
    {
        if (!is_string($key) || preg_match('#[{}()/@:\\]#', $key)) {
            throw new InvalidArgumentException(
                'Invalid key: ' . var_export($key, true)
            );
        }
    }
}</code>

実践的使用 (5 分)

実際のコードでの使用方法を見てみましょう:

<code class="language-php">// 基本用法
$pool = new FileCachePool('/path/to/cache');

try {
    // 存储值
    $item = $pool->getItem('user.1');
    if (!$item->isHit()) {
        $userData = $database->fetchUser(1); // 您的数据库调用
        $item->set($userData)
             ->expiresAfter(3600); // 1 小时
        $pool->save($item);
    }
    $user = $item->get();
} catch (\Exception $e) {
    // 优雅地处理错误
    log_error('缓存操作失败:' . $e->getMessage());
    $user = $database->fetchUser(1); // 回退到数据库
}</code>

一般的なトラップ (3 分)

  1. キーの検証
  2. エラー処理

次は何ですか?

明日は、PSR-7 (HTTP メッセージ インターフェイス) について説明します。より単純なキャッシュに興味がある場合は、PSR-6 のより簡単な代替手段を提供する今後の PSR-16 (単純なキャッシュ) の記事をお待ちください。

リソース

  • PSR-6 公式仕様
  • サンプル コードベース (v0.6.0 - PSR-6 実装)
  • Symfony キャッシュコンポーネント
  • PHP キャッシュ

以上がPHP の PSR キャッシュ インターフェイスの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。