Passed
Push — develop ( f228ff...90ed2c )
by Andrew
20:04 queued 10:06
created

UrlHelper::getSiteUrlOverrideSetting()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
eloc 12
c 0
b 0
f 0
dl 0
loc 22
ccs 0
cts 13
cp 0
rs 9.5555
cc 5
nc 5
nop 1
crap 30
1
<?php
2
/**
3
 * SEOmatic plugin for Craft CMS 3.x
4
 *
5
 * A turnkey SEO implementation for Craft CMS that is comprehensive, powerful,
6
 * and flexible
7
 *
8
 * @link      https://nystudio107.com
9
 * @copyright Copyright (c) 2017 nystudio107
10
 */
11
12
namespace nystudio107\seomatic\helpers;
13
14
use Craft;
15
use craft\errors\SiteNotFoundException;
16
use craft\helpers\UrlHelper as CraftUrlHelper;
17
use nystudio107\seomatic\Seomatic;
18
use yii\base\Exception;
19
20
/**
21
 * @author    nystudio107
22
 * @package   Seomatic
23
 * @since     3.0.0
24
 */
25
class UrlHelper extends CraftUrlHelper
26
{
27
    // Public Static Properties
28
    // =========================================================================
29
30
    // Public Static Methods
31
    // =========================================================================
32
33
    /**
34
     * @inheritDoc
35
     */
36
    public static function siteUrl(string $path = '', $params = null, string $scheme = null, int $siteId = null): string
37
    {
38
        try {
39
            $siteUrl = self::getSiteUrlOverrideSetting($siteId);
40
        } catch (\Throwable $e) {
41
            // That's okay
42
        }
43
        if (!empty($siteUrl)) {
44
            $siteUrl = MetaValue::parseString($siteUrl);
45
            // Extract out just the path part
46
            $parts = self::decomposeUrl($path);
47
            $path = $parts['path'] . $parts['suffix'];
48
            $url = rtrim($siteUrl, '/') . '/' . ltrim($path, '/');
49
            // Handle trailing slashes properly for generated URLs
50
            $generalConfig = Craft::$app->getConfig()->getGeneral();
51
            if ($generalConfig->addTrailingSlashesToUrls && !preg_match('/\.[^\/]+$/', $url)) {
52
                $url = rtrim($url, '/') . '/';
53
            }
54
            if (!$generalConfig->addTrailingSlashesToUrls) {
55
                $url = rtrim($url, '/');
56
            }
57
58
            return DynamicMeta::sanitizeUrl(parent::urlWithParams($url, $params), false, false);
59
        }
60
61
        return DynamicMeta::sanitizeUrl(parent::siteUrl($path, $params, $scheme, $siteId), false, false);
62
    }
63
64
    /**
65
     * Return the page trigger and the value of the page trigger (null if it doesn't exist)
66
     *
67
     * @return array
68
     */
69
    public static function pageTriggerValue(): array
70
    {
71
        $pageTrigger = Craft::$app->getConfig()->getGeneral()->pageTrigger;
72
        if (!\is_string($pageTrigger) || $pageTrigger === '') {
73
            $pageTrigger = 'p';
74
        }
75
        // Is this query string-based pagination?
76
        if ($pageTrigger[0] === '?') {
77
            $pageTrigger = trim($pageTrigger, '?=');
78
        }
79
        // Avoid conflict with the path param
80
        $pathParam = Craft::$app->getConfig()->getGeneral()->pathParam;
81
        if ($pageTrigger === $pathParam) {
82
            $pageTrigger = $pathParam === 'p' ? 'pg' : 'p';
83
        }
84
        $pageTriggerValue = Craft::$app->getRequest()->getParam($pageTrigger);
85
86
        return [$pageTrigger, $pageTriggerValue];
87
    }
88
89
    /**
90
     * Return an absolute URL with protocol that curl will be happy with
91
     *
92
     * @param string $url
93
     *
94
     * @return string
95
     */
96
    public static function absoluteUrlWithProtocol($url): string
97
    {
98
        // Make this a full URL
99
        if (!self::isAbsoluteUrl($url)) {
100
            $protocol = 'http';
101
            if (isset($_SERVER['HTTPS']) && (strcasecmp($_SERVER['HTTPS'], 'on') === 0 || $_SERVER['HTTPS'] == 1)
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: (IssetNode && strcasecmp...PROTO'], 'https') === 0, Probably Intended Meaning: IssetNode && (strcasecmp...ROTO'], 'https') === 0)
Loading history...
102
                || isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0
103
            ) {
104
                $protocol = 'https';
105
            }
106
            if (self::isProtocolRelativeUrl($url)) {
107
                try {
108
                    $url = self::urlWithScheme($url, $protocol);
109
                } catch (SiteNotFoundException $e) {
110
                    Craft::error($e->getMessage(), __METHOD__);
111
                }
112
            } else {
113
                try {
114
                    $url = self::siteUrl($url, null, $protocol);
115
                    if (self::isProtocolRelativeUrl($url)) {
116
                        $url = self::urlWithScheme($url, $protocol);
117
                    }
118
                } catch (Exception $e) {
119
                    Craft::error($e->getMessage(), __METHOD__);
120
                }
121
            }
122
        }
