Merge pull request #12385 from mattwire/test_transactions
[civicrm-core.git] / Civi / Core / Paths.php
1 <?php
2 namespace Civi\Core;
3
4 /**
5 * Class Paths
6 * @package Civi\Core
7 *
8 * This paths class translates path-expressions into local file paths and
9 * URLs. Path-expressions may take a few forms:
10 *
11 * - Paths and URLs may use a variable prefix. For example, '[civicrm.files]/upload'
12 * - Paths and URLS may be absolute.
13 * - Paths may be relative (base dir: [civicrm.files]).
14 * - URLs may be relative (base dir: [cms.root]).
15 */
16 class Paths {
17
18 const DEFAULT_URL = 'cms.root';
19 const DEFAULT_PATH = 'civicrm.files';
20
21 /**
22 * @var array
23 * Array(string $name => array(url => $, path => $)).
24 */
25 private $variables = array();
26
27 private $variableFactory = array();
28
29 /**
30 * Class constructor.
31 */
32 public function __construct() {
33 $paths = $this;
34 $this
35 ->register('civicrm.root', function () {
36 return \CRM_Core_Config::singleton()->userSystem->getCiviSourceStorage();
37 })
38 ->register('civicrm.packages', function () {
39 return array(
40 'path' => \Civi::paths()->getPath('[civicrm.root]/packages/'),
41 'url' => \Civi::paths()->getUrl('[civicrm.root]/packages/'),
42 );
43 })
44 ->register('civicrm.vendor', function () {
45 return array(
46 'path' => \Civi::paths()->getPath('[civicrm.root]/vendor/'),
47 'url' => \Civi::paths()->getUrl('[civicrm.root]/vendor/'),
48 );
49 })
50 ->register('civicrm.bower', function () {
51 return array(
52 'path' => \Civi::paths()->getPath('[civicrm.root]/bower_components/'),
53 'url' => \Civi::paths()->getUrl('[civicrm.root]/bower_components/'),
54 );
55 })
56 ->register('civicrm.files', function () {
57 return \CRM_Core_Config::singleton()->userSystem->getDefaultFileStorage();
58 })
59 ->register('wp.frontend.base', function () {
60 return array('url' => rtrim(CIVICRM_UF_BASEURL, '/') . '/');
61 })
62 ->register('wp.frontend', function () use ($paths) {
63 $config = \CRM_Core_Config::singleton();
64 $suffix = defined('CIVICRM_UF_WP_BASEPAGE') ? CIVICRM_UF_WP_BASEPAGE : $config->wpBasePage;
65 return array(
66 'url' => $paths->getVariable('wp.frontend.base', 'url') . $suffix,
67 );
68 })
69 ->register('wp.backend.base', function () {
70 return array('url' => rtrim(CIVICRM_UF_BASEURL, '/') . '/wp-admin/');
71 })
72 ->register('wp.backend', function () use ($paths) {
73 return array(
74 'url' => $paths->getVariable('wp.backend.base', 'url') . 'admin.php',
75 );
76 })
77 ->register('cms', function () {
78 return array(
79 'path' => \CRM_Core_Config::singleton()->userSystem->cmsRootPath(),
80 'url' => \CRM_Utils_System::baseCMSURL(),
81 );
82 })
83 ->register('cms.root', function () {
84 return array(
85 'path' => \CRM_Core_Config::singleton()->userSystem->cmsRootPath(),
86 // Misleading: this *removes* the language part of the URL, producing a pristine base URL.
87 'url' => \CRM_Utils_System::languageNegotiationURL(\CRM_Utils_System::baseCMSURL(), FALSE, TRUE),
88 );
89 });
90 }
91
92 /**
93 * Register a new URL/file path mapping.
94 *
95 * @param string $name
96 * The name of the variable.
97 * @param callable $factory
98 * Function which returns an array with keys:
99 * - path: string.
100 * - url: string.
101 * @return Paths
102 */
103 public function register($name, $factory) {
104 $this->variableFactory[$name] = $factory;
105 return $this;
106 }
107
108 /**
109 * @param string $name
110 * Ex: 'civicrm.root'.
111 * @param string $attr
112 * Ex: 'url', 'path'.
113 * @return mixed
114 */
115 public function getVariable($name, $attr) {
116 if (!isset($this->variables[$name])) {
117 $this->variables[$name] = call_user_func($this->variableFactory[$name]);
118 if (isset($GLOBALS['civicrm_paths'][$name])) {
119 $this->variables[$name] = array_merge($this->variables[$name], $GLOBALS['civicrm_paths'][$name]);
120 }
121 }
122 if (!isset($this->variables[$name][$attr])) {
123 throw new \RuntimeException("Cannot resolve path using \"$name.$attr\"");
124 }
125 return $this->variables[$name][$attr];
126 }
127
128 /**
129 * Does the variable exist.
130 *
131 * @param string $name
132 *
133 * @return bool
134 */
135 public function hasVariable($name) {
136 return isset($this->variableFactory[$name]);
137 }
138
139 /**
140 * Determine the absolute path to a file, given that the file is most likely
141 * in a given particular variable.
142 *
143 * @param string $value
144 * The file path.
145 * Use "." to reference to default file root.
146 * Values may begin with a variable, e.g. "[civicrm.files]/upload".
147 * @return mixed|string
148 */
149 public function getPath($value) {
150 $defaultContainer = self::DEFAULT_PATH;
151 if ($value && $value{0} == '[' && preg_match(';^\[([a-zA-Z0-9\._]+)\]/(.*);', $value, $matches)) {
152 $defaultContainer = $matches[1];
153 $value = $matches[2];
154 }
155 if (empty($value)) {
156 return FALSE;
157 }
158 if ($value === '.') {
159 $value = '';
160 }
161 return \CRM_Utils_File::absoluteDirectory($value, $this->getVariable($defaultContainer, 'path'));
162 }
163
164 /**
165 * Determine the URL to a file.
166 *
167 * @param string $value
168 * The file path. The path may begin with a variable, e.g. "[civicrm.files]/upload".
169 * @param string $preferFormat
170 * The preferred format ('absolute', 'relative').
171 * The result data may not meet the preference -- if the setting
172 * refers to an external domain, then the result will be
173 * absolute (regardless of preference).
174 * @param bool|NULL $ssl
175 * NULL to autodetect. TRUE to force to SSL.
176 * @return mixed|string
177 */
178 public function getUrl($value, $preferFormat = 'relative', $ssl = NULL) {
179 $defaultContainer = self::DEFAULT_URL;
180 if ($value && $value{0} == '[' && preg_match(';^\[([a-zA-Z0-9\._]+)\](/(.*))$;', $value, $matches)) {
181 $defaultContainer = $matches[1];
182 $value = empty($matches[3]) ? '.' : $matches[3];
183 }
184
185 if (empty($value)) {
186 return FALSE;
187 }
188 if ($value === '.') {
189 $value = '';
190 }
191 if (substr($value, 0, 4) == 'http') {
192 return $value;
193 }
194
195 $value = $this->getVariable($defaultContainer, 'url') . $value;
196
197 if ($preferFormat === 'relative') {
198 $parsed = parse_url($value);
199 if (isset($_SERVER['HTTP_HOST']) && isset($parsed['host']) && $_SERVER['HTTP_HOST'] == $parsed['host']) {
200 $value = $parsed['path'];
201 }
202 }
203
204 if ($ssl || ($ssl === NULL && \CRM_Utils_System::isSSL())) {
205 $value = str_replace('http://', 'https://', $value);
206 }
207
208 return $value;
209 }
210
211 }