1<?php
2// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
3//
4// All Rights Reserved. See copyright.txt for details and a complete list of authors.
5// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
6// $Id$
7
8class Tiki_PageCache
9{
10	private $cacheData = [];
11	private $key;
12	private $meta = null;
13	private $headerLibCopy = null;
14
15	public static function create()
16	{
17		return new self;
18	}
19
20	function disableForRegistered()
21	{
22		global $user;
23
24		if ($user) {
25			$this->cacheData = null;
26		}
27
28		return $this;
29	}
30
31	function onlyForGet()
32	{
33		if ($_SERVER['REQUEST_METHOD'] != 'GET') {
34			$this->cacheData = null;
35		}
36
37		return $this;
38	}
39
40	function requiresPreference($preference)
41	{
42		global $prefs;
43
44		if ($prefs[$preference] != 'y') {
45			$this->cacheData = null;
46		}
47
48		return $this;
49	}
50
51	function addKeys($array, $keys)
52	{
53		if (is_array($this->cacheData)) {
54			foreach ($keys as $k) {
55				if (! isset($this->cacheData[$k])) {
56					$this->cacheData[$k] = isset($array[$k]) ? $array[$k] : null;
57				}
58			}
59		}
60
61		return $this;
62	}
63
64	function addArray($array)
65	{
66		if (is_array($this->cacheData)) {
67			$this->cacheData = array_merge($this->cacheData, $array);
68		}
69
70		return $this;
71	}
72
73	function addValue($key, $value)
74	{
75		if (is_array($this->cacheData)) {
76			$this->cacheData[$key] = $value;
77		}
78
79		return $this;
80	}
81
82	function checkMeta($role, $data)
83	{
84		$this->meta = array_merge([ 'role' => $role ], $data);
85
86		return $this;
87	}
88
89	function applyCache()
90	{
91		if (is_array($this->cacheData)) {
92			$memcachelib = TikiLib::lib("memcache");
93
94			if (TikiLib::lib("memcache")->isEnabled()) {
95				$this->key = $memcachelib->buildKey($this->cacheData);
96
97				if ($this->meta) {
98					list($cachedOutput, $metaTime) = $memcachelib->getMulti(
99						[
100							$this->key,
101							$this->meta,
102						]
103					);
104
105					if ($cachedOutput && $metaTime && $metaTime > $cachedOutput['timestamp']) {
106						$cachedOutput = null;
107					}
108				} else {
109					$cachedOutput = $memcachelib->get($this->key);
110				}
111
112				if ($cachedOutput && $cachedOutput['output']) {
113					$headerlib = TikiLib::lib('header');
114					if (is_array($cachedOutput['jsfiles'])) {
115						foreach ($cachedOutput['jsfiles'] as $rank => $files) {
116							foreach ($files as $file) {
117								$skip_minify = isset($cachedOutput['skip_minify']) ? true : false;
118								$headerlib->add_jsfile_by_rank($file, $rank, $skip_minify);
119							}
120						}
121					}
122					if (is_array($cachedOutput['js'])) {
123						foreach ($cachedOutput['js'] as $rank => $js) {
124							foreach ($js as $j) {
125								$headerlib->add_js($j, $rank);
126							}
127						}
128					}
129					if (is_array($cachedOutput['jq_onready'])) {
130						foreach ($cachedOutput['jq_onready'] as $rank => $js) {
131							foreach ($js as $j) {
132								$headerlib->add_jq_onready($j, $rank);
133							}
134						}
135					}
136					if (is_array($cachedOutput['css'])) {
137						foreach ($cachedOutput['css'] as $rank => $css) {
138							foreach ($css as $c) {
139								$headerlib->add_css($c, $rank);
140							}
141						}
142					}
143					if (is_array($cachedOutput['cssfile'])) {
144						foreach ($cachedOutput['cssfile'] as $rank => $css) {
145							foreach ($css as $c) {
146								$headerlib->add_cssfile($c, $rank);
147							}
148						}
149					}
150
151
152					echo $cachedOutput['output'];
153					echo "\n<!-- memcache " . htmlspecialchars($this->key) . "-->";
154					exit;
155				}
156
157				// save state of headerlib
158				$this->headerLibCopy = unserialize(serialize(TikiLib::lib('header')));
159
160				// Start caching, automatically gather at destruction
161				ob_start();
162			}
163		}
164
165		return $this;
166	}
167
168	function cleanUp()
169	{
170		if ($this->key) {
171			$cachedOutput = [
172				'timestamp' => time(),
173				'output'    => ob_get_contents()
174			];
175
176			if ($this->headerLibCopy) {
177				$headerlib = TikiLib::lib('header');
178				$cachedOutput['jsfiles']    = array_diff($headerlib->jsfiles, $this->headerLibCopy->jsfiles);
179				$cachedOutput['skip_minify'] = array_diff($headerlib->skip_minify, $this->headerLibCopy->skip_minify);
180				$cachedOutput['jq_onready'] = array_diff($headerlib->jq_onready, $this->headerLibCopy->jq_onready);
181				$cachedOutput['js']         = array_diff($headerlib->js, $this->headerLibCopy->js);
182				$cachedOutput['css']        = array_diff($headerlib->css, $this->headerLibCopy->css);
183				$cachedOutput['cssfiles']   = array_diff($headerlib->cssfiles, $this->headerLibCopy->cssfiles);
184			}
185
186			if ($cachedOutput['output']) {
187				TikiLib::lib("memcache")->set($this->key, $cachedOutput);
188			}
189
190			ob_end_flush();
191		}
192
193		$this->cacheData = [];
194		$this->key = null;
195		$this->meta = null;
196	}
197
198	function invalidate()
199	{
200		if ($this->meta) {
201			TikiLib::lib("memcache")->set($this->meta, time());
202		}
203	}
204
205	function __destruct()
206	{
207		$this->cleanUp();
208	}
209}
210