Completed
Pull Request — master (#1233)
by
unknown
02:44
created

_getRequestSourceUrl()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 12
rs 9.2
cc 4
eloc 7
nc 4
nop 0
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_EsiController extends Mage_Core_Controller_Front_Action {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
23
    /**
24
     * It seems this has to exist so we just make it redirect to the base URL
25
     * for lack of anything better to do.
26
     *
27
     * @return null
28
     */
29
    public function indexAction() {
30
        $this->getResponse()->setRedirect(Mage::getBaseUrl());
31
    }
32
33
    /**
34
     * Spit out the form key for this session
35
     *
36
     * @return null
37
     */
38
    public function getFormKeyAction() {
39
        $resp = $this->getResponse();
40
        $resp->setBody(
41
            Mage::getSingleton('core/session')->real_getFormKey() );
42
        $resp->setHeader('X-Turpentine-Cache', '1');
43
        $resp->setHeader('X-Turpentine-Flush-Events',
44
            implode(',', Mage::helper('turpentine/esi')
45
                ->getDefaultCacheClearEvents()));
46
        $resp->setHeader('X-Turpentine-Block', 'form_key');
47
        Mage::register('turpentine_nocache_flag', false, true);
48
49
        Mage::helper('turpentine/debug')->logDebug('Generated form_key: %s',
50
            $resp->getBody());
51
    }
52
53
    /**
54
     * Spit out the rendered block from the URL-encoded data
55
     *
56
     * @return null
57
     */
58
    public function getBlockAction() {
59
        $resp = $this->getResponse();
60
        $cacheFlag = false;
61
        if (Mage::helper('turpentine/esi')->shouldResponseUseEsi()) {
62
            $req = $this->getRequest();
63
            $esiHelper = Mage::helper('turpentine/esi');
64
            $dataHelper = Mage::helper('turpentine/data');
65
            $debugHelper = Mage::helper('turpentine/debug');
66
            $esiDataHmac = $req->getParam($esiHelper->getEsiHmacParam());
67
            $esiDataParamValue = $req->getParam($esiHelper->getEsiDataParam());
68
            if ($esiDataHmac !== ($hmac = $dataHelper->getHmac($esiDataParamValue))) {
69
                $debugHelper->logWarn('ESI data HMAC mismatch, expected (%s) but received (%s)',
70
                    $hmac, $esiDataHmac);
71
                $resp->setHttpResponseCode(500);
72
                $resp->setBody('ESI data is not valid');
73
            } elseif ( ! ($esiDataArray = $dataHelper->thaw($esiDataParamValue))) {
74
                $debugHelper->logWarn('Invalid ESI data in URL: %s',
75
                    $esiDataParamValue);
76
                $resp->setHttpResponseCode(500);
77
                $resp->setBody('ESI data is not valid');
78
            } elseif ( ! $esiHelper->getEsiDebugEnabled() &&
79
                    $esiDataArray['esi_method'] !==
80
                    $req->getParam($esiHelper->getEsiMethodParam())) {
81
                $resp->setHttpResponseCode(403);
82
                $resp->setBody('ESI method mismatch');
83
                $debugHelper->logWarn('Blocking change of ESI method: %s -> %s',
84
                    $esiDataArray['esi_method'],
85
                    $req->getParam($esiHelper->getEsiMethodParam()));
86
            } else {
87
                $esiData = new Varien_Object($esiDataArray);
88
                $origRequest = Mage::app()->getRequest();
89
                Mage::app()->setCurrentStore(
90
                    Mage::app()->getStore($esiData->getStoreId()) );
91
                $appShim = Mage::getModel('turpentine/shim_mage_core_app');
92
                $dummyRequest = Mage::helper('turpentine/esi')->getDummyRequest($this->_getRequestSourceUrl());
93
                $appShim->shim_setRequest($dummyRequest);
94
                $block = $this->_getEsiBlock($esiData);
95
                if ($block) {
96
                    $blockEsiOptions = $block->getEsiOptions();
97
                    $block->setEsiOptions(false);
98
                    $resp->setBody($block->toHtml());
99
                    if ((int) $req->getParam($esiHelper->getEsiTtlParam()) > 0) {
100
                        $cacheFlag = true;
101
                        if (isset($blockEsiOptions['only_cache_if'])) {
102
                            switch ($blockEsiOptions['only_cache_if']) {
103
                                case 'empty':
104
                                    $cacheFlag = ('' === $resp->getBody());
105
                                    break;
106
                                case 'no_text':
107
                                    $cacheFlag = ('' === trim(strip_tags($resp->getBody())));
108
                                    break;
109
                                default:
110
                                    $cacheFlag = false;
111
                            }
112
                        }
113
                    }
114
                    if ($esiData->getEsiMethod() == 'ajax') {
115
                        $resp->setHeader('Access-Control-Allow-Origin',
116
                            $esiHelper->getCorsOrigin());
117
                    }
118
                    if ( ! is_null($flushEvents = $esiData->getFlushEvents())) {
119
                        $resp->setHeader('X-Turpentine-Flush-Events',
120
                            implode(',', $flushEvents));
121
                    }
122
                    if ($esiHelper->getEsiDebugEnabled()) {
123
                        $resp->setHeader('X-Turpentine-Block',
124
                            $block->getNameInLayout());
125
                    }
126
                } else {
127
                    $resp->setHttpResponseCode(404);
128
                    $resp->setBody('ESI block not found');
129
                }
130
                $appShim->shim_setRequest($origRequest);
131
            }
132
        } else {
133
            $resp->setHttpResponseCode(403);
134
            $resp->setBody('ESI includes are not enabled');
135
        }
136
        Mage::register('turpentine_nocache_flag', ! $cacheFlag, true);
137
    }
138
139
    /**
140
     * Need to disable this flag to prevent setting the last URL but we
141
     * don't want to completely break sessions.
142
     *
143
     * see Mage_Core_Controller_Front_Action::postDispatch
144
     *
145
     * @return null
146
     */
147
    public function postDispatch() {
148
        $flag = $this->getFlag('', self::FLAG_NO_START_SESSION);
149
        $this->setFlag('', self::FLAG_NO_START_SESSION, true);
150
        parent::postDispatch();
151
        $this->setFlag('', self::FLAG_NO_START_SESSION, $flag);
152
    }
153
154
    /**
155
     * Generate the ESI block
156
     *
157
     * @param  Varien_Object $esiData
158
     * @return Mage_Core_Block_Template|null
159
     */
160
    protected function _getEsiBlock($esiData) {
161
        $block = null;
0 ignored issues
show
Unused Code introduced by
$block is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
162
        Varien_Profiler::start('turpentine::controller::esi::_getEsiBlock');
163
        foreach ($esiData->getSimpleRegistry() as $key => $value) {
164
            Mage::register($key, $value, true);
165
        }
166
        foreach ($esiData->getComplexRegistry() as $key => $data) {
167
            $value = Mage::getModel($data['model']);
168
            if ( ! is_object($value)) {
169
                Mage::helper('turpentine/debug')->logWarn(
170
                    'Failed to register key/model: %s as %s(%s)',
171
                    $key, $data['model'], $data['id'] );
172
                continue;
173
            } else {
174
                $value->load($data['id']);
175
                Mage::register($key, $value, true);
176
            }
177
        }
178
        $layout = Mage::getSingleton('core/layout');
179
180
        // dispatch event for adding handles to layout update
181
        Mage::dispatchEvent(
182
            'controller_action_layout_load_before',
183
            array('action'=>$this, 'layout'=>$layout)
184
        );
185
186
        $layoutUpdate = $layout->getUpdate();
187
        $layoutUpdate->load($this->_swapCustomerHandles(
188
            $esiData->getLayoutHandles() ));
189
        foreach ($esiData->getDummyBlocks() as $blockName) {
190
            $layout->createBlock('Mage_Core_Block_Template', $blockName);
191
        }
192
193
        if ( ! $this->getFlag('', self::FLAG_NO_DISPATCH_BLOCK_EVENT)) {
194
            Mage::dispatchEvent(
195
                'controller_action_layout_generate_xml_before',
196
                array('action'=>$this, 'layout'=>$layout)
197
            );
198
        }
199
        $layout->generateXml();
200
201
        /** @var Nexcessnet_Turpentine_Helper_Data $turpentineHelper */
202
        $turpentineHelper = Mage::helper('turpentine/data')
203
            ->setLayout($layout);
204
205
        $blockNode = current($layout->getNode()->xpath(
206
                sprintf('//block[@name=\'%s\']', $esiData->getNameInLayout())
207
            ));
208
209
        if ( ! ($blockNode instanceof Mage_Core_Model_Layout_Element)) {
0 ignored issues
show
Bug introduced by
The class Mage_Core_Model_Layout_Element does not exist. Did you forget a USE statement, or did you not list all dependencies?

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 the composer.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 or require-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 ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
210
            Mage::helper('turpentine/debug')->logWarn(
211
                            'No block node found with @name="%s"',
212
                            $esiData->getNameInLayout() );
213
            return null;
214
        }
215
216
        $nodesToGenerate = $turpentineHelper->getChildBlockNames($blockNode);
217
        Mage::getModel('turpentine/shim_mage_core_layout')
218
            ->shim_generateFullBlock($blockNode);
219
220
        //find addional blocks that aren't defined in the <block/> but via <reference name="%s">
221
        $referenceNodes = $layout->getNode()->xpath(sprintf(
222
            '//reference[@name=\'%s\']',
223
            $esiData->getNameInLayout() ));
224
        if ($referenceNodes) {
225
            foreach ($referenceNodes as $referenceNode) {
226
                if ($referenceNode instanceof Mage_Core_Model_Layout_Element) {
0 ignored issues
show
Bug introduced by
The class Mage_Core_Model_Layout_Element does not exist. Did you forget a USE statement, or did you not list all dependencies?

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 the composer.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 or require-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 ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
227
                    $referencesToGenerate = $turpentineHelper
228
                        ->getChildBlockNames($referenceNode);
229
                    $nodesToGenerate =
230
                        array_merge($nodesToGenerate, $referencesToGenerate);
231
                }
232
            }
233
        }
234
235
        // dispatch event for adding xml layout elements
236
        if ( ! $this->getFlag('', self::FLAG_NO_DISPATCH_BLOCK_EVENT)) {
237
            Mage::dispatchEvent(
238
                'controller_action_layout_generate_blocks_before',
239
                array('action'=>$this, 'layout'=>$layout)
240
            );
241
        }
242
243
        foreach (array_unique($nodesToGenerate) as $nodeName) {
244
            foreach ($layout->getNode()->xpath(sprintf(
245
                    '//reference[@name=\'%s\']', $nodeName )) as $node) {
246
                $layout->generateBlocks($node);
247
            }
248
        }
249
        $block = $layout->getBlock($esiData->getNameInLayout());
250
251
        if ( ! $this->getFlag('', self::FLAG_NO_DISPATCH_BLOCK_EVENT)) {
252
            Mage::dispatchEvent(
253
                'controller_action_layout_generate_blocks_after',
254
                array('action'=>$this, 'layout'=>$layout)
255
            );
256
        }
257
258
        $this->_isLayoutLoaded = true;
259
        Varien_Profiler::stop('turpentine::controller::esi::_getEsiBlock');
260
        return $block;
261
    }
262
263
    /**
264
     * Swap customer_logged_in and customer_logged_out cached handles based on
265
     * actual customer login status. Fixes stale Login/Logout links (and
266
     * probably other things).
267
     *
268
     * This is definitely a hack, need a more general solution to this problem.
269
     *
270
     * @param  array $handles
271
     * @return array
272
     */
273
    protected function _swapCustomerHandles($handles) {
274
        if (Mage::helper('customer')->isLoggedIn()) {
275
            $replacement = array('customer_logged_out', 'customer_logged_in');
276
        } else {
277
            $replacement = array('customer_logged_in', 'customer_logged_out');
278
        }
279
        if (($pos = array_search($replacement[0], $handles)) !== false) {
280
            $handles[$pos] = $replacement[1];
281
        }
282
        return $handles;
283
    }
284
    /**
285
     * Only rely on r64-param for DummyRequest-Url.
286
     *
287
     * Don't use the referrer (which may be bad encoded) as the DummyRequest-Url. So it is empty for any global
288
     * ESI-Block, as they shouldn't depend on the current url.
289
     *
290
     * @return string
291
     */
292
    protected function _getRequestSourceUrl()
293
    {
294
        $requestUrl = null;
295
        if ($url = $this->getRequest()->getParam(self::PARAM_NAME_BASE64_URL)) {
296
            $requestUrl = Mage::helper('core')->urlDecodeAndEscape($url);
297
        }
298
299
        if ($requestUrl && !$this->_isUrlInternal($requestUrl)) {
300
            $requestUrl = Mage::app()->getStore()->getBaseUrl();
301
        }
302
        return $requestUrl;
303
    }
304
}
305