1: <?php
2:
3: namespace Charcoal\Object;
4:
5: // Dependencies from `PHP`
6: use \DateTime;
7: use \DateTimeInterface;
8: use \InvalidArgumentException;
9:
10: /**
11: * A full implementation, as trait, of the `PublishableInterface`.
12: */
13: trait PublishableTrait
14: {
15: /**
16: * @var DateTimeInterface $publishDate
17: */
18: protected $publishDate;
19:
20: /**
21: * @var DateTimeInterface $expiryDate
22: */
23: protected $expiryDate;
24:
25: /**
26: * @var string $publishStatus
27: */
28: protected $publishStatus;
29:
30: /**
31: * @param string|DateTimeInterface|null $publishDate The publishing date.
32: * @throws InvalidArgumentException If the date/time is invalid.
33: * @return PublishableInterface Chainable
34: */
35: public function setPublishDate($publishDate)
36: {
37: if ($publishDate === null || $publishDate === '') {
38: $this->publishDate = null;
39: return $this;
40: }
41: if (is_string($publishDate)) {
42: $publishDate = new DateTime($publishDate);
43: }
44: if (!($publishDate instanceof DateTimeInterface)) {
45: throw new InvalidArgumentException(
46: 'Invalid "Publish Date" value. Must be a date/time string or a DateTime object.'
47: );
48: }
49: $this->publishDate = $publishDate;
50: return $this;
51: }
52:
53: /**
54: * @return DateTimeInterface|null
55: */
56: public function publishDate()
57: {
58: return $this->publishDate;
59: }
60:
61: /**
62: * @param string|DateTimeInterface|null $expiryDate The expiry date.
63: * @throws InvalidArgumentException If the date/time is invalid.
64: * @return PublishableInterface Chainable
65: */
66: public function setExpiryDate($expiryDate)
67: {
68: if ($expiryDate === null || $expiryDate === '') {
69: $this->expiryDate = null;
70: return $this;
71: }
72: if (is_string($expiryDate)) {
73: $expiryDate = new DateTime($expiryDate);
74: }
75: if (!($expiryDate instanceof DateTimeInterface)) {
76: throw new InvalidArgumentException(
77: 'Invalid "Expiry Date" value. Must be a date/time string or a DateTime object.'
78: );
79: }
80: $this->expiryDate = $expiryDate;
81: return $this;
82: }
83:
84: /**
85: * @return DateTimeInterface|null
86: */
87: public function expiryDate()
88: {
89: return $this->expiryDate;
90: }
91:
92: /**
93: * @param string $status The publish status (draft, pending or published).
94: * @throws InvalidArgumentException If the status is not one of the 3 valid status.
95: * @return PublishableInterface Chainable
96: */
97: public function setPublishStatus($status)
98: {
99: $validStatus = [
100: '',
101: 'draft',
102: 'pending',
103: 'expired',
104: 'published'
105: ];
106: if (!in_array($status, $validStatus)) {
107: throw new InvalidArgumentException(
108: sprintf('Status "%s" is not a valid publish status.', $status)
109: );
110: }
111: $this->publishStatus = $status;
112: return $this;
113: }
114:
115: /**
116: * Get the object's publish status.
117: *
118: * Status can be:
119: * - `draft`
120: * - `pending`
121: * - `published`
122: * - `upcoming`
123: * - `expired`
124: *
125: * Note that the `upcoming` and `expired` status are specialized status when
126: * the object is set to `published` but the `publishDate` or `expiryDate` do not match.
127: *
128: * @return string
129: */
130: public function publishStatus()
131: {
132: $status = $this->publishStatus;
133: if (!$status || $status == 'published') {
134: $status = $this->publishDateStatus();
135: }
136: return $status;
137: }
138:
139: /**
140: * Get the "publish status" from the publish date / expiry date.
141: *
142: * - If no publish date is set, then it is assumed to be "always published." (or expired)
143: * - If no expiry date is set, then it is assumed to never expire.
144: *
145: * @return string
146: */
147: private function publishDateStatus()
148: {
149: $now = new DateTime();
150: $publish = $this->publishDate();
151: $expiry = $this->expiryDate();
152:
153: if (!$publish) {
154: if (!$expiry || $now < $expiry) {
155: return 'published';
156: } else {
157: return 'expired';
158: }
159: } else {
160: if ($now < $publish) {
161: return 'pending';
162: } else {
163: if (!$expiry || $now < $expiry) {
164: return 'published';
165: } else {
166: return 'expired';
167: }
168: }
169: }
170: }
171:
172: /**
173: * @return boolean
174: */
175: public function isPublished()
176: {
177: return ($this->publishStatus() == 'published');
178: }
179: }
180: