PageRenderTime 50ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Zend/File/Transfer/Adapter/Http.php

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