1<?php
2/**
3 * Copyright 2017 Facebook, Inc.
4 *
5 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
6 * use, copy, modify, and distribute this software in source code or binary
7 * form for use in connection with the web services and APIs provided by
8 * Facebook.
9 *
10 * As with any software that integrates with the Facebook platform, your use
11 * of this software is subject to the Facebook Developer Principles and
12 * Policies [http://developers.facebook.com/policy/]. This copyright notice
13 * shall be included in all copies or substantial portions of the software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 */
24namespace Facebook\Url;
25
26/**
27 * Class FacebookUrlManipulator
28 *
29 * @package Facebook
30 */
31class FacebookUrlManipulator
32{
33    /**
34     * Remove params from a URL.
35     *
36     * @param string $url            The URL to filter.
37     * @param array  $paramsToFilter The params to filter from the URL.
38     *
39     * @return string The URL with the params removed.
40     */
41    public static function removeParamsFromUrl($url, array $paramsToFilter)
42    {
43        $parts = parse_url($url);
44
45        $query = '';
46        if (isset($parts['query'])) {
47            $params = [];
48            parse_str($parts['query'], $params);
49
50            // Remove query params
51            foreach ($paramsToFilter as $paramName) {
52                unset($params[$paramName]);
53            }
54
55            if (count($params) > 0) {
56                $query = '?' . http_build_query($params, null, '&');
57            }
58        }
59
60        $scheme = isset($parts['scheme']) ? $parts['scheme'] . '://' : '';
61        $host = isset($parts['host']) ? $parts['host'] : '';
62        $port = isset($parts['port']) ? ':' . $parts['port'] : '';
63        $path = isset($parts['path']) ? $parts['path'] : '';
64        $fragment = isset($parts['fragment']) ? '#' . $parts['fragment'] : '';
65
66        return $scheme . $host . $port . $path . $query . $fragment;
67    }
68
69    /**
70     * Gracefully appends params to the URL.
71     *
72     * @param string $url       The URL that will receive the params.
73     * @param array  $newParams The params to append to the URL.
74     *
75     * @return string
76     */
77    public static function appendParamsToUrl($url, array $newParams = [])
78    {
79        if (empty($newParams)) {
80            return $url;
81        }
82
83        if (strpos($url, '?') === false) {
84            return $url . '?' . http_build_query($newParams, null, '&');
85        }
86
87        list($path, $query) = explode('?', $url, 2);
88        $existingParams = [];
89        parse_str($query, $existingParams);
90
91        // Favor params from the original URL over $newParams
92        $newParams = array_merge($newParams, $existingParams);
93
94        // Sort for a predicable order
95        ksort($newParams);
96
97        return $path . '?' . http_build_query($newParams, null, '&');
98    }
99
100    /**
101     * Returns the params from a URL in the form of an array.
102     *
103     * @param string $url The URL to parse the params from.
104     *
105     * @return array
106     */
107    public static function getParamsAsArray($url)
108    {
109        $query = parse_url($url, PHP_URL_QUERY);
110        if (!$query) {
111            return [];
112        }
113        $params = [];
114        parse_str($query, $params);
115
116        return $params;
117    }
118
119    /**
120     * Adds the params of the first URL to the second URL.
121     *
122     * Any params that already exist in the second URL will go untouched.
123     *
124     * @param string $urlToStealFrom The URL harvest the params from.
125     * @param string $urlToAddTo     The URL that will receive the new params.
126     *
127     * @return string The $urlToAddTo with any new params from $urlToStealFrom.
128     */
129    public static function mergeUrlParams($urlToStealFrom, $urlToAddTo)
130    {
131        $newParams = static::getParamsAsArray($urlToStealFrom);
132        // Nothing new to add, return as-is
133        if (!$newParams) {
134            return $urlToAddTo;
135        }
136
137        return static::appendParamsToUrl($urlToAddTo, $newParams);
138    }
139
140    /**
141     * Check for a "/" prefix and prepend it if not exists.
142     *
143     * @param string|null $string
144     *
145     * @return string|null
146     */
147    public static function forceSlashPrefix($string)
148    {
149        if (!$string) {
150            return $string;
151        }
152
153        return strpos($string, '/') === 0 ? $string : '/' . $string;
154    }
155
156    /**
157     * Trims off the hostname and Graph version from a URL.
158     *
159     * @param string $urlToTrim The URL the needs the surgery.
160     *
161     * @return string The $urlToTrim with the hostname and Graph version removed.
162     */
163    public static function baseGraphUrlEndpoint($urlToTrim)
164    {
165        return '/' . preg_replace('/^https:\/\/.+\.facebook\.com(\/v.+?)?\//', '', $urlToTrim);
166    }
167}
168