Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 /*
|
Chris@0
|
4 * This file is part of the Symfony package.
|
Chris@0
|
5 *
|
Chris@0
|
6 * (c) Fabien Potencier <fabien@symfony.com>
|
Chris@0
|
7 *
|
Chris@0
|
8 * For the full copyright and license information, please view the LICENSE
|
Chris@0
|
9 * file that was distributed with this source code.
|
Chris@0
|
10 */
|
Chris@0
|
11
|
Chris@0
|
12 namespace Symfony\Component\DomCrawler;
|
Chris@0
|
13
|
Chris@0
|
14 /**
|
Chris@0
|
15 * Any HTML element that can link to an URI.
|
Chris@0
|
16 *
|
Chris@0
|
17 * @author Fabien Potencier <fabien@symfony.com>
|
Chris@0
|
18 */
|
Chris@0
|
19 abstract class AbstractUriElement
|
Chris@0
|
20 {
|
Chris@0
|
21 /**
|
Chris@0
|
22 * @var \DOMElement
|
Chris@0
|
23 */
|
Chris@0
|
24 protected $node;
|
Chris@0
|
25
|
Chris@0
|
26 /**
|
Chris@0
|
27 * @var string The method to use for the element
|
Chris@0
|
28 */
|
Chris@0
|
29 protected $method;
|
Chris@0
|
30
|
Chris@0
|
31 /**
|
Chris@0
|
32 * @var string The URI of the page where the element is embedded (or the base href)
|
Chris@0
|
33 */
|
Chris@0
|
34 protected $currentUri;
|
Chris@0
|
35
|
Chris@0
|
36 /**
|
Chris@0
|
37 * @param \DOMElement $node A \DOMElement instance
|
Chris@0
|
38 * @param string $currentUri The URI of the page where the link is embedded (or the base href)
|
Chris@0
|
39 * @param string $method The method to use for the link (get by default)
|
Chris@0
|
40 *
|
Chris@0
|
41 * @throws \InvalidArgumentException if the node is not a link
|
Chris@0
|
42 */
|
Chris@0
|
43 public function __construct(\DOMElement $node, $currentUri, $method = 'GET')
|
Chris@0
|
44 {
|
Chris@17
|
45 if (!\in_array(strtolower(substr($currentUri, 0, 4)), ['http', 'file'])) {
|
Chris@0
|
46 throw new \InvalidArgumentException(sprintf('Current URI must be an absolute URL ("%s").', $currentUri));
|
Chris@0
|
47 }
|
Chris@0
|
48
|
Chris@0
|
49 $this->setNode($node);
|
Chris@0
|
50 $this->method = $method ? strtoupper($method) : null;
|
Chris@0
|
51 $this->currentUri = $currentUri;
|
Chris@0
|
52 }
|
Chris@0
|
53
|
Chris@0
|
54 /**
|
Chris@0
|
55 * Gets the node associated with this link.
|
Chris@0
|
56 *
|
Chris@0
|
57 * @return \DOMElement A \DOMElement instance
|
Chris@0
|
58 */
|
Chris@0
|
59 public function getNode()
|
Chris@0
|
60 {
|
Chris@0
|
61 return $this->node;
|
Chris@0
|
62 }
|
Chris@0
|
63
|
Chris@0
|
64 /**
|
Chris@0
|
65 * Gets the method associated with this link.
|
Chris@0
|
66 *
|
Chris@0
|
67 * @return string The method
|
Chris@0
|
68 */
|
Chris@0
|
69 public function getMethod()
|
Chris@0
|
70 {
|
Chris@0
|
71 return $this->method;
|
Chris@0
|
72 }
|
Chris@0
|
73
|
Chris@0
|
74 /**
|
Chris@0
|
75 * Gets the URI associated with this link.
|
Chris@0
|
76 *
|
Chris@0
|
77 * @return string The URI
|
Chris@0
|
78 */
|
Chris@0
|
79 public function getUri()
|
Chris@0
|
80 {
|
Chris@0
|
81 $uri = trim($this->getRawUri());
|
Chris@0
|
82
|
Chris@0
|
83 // absolute URL?
|
Chris@0
|
84 if (null !== parse_url($uri, PHP_URL_SCHEME)) {
|
Chris@0
|
85 return $uri;
|
Chris@0
|
86 }
|
Chris@0
|
87
|
Chris@0
|
88 // empty URI
|
Chris@0
|
89 if (!$uri) {
|
Chris@0
|
90 return $this->currentUri;
|
Chris@0
|
91 }
|
Chris@0
|
92
|
Chris@0
|
93 // an anchor
|
Chris@0
|
94 if ('#' === $uri[0]) {
|
Chris@0
|
95 return $this->cleanupAnchor($this->currentUri).$uri;
|
Chris@0
|
96 }
|
Chris@0
|
97
|
Chris@0
|
98 $baseUri = $this->cleanupUri($this->currentUri);
|
Chris@0
|
99
|
Chris@0
|
100 if ('?' === $uri[0]) {
|
Chris@0
|
101 return $baseUri.$uri;
|
Chris@0
|
102 }
|
Chris@0
|
103
|
Chris@0
|
104 // absolute URL with relative schema
|
Chris@0
|
105 if (0 === strpos($uri, '//')) {
|
Chris@0
|
106 return preg_replace('#^([^/]*)//.*$#', '$1', $baseUri).$uri;
|
Chris@0
|
107 }
|
Chris@0
|
108
|
Chris@0
|
109 $baseUri = preg_replace('#^(.*?//[^/]*)(?:\/.*)?$#', '$1', $baseUri);
|
Chris@0
|
110
|
Chris@0
|
111 // absolute path
|
Chris@0
|
112 if ('/' === $uri[0]) {
|
Chris@0
|
113 return $baseUri.$uri;
|
Chris@0
|
114 }
|
Chris@0
|
115
|
Chris@0
|
116 // relative path
|
Chris@17
|
117 $path = parse_url(substr($this->currentUri, \strlen($baseUri)), PHP_URL_PATH);
|
Chris@0
|
118 $path = $this->canonicalizePath(substr($path, 0, strrpos($path, '/')).'/'.$uri);
|
Chris@0
|
119
|
Chris@0
|
120 return $baseUri.('' === $path || '/' !== $path[0] ? '/' : '').$path;
|
Chris@0
|
121 }
|
Chris@0
|
122
|
Chris@0
|
123 /**
|
Chris@0
|
124 * Returns raw URI data.
|
Chris@0
|
125 *
|
Chris@0
|
126 * @return string
|
Chris@0
|
127 */
|
Chris@0
|
128 abstract protected function getRawUri();
|
Chris@0
|
129
|
Chris@0
|
130 /**
|
Chris@0
|
131 * Returns the canonicalized URI path (see RFC 3986, section 5.2.4).
|
Chris@0
|
132 *
|
Chris@0
|
133 * @param string $path URI path
|
Chris@0
|
134 *
|
Chris@0
|
135 * @return string
|
Chris@0
|
136 */
|
Chris@0
|
137 protected function canonicalizePath($path)
|
Chris@0
|
138 {
|
Chris@0
|
139 if ('' === $path || '/' === $path) {
|
Chris@0
|
140 return $path;
|
Chris@0
|
141 }
|
Chris@0
|
142
|
Chris@0
|
143 if ('.' === substr($path, -1)) {
|
Chris@0
|
144 $path .= '/';
|
Chris@0
|
145 }
|
Chris@0
|
146
|
Chris@17
|
147 $output = [];
|
Chris@0
|
148
|
Chris@0
|
149 foreach (explode('/', $path) as $segment) {
|
Chris@0
|
150 if ('..' === $segment) {
|
Chris@0
|
151 array_pop($output);
|
Chris@0
|
152 } elseif ('.' !== $segment) {
|
Chris@0
|
153 $output[] = $segment;
|
Chris@0
|
154 }
|
Chris@0
|
155 }
|
Chris@0
|
156
|
Chris@0
|
157 return implode('/', $output);
|
Chris@0
|
158 }
|
Chris@0
|
159
|
Chris@0
|
160 /**
|
Chris@0
|
161 * Sets current \DOMElement instance.
|
Chris@0
|
162 *
|
Chris@0
|
163 * @param \DOMElement $node A \DOMElement instance
|
Chris@0
|
164 *
|
Chris@0
|
165 * @throws \LogicException If given node is not an anchor
|
Chris@0
|
166 */
|
Chris@0
|
167 abstract protected function setNode(\DOMElement $node);
|
Chris@0
|
168
|
Chris@0
|
169 /**
|
Chris@0
|
170 * Removes the query string and the anchor from the given uri.
|
Chris@0
|
171 *
|
Chris@0
|
172 * @param string $uri The uri to clean
|
Chris@0
|
173 *
|
Chris@0
|
174 * @return string
|
Chris@0
|
175 */
|
Chris@0
|
176 private function cleanupUri($uri)
|
Chris@0
|
177 {
|
Chris@0
|
178 return $this->cleanupQuery($this->cleanupAnchor($uri));
|
Chris@0
|
179 }
|
Chris@0
|
180
|
Chris@0
|
181 /**
|
Chris@0
|
182 * Remove the query string from the uri.
|
Chris@0
|
183 *
|
Chris@0
|
184 * @param string $uri
|
Chris@0
|
185 *
|
Chris@0
|
186 * @return string
|
Chris@0
|
187 */
|
Chris@0
|
188 private function cleanupQuery($uri)
|
Chris@0
|
189 {
|
Chris@0
|
190 if (false !== $pos = strpos($uri, '?')) {
|
Chris@0
|
191 return substr($uri, 0, $pos);
|
Chris@0
|
192 }
|
Chris@0
|
193
|
Chris@0
|
194 return $uri;
|
Chris@0
|
195 }
|
Chris@0
|
196
|
Chris@0
|
197 /**
|
Chris@0
|
198 * Remove the anchor from the uri.
|
Chris@0
|
199 *
|
Chris@0
|
200 * @param string $uri
|
Chris@0
|
201 *
|
Chris@0
|
202 * @return string
|
Chris@0
|
203 */
|
Chris@0
|
204 private function cleanupAnchor($uri)
|
Chris@0
|
205 {
|
Chris@0
|
206 if (false !== $pos = strpos($uri, '#')) {
|
Chris@0
|
207 return substr($uri, 0, $pos);
|
Chris@0
|
208 }
|
Chris@0
|
209
|
Chris@0
|
210 return $uri;
|
Chris@0
|
211 }
|
Chris@0
|
212 }
|