Completed
Push — master ( 68c2d1...79d43d )
by Angus
02:33
created

CI_DB_Cache::get_folder_layers()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 11
nc 3
nop 1
dl 0
loc 17
ccs 0
cts 11
cp 0
crap 12
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * CodeIgniter
4
 *
5
 * An open source application development framework for PHP
6
 *
7
 * This content is released under the MIT License (MIT)
8
 *
9
 * Copyright (c) 2014 - 2016, British Columbia Institute of Technology
10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a copy
12
 * of this software and associated documentation files (the "Software"), to deal
13
 * in the Software without restriction, including without limitation the rights
14
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
 * copies of the Software, and to permit persons to whom the Software is
16
 * furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in
19
 * all copies or substantial portions of the Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27
 * THE SOFTWARE.
28
 *
29
 * @package      CodeIgniter
30
 * @author       EllisLab Dev Team
31
 * @copyright    Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
32
 * @copyright    Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/)
33
 * @license      http://opensource.org/licenses/MIT    MIT License
34
 * @link         https://codeigniter.com
35
 * @since        Version 1.0.0
36
 * @filesource
37
 */
38
defined('BASEPATH') OR exit('No direct script access allowed');
39
40
/**
41
 * Database Cache Class
42
 *
43
 * @category      Database
44
 * @author        EllisLab Dev Team
45
 * @link          https://codeigniter.com/user_guide/database/
46
 */
