1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Nexcess.net Turpentine Extension for Magento |
5
|
|
|
* Copyright (C) 2012 Nexcess.net L.L.C. |
6
|
|
|
* |
7
|
|
|
* This program is free software; you can redistribute it and/or modify |
8
|
|
|
* it under the terms of the GNU General Public License as published by |
9
|
|
|
* the Free Software Foundation; either version 2 of the License, or |
10
|
|
|
* (at your option) any later version. |
11
|
|
|
* |
12
|
|
|
* This program is distributed in the hope that it will be useful, |
13
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
14
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15
|
|
|
* GNU General Public License for more details. |
16
|
|
|
* |
17
|
|
|
* You should have received a copy of the GNU General Public License along |
18
|
|
|
* with this program; if not, write to the Free Software Foundation, Inc., |
19
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
20
|
|
|
*/ |
21
|
|
|
|
22
|
|
|
class Nexcessnet_Turpentine_Helper_Data extends Mage_Core_Helper_Abstract { |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* Contains a newly generated v4 uuid whenever read, possibly not available |
26
|
|
|
* on all kernels |
27
|
|
|
*/ |
28
|
|
|
const UUID_SOURCE = '/proc/sys/kernel/random/uuid'; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* Compression level for serialization compression |
32
|
|
|
* |
33
|
|
|
* Testing showed no significant (size) difference between levels 1 and 9 |
34
|
|
|
* so using 1 since it's faster |
35
|
|
|
*/ |
36
|
|
|
const COMPRESSION_LEVEL = 1; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* Hash algorithm to use in various cryptographic methods |
40
|
|
|
*/ |
41
|
|
|
const HASH_ALGORITHM = 'sha256'; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* Cookie name for the Varnish bypass |
45
|
|
|
* |
46
|
|
|
* @var string |
47
|
|
|
*/ |
48
|
|
|
const BYPASS_COOKIE_NAME = 'varnish_bypass'; |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* encryption singleton thing |
52
|
|
|
* |
53
|
|
|
* @var Mage_Core_Model_Encryption |
54
|
|
|
*/ |
55
|
|
|
protected $_crypt = null; |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* Like built-in explode() but applies trim to each exploded element and |
59
|
|
|
* filters out empty elements from result |
60
|
|
|
* |
61
|
|
|
* @param string $token [description] |
62
|
|
|
* @param string $data [description] |
63
|
|
|
* @return array |
64
|
|
|
*/ |
65
|
|
|
public function cleanExplode($token, $data) { |
66
|
|
|
return array_filter(array_map('trim', |
67
|
|
|
explode($token, trim($data)))); |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* Generate a v4 UUID |
72
|
|
|
* |
73
|
|
|
* @return string |
74
|
|
|
*/ |
75
|
|
|
public function generateUuid() { |
76
|
|
|
if (is_readable(self::UUID_SOURCE)) { |
77
|
|
|
$uuid = trim(file_get_contents(self::UUID_SOURCE)); |
78
|
|
|
} elseif (function_exists('mt_rand')) { |
79
|
|
|
/** |
80
|
|
|
* Taken from stackoverflow answer, possibly not the fastest or |
81
|
|
|
* strictly standards compliant |
82
|
|
|
* @link http://stackoverflow.com/a/2040279 |
83
|
|
|
*/ |
84
|
|
|
$uuid = sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', |
85
|
|
|
// 32 bits for "time_low" |
86
|
|
|
mt_rand(0, 0xffff), mt_rand(0, 0xffff), |
87
|
|
|
|
88
|
|
|
// 16 bits for "time_mid" |
89
|
|
|
mt_rand(0, 0xffff), |
90
|
|
|
|
91
|
|
|
// 16 bits for "time_hi_and_version", |
92
|
|
|
// four most significant bits holds version number 4 |
93
|
|
|
mt_rand(0, 0x0fff) | 0x4000, |
94
|
|
|
|
95
|
|
|
// 16 bits, 8 bits for "clk_seq_hi_res", |
96
|
|
|
// 8 bits for "clk_seq_low", |
97
|
|
|
// two most significant bits holds zero and one for variant DCE1.1 |
98
|
|
|
mt_rand(0, 0x3fff) | 0x8000, |
99
|
|
|
|
100
|
|
|
// 48 bits for "node" |
101
|
|
|
mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff) |
102
|
|
|
); |
103
|
|
|
} else { |
104
|
|
|
// chosen by dice roll, guaranteed to be random |
105
|
|
|
$uuid = '4'; |
106
|
|
|
} |
107
|
|
|
return $uuid; |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* Get the Turpentine version |
112
|
|
|
* |
113
|
|
|
* @return string |
114
|
|
|
*/ |
115
|
|
|
public function getVersion() { |
116
|
|
|
return Mage::getConfig() |
117
|
|
|
->getModuleConfig('Nexcessnet_Turpentine')->version; |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
/** |
121
|
|
|
* Base64 encode a string |
122
|
|
|
* |
123
|
|
|
* NOTE this changes the last 2 characters to be friendly to URLs |
124
|
|
|
* / => . |
125
|
|
|
* + => - |
126
|
|
|
* |
127
|
|
|
* @param string $str |
128
|
|
|
* @return string |
129
|
|
|
*/ |
130
|
|
|
public function urlBase64Encode($str) { |
131
|
|
|
return str_replace( |
132
|
|
|
array('/', '+'), |
133
|
|
|
array('_', '-'), |
134
|
|
|
base64_encode($str) ); |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
/** |
138
|
|
|
* Base64 decode a string, counterpart to urlBase64Encode |
139
|
|
|
* |
140
|
|
|
* @param string $str |
141
|
|
|
* @return string |
142
|
|
|
*/ |
143
|
|
|
public function urlBase64Decode($str) { |
144
|
|
|
return base64_decode( |
145
|
|
|
str_replace( |
146
|
|
|
array('_', '-'), |
147
|
|
|
array('/', '+'), |
148
|
|
|
$str ) ); |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
/** |
152
|
|
|
* Serialize a variable into a string that can be used in a URL |
153
|
|
|
* |
154
|
|
|
* Using gzdeflate to avoid the checksum/metadata overhead in gzencode and |
155
|
|
|
* gzcompress |
156
|
|
|
* |
157
|
|
|
* @param mixed $data |
158
|
|
|
* @return string |
159
|
|
|
*/ |
160
|
|
|
public function freeze($data) { |
161
|
|
|
Varien_Profiler::start('turpentine::helper::data::freeze'); |
162
|
|
|
$frozenData = $this->urlBase64Encode( |
163
|
|
|
$this->_getCrypt()->encrypt( |
164
|
|
|
gzdeflate( |
165
|
|
|
serialize($data), |
166
|
|
|
self::COMPRESSION_LEVEL ) ) ); |
167
|
|
|
Varien_Profiler::stop('turpentine::helper::data::freeze'); |
168
|
|
|
return $frozenData; |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
/** |
172
|
|
|
* Unserialize data |
173
|
|
|
* |
174
|
|
|
* @param string $data |
175
|
|
|
* @return mixed |
176
|
|
|
*/ |
177
|
|
|
public function thaw($data) { |
178
|
|
|
Varien_Profiler::start('turpentine::helper::data::thaw'); |
179
|
|
|
$thawedData = unserialize( |
180
|
|
|
gzinflate( |
181
|
|
|
$this->_getCrypt()->decrypt( |
182
|
|
|
$this->urlBase64Decode($data) ) ) ); |
183
|
|
|
Varien_Profiler::stop('turpentine::helper::data::thaw'); |
184
|
|
|
return $thawedData; |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
/** |
188
|
|
|
* Get SHA256 hash of a string, salted with encryption key |
189
|
|
|
* |
190
|
|
|
* @param string $data |
191
|
|
|
* @return string |
192
|
|
|
*/ |
193
|
|
|
public function secureHash($data) { |
194
|
|
|
$salt = $this->_getCryptKey(); |
195
|
|
|
return hash(self::HASH_ALGORITHM, sprintf('%s:%s', $salt, $data)); |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
/** |
199
|
|
|
* Get the HMAC hash for given data |
200
|
|
|
* |
201
|
|
|
* @param string $data |
202
|
|
|
* @return string |
203
|
|
|
*/ |
204
|
|
|
public function getHmac($data) { |
205
|
|
|
return hash_hmac(self::HASH_ALGORITHM, $data, $this->_getCryptKey()); |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* Hash a cache key the same way blocks do |
210
|
|
|
* |
211
|
|
|
* @param array $key |
212
|
|
|
* @return string |
213
|
|
|
*/ |
214
|
|
|
public function getCacheKeyHash($key) { |
215
|
|
|
return sha1(implode('|', array_values($key))); |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
/** |
219
|
|
|
* Get a list of child blocks inside the given block |
220
|
|
|
* |
221
|
|
|
* @param Mage_Core_Model_Layout_Element $blockNode |
222
|
|
|
* @return array |
223
|
|
|
*/ |
224
|
|
|
public function getChildBlockNames($blockNode) { |
225
|
|
|
return array_unique($this->_getChildBlockNames($blockNode)); |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
/** |
229
|
|
|
* Get the getModel formatted name of a model classname or object |
230
|
|
|
* |
231
|
|
|
* @param string|object $model |
232
|
|
|
* @return string |
233
|
|
|
*/ |
234
|
|
|
public function getModelName($model) { |
235
|
|
|
if (is_object($model)) { |
236
|
|
|
$model = get_class($model); |
237
|
|
|
} |
238
|
|
|
// This guess may work if the extension uses its lowercased name as model group name. |
239
|
|
|
$result = strtolower(preg_replace( |
240
|
|
|
'~^[^_]+_([^_]+)_Model_(.+)$~', '$1/$2', $model )); |
241
|
|
|
// This check is not expensive because the answer should come from Magento's classNameCache |
242
|
|
|
$checkModel = Mage::getConfig()->getModelClassName($result); |
243
|
|
|
if ('Mage_' == substr($checkModel, 0, 5) && ! class_exists($result)) { |
244
|
|
|
// Fallback to full model name. |
245
|
|
|
$result = $model; |
246
|
|
|
} |
247
|
|
|
return $result; |
248
|
|
|
} |
249
|
|
|
|
250
|
|
|
/** |
251
|
|
|
* Check config to see if Turpentine should handle the flash messages |
252
|
|
|
* |
253
|
|
|
* @return bool |
254
|
|
|
*/ |
255
|
|
|
public function useFlashMessagesFix() { |
256
|
|
|
return (bool) Mage::getStoreConfig( |
257
|
|
|
'turpentine_varnish/general/ajax_messages' ); |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
/** |
261
|
|
|
* Check config to see if Turpentine should apply the product list toolbar |
262
|
|
|
* fix |
263
|
|
|
* |
264
|
|
|
* @return bool |
265
|
|
|
*/ |
266
|
|
|
public function useProductListToolbarFix() { |
267
|
|
|
return (bool) Mage::getStoreConfig( |
268
|
|
|
'turpentine_varnish/general/fix_product_toolbar' ); |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
/** |
272
|
|
|
* Check if Turpentine should apply the new VCL on config changes |
273
|
|
|
* |
274
|
|
|
* @return bool |
275
|
|
|
*/ |
276
|
|
|
public function getAutoApplyOnSave() { |
277
|
|
|
return (bool) Mage::getStoreConfig( |
278
|
|
|
'turpentine_varnish/general/auto_apply_on_save' ); |
279
|
|
|
} |
280
|
|
|
|
281
|
|
|
/** |
282
|
|
|
* Get config value specifying when to strip VCL whitespaces |
283
|
|
|
* |
284
|
|
|
* @return string |
285
|
|
|
*/ |
286
|
|
|
public function getVclFix() { |
287
|
|
|
return Mage::getStoreConfig( |
288
|
|
|
'turpentine_varnish/general/vcl_fix' ); |
289
|
|
|
} |
290
|
|
|
|
291
|
|
|
/** |
292
|
|
|
* Get config value specifying when to strip VCL whitespaces |
293
|
|
|
* |
294
|
|
|
* @return string |
295
|
|
|
*/ |
296
|
|
|
public function getStripVclWhitespace() { |
297
|
|
|
return Mage::getStoreConfig( |
298
|
|
|
'turpentine_varnish/general/strip_vcl_whitespace' ); |
299
|
|
|
} |
300
|
|
|
|
301
|
|
|
/** |
302
|
|
|
* Check if VCL whitespaces should be stripped for the given action |
303
|
|
|
* |
304
|
|
|
* @param string $action can be either "apply", "save" or "download" |
305
|
|
|
* @return bool |
306
|
|
|
*/ |
307
|
|
|
public function shouldStripVclWhitespace($action) { |
308
|
|
|
$configValue = $this->getStripVclWhitespace(); |
309
|
|
|
if ($configValue === 'always') { |
310
|
|
|
return true; |
311
|
|
|
} elseif ($configValue === 'apply' && $action === 'apply') { |
312
|
|
|
return true; |
313
|
|
|
} |
314
|
|
|
return false; |
315
|
|
|
} |
316
|
|
|
|
317
|
|
|
/** |
318
|
|
|
* Get the cookie name for the Varnish bypass |
319
|
|
|
* |
320
|
|
|
* @return string |
321
|
|
|
*/ |
322
|
|
|
public function getBypassCookieName() { |
323
|
|
|
return self::BYPASS_COOKIE_NAME; |
324
|
|
|
} |
325
|
|
|
|
326
|
|
|
/** |
327
|
|
|
* The actual recursive implementation of getChildBlockNames |
328
|
|
|
* |
329
|
|
|
* @param Mage_Core_Model_Layout_Element $blockNode |
330
|
|
|
* @return array |
331
|
|
|
*/ |
332
|
|
|
protected function _getChildBlockNames($blockNode) { |
333
|
|
|
Varien_Profiler::start('turpentine::helper::data::_getChildBlockNames'); |
334
|
|
|
if ($blockNode instanceof Mage_Core_Model_Layout_Element) { |
|
|
|
|
335
|
|
|
$blockNames = array((string) $blockNode['name']); |
336
|
|
|
foreach ($blockNode->xpath('./block | ./reference') as $childBlockNode) { |
337
|
|
|
$blockNames = array_merge($blockNames, |
338
|
|
|
$this->_getChildBlockNames($childBlockNode)); |
339
|
|
|
if ($this->getLayout() instanceof Varien_Simplexml_Config) { |
|
|
|
|
340
|
|
|
foreach ($this->getLayout()->getNode()->xpath(sprintf( |
341
|
|
|
'//reference[@name=\'%s\']', (string) $childBlockNode['name'] )) |
342
|
|
|
as $childBlockLayoutNode) { |
343
|
|
|
$blockNames = array_merge($blockNames, |
344
|
|
|
$this->_getChildBlockNames($childBlockLayoutNode)); |
345
|
|
|
|
346
|
|
|
} |
347
|
|
|
} |
348
|
|
|
} |
349
|
|
|
} else { |
350
|
|
|
$blockNames = array(); |
351
|
|
|
} |
352
|
|
|
Varien_Profiler::stop('turpentine::helper::data::_getChildBlockNames'); |
353
|
|
|
return $blockNames; |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
/** |
357
|
|
|
* Get encryption singleton thing |
358
|
|
|
* |
359
|
|
|
* Not using core/cryption because it auto-base64 encodes stuff which we |
360
|
|
|
* don't want in this case |
361
|
|
|
* |
362
|
|
|
* @return Mage_Core_Model_Encryption |
363
|
|
|
*/ |
364
|
|
|
protected function _getCrypt() { |
365
|
|
|
if (is_null($this->_crypt)) { |
366
|
|
|
$this->_crypt = Varien_Crypt::factory() |
367
|
|
|
->init($this->_getCryptKey()); |
368
|
|
|
} |
369
|
|
|
return $this->_crypt; |
370
|
|
|
} |
371
|
|
|
|
372
|
|
|
/** |
373
|
|
|
* Get Magento's encryption key |
374
|
|
|
* |
375
|
|
|
* @return string |
376
|
|
|
*/ |
377
|
|
|
protected function _getCryptKey() { |
378
|
|
|
return (string) Mage::getConfig()->getNode('global/crypt/key'); |
379
|
|
|
} |
380
|
|
|
} |
381
|
|
|
|
This error could be the result of:
1. Missing dependencies
PHP Analyzer uses your
composer.json
file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects thecomposer.json
to be in the root folder of your repository.Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the
require
orrequire-dev
section?2. Missing use statement
PHP does not complain about undefined classes in
ìnstanceof
checks. For example, the following PHP code will work perfectly fine:If you have not tested against this specific condition, such errors might go unnoticed.