1<?php
2/*
3 * Copyright 2016 Google Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18namespace Google\Auth\Cache;
19
20use DateTime;
21use DateTimeInterface;
22use DateTimeZone;
23use Psr\Cache\CacheItemInterface;
24use TypeError;
25
26/**
27 * A cache item.
28 */
29final class Item implements CacheItemInterface
30{
31    /**
32     * @var string
33     */
34    private $key;
35
36    /**
37     * @var mixed
38     */
39    private $value;
40
41    /**
42     * @var DateTimeInterface|null
43     */
44    private $expiration;
45
46    /**
47     * @var bool
48     */
49    private $isHit = false;
50
51    /**
52     * @param string $key
53     */
54    public function __construct($key)
55    {
56        $this->key = $key;
57    }
58
59    /**
60     * {@inheritdoc}
61     */
62    public function getKey()
63    {
64        return $this->key;
65    }
66
67    /**
68     * {@inheritdoc}
69     */
70    public function get()
71    {
72        return $this->isHit() ? $this->value : null;
73    }
74
75    /**
76     * {@inheritdoc}
77     */
78    public function isHit()
79    {
80        if (!$this->isHit) {
81            return false;
82        }
83
84        if ($this->expiration === null) {
85            return true;
86        }
87
88        return $this->currentTime()->getTimestamp() < $this->expiration->getTimestamp();
89    }
90
91    /**
92     * {@inheritdoc}
93     */
94    public function set($value)
95    {
96        $this->isHit = true;
97        $this->value = $value;
98
99        return $this;
100    }
101
102    /**
103     * {@inheritdoc}
104     */
105    public function expiresAt($expiration)
106    {
107        if ($this->isValidExpiration($expiration)) {
108            $this->expiration = $expiration;
109
110            return $this;
111        }
112
113        $error = sprintf(
114            'Argument 1 passed to %s::expiresAt() must implement interface DateTimeInterface, %s given',
115            get_class($this),
116            gettype($expiration)
117        );
118
119        throw new TypeError($error);
120    }
121
122    /**
123     * {@inheritdoc}
124     */
125    public function expiresAfter($time)
126    {
127        if (is_int($time)) {
128            $this->expiration = $this->currentTime()->add(new \DateInterval("PT{$time}S"));
129        } elseif ($time instanceof \DateInterval) {
130            $this->expiration = $this->currentTime()->add($time);
131        } elseif ($time === null) {
132            $this->expiration = $time;
133        } else {
134            $message = 'Argument 1 passed to %s::expiresAfter() must be an ' .
135                       'instance of DateInterval or of the type integer, %s given';
136            $error = sprintf($message, get_class($this), gettype($time));
137
138            throw new TypeError($error);
139        }
140
141        return $this;
142    }
143
144    /**
145     * Determines if an expiration is valid based on the rules defined by PSR6.
146     *
147     * @param mixed $expiration
148     * @return bool
149     */
150    private function isValidExpiration($expiration)
151    {
152        if ($expiration === null) {
153            return true;
154        }
155
156        if ($expiration instanceof DateTimeInterface) {
157            return true;
158        }
159
160        return false;
161    }
162
163    /**
164     * @return DateTime
165     */
166    protected function currentTime()
167    {
168        return new DateTime('now', new DateTimeZone('UTC'));
169    }
170}
171