Http.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  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-2008 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. * @version $Id: $
  20. */
  21. require_once 'Zend/File/Transfer/Adapter/Abstract.php';
  22. /**
  23. * File transfer adapter class for the HTTP protocol
  24. *
  25. * @category Zend
  26. * @package Zend_File_Transfer
  27. * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
  28. * @license http://framework.zend.com/license/new-bsd New BSD License
  29. */
  30. class Zend_File_Transfer_Adapter_Http extends Zend_File_Transfer_Adapter_Abstract
  31. {
  32. protected static $_callbackApc = 'apc_fetch';
  33. protected static $_callbackUploadProgress = 'uploadprogress_get_info';
  34. /**
  35. * Constructor for Http File Transfers
  36. *
  37. * @param array $options OPTIONAL Options to set
  38. */
  39. public function __construct($options = array())
  40. {
  41. if (ini_get('file_uploads') == false) {
  42. require_once 'Zend/File/Transfer/Exception.php';
  43. throw new Zend_File_Transfer_Exception('File uploads are not allowed in your php config!');
  44. }
  45. $this->_files = $this->_prepareFiles($_FILES);
  46. $this->addValidator('Upload', false, $this->_files);
  47. if (is_array($options)) {
  48. $this->setOptions($options);
  49. }
  50. }
  51. /**
  52. * Sets a validator for the class, erasing all previous set
  53. *
  54. * @param string|array $validator Validator to set
  55. * @param string|array $files Files to limit this validator to
  56. * @return Zend_File_Transfer_Adapter
  57. */
  58. public function setValidators(array $validators, $files = null)
  59. {
  60. $this->clearValidators();
  61. return $this->addValidators($validators, $files);
  62. }
  63. /**
  64. * Remove an individual validator
  65. *
  66. * @param string $name
  67. * @return Zend_File_Transfer_Adapter_Abstract
  68. */
  69. public function removeValidator($name)
  70. {
  71. if ($name == 'Upload') {
  72. return $this;
  73. }
  74. return parent::removeValidator($name);
  75. }
  76. /**
  77. * Remove an individual validator
  78. *
  79. * @param string $name
  80. * @return Zend_File_Transfer_Adapter_Abstract
  81. */
  82. public function clearValidators()
  83. {
  84. parent::clearValidators();
  85. $this->addValidator('Upload', false, $this->_files);
  86. return $this;
  87. }
  88. /**
  89. * Send the file to the client (Download)
  90. *
  91. * @param string|array $options Options for the file(s) to send
  92. * @return void
  93. * @throws Zend_File_Transfer_Exception Not implemented
  94. */
  95. public function send($options = null)
  96. {
  97. require_once 'Zend/File/Transfer/Exception.php';
  98. throw new Zend_File_Transfer_Exception('Method not implemented');
  99. }
  100. /**
  101. * Checks if the files are valid
  102. *
  103. * @param string|array $files (Optional) Files to check
  104. * @return boolean True if all checks are valid
  105. */
  106. public function isValid($files = null)
  107. {
  108. // Workaround for a PHP error returning empty $_FILES when form data exceeds php settings
  109. if (empty($this->_files) && ($_SERVER['CONTENT_LENGTH'] > 0)) {
  110. if (is_array($files)) {
  111. $files = current($files);
  112. }
  113. $temp = array($files => array(
  114. 'name' => $files,
  115. 'error' => 1));
  116. $validator = $this->_validators['Zend_Validate_File_Upload'];
  117. $validator->setFiles($temp)
  118. ->isValid($files, null);
  119. $this->_messages += $validator->getMessages();
  120. return false;
  121. }
  122. return parent::isValid($files);
  123. }
  124. /**
  125. * Receive the file from the client (Upload)
  126. *
  127. * @param string|array $files (Optional) Files to receive
  128. * @return bool
  129. */
  130. public function receive($files = null)
  131. {
  132. if (!$this->isValid($files)) {
  133. return false;
  134. }
  135. $check = $this->_getFiles($files);
  136. foreach ($check as $file => $content) {
  137. if (!$content['received']) {
  138. $directory = '';
  139. $destination = $this->getDestination($file);
  140. if ($destination !== null) {
  141. $directory = $destination . DIRECTORY_SEPARATOR;
  142. }
  143. $filename = $directory . $content['name'];
  144. $rename = $this->getFilter('Rename');
  145. if ($rename !== null) {
  146. $filename = $rename->getNewName($content['tmp_name']);
  147. $key = array_search(get_class($rename), $this->_files[$file]['filters']);
  148. unset($this->_files[$file]['filters'][$key]);
  149. }
  150. // Should never return false when it's tested by the upload validator
  151. if (!move_uploaded_file($content['tmp_name'], $filename)) {
  152. if ($content['options']['ignoreNoFile']) {
  153. $this->_files[$file]['received'] = true;
  154. $this->_files[$file]['filtered'] = true;
  155. continue;
  156. }
  157. $this->_files[$file]['received'] = false;
  158. return false;
  159. }
  160. if ($rename !== null) {
  161. $this->_files[$file]['destination'] = dirname($filename);
  162. $this->_files[$file]['name'] = basename($filename);
  163. }
  164. $this->_files[$file]['tmp_name'] = $filename;
  165. $this->_files[$file]['received'] = true;
  166. }
  167. if (!$content['filtered']) {
  168. if (!$this->_filter($file)) {
  169. $this->_files[$file]['filtered'] = false;
  170. return false;
  171. }
  172. $this->_files[$file]['filtered'] = true;
  173. }
  174. }
  175. return true;
  176. }
  177. /**
  178. * Checks if the file was already sent
  179. *
  180. * @param string|array $file Files to check
  181. * @return bool
  182. * @throws Zend_File_Transfer_Exception Not implemented
  183. */
  184. public function isSent($files = null)
  185. {
  186. require_once 'Zend/File/Transfer/Exception.php';
  187. throw new Zend_File_Transfer_Exception('Method not implemented');
  188. }
  189. /**
  190. * Checks if the file was already received
  191. *
  192. * @param string|array $files (Optional) Files to check
  193. * @return bool
  194. */
  195. public function isReceived($files = null)
  196. {
  197. $files = $this->_getFiles($files, false, true);
  198. if (empty($files)) {
  199. return false;
  200. }
  201. foreach ($files as $content) {
  202. if ($content['received'] !== true) {
  203. return false;
  204. }
  205. }
  206. return true;
  207. }
  208. /**
  209. * Checks if the file was already filtered
  210. *
  211. * @param string|array $files (Optional) Files to check
  212. * @return bool
  213. */
  214. public function isFiltered($files = null)
  215. {
  216. $files = $this->_getFiles($files, false, true);
  217. if (empty($files)) {
  218. return false;
  219. }
  220. foreach ($files as $content) {
  221. if ($content['filtered'] !== true) {
  222. return false;
  223. }
  224. }
  225. return true;
  226. }
  227. /**
  228. * Has a file been uploaded ?
  229. *
  230. * @param array|string|null $file
  231. * @return bool
  232. */
  233. public function isUploaded($files = null)
  234. {
  235. $files = $this->_getFiles($files, false, true);
  236. if (empty($files)) {
  237. return false;
  238. }
  239. foreach ($files as $file) {
  240. if (empty($file['name'])) {
  241. return false;
  242. }
  243. }
  244. return true;
  245. }
  246. /**
  247. * Returns the actual progress of file up-/downloads
  248. *
  249. * @param string $id The upload to get the progress for
  250. * @return array|null
  251. */
  252. public static function getProgress($id = null)
  253. {
  254. if (!function_exists('apc_fetch') and !function_exists('uploadprogress_get_info')) {
  255. require_once 'Zend/File/Transfer/Exception.php';
  256. throw new Zend_File_Transfer_Exception('Wether APC nor uploadprogress extension installed');
  257. }
  258. $session = 'Zend_File_Transfer_Adapter_Http_ProgressBar';
  259. $status = array(
  260. 'total' => 0,
  261. 'current' => 0,
  262. 'rate' => 0,
  263. 'message' => '',
  264. 'done' => false
  265. );
  266. if (is_array($id)) {
  267. if (isset($id['progress'])) {
  268. $adapter = $id['progress'];
  269. }
  270. if (isset($id['session'])) {
  271. $session = $id['session'];
  272. }
  273. if (isset($id['id'])) {
  274. $id = $id['id'];
  275. } else {
  276. unset($id);
  277. }
  278. }
  279. if (!empty($id) && (($id instanceof Zend_ProgressBar_Adapter) || ($id instanceof Zend_ProgressBar))) {
  280. $adapter = $id;
  281. unset($id);
  282. }
  283. if (empty($id)) {
  284. if (!isset($_GET['progress_key'])) {
  285. $status['message'] = 'No upload in progress';
  286. $status['done'] = true;
  287. } else {
  288. $id = $_GET['progress_key'];
  289. }
  290. }
  291. if (!empty($id)) {
  292. if (self::isApcAvailable()) {
  293. $call = call_user_func(self::$_callbackApc, 'upload_' . $id);
  294. if (is_array($call)) {
  295. $status = $call + $status;
  296. }
  297. } else if (self::isUploadProgressAvailable()) {
  298. $call = call_user_func(self::$_callbackUploadProgress, $id);
  299. if (is_array($call)) {
  300. $status = $call + $status;
  301. $status['total'] = $status['bytes_total'];
  302. $status['current'] = $status['bytes_uploaded'];
  303. $status['rate'] = $status['speed_average'];
  304. if ($status['total'] == $status['current']) {
  305. $status['done'] = true;
  306. }
  307. }
  308. }
  309. if (!is_array($call)) {
  310. $status['done'] = true;
  311. $status['message'] = 'Failure while retrieving the upload progress';
  312. } else if (!empty($status['cancel_upload'])) {
  313. $status['done'] = true;
  314. $status['message'] = 'The upload has been canceled';
  315. } else {
  316. $status['message'] = self::_toByteString($status['current']) . " - " . self::_toByteString($status['total']);
  317. }
  318. $status['id'] = $id;
  319. }
  320. if (isset($adapter) && isset($status['id'])) {
  321. if ($adapter instanceof Zend_ProgressBar_Adapter) {
  322. require_once 'Zend/ProgressBar.php';
  323. $adapter = new Zend_ProgressBar($adapter, 0, $status['total'], $session);
  324. }
  325. if (!($adapter instanceof Zend_ProgressBar)) {
  326. require_once 'Zend/File/Transfer/Exception.php';
  327. throw new Zend_File_Transfer_Exception('Unknown Adapter given');
  328. }
  329. if ($status['done']) {
  330. $adapter->finish();
  331. } else {
  332. $adapter->update($status['current'], $status['message']);
  333. }
  334. $status['progress'] = $adapter;
  335. }
  336. return $status;
  337. }
  338. /**
  339. * Checks the APC extension for progress information
  340. *
  341. * @return boolean
  342. */
  343. public static function isApcAvailable()
  344. {
  345. return (bool) ini_get('apc.enabled') && (bool) ini_get('apc.rfc1867') && is_callable(self::$_callbackApc);
  346. }
  347. /**
  348. * Checks the UploadProgress extension for progress information
  349. *
  350. * @return boolean
  351. */
  352. public static function isUploadProgressAvailable()
  353. {
  354. return is_callable(self::$_callbackUploadProgress);
  355. }
  356. /**
  357. * Prepare the $_FILES array to match the internal syntax of one file per entry
  358. *
  359. * @param array $files
  360. * @return array
  361. */
  362. protected function _prepareFiles(array $files = array())
  363. {
  364. $result = array();
  365. foreach ($files as $form => $content) {
  366. if (is_array($content['name'])) {
  367. foreach ($content as $param => $file) {
  368. foreach ($file as $number => $target) {
  369. $result[$form . '_' . $number . '_'][$param] = $target;
  370. $result[$form . '_' . $number . '_']['options'] = $this->_options;
  371. $result[$form . '_' . $number . '_']['validated'] = false;
  372. $result[$form . '_' . $number . '_']['received'] = false;
  373. $result[$form . '_' . $number . '_']['filtered'] = false;
  374. $result[$form]['multifiles'][$number] = $form . '_' . $number . '_';
  375. $result[$form]['name'] = $form;
  376. }
  377. }
  378. } else {
  379. $result[$form] = $content;
  380. $result[$form]['options'] = $this->_options;
  381. $result[$form]['validated'] = false;
  382. $result[$form]['received'] = false;
  383. $result[$form]['filtered'] = false;
  384. }
  385. }
  386. return $result;
  387. }
  388. }