postDispatch()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 1
nc 1
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 {
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
                if ($referer = $this->_getRefererUrl()) {
93
                    $referer = htmlspecialchars_decode($referer);
94
                    $dummyRequest = Mage::helper('turpentine/esi')
95
                        ->getDummyRequest($referer);
96
                } else {
97
                    $dummyRequest = Mage::helper('turpentine/esi')
98
                        ->getDummyRequest();
99
                }
100
                $appShim->shim_setRequest($dummyRequest);
101
                $block = $this->_getEsiBlock($esiData);
102
                if ($block) {
103
                    $blockEsiOptions = $block->getEsiOptions();
104
                    $block->setEsiOptions(false);
105
                    $resp->setBody($block->toHtml());
106
                    if ((int) $req->getParam($esiHelper->getEsiTtlParam()) > 0) {
107
                        $cacheFlag = true;
108
                        if (isset($blockEsiOptions['only_cache_if'])) {
109
                            switch ($blockEsiOptions['only_cache_if']) {
110
                                case 'empty':
111
                                    $cacheFlag = ('' === $resp->getBody());
112
                                    break;
113
                                case 'no_text':
114
                                    $cacheFlag = ('' === trim(strip_tags($resp->getBody())));
115
                                    break;
116
                                default:
117
                                    $cacheFlag = false;
118
                            }
119
                        }
120
                    }
121
                    if ($esiData->getEsiMethod() == 'ajax') {
122
                        $resp->setHeader('Access-Control-Allow-Origin',
123
                            $esiHelper->getCorsOrigin());
124
                    }
125
                    if ( ! is_null($flushEvents = $esiData->getFlushEvents())) {
126
                        $resp->setHeader('X-Turpentine-Flush-Events',
127
                            implode(',', $flushEvents));
128
                    }
129
                    if ($esiHelper->getEsiDebugEnabled()) {
130
                        $resp->setHeader('X-Turpentine-Block',
131
                            $block->getNameInLayout());
132
                    }
133
                } else {
134
                    $resp->setHttpResponseCode(404);
135
                    $resp->setBody('ESI block not found');
136
                }
137
                $appShim->shim_setRequest($origRequest);
138
            }
139
        } else {
140
            $resp->setHttpResponseCode(403);
141
            $resp->setBody('ESI includes are not enabled');
142
        }
143
        Mage::register('turpentine_nocache_flag', ! $cacheFlag, true);
144
    }
145
146
    /**
147
     * Need to disable this flag to prevent setting the last URL but we
148
     * don't want to completely break sessions.
149
     *
150
     * see Mage_Core_Controller_Front_Action::postDispatch
151
     *
152
     * @return null
153
     */
154
    public function postDispatch() {
155
        $flag = $this->getFlag('', self::FLAG_NO_START_SESSION);
156
        $this->setFlag('', self::FLAG_NO_START_SESSION, true);
157
        parent::postDispatch();
158
        $this->setFlag('', self::FLAG_NO_START_SESSION, $flag);
159
    }
160
161
    /**
162
     * Generate the ESI block
163
     *
164
     * @param  Varien_Object $esiData
165
     * @return Mage_Core_Block_Template|null
166
     */
167
    protected function _getEsiBlock($esiData) {
168
        $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...
169
        Varien_Profiler::start('turpentine::controller::esi::_getEsiBlock');
170
        foreach ($esiData->getSimpleRegistry() as $key => $value) {
171
            Mage::register($key, $value, true);
172
        }
173
        foreach ($esiData->getComplexRegistry() as $key => $data) {
174
            $value = Mage::getModel($data['model']);
175
            if ( ! is_object($value)) {
176
                Mage::helper('turpentine/debug')->logWarn(
177
                    'Failed to register key/model: %s as %s(%s)',
178
                    $key, $data['model'], $data['id'] );
179
                continue;
180
            } else {
181
                $value->load($data['id']);
182
                Mage::register($key, $value, true);
183
            }
184
        }
185
        $layout = Mage::getSingleton('core/layout');
186
        Mage::getSingleton('core/design_package')
187
                ->setPackageName($esiData->getDesignPackage())
188
                ->setTheme($esiData->getDesignTheme());
189
190
        // dispatch event for adding handles to layout update
191
        Mage::dispatchEvent(
192
            'controller_action_layout_load_before',
193
            array('action'=>$this, 'layout'=>$layout)
194
        );
195
196
        $layoutUpdate = $layout->getUpdate();
197
        $layoutUpdate->load($this->_swapCustomerHandles(
198
            $esiData->getLayoutHandles() ));
199
        foreach ($esiData->getDummyBlocks() as $blockName) {
200
            $layout->createBlock('Mage_Core_Block_Template', $blockName);
201
        }
202
203
        if ( ! $this->getFlag('', self::FLAG_NO_DISPATCH_BLOCK_EVENT)) {
204
            Mage::dispatchEvent(
205
                'controller_action_layout_generate_xml_before',
206
                array('action'=>$this, 'layout'=>$layout)
207
            );
208
        }
209
        $layout->generateXml();
210
211
        /** @var Nexcessnet_Turpentine_Helper_Data $turpentineHelper */
212
        $turpentineHelper = Mage::helper('turpentine/data')
213
            ->setLayout($layout);
214
215
        $blockNode = Mage::helper('turpentine/esi')->getEsiLayoutBlockNode(
216
            $layout, $esiData->getNameInLayout());
217
        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...
218
            Mage::helper('turpentine/debug')->logWarn(
219
                            'No block node found with @name="%s"',
220
                            $esiData->getNameInLayout() );
221
            return null;
222
        }