47
class CI_DB_Cache {
48
49
	/**
50
	 * CI Singleton
51
	 *
52
	 * @var    object
53
	 */
54
	public $CI;
55
56
	/**
57
	 * Database object
58
	 *
59
	 * Allows passing of DB object so that multiple database connections
60
	 * and returned DB objects can be supported.
61
	 *
62
	 * @var    object
63
	 */
64
	public $db;
65
66
	// --------------------------------------------------------------------
67
68
	/**
69
	 * Constructor
70
	 *
71
	 * @param    object &$db
72
	 *
73
	 * @return    void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
74
	 */
75
	public function __construct(&$db) {
76
		// Assign the main CI object to $this->CI and load the file helper since we use it a lot
77
		$this->CI =& get_instance();
78
		$this->db =& $db;
79
		$this->CI->load->helper('file');
80
81
		$this->check_path();
82
83
	}
84
85
	// --------------------------------------------------------------------
86
87
	/**
88
	 * Set Cache Directory Path
89
	 *
90
	 * @param    string $path Path to the cache directory
91
	 *
92
	 * @return    bool
93
	 */
94
	public function check_path($path = '') {
95
		if($path === '') {
96
			if($this->db->cachedir === '') {
97
				return $this->db->cache_off();
98
			}
99
100
			$path = $this->db->cachedir;
101
		}
102
103
		// Add a trailing slash to the path if needed
104
		$path = realpath($path)
105
			? rtrim(realpath($path), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR
106
			: rtrim($path, '/') . '/';
107
108
		if(!is_dir($path)) {
109
			log_message('debug', 'DB cache path error: ' . $path);
110
111
			// If the path is wrong we'll turn off caching
112
			return $this->db->cache_off();
113
		}
114
115
		if(!is_really_writable($path)) {
116
			log_message('debug', 'DB cache dir not writable: ' . $path);
117
118
			// If the path is not really writable we'll turn off caching
119
			return $this->db->cache_off();
120
		}
121
122
		$this->db->cachedir = $path;
123
124
		return TRUE;
125
	}
126
127
	// --------------------------------------------------------------------
128
129
	/**
130
	 * Retrieve a cached query
131
	 *
132
	 * The URI being requested will become the name of the cache sub-folder.
133
	 * An MD5 hash of the SQL statement will become the cache file name.
134
	 *
135
	 * @param    string $sql
136
	 *
137
	 * @return    string
138
	 */
139
	public function read($sql) {
140
		$segment_one = ($this->CI->uri->segment(1) == FALSE) ? 'default' : $this->CI->uri->segment(1);
141
		$segment_two = ($this->CI->uri->segment(2) == FALSE) ? 'index' : $this->CI->uri->segment(2);
142
143
144
		// BEGIN --- modification for supporting multi-level cache folders
145
146
		if(is_array($this->CI->config->item('multi_level_cache_folders'))) {
147
			$uri_md5  = md5($segment_one . '+' . $segment_two);
148
			$layer    = $this->get_folder_layers($uri_md5);
149
			$filepath = $this->db->cachedir . $segment_one.'/'.$segment_two . '/' . implode('/', $layer) . '/' . $uri_md5;
150
		} else {
151
			$filepath = $this->db->cachedir . $segment_one.'/'. $segment_two . '/' . md5($sql);
152
		}
153
154
		// END --- modification for supporting multi-level cache folders
155
156
		if(FALSE === ($cachedata = @file_get_contents($filepath))) {
157
			return FALSE;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return FALSE; (false) is incompatible with the return type documented by CI_DB_Cache::read of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
158
		}
159
160
		return unserialize($cachedata);
161
	}
162
163
	// --------------------------------------------------------------------
164
165
	/**
166
	 * Write a query to a cache file
167
	 *
168
	 * @param    string $sql
169
	 * @param    object $object
170
	 *
171
	 * @return    bool
172
	 */
173
	public function write($sql, $object) {
174
		$segment_one = ($this->CI->uri->segment(1) == FALSE) ? 'default' : $this->CI->uri->segment(1);
175
		$segment_two = ($this->CI->uri->segment(2) == FALSE) ? 'index' : $this->CI->uri->segment(2);
176
177
178
		// BEGIN --- modification for supporting multi-level cache folders
179
180
		if(is_array($this->CI->config->item('multi_level_cache_folders'))) {
181
			$uri_md5  = md5($segment_one . '+' . $segment_two);
182
			$layer    = $this->get_folder_layers($uri_md5);
183
			$dir_path = $this->db->cachedir . $segment_one.'/'.$segment_two . '/' . implode('/', $layer) . '/';
184
185
			if(!is_dir($dir_path)) {
186
				$old = umask(0);
187
				mkdir($dir_path, 0777, TRUE);
188
				umask($old);
189
			}
190
		} else {
191
			$dir_path = $this->db->cachedir . $segment_one.'/'. $segment_two . '/';
192
		}
193
194
		$filename = md5($sql);
195
196
		// END --- modification for supporting multi-level cache folders
197
198
		if(write_file($dir_path . $filename, serialize($object)) === FALSE) {
199
			return FALSE;
200
		}
201
202
		chmod($dir_path . $filename, 0640);
203
204
		return TRUE;
205
	}
206
207
	// --------------------------------------------------------------------
208
209
	/**
210
	 * Delete cache files within a particular directory
211
	 *
212
	 * @param    string $segment_one
213
	 * @param    string $segment_two
214
	 *
215
	 * @return    void
216
	 */
217
	public function delete($segment_one = '', $segment_two = '') {
218
		if($segment_one === '') {
219
			$segment_one = ($this->CI->uri->segment(1) == FALSE) ? 'default' : $this->CI->uri->segment(1);
220
		}
221
222
		if($segment_two === '') {
223
			$segment_two = ($this->CI->uri->segment(2) == FALSE) ? 'index' : $this->CI->uri->segment(2);
224
		}
225
226
		if(is_array($this->CI->config->item('multi_level_cache_folders'))) {
227
			$uri_md5  = md5($segment_one . '+' . $segment_two);
228
			$layer    = $this->get_folder_layers($uri_md5);
229
			$dir_path = $this->db->cachedir . $segment_one.'/'.$segment_two . '/' . implode('/', $layer) . '/';
230
		} else {
231
			$dir_path = $this->db->cachedir . $segment_one.'/'. $segment_two . '/';
232
		}
233
		delete_files($dir_path, TRUE);
234
	}
235
236
	// --------------------------------------------------------------------
237
238
	/**
239
	 * Delete all existing cache files
240
	 *
241
	 * @return    void
242
	 */
243
	public function delete_all() {
244
		delete_files($this->db->cachedir, TRUE, TRUE);
245
	}
246
247
	/**
248
	 * @param $md5
249
	 *
250
	 * @return array
251
	 */
252
	private function get_folder_layers($md5) {
253
		$layer      = array();
254
		$last_key   = 0;
255
		$last_value = 0;
256
257
		foreach($this->CI->config->item('multi_level_cache_folders') AS $key => $value) {
258
			if($key != 0) {
259
				$key = $last_key + $last_value;
260
			}
261
			$layer[] = substr($md5, $key, $value);
262
263
			$last_key   = (int) $key;
264
			$last_value = (int) $value;
265
		}
266
267
		return $layer;
268
	}
269
270
}
271