From b868320b708e753def1fff183cc1318029a5cae3 Mon Sep 17 00:00:00 2001 From: Ryan Cramer Date: Fri, 20 Sep 2019 10:48:12 -0400 Subject: [PATCH] Add $config->setLocation(), $config->setPath() and $config->setUrl() methods to allow runtime modification of system paths/URLs. --- wire/core/Config.php | 143 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/wire/core/Config.php b/wire/core/Config.php index 9156b023..7455cd43 100644 --- a/wire/core/Config.php +++ b/wire/core/Config.php @@ -218,6 +218,149 @@ class Config extends WireData { return $for === '' ? $this->urls : $this->url($for); } + /** + * Given a directory to a named location, updates $config->paths and $config->urls for it + * + * - Paths relative to PW installation root should omit the leading slash, i.e. use `site/templates/` and NOT `/site/templates/`. + * + * - If specifying just the `$dir` argument, it updates both `$config->paths` and `$config->urls` for it. + * + * - If specifying both `$dir` and `$url` arguments, then `$dir` refers to `$config->paths` and `$url` refers to `$config->urls`. + * + * - The `$for` argument can be: `cache`, `logs`, `files`, `tmp`, `templates`, or one of your own. Other named locations may + * also work, but since they can potentially be used before PW’s “ready” state, they may not be reliable. + * + * - **Warning:** anything that changes a system URL may make the URL no longer have the protection of the root .htaccess file. + * As a result, if you modify system URLs for anything on a live server, you should also update your .htaccess file to + * reflect your changes (while leaving existing rules for original URL in place). + * + * The following example would be in /site/init.php or /site/ready.php (or equivalent module method). In this example we + * are changing the location (path and URL) of our /site/templates/ to use a new version of the files in /site/dev-templates/ + * so that we can test them out with user 'karen', while all other users on the site get our regular templates. + * ~~~~~ + * // change templates path and URL to /site/dev-templates/ when user name is 'karen' + * if($user->name == 'karen') { + * $config->setLocation('templates', 'site/dev-templates/'); + * } + * ~~~~~ + * + * @param string $for Named location from `$config->paths` or `$config->urls`, one of: `cache`, `logs`, `files`, `tmp`, `templates`, or your own. + * @param string $dir Directory or URL to the location. Should be either a path or URL relative to current installation root (recommended), + * or an absolute disk path that resolves somewhere in current installation root. If specifying an absolute path outside of the installation + * root, then you’ll also want to provide the $url argument since PW won’t know it. You may also specify a blank string for this argument + * if you only want to set the $url argument. + * @param string|bool $url If the $dir argument represents both the path and URL relative to site root, you can omit this argument. + * If path and URL cannot be derived from one another, or you only want to modify the $url (leaving $dir blank), you + * can specify the URL in this argument. Specify boolean false if you only want to set the $dir (path) and not detect the $url from it. + * @return self + * @throws WireException If request cannot be accommodated + * @since 3.0.141 + * + */ + public function setLocation($for, $dir, $url = '') { + + if($for === 'root') throw new WireException('Root path can only be changed at boot'); + + if(!empty($dir)) { + $rootPath = $this->paths->get('root'); + + // make sure path uses unix-style slashes + $dir = Paths::normalizeSeparators($dir); + + // if given path is inclusive of root path, make path relative to site root + if(strpos($dir, $rootPath) === 0) $dir = substr($dir, strlen($rootPath)); + + // ensure trailing slash + if(substr($dir, -1) !== '/') $dir .= '/'; + } + + // now determine the URL to set + if($url === false) { + // arguments say to skip setting URL + } else if(empty($url)) { + // URL and path are the same relative to site root + if(!empty($dir)) $url = $dir; + } else { + // given a custom URL + $rootUrl = $this->urls->get('root'); + // if URL begins at PW installation root, remove the root part of the URL + if(strpos($url, $rootUrl) === 0) $url = substr($url, strlen($rootUrl)); + // ensure trailing slash + if(substr($url, -1) !== '/' && strpos($url, '?') === false && strpos($url, '#') === false) $url .= '/'; + } + + if(!empty($path)) $this->paths->set($for, $dir); + if(!empty($url)) $this->urls->set($for, $url); + + return $this; + } + + /** + * Change or set just the server disk path for the named location (leaving URL as-is) + * + * - If you want to update both disk path and URL at the same time, or if URL and path are going to be the same relative to + * installation root, use the `setLocation()` method instead. + * + * - Paths relative to PW installation root should omit the leading slash, i.e. use `site/templates/` and NOT `/site/templates/`. + * + * - The `$for` argument can be: `cache`, `logs`, `files`, `tmp`, `templates`, or one of your own. Other named locations may + * also work, but since they can potentially be used before PW’s “ready” state, they may not be reliable. + * + * @param string $for Named location from `$config->paths`, one of: `cache`, `logs`, `files`, `tmp`, `templates`, or your own. + * @param string $path Path relative to PW installation root (no leading slash), or absolute path if not. + * @return self + * @throws WireException + * @since 3.0.141 + * + */ + public function setPath($for, $path) { + return $this->setLocation($for, $path, false); + } + + /** + * Change or set just the URL for the named location (leaving server disk path as-is) + * + * - If you want to update both disk path and URL at the same time, or if URL and path are going to be the same relative to + * installation root, use the `setLocation()` method instead. + * + * - Paths relative to PW installation root should omit the leading slash, i.e. use `site/templates/` and NOT `/site/templates/`. + * + * - The `$for` argument can be: `cache`, `logs`, `files`, `tmp`, `templates`, or one of your own. Other named locations may + * also work, but since they can potentially be used before PW’s “ready” state, they may not be reliable. + * + * - **Warning:** anything that changes a system URL may make the URL no longer have the protection of the root .htaccess file. + * As a result, if you modify system URLs for anything on a live server, you should also update your .htaccess file to + * reflect your changes (while leaving existing rules for original URL in place). + * + * The following examples would go in /site/ready.php. + * + * Let’s say we created a symbolic link in our web root `/tiedostot/` (Finnish for “files”) that points to /site/assets/files/. + * We want all of our file URLs to appear as “/tiedostot/1234/img.jpg” rather than “/site/assets/files/1234/img.jpg”. We would + * change the URL for ProcessWire’s `$config->urls->files` to point there like this example below. (Please also see warning above) + * ~~~~~ + * if($page->template != 'admin') { + * $config->setUrl('files', 'tiedostot/'); + * } + * ~~~~~ + * In this next example, we are changing all of our file URLs on the front-end to point a cookieless subdomain that maps all + * requests to the root path of https://files.domain.com to /site/assets/files/. The example works for CDNs as well. + * ~~~~~ + * if($page->template != 'admin) { + * $config->setUrl('files', 'https://files.domain.com/'); + * } + * ~~~~~ + * + * @param string $for Named location from `$config->urls`, one of: `cache`, `logs`, `files`, `tmp`, `templates`, or your own. + * @param string $url URL relative to PW installation root (no leading slash) or absolute URL if not (optionally including scheme and domain). + * @return self + * @throws WireException + * @since 3.0.141 + * + */ + public function setUrl($for, $url) { + return $this->setLocation($for, '', $url); + } + /** * Get disk path for requested resource or module *