123
        // Ensure that any spaces in the URL are encoded
124
        $url = str_replace(' ', '%20', $url);
125
126
        // Handle trailing slashes properly for generated URLs
127
        $generalConfig = Craft::$app->getConfig()->getGeneral();
128
        if ($generalConfig->addTrailingSlashesToUrls && !preg_match('/\.[^\/]+$/', $url)) {
129
            $url = rtrim($url, '/') . '/';
130
        }
131
        if (!$generalConfig->addTrailingSlashesToUrls) {
132
            $url = rtrim($url, '/');
133
        }
134
135
        return DynamicMeta::sanitizeUrl($url, false, false);
136
    }
137
138
    /**
139
     * urlencode() just the query parameters in the URL
140
     *
141
     * @param string $url
142
     * @return string
143
     */
144 2
    public static function encodeUrlQueryParams(string $url): string
145
    {
146 2
        $urlParts = parse_url($url);
147 2
        $encodedUrl = "";
148 2
        if (isset($urlParts['scheme'])) {
149
            $encodedUrl .= $urlParts['scheme'] . '://';
150
        }
151 2
        if (isset($urlParts['host'])) {
152
            $encodedUrl .= $urlParts['host'];
153
        }
154 2
        if (isset($urlParts['port'])) {
155
            $encodedUrl .= ':' . $urlParts['port'];
156
        }
157 2
        if (isset($urlParts['path'])) {
158 2
            $encodedUrl .= $urlParts['path'];
159
        }
160 2
        if (isset($urlParts['query'])) {
161
            $query = explode('&', $urlParts['query']);
162
            foreach ($query as $j => $value) {
163
                $value = explode('=', $value, 2);
164
                if (count($value) === 2) {
165
                    $query[$j] = urlencode($value[0]) . '=' . urlencode($value[1]);
166
                } else {
167
                    $query[$j] = urlencode($value[0]);
168
                }
169
            }
170
            $encodedUrl .= '?' . implode('&', $query);
171
        }
172 2
        if (isset($urlParts['fragment'])) {
173
            $encodedUrl .= '#' . $urlParts['fragment'];
174
        }
175
176 2
        return $encodedUrl;
177
    }
178
179
    /**
180
     * Return whether this URL has a sub-directory as part of it
181
     *
182
     * @param string $url
183
     * @return bool
184
     */
185
    public static function urlHasSubDir(string $url): bool
186
    {
187
        return !empty(parse_url(trim($url, '/'), PHP_URL_PATH));
188
    }
189
190
    /**
191
     * Return the siteUrlOverride setting, which can be a string or an array of site URLs
192
     * indexed by the site handle
193
     *
194
     * @param int|null $siteId
195
     * @return string
196
     * @throws Exception
197
     * @throws SiteNotFoundException
198
     */
199
    public static function getSiteUrlOverrideSetting(?int $siteId = null): string
200
    {
201
        // If the override is a string, just return it
202
        $siteUrlOverride = Seomatic::$settings->siteUrlOverride;
203
        if (is_string($siteUrlOverride)) {
204
            return $siteUrlOverride;
205
        }
206
        // If the override is an array, pluck the appropriate one by handle
207
        if (is_array($siteUrlOverride)) {
0 ignored issues
show
introduced by
The condition is_array($siteUrlOverride) is always true.
Loading history...
208
            $sites = Craft::$app->getSites();
209
            $site = $sites->getCurrentSite();
210
            if ($siteId !== null) {
211
                $site = $sites->getSiteById($siteId, true);
212
                if (!$site) {
213
                    throw new Exception('Invalid site ID: ' . $siteId);
214
                }
215
            }
216
217
            return $siteUrlOverride[$site->handle] ?? '';
218
        }
219
220
        return '';
221
    }
222
223
    // Protected Methods
224
    // =========================================================================
225
226
    /**
227
     * Decompose a url into a prefix, path, and suffix
228
     *
229
     * @param $pathOrUrl
230
     *
231
     * @return array
232
     */
233
    protected static function decomposeUrl($pathOrUrl): array
234
    {
235
        $result = array();
236
237
        if (filter_var($pathOrUrl, FILTER_VALIDATE_URL)) {
238
            $url_parts = parse_url($pathOrUrl);
239
            $result['prefix'] = $url_parts['scheme'] . '://' . $url_parts['host'];
240
            $result['path'] = $url_parts['path'] ?? '';
241
            $result['suffix'] = '';
242
            $result['suffix'] .= empty($url_parts['query']) ? '' : '?' . $url_parts['query'];
243
            $result['suffix'] .= empty($url_parts['fragment']) ? '' : '#' . $url_parts['fragment'];
244
        } else {
245
            $result['prefix'] = '';
246
            $result['path'] = $pathOrUrl;
247
            $result['suffix'] = '';
248
        }
249
250
        return $result;
251
    }
252
}
253