|  | 
|  | 1 | +<?php | 
|  | 2 | + | 
|  | 3 | +/** | 
|  | 4 | + * This file is part of the Nette Framework (https://nette.org) | 
|  | 5 | + * Copyright (c) 2004 David Grudl (https://davidgrudl.com) | 
|  | 6 | + */ | 
|  | 7 | + | 
|  | 8 | +namespace Nette\Caching\Storages; | 
|  | 9 | + | 
|  | 10 | +use Nette; | 
|  | 11 | +use Nette\Caching\Cache; | 
|  | 12 | + | 
|  | 13 | + | 
|  | 14 | +/** | 
|  | 15 | + * Memcached storage using memcached extension. | 
|  | 16 | + */ | 
|  | 17 | +class NewMemcachedStorage extends Nette\Object implements Nette\Caching\IStorage | 
|  | 18 | +{ | 
|  | 19 | +	/** @internal cache structure */ | 
|  | 20 | +	const META_CALLBACKS = 'callbacks', | 
|  | 21 | +		META_DATA = 'data', | 
|  | 22 | +		META_DELTA = 'delta'; | 
|  | 23 | + | 
|  | 24 | +	/** @var \Memcached */ | 
|  | 25 | +	private $memcached; | 
|  | 26 | + | 
|  | 27 | +	/** @var string */ | 
|  | 28 | +	private $prefix; | 
|  | 29 | + | 
|  | 30 | +	/** @var IJournal */ | 
|  | 31 | +	private $journal; | 
|  | 32 | + | 
|  | 33 | + | 
|  | 34 | +	/** | 
|  | 35 | +	 * Checks if Memcached extension is available. | 
|  | 36 | +	 * @return bool | 
|  | 37 | +	 */ | 
|  | 38 | +	public static function isAvailable() | 
|  | 39 | +	{ | 
|  | 40 | +		return extension_loaded('memcached'); | 
|  | 41 | +	} | 
|  | 42 | + | 
|  | 43 | + | 
|  | 44 | +	public function __construct($host = 'localhost', $port = 11211, $prefix = '', IJournal $journal = NULL) | 
|  | 45 | +	{ | 
|  | 46 | +		if (!static::isAvailable()) { | 
|  | 47 | +			throw new Nette\NotSupportedException("PHP extension 'memcached' is not loaded."); | 
|  | 48 | +		} | 
|  | 49 | + | 
|  | 50 | +		$this->prefix = $prefix; | 
|  | 51 | +		$this->journal = $journal; | 
|  | 52 | +		$this->memcached = new \Memcached; | 
|  | 53 | +		if ($host) { | 
|  | 54 | +			$this->addServer($host, $port); | 
|  | 55 | +		} | 
|  | 56 | +	} | 
|  | 57 | + | 
|  | 58 | + | 
|  | 59 | +	public function addServer($host = 'localhost', $port = 11211) | 
|  | 60 | +	{ | 
|  | 61 | +		if ($this->memcached->addServer($host, $port, 1) === FALSE) { | 
|  | 62 | +			$error = error_get_last(); | 
|  | 63 | +			throw new Nette\InvalidStateException("Memcached::addServer(): $error[message]."); | 
|  | 64 | +		} | 
|  | 65 | +	} | 
|  | 66 | + | 
|  | 67 | + | 
|  | 68 | +	/** | 
|  | 69 | +	 * @return \Memcached | 
|  | 70 | +	 */ | 
|  | 71 | +	public function getConnection() | 
|  | 72 | +	{ | 
|  | 73 | +		return $this->memcached; | 
|  | 74 | +	} | 
|  | 75 | + | 
|  | 76 | + | 
|  | 77 | +	/** | 
|  | 78 | +	 * Read from cache. | 
|  | 79 | +	 * @param  string key | 
|  | 80 | +	 * @return mixed|NULL | 
|  | 81 | +	 */ | 
|  | 82 | +	public function read($key) | 
|  | 83 | +	{ | 
|  | 84 | +		$key = urlencode($this->prefix . $key); | 
|  | 85 | +		$meta = $this->memcached->get($key); | 
|  | 86 | +		if (!$meta) { | 
|  | 87 | +			return NULL; | 
|  | 88 | +		} | 
|  | 89 | + | 
|  | 90 | +		// meta structure: | 
|  | 91 | +		// array( | 
|  | 92 | +		//     data => stored data | 
|  | 93 | +		//     delta => relative (sliding) expiration | 
|  | 94 | +		//     callbacks => array of callbacks (function, args) | 
|  | 95 | +		// ) | 
|  | 96 | + | 
|  | 97 | +		// verify dependencies | 
|  | 98 | +		if (!empty($meta[self::META_CALLBACKS]) && !Cache::checkCallbacks($meta[self::META_CALLBACKS])) { | 
|  | 99 | +			$this->memcached->delete($key, 0); | 
|  | 100 | +			return NULL; | 
|  | 101 | +		} | 
|  | 102 | + | 
|  | 103 | +		if (!empty($meta[self::META_DELTA])) { | 
|  | 104 | +			$this->memcached->replace($key, $meta, $meta[self::META_DELTA] + time()); | 
|  | 105 | +		} | 
|  | 106 | + | 
|  | 107 | +		return $meta[self::META_DATA]; | 
|  | 108 | +	} | 
|  | 109 | + | 
|  | 110 | + | 
|  | 111 | +	/** | 
|  | 112 | +	 * Prevents item reading and writing. Lock is released by write() or remove(). | 
|  | 113 | +	 * @param  string key | 
|  | 114 | +	 * @return void | 
|  | 115 | +	 */ | 
|  | 116 | +	public function lock($key) | 
|  | 117 | +	{ | 
|  | 118 | +	} | 
|  | 119 | + | 
|  | 120 | + | 
|  | 121 | +	/** | 
|  | 122 | +	 * Writes item into the cache. | 
|  | 123 | +	 * @param  string key | 
|  | 124 | +	 * @param  mixed  data | 
|  | 125 | +	 * @param  array  dependencies | 
|  | 126 | +	 * @return void | 
|  | 127 | +	 */ | 
|  | 128 | +	public function write($key, $data, array $dp) | 
|  | 129 | +	{ | 
|  | 130 | +		if (isset($dp[Cache::ITEMS])) { | 
|  | 131 | +			throw new Nette\NotSupportedException('Dependent items are not supported by MemcachedStorage.'); | 
|  | 132 | +		} | 
|  | 133 | + | 
|  | 134 | +		$key = urlencode($this->prefix . $key); | 
|  | 135 | +		$meta = [ | 
|  | 136 | +			self::META_DATA => $data, | 
|  | 137 | +		]; | 
|  | 138 | + | 
|  | 139 | +		$expire = 0; | 
|  | 140 | +		if (isset($dp[Cache::EXPIRATION])) { | 
|  | 141 | +			$expire = (int) $dp[Cache::EXPIRATION]; | 
|  | 142 | +			if (!empty($dp[Cache::SLIDING])) { | 
|  | 143 | +				$meta[self::META_DELTA] = $expire; // sliding time | 
|  | 144 | +			} | 
|  | 145 | +		} | 
|  | 146 | + | 
|  | 147 | +		if (isset($dp[Cache::CALLBACKS])) { | 
|  | 148 | +			$meta[self::META_CALLBACKS] = $dp[Cache::CALLBACKS]; | 
|  | 149 | +		} | 
|  | 150 | + | 
|  | 151 | +		if (isset($dp[Cache::TAGS]) || isset($dp[Cache::PRIORITY])) { | 
|  | 152 | +			if (!$this->journal) { | 
|  | 153 | +				throw new Nette\InvalidStateException('CacheJournal has not been provided.'); | 
|  | 154 | +			} | 
|  | 155 | +			$this->journal->write($key, $dp); | 
|  | 156 | +		} | 
|  | 157 | + | 
|  | 158 | +		$this->memcached->set($key, $meta, $expire); | 
|  | 159 | +	} | 
|  | 160 | + | 
|  | 161 | + | 
|  | 162 | +	/** | 
|  | 163 | +	 * Removes item from the cache. | 
|  | 164 | +	 * @param  string key | 
|  | 165 | +	 * @return void | 
|  | 166 | +	 */ | 
|  | 167 | +	public function remove($key) | 
|  | 168 | +	{ | 
|  | 169 | +		$this->memcached->delete(urlencode($this->prefix . $key), 0); | 
|  | 170 | +	} | 
|  | 171 | + | 
|  | 172 | + | 
|  | 173 | +	/** | 
|  | 174 | +	 * Removes items from the cache by conditions & garbage collector. | 
|  | 175 | +	 * @param  array  conditions | 
|  | 176 | +	 * @return void | 
|  | 177 | +	 */ | 
|  | 178 | +	public function clean(array $conditions) | 
|  | 179 | +	{ | 
|  | 180 | +		if (!empty($conditions[Cache::ALL])) { | 
|  | 181 | +			$this->memcached->flush(); | 
|  | 182 | + | 
|  | 183 | +		} elseif ($this->journal) { | 
|  | 184 | +			foreach ($this->journal->clean($conditions) as $entry) { | 
|  | 185 | +				$this->memcached->delete($entry, 0); | 
|  | 186 | +			} | 
|  | 187 | +		} | 
|  | 188 | +	} | 
|  | 189 | + | 
|  | 190 | +} | 
0 commit comments