PageRenderTime 45ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/wp-content/plugins/zencache/src/includes/closures/Plugin/HtaccessUtils.php

https://gitlab.com/iamgraeme/royalmile
PHP | 273 lines | 151 code | 29 blank | 93 comment | 45 complexity | c7ebf73a9e88322f0c79a0299780b16e MD5 | raw file
  1. <?php
  2. namespace WebSharks\ZenCache;
  3. /*
  4. * Unique comment marker.
  5. *
  6. * @since 151220 Enhancing `.htaccess` tweaks.
  7. *
  8. * @return string Used in `.htaccess` parsing.
  9. */
  10. $self->htaccess_marker = 'WmVuQ2FjaGU';
  11. /*
  12. * Plugin options that have associated htaccess rules.
  13. *
  14. * @since 15xxxx Improving `.htaccess` tweaks.
  15. *
  16. * @return array Plugin options that have associated htaccess rules
  17. *
  18. * @note We keep track of this to avoid the issue described here: http://git.io/vEFIH
  19. */
  20. $self->options_with_htaccess_rules = array('cdn_enable');
  21. /*
  22. * Add template blocks to `/.htaccess` file.
  23. *
  24. * @since 151114 Adding `.htaccess` tweaks.
  25. *
  26. * @return boolean True if added successfully.
  27. *
  28. * @TODO Improve error reporting detail to better catch unexpected failures; see http://git.io/vEFLT
  29. */
  30. $self->addWpHtaccess = function () use ($self) {
  31. global $is_apache;
  32. if (!$is_apache) {
  33. return false; // Not running the Apache web server.
  34. }
  35. if (!$self->options['enable']) {
  36. return true; // Nothing to do.
  37. }
  38. if (!$self->needHtaccessRules()) {
  39. if($self->findHtaccessMarker()) { // Do we need to clean up previously added rules?
  40. $self->removeWpHtaccess(); // Fail silently since we don't need rules in place.
  41. }
  42. return true; // Nothing to do; no options enabled that require htaccess rules.
  43. }
  44. if (!$self->removeWpHtaccess()) {
  45. return false; // Unable to remove.
  46. }
  47. if (!($htaccess = $self->readHtaccessFile())) {
  48. return false; // Failure; could not read file or invalid UTF8 encountered, file may be corrupt.
  49. }
  50. $template_blocks = ''; // Initialize.
  51. if (is_dir($templates_dir = dirname(dirname(dirname(__FILE__))).'/templates/htaccess')) {
  52. foreach (scandir($templates_dir) as $_template_file) {
  53. switch ($_template_file) {
  54. }
  55. }
  56. unset($_template_file); // Housekeeping.
  57. }
  58. if(empty($template_blocks)) { // Do we need to add anything to htaccess?
  59. $self->closeHtaccessFile($htaccess); // No need to write to htaccess file in this case.
  60. return true; // Nothing to do, but no failures either.
  61. }
  62. $template_header = '# BEGIN '.NAME.' '.$self->htaccess_marker.' (the '.$self->htaccess_marker.' marker is required for '.NAME.'; do not remove)'."\n";
  63. $template_footer = '# END '.NAME.' '.$self->htaccess_marker;
  64. $htaccess['file_contents'] = $template_header.trim($template_blocks)."\n".$template_footer."\n\n".$htaccess['file_contents'];
  65. if (!$self->writeHtaccessFile($htaccess, true)) {
  66. return false; // Failure; could not write changes.
  67. }
  68. return true; // Added successfully.
  69. };
  70. /*
  71. * Remove template blocks from `/.htaccess` file.
  72. *
  73. * @since 151114 Adding `.htaccess` tweaks.
  74. *
  75. * @return boolean True if removed successfully.
  76. *
  77. * @TODO Improve error reporting detail to better catch unexpected failures; see http://git.io/vEFLT
  78. */
  79. $self->removeWpHtaccess = function () use ($self) {
  80. global $is_apache;
  81. if (!$is_apache) {
  82. return false; // Not running the Apache web server.
  83. }
  84. if (!($htaccess_file = $self->findHtaccessFile())) {
  85. return true; // File does not exist.
  86. }
  87. if (!$self->findHtaccessMarker()) {
  88. return true; // Template blocks are already gone.
  89. }
  90. if (!($htaccess = $self->readHtaccessFile())) {
  91. return false; // Failure; could not read file, create file, or invalid UTF8 encountered, file may be corrupt.
  92. }
  93. $regex = '/#\s*BEGIN\s+'.preg_quote(NAME, '/').'\s+'.$self->htaccess_marker.'.*?#\s*END\s+'.preg_quote(NAME, '/').'\s+'.$self->htaccess_marker.'\s*/is';
  94. $htaccess['file_contents'] = preg_replace($regex, '', $htaccess['file_contents']);
  95. if (!$self->writeHtaccessFile($htaccess, false)) {
  96. return false; // Failure; could not write changes.
  97. }
  98. return true; // Removed successfully.
  99. };
  100. /*
  101. * Finds absolute server path to `/.htaccess` file.
  102. *
  103. * @since 151114 Adding `.htaccess` tweaks.
  104. *
  105. * @return string Absolute server path to `/.htaccess` file;
  106. * else an empty string if unable to locate the file.
  107. */
  108. $self->findHtaccessFile = function () use ($self) {
  109. $file = ''; // Initialize.
  110. $home_path = $self->wpHomePath();
  111. if (is_file($htaccess_file = $home_path.'.htaccess')) {
  112. $file = $htaccess_file;
  113. }
  114. return $file;
  115. };
  116. /*
  117. * Determines if there are any plugin options enabled that require htaccess rules to be added.
  118. *
  119. * @since 15xxxx Improving `.htaccess` tweaks.
  120. *
  121. * @return bool True when an option is enabled that requires htaccess rules, false otherwise.
  122. */
  123. $self->needHtaccessRules = function () use ($self) {
  124. if(!is_array($self->options_with_htaccess_rules)) {
  125. return false; // Nothing to do.
  126. }
  127. foreach ($self->options_with_htaccess_rules as $option) {
  128. if ($self->options[$option]) {
  129. return true; // Yes, there are options enabled that require htaccess rules.
  130. }
  131. }
  132. return false; // No, there are no options enabled that require htaccess rules.
  133. };
  134. /*
  135. * Utility method used to check if htaccess file contains $htaccess_marker
  136. *
  137. * @since 151114 Adding `.htaccess` tweaks.
  138. *
  139. * @param string $htaccess_marker Unique comment marker used to identify rules added by this plugin.
  140. *
  141. * @return bool False on failure or when marker does not exist in htaccess, true otherwise.
  142. */
  143. $self->findHtaccessMarker = function ($htaccess_marker = '') use ($self) {
  144. if (!($htaccess_file = $self->findHtaccessFile())) {
  145. return false; // File does not exist.
  146. }
  147. if (!is_readable($htaccess_file)) {
  148. return false; // Not possible.
  149. }
  150. if (($htaccess_file_contents = file_get_contents($htaccess_file)) === false) {
  151. return false; // Failure; could not read file.
  152. }
  153. if (empty($htaccess_marker)) {
  154. $htaccess_marker = $self->htaccess_marker;
  155. }
  156. if (stripos($htaccess_file_contents, $htaccess_marker) === false) {
  157. return false; // Htaccess marker is missing
  158. }
  159. return true; // Htaccess has the marker
  160. };
  161. /*
  162. * Gets contents of `/.htaccess` file with exclusive lock to read+write. If file doesn't exist, we attempt to create it.
  163. *
  164. * @since 151220 Improving `.htaccess` utils.
  165. *
  166. * @param string $htaccess_file Absolute path to the htaccess file. Optional.
  167. * If not provided, we attempt to find it or create it if it doesn't exist.
  168. *
  169. * @return array|bool Returns an array with data necessary to call $self->writeHtaccessFile():
  170. * `fp` a file pointer resource, `file_contents` a string. Returns `false` on failure.
  171. *
  172. * @note If a call to this method is not followed by a call to $self->writeHtaccessFile(),
  173. * you must make sure that you unlock and close the `fp` resource yourself.
  174. */
  175. $self->readHtaccessFile = function ($htaccess_file = '') use ($self) {
  176. if (empty($htaccess_file) && !($htaccess_file = $self->findHtaccessFile())) {
  177. if (!is_writable($self->wpHomePath()) || file_put_contents($htaccess_file = $self->wpHomePath().'.htaccess', '') === false) {
  178. return false; // Unable to find and/or create `.htaccess`.
  179. } // If it doesn't exist, we create the `.htaccess` file here.
  180. }
  181. if (!is_readable($htaccess_file) || !is_writable($htaccess_file) || (defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS)) {
  182. return false; // Not possible.
  183. }
  184. if (!($fp = fopen($htaccess_file, 'rb+')) || !flock($fp, LOCK_EX)) {
  185. fclose($fp); // Just in case we opened it before failing to obtain a lock.
  186. return false; // Failure; could not open file and obtain an exclusive lock.
  187. }
  188. if (($file_contents = fread($fp, filesize($htaccess_file))) && ($file_contents === wp_check_invalid_utf8($file_contents))) {
  189. rewind($fp); // Rewind pointer to beginning of file.
  190. return compact('fp', 'file_contents');
  191. } else { // Failure; could not read file or invalid UTF8 encountered, file may be corrupt.
  192. flock($fp, LOCK_UN);
  193. fclose($fp);
  194. return false;
  195. }
  196. };
  197. /*
  198. * Writes to `/.htaccess` file using provided file pointer.
  199. *
  200. * @since 151220 Improving `.htaccess` utils.
  201. *
  202. * @param array $htaccess Array containing `fp` file resource pointing to htaccess file and `file_contents` to write to file.
  203. * @param bool $require_marker Whether or not to require the marker be present in contents before writing.
  204. * @param string $htaccess_marker Unique comment marker used to identify rules added by this plugin.
  205. *
  206. * @return bool True on success, false on failure.
  207. */
  208. $self->writeHtaccessFile = function (array $htaccess, $require_marker = true, $htaccess_marker = '') use ($self) {
  209. if (defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS) {
  210. return false; // Not possible.
  211. }
  212. if (!is_resource($htaccess['fp'])) {
  213. return false;
  214. }
  215. $htaccess_marker = $htaccess_marker ?: $self->htaccess_marker;
  216. $_have_marker = stripos($htaccess['file_contents'], $htaccess_marker);
  217. // Note: rewind() necessary here because we fread() above.
  218. if (($require_marker && $_have_marker === false) || !rewind($htaccess['fp']) || !ftruncate($htaccess['fp'], 0) || !fwrite($htaccess['fp'], $htaccess['file_contents'])) {
  219. flock($htaccess['fp'], LOCK_UN);
  220. fclose($htaccess['fp']);
  221. return false; // Failure; could not write changes.
  222. }
  223. fflush($htaccess['fp']);
  224. flock($htaccess['fp'], LOCK_UN);
  225. fclose($htaccess['fp']);
  226. return true;
  227. };
  228. /*
  229. * Utility method used to unlock and close htaccess file resource.
  230. *
  231. * @since 151114 Adding `.htaccess` tweaks.
  232. *
  233. * @param array $htaccess Array containing at least an `fp` file resource pointing to htaccess file.
  234. *
  235. * @return bool False on failure, true otherwise.
  236. */
  237. $self->closeHtaccessFile = function (array $htaccess) use ($self) {
  238. if (!is_resource($htaccess['fp'])) {
  239. return false; // Failure; requires a valid file resource.
  240. }
  241. flock($htaccess['fp'], LOCK_UN);
  242. fclose($htaccess['fp']);
  243. return true;
  244. };