223
224
        $nodesToGenerate = $turpentineHelper->getChildBlockNames($blockNode);
225
        Mage::getModel('turpentine/shim_mage_core_layout')
226
            ->shim_generateFullBlock($blockNode);
227
228
        //find addional blocks that aren't defined in the <block/> but via <reference name="%s">
229
        $referenceNodes = $layout->getNode()->xpath(sprintf(
230
            '//reference[@name=\'%s\']',
231
            $esiData->getNameInLayout() ));
232
        if ($referenceNodes) {
233
            foreach ($referenceNodes as $referenceNode) {
234
                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...
235
                    $referencesToGenerate = $turpentineHelper
236
                        ->getChildBlockNames($referenceNode);
237
                    $nodesToGenerate =
238
                        array_merge($nodesToGenerate, $referencesToGenerate);
239
                }
240
            }
241
        }
242
243
        // dispatch event for adding xml layout elements
244
        if ( ! $this->getFlag('', self::FLAG_NO_DISPATCH_BLOCK_EVENT)) {
245
            Mage::dispatchEvent(
246
                'controller_action_layout_generate_blocks_before',
247
                array('action'=>$this, 'layout'=>$layout)
248
            );
249
        }
250
251
        foreach (array_unique($nodesToGenerate) as $nodeName) {
252
            foreach ($layout->getNode()->xpath(sprintf(
253
                    '//reference[@name=\'%s\']', $nodeName )) as $node) {
254
                $layout->generateBlocks($node);
255
            }
256
        }
257
        if ($roots = $layout->getNode()->xpath('//block[@name=\'root\']')) {
258
            foreach (array('formkey') as $globalBlock) {
259
                if ($blocks = $layout->getNode()->xpath(sprintf('//block[@name=\'%s\']', $globalBlock))) {
260
                    $dummy = $roots[0]->addChild('reference');
261
                    $dummy->appendChild($blocks[0]);
262
                    $layout->generateBlocks($dummy);
263
                }
264
            }
265
        }
266
        $block = $layout->getBlock($esiData->getNameInLayout());
267
268
        if ( ! $this->getFlag('', self::FLAG_NO_DISPATCH_BLOCK_EVENT)) {
269
            Mage::dispatchEvent(
270
                'controller_action_layout_generate_blocks_after',
271
                array('action'=>$this, 'layout'=>$layout)
272
            );
273
        }
274
275
        $this->_isLayoutLoaded = true;
276
        Varien_Profiler::stop('turpentine::controller::esi::_getEsiBlock');
277
        return $block;
278
    }
279
280
    /**
281
     * Swap customer_logged_in and customer_logged_out cached handles based on
282
     * actual customer login status. Fixes stale Login/Logout links (and
283
     * probably other things).
284
     *
285
     * This is definitely a hack, need a more general solution to this problem.
286
     *
287
     * @param  array $handles
288
     * @return array
289
     */
290
    protected function _swapCustomerHandles($handles) {
291
        if (Mage::helper('customer')->isLoggedIn()) {
292
            $replacement = array('customer_logged_out', 'customer_logged_in');
293
        } else {
294
            $replacement = array('customer_logged_in', 'customer_logged_out');
295
        }
296
        if (($pos = array_search($replacement[0], $handles)) !== false) {
297
            $handles[$pos] = $replacement[1];
298
        }
299
        return $handles;
300
    }
301
}
302