Skip to content

Commit 8ae1245

Browse files
committed
Merge branch 'develop' of https://github.com/jim-parry/CodeIgniter4 into docs/workflow
2 parents 6fc4684 + 824a94e commit 8ae1245

3 files changed

Lines changed: 347 additions & 7 deletions

File tree

system/HTTP/URI.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ class URI
132132
protected $defaultPorts = [
133133
'http' => 80,
134134
'https' => 443,
135+
'ftp' => 21,
136+
'sftp' => 22
135137
];
136138

137139
/**
@@ -301,9 +303,11 @@ public function getUserInfo()
301303
* Temporarily sets the URI to show a password in userInfo. Will
302304
* reset itself after the first call to authority().
303305
*/
304-
public function showPassword()
306+
public function showPassword(bool $val = true)
305307
{
306-
$this->showPassword = true;
308+
$this->showPassword = $val;
309+
310+
return $this;
307311
}
308312

309313
//--------------------------------------------------------------------
@@ -749,6 +753,8 @@ public function setQueryArray(array $query)
749753
/**
750754
* Sets the fragment portion of the URI.
751755
*
756+
* @see https://tools.ietf.org/html/rfc3986#section-3.5
757+
*
752758
* @param string $string
753759
*
754760
* @return $this

user_guide_src/source/libraries/content_negotiation.rst

Lines changed: 124 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,127 @@
22
Content Negotiation
33
*******************
44

5-
TODO
5+
Content negotiation is a way to determine what type of content to return to the client based on what the client
6+
can handle, and what the server can handle. This can be used to determine whether the client is wanting HTML or JSON
7+
returned, whether the image should be returned as a jpg or png, what type of compression is supported and more. This
8+
is done by analyzing four different headers which can each support multiple value options, each with their own priority.
9+
Trying to match this up manually can be pretty challenging. CodeIgniter provides the ``Negotiator`` class that
10+
can handle this for you.
11+
12+
=================
13+
Loading the Class
14+
=================
15+
16+
You can load an instance of the class manually through the Service class::
17+
18+
$negotiator = Config\Services::negotiator();
19+
20+
This will grab the current request instance and automatically inject it into the Negotiator class.
21+
22+
This class does not need to be loaded on it's own. Instead, it can be accessed through this request's ``IncomingRequest``
23+
instance. While you cannot access it directly this way, you can easily access all of methods through the ``negotiate()``
24+
method::
25+
26+
$request->negotiate('media', ['foo', 'bar']);
27+
28+
When accessed this way, the first parameter is the type of content you're trying to find a match for, while the
29+
second is an array of supported values.
30+
31+
===========
32+
Negotiating
33+
===========
34+
35+
In this section we will discuss the 4 types of content that can be negotiated and show how that would look using
36+
both of the methods described above to access the negotiator.
37+
38+
Media
39+
=====
40+
41+
The first aspect to look at is handling 'media' negotiations. These are provided by the ``Accept`` header and
42+
is one of the most complex headers available. A common example is the client telling the server what format it
43+
wants the data in. This is especially common in API's. For example, a client might request JSON formatted data
44+
from an API endpoint::
45+
46+
GET /foo HTTP/1.1
47+
Accept: application/json
48+
49+
The server now needs to provide a list of what type of content it can provide. In this example, the API might
50+
be able to return data as raw HTML, JSON, or XML. This list should be provided in order of preference.::
51+
52+
$supported = [
53+
'application/json',
54+
'text/html',
55+
'application/xml'
56+
];
57+
58+
$format = $request->negotiate('media', $supported);
59+
// or
60+
$format = $negotiate->media($supported);
61+
62+
In this case, both the client and the server can agree on formatting the data as JSON so 'json' is returned from
63+
the negotiate method. By default, if no match is found, the first element in the $supported array would be returned.
64+
In some cases, though, you might need to enforce the format to be a strict match. If you pass ``true`` as the
65+
final value, it will return an empty string if no match is found::
66+
67+
$format = $request->negotiate('media', $supported, true);
68+
// or
69+
$format = $negotiate->media($supported, true);
70+
71+
Language
72+
========
73+
74+
Another common usage is to determine the language the content should be served in. If you are running only a single
75+
language site, this obviously isn't going to make much difference, but any site that can offer up multiple translations
76+
of content will find this useful, since the browser will typically send the preferred language along in the ``Accept-Language``
77+
header::
78+
79+
GET /foo HTTP/1.1
80+
Accept-Language: fr; q=1.0, en; q=0.5
81+
82+
In this example, the browser would prefer french, with a second choice of english. If your website supports english
83+
and german you would do something like::
84+
85+
$supported = [
86+
'en',
87+
'de'
88+
];
89+
90+
$lang = $request->negotiate('language', $supported);
91+
// or
92+
$lang = $negotiate->language($supported);
93+
94+
In this example, 'en' would be returned as the current language. If no match is found, it will return the first element
95+
in the $supported array, so that should always be the preferred language.
96+
97+
Encoding
98+
========
99+
100+
The ``Accept-Encoding`` header contains the character sets the client prefers to receive, and is used to
101+
specify the type of compression the client supports::
102+
103+
GET /foo HTTP/1.1
104+
Accept-Encoding: compress, gzip
105+
106+
Your web server will define what types of compression you can use. Some, like Apache, only support **gzip**::
107+
108+
$type = $request->negotiate('encoding', ['gzip']);
109+
// or
110+
$type = $negotiate->encoding(['gzip']);
111+
112+
See more at `Wikipedia <https://en.wikipedia.org/wiki/HTTP_compression>`_.
113+
114+
Character Set
115+
=============
116+
117+
The desired character set is passed through the ``Accept-Charset`` header::
118+
119+
GET /foo HTTP/1.1
120+
Accept-Charset: utf-16, utf-8
121+
122+
By default, if no matches are found, **utf-8** will be returned::
123+
124+
$charset = $request->negotiate('charset', ['utf-8']);
125+
// or
126+
$charset = $negotiate->charset(['utf-8']);
127+
128+
Lines changed: 215 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,216 @@
1-
***
2-
URI
3-
***
1+
*****************
2+
Working with URIs
3+
*****************
44

5-
TODO
5+
CodeIngiter provides an object oriented solution for working with URI's in your application. Using this makes it
6+
simple to ensure that the structure is always correct, no matter how complex the URI might be, as well as adding
7+
relative URI to an existing one and have it resolved safely and correctly.
8+
9+
.. contents::
10+
11+
12+
======================
13+
Creating URI instances
14+
======================
15+
16+
Creating a URI instance is as simple as creating a new class instance::
17+
18+
$uri = new \CodeIgniter\HTTP\URI();
19+
20+
Alternatively, you can use the ``service()`` function to return an instance for you::
21+
22+
$uri = service('uri');
23+
24+
When you create the new instance, you can pass a full or partial URL in the constructor and it will be parsed
25+
into it's appropriate sections::
26+
27+
$uri = new \CodeIgniter\HTTP\URI('http://www.example.com/some/path');
28+
$uri = service('uri', 'http://www.example.com/some/path');
29+
30+
The Current URI
31+
---------------
32+
33+
Many times, all you really want is an object representing the current URL of this request. This can be accesssed
34+
in two different ways. The first, is to grab it directly from the current request object. Assuming that you're in
35+
a controller that extends ``CodeIgniter\Controller`` you can get it like::
36+
37+
$uri = $this->request->uri;
38+
39+
Second, you can use one of the functions available in the **url_helper**::
40+
41+
helper('url');
42+
$uri = current_url(true);
43+
44+
You must pass ``true`` as the first parameter, otherwise it will return the string representation of the current URL.
45+
46+
===========
47+
URI Strings
48+
===========
49+
50+
Many times, all you really want is to get a string representation of a URI. This is easy to do by simply casting
51+
the URI as a string::
52+
53+
$uri = current_url(true);
54+
echo (string)$uri; // http://example.com
55+
56+
If you know the pieces of the URI and just want to ensure it's all formatted correctly, you can generate a string
57+
using the URI class' static ``createURIString()`` method::
58+
59+
$uriString = URI::createURIString($scheme, $authority, $path, $query, $fragment);
60+
61+
// Creates: http://exmample.com/some/path?foo=bar#first-heading
62+
echo URI::createURIString('http', 'example.com', 'some/path', 'foo=bar', 'first-heading');
63+
64+
=============
65+
The URI Parts
66+
=============
67+
68+
Once you have a URI instance, you can set or retrieve any of the various parts of the URI. This section will provide
69+
details on what those parts are, and how to work with them.
70+
71+
Scheme
72+
------
73+
74+
The scheme is frequently 'http' or 'https', but any scheme is supported, including 'file', 'mailto', etc.
75+
::
76+
77+
$uri = new \CodeIgniter\HTTP\URI('http://www.example.com/some/path');
78+
79+
echo $uri->getScheme(); // 'http'
80+
$uri->setScheme('https');
81+
82+
Authority
83+
---------
84+
85+
Many URIs contain several elements that are collectively known as the 'authority'. This includes any user info,
86+
the host and the port number. You can retrieve all of these pieces as one single string with the ``getAuthority()``
87+
method, or you can manipulate the individual parts.
88+
::
89+
90+
$uri = new \CodeIgniter\HTTP\URI('ftp://user:password@example.com:21/some/path');
91+
92+
echo $uri->getAuthority(); // user@example.com:21
93+
94+
By default, this will not display the password portion since you wouldn't want to show that to anyone. If you want
95+
to show the password, you can use the ``showPassword()`` method. This URI instance will continue to show that password
96+
until you turn it off again, so always make sure that you turn it off as soon as you are finished with it::
97+
98+
echo $uri->getAuthority(); // user@example.com:21
99+
echo $uri->showPassword()->getAuthority(); // user:password@example.com:21
100+
101+
// Turn password display off again.
102+
$uri->showPassword(false);
103+
104+
If you do not want to display the port, pass in ``true`` as the only parameter::
105+
106+
echo $uri->getAuthority(true); // user@example.com
107+
108+
.. note:: If the current port is the default port for the scheme it will never be displayed.
109+
110+
Userinfo
111+
--------
112+
113+
The userinfo section is simply the username and password that you might see with an FTP URI. While you can get
114+
this as part of the Authority, you can also retrieve it yourself::
115+
116+
echo $uri->getUserInfo(); // user
117+
118+
By default, it will not display the password, but you can override that with the ``showPassword()`` method::
119+
120+
echo $uri->showPassword()->getUserInfo(); // user:password
121+
$uri->showPassword(false);
122+
123+
Host
124+
----
125+
126+
The host portion of the URI is typically the domain name of the URL. This can be easily set and retrieved with the
127+
``getHost()`` and ``setHost()`` methods::
128+
129+
$uri = new \CodeIgniter\HTTP\URI('http://www.example.com/some/path');
130+
131+
echo $uri->getHost(); // www.example.com
132+
echo $uri->setHost('anotherexample.com')->getHost(); // anotherexample.com
133+
134+
Port
135+
----
136+
137+
The port is an integer number between 0 and 65535. Each sheme has a default value associated with it.
138+
::
139+
140+
$uri = new \CodeIgniter\HTTP\URI('ftp://user:password@example.com:21/some/path');
141+
142+
echo $uri->getPort(); // 21
143+
echo $uri->setPort(2201)->getPort(); // 2201
144+
145+
When using the ``setPort()`` method, the port will be checked that it is within the valid range and assigned.
146+
147+
Path
148+
----
149+
150+
The path are all of the segments within the site itself. As expected, the ``getPath()`` and ``setPath()`` methods
151+
can be used to manipulate it::
152+
153+
$uri = new \CodeIgniter\HTTP\URI('http://www.example.com/some/path');
154+
155+
echo $uri->getPath(); // 'some/path'
156+
echo $uri->setPath('another/path')->getPath(); // 'another/path'
157+
158+
.. note:: When setting the path this way, or any other way the class allows, it is sanitized to encode any dangerous
159+
characters, and remove dot segments for safety.
160+
161+
Query
162+
-----
163+
164+
The query variables can be manipulated through the class using simple string representations. Query values can only
165+
be set as a string currently.
166+
::
167+
168+
$uri = new \CodeIgniter\HTTP\URI('http://www.example.com?foo=bar');
169+
170+
echo $uri->getQuery(); // 'foo=bar'
171+
$uri->setQuery('foo=bar&bar=baz');
172+
173+
.. note:: Query values cannot contain fragments. An InvalidArgumentException will be thrown if it does.
174+
175+
Fragment
176+
--------
177+
178+
Fragments are the portion at the end of the URL, preceeded by the pound-sign (#). In HTML URL's these are links
179+
to an on-page anchor. Media URI's can make use of them in various other ways.
180+
::
181+
182+
$uri = new \CodeIgniter\HTTP\URI('http://www.example.com/some/path#first-heading');
183+
184+
echo $uri->getFragment(); // 'first-heading'
185+
echo $uri->setFragment('second-heading')->getFragment(); // 'second-heading'
186+
187+
============
188+
URI Segments
189+
============
190+
191+
Each section of the path between the slashes are a single segment. The URI class provides a simple way to determine
192+
what the value of the segments are. The segments start at 1 being the furthest left of the path.
193+
::
194+
195+
// URI = http://example.com/users/15/profile
196+
197+
// Prints '15'
198+
if ($request->uri->getSegment(1) == 'users')
199+
{
200+
echo $request->uri->getSegment(2);
201+
}
202+
203+
You can get a count of the total segments::
204+
205+
$total = $request->uri->getTotalSegments(); // 3
206+
207+
Finally, you can retrieve an array of all of the segments::
208+
209+
$segments = $request->uri->getSegments();
210+
211+
// $segments =
212+
[
213+
0 => 'users,
214+
1 => '15',
215+
2 => 'profile'
216+
]

0 commit comments

Comments
 (0)