PageRenderTime 37ms CodeModel.GetById 8ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-content/plugins/w3-total-cache/Varnish_Flush.php

https://bitbucket.org/reareaf/wp-re
PHP | 433 lines | 253 code | 70 blank | 110 comment | 58 complexity | 3e907df0b0d0ffe991290f71e6731a3d MD5 | raw file
Possible License(s): BSD-3-Clause, MIT, Apache-2.0
  1. <?php
  2. namespace W3TC;
  3. /**
  4. * Class Varnish_Flush
  5. */
  6. class Varnish_Flush {
  7. /**
  8. * Debug flag
  9. *
  10. * @var bool
  11. */
  12. var $_debug = false;
  13. /**
  14. * Varnish servers
  15. *
  16. * @var array
  17. */
  18. var $_servers = array();
  19. /**
  20. * Operation timeout
  21. *
  22. * @var int
  23. */
  24. var $_timeout = 30;
  25. /**
  26. * Advanced cache config
  27. */
  28. var $_config = null;
  29. /**
  30. * Array of already flushed urls
  31. *
  32. * @var array
  33. */
  34. private $queued_urls = array();
  35. private $flush_operation_requested = false;
  36. /**
  37. * PHP5-style constructor
  38. */
  39. function __construct() {
  40. $this->_config = Dispatcher::config();
  41. $this->_debug = $this->_config->get_boolean( 'varnish.debug' );
  42. $this->_servers = $this->_config->get_array( 'varnish.servers' );
  43. $this->_timeout = $this->_config->get_integer( 'timelimit.varnish_purge' );
  44. }
  45. /**
  46. * Purge URI
  47. *
  48. * @param string $url
  49. * @return boolean
  50. */
  51. protected function _purge( $url ) {
  52. @set_time_limit( $this->_timeout );
  53. $return = true;
  54. foreach ( (array) $this->_servers as $server ) {
  55. $response = $this->_request( $server, $url );
  56. if ( is_wp_error( $response ) ) {
  57. $this->_log( $url, sprintf( 'Unable to send request: %s.', implode( '; ', $response->get_error_messages() ) ) );
  58. $return = false;
  59. } elseif ( $response['response']['code'] !== 200 ) {
  60. $this->_log( $url, 'Bad response: ' . $response['response']['status'] );
  61. $return = false;
  62. } else {
  63. $this->_log( $url, 'PURGE OK' );
  64. }
  65. }
  66. return $return;
  67. }
  68. /*
  69. * Sends purge request. Cannt use default wp HTTP implementation
  70. * if we send request to different host than specified in $url
  71. *
  72. * @param $url string
  73. */
  74. function _request( $varnish_server, $url ) {
  75. $parse_url = @parse_url( $url );
  76. if ( !$parse_url || !isset( $parse_url['host'] ) )
  77. return new \WP_Error( 'http_request_failed', 'Unrecognized URL format ' . $url );
  78. $host = $parse_url['host'];
  79. $port = ( isset( $parse_url['port'] ) ? (int) $parse_url['port'] : 80 );
  80. $path = ( !empty( $parse_url['path'] ) ? $parse_url['path'] : '/' );
  81. $query = ( isset( $parse_url['query'] ) ? $parse_url['query'] : '' );
  82. $request_uri = $path . ( $query != '' ? '?' . $query : '' );
  83. if ( strpos( $varnish_server, ':' ) )
  84. list( $varnish_host, $varnish_port ) = explode( ':', $varnish_server );
  85. else {
  86. $varnish_host = $varnish_server;
  87. $varnish_port = 80;
  88. }
  89. // if url host is the same as varnish server - we can use regular
  90. // wordpress http infrastructure, otherwise custom request should be
  91. // sent using fsockopen, since we send request to other server than
  92. // specified by $url
  93. if ( $host == $varnish_host && $port == $varnish_port )
  94. return Util_Http::request( $url, array( 'method' => 'PURGE' ) );
  95. $request_headers_array = array(
  96. sprintf( 'PURGE %s HTTP/1.1', $request_uri ),
  97. sprintf( 'Host: %s', $host ),
  98. sprintf( 'User-Agent: %s', W3TC_POWERED_BY ),
  99. 'Connection: close'
  100. );
  101. $request_headers = implode( "\r\n", $request_headers_array );
  102. $request = $request_headers . "\r\n\r\n";
  103. // log what we are about to do
  104. $this->_log( $url, sprintf( 'Connecting to %s ...', $varnish_host ) );
  105. $this->_log( $url, sprintf( 'PURGE %s HTTP/1.1', $request_uri ) );
  106. $this->_log( $url, sprintf( 'Host: %s', $host ) );
  107. $errno = null;
  108. $errstr = null;
  109. $fp = @fsockopen( $varnish_host, $varnish_port, $errno, $errstr, 10 );
  110. if ( !$fp )
  111. return new \WP_Error( 'http_request_failed', $errno . ': ' . $errstr );
  112. @stream_set_timeout( $fp, 60 );
  113. @fputs( $fp, $request );
  114. $response = '';
  115. while ( !@feof( $fp ) )
  116. $response .= @fgets( $fp, 4096 );
  117. @fclose( $fp );
  118. list( $response_headers, $contents ) = explode( "\r\n\r\n", $response, 2 );
  119. $matches = null;
  120. if ( preg_match( '~^HTTP/1.[01] (\d+)~', $response_headers, $matches ) ) {
  121. $code = (int)$matches[1];
  122. $a = explode( "\n", $response_headers );
  123. $status = ( count( $a ) >= 1 ? $a[0] : '' );
  124. $return = array(
  125. 'response' => array(
  126. 'code' => $code,
  127. 'status' => $status
  128. )
  129. );
  130. return $return;
  131. }
  132. return new \WP_Error( 'http_request_failed',
  133. 'Unrecognized response header' . $response_headers );
  134. }
  135. /**
  136. * Write log entry
  137. *
  138. * @param string $url
  139. * @param string $msg
  140. * @return bool|int
  141. */
  142. function _log( $url, $msg ) {
  143. if ( $this->_debug ) {
  144. $data = sprintf( "[%s] [%s] %s\n", date( 'r' ), $url, $msg );
  145. $data = strtr( $data, '<>', '' );
  146. $filename = Util_Debug::log_filename( 'varnish' );
  147. return @file_put_contents( $filename, $data, FILE_APPEND );
  148. }
  149. return true;
  150. }
  151. /**
  152. * Flush varnish cache
  153. */
  154. function flush() {
  155. $this->flush_operation_requested = true;
  156. return true;
  157. }
  158. private function do_flush() {
  159. if ( !is_network_admin() ) {
  160. $full_urls = array( get_home_url() . '/.*' );
  161. $full_urls = Util_PageUrls::complement_with_mirror_urls(
  162. $full_urls );
  163. foreach ( $full_urls as $url )
  164. $this->_purge( $url );
  165. } else {
  166. // todo: remove. doesnt work for all caches.
  167. // replace with tool to flush network
  168. global $wpdb;
  169. $protocall = Util_Environment::is_https() ? 'https://' : 'http://';
  170. // If WPMU Domain Mapping plugin is installed and active
  171. if ( defined( 'SUNRISE_LOADED' ) && SUNRISE_LOADED && isset( $wpdb->dmtable ) && !empty( $wpdb->dmtable ) ) {
  172. $blogs = $wpdb->get_results( "SELECT {$wpdb->blogs}.domain, {$wpdb->blogs}.path, {$wpdb->dmtable}.domain AS mapped_domain
  173. FROM {$wpdb->dmtable}
  174. RIGHT JOIN {$wpdb->blogs} ON {$wpdb->dmtable}.blog_id = {$wpdb->blogs}.blog_id
  175. WHERE site_id = {$wpdb->siteid}
  176. AND spam = 0
  177. AND deleted = 0
  178. AND archived = '0'
  179. " );
  180. foreach ( $blogs as $blog ) {
  181. if ( !isset( $blog->mapped_domain ) )
  182. $url = $protocall . $blog->domain . ( strlen( $blog->path )>1? '/' . trim( $blog->path, '/' ) : '' ) . '/.*';
  183. else
  184. $url = $protocall . $blog->mapped_domain . '/.*';
  185. $this->_purge( $url );
  186. }
  187. }else {
  188. if ( !Util_Environment::is_wpmu_subdomain() ) {
  189. $this->_purge( get_home_url().'/.*' );
  190. } else {
  191. $blogs = $wpdb->get_results( "
  192. SELECT domain, path
  193. FROM {$wpdb->blogs}
  194. WHERE site_id = '{$wpdb->siteid}'
  195. AND spam = 0
  196. AND deleted = 0
  197. AND archived = '0'
  198. " );
  199. foreach ( $blogs as $blog ) {
  200. $url = $protocall . $blog->domain . ( strlen( $blog->path )>1? '/' . trim( $blog->path, '/' ) : '' ) . '/.*';
  201. $this->_purge( $url );
  202. }
  203. }
  204. }
  205. }
  206. }
  207. /**
  208. * Flushes varnish post cache
  209. *
  210. * @param integer $post_id
  211. * @return boolean
  212. */
  213. function flush_post( $post_id ) {
  214. if ( !$post_id ) {
  215. $post_id = Util_Environment::detect_post_id();
  216. }
  217. if ( $post_id ) {
  218. $full_urls = array();
  219. $post = null;
  220. $terms = array();
  221. $feeds = $this->_config->get_array( 'pgcache.purge.feed.types' );
  222. $limit_post_pages = $this->_config->get_integer( 'pgcache.purge.postpages_limit' );
  223. if ( $this->_config->get_boolean( 'pgcache.purge.terms' ) || $this->_config->get_boolean( 'varnish.pgcache.feed.terms' ) ) {
  224. $taxonomies = get_post_taxonomies( $post_id );
  225. $terms = wp_get_post_terms( $post_id, $taxonomies );
  226. }
  227. switch ( true ) {
  228. case $this->_config->get_boolean( 'pgcache.purge.author' ):
  229. case $this->_config->get_boolean( 'pgcache.purge.archive.daily' ):
  230. case $this->_config->get_boolean( 'pgcache.purge.archive.monthly' ):
  231. case $this->_config->get_boolean( 'pgcache.purge.archive.yearly' ):
  232. case $this->_config->get_boolean( 'pgcache.purge.feed.author' ):
  233. $post = get_post( $post_id );
  234. }
  235. $front_page = get_option( 'show_on_front' );
  236. /**
  237. * Home (Frontpage) URL
  238. */
  239. if ( ( $this->_config->get_boolean( 'pgcache.purge.home' ) && $front_page == 'posts' )||
  240. $this->_config->get_boolean( 'pgcache.purge.front_page' ) ) {
  241. $full_urls = array_merge( $full_urls,
  242. Util_PageUrls::get_frontpage_urls( $limit_post_pages ) );
  243. }
  244. /**
  245. * Home (Post page) URL
  246. */
  247. if ( $this->_config->get_boolean( 'pgcache.purge.home' ) && $front_page != 'posts' ) {
  248. $full_urls = array_merge( $full_urls,
  249. Util_PageUrls::get_postpage_urls( $limit_post_pages ) );
  250. }
  251. /**
  252. * Post URL
  253. */
  254. if ( $this->_config->get_boolean( 'pgcache.purge.post' ) ) {
  255. $full_urls = array_merge( $full_urls, Util_PageUrls::get_post_urls( $post_id ) );
  256. }
  257. /**
  258. * Post comments URLs
  259. */
  260. if ( $this->_config->get_boolean( 'pgcache.purge.comments' ) && function_exists( 'get_comments_pagenum_link' ) ) {
  261. $full_urls = array_merge( $full_urls, Util_PageUrls::get_post_comments_urls( $post_id ) );
  262. }
  263. /**
  264. * Post author URLs
  265. */
  266. if ( $this->_config->get_boolean( 'pgcache.purge.author' ) && $post ) {
  267. $full_urls = array_merge( $full_urls, Util_PageUrls::get_post_author_urls( $post->post_author, $limit_post_pages ) );
  268. }
  269. /**
  270. * Post terms URLs
  271. */
  272. if ( $this->_config->get_boolean( 'pgcache.purge.terms' ) ) {
  273. $full_urls = array_merge( $full_urls, Util_PageUrls::get_post_terms_urls( $terms, $limit_post_pages ) );
  274. }
  275. /**
  276. * Daily archive URLs
  277. */
  278. if ( $this->_config->get_boolean( 'pgcache.purge.archive.daily' ) && $post ) {
  279. $full_urls = array_merge( $full_urls, Util_PageUrls::get_daily_archive_urls( $post, $limit_post_pages ) );
  280. }
  281. /**
  282. * Monthly archive URLs
  283. */
  284. if ( $this->_config->get_boolean( 'pgcache.purge.archive.monthly' ) && $post ) {
  285. $full_urls = array_merge( $full_urls, Util_PageUrls::get_monthly_archive_urls( $post, $limit_post_pages ) );
  286. }
  287. /**
  288. * Yearly archive URLs
  289. */
  290. if ( $this->_config->get_boolean( 'pgcache.purge.archive.yearly' ) && $post ) {
  291. $full_urls = array_merge( $full_urls, Util_PageUrls::get_yearly_archive_urls( $post, $limit_post_pages ) );
  292. }
  293. /**
  294. * Feed URLs
  295. */
  296. if ( $this->_config->get_boolean( 'pgcache.purge.feed.blog' ) ) {
  297. $full_urls = array_merge( $full_urls,
  298. Util_PageUrls::get_feed_urls( $feeds ) );
  299. }
  300. if ( $this->_config->get_boolean( 'pgcache.purge.feed.comments' ) ) {
  301. $full_urls = array_merge( $full_urls, Util_PageUrls::get_feed_comments_urls( $post_id, $feeds ) );
  302. }
  303. if ( $this->_config->get_boolean( 'pgcache.purge.feed.author' ) && $post ) {
  304. $full_urls = array_merge( $full_urls, Util_PageUrls::get_feed_author_urls( $post->post_author, $feeds ) );
  305. }
  306. if ( $this->_config->get_boolean( 'pgcache.purge.feed.terms' ) ) {
  307. $full_urls = array_merge( $full_urls, Util_PageUrls::get_feed_terms_urls( $terms, $feeds ) );
  308. }
  309. /**
  310. * Purge selected pages
  311. */
  312. if ( $this->_config->get_array( 'pgcache.purge.pages' ) ) {
  313. $pages = $this->_config->get_array( 'pgcache.purge.pages' );
  314. $full_urls = array_merge( $full_urls, Util_PageUrls::get_pages_urls( $pages ) );
  315. }
  316. if ( $this->_config->get_string( 'pgcache.purge.sitemap_regex' ) ) {
  317. $sitemap_regex = $this->_config->get_string( 'pgcache.purge.sitemap_regex' );
  318. $full_urls[] = Util_Environment::home_domain_root_url() . '/' . trim( $sitemap_regex, "^$" );
  319. }
  320. // add mirror urls
  321. $full_urls = Util_PageUrls::complement_with_mirror_urls(
  322. $full_urls );
  323. $full_urls = apply_filters( 'varnish_flush_post_queued_urls',
  324. $full_urls );
  325. /**
  326. * Queue flush
  327. */
  328. if ( count( $full_urls ) ) {
  329. foreach ( $full_urls as $url )
  330. $this->queued_urls[$url] = '*';
  331. }
  332. return true;
  333. }
  334. return false;
  335. }
  336. /**
  337. * Flush a single url
  338. *
  339. * @param unknown $url
  340. */
  341. function flush_url( $url ) {
  342. $this->_purge( $url );
  343. }
  344. /**
  345. * Flushes global and repeated urls
  346. */
  347. function flush_post_cleanup() {
  348. if ( $this->flush_operation_requested ) {
  349. $this->do_flush();
  350. $count = 999;
  351. $this->flush_operation_requested = false;
  352. $this->queued_urls = array();
  353. } else {
  354. $count = count( $this->queued_urls );
  355. if ( $count > 0 ) {
  356. foreach ( $this->queued_urls as $url => $nothing )
  357. $this->flush_url( $url );
  358. $this->queued_urls = array();
  359. }
  360. }
  361. return $count;
  362. }
  363. }