web/lib/Zend/File/Transfer/Adapter/Http.php
changeset 64 162c1de6545a
parent 19 1c2f13fd785c
child 68 ecaf28ffe26e
equal deleted inserted replaced
63:5b37998e522e 64:162c1de6545a
       
     1 <?php
       
     2 /**
       
     3  * Zend Framework
       
     4  *
       
     5  * LICENSE
       
     6  *
       
     7  * This source file is subject to the new BSD license that is bundled
       
     8  * with this package in the file LICENSE.txt.
       
     9  * It is also available through the world-wide-web at this URL:
       
    10  * http://framework.zend.com/license/new-bsd
       
    11  * If you did not receive a copy of the license and are unable to
       
    12  * obtain it through the world-wide-web, please send an email
       
    13  * to license@zend.com so we can send you a copy immediately.
       
    14  *
       
    15  * @category  Zend
       
    16  * @package   Zend_File_Transfer
       
    17  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    18  * @license   http://framework.zend.com/license/new-bsd     New BSD License
       
    19  * @version   $Id: Http.php 22563 2010-07-15 20:42:04Z thomas $
       
    20  */
       
    21 
       
    22 /**
       
    23  * @see Zend_File_Transfer_Adapter_Abstract
       
    24  */
       
    25 require_once 'Zend/File/Transfer/Adapter/Abstract.php';
       
    26 
       
    27 /**
       
    28  * File transfer adapter class for the HTTP protocol
       
    29  *
       
    30  * @category  Zend
       
    31  * @package   Zend_File_Transfer
       
    32  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    33  * @license   http://framework.zend.com/license/new-bsd     New BSD License
       
    34  */
       
    35 class Zend_File_Transfer_Adapter_Http extends Zend_File_Transfer_Adapter_Abstract
       
    36 {
       
    37     protected static $_callbackApc            = 'apc_fetch';
       
    38     protected static $_callbackUploadProgress = 'uploadprogress_get_info';
       
    39 
       
    40     /**
       
    41      * Constructor for Http File Transfers
       
    42      *
       
    43      * @param array $options OPTIONAL Options to set
       
    44      */
       
    45     public function __construct($options = array())
       
    46     {
       
    47         if (ini_get('file_uploads') == false) {
       
    48             require_once 'Zend/File/Transfer/Exception.php';
       
    49             throw new Zend_File_Transfer_Exception('File uploads are not allowed in your php config!');
       
    50         }
       
    51 
       
    52         $this->setOptions($options);
       
    53         $this->_prepareFiles();
       
    54         $this->addValidator('Upload', false, $this->_files);
       
    55     }
       
    56 
       
    57     /**
       
    58      * Sets a validator for the class, erasing all previous set
       
    59      *
       
    60      * @param  string|array $validator Validator to set
       
    61      * @param  string|array $files     Files to limit this validator to
       
    62      * @return Zend_File_Transfer_Adapter
       
    63      */
       
    64     public function setValidators(array $validators, $files = null)
       
    65     {
       
    66         $this->clearValidators();
       
    67         return $this->addValidators($validators, $files);
       
    68     }
       
    69 
       
    70     /**
       
    71      * Remove an individual validator
       
    72      *
       
    73      * @param  string $name
       
    74      * @return Zend_File_Transfer_Adapter_Abstract
       
    75      */
       
    76     public function removeValidator($name)
       
    77     {
       
    78         if ($name == 'Upload') {
       
    79             return $this;
       
    80         }
       
    81 
       
    82         return parent::removeValidator($name);
       
    83     }
       
    84 
       
    85     /**
       
    86      * Remove an individual validator
       
    87      *
       
    88      * @param  string $name
       
    89      * @return Zend_File_Transfer_Adapter_Abstract
       
    90      */
       
    91     public function clearValidators()
       
    92     {
       
    93         parent::clearValidators();
       
    94         $this->addValidator('Upload', false, $this->_files);
       
    95 
       
    96         return $this;
       
    97     }
       
    98 
       
    99     /**
       
   100      * Send the file to the client (Download)
       
   101      *
       
   102      * @param  string|array $options Options for the file(s) to send
       
   103      * @return void
       
   104      * @throws Zend_File_Transfer_Exception Not implemented
       
   105      */
       
   106     public function send($options = null)
       
   107     {
       
   108         require_once 'Zend/File/Transfer/Exception.php';
       
   109         throw new Zend_File_Transfer_Exception('Method not implemented');
       
   110     }
       
   111 
       
   112     /**
       
   113      * Checks if the files are valid
       
   114      *
       
   115      * @param  string|array $files (Optional) Files to check
       
   116      * @return boolean True if all checks are valid
       
   117      */
       
   118     public function isValid($files = null)
       
   119     {
       
   120         // Workaround for WebServer not conforming HTTP and omitting CONTENT_LENGTH
       
   121         $content = 0;
       
   122         if (isset($_SERVER['CONTENT_LENGTH'])) {
       
   123             $content = $_SERVER['CONTENT_LENGTH'];
       
   124         } else if (!empty($_POST)) {
       
   125             $content = serialize($_POST);
       
   126         }
       
   127 
       
   128         // Workaround for a PHP error returning empty $_FILES when form data exceeds php settings
       
   129         if (empty($this->_files) && ($content > 0)) {
       
   130             if (is_array($files)) {
       
   131                 $files = current($files);
       
   132             }
       
   133 
       
   134             $temp = array($files => array(
       
   135                 'name'  => $files,
       
   136                 'error' => 1));
       
   137             $validator = $this->_validators['Zend_Validate_File_Upload'];
       
   138             $validator->setFiles($temp)
       
   139                       ->isValid($files, null);
       
   140             $this->_messages += $validator->getMessages();
       
   141             return false;
       
   142         }
       
   143 
       
   144         return parent::isValid($files);
       
   145     }
       
   146 
       
   147     /**
       
   148      * Receive the file from the client (Upload)
       
   149      *
       
   150      * @param  string|array $files (Optional) Files to receive
       
   151      * @return bool
       
   152      */
       
   153     public function receive($files = null)
       
   154     {
       
   155         if (!$this->isValid($files)) {
       
   156             return false;
       
   157         }
       
   158 
       
   159         $check = $this->_getFiles($files);
       
   160         foreach ($check as $file => $content) {
       
   161             if (!$content['received']) {
       
   162                 $directory   = '';
       
   163                 $destination = $this->getDestination($file);
       
   164                 if ($destination !== null) {
       
   165                     $directory = $destination . DIRECTORY_SEPARATOR;
       
   166                 }
       
   167 
       
   168                 $filename = $directory . $content['name'];
       
   169                 $rename   = $this->getFilter('Rename');
       
   170                 if ($rename !== null) {
       
   171                     $tmp = $rename->getNewName($content['tmp_name']);
       
   172                     if ($tmp != $content['tmp_name']) {
       
   173                         $filename = $tmp;
       
   174                     }
       
   175 
       
   176                     if (dirname($filename) == '.') {
       
   177                         $filename = $directory . $filename;
       
   178                     }
       
   179 
       
   180                     $key = array_search(get_class($rename), $this->_files[$file]['filters']);
       
   181                     unset($this->_files[$file]['filters'][$key]);
       
   182                 }
       
   183 
       
   184                 // Should never return false when it's tested by the upload validator
       
   185                 if (!move_uploaded_file($content['tmp_name'], $filename)) {
       
   186                     if ($content['options']['ignoreNoFile']) {
       
   187                         $this->_files[$file]['received'] = true;
       
   188                         $this->_files[$file]['filtered'] = true;
       
   189                         continue;
       
   190                     }
       
   191 
       
   192                     $this->_files[$file]['received'] = false;
       
   193                     return false;
       
   194                 }
       
   195 
       
   196                 if ($rename !== null) {
       
   197                     $this->_files[$file]['destination'] = dirname($filename);
       
   198                     $this->_files[$file]['name']        = basename($filename);
       
   199                 }
       
   200 
       
   201                 $this->_files[$file]['tmp_name'] = $filename;
       
   202                 $this->_files[$file]['received'] = true;
       
   203             }
       
   204 
       
   205             if (!$content['filtered']) {
       
   206                 if (!$this->_filter($file)) {
       
   207                     $this->_files[$file]['filtered'] = false;
       
   208                     return false;
       
   209                 }
       
   210 
       
   211                 $this->_files[$file]['filtered'] = true;
       
   212             }
       
   213         }
       
   214 
       
   215         return true;
       
   216     }
       
   217 
       
   218     /**
       
   219      * Checks if the file was already sent
       
   220      *
       
   221      * @param  string|array $file Files to check
       
   222      * @return bool
       
   223      * @throws Zend_File_Transfer_Exception Not implemented
       
   224      */
       
   225     public function isSent($files = null)
       
   226     {
       
   227         require_once 'Zend/File/Transfer/Exception.php';
       
   228         throw new Zend_File_Transfer_Exception('Method not implemented');
       
   229     }
       
   230 
       
   231     /**
       
   232      * Checks if the file was already received
       
   233      *
       
   234      * @param  string|array $files (Optional) Files to check
       
   235      * @return bool
       
   236      */
       
   237     public function isReceived($files = null)
       
   238     {
       
   239         $files = $this->_getFiles($files, false, true);
       
   240         if (empty($files)) {
       
   241             return false;
       
   242         }
       
   243 
       
   244         foreach ($files as $content) {
       
   245             if ($content['received'] !== true) {
       
   246                 return false;
       
   247             }
       
   248         }
       
   249 
       
   250         return true;
       
   251     }
       
   252 
       
   253     /**
       
   254      * Checks if the file was already filtered
       
   255      *
       
   256      * @param  string|array $files (Optional) Files to check
       
   257      * @return bool
       
   258      */
       
   259     public function isFiltered($files = null)
       
   260     {
       
   261         $files = $this->_getFiles($files, false, true);
       
   262         if (empty($files)) {
       
   263             return false;
       
   264         }
       
   265 
       
   266         foreach ($files as $content) {
       
   267             if ($content['filtered'] !== true) {
       
   268                 return false;
       
   269             }
       
   270         }
       
   271 
       
   272         return true;
       
   273     }
       
   274 
       
   275     /**
       
   276      * Has a file been uploaded ?
       
   277      *
       
   278      * @param  array|string|null $file
       
   279      * @return bool
       
   280      */
       
   281     public function isUploaded($files = null)
       
   282     {
       
   283         $files = $this->_getFiles($files, false, true);
       
   284         if (empty($files)) {
       
   285             return false;
       
   286         }
       
   287 
       
   288         foreach ($files as $file) {
       
   289             if (empty($file['name'])) {
       
   290                 return false;
       
   291             }
       
   292         }
       
   293 
       
   294         return true;
       
   295     }
       
   296 
       
   297     /**
       
   298      * Returns the actual progress of file up-/downloads
       
   299      *
       
   300      * @param  string $id The upload to get the progress for
       
   301      * @return array|null
       
   302      */
       
   303     public static function getProgress($id = null)
       
   304     {
       
   305         if (!function_exists('apc_fetch') and !function_exists('uploadprogress_get_info')) {
       
   306             require_once 'Zend/File/Transfer/Exception.php';
       
   307             throw new Zend_File_Transfer_Exception('Neither APC nor uploadprogress extension installed');
       
   308         }
       
   309 
       
   310         $session = 'Zend_File_Transfer_Adapter_Http_ProgressBar';
       
   311         $status  = array(
       
   312             'total'    => 0,
       
   313             'current'  => 0,
       
   314             'rate'     => 0,
       
   315             'message'  => '',
       
   316             'done'     => false
       
   317         );
       
   318 
       
   319         if (is_array($id)) {
       
   320             if (isset($id['progress'])) {
       
   321                 $adapter = $id['progress'];
       
   322             }
       
   323 
       
   324             if (isset($id['session'])) {
       
   325                 $session = $id['session'];
       
   326             }
       
   327 
       
   328             if (isset($id['id'])) {
       
   329                 $id = $id['id'];
       
   330             } else {
       
   331                 unset($id);
       
   332             }
       
   333         }
       
   334 
       
   335         if (!empty($id) && (($id instanceof Zend_ProgressBar_Adapter) || ($id instanceof Zend_ProgressBar))) {
       
   336             $adapter = $id;
       
   337             unset($id);
       
   338         }
       
   339 
       
   340         if (empty($id)) {
       
   341             if (!isset($_GET['progress_key'])) {
       
   342                 $status['message'] = 'No upload in progress';
       
   343                 $status['done']    = true;
       
   344             } else {
       
   345                 $id = $_GET['progress_key'];
       
   346             }
       
   347         }
       
   348 
       
   349         if (!empty($id)) {
       
   350             if (self::isApcAvailable()) {
       
   351 
       
   352                 $call = call_user_func(self::$_callbackApc, ini_get('apc.rfc1867_prefix') . $id);
       
   353                 if (is_array($call)) {
       
   354                     $status = $call + $status;
       
   355                 }
       
   356             } else if (self::isUploadProgressAvailable()) {
       
   357                 $call = call_user_func(self::$_callbackUploadProgress, $id);
       
   358                 if (is_array($call)) {
       
   359                     $status = $call + $status;
       
   360                     $status['total']   = $status['bytes_total'];
       
   361                     $status['current'] = $status['bytes_uploaded'];
       
   362                     $status['rate']    = $status['speed_average'];
       
   363                     if ($status['total'] == $status['current']) {
       
   364                         $status['done'] = true;
       
   365                     }
       
   366                 }
       
   367             }
       
   368 
       
   369             if (!is_array($call)) {
       
   370                 $status['done']    = true;
       
   371                 $status['message'] = 'Failure while retrieving the upload progress';
       
   372             } else if (!empty($status['cancel_upload'])) {
       
   373                 $status['done']    = true;
       
   374                 $status['message'] = 'The upload has been canceled';
       
   375             } else {
       
   376                 $status['message'] = self::_toByteString($status['current']) . " - " . self::_toByteString($status['total']);
       
   377             }
       
   378 
       
   379             $status['id'] = $id;
       
   380         }
       
   381 
       
   382         if (isset($adapter) && isset($status['id'])) {
       
   383             if ($adapter instanceof Zend_ProgressBar_Adapter) {
       
   384                 require_once 'Zend/ProgressBar.php';
       
   385                 $adapter = new Zend_ProgressBar($adapter, 0, $status['total'], $session);
       
   386             }
       
   387 
       
   388             if (!($adapter instanceof Zend_ProgressBar)) {
       
   389                 require_once 'Zend/File/Transfer/Exception.php';
       
   390                 throw new Zend_File_Transfer_Exception('Unknown Adapter given');
       
   391             }
       
   392 
       
   393             if ($status['done']) {
       
   394                 $adapter->finish();
       
   395             } else {
       
   396                 $adapter->update($status['current'], $status['message']);
       
   397             }
       
   398 
       
   399             $status['progress'] = $adapter;
       
   400         }
       
   401 
       
   402         return $status;
       
   403     }
       
   404 
       
   405     /**
       
   406      * Checks the APC extension for progress information
       
   407      *
       
   408      * @return boolean
       
   409      */
       
   410     public static function isApcAvailable()
       
   411     {
       
   412         return (bool) ini_get('apc.enabled') && (bool) ini_get('apc.rfc1867') && is_callable(self::$_callbackApc);
       
   413     }
       
   414 
       
   415     /**
       
   416      * Checks the UploadProgress extension for progress information
       
   417      *
       
   418      * @return boolean
       
   419      */
       
   420     public static function isUploadProgressAvailable()
       
   421     {
       
   422         return is_callable(self::$_callbackUploadProgress);
       
   423     }
       
   424 
       
   425     /**
       
   426      * Prepare the $_FILES array to match the internal syntax of one file per entry
       
   427      *
       
   428      * @param  array $files
       
   429      * @return array
       
   430      */
       
   431     protected function _prepareFiles()
       
   432     {
       
   433         $this->_files = array();
       
   434         foreach ($_FILES as $form => $content) {
       
   435             if (is_array($content['name'])) {
       
   436                 foreach ($content as $param => $file) {
       
   437                     foreach ($file as $number => $target) {
       
   438                         $this->_files[$form . '_' . $number . '_'][$param]      = $target;
       
   439                         $this->_files[$form]['multifiles'][$number] = $form . '_' . $number . '_';
       
   440                     }
       
   441                 }
       
   442 
       
   443                 $this->_files[$form]['name'] = $form;
       
   444                 foreach($this->_files[$form]['multifiles'] as $key => $value) {
       
   445                     $this->_files[$value]['options']   = $this->_options;
       
   446                     $this->_files[$value]['validated'] = false;
       
   447                     $this->_files[$value]['received']  = false;
       
   448                     $this->_files[$value]['filtered']  = false;
       
   449 
       
   450                     $mimetype = $this->_detectMimeType($this->_files[$value]);
       
   451                     $this->_files[$value]['type'] = $mimetype;
       
   452 
       
   453                     $filesize = $this->_detectFileSize($this->_files[$value]);
       
   454                     $this->_files[$value]['size'] = $filesize;
       
   455 
       
   456                     if ($this->_options['detectInfos']) {
       
   457                         $_FILES[$form]['type'][$key] = $mimetype;
       
   458                         $_FILES[$form]['size'][$key] = $filesize;
       
   459                     }
       
   460                 }
       
   461             } else {
       
   462                 $this->_files[$form]              = $content;
       
   463                 $this->_files[$form]['options']   = $this->_options;
       
   464                 $this->_files[$form]['validated'] = false;
       
   465                 $this->_files[$form]['received']  = false;
       
   466                 $this->_files[$form]['filtered']  = false;
       
   467 
       
   468                 $mimetype = $this->_detectMimeType($this->_files[$form]);
       
   469                 $this->_files[$form]['type'] = $mimetype;
       
   470 
       
   471                 $filesize = $this->_detectFileSize($this->_files[$form]);
       
   472                 $this->_files[$form]['size'] = $filesize;
       
   473 
       
   474                 if ($this->_options['detectInfos']) {
       
   475                     $_FILES[$form]['type'] = $mimetype;
       
   476                     $_FILES[$form]['size'] = $filesize;
       
   477                 }
       
   478             }
       
   479         }
       
   480 
       
   481         return $this;
       
   482     }
       
   483 }