Source of file FilePostRedirectGet.php
Size: 11,761 Bytes - Last Modified: 2014-03-12T23:21:18+01:00
/home/theseer/Downloads/ZendFramework-2.3.0/library/Zend/Mvc/Controller/Plugin/FilePostRedirectGet.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342 | <?php /** * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ namespace Zend\Mvc\Controller\Plugin; use Zend\Filter\FilterChain; use Zend\Form\FormInterface; use Zend\Http\Response; use Zend\InputFilter\FileInput; use Zend\InputFilter\InputFilterInterface; use Zend\Mvc\Exception\RuntimeException; use Zend\Session\Container; use Zend\Stdlib\ArrayUtils; use Zend\Validator\ValidatorChain; /** * Plugin to help facilitate Post/Redirect/Get for file upload forms * (http://en.wikipedia.org/wiki/Post/Redirect/Get) * * Requires that the Form's File inputs contain a 'fileRenameUpload' filter * with the target option set: 'target' => /valid/target/path'. * This is so the files are moved to a new location between requests. * If this filter is not added, the temporary upload files will disappear * between requests. */ class FilePostRedirectGet extends AbstractPlugin { /** * @var Container */ protected $sessionContainer; /** * @param FormInterface $form * @param string $redirect Route or URL string (default: current route) * @param bool $redirectToUrl Use $redirect as a URL string (default: false) * @return bool|array|Response */ public function __invoke(FormInterface $form, $redirect = null, $redirectToUrl = false) { $request = $this->getController()->getRequest(); if ($request->isPost()) { return $this->handlePostRequest($form, $redirect, $redirectToUrl); } else { return $this->handleGetRequest($form); } } /** * @param FormInterface $form * @param string $redirect Route or URL string (default: current route) * @param bool $redirectToUrl Use $redirect as a URL string (default: false) * @return Response */ protected function handlePostRequest(FormInterface $form, $redirect, $redirectToUrl) { $container = $this->getSessionContainer(); $request = $this->getController()->getRequest(); $postFiles = $request->getFiles()->toArray(); $postOther = $request->getPost()->toArray(); $post = ArrayUtils::merge($postOther, $postFiles, true); // Fill form with the data first, collections may alter the form/filter structure $form->setData($post); // Change required flag to false for any previously uploaded files $inputFilter = $form->getInputFilter(); $previousFiles = ($container->files) ?: array(); $this->traverseInputs( $inputFilter, $previousFiles, function ($input, $value) { if ($input instanceof FileInput) { $input->setRequired(false); } return $value; } ); // Run the form validations/filters and retrieve any errors $isValid = $form->isValid(); $data = $form->getData(FormInterface::VALUES_AS_ARRAY); $errors = (!$isValid) ? $form->getMessages() : null; // Merge and replace previous files with new valid files $prevFileData = $this->getEmptyUploadData($inputFilter, $previousFiles); $newFileData = $this->getNonEmptyUploadData($inputFilter, $data); $postFiles = ArrayUtils::merge( $prevFileData ?: array(), $newFileData ?: array(), true ); $post = ArrayUtils::merge($postOther, $postFiles, true); // Save form data in session $container->setExpirationHops(1, array('post', 'errors', 'isValid')); $container->post = $post; $container->errors = $errors; $container->isValid = $isValid; $container->files = $postFiles; return $this->redirect($redirect, $redirectToUrl); } /** * @param FormInterface $form * @return bool|array */ protected function handleGetRequest(FormInterface $form) { $container = $this->getSessionContainer(); if (null === $container->post) { // No previous post, bail early unset($container->files); return false; } // Collect data from session $post = $container->post; $errors = $container->errors; $isValid = $container->isValid; $previousFiles = ($container->files) ?: array(); unset($container->post); unset($container->errors); unset($container->isValid); // Fill form with the data first, collections may alter the form/filter structure $form->setData($post); // Remove File Input validators and filters on previously uploaded files // in case $form->isValid() or $form->bindValues() is run $inputFilter = $form->getInputFilter(); $this->traverseInputs( $inputFilter, $post, function ($input, $value) { if ($input instanceof FileInput) { $input->setAutoPrependUploadValidator(false) ->setValidatorChain(new ValidatorChain()) ->setFilterChain(new FilterChain); } return $value; } ); // set previous state $form->isValid(); // re-validate to bind values if (null !== $errors) { $form->setMessages($errors); // overwrite messages } $this->setProtectedFormProperty($form, 'isValid', $isValid); // force previous state // Clear previous files from session data if form was valid if ($isValid) { unset($container->files); } return $post; } /** * @return Container */ public function getSessionContainer() { if (!isset($this->sessionContainer)) { $this->sessionContainer = new Container('file_prg_post1'); } return $this->sessionContainer; } /** * @param Container $container * @return FilePostRedirectGet */ public function setSessionContainer(Container $container) { $this->sessionContainer = $container; return $this; } /** * @param FormInterface $form * @param string $property * @param mixed $value * @return FilePostRedirectGet */ protected function setProtectedFormProperty(FormInterface $form, $property, $value) { $formClass = new \ReflectionClass($form); $property = $formClass->getProperty($property); $property->setAccessible(true); $property->setValue($form, $value); return $this; } /** * Traverse the InputFilter and run a callback against each Input and associated value * * @param InputFilterInterface $inputFilter * @param array $values * @param callable $callback * @return array|null */ protected function traverseInputs(InputFilterInterface $inputFilter, $values, $callback) { $returnValues = null; foreach ($values as $name => $value) { if (!$inputFilter->has($name)) { continue; } $input = $inputFilter->get($name); if ($input instanceof InputFilterInterface && is_array($value)) { $retVal = $this->traverseInputs($input, $value, $callback); if (null !== $retVal) { $returnValues[$name] = $retVal; } continue; } $retVal = $callback($input, $value); if (null !== $retVal) { $returnValues[$name] = $retVal; } } return $returnValues; } /** * Traverse the InputFilter and only return the data of FileInputs that have an upload * * @param InputFilterInterface $inputFilter * @param array $data * @return array */ protected function getNonEmptyUploadData(InputFilterInterface $inputFilter, $data) { return $this->traverseInputs( $inputFilter, $data, function ($input, $value) { $messages = $input->getMessages(); if (is_array($value) && $input instanceof FileInput && empty($messages)) { $rawValue = $input->getRawValue(); if ( (isset($rawValue['error']) && $rawValue['error'] !== UPLOAD_ERR_NO_FILE) || (isset($rawValue[0]['error']) && $rawValue[0]['error'] !== UPLOAD_ERR_NO_FILE) ) { return $value; } } return null; } ); } /** * Traverse the InputFilter and only return the data of FileInputs that are empty * * @param InputFilterInterface $inputFilter * @param array $data * @return array */ protected function getEmptyUploadData(InputFilterInterface $inputFilter, $data) { return $this->traverseInputs( $inputFilter, $data, function ($input, $value) { $messages = $input->getMessages(); if (is_array($value) && $input instanceof FileInput && empty($messages)) { $rawValue = $input->getRawValue(); if ( (isset($rawValue['error']) && $rawValue['error'] === UPLOAD_ERR_NO_FILE) || (isset($rawValue[0]['error']) && $rawValue[0]['error'] === UPLOAD_ERR_NO_FILE) ) { return $value; } } return null; } ); } /** * TODO: Good candidate for traits method in PHP 5.4 with PostRedirectGet plugin * * @param string $redirect * @param bool $redirectToUrl * @return Response * @throws \Zend\Mvc\Exception\RuntimeException */ protected function redirect($redirect, $redirectToUrl) { $controller = $this->getController(); $params = array(); $options = array(); $reuseMatchedParams = false; if (null === $redirect) { $routeMatch = $controller->getEvent()->getRouteMatch(); $redirect = $routeMatch->getMatchedRouteName(); //null indicates to redirect for self. $reuseMatchedParams = true; } if (method_exists($controller, 'getPluginManager')) { // get the redirect plugin from the plugin manager $redirector = $controller->getPluginManager()->get('Redirect'); } else { /* * 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 ($redirectToUrl === false) { throw new RuntimeException('Could not redirect to a route without a router'); } $redirector = new Redirect(); } if ($redirectToUrl === false) { $response = $redirector->toRoute($redirect, $params, $options, $reuseMatchedParams); $response->setStatusCode(303); return $response; } $response = $redirector->toUrl($redirect); $response->setStatusCode(303); return $response; } } |