Нет описания

class-wc-product-download.php 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. <?php
  2. /**
  3. * Represents a file which can be downloaded.
  4. *
  5. * @package WooCommerce\Classes
  6. * @version 3.0.0
  7. * @since 3.0.0
  8. */
  9. defined( 'ABSPATH' ) || exit;
  10. /**
  11. * Product download class.
  12. */
  13. class WC_Product_Download implements ArrayAccess {
  14. /**
  15. * Data array.
  16. *
  17. * @since 3.0.0
  18. * @var array
  19. */
  20. protected $data = array(
  21. 'id' => '',
  22. 'name' => '',
  23. 'file' => '',
  24. );
  25. /**
  26. * Returns all data for this object.
  27. *
  28. * @return array
  29. */
  30. public function get_data() {
  31. return $this->data;
  32. }
  33. /**
  34. * Get allowed mime types.
  35. *
  36. * @return array
  37. */
  38. public function get_allowed_mime_types() {
  39. return apply_filters( 'woocommerce_downloadable_file_allowed_mime_types', get_allowed_mime_types() );
  40. }
  41. /**
  42. * Get type of file path set.
  43. *
  44. * @param string $file_path optional.
  45. * @return string absolute, relative, or shortcode.
  46. */
  47. public function get_type_of_file_path( $file_path = '' ) {
  48. $file_path = $file_path ? $file_path : $this->get_file();
  49. $parsed_url = parse_url( $file_path );
  50. if (
  51. $parsed_url &&
  52. isset( $parsed_url['host'] ) && // Absolute url means that it has a host.
  53. ( // Theoretically we could permit any scheme (like ftp as well), but that has not been the case before. So we allow none or http(s).
  54. ! isset( $parsed_url['scheme'] ) ||
  55. in_array( $parsed_url['scheme'], array( 'http', 'https' ) )
  56. )
  57. ) {
  58. return 'absolute';
  59. } elseif ( '[' === substr( $file_path, 0, 1 ) && ']' === substr( $file_path, -1 ) ) {
  60. return 'shortcode';
  61. } else {
  62. return 'relative';
  63. }
  64. }
  65. /**
  66. * Get file type.
  67. *
  68. * @return string
  69. */
  70. public function get_file_type() {
  71. $type = wp_check_filetype( strtok( $this->get_file(), '?' ), $this->get_allowed_mime_types() );
  72. return $type['type'];
  73. }
  74. /**
  75. * Get file extension.
  76. *
  77. * @return string
  78. */
  79. public function get_file_extension() {
  80. $parsed_url = wp_parse_url( $this->get_file(), PHP_URL_PATH );
  81. return pathinfo( $parsed_url, PATHINFO_EXTENSION );
  82. }
  83. /**
  84. * Check if file is allowed.
  85. *
  86. * @return boolean
  87. */
  88. public function is_allowed_filetype() {
  89. $file_path = $this->get_file();
  90. // File types for URL-based files located on the server should get validated.
  91. $is_file_on_server = false;
  92. if ( false !== stripos( $file_path, network_site_url( '/', 'https' ) ) ||
  93. false !== stripos( $file_path, network_site_url( '/', 'http' ) ) ||
  94. false !== stripos( $file_path, site_url( '/', 'https' ) ) ||
  95. false !== stripos( $file_path, site_url( '/', 'http' ) )
  96. ) {
  97. $is_file_on_server = true;
  98. }
  99. if ( ! $is_file_on_server && 'relative' !== $this->get_type_of_file_path() ) {
  100. return true;
  101. }
  102. return ! $this->get_file_extension() || in_array( $this->get_file_type(), $this->get_allowed_mime_types(), true );
  103. }
  104. /**
  105. * Validate file exists.
  106. *
  107. * @return boolean
  108. */
  109. public function file_exists() {
  110. if ( 'relative' !== $this->get_type_of_file_path() ) {
  111. return true;
  112. }
  113. $file_url = $this->get_file();
  114. if ( '..' === substr( $file_url, 0, 2 ) || '/' !== substr( $file_url, 0, 1 ) ) {
  115. $file_url = realpath( ABSPATH . $file_url );
  116. } elseif ( substr( WP_CONTENT_DIR, strlen( untrailingslashit( ABSPATH ) ) ) === substr( $file_url, 0, strlen( substr( WP_CONTENT_DIR, strlen( untrailingslashit( ABSPATH ) ) ) ) ) ) {
  117. $file_url = realpath( WP_CONTENT_DIR . substr( $file_url, 11 ) );
  118. }
  119. return apply_filters( 'woocommerce_downloadable_file_exists', file_exists( $file_url ), $this->get_file() );
  120. }
  121. /*
  122. |--------------------------------------------------------------------------
  123. | Setters
  124. |--------------------------------------------------------------------------
  125. */
  126. /**
  127. * Set ID.
  128. *
  129. * @param string $value Download ID.
  130. */
  131. public function set_id( $value ) {
  132. $this->data['id'] = wc_clean( $value );
  133. }
  134. /**
  135. * Set name.
  136. *
  137. * @param string $value Download name.
  138. */
  139. public function set_name( $value ) {
  140. $this->data['name'] = wc_clean( $value );
  141. }
  142. /**
  143. * Set previous_hash.
  144. *
  145. * @deprecated 3.3.0 No longer using filename based hashing to keep track of files.
  146. * @param string $value Previous hash.
  147. */
  148. public function set_previous_hash( $value ) {
  149. wc_deprecated_function( __FUNCTION__, '3.3' );
  150. $this->data['previous_hash'] = wc_clean( $value );
  151. }
  152. /**
  153. * Set file.
  154. *
  155. * @param string $value File URL/Path.
  156. */
  157. public function set_file( $value ) {
  158. // A `///` is recognized as an "absolute", but on the filesystem, so it bypasses the mime check in `self::is_allowed_filetype`.
  159. // This will strip extra prepending / to the maximum of 2.
  160. if ( preg_match( '#^//+(/[^/].+)$#i', $value, $matches ) ) {
  161. $value = $matches[1];
  162. }
  163. switch ( $this->get_type_of_file_path( $value ) ) {
  164. case 'absolute':
  165. $this->data['file'] = esc_url_raw( $value );
  166. break;
  167. default:
  168. $this->data['file'] = wc_clean( $value );
  169. break;
  170. }
  171. }
  172. /*
  173. |--------------------------------------------------------------------------
  174. | Getters
  175. |--------------------------------------------------------------------------
  176. */
  177. /**
  178. * Get id.
  179. *
  180. * @return string
  181. */
  182. public function get_id() {
  183. return $this->data['id'];
  184. }
  185. /**
  186. * Get name.
  187. *
  188. * @return string
  189. */
  190. public function get_name() {
  191. return $this->data['name'];
  192. }
  193. /**
  194. * Get previous_hash.
  195. *
  196. * @deprecated 3.3.0 No longer using filename based hashing to keep track of files.
  197. * @return string
  198. */
  199. public function get_previous_hash() {
  200. wc_deprecated_function( __FUNCTION__, '3.3' );
  201. return $this->data['previous_hash'];
  202. }
  203. /**
  204. * Get file.
  205. *
  206. * @return string
  207. */
  208. public function get_file() {
  209. return $this->data['file'];
  210. }
  211. /*
  212. |--------------------------------------------------------------------------
  213. | ArrayAccess/Backwards compatibility.
  214. |--------------------------------------------------------------------------
  215. */
  216. /**
  217. * OffsetGet.
  218. *
  219. * @param string $offset Offset.
  220. * @return mixed
  221. */
  222. public function offsetGet( $offset ) {
  223. switch ( $offset ) {
  224. default:
  225. if ( is_callable( array( $this, "get_$offset" ) ) ) {
  226. return $this->{"get_$offset"}();
  227. }
  228. break;
  229. }
  230. return '';
  231. }
  232. /**
  233. * OffsetSet.
  234. *
  235. * @param string $offset Offset.
  236. * @param mixed $value Offset value.
  237. */
  238. public function offsetSet( $offset, $value ) {
  239. switch ( $offset ) {
  240. default:
  241. if ( is_callable( array( $this, "set_$offset" ) ) ) {
  242. return $this->{"set_$offset"}( $value );
  243. }
  244. break;
  245. }
  246. }
  247. /**
  248. * OffsetUnset.
  249. *
  250. * @param string $offset Offset.
  251. */
  252. public function offsetUnset( $offset ) {}
  253. /**
  254. * OffsetExists.
  255. *
  256. * @param string $offset Offset.
  257. * @return bool
  258. */
  259. public function offsetExists( $offset ) {
  260. return in_array( $offset, array_keys( $this->data ), true );
  261. }
  262. }