summaryrefslogtreecommitdiffstats
path: root/zend-mvc-plugin-prg-8c7ccb9f0004e92ff258b483447d914f42cb7448/src/PostRedirectGet.php
blob: 9a6279b122984ac3a4eea250d8750fb412665297 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
<?php
/**
 * @link      http://github.com/zendframework/zend-mvc-plugin-prg for the canonical source repository
 * @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
 * @license   http://framework.zend.com/license/new-bsd New BSD License
 */

namespace Zend\Mvc\Plugin\Prg;

use Zend\Mvc\Controller\Plugin\AbstractPlugin;
use Zend\Mvc\Controller\Plugin\Redirect;
use Zend\Mvc\Exception\RuntimeException;
use Zend\Session\Container;
use Zend\Stdlib\DispatchableInterface;

/**
 * Plugin to help facilitate Post/Redirect/Get (http://en.wikipedia.org/wiki/Post/Redirect/Get)
 */
class PostRedirectGet extends AbstractPlugin
{
    /**
     * @var Container
     */
    protected $sessionContainer;

    /**
     * Perform PRG logic
     *
     * If a null value is present for the $redirect, the current route is
     * retrieved and use to generate the URL for redirect.
     *
     * If the request method is POST, creates a session container set to expire
     * after 1 hop containing the values of the POST. It then redirects to the
     * specified URL using a status 303.
     *
     * If the request method is GET, checks to see if we have values in the
     * session container, and, if so, returns them; otherwise, it returns a
     * boolean false.
     *
     * @param  null|string $redirect
     * @param  bool        $redirectToUrl
     * @return \Zend\Http\Response|array|\Traversable|false
     */
    public function __invoke($redirect = null, $redirectToUrl = false)
    {
        $controller = $this->getController();
        $request    = $controller->getRequest();
        $container  = $this->getSessionContainer();

        if ($request->isPost()) {
            $container->setExpirationHops(1, 'post');
            $container->post = $request->getPost()->toArray();
            return $this->redirect($redirect, $redirectToUrl);
        }

        if (null !== $container->post) {
            $post = $container->post;
            unset($container->post);
            return $post;
        }

        return false;
    }

    /**
     * @return Container
     */
    public function getSessionContainer()
    {
        if (! $this->sessionContainer) {
            $this->sessionContainer = new Container('prg_post1');
        }
        return $this->sessionContainer;
    }

    /**
     * @param  Container $container
     * @return PostRedirectGet
     */
    public function setSessionContainer(Container $container)
    {
        $this->sessionContainer = $container;
        return $this;
    }

    /**
     * TODO: Good candidate for traits method in PHP 5.4 with FilePostRedirectGet plugin
     *
     * @param  string  $redirect
     * @param  bool    $redirectToUrl
     * @return \Zend\Http\Response
     * @throws RuntimeException if route-based redirection is requested, but no
     *     plugin manager is composed in the controller.
     */
    protected function redirect($redirect, $redirectToUrl)
    {
        $controller         = $this->getController();
        $params             = [];
        $options            = ['query' => $controller->params()->fromQuery()];
        $reuseMatchedParams = false;

        if (null === $redirect) {
            $routeMatch = $controller->getEvent()->getRouteMatch();

            $redirect = $routeMatch->getMatchedRouteName();
            // null indicates to redirect to self.
            $reuseMatchedParams = true;
        }

        $redirector = $this->marshalRedirectPlugin($controller, $redirectToUrl);

        // Redirect to route-based URL
        if (false === $redirectToUrl) {
            $response = $redirector->toRoute($redirect, $params, $options, $reuseMatchedParams);
            $response->setStatusCode(303);
            return $response;
        }

        // Redirect to specific URL
        $response = $redirector->toUrl($redirect);
        $response->setStatusCode(303);

        return $response;
    }

    /**
     * Marshal a redirect plugin instance.
     *
     * @param DispatchableInterface $controller
     * @param bool $redirectToUrl
     * @return Redirect
     * @throws RuntimeException if route-based redirection is requested, but no
     *     plugin manager is composed in the controller.
     */
    private function marshalRedirectPlugin(DispatchableInterface $controller, $redirectToUrl)
    {
        if (method_exists($controller, 'getPluginManager')) {
            // get the redirect plugin from the plugin manager
            return $controller->getPluginManager()->get('Redirect');
        }

        // If the user wants to redirect to a route, the redirector has to come
        // from the plugin manager; otherwise no router will be injected
        if (false === $redirectToUrl) {
            throw new RuntimeException('Could not redirect to a route without a router');
        }

        return new Redirect();
    }
}