├── README.md └── README ├── Cache ├── Cache.php ├── drivers │ ├── Cache_apc.php │ ├── Cache_dummy.php │ ├── Cache_file.php │ ├── Cache_memcached.php │ ├── Cache_redis.php │ ├── Cache_wincache.php │ └── index.html └── index.html ├── Calendar.php ├── Cart.php ├── Driver.php ├── Email.php ├── Encrypt.php ├── Encryption.php ├── Form_validation.php ├── Ftp.php ├── Image_lib.php ├── Javascript.php ├── Javascript ├── Jquery.php └── index.html ├── Migration.php ├── Pagination.php ├── Parser.php ├── Profiler.php ├── README.php ├── Session ├── Session.php ├── SessionHandlerInterface.php ├── Session_driver.php ├── drivers │ ├── Session_database_driver.php │ ├── Session_files_driver.php │ ├── Session_memcached_driver.php │ ├── Session_redis_driver.php │ └── index.html └── index.html ├── Table.php ├── Trackback.php ├── Typography.php ├── Unit_test.php ├── Upload.php ├── User_agent.php ├── Xmlrpc.php ├── Xmlrpcs.php ├── Zip.php └── index.html /README/Cache/Cache.php: -------------------------------------------------------------------------------- 1 | _adapter = $config['adapter']; 104 | isset($config['backup']) && $this->_backup_driver = $config['backup']; 105 | isset($config['key_prefix']) && $this->key_prefix = $config['key_prefix']; 106 | 107 | // If the specified adapter isn't available, check the backup. 108 | if ( ! $this->is_supported($this->_adapter)) 109 | { 110 | if ( ! $this->is_supported($this->_backup_driver)) 111 | { 112 | // Backup isn't supported either. Default to 'Dummy' driver. 113 | log_message('error', 'Cache adapter "'.$this->_adapter.'" and backup "'.$this->_backup_driver.'" are both unavailable. Cache is now using "Dummy" adapter.'); 114 | $this->_adapter = 'dummy'; 115 | } 116 | else 117 | { 118 | // Backup is supported. Set it to primary. 119 | log_message('debug', 'Cache adapter "'.$this->_adapter.'" is unavailable. Falling back to "'.$this->_backup_driver.'" backup adapter.'); 120 | $this->_adapter = $this->_backup_driver; 121 | } 122 | } 123 | } 124 | 125 | // ------------------------------------------------------------------------ 126 | 127 | /** 128 | * Get 129 | * 130 | * Look for a value in the cache. If it exists, return the data 131 | * if not, return FALSE 132 | * 133 | * @param string $id 134 | * @return mixed value matching $id or FALSE on failure 135 | */ 136 | public function get($id) 137 | { 138 | return $this->{$this->_adapter}->get($this->key_prefix.$id); 139 | } 140 | 141 | // ------------------------------------------------------------------------ 142 | 143 | /** 144 | * Cache Save 145 | * 146 | * @param string $id Cache ID 147 | * @param mixed $data Data to store 148 | * @param int $ttl Cache TTL (in seconds) 149 | * @param bool $raw Whether to store the raw value 150 | * @return bool TRUE on success, FALSE on failure 151 | */ 152 | public function save($id, $data, $ttl = 60, $raw = FALSE) 153 | { 154 | return $this->{$this->_adapter}->save($this->key_prefix.$id, $data, $ttl, $raw); 155 | } 156 | 157 | // ------------------------------------------------------------------------ 158 | 159 | /** 160 | * Delete from Cache 161 | * 162 | * @param string $id Cache ID 163 | * @return bool TRUE on success, FALSE on failure 164 | */ 165 | public function delete($id) 166 | { 167 | return $this->{$this->_adapter}->delete($this->key_prefix.$id); 168 | } 169 | 170 | // ------------------------------------------------------------------------ 171 | 172 | /** 173 | * Increment a raw value 174 | * 175 | * @param string $id Cache ID 176 | * @param int $offset Step/value to add 177 | * @return mixed New value on success or FALSE on failure 178 | */ 179 | public function increment($id, $offset = 1) 180 | { 181 | return $this->{$this->_adapter}->increment($this->key_prefix.$id, $offset); 182 | } 183 | 184 | // ------------------------------------------------------------------------ 185 | 186 | /** 187 | * Decrement a raw value 188 | * 189 | * @param string $id Cache ID 190 | * @param int $offset Step/value to reduce by 191 | * @return mixed New value on success or FALSE on failure 192 | */ 193 | public function decrement($id, $offset = 1) 194 | { 195 | return $this->{$this->_adapter}->decrement($this->key_prefix.$id, $offset); 196 | } 197 | 198 | // ------------------------------------------------------------------------ 199 | 200 | /** 201 | * Clean the cache 202 | * 203 | * @return bool TRUE on success, FALSE on failure 204 | */ 205 | public function clean() 206 | { 207 | return $this->{$this->_adapter}->clean(); 208 | } 209 | 210 | // ------------------------------------------------------------------------ 211 | 212 | /** 213 | * Cache Info 214 | * 215 | * @param string $type = 'user' user/filehits 216 | * @return mixed array containing cache info on success OR FALSE on failure 217 | */ 218 | public function cache_info($type = 'user') 219 | { 220 | return $this->{$this->_adapter}->cache_info($type); 221 | } 222 | 223 | // ------------------------------------------------------------------------ 224 | 225 | /** 226 | * Get Cache Metadata 227 | * 228 | * @param string $id key to get cache metadata on 229 | * @return mixed cache item metadata 230 | */ 231 | public function get_metadata($id) 232 | { 233 | return $this->{$this->_adapter}->get_metadata($this->key_prefix.$id); 234 | } 235 | 236 | // ------------------------------------------------------------------------ 237 | 238 | /** 239 | * Is the requested driver supported in this environment? 240 | * 241 | * @param string $driver The driver to test 242 | * @return array 243 | */ 244 | public function is_supported($driver) 245 | { 246 | static $support; 247 | 248 | if ( ! isset($support, $support[$driver])) 249 | { 250 | $support[$driver] = $this->{$driver}->is_supported(); 251 | } 252 | 253 | return $support[$driver]; 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /README/Cache/drivers/Cache_apc.php: -------------------------------------------------------------------------------- 1 | $time + $ttl, 185 | 'mtime' => $time, 186 | 'data' => unserialize($data) 187 | ); 188 | } 189 | 190 | // ------------------------------------------------------------------------ 191 | 192 | /** 193 | * is_supported() 194 | * 195 | * Check to see if APC is available on this system, bail if it isn't. 196 | * 197 | * @return bool 198 | */ 199 | public function is_supported() 200 | { 201 | if ( ! extension_loaded('apc') OR ! ini_get('apc.enabled')) 202 | { 203 | log_message('debug', 'The APC PHP extension must be loaded to use APC Cache.'); 204 | return FALSE; 205 | } 206 | 207 | return TRUE; 208 | } 209 | 210 | } 211 | -------------------------------------------------------------------------------- /README/Cache/drivers/Cache_dummy.php: -------------------------------------------------------------------------------- 1 | load->helper('file'); 67 | $path = $CI->config->item('cache_path'); 68 | $this->_cache_path = ($path === '') ? APPPATH.'cache/' : $path; 69 | } 70 | 71 | // ------------------------------------------------------------------------ 72 | 73 | /** 74 | * Fetch from cache 75 | * 76 | * @param string $id Cache ID 77 | * @return mixed Data on success, FALSE on failure 78 | */ 79 | public function get($id) 80 | { 81 | $data = $this->_get($id); 82 | return is_array($data) ? $data['data'] : FALSE; 83 | } 84 | 85 | // ------------------------------------------------------------------------ 86 | 87 | /** 88 | * Save into cache 89 | * 90 | * @param string $id Cache ID 91 | * @param mixed $data Data to store 92 | * @param int $ttl Time to live in seconds 93 | * @param bool $raw Whether to store the raw value (unused) 94 | * @return bool TRUE on success, FALSE on failure 95 | */ 96 | public function save($id, $data, $ttl = 60, $raw = FALSE) 97 | { 98 | $contents = array( 99 | 'time' => time(), 100 | 'ttl' => $ttl, 101 | 'data' => $data 102 | ); 103 | 104 | if (write_file($this->_cache_path.$id, serialize($contents))) 105 | { 106 | chmod($this->_cache_path.$id, 0640); 107 | return TRUE; 108 | } 109 | 110 | return FALSE; 111 | } 112 | 113 | // ------------------------------------------------------------------------ 114 | 115 | /** 116 | * Delete from Cache 117 | * 118 | * @param mixed unique identifier of item in cache 119 | * @return bool true on success/false on failure 120 | */ 121 | public function delete($id) 122 | { 123 | return file_exists($this->_cache_path.$id) ? unlink($this->_cache_path.$id) : FALSE; 124 | } 125 | 126 | // ------------------------------------------------------------------------ 127 | 128 | /** 129 | * Increment a raw value 130 | * 131 | * @param string $id Cache ID 132 | * @param int $offset Step/value to add 133 | * @return New value on success, FALSE on failure 134 | */ 135 | public function increment($id, $offset = 1) 136 | { 137 | $data = $this->_get($id); 138 | 139 | if ($data === FALSE) 140 | { 141 | $data = array('data' => 0, 'ttl' => 60); 142 | } 143 | elseif ( ! is_int($data['data'])) 144 | { 145 | return FALSE; 146 | } 147 | 148 | $new_value = $data['data'] + $offset; 149 | return $this->save($id, $new_value, $data['ttl']) 150 | ? $new_value 151 | : FALSE; 152 | } 153 | 154 | // ------------------------------------------------------------------------ 155 | 156 | /** 157 | * Decrement a raw value 158 | * 159 | * @param string $id Cache ID 160 | * @param int $offset Step/value to reduce by 161 | * @return New value on success, FALSE on failure 162 | */ 163 | public function decrement($id, $offset = 1) 164 | { 165 | $data = $this->_get($id); 166 | 167 | if ($data === FALSE) 168 | { 169 | $data = array('data' => 0, 'ttl' => 60); 170 | } 171 | elseif ( ! is_int($data['data'])) 172 | { 173 | return FALSE; 174 | } 175 | 176 | $new_value = $data['data'] - $offset; 177 | return $this->save($id, $new_value, $data['ttl']) 178 | ? $new_value 179 | : FALSE; 180 | } 181 | 182 | // ------------------------------------------------------------------------ 183 | 184 | /** 185 | * Clean the Cache 186 | * 187 | * @return bool false on failure/true on success 188 | */ 189 | public function clean() 190 | { 191 | return delete_files($this->_cache_path, FALSE, TRUE); 192 | } 193 | 194 | // ------------------------------------------------------------------------ 195 | 196 | /** 197 | * Cache Info 198 | * 199 | * Not supported by file-based caching 200 | * 201 | * @param string user/filehits 202 | * @return mixed FALSE 203 | */ 204 | public function cache_info($type = NULL) 205 | { 206 | return get_dir_file_info($this->_cache_path); 207 | } 208 | 209 | // ------------------------------------------------------------------------ 210 | 211 | /** 212 | * Get Cache Metadata 213 | * 214 | * @param mixed key to get cache metadata on 215 | * @return mixed FALSE on failure, array on success. 216 | */ 217 | public function get_metadata($id) 218 | { 219 | if ( ! file_exists($this->_cache_path.$id)) 220 | { 221 | return FALSE; 222 | } 223 | 224 | $data = unserialize(file_get_contents($this->_cache_path.$id)); 225 | 226 | if (is_array($data)) 227 | { 228 | $mtime = filemtime($this->_cache_path.$id); 229 | 230 | if ( ! isset($data['ttl'])) 231 | { 232 | return FALSE; 233 | } 234 | 235 | return array( 236 | 'expire' => $mtime + $data['ttl'], 237 | 'mtime' => $mtime 238 | ); 239 | } 240 | 241 | return FALSE; 242 | } 243 | 244 | // ------------------------------------------------------------------------ 245 | 246 | /** 247 | * Is supported 248 | * 249 | * In the file driver, check to see that the cache directory is indeed writable 250 | * 251 | * @return bool 252 | */ 253 | public function is_supported() 254 | { 255 | return is_really_writable($this->_cache_path); 256 | } 257 | 258 | // ------------------------------------------------------------------------ 259 | 260 | /** 261 | * Get all data 262 | * 263 | * Internal method to get all the relevant data about a cache item 264 | * 265 | * @param string $id Cache ID 266 | * @return mixed Data array on success or FALSE on failure 267 | */ 268 | protected function _get($id) 269 | { 270 | if ( ! is_file($this->_cache_path.$id)) 271 | { 272 | return FALSE; 273 | } 274 | 275 | $data = unserialize(file_get_contents($this->_cache_path.$id)); 276 | 277 | if ($data['ttl'] > 0 && time() > $data['time'] + $data['ttl']) 278 | { 279 | unlink($this->_cache_path.$id); 280 | return FALSE; 281 | } 282 | 283 | return $data; 284 | } 285 | 286 | } 287 | -------------------------------------------------------------------------------- /README/Cache/drivers/Cache_memcached.php: -------------------------------------------------------------------------------- 1 | array( 65 | 'host' => '127.0.0.1', 66 | 'port' => 11211, 67 | 'weight' => 1 68 | ) 69 | ); 70 | 71 | // ------------------------------------------------------------------------ 72 | 73 | /** 74 | * Class constructor 75 | * 76 | * Setup Memcache(d) 77 | * 78 | * @return void 79 | */ 80 | public function __construct() 81 | { 82 | // Try to load memcached server info from the config file. 83 | $CI =& get_instance(); 84 | $defaults = $this->_memcache_conf['default']; 85 | 86 | if ($CI->config->load('memcached', TRUE, TRUE)) 87 | { 88 | if (is_array($CI->config->config['memcached'])) 89 | { 90 | $this->_memcache_conf = array(); 91 | 92 | foreach ($CI->config->config['memcached'] as $name => $conf) 93 | { 94 | $this->_memcache_conf[$name] = $conf; 95 | } 96 | } 97 | } 98 | 99 | if (class_exists('Memcached', FALSE)) 100 | { 101 | $this->_memcached = new Memcached(); 102 | } 103 | elseif (class_exists('Memcache', FALSE)) 104 | { 105 | $this->_memcached = new Memcache(); 106 | } 107 | else 108 | { 109 | log_message('error', 'Cache: Failed to create Memcache(d) object; extension not loaded?'); 110 | } 111 | 112 | foreach ($this->_memcache_conf as $cache_server) 113 | { 114 | isset($cache_server['hostname']) OR $cache_server['hostname'] = $defaults['host']; 115 | isset($cache_server['port']) OR $cache_server['port'] = $defaults['port']; 116 | isset($cache_server['weight']) OR $cache_server['weight'] = $defaults['weight']; 117 | 118 | if (get_class($this->_memcached) === 'Memcache') 119 | { 120 | // Third parameter is persistance and defaults to TRUE. 121 | $this->_memcached->addServer( 122 | $cache_server['hostname'], 123 | $cache_server['port'], 124 | TRUE, 125 | $cache_server['weight'] 126 | ); 127 | } 128 | else 129 | { 130 | $this->_memcached->addServer( 131 | $cache_server['hostname'], 132 | $cache_server['port'], 133 | $cache_server['weight'] 134 | ); 135 | } 136 | } 137 | } 138 | 139 | // ------------------------------------------------------------------------ 140 | 141 | /** 142 | * Fetch from cache 143 | * 144 | * @param string $id Cache ID 145 | * @return mixed Data on success, FALSE on failure 146 | */ 147 | public function get($id) 148 | { 149 | $data = $this->_memcached->get($id); 150 | 151 | return is_array($data) ? $data[0] : $data; 152 | } 153 | 154 | // ------------------------------------------------------------------------ 155 | 156 | /** 157 | * Save 158 | * 159 | * @param string $id Cache ID 160 | * @param mixed $data Data being cached 161 | * @param int $ttl Time to live 162 | * @param bool $raw Whether to store the raw value 163 | * @return bool TRUE on success, FALSE on failure 164 | */ 165 | public function save($id, $data, $ttl = 60, $raw = FALSE) 166 | { 167 | if ($raw !== TRUE) 168 | { 169 | $data = array($data, time(), $ttl); 170 | } 171 | 172 | if (get_class($this->_memcached) === 'Memcached') 173 | { 174 | return $this->_memcached->set($id, $data, $ttl); 175 | } 176 | elseif (get_class($this->_memcached) === 'Memcache') 177 | { 178 | return $this->_memcached->set($id, $data, 0, $ttl); 179 | } 180 | 181 | return FALSE; 182 | } 183 | 184 | // ------------------------------------------------------------------------ 185 | 186 | /** 187 | * Delete from Cache 188 | * 189 | * @param mixed key to be deleted. 190 | * @return bool true on success, false on failure 191 | */ 192 | public function delete($id) 193 | { 194 | return $this->_memcached->delete($id); 195 | } 196 | 197 | // ------------------------------------------------------------------------ 198 | 199 | /** 200 | * Increment a raw value 201 | * 202 | * @param string $id Cache ID 203 | * @param int $offset Step/value to add 204 | * @return mixed New value on success or FALSE on failure 205 | */ 206 | public function increment($id, $offset = 1) 207 | { 208 | return $this->_memcached->increment($id, $offset); 209 | } 210 | 211 | // ------------------------------------------------------------------------ 212 | 213 | /** 214 | * Decrement a raw value 215 | * 216 | * @param string $id Cache ID 217 | * @param int $offset Step/value to reduce by 218 | * @return mixed New value on success or FALSE on failure 219 | */ 220 | public function decrement($id, $offset = 1) 221 | { 222 | return $this->_memcached->decrement($id, $offset); 223 | } 224 | 225 | // ------------------------------------------------------------------------ 226 | 227 | /** 228 | * Clean the Cache 229 | * 230 | * @return bool false on failure/true on success 231 | */ 232 | public function clean() 233 | { 234 | return $this->_memcached->flush(); 235 | } 236 | 237 | // ------------------------------------------------------------------------ 238 | 239 | /** 240 | * Cache Info 241 | * 242 | * @return mixed array on success, false on failure 243 | */ 244 | public function cache_info() 245 | { 246 | return $this->_memcached->getStats(); 247 | } 248 | 249 | // ------------------------------------------------------------------------ 250 | 251 | /** 252 | * Get Cache Metadata 253 | * 254 | * @param mixed key to get cache metadata on 255 | * @return mixed FALSE on failure, array on success. 256 | */ 257 | public function get_metadata($id) 258 | { 259 | $stored = $this->_memcached->get($id); 260 | 261 | if (count($stored) !== 3) 262 | { 263 | return FALSE; 264 | } 265 | 266 | list($data, $time, $ttl) = $stored; 267 | 268 | return array( 269 | 'expire' => $time + $ttl, 270 | 'mtime' => $time, 271 | 'data' => $data 272 | ); 273 | } 274 | 275 | // ------------------------------------------------------------------------ 276 | 277 | /** 278 | * Is supported 279 | * 280 | * Returns FALSE if memcached is not supported on the system. 281 | * If it is, we setup the memcached object & return TRUE 282 | * 283 | * @return bool 284 | */ 285 | public function is_supported() 286 | { 287 | return (extension_loaded('memcached') OR extension_loaded('memcache')); 288 | } 289 | } 290 | -------------------------------------------------------------------------------- /README/Cache/drivers/Cache_redis.php: -------------------------------------------------------------------------------- 1 | 47 | * @link 48 | */ 49 | class CI_Cache_redis extends CI_Driver 50 | { 51 | /** 52 | * Default config 53 | * 54 | * @static 55 | * @var array 56 | */ 57 | protected static $_default_config = array( 58 | 'socket_type' => 'tcp', 59 | 'host' => '127.0.0.1', 60 | 'password' => NULL, 61 | 'port' => 6379, 62 | 'timeout' => 0 63 | ); 64 | 65 | /** 66 | * Redis connection 67 | * 68 | * @var Redis 69 | */ 70 | protected $_redis; 71 | 72 | /** 73 | * An internal cache for storing keys of serialized values. 74 | * 75 | * @var array 76 | */ 77 | protected $_serialized = array(); 78 | 79 | // ------------------------------------------------------------------------ 80 | 81 | /** 82 | * Class constructor 83 | * 84 | * Setup Redis 85 | * 86 | * Loads Redis config file if present. Will halt execution 87 | * if a Redis connection can't be established. 88 | * 89 | * @return void 90 | * @see Redis::connect() 91 | */ 92 | public function __construct() 93 | { 94 | $config = array(); 95 | $CI =& get_instance(); 96 | 97 | if ($CI->config->load('redis', TRUE, TRUE)) 98 | { 99 | $config = $CI->config->item('redis'); 100 | } 101 | 102 | $config = array_merge(self::$_default_config, $config); 103 | $this->_redis = new Redis(); 104 | 105 | try 106 | { 107 | if ($config['socket_type'] === 'unix') 108 | { 109 | $success = $this->_redis->connect($config['socket']); 110 | } 111 | else // tcp socket 112 | { 113 | $success = $this->_redis->connect($config['host'], $config['port'], $config['timeout']); 114 | } 115 | 116 | if ( ! $success) 117 | { 118 | log_message('error', 'Cache: Redis connection failed. Check your configuration.'); 119 | } 120 | 121 | if (isset($config['password']) && ! $this->_redis->auth($config['password'])) 122 | { 123 | log_message('error', 'Cache: Redis authentication failed.'); 124 | } 125 | } 126 | catch (RedisException $e) 127 | { 128 | log_message('error', 'Cache: Redis connection refused ('.$e->getMessage().')'); 129 | } 130 | 131 | // Initialize the index of serialized values. 132 | $serialized = $this->_redis->sMembers('_ci_redis_serialized'); 133 | empty($serialized) OR $this->_serialized = array_flip($serialized); 134 | } 135 | 136 | // ------------------------------------------------------------------------ 137 | 138 | /** 139 | * Get cache 140 | * 141 | * @param string Cache ID 142 | * @return mixed 143 | */ 144 | public function get($key) 145 | { 146 | $value = $this->_redis->get($key); 147 | 148 | if ($value !== FALSE && isset($this->_serialized[$key])) 149 | { 150 | return unserialize($value); 151 | } 152 | 153 | return $value; 154 | } 155 | 156 | // ------------------------------------------------------------------------ 157 | 158 | /** 159 | * Save cache 160 | * 161 | * @param string $id Cache ID 162 | * @param mixed $data Data to save 163 | * @param int $ttl Time to live in seconds 164 | * @param bool $raw Whether to store the raw value (unused) 165 | * @return bool TRUE on success, FALSE on failure 166 | */ 167 | public function save($id, $data, $ttl = 60, $raw = FALSE) 168 | { 169 | if (is_array($data) OR is_object($data)) 170 | { 171 | if ( ! $this->_redis->sIsMember('_ci_redis_serialized', $id) && ! $this->_redis->sAdd('_ci_redis_serialized', $id)) 172 | { 173 | return FALSE; 174 | } 175 | 176 | isset($this->_serialized[$id]) OR $this->_serialized[$id] = TRUE; 177 | $data = serialize($data); 178 | } 179 | elseif (isset($this->_serialized[$id])) 180 | { 181 | $this->_serialized[$id] = NULL; 182 | $this->_redis->sRemove('_ci_redis_serialized', $id); 183 | } 184 | 185 | return $this->_redis->set($id, $data, $ttl); 186 | } 187 | 188 | // ------------------------------------------------------------------------ 189 | 190 | /** 191 | * Delete from cache 192 | * 193 | * @param string Cache key 194 | * @return bool 195 | */ 196 | public function delete($key) 197 | { 198 | if ($this->_redis->delete($key) !== 1) 199 | { 200 | return FALSE; 201 | } 202 | 203 | if (isset($this->_serialized[$key])) 204 | { 205 | $this->_serialized[$key] = NULL; 206 | $this->_redis->sRemove('_ci_redis_serialized', $key); 207 | } 208 | 209 | return TRUE; 210 | } 211 | 212 | // ------------------------------------------------------------------------ 213 | 214 | /** 215 | * Increment a raw value 216 | * 217 | * @param string $id Cache ID 218 | * @param int $offset Step/value to add 219 | * @return mixed New value on success or FALSE on failure 220 | */ 221 | public function increment($id, $offset = 1) 222 | { 223 | return $this->_redis->incr($id, $offset); 224 | } 225 | 226 | // ------------------------------------------------------------------------ 227 | 228 | /** 229 | * Decrement a raw value 230 | * 231 | * @param string $id Cache ID 232 | * @param int $offset Step/value to reduce by 233 | * @return mixed New value on success or FALSE on failure 234 | */ 235 | public function decrement($id, $offset = 1) 236 | { 237 | return $this->_redis->decr($id, $offset); 238 | } 239 | 240 | // ------------------------------------------------------------------------ 241 | 242 | /** 243 | * Clean cache 244 | * 245 | * @return bool 246 | * @see Redis::flushDB() 247 | */ 248 | public function clean() 249 | { 250 | return $this->_redis->flushDB(); 251 | } 252 | 253 | // ------------------------------------------------------------------------ 254 | 255 | /** 256 | * Get cache driver info 257 | * 258 | * @param string Not supported in Redis. 259 | * Only included in order to offer a 260 | * consistent cache API. 261 | * @return array 262 | * @see Redis::info() 263 | */ 264 | public function cache_info($type = NULL) 265 | { 266 | return $this->_redis->info(); 267 | } 268 | 269 | // ------------------------------------------------------------------------ 270 | 271 | /** 272 | * Get cache metadata 273 | * 274 | * @param string Cache key 275 | * @return array 276 | */ 277 | public function get_metadata($key) 278 | { 279 | $value = $this->get($key); 280 | 281 | if ($value !== FALSE) 282 | { 283 | return array( 284 | 'expire' => time() + $this->_redis->ttl($key), 285 | 'data' => $value 286 | ); 287 | } 288 | 289 | return FALSE; 290 | } 291 | 292 | // ------------------------------------------------------------------------ 293 | 294 | /** 295 | * Check if Redis driver is supported 296 | * 297 | * @return bool 298 | */ 299 | public function is_supported() 300 | { 301 | return extension_loaded('redis'); 302 | } 303 | 304 | // ------------------------------------------------------------------------ 305 | 306 | /** 307 | * Class destructor 308 | * 309 | * Closes the connection to Redis if present. 310 | * 311 | * @return void 312 | */ 313 | public function __destruct() 314 | { 315 | if ($this->_redis) 316 | { 317 | $this->_redis->close(); 318 | } 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /README/Cache/drivers/Cache_wincache.php: -------------------------------------------------------------------------------- 1 | $ttl - $age, 177 | 'hitcount' => $hitcount, 178 | 'age' => $age, 179 | 'ttl' => $ttl 180 | ); 181 | } 182 | 183 | return FALSE; 184 | } 185 | 186 | // ------------------------------------------------------------------------ 187 | 188 | /** 189 | * is_supported() 190 | * 191 | * Check to see if WinCache is available on this system, bail if it isn't. 192 | * 193 | * @return bool 194 | */ 195 | public function is_supported() 196 | { 197 | if ( ! extension_loaded('wincache') OR ! ini_get('wincache.ucenabled')) 198 | { 199 | log_message('debug', 'The Wincache PHP extension must be loaded to use Wincache Cache.'); 200 | return FALSE; 201 | } 202 | 203 | return TRUE; 204 | } 205 | 206 | } 207 | -------------------------------------------------------------------------------- /README/Cache/drivers/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |Directory access is forbidden.
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /README/Cache/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |Directory access is forbidden.
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /README/Driver.php: -------------------------------------------------------------------------------- 1 | load_driver($child); 81 | } 82 | 83 | /** 84 | * Load driver 85 | * 86 | * Separate load_driver call to support explicit driver load by library or user 87 | * 88 | * @param string Driver name (w/o parent prefix) 89 | * @return object Child class 90 | */ 91 | public function load_driver($child) 92 | { 93 | // Get CodeIgniter instance and subclass prefix 94 | $prefix = config_item('subclass_prefix'); 95 | 96 | if ( ! isset($this->lib_name)) 97 | { 98 | // Get library name without any prefix 99 | $this->lib_name = str_replace(array('CI_', $prefix), '', get_class($this)); 100 | } 101 | 102 | // The child will be prefixed with the parent lib 103 | $child_name = $this->lib_name.'_'.$child; 104 | 105 | // See if requested child is a valid driver 106 | if ( ! in_array($child, $this->valid_drivers)) 107 | { 108 | // The requested driver isn't valid! 109 | $msg = 'Invalid driver requested: '.$child_name; 110 | log_message('error', $msg); 111 | show_error($msg); 112 | } 113 | 114 | // Get package paths and filename case variations to search 115 | $CI = get_instance(); 116 | $paths = $CI->load->get_package_paths(TRUE); 117 | 118 | // Is there an extension? 119 | $class_name = $prefix.$child_name; 120 | $found = class_exists($class_name, FALSE); 121 | if ( ! $found) 122 | { 123 | // Check for subclass file 124 | foreach ($paths as $path) 125 | { 126 | // Does the file exist? 127 | $file = $path.'libraries/'.$this->lib_name.'/drivers/'.$prefix.$child_name.'.php'; 128 | if (file_exists($file)) 129 | { 130 | // Yes - require base class from BASEPATH 131 | $basepath = BASEPATH.'libraries/'.$this->lib_name.'/drivers/'.$child_name.'.php'; 132 | if ( ! file_exists($basepath)) 133 | { 134 | $msg = 'Unable to load the requested class: CI_'.$child_name; 135 | log_message('error', $msg); 136 | show_error($msg); 137 | } 138 | 139 | // Include both sources and mark found 140 | include_once($basepath); 141 | include_once($file); 142 | $found = TRUE; 143 | break; 144 | } 145 | } 146 | } 147 | 148 | // Do we need to search for the class? 149 | if ( ! $found) 150 | { 151 | // Use standard class name 152 | $class_name = 'CI_'.$child_name; 153 | if ( ! class_exists($class_name, FALSE)) 154 | { 155 | // Check package paths 156 | foreach ($paths as $path) 157 | { 158 | // Does the file exist? 159 | $file = $path.'libraries/'.$this->lib_name.'/drivers/'.$child_name.'.php'; 160 | if (file_exists($file)) 161 | { 162 | // Include source 163 | include_once($file); 164 | break; 165 | } 166 | } 167 | } 168 | } 169 | 170 | // Did we finally find the class? 171 | if ( ! class_exists($class_name, FALSE)) 172 | { 173 | if (class_exists($child_name, FALSE)) 174 | { 175 | $class_name = $child_name; 176 | } 177 | else 178 | { 179 | $msg = 'Unable to load the requested driver: '.$class_name; 180 | log_message('error', $msg); 181 | show_error($msg); 182 | } 183 | } 184 | 185 | // Instantiate, decorate and add child 186 | $obj = new $class_name(); 187 | $obj->decorate($this); 188 | $this->$child = $obj; 189 | return $this->$child; 190 | } 191 | 192 | } 193 | 194 | // -------------------------------------------------------------------------- 195 | 196 | /** 197 | * CodeIgniter Driver Class 198 | * 199 | * This class enables you to create drivers for a Library based on the Driver Library. 200 | * It handles the drivers' access to the parent library 201 | * 202 | * @package CodeIgniter 203 | * @subpackage Libraries 204 | * @category Libraries 205 | * @author EllisLab Dev Team 206 | * @link 207 | */ 208 | class CI_Driver { 209 | 210 | /** 211 | * Instance of the parent class 212 | * 213 | * @var object 214 | */ 215 | protected $_parent; 216 | 217 | /** 218 | * List of methods in the parent class 219 | * 220 | * @var array 221 | */ 222 | protected $_methods = array(); 223 | 224 | /** 225 | * List of properties in the parent class 226 | * 227 | * @var array 228 | */ 229 | protected $_properties = array(); 230 | 231 | /** 232 | * Array of methods and properties for the parent class(es) 233 | * 234 | * @static 235 | * @var array 236 | */ 237 | protected static $_reflections = array(); 238 | 239 | /** 240 | * Decorate 241 | * 242 | * Decorates the child with the parent driver lib's methods and properties 243 | * 244 | * @param object 245 | * @return void 246 | */ 247 | public function decorate($parent) 248 | { 249 | $this->_parent = $parent; 250 | 251 | // Lock down attributes to what is defined in the class 252 | // and speed up references in magic methods 253 | 254 | $class_name = get_class($parent); 255 | 256 | if ( ! isset(self::$_reflections[$class_name])) 257 | { 258 | $r = new ReflectionObject($parent); 259 | 260 | foreach ($r->getMethods() as $method) 261 | { 262 | if ($method->isPublic()) 263 | { 264 | $this->_methods[] = $method->getName(); 265 | } 266 | } 267 | 268 | foreach ($r->getProperties() as $prop) 269 | { 270 | if ($prop->isPublic()) 271 | { 272 | $this->_properties[] = $prop->getName(); 273 | } 274 | } 275 | 276 | self::$_reflections[$class_name] = array($this->_methods, $this->_properties); 277 | } 278 | else 279 | { 280 | list($this->_methods, $this->_properties) = self::$_reflections[$class_name]; 281 | } 282 | } 283 | 284 | // -------------------------------------------------------------------- 285 | 286 | /** 287 | * __call magic method 288 | * 289 | * Handles access to the parent driver library's methods 290 | * 291 | * @param string 292 | * @param array 293 | * @return mixed 294 | */ 295 | public function __call($method, $args = array()) 296 | { 297 | if (in_array($method, $this->_methods)) 298 | { 299 | return call_user_func_array(array($this->_parent, $method), $args); 300 | } 301 | 302 | throw new BadMethodCallException('No such method: '.$method.'()'); 303 | } 304 | 305 | // -------------------------------------------------------------------- 306 | 307 | /** 308 | * __get magic method 309 | * 310 | * Handles reading of the parent driver library's properties 311 | * 312 | * @param string 313 | * @return mixed 314 | */ 315 | public function __get($var) 316 | { 317 | if (in_array($var, $this->_properties)) 318 | { 319 | return $this->_parent->$var; 320 | } 321 | } 322 | 323 | // -------------------------------------------------------------------- 324 | 325 | /** 326 | * __set magic method 327 | * 328 | * Handles writing to the parent driver library's properties 329 | * 330 | * @param string 331 | * @param array 332 | * @return mixed 333 | */ 334 | public function __set($var, $val) 335 | { 336 | if (in_array($var, $this->_properties)) 337 | { 338 | $this->_parent->$var = $val; 339 | } 340 | } 341 | 342 | } 343 | -------------------------------------------------------------------------------- /README/Encrypt.php: -------------------------------------------------------------------------------- 1 | _mcrypt_exists = function_exists('mcrypt_encrypt')) === FALSE) 96 | { 97 | show_error('The Encrypt library requires the Mcrypt extension.'); 98 | } 99 | 100 | log_message('info', 'Encrypt Class Initialized'); 101 | } 102 | 103 | // -------------------------------------------------------------------- 104 | 105 | /** 106 | * Fetch the encryption key 107 | * 108 | * Returns it as MD5 in order to have an exact-length 128 bit key. 109 | * Mcrypt is sensitive to keys that are not the correct length 110 | * 111 | * @param string 112 | * @return string 113 | */ 114 | public function get_key($key = '') 115 | { 116 | if ($key === '') 117 | { 118 | if ($this->encryption_key !== '') 119 | { 120 | return $this->encryption_key; 121 | } 122 | 123 | $key = config_item('encryption_key'); 124 | 125 | if ( ! strlen($key)) 126 | { 127 | show_error('In order to use the encryption class requires that you set an encryption key in your config file.'); 128 | } 129 | } 130 | 131 | return md5($key); 132 | } 133 | 134 | // -------------------------------------------------------------------- 135 | 136 | /** 137 | * Set the encryption key 138 | * 139 | * @param string 140 | * @return CI_Encrypt 141 | */ 142 | public function set_key($key = '') 143 | { 144 | $this->encryption_key = $key; 145 | return $this; 146 | } 147 | 148 | // -------------------------------------------------------------------- 149 | 150 | /** 151 | * Encode 152 | * 153 | * Encodes the message string using bitwise XOR encoding. 154 | * The key is combined with a random hash, and then it 155 | * too gets converted using XOR. The whole thing is then run 156 | * through mcrypt using the randomized key. The end result 157 | * is a double-encrypted message string that is randomized 158 | * with each call to this function, even if the supplied 159 | * message and key are the same. 160 | * 161 | * @param string the string to encode 162 | * @param string the key 163 | * @return string 164 | */ 165 | public function encode($string, $key = '') 166 | { 167 | return base64_encode($this->mcrypt_encode($string, $this->get_key($key))); 168 | } 169 | 170 | // -------------------------------------------------------------------- 171 | 172 | /** 173 | * Decode 174 | * 175 | * Reverses the above process 176 | * 177 | * @param string 178 | * @param string 179 | * @return string 180 | */ 181 | public function decode($string, $key = '') 182 | { 183 | if (preg_match('/[^a-zA-Z0-9\/\+=]/', $string) OR base64_encode(base64_decode($string)) !== $string) 184 | { 185 | return FALSE; 186 | } 187 | 188 | return $this->mcrypt_decode(base64_decode($string), $this->get_key($key)); 189 | } 190 | 191 | // -------------------------------------------------------------------- 192 | 193 | /** 194 | * Encode from Legacy 195 | * 196 | * Takes an encoded string from the original Encryption class algorithms and 197 | * returns a newly encoded string using the improved method added in 2.0.0 198 | * This allows for backwards compatibility and a method to transition to the 199 | * new encryption algorithms. 200 | * 201 | * For more details, see http://codeigniter.com/user_guide/installation/upgrade_200.html#encryption 202 | * 203 | * @param string 204 | * @param int (mcrypt mode constant) 205 | * @param string 206 | * @return string 207 | */ 208 | public function encode_from_legacy($string, $legacy_mode = MCRYPT_MODE_ECB, $key = '') 209 | { 210 | if (preg_match('/[^a-zA-Z0-9\/\+=]/', $string)) 211 | { 212 | return FALSE; 213 | } 214 | 215 | // decode it first 216 | // set mode temporarily to what it was when string was encoded with the legacy 217 | // algorithm - typically MCRYPT_MODE_ECB 218 | $current_mode = $this->_get_mode(); 219 | $this->set_mode($legacy_mode); 220 | 221 | $key = $this->get_key($key); 222 | $dec = base64_decode($string); 223 | if (($dec = $this->mcrypt_decode($dec, $key)) === FALSE) 224 | { 225 | $this->set_mode($current_mode); 226 | return FALSE; 227 | } 228 | 229 | $dec = $this->_xor_decode($dec, $key); 230 | 231 | // set the mcrypt mode back to what it should be, typically MCRYPT_MODE_CBC 232 | $this->set_mode($current_mode); 233 | 234 | // and re-encode 235 | return base64_encode($this->mcrypt_encode($dec, $key)); 236 | } 237 | 238 | // -------------------------------------------------------------------- 239 | 240 | /** 241 | * XOR Decode 242 | * 243 | * Takes an encoded string and key as input and generates the 244 | * plain-text original message 245 | * 246 | * @param string 247 | * @param string 248 | * @return string 249 | */ 250 | protected function _xor_decode($string, $key) 251 | { 252 | $string = $this->_xor_merge($string, $key); 253 | 254 | $dec = ''; 255 | for ($i = 0, $l = strlen($string); $i < $l; $i++) 256 | { 257 | $dec .= ($string[$i++] ^ $string[$i]); 258 | } 259 | 260 | return $dec; 261 | } 262 | 263 | // -------------------------------------------------------------------- 264 | 265 | /** 266 | * XOR key + string Combiner 267 | * 268 | * Takes a string and key as input and computes the difference using XOR 269 | * 270 | * @param string 271 | * @param string 272 | * @return string 273 | */ 274 | protected function _xor_merge($string, $key) 275 | { 276 | $hash = $this->hash($key); 277 | $str = ''; 278 | for ($i = 0, $ls = strlen($string), $lh = strlen($hash); $i < $ls; $i++) 279 | { 280 | $str .= $string[$i] ^ $hash[($i % $lh)]; 281 | } 282 | 283 | return $str; 284 | } 285 | 286 | // -------------------------------------------------------------------- 287 | 288 | /** 289 | * Encrypt using Mcrypt 290 | * 291 | * @param string 292 | * @param string 293 | * @return string 294 | */ 295 | public function mcrypt_encode($data, $key) 296 | { 297 | $init_size = mcrypt_get_iv_size($this->_get_cipher(), $this->_get_mode()); 298 | $init_vect = mcrypt_create_iv($init_size, MCRYPT_RAND); 299 | return $this->_add_cipher_noise($init_vect.mcrypt_encrypt($this->_get_cipher(), $key, $data, $this->_get_mode(), $init_vect), $key); 300 | } 301 | 302 | // -------------------------------------------------------------------- 303 | 304 | /** 305 | * Decrypt using Mcrypt 306 | * 307 | * @param string 308 | * @param string 309 | * @return string 310 | */ 311 | public function mcrypt_decode($data, $key) 312 | { 313 | $data = $this->_remove_cipher_noise($data, $key); 314 | $init_size = mcrypt_get_iv_size($this->_get_cipher(), $this->_get_mode()); 315 | 316 | if ($init_size > strlen($data)) 317 | { 318 | return FALSE; 319 | } 320 | 321 | $init_vect = substr($data, 0, $init_size); 322 | $data = substr($data, $init_size); 323 | return rtrim(mcrypt_decrypt($this->_get_cipher(), $key, $data, $this->_get_mode(), $init_vect), "\0"); 324 | } 325 | 326 | // -------------------------------------------------------------------- 327 | 328 | /** 329 | * Adds permuted noise to the IV + encrypted data to protect 330 | * against Man-in-the-middle attacks on CBC mode ciphers 331 | * http://www.ciphersbyritter.com/GLOSSARY.HTM#IV 332 | * 333 | * @param string 334 | * @param string 335 | * @return string 336 | */ 337 | protected function _add_cipher_noise($data, $key) 338 | { 339 | $key = $this->hash($key); 340 | $str = ''; 341 | 342 | for ($i = 0, $j = 0, $ld = strlen($data), $lk = strlen($key); $i < $ld; ++$i, ++$j) 343 | { 344 | if ($j >= $lk) 345 | { 346 | $j = 0; 347 | } 348 | 349 | $str .= chr((ord($data[$i]) + ord($key[$j])) % 256); 350 | } 351 | 352 | return $str; 353 | } 354 | 355 | // -------------------------------------------------------------------- 356 | 357 | /** 358 | * Removes permuted noise from the IV + encrypted data, reversing 359 | * _add_cipher_noise() 360 | * 361 | * Function description 362 | * 363 | * @param string $data 364 | * @param string $key 365 | * @return string 366 | */ 367 | protected function _remove_cipher_noise($data, $key) 368 | { 369 | $key = $this->hash($key); 370 | $str = ''; 371 | 372 | for ($i = 0, $j = 0, $ld = strlen($data), $lk = strlen($key); $i < $ld; ++$i, ++$j) 373 | { 374 | if ($j >= $lk) 375 | { 376 | $j = 0; 377 | } 378 | 379 | $temp = ord($data[$i]) - ord($key[$j]); 380 | 381 | if ($temp < 0) 382 | { 383 | $temp += 256; 384 | } 385 | 386 | $str .= chr($temp); 387 | } 388 | 389 | return $str; 390 | } 391 | 392 | // -------------------------------------------------------------------- 393 | 394 | /** 395 | * Set the Mcrypt Cipher 396 | * 397 | * @param int 398 | * @return CI_Encrypt 399 | */ 400 | public function set_cipher($cipher) 401 | { 402 | $this->_mcrypt_cipher = $cipher; 403 | return $this; 404 | } 405 | 406 | // -------------------------------------------------------------------- 407 | 408 | /** 409 | * Set the Mcrypt Mode 410 | * 411 | * @param int 412 | * @return CI_Encrypt 413 | */ 414 | public function set_mode($mode) 415 | { 416 | $this->_mcrypt_mode = $mode; 417 | return $this; 418 | } 419 | 420 | // -------------------------------------------------------------------- 421 | 422 | /** 423 | * Get Mcrypt cipher Value 424 | * 425 | * @return int 426 | */ 427 | protected function _get_cipher() 428 | { 429 | if ($this->_mcrypt_cipher === NULL) 430 | { 431 | return $this->_mcrypt_cipher = MCRYPT_RIJNDAEL_256; 432 | } 433 | 434 | return $this->_mcrypt_cipher; 435 | } 436 | 437 | // -------------------------------------------------------------------- 438 | 439 | /** 440 | * Get Mcrypt Mode Value 441 | * 442 | * @return int 443 | */ 444 | protected function _get_mode() 445 | { 446 | if ($this->_mcrypt_mode === NULL) 447 | { 448 | return $this->_mcrypt_mode = MCRYPT_MODE_CBC; 449 | } 450 | 451 | return $this->_mcrypt_mode; 452 | } 453 | 454 | // -------------------------------------------------------------------- 455 | 456 | /** 457 | * Set the Hash type 458 | * 459 | * @param string 460 | * @return void 461 | */ 462 | public function set_hash($type = 'sha1') 463 | { 464 | $this->_hash_type = in_array($type, hash_algos()) ? $type : 'sha1'; 465 | } 466 | 467 | // -------------------------------------------------------------------- 468 | 469 | /** 470 | * Hash encode a string 471 | * 472 | * @param string 473 | * @return string 474 | */ 475 | public function hash($str) 476 | { 477 | return hash($this->_hash_type, $str); 478 | } 479 | 480 | } 481 | -------------------------------------------------------------------------------- /README/Javascript/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |Directory access is forbidden.
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /README/Migration.php: -------------------------------------------------------------------------------- 1 | $val) 125 | { 126 | $this->{'_'.$key} = $val; 127 | } 128 | 129 | log_message('info', 'Migrations Class Initialized'); 130 | 131 | // Are they trying to use migrations while it is disabled? 132 | if ($this->_migration_enabled !== TRUE) 133 | { 134 | show_error('Migrations has been loaded but is disabled or set up incorrectly.'); 135 | } 136 | 137 | // If not set, set it 138 | $this->_migration_path !== '' OR $this->_migration_path = APPPATH.'migrations/'; 139 | 140 | // Add trailing slash if not set 141 | $this->_migration_path = rtrim($this->_migration_path, '/').'/'; 142 | 143 | // Load migration language 144 | $this->lang->load('migration'); 145 | 146 | // They'll probably be using dbforge 147 | $this->load->dbforge(); 148 | 149 | // Make sure the migration table name was set. 150 | if (empty($this->_migration_table)) 151 | { 152 | show_error('Migrations configuration file (migration.php) must have "migration_table" set.'); 153 | } 154 | 155 | // Migration basename regex 156 | $this->_migration_regex = ($this->_migration_type === 'timestamp') 157 | ? '/^\d{14}_(\w+)$/' 158 | : '/^\d{3}_(\w+)$/'; 159 | 160 | // Make sure a valid migration numbering type was set. 161 | if ( ! in_array($this->_migration_type, array('sequential', 'timestamp'))) 162 | { 163 | show_error('An invalid migration numbering type was specified: '.$this->_migration_type); 164 | } 165 | 166 | // If the migrations table is missing, make it 167 | if ( ! $this->db->table_exists($this->_migration_table)) 168 | { 169 | $this->dbforge->add_field(array( 170 | 'version' => array('type' => 'BIGINT', 'constraint' => 20), 171 | )); 172 | 173 | $this->dbforge->create_table($this->_migration_table, TRUE); 174 | 175 | $this->db->insert($this->_migration_table, array('version' => 0)); 176 | } 177 | 178 | // Do we auto migrate to the latest migration? 179 | if ($this->_migration_auto_latest === TRUE && ! $this->latest()) 180 | { 181 | show_error($this->error_string()); 182 | } 183 | } 184 | 185 | // -------------------------------------------------------------------- 186 | 187 | /** 188 | * Migrate to a schema version 189 | * 190 | * Calls each migration step required to get to the schema version of 191 | * choice 192 | * 193 | * @param string $target_version Target schema version 194 | * @return mixed TRUE if no migrations are found, current version string on success, FALSE on failure 195 | */ 196 | public function version($target_version) 197 | { 198 | // Note: We use strings, so that timestamp versions work on 32-bit systems 199 | $current_version = $this->_get_version(); 200 | 201 | if ($this->_migration_type === 'sequential') 202 | { 203 | $target_version = sprintf('%03d', $target_version); 204 | } 205 | else 206 | { 207 | $target_version = (string) $target_version; 208 | } 209 | 210 | $migrations = $this->find_migrations(); 211 | 212 | if ($target_version > 0 && ! isset($migrations[$target_version])) 213 | { 214 | $this->_error_string = sprintf($this->lang->line('migration_not_found'), $target_version); 215 | return FALSE; 216 | } 217 | 218 | if ($target_version > $current_version) 219 | { 220 | // Moving Up 221 | $method = 'up'; 222 | } 223 | else 224 | { 225 | // Moving Down, apply in reverse order 226 | $method = 'down'; 227 | krsort($migrations); 228 | } 229 | 230 | if (empty($migrations)) 231 | { 232 | return TRUE; 233 | } 234 | 235 | $previous = FALSE; 236 | 237 | // Validate all available migrations, and run the ones within our target range 238 | foreach ($migrations as $number => $file) 239 | { 240 | // Check for sequence gaps 241 | if ($this->_migration_type === 'sequential' && $previous !== FALSE && abs($number - $previous) > 1) 242 | { 243 | $this->_error_string = sprintf($this->lang->line('migration_sequence_gap'), $number); 244 | return FALSE; 245 | } 246 | 247 | include_once($file); 248 | $class = 'Migration_'.ucfirst(strtolower($this->_get_migration_name(basename($file, '.php')))); 249 | 250 | // Validate the migration file structure 251 | if ( ! class_exists($class, FALSE)) 252 | { 253 | $this->_error_string = sprintf($this->lang->line('migration_class_doesnt_exist'), $class); 254 | return FALSE; 255 | } 256 | 257 | $previous = $number; 258 | 259 | // Run migrations that are inside the target range 260 | if ( 261 | ($method === 'up' && $number > $current_version && $number <= $target_version) OR 262 | ($method === 'down' && $number <= $current_version && $number > $target_version) 263 | ) 264 | { 265 | $instance = new $class(); 266 | if ( ! is_callable(array($instance, $method))) 267 | { 268 | $this->_error_string = sprintf($this->lang->line('migration_missing_'.$method.'_method'), $class); 269 | return FALSE; 270 | } 271 | 272 | log_message('debug', 'Migrating '.$method.' from version '.$current_version.' to version '.$number); 273 | call_user_func(array($instance, $method)); 274 | $current_version = $number; 275 | $this->_update_version($current_version); 276 | } 277 | } 278 | 279 | // This is necessary when moving down, since the the last migration applied 280 | // will be the down() method for the next migration up from the target 281 | if ($current_version <> $target_version) 282 | { 283 | $current_version = $target_version; 284 | $this->_update_version($current_version); 285 | } 286 | 287 | log_message('debug', 'Finished migrating to '.$current_version); 288 | 289 | return $current_version; 290 | } 291 | 292 | // -------------------------------------------------------------------- 293 | 294 | /** 295 | * Sets the schema to the latest migration 296 | * 297 | * @return mixed Current version string on success, FALSE on failure 298 | */ 299 | public function latest() 300 | { 301 | $migrations = $this->find_migrations(); 302 | 303 | if (empty($migrations)) 304 | { 305 | $this->_error_string = $this->lang->line('migration_none_found'); 306 | return FALSE; 307 | } 308 | 309 | $last_migration = basename(end($migrations)); 310 | 311 | // Calculate the last migration step from existing migration 312 | // filenames and proceed to the standard version migration 313 | return $this->version($this->_get_migration_number($last_migration)); 314 | } 315 | 316 | // -------------------------------------------------------------------- 317 | 318 | /** 319 | * Sets the schema to the migration version set in config 320 | * 321 | * @return mixed TRUE if no migrations are found, current version string on success, FALSE on failure 322 | */ 323 | public function current() 324 | { 325 | return $this->version($this->_migration_version); 326 | } 327 | 328 | // -------------------------------------------------------------------- 329 | 330 | /** 331 | * Error string 332 | * 333 | * @return string Error message returned as a string 334 | */ 335 | public function error_string() 336 | { 337 | return $this->_error_string; 338 | } 339 | 340 | // -------------------------------------------------------------------- 341 | 342 | /** 343 | * Retrieves list of available migration scripts 344 | * 345 | * @return array list of migration file paths sorted by version 346 | */ 347 | public function find_migrations() 348 | { 349 | $migrations = array(); 350 | 351 | // Load all *_*.php files in the migrations path 352 | foreach (glob($this->_migration_path.'*_*.php') as $file) 353 | { 354 | $name = basename($file, '.php'); 355 | 356 | // Filter out non-migration files 357 | if (preg_match($this->_migration_regex, $name)) 358 | { 359 | $number = $this->_get_migration_number($name); 360 | 361 | // There cannot be duplicate migration numbers 362 | if (isset($migrations[$number])) 363 | { 364 | $this->_error_string = sprintf($this->lang->line('migration_multiple_version'), $number); 365 | show_error($this->_error_string); 366 | } 367 | 368 | $migrations[$number] = $file; 369 | } 370 | } 371 | 372 | ksort($migrations); 373 | return $migrations; 374 | } 375 | 376 | // -------------------------------------------------------------------- 377 | 378 | /** 379 | * Extracts the migration number from a filename 380 | * 381 | * @param string $migration 382 | * @return string Numeric portion of a migration filename 383 | */ 384 | protected function _get_migration_number($migration) 385 | { 386 | return sscanf($migration, '%[0-9]+', $number) 387 | ? $number : '0'; 388 | } 389 | 390 | // -------------------------------------------------------------------- 391 | 392 | /** 393 | * Extracts the migration class name from a filename 394 | * 395 | * @param string $migration 396 | * @return string text portion of a migration filename 397 | */ 398 | protected function _get_migration_name($migration) 399 | { 400 | $parts = explode('_', $migration); 401 | array_shift($parts); 402 | return implode('_', $parts); 403 | } 404 | 405 | // -------------------------------------------------------------------- 406 | 407 | /** 408 | * Retrieves current schema version 409 | * 410 | * @return string Current migration version 411 | */ 412 | protected function _get_version() 413 | { 414 | $row = $this->db->select('version')->get($this->_migration_table)->row(); 415 | return $row ? $row->version : '0'; 416 | } 417 | 418 | // -------------------------------------------------------------------- 419 | 420 | /** 421 | * Stores the current schema version 422 | * 423 | * @param string $migration Migration reached 424 | * @return void 425 | */ 426 | protected function _update_version($migration) 427 | { 428 | $this->db->update($this->_migration_table, array( 429 | 'version' => $migration 430 | )); 431 | } 432 | 433 | // -------------------------------------------------------------------- 434 | 435 | /** 436 | * Enable the use of CI super-global 437 | * 438 | * @param string $var 439 | * @return mixed 440 | */ 441 | public function __get($var) 442 | { 443 | return get_instance()->$var; 444 | } 445 | 446 | } 447 | -------------------------------------------------------------------------------- /README/Parser.php: -------------------------------------------------------------------------------- 1 | CI =& get_instance(); 82 | log_message('info', 'Parser Class Initialized'); 83 | } 84 | 85 | // -------------------------------------------------------------------- 86 | 87 | /** 88 | * Parse a template 89 | * 90 | * Parses pseudo-variables contained in the specified template view, 91 | * replacing them with the data in the second param 92 | * 93 | * @param string 94 | * @param array 95 | * @param bool 96 | * @return string 97 | */ 98 | public function parse($template, $data, $return = FALSE) 99 | { 100 | $template = $this->CI->load->view($template, $data, TRUE); 101 | 102 | return $this->_parse($template, $data, $return); 103 | } 104 | 105 | // -------------------------------------------------------------------- 106 | 107 | /** 108 | * Parse a String 109 | * 110 | * Parses pseudo-variables contained in the specified string, 111 | * replacing them with the data in the second param 112 | * 113 | * @param string 114 | * @param array 115 | * @param bool 116 | * @return string 117 | */ 118 | public function parse_string($template, $data, $return = FALSE) 119 | { 120 | return $this->_parse($template, $data, $return); 121 | } 122 | 123 | // -------------------------------------------------------------------- 124 | 125 | /** 126 | * Parse a template 127 | * 128 | * Parses pseudo-variables contained in the specified template, 129 | * replacing them with the data in the second param 130 | * 131 | * @param string 132 | * @param array 133 | * @param bool 134 | * @return string 135 | */ 136 | protected function _parse($template, $data, $return = FALSE) 137 | { 138 | if ($template === '') 139 | { 140 | return FALSE; 141 | } 142 | 143 | $replace = array(); 144 | foreach ($data as $key => $val) 145 | { 146 | $replace = array_merge( 147 | $replace, 148 | is_array($val) 149 | ? $this->_parse_pair($key, $val, $template) 150 | : $this->_parse_single($key, (string) $val, $template) 151 | ); 152 | } 153 | 154 | unset($data); 155 | $template = strtr($template, $replace); 156 | 157 | if ($return === FALSE) 158 | { 159 | $this->CI->output->append_output($template); 160 | } 161 | 162 | return $template; 163 | } 164 | 165 | // -------------------------------------------------------------------- 166 | 167 | /** 168 | * Set the left/right variable delimiters 169 | * 170 | * @param string 171 | * @param string 172 | * @return void 173 | */ 174 | public function set_delimiters($l = '{', $r = '}') 175 | { 176 | $this->l_delim = $l; 177 | $this->r_delim = $r; 178 | } 179 | 180 | // -------------------------------------------------------------------- 181 | 182 | /** 183 | * Parse a single key/value 184 | * 185 | * @param string 186 | * @param string 187 | * @param string 188 | * @return string 189 | */ 190 | protected function _parse_single($key, $val, $string) 191 | { 192 | return array($this->l_delim.$key.$this->r_delim => (string) $val); 193 | } 194 | 195 | // -------------------------------------------------------------------- 196 | 197 | /** 198 | * Parse a tag pair 199 | * 200 | * Parses tag pairs: {some_tag} string... {/some_tag} 201 | * 202 | * @param string 203 | * @param array 204 | * @param string 205 | * @return string 206 | */ 207 | protected function _parse_pair($variable, $data, $string) 208 | { 209 | $replace = array(); 210 | preg_match_all( 211 | '#'.preg_quote($this->l_delim.$variable.$this->r_delim).'(.+?)'.preg_quote($this->l_delim.'/'.$variable.$this->r_delim).'#s', 212 | $string, 213 | $matches, 214 | PREG_SET_ORDER 215 | ); 216 | 217 | foreach ($matches as $match) 218 | { 219 | $str = ''; 220 | foreach ($data as $row) 221 | { 222 | $temp = array(); 223 | foreach ($row as $key => $val) 224 | { 225 | if (is_array($val)) 226 | { 227 | $pair = $this->_parse_pair($key, $val, $match[1]); 228 | if ( ! empty($pair)) 229 | { 230 | $temp = array_merge($temp, $pair); 231 | } 232 | 233 | continue; 234 | } 235 | 236 | $temp[$this->l_delim.$key.$this->r_delim] = $val; 237 | } 238 | 239 | $str .= strtr($match[1], $temp); 240 | } 241 | 242 | $replace[$match[0]] = $str; 243 | } 244 | 245 | return $replace; 246 | } 247 | 248 | } 249 | -------------------------------------------------------------------------------- /README/Session/SessionHandlerInterface.php: -------------------------------------------------------------------------------- 1 | _config =& $params; 88 | } 89 | 90 | // ------------------------------------------------------------------------ 91 | 92 | /** 93 | * Cookie destroy 94 | * 95 | * Internal method to force removal of a cookie by the client 96 | * when session_destroy() is called. 97 | * 98 | * @return bool 99 | */ 100 | protected function _cookie_destroy() 101 | { 102 | return setcookie( 103 | $this->_config['cookie_name'], 104 | NULL, 105 | 1, 106 | $this->_config['cookie_path'], 107 | $this->_config['cookie_domain'], 108 | $this->_config['cookie_secure'], 109 | TRUE 110 | ); 111 | } 112 | 113 | // ------------------------------------------------------------------------ 114 | 115 | /** 116 | * Get lock 117 | * 118 | * A dummy method allowing drivers with no locking functionality 119 | * (databases other than PostgreSQL and MySQL) to act as if they 120 | * do acquire a lock. 121 | * 122 | * @param string $session_id 123 | * @return bool 124 | */ 125 | protected function _get_lock($session_id) 126 | { 127 | $this->_lock = TRUE; 128 | return TRUE; 129 | } 130 | 131 | // ------------------------------------------------------------------------ 132 | 133 | /** 134 | * Release lock 135 | * 136 | * @return bool 137 | */ 138 | protected function _release_lock() 139 | { 140 | if ($this->_lock) 141 | { 142 | $this->_lock = FALSE; 143 | } 144 | 145 | return TRUE; 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /README/Session/drivers/Session_database_driver.php: -------------------------------------------------------------------------------- 1 | db) OR $CI->load->database(); 86 | $this->_db = $CI->db; 87 | 88 | if ( ! $this->_db instanceof CI_DB_query_builder) 89 | { 90 | throw new Exception('Query Builder not enabled for the configured database. Aborting.'); 91 | } 92 | elseif ($this->_db->pconnect) 93 | { 94 | throw new Exception('Configured database connection is persistent. Aborting.'); 95 | } 96 | elseif ($this->_db->cache_on) 97 | { 98 | throw new Exception('Configured database connection has cache enabled. Aborting.'); 99 | } 100 | 101 | $db_driver = $this->_db->dbdriver.(empty($this->_db->subdriver) ? '' : '_'.$this->_db->subdriver); 102 | if (strpos($db_driver, 'mysql') !== FALSE) 103 | { 104 | $this->_platform = 'mysql'; 105 | } 106 | elseif (in_array($db_driver, array('postgre', 'pdo_pgsql'), TRUE)) 107 | { 108 | $this->_platform = 'postgre'; 109 | } 110 | 111 | // Note: BC work-around for the old 'sess_table_name' setting, should be removed in the future. 112 | isset($this->_config['save_path']) OR $this->_config['save_path'] = config_item('sess_table_name'); 113 | } 114 | 115 | // ------------------------------------------------------------------------ 116 | 117 | /** 118 | * Open 119 | * 120 | * Initializes the database connection 121 | * 122 | * @param string $save_path Table name 123 | * @param string $name Session cookie name, unused 124 | * @return bool 125 | */ 126 | public function open($save_path, $name) 127 | { 128 | return empty($this->_db->conn_id) 129 | ? (bool) $this->_db->db_connect() 130 | : TRUE; 131 | } 132 | 133 | // ------------------------------------------------------------------------ 134 | 135 | /** 136 | * Read 137 | * 138 | * Reads session data and acquires a lock 139 | * 140 | * @param string $session_id Session ID 141 | * @return string Serialized session data 142 | */ 143 | public function read($session_id) 144 | { 145 | if ($this->_get_lock($session_id) !== FALSE) 146 | { 147 | // Needed by write() to detect session_regenerate_id() calls 148 | $this->_session_id = $session_id; 149 | 150 | $this->_db 151 | ->select('data') 152 | ->from($this->_config['save_path']) 153 | ->where('id', $session_id); 154 | 155 | if ($this->_config['match_ip']) 156 | { 157 | $this->_db->where('ip_address', $_SERVER['REMOTE_ADDR']); 158 | } 159 | 160 | if (($result = $this->_db->get()->row()) === NULL) 161 | { 162 | $this->_fingerprint = md5(''); 163 | return ''; 164 | } 165 | 166 | // PostgreSQL's variant of a BLOB datatype is Bytea, which is a 167 | // PITA to work with, so we use base64-encoded data in a TEXT 168 | // field instead. 169 | $result = ($this->_platform === 'postgre') 170 | ? base64_decode(rtrim($result->data)) 171 | : $result->data; 172 | 173 | $this->_fingerprint = md5($result); 174 | $this->_row_exists = TRUE; 175 | return $result; 176 | } 177 | 178 | $this->_fingerprint = md5(''); 179 | return ''; 180 | } 181 | 182 | // ------------------------------------------------------------------------ 183 | 184 | /** 185 | * Write 186 | * 187 | * Writes (create / update) session data 188 | * 189 | * @param string $session_id Session ID 190 | * @param string $session_data Serialized session data 191 | * @return bool 192 | */ 193 | public function write($session_id, $session_data) 194 | { 195 | // Was the ID regenerated? 196 | if ($session_id !== $this->_session_id) 197 | { 198 | if ( ! $this->_release_lock() OR ! $this->_get_lock($session_id)) 199 | { 200 | return FALSE; 201 | } 202 | 203 | $this->_row_exists = FALSE; 204 | $this->_session_id = $session_id; 205 | } 206 | elseif ($this->_lock === FALSE) 207 | { 208 | return FALSE; 209 | } 210 | 211 | if ($this->_row_exists === FALSE) 212 | { 213 | $insert_data = array( 214 | 'id' => $session_id, 215 | 'ip_address' => $_SERVER['REMOTE_ADDR'], 216 | 'timestamp' => time(), 217 | 'data' => ($this->_platform === 'postgre' ? base64_encode($session_data) : $session_data) 218 | ); 219 | 220 | if ($this->_db->insert($this->_config['save_path'], $insert_data)) 221 | { 222 | $this->_fingerprint = md5($session_data); 223 | return $this->_row_exists = TRUE; 224 | } 225 | 226 | return FALSE; 227 | } 228 | 229 | $this->_db->where('id', $session_id); 230 | if ($this->_config['match_ip']) 231 | { 232 | $this->_db->where('ip_address', $_SERVER['REMOTE_ADDR']); 233 | } 234 | 235 | $update_data = array('timestamp' => time()); 236 | if ($this->_fingerprint !== md5($session_data)) 237 | { 238 | $update_data['data'] = ($this->_platform === 'postgre') 239 | ? base64_encode($session_data) 240 | : $session_data; 241 | } 242 | 243 | if ($this->_db->update($this->_config['save_path'], $update_data)) 244 | { 245 | $this->_fingerprint = md5($session_data); 246 | return TRUE; 247 | } 248 | 249 | return FALSE; 250 | } 251 | 252 | // ------------------------------------------------------------------------ 253 | 254 | /** 255 | * Close 256 | * 257 | * Releases locks 258 | * 259 | * @return bool 260 | */ 261 | public function close() 262 | { 263 | return ($this->_lock) 264 | ? $this->_release_lock() 265 | : TRUE; 266 | } 267 | 268 | // ------------------------------------------------------------------------ 269 | 270 | /** 271 | * Destroy 272 | * 273 | * Destroys the current session. 274 | * 275 | * @param string $session_id Session ID 276 | * @return bool 277 | */ 278 | public function destroy($session_id) 279 | { 280 | if ($this->_lock) 281 | { 282 | $this->_db->where('id', $session_id); 283 | if ($this->_config['match_ip']) 284 | { 285 | $this->_db->where('ip_address', $_SERVER['REMOTE_ADDR']); 286 | } 287 | 288 | return $this->_db->delete($this->_config['save_path']) 289 | ? ($this->close() && $this->_cookie_destroy()) 290 | : FALSE; 291 | } 292 | 293 | return ($this->close() && $this->_cookie_destroy()); 294 | } 295 | 296 | // ------------------------------------------------------------------------ 297 | 298 | /** 299 | * Garbage Collector 300 | * 301 | * Deletes expired sessions 302 | * 303 | * @param int $maxlifetime Maximum lifetime of sessions 304 | * @return bool 305 | */ 306 | public function gc($maxlifetime) 307 | { 308 | return $this->_db->delete($this->_config['save_path'], 'timestamp < '.(time() - $maxlifetime)); 309 | } 310 | 311 | // ------------------------------------------------------------------------ 312 | 313 | /** 314 | * Get lock 315 | * 316 | * Acquires a lock, depending on the underlying platform. 317 | * 318 | * @param string $session_id Session ID 319 | * @return bool 320 | */ 321 | protected function _get_lock($session_id) 322 | { 323 | if ($this->_platform === 'mysql') 324 | { 325 | $arg = $session_id.($this->_config['match_ip'] ? '_'.$_SERVER['REMOTE_ADDR'] : ''); 326 | if ($this->_db->query("SELECT GET_LOCK('".$arg."', 300) AS ci_session_lock")->row()->ci_session_lock) 327 | { 328 | $this->_lock = $arg; 329 | return TRUE; 330 | } 331 | 332 | return FALSE; 333 | } 334 | elseif ($this->_platform === 'postgre') 335 | { 336 | $arg = "hashtext('".$session_id."')".($this->_config['match_ip'] ? ", hashtext('".$_SERVER['REMOTE_ADDR']."')" : ''); 337 | if ($this->_db->simple_query('SELECT pg_advisory_lock('.$arg.')')) 338 | { 339 | $this->_lock = $arg; 340 | return TRUE; 341 | } 342 | 343 | return FALSE; 344 | } 345 | 346 | return parent::_get_lock($session_id); 347 | } 348 | 349 | // ------------------------------------------------------------------------ 350 | 351 | /** 352 | * Release lock 353 | * 354 | * Releases a previously acquired lock 355 | * 356 | * @return bool 357 | */ 358 | protected function _release_lock() 359 | { 360 | if ( ! $this->_lock) 361 | { 362 | return TRUE; 363 | } 364 | 365 | if ($this->_platform === 'mysql') 366 | { 367 | if ($this->_db->query("SELECT RELEASE_LOCK('".$this->_lock."') AS ci_session_lock")->row()->ci_session_lock) 368 | { 369 | $this->_lock = FALSE; 370 | return TRUE; 371 | } 372 | 373 | return FALSE; 374 | } 375 | elseif ($this->_platform === 'postgre') 376 | { 377 | if ($this->_db->simple_query('SELECT pg_advisory_unlock('.$this->_lock.')')) 378 | { 379 | $this->_lock = FALSE; 380 | return TRUE; 381 | } 382 | 383 | return FALSE; 384 | } 385 | 386 | return parent::_release_lock(); 387 | } 388 | 389 | } 390 | -------------------------------------------------------------------------------- /README/Session/drivers/Session_files_driver.php: -------------------------------------------------------------------------------- 1 | _config['save_path'])) 92 | { 93 | $this->_config['save_path'] = rtrim($this->_config['save_path'], '/\\'); 94 | ini_set('session.save_path', $this->_config['save_path']); 95 | } 96 | else 97 | { 98 | $this->_config['save_path'] = rtrim(ini_get('session.save_path'), '/\\'); 99 | } 100 | } 101 | 102 | // ------------------------------------------------------------------------ 103 | 104 | /** 105 | * Open 106 | * 107 | * Sanitizes the save_path directory. 108 | * 109 | * @param string $save_path Path to session files' directory 110 | * @param string $name Session cookie name 111 | * @return bool 112 | */ 113 | public function open($save_path, $name) 114 | { 115 | if ( ! is_dir($save_path)) 116 | { 117 | if ( ! mkdir($save_path, 0700, TRUE)) 118 | { 119 | throw new Exception("Session: Configured save path '".$this->_config['save_path']."' is not a directory, doesn't exist or cannot be created."); 120 | } 121 | } 122 | elseif ( ! is_writable($save_path)) 123 | { 124 | throw new Exception("Session: Configured save path '".$this->_config['save_path']."' is not writable by the PHP process."); 125 | } 126 | 127 | $this->_config['save_path'] = $save_path; 128 | $this->_file_path = $this->_config['save_path'].DIRECTORY_SEPARATOR 129 | .$name // we'll use the session cookie name as a prefix to avoid collisions 130 | .($this->_config['match_ip'] ? md5($_SERVER['REMOTE_ADDR']) : ''); 131 | 132 | return TRUE; 133 | } 134 | 135 | // ------------------------------------------------------------------------ 136 | 137 | /** 138 | * Read 139 | * 140 | * Reads session data and acquires a lock 141 | * 142 | * @param string $session_id Session ID 143 | * @return string Serialized session data 144 | */ 145 | public function read($session_id) 146 | { 147 | // This might seem weird, but PHP 5.6 introduces session_reset(), 148 | // which re-reads session data 149 | if ($this->_file_handle === NULL) 150 | { 151 | // Just using fopen() with 'c+b' mode would be perfect, but it is only 152 | // available since PHP 5.2.6 and we have to set permissions for new files, 153 | // so we'd have to hack around this ... 154 | if (($this->_file_new = ! file_exists($this->_file_path.$session_id)) === TRUE) 155 | { 156 | if (($this->_file_handle = fopen($this->_file_path.$session_id, 'w+b')) === FALSE) 157 | { 158 | log_message('error', "Session: File '".$this->_file_path.$session_id."' doesn't exist and cannot be created."); 159 | return FALSE; 160 | } 161 | } 162 | elseif (($this->_file_handle = fopen($this->_file_path.$session_id, 'r+b')) === FALSE) 163 | { 164 | log_message('error', "Session: Unable to open file '".$this->_file_path.$session_id."'."); 165 | return FALSE; 166 | } 167 | 168 | if (flock($this->_file_handle, LOCK_EX) === FALSE) 169 | { 170 | log_message('error', "Session: Unable to obtain lock for file '".$this->_file_path.$session_id."'."); 171 | fclose($this->_file_handle); 172 | $this->_file_handle = NULL; 173 | return FALSE; 174 | } 175 | 176 | // Needed by write() to detect session_regenerate_id() calls 177 | $this->_session_id = $session_id; 178 | 179 | if ($this->_file_new) 180 | { 181 | chmod($this->_file_path.$session_id, 0600); 182 | $this->_fingerprint = md5(''); 183 | return ''; 184 | } 185 | } 186 | else 187 | { 188 | rewind($this->_file_handle); 189 | } 190 | 191 | $session_data = ''; 192 | for ($read = 0, $length = filesize($this->_file_path.$session_id); $read < $length; $read += strlen($buffer)) 193 | { 194 | if (($buffer = fread($this->_file_handle, $length - $read)) === FALSE) 195 | { 196 | break; 197 | } 198 | 199 | $session_data .= $buffer; 200 | } 201 | 202 | $this->_fingerprint = md5($session_data); 203 | return $session_data; 204 | } 205 | 206 | // ------------------------------------------------------------------------ 207 | 208 | /** 209 | * Write 210 | * 211 | * Writes (create / update) session data 212 | * 213 | * @param string $session_id Session ID 214 | * @param string $session_data Serialized session data 215 | * @return bool 216 | */ 217 | public function write($session_id, $session_data) 218 | { 219 | // If the two IDs don't match, we have a session_regenerate_id() call 220 | // and we need to close the old handle and open a new one 221 | if ($session_id !== $this->_session_id && ( ! $this->close() OR $this->read($session_id) === FALSE)) 222 | { 223 | return FALSE; 224 | } 225 | 226 | if ( ! is_resource($this->_file_handle)) 227 | { 228 | return FALSE; 229 | } 230 | elseif ($this->_fingerprint === md5($session_data)) 231 | { 232 | return ($this->_file_new) 233 | ? TRUE 234 | : touch($this->_file_path.$session_id); 235 | } 236 | 237 | if ( ! $this->_file_new) 238 | { 239 | ftruncate($this->_file_handle, 0); 240 | rewind($this->_file_handle); 241 | } 242 | 243 | if (($length = strlen($session_data)) > 0) 244 | { 245 | for ($written = 0; $written < $length; $written += $result) 246 | { 247 | if (($result = fwrite($this->_file_handle, substr($session_data, $written))) === FALSE) 248 | { 249 | break; 250 | } 251 | } 252 | 253 | if ( ! is_int($result)) 254 | { 255 | $this->_fingerprint = md5(substr($session_data, 0, $written)); 256 | log_message('error', 'Session: Unable to write data.'); 257 | return FALSE; 258 | } 259 | } 260 | 261 | $this->_fingerprint = md5($session_data); 262 | return TRUE; 263 | } 264 | 265 | // ------------------------------------------------------------------------ 266 | 267 | /** 268 | * Close 269 | * 270 | * Releases locks and closes file descriptor. 271 | * 272 | * @return bool 273 | */ 274 | public function close() 275 | { 276 | if (is_resource($this->_file_handle)) 277 | { 278 | flock($this->_file_handle, LOCK_UN); 279 | fclose($this->_file_handle); 280 | 281 | $this->_file_handle = $this->_file_new = $this->_session_id = NULL; 282 | return TRUE; 283 | } 284 | 285 | return TRUE; 286 | } 287 | 288 | // ------------------------------------------------------------------------ 289 | 290 | /** 291 | * Destroy 292 | * 293 | * Destroys the current session. 294 | * 295 | * @param string $session_id Session ID 296 | * @return bool 297 | */ 298 | public function destroy($session_id) 299 | { 300 | if ($this->close()) 301 | { 302 | return file_exists($this->_file_path.$session_id) 303 | ? (unlink($this->_file_path.$session_id) && $this->_cookie_destroy()) 304 | : TRUE; 305 | } 306 | elseif ($this->_file_path !== NULL) 307 | { 308 | clearstatcache(); 309 | return file_exists($this->_file_path.$session_id) 310 | ? (unlink($this->_file_path.$session_id) && $this->_cookie_destroy()) 311 | : TRUE; 312 | } 313 | 314 | return FALSE; 315 | } 316 | 317 | // ------------------------------------------------------------------------ 318 | 319 | /** 320 | * Garbage Collector 321 | * 322 | * Deletes expired sessions 323 | * 324 | * @param int $maxlifetime Maximum lifetime of sessions 325 | * @return bool 326 | */ 327 | public function gc($maxlifetime) 328 | { 329 | if ( ! is_dir($this->_config['save_path']) OR ($directory = opendir($this->_config['save_path'])) === FALSE) 330 | { 331 | log_message('debug', "Session: Garbage collector couldn't list files under directory '".$this->_config['save_path']."'."); 332 | return FALSE; 333 | } 334 | 335 | $ts = time() - $maxlifetime; 336 | 337 | $pattern = sprintf( 338 | '/^%s[0-9a-f]{%d}$/', 339 | preg_quote($this->_config['cookie_name'], '/'), 340 | ($this->_config['match_ip'] === TRUE ? 72 : 40) 341 | ); 342 | 343 | while (($file = readdir($directory)) !== FALSE) 344 | { 345 | // If the filename doesn't match this pattern, it's either not a session file or is not ours 346 | if ( ! preg_match($pattern, $file) 347 | OR ! is_file($this->_config['save_path'].DIRECTORY_SEPARATOR.$file) 348 | OR ($mtime = filemtime($this->_config['save_path'].DIRECTORY_SEPARATOR.$file)) === FALSE 349 | OR $mtime > $ts) 350 | { 351 | continue; 352 | } 353 | 354 | unlink($this->_config['save_path'].DIRECTORY_SEPARATOR.$file); 355 | } 356 | 357 | closedir($directory); 358 | 359 | return TRUE; 360 | } 361 | 362 | } 363 | -------------------------------------------------------------------------------- /README/Session/drivers/Session_memcached_driver.php: -------------------------------------------------------------------------------- 1 | _config['save_path'])) 85 | { 86 | log_message('error', 'Session: No Memcached save path configured.'); 87 | } 88 | 89 | if ($this->_config['match_ip'] === TRUE) 90 | { 91 | $this->_key_prefix .= $_SERVER['REMOTE_ADDR'].':'; 92 | } 93 | } 94 | 95 | // ------------------------------------------------------------------------ 96 | 97 | /** 98 | * Open 99 | * 100 | * Sanitizes save_path and initializes connections. 101 | * 102 | * @param string $save_path Server path(s) 103 | * @param string $name Session cookie name, unused 104 | * @return bool 105 | */ 106 | public function open($save_path, $name) 107 | { 108 | $this->_memcached = new Memcached(); 109 | $this->_memcached->setOption(Memcached::OPT_BINARY_PROTOCOL, TRUE); // required for touch() usage 110 | $server_list = array(); 111 | foreach ($this->_memcached->getServerList() as $server) 112 | { 113 | $server_list[] = $server['host'].':'.$server['port']; 114 | } 115 | 116 | if ( ! preg_match_all('#,?([^,:]+)\:(\d{1,5})(?:\:(\d+))?#', $this->_config['save_path'], $matches, PREG_SET_ORDER)) 117 | { 118 | $this->_memcached = NULL; 119 | log_message('error', 'Session: Invalid Memcached save path format: '.$this->_config['save_path']); 120 | return FALSE; 121 | } 122 | 123 | foreach ($matches as $match) 124 | { 125 | // If Memcached already has this server (or if the port is invalid), skip it 126 | if (in_array($match[1].':'.$match[2], $server_list, TRUE)) 127 | { 128 | log_message('debug', 'Session: Memcached server pool already has '.$match[1].':'.$match[2]); 129 | continue; 130 | } 131 | 132 | if ( ! $this->_memcached->addServer($match[1], $match[2], isset($match[3]) ? $match[3] : 0)) 133 | { 134 | log_message('error', 'Could not add '.$match[1].':'.$match[2].' to Memcached server pool.'); 135 | } 136 | else 137 | { 138 | $server_list[] = $match[1].':'.$match[2]; 139 | } 140 | } 141 | 142 | if (empty($server_list)) 143 | { 144 | log_message('error', 'Session: Memcached server pool is empty.'); 145 | return FALSE; 146 | } 147 | 148 | return TRUE; 149 | } 150 | 151 | // ------------------------------------------------------------------------ 152 | 153 | /** 154 | * Read 155 | * 156 | * Reads session data and acquires a lock 157 | * 158 | * @param string $session_id Session ID 159 | * @return string Serialized session data 160 | */ 161 | public function read($session_id) 162 | { 163 | if (isset($this->_memcached) && $this->_get_lock($session_id)) 164 | { 165 | // Needed by write() to detect session_regenerate_id() calls 166 | $this->_session_id = $session_id; 167 | 168 | $session_data = (string) $this->_memcached->get($this->_key_prefix.$session_id); 169 | $this->_fingerprint = md5($session_data); 170 | return $session_data; 171 | } 172 | 173 | return FALSE; 174 | } 175 | 176 | // ------------------------------------------------------------------------ 177 | 178 | /** 179 | * Write 180 | * 181 | * Writes (create / update) session data 182 | * 183 | * @param string $session_id Session ID 184 | * @param string $session_data Serialized session data 185 | * @return bool 186 | */ 187 | public function write($session_id, $session_data) 188 | { 189 | if ( ! isset($this->_memcached)) 190 | { 191 | return FALSE; 192 | } 193 | // Was the ID regenerated? 194 | elseif ($session_id !== $this->_session_id) 195 | { 196 | if ( ! $this->_release_lock() OR ! $this->_get_lock($session_id)) 197 | { 198 | return FALSE; 199 | } 200 | 201 | $this->_fingerprint = md5(''); 202 | $this->_session_id = $session_id; 203 | } 204 | 205 | if (isset($this->_lock_key)) 206 | { 207 | $this->_memcached->replace($this->_lock_key, time(), 300); 208 | if ($this->_fingerprint !== ($fingerprint = md5($session_data))) 209 | { 210 | if ($this->_memcached->set($this->_key_prefix.$session_id, $session_data, $this->_config['expiration'])) 211 | { 212 | $this->_fingerprint = $fingerprint; 213 | return TRUE; 214 | } 215 | 216 | return FALSE; 217 | } 218 | 219 | return $this->_memcached->touch($this->_key_prefix.$session_id, $this->_config['expiration']); 220 | } 221 | 222 | return FALSE; 223 | } 224 | 225 | // ------------------------------------------------------------------------ 226 | 227 | /** 228 | * Close 229 | * 230 | * Releases locks and closes connection. 231 | * 232 | * @return bool 233 | */ 234 | public function close() 235 | { 236 | if (isset($this->_memcached)) 237 | { 238 | isset($this->_lock_key) && $this->_memcached->delete($this->_lock_key); 239 | if ( ! $this->_memcached->quit()) 240 | { 241 | return FALSE; 242 | } 243 | 244 | $this->_memcached = NULL; 245 | return TRUE; 246 | } 247 | 248 | return FALSE; 249 | } 250 | 251 | // ------------------------------------------------------------------------ 252 | 253 | /** 254 | * Destroy 255 | * 256 | * Destroys the current session. 257 | * 258 | * @param string $session_id Session ID 259 | * @return bool 260 | */ 261 | public function destroy($session_id) 262 | { 263 | if (isset($this->_memcached, $this->_lock_key)) 264 | { 265 | $this->_memcached->delete($this->_key_prefix.$session_id); 266 | return $this->_cookie_destroy(); 267 | } 268 | 269 | return FALSE; 270 | } 271 | 272 | // ------------------------------------------------------------------------ 273 | 274 | /** 275 | * Garbage Collector 276 | * 277 | * Deletes expired sessions 278 | * 279 | * @param int $maxlifetime Maximum lifetime of sessions 280 | * @return bool 281 | */ 282 | public function gc($maxlifetime) 283 | { 284 | // Not necessary, Memcached takes care of that. 285 | return TRUE; 286 | } 287 | 288 | // ------------------------------------------------------------------------ 289 | 290 | /** 291 | * Get lock 292 | * 293 | * Acquires an (emulated) lock. 294 | * 295 | * @param string $session_id Session ID 296 | * @return bool 297 | */ 298 | protected function _get_lock($session_id) 299 | { 300 | if (isset($this->_lock_key)) 301 | { 302 | return $this->_memcached->replace($this->_lock_key, time(), 300); 303 | } 304 | 305 | // 30 attempts to obtain a lock, in case another request already has it 306 | $lock_key = $this->_key_prefix.$session_id.':lock'; 307 | $attempt = 0; 308 | do 309 | { 310 | if ($this->_memcached->get($lock_key)) 311 | { 312 | sleep(1); 313 | continue; 314 | } 315 | 316 | if ( ! $this->_memcached->set($lock_key, time(), 300)) 317 | { 318 | log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id); 319 | return FALSE; 320 | } 321 | 322 | $this->_lock_key = $lock_key; 323 | break; 324 | } 325 | while (++$attempt < 30); 326 | 327 | if ($attempt === 30) 328 | { 329 | log_message('error', 'Session: Unable to obtain lock for '.$this->_key_prefix.$session_id.' after 30 attempts, aborting.'); 330 | return FALSE; 331 | } 332 | 333 | $this->_lock = TRUE; 334 | return TRUE; 335 | } 336 | 337 | // ------------------------------------------------------------------------ 338 | 339 | /** 340 | * Release lock 341 | * 342 | * Releases a previously acquired lock 343 | * 344 | * @return bool 345 | */ 346 | protected function _release_lock() 347 | { 348 | if (isset($this->_memcached, $this->_lock_key) && $this->_lock) 349 | { 350 | if ( ! $this->_memcached->delete($this->_lock_key) && $this->_memcached->getResultCode() !== Memcached::RES_NOTFOUND) 351 | { 352 | log_message('error', 'Session: Error while trying to free lock for '.$this->_lock_key); 353 | return FALSE; 354 | } 355 | 356 | $this->_lock_key = NULL; 357 | $this->_lock = FALSE; 358 | } 359 | 360 | return TRUE; 361 | } 362 | 363 | } 364 | -------------------------------------------------------------------------------- /README/Session/drivers/Session_redis_driver.php: -------------------------------------------------------------------------------- 1 | _config['save_path'])) 85 | { 86 | log_message('error', 'Session: No Redis save path configured.'); 87 | } 88 | elseif (preg_match('#(?:tcp://)?([^:?]+)(?:\:(\d+))?(\?.+)?#', $this->_config['save_path'], $matches)) 89 | { 90 | isset($matches[3]) OR $matches[3] = ''; // Just to avoid undefined index notices below 91 | $this->_config['save_path'] = array( 92 | 'host' => $matches[1], 93 | 'port' => empty($matches[2]) ? NULL : $matches[2], 94 | 'password' => preg_match('#auth=([^\s&]+)#', $matches[3], $match) ? $match[1] : NULL, 95 | 'database' => preg_match('#database=(\d+)#', $matches[3], $match) ? (int) $match[1] : NULL, 96 | 'timeout' => preg_match('#timeout=(\d+\.\d+)#', $matches[3], $match) ? (float) $match[1] : NULL 97 | ); 98 | 99 | preg_match('#prefix=([^\s&]+)#', $matches[3], $match) && $this->_key_prefix = $match[1]; 100 | } 101 | else 102 | { 103 | log_message('error', 'Session: Invalid Redis save path format: '.$this->_config['save_path']); 104 | } 105 | 106 | if ($this->_config['match_ip'] === TRUE) 107 | { 108 | $this->_key_prefix .= $_SERVER['REMOTE_ADDR'].':'; 109 | } 110 | } 111 | 112 | // ------------------------------------------------------------------------ 113 | 114 | /** 115 | * Open 116 | * 117 | * Sanitizes save_path and initializes connection. 118 | * 119 | * @param string $save_path Server path 120 | * @param string $name Session cookie name, unused 121 | * @return bool 122 | */ 123 | public function open($save_path, $name) 124 | { 125 | if (empty($this->_config['save_path'])) 126 | { 127 | return FALSE; 128 | } 129 | 130 | $redis = new Redis(); 131 | if ( ! $redis->connect($this->_config['save_path']['host'], $this->_config['save_path']['port'], $this->_config['save_path']['timeout'])) 132 | { 133 | log_message('error', 'Session: Unable to connect to Redis with the configured settings.'); 134 | } 135 | elseif (isset($this->_config['save_path']['password']) && ! $redis->auth($this->_config['save_path']['password'])) 136 | { 137 | log_message('error', 'Session: Unable to authenticate to Redis instance.'); 138 | } 139 | elseif (isset($this->_config['save_path']['database']) && ! $redis->select($this->_config['save_path']['database'])) 140 | { 141 | log_message('error', 'Session: Unable to select Redis database with index '.$this->_config['save_path']['database']); 142 | } 143 | else 144 | { 145 | $this->_redis = $redis; 146 | return TRUE; 147 | } 148 | 149 | return FALSE; 150 | } 151 | 152 | // ------------------------------------------------------------------------ 153 | 154 | /** 155 | * Read 156 | * 157 | * Reads session data and acquires a lock 158 | * 159 | * @param string $session_id Session ID 160 | * @return string Serialized session data 161 | */ 162 | public function read($session_id) 163 | { 164 | if (isset($this->_redis) && $this->_get_lock($session_id)) 165 | { 166 | // Needed by write() to detect session_regenerate_id() calls 167 | $this->_session_id = $session_id; 168 | 169 | $session_data = (string) $this->_redis->get($this->_key_prefix.$session_id); 170 | $this->_fingerprint = md5($session_data); 171 | return $session_data; 172 | } 173 | 174 | return FALSE; 175 | } 176 | 177 | // ------------------------------------------------------------------------ 178 | 179 | /** 180 | * Write 181 | * 182 | * Writes (create / update) session data 183 | * 184 | * @param string $session_id Session ID 185 | * @param string $session_data Serialized session data 186 | * @return bool 187 | */ 188 | public function write($session_id, $session_data) 189 | { 190 | if ( ! isset($this->_redis)) 191 | { 192 | return FALSE; 193 | } 194 | // Was the ID regenerated? 195 | elseif ($session_id !== $this->_session_id) 196 | { 197 | if ( ! $this->_release_lock() OR ! $this->_get_lock($session_id)) 198 | { 199 | return FALSE; 200 | } 201 | 202 | $this->_fingerprint = md5(''); 203 | $this->_session_id = $session_id; 204 | } 205 | 206 | if (isset($this->_lock_key)) 207 | { 208 | $this->_redis->setTimeout($this->_lock_key, 300); 209 | if ($this->_fingerprint !== ($fingerprint = md5($session_data))) 210 | { 211 | if ($this->_redis->set($this->_key_prefix.$session_id, $session_data, $this->_config['expiration'])) 212 | { 213 | $this->_fingerprint = $fingerprint; 214 | return TRUE; 215 | } 216 | 217 | return FALSE; 218 | } 219 | 220 | return $this->_redis->setTimeout($this->_key_prefix.$session_id, $this->_config['expiration']); 221 | } 222 | 223 | return FALSE; 224 | } 225 | 226 | // ------------------------------------------------------------------------ 227 | 228 | /** 229 | * Close 230 | * 231 | * Releases locks and closes connection. 232 | * 233 | * @return bool 234 | */ 235 | public function close() 236 | { 237 | if (isset($this->_redis)) 238 | { 239 | try { 240 | if ($this->_redis->ping() === '+PONG') 241 | { 242 | isset($this->_lock_key) && $this->_redis->delete($this->_lock_key); 243 | if ( ! $this->_redis->close()) 244 | { 245 | return FALSE; 246 | } 247 | } 248 | } 249 | catch (RedisException $e) 250 | { 251 | log_message('error', 'Session: Got RedisException on close(): '.$e->getMessage()); 252 | } 253 | 254 | $this->_redis = NULL; 255 | return TRUE; 256 | } 257 | 258 | return TRUE; 259 | } 260 | 261 | // ------------------------------------------------------------------------ 262 | 263 | /** 264 | * Destroy 265 | * 266 | * Destroys the current session. 267 | * 268 | * @param string $session_id Session ID 269 | * @return bool 270 | */ 271 | public function destroy($session_id) 272 | { 273 | if (isset($this->_redis, $this->_lock_key)) 274 | { 275 | if (($result = $this->_redis->delete($this->_key_prefix.$session_id)) !== 1) 276 | { 277 | log_message('debug', 'Session: Redis::delete() expected to return 1, got '.var_export($result, TRUE).' instead.'); 278 | } 279 | 280 | return $this->_cookie_destroy(); 281 | } 282 | 283 | return FALSE; 284 | } 285 | 286 | // ------------------------------------------------------------------------ 287 | 288 | /** 289 | * Garbage Collector 290 | * 291 | * Deletes expired sessions 292 | * 293 | * @param int $maxlifetime Maximum lifetime of sessions 294 | * @return bool 295 | */ 296 | public function gc($maxlifetime) 297 | { 298 | // Not necessary, Redis takes care of that. 299 | return TRUE; 300 | } 301 | 302 | // ------------------------------------------------------------------------ 303 | 304 | /** 305 | * Get lock 306 | * 307 | * Acquires an (emulated) lock. 308 | * 309 | * @param string $session_id Session ID 310 | * @return bool 311 | */ 312 | protected function _get_lock($session_id) 313 | { 314 | if (isset($this->_lock_key)) 315 | { 316 | return $this->_redis->setTimeout($this->_lock_key, 300); 317 | } 318 | 319 | // 30 attempts to obtain a lock, in case another request already has it 320 | $lock_key = $this->_key_prefix.$session_id.':lock'; 321 | $attempt = 0; 322 | do 323 | { 324 | if (($ttl = $this->_redis->ttl($lock_key)) > 0) 325 | { 326 | sleep(1); 327 | continue; 328 | } 329 | 330 | if ( ! $this->_redis->setex($lock_key, 300, time())) 331 | { 332 | log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id); 333 | return FALSE; 334 | } 335 | 336 | $this->_lock_key = $lock_key; 337 | break; 338 | } 339 | while (++$attempt < 30); 340 | 341 | if ($attempt === 30) 342 | { 343 | log_message('error', 'Session: Unable to obtain lock for '.$this->_key_prefix.$session_id.' after 30 attempts, aborting.'); 344 | return FALSE; 345 | } 346 | elseif ($ttl === -1) 347 | { 348 | log_message('debug', 'Session: Lock for '.$this->_key_prefix.$session_id.' had no TTL, overriding.'); 349 | } 350 | 351 | $this->_lock = TRUE; 352 | return TRUE; 353 | } 354 | 355 | // ------------------------------------------------------------------------ 356 | 357 | /** 358 | * Release lock 359 | * 360 | * Releases a previously acquired lock 361 | * 362 | * @return bool 363 | */ 364 | protected function _release_lock() 365 | { 366 | if (isset($this->_redis, $this->_lock_key) && $this->_lock) 367 | { 368 | if ( ! $this->_redis->delete($this->_lock_key)) 369 | { 370 | log_message('error', 'Session: Error while trying to free lock for '.$this->_lock_key); 371 | return FALSE; 372 | } 373 | 374 | $this->_lock_key = NULL; 375 | $this->_lock = FALSE; 376 | } 377 | 378 | return TRUE; 379 | } 380 | 381 | } 382 | -------------------------------------------------------------------------------- /README/Session/drivers/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |Directory access is forbidden.
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /README/Session/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |Directory access is forbidden.
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /README/Table.php: -------------------------------------------------------------------------------- 1 | $val) 119 | { 120 | $this->template[$key] = $val; 121 | } 122 | 123 | log_message('info', 'Table Class Initialized'); 124 | } 125 | 126 | // -------------------------------------------------------------------- 127 | 128 | /** 129 | * Set the template 130 | * 131 | * @param array $template 132 | * @return bool 133 | */ 134 | public function set_template($template) 135 | { 136 | if ( ! is_array($template)) 137 | { 138 | return FALSE; 139 | } 140 | 141 | $this->template = $template; 142 | return TRUE; 143 | } 144 | 145 | // -------------------------------------------------------------------- 146 | 147 | /** 148 | * Set the table heading 149 | * 150 | * Can be passed as an array or discreet params 151 | * 152 | * @param mixed 153 | * @return CI_Table 154 | */ 155 | public function set_heading($args = array()) 156 | { 157 | $this->heading = $this->_prep_args(func_get_args()); 158 | return $this; 159 | } 160 | 161 | // -------------------------------------------------------------------- 162 | 163 | /** 164 | * Set columns. Takes a one-dimensional array as input and creates 165 | * a multi-dimensional array with a depth equal to the number of 166 | * columns. This allows a single array with many elements to be 167 | * displayed in a table that has a fixed column count. 168 | * 169 | * @param array $array 170 | * @param int $col_limit 171 | * @return array 172 | */ 173 | public function make_columns($array = array(), $col_limit = 0) 174 | { 175 | if ( ! is_array($array) OR count($array) === 0 OR ! is_int($col_limit)) 176 | { 177 | return FALSE; 178 | } 179 | 180 | // Turn off the auto-heading feature since it's doubtful we 181 | // will want headings from a one-dimensional array 182 | $this->auto_heading = FALSE; 183 | 184 | if ($col_limit === 0) 185 | { 186 | return $array; 187 | } 188 | 189 | $new = array(); 190 | do 191 | { 192 | $temp = array_splice($array, 0, $col_limit); 193 | 194 | if (count($temp) < $col_limit) 195 | { 196 | for ($i = count($temp); $i < $col_limit; $i++) 197 | { 198 | $temp[] = ' '; 199 | } 200 | } 201 | 202 | $new[] = $temp; 203 | } 204 | while (count($array) > 0); 205 | 206 | return $new; 207 | } 208 | 209 | // -------------------------------------------------------------------- 210 | 211 | /** 212 | * Set "empty" cells 213 | * 214 | * Can be passed as an array or discreet params 215 | * 216 | * @param mixed $value 217 | * @return CI_Table 218 | */ 219 | public function set_empty($value) 220 | { 221 | $this->empty_cells = $value; 222 | return $this; 223 | } 224 | 225 | // -------------------------------------------------------------------- 226 | 227 | /** 228 | * Add a table row 229 | * 230 | * Can be passed as an array or discreet params 231 | * 232 | * @param mixed 233 | * @return CI_Table 234 | */ 235 | public function add_row($args = array()) 236 | { 237 | $this->rows[] = $this->_prep_args(func_get_args()); 238 | return $this; 239 | } 240 | 241 | // -------------------------------------------------------------------- 242 | 243 | /** 244 | * Prep Args 245 | * 246 | * Ensures a standard associative array format for all cell data 247 | * 248 | * @param array 249 | * @return array 250 | */ 251 | protected function _prep_args($args) 252 | { 253 | // If there is no $args[0], skip this and treat as an associative array 254 | // This can happen if there is only a single key, for example this is passed to table->generate 255 | // array(array('foo'=>'bar')) 256 | if (isset($args[0]) && count($args) === 1 && is_array($args[0]) && ! isset($args[0]['data'])) 257 | { 258 | $args = $args[0]; 259 | } 260 | 261 | foreach ($args as $key => $val) 262 | { 263 | is_array($val) OR $args[$key] = array('data' => $val); 264 | } 265 | 266 | return $args; 267 | } 268 | 269 | // -------------------------------------------------------------------- 270 | 271 | /** 272 | * Add a table caption 273 | * 274 | * @param string $caption 275 | * @return CI_Table 276 | */ 277 | public function set_caption($caption) 278 | { 279 | $this->caption = $caption; 280 | } 281 | 282 | // -------------------------------------------------------------------- 283 | 284 | /** 285 | * Generate the table 286 | * 287 | * @param mixed $table_data 288 | * @return string 289 | */ 290 | public function generate($table_data = NULL) 291 | { 292 | // The table data can optionally be passed to this function 293 | // either as a database result object or an array 294 | if ( ! empty($table_data)) 295 | { 296 | if ($table_data instanceof CI_DB_result) 297 | { 298 | $this->_set_from_db_result($table_data); 299 | } 300 | elseif (is_array($table_data)) 301 | { 302 | $this->_set_from_array($table_data); 303 | } 304 | } 305 | 306 | // Is there anything to display? No? Smite them! 307 | if (empty($this->heading) && empty($this->rows)) 308 | { 309 | return 'Undefined table data'; 310 | } 311 | 312 | // Compile and validate the template date 313 | $this->_compile_template(); 314 | 315 | // Validate a possibly existing custom cell manipulation function 316 | if (isset($this->function) && ! is_callable($this->function)) 317 | { 318 | $this->function = NULL; 319 | } 320 | 321 | // Build the table! 322 | 323 | $out = $this->template['table_open'].$this->newline; 324 | 325 | // Add any caption here 326 | if ($this->caption) 327 | { 328 | $out .= '', 518 | 'heading_cell_end' => ' | ', 519 | 520 | 'tbody_open' => '', 521 | 'tbody_close' => '', 522 | 523 | 'row_start' => '
---|
', 526 | 'cell_end' => ' | ', 527 | 528 | 'row_alt_start' => '
', 531 | 'cell_alt_end' => ' | ', 532 | 533 | 'table_close' => '
', $close = '
') 552 | { 553 | return (count($this->error_msg) > 0) ? $open.implode($close.$open, $this->error_msg).$close : ''; 554 | } 555 | 556 | } 557 | -------------------------------------------------------------------------------- /README/Typography.php: -------------------------------------------------------------------------------- 1 | tags 53 | * 54 | * @var string 55 | */ 56 | public $block_elements = 'address|blockquote|div|dl|fieldset|form|h\d|hr|noscript|object|ol|p|pre|script|table|ul'; 57 | 58 | /** 59 | * Elements that should not have and
tags within them.
60 | *
61 | * @var string
62 | */
63 | public $skip_elements = 'p|pre|ol|ul|dl|object|table|h\d';
64 |
65 | /**
66 | * Tags we want the parser to completely ignore when splitting the string.
67 | *
68 | * @var string
69 | */
70 | public $inline_elements = 'a|abbr|acronym|b|bdo|big|br|button|cite|code|del|dfn|em|i|img|ins|input|label|map|kbd|q|samp|select|small|span|strong|sub|sup|textarea|tt|var';
71 |
72 | /**
73 | * array of block level elements that require inner content to be within another block level element
74 | *
75 | * @var array
76 | */
77 | public $inner_block_required = array('blockquote');
78 |
79 | /**
80 | * the last block element parsed
81 | *
82 | * @var string
83 | */
84 | public $last_block_element = '';
85 |
86 | /**
87 | * whether or not to protect quotes within { curly braces }
88 | *
89 | * @var bool
90 | */
91 | public $protect_braced_quotes = FALSE;
92 |
93 | /**
94 | * Auto Typography
95 | *
96 | * This function converts text, making it typographically correct:
97 | * - Converts double spaces into paragraphs.
98 | * - Converts single line breaks into
tags
99 | * - Converts single and double quotes into correctly facing curly quote entities.
100 | * - Converts three dots into ellipsis.
101 | * - Converts double dashes into em-dashes.
102 | * - Converts two spaces into entities
103 | *
104 | * @param string
105 | * @param bool whether to reduce more then two consecutive newlines to two
106 | * @return string
107 | */
108 | public function auto_typography($str, $reduce_linebreaks = FALSE)
109 | {
110 | if ($str === '')
111 | {
112 | return '';
113 | }
114 |
115 | // Standardize Newlines to make matching easier
116 | if (strpos($str, "\r") !== FALSE)
117 | {
118 | $str = str_replace(array("\r\n", "\r"), "\n", $str);
119 | }
120 |
121 | // Reduce line breaks. If there are more than two consecutive linebreaks
122 | // we'll compress them down to a maximum of two since there's no benefit to more.
123 | if ($reduce_linebreaks === TRUE)
124 | {
125 | $str = preg_replace("/\n\n+/", "\n\n", $str);
126 | }
127 |
128 | // HTML comment tags don't conform to patterns of normal tags, so pull them out separately, only if needed
129 | $html_comments = array();
130 | if (strpos($str, '