Skip to content

Commit 62ff134

Browse files
authored
Merge pull request #3946 from MGatner/http-agnostic
HTTP Phase 1
2 parents bfa720e + daca297 commit 62ff134

25 files changed

+2041
-1713
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
- `CodeIgniter\Database\ModelFactory` is now deprecated in favor of `CodeIgniter\Config\Factories::models()`
1010
- `CodeIgniter\Config\Config` is now deprecated in favor of `CodeIgniter\Config\Factories::config()`
11+
- HTTP Layer Refactor: Numerous deprecations have been made towards a transition to a PSR-compliant HTTP layer. [See the User Guide](user_guide_src/source/installation/upgrade_405.rst)
1112

1213
## [v4.0.4](https://github.com/codeigniter4/CodeIgniter4/tree/v4.0.4) (2020-07-15)
1314

phpstan.neon.dist

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,10 @@ parameters:
3131
- '#Access to an undefined property CodeIgniter\\Database\\Forge::\$dropConstraintStr#'
3232
- '#Access to an undefined property CodeIgniter\\Database\\BaseConnection::\$mysqli|\$schema#'
3333
- '#Access to an undefined property CodeIgniter\\Database\\ConnectionInterface::(\$DBDriver|\$connID|\$likeEscapeStr|\$likeEscapeChar|\$escapeChar|\$protectIdentifiers|\$schema)#'
34-
- '#Access to an undefined property CodeIgniter\\HTTP\\Request::\$uri#'
3534
- '#Access to protected property CodeIgniter\\Database\\BaseConnection::(\$DBDebug|\$DBPrefix|\$swapPre|\$charset|\$DBCollat|\$database)#'
3635
- '#Call to an undefined method CodeIgniter\\Database\\BaseConnection::_(disable|enable)ForeignKeyChecks\(\)#'
3736
- '#Call to an undefined method CodeIgniter\\Database\\BaseConnection::supportsForeignKeys\(\)#'
3837
- '#Call to an undefined method CodeIgniter\\Database\\ConnectionInterface::(tableExists|protectIdentifiers|setAliasedTables|escapeIdentifiers|affectedRows|addTableAlias|getIndexData)\(\)#'
39-
- '#Call to an undefined method CodeIgniter\\HTTP\\Request::(getPath|getSegments|getMethod|setLocale|getPost)\(\)#'
4038
- '#Call to an undefined method CodeIgniter\\Router\\RouteCollectionInterface::(getDefaultNamespace|isFiltered|getFilterForRoute|getRoutesOptions)\(\)#'
4139
- '#Cannot access property [\$a-z_]+ on ((bool\|)?object\|resource)#'
4240
- '#Cannot call method [a-zA-Z_]+\(\) on ((bool\|)?object\|resource)#'

system/CLI/CLI.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -869,7 +869,7 @@ public static function wrap(string $string = null, int $max = 0, int $padLeft =
869869
*/
870870
protected static function parseCommandLine()
871871
{
872-
$args = $_SERVER['argv'];
872+
$args = $_SERVER['argv'] ?? [];
873873
array_shift($args); // scrap invoking program
874874
$optionValue = false;
875875

system/CodeIgniter.php

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class CodeIgniter
6262
/**
6363
* Main application configuration
6464
*
65-
* @var \Config\App
65+
* @var App
6666
*/
6767
protected $config;
6868

@@ -76,7 +76,7 @@ class CodeIgniter
7676
/**
7777
* Current request.
7878
*
79-
* @var HTTP\Request|HTTP\IncomingRequest|CLIRequest
79+
* @var Request|HTTP\IncomingRequest|CLIRequest
8080
*/
8181
protected $request;
8282

@@ -395,7 +395,7 @@ protected function handleRequest(RouteCollectionInterface $routes = null, Cache
395395
$filters->enableFilter($routeFilter, 'after');
396396
}
397397

398-
$uri = $this->request instanceof CLIRequest ? $this->request->getPath() : $this->request->uri->getPath();
398+
$uri = $this->request instanceof CLIRequest ? $this->request->getPath() : $this->request->getUri()->getPath();
399399

400400
// Never run filters when running through Spark cli
401401
if (! defined('SPARKED'))
@@ -714,9 +714,7 @@ public function cachePage(Cache $config)
714714
$headers[$header->getName()] = $header->getValueLine();
715715
}
716716

717-
return cache()->save(
718-
$this->generateCacheName($config), serialize(['headers' => $headers, 'output' => $this->output]), static::$cacheTTL
719-
);
717+
return cache()->save($this->generateCacheName($config), serialize(['headers' => $headers, 'output' => $this->output]), static::$cacheTTL);
720718
}
721719

722720
//--------------------------------------------------------------------
@@ -745,24 +743,20 @@ public function getPerformanceStats(): array
745743
*/
746744
protected function generateCacheName(Cache $config): string
747745
{
748-
if (get_class($this->request) === CLIRequest::class)
746+
if ($this->request instanceof CLIRequest)
749747
{
750748
return md5($this->request->getPath());
751749
}
752750

753-
$uri = $this->request->uri;
751+
$uri = $this->request->getUri();
754752

755753
if ($config->cacheQueryString)
756754
{
757-
$name = URI::createURIString(
758-
$uri->getScheme(), $uri->getAuthority(), $uri->getPath(), $uri->getQuery()
759-
);
755+
$name = URI::createURIString($uri->getScheme(), $uri->getAuthority(), $uri->getPath(), $uri->getQuery());
760756
}
761757
else
762758
{
763-
$name = URI::createURIString(
764-
$uri->getScheme(), $uri->getAuthority(), $uri->getPath()
765-
);
759+
$name = URI::createURIString($uri->getScheme(), $uri->getAuthority(), $uri->getPath());
766760
}
767761

768762
return md5($name);
@@ -821,7 +815,7 @@ protected function tryToRouteIt(RouteCollectionInterface $routes = null)
821815
// then we need to set the correct locale on our Request.
822816
if ($this->router->hasLocale())
823817
{
824-
$this->request->setLocale($this->router->getLocale());
818+
$this->request->setLocale($this->router->getLocale()); // @phpstan-ignore-line
825819
}
826820

827821
$this->benchmark->stop('routing');
@@ -926,7 +920,7 @@ protected function createController()
926920
protected function runController($class)
927921
{
928922
// If this is a console request then use the input segments as parameters
929-
$params = defined('SPARKED') ? $this->request->getSegments() : $this->router->params();
923+
$params = defined('SPARKED') ? $this->request->getSegments() : $this->router->params(); // @phpstan-ignore-line
930924

931925
if (method_exists($class, '_remap'))
932926
{
@@ -1107,7 +1101,7 @@ public function spoofRequestMethod()
11071101
return;
11081102
}
11091103

1110-
$method = $this->request->getPost('_method');
1104+
$method = $this->request->getPost('_method'); // @phpstan-ignore-line
11111105

11121106
if (empty($method))
11131107
{

system/HTTP/DownloadResponse.php

Lines changed: 21 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
/**
2121
* HTTP response when a download is requested.
2222
*/
23-
class DownloadResponse extends Message implements ResponseInterface
23+
class DownloadResponse extends Response
2424
{
2525
/**
2626
* Download file name
@@ -51,25 +51,25 @@ class DownloadResponse extends Message implements ResponseInterface
5151
private $binary;
5252

5353
/**
54-
* Download reason
54+
* Download charset
5555
*
5656
* @var string
5757
*/
58-
private $reason = 'OK';
58+
private $charset = 'UTF-8';
5959

6060
/**
61-
* Download charset
61+
* Download reason
6262
*
6363
* @var string
6464
*/
65-
private $charset = 'UTF-8';
65+
protected $reason = 'OK';
6666

6767
/**
68-
* pretend
68+
* The current status code for this response.
6969
*
70-
* @var boolean
70+
* @var integer
7171
*/
72-
private $pretend = false;
72+
protected $statusCode = 200;
7373

7474
/**
7575
* Constructor.
@@ -79,8 +79,13 @@ class DownloadResponse extends Message implements ResponseInterface
7979
*/
8080
public function __construct(string $filename, bool $setMime)
8181
{
82+
parent::__construct(config('App'));
83+
8284
$this->filename = $filename;
8385
$this->setMime = $setMime;
86+
87+
// Make sure the content type is either specified or detected
88+
$this->removeHeader('Content-Type');
8489
}
8590

8691
/**
@@ -227,29 +232,13 @@ private function getContentDisposition() : string
227232
return $result;
228233
}
229234

230-
/**
231-
* {@inheritDoc}
232-
*/
233-
public function getStatusCode(): int
234-
{
235-
return 200;
236-
}
237-
238235
//--------------------------------------------------------------------
239236

240237
/**
241-
* Return an instance with the specified status code and, optionally, reason phrase.
238+
* Disallows status changing.
242239
*
243-
* If no reason phrase is specified, will default recommended reason phrase for
244-
* the response's status code.
245-
*
246-
* @see http://tools.ietf.org/html/rfc7231#section-6
247-
* @see http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
248-
*
249-
* @param integer $code The 3-digit integer result code to set.
250-
* @param string $reason The reason phrase to use with the
251-
* provided status code; if none is provided, will
252-
* default to the IANA name.
240+
* @param integer $code
241+
* @param string $reason
253242
*
254243
* @throws DownloadException
255244
*/
@@ -260,42 +249,6 @@ public function setStatusCode(int $code, string $reason = '')
260249

261250
//--------------------------------------------------------------------
262251

263-
/**
264-
* Gets the response response phrase associated with the status code.
265-
*
266-
* @see http://tools.ietf.org/html/rfc7231#section-6
267-
* @see http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
268-
*
269-
* @return string
270-
*/
271-
public function getReason(): string
272-
{
273-
return $this->reason;
274-
}
275-
276-
//--------------------------------------------------------------------
277-
//--------------------------------------------------------------------
278-
// Convenience Methods
279-
//--------------------------------------------------------------------
280-
281-
/**
282-
* Sets the date header
283-
*
284-
* @param DateTime $date
285-
*
286-
* @return ResponseInterface
287-
*/
288-
public function setDate(DateTime $date)
289-
{
290-
$date->setTimezone(new DateTimeZone('UTC'));
291-
292-
$this->setHeader('Date', $date->format('D, d M Y H:i:s') . ' GMT');
293-
294-
return $this;
295-
}
296-
297-
//--------------------------------------------------------------------
298-
299252
/**
300253
* Sets the Content Type header for this response with the mime type
301254
* and, optionally, the charset.
@@ -307,15 +260,9 @@ public function setDate(DateTime $date)
307260
*/
308261
public function setContentType(string $mime, string $charset = 'UTF-8')
309262
{
310-
// add charset attribute if not already there and provided as parm
311-
if ((strpos($mime, 'charset=') < 1) && ! empty($charset))
312-
{
313-
$mime .= '; charset=' . $charset;
314-
}
263+
parent::setContentType($mime, $charset);
315264

316-
$this->removeHeader('Content-Type'); // replace existing content type
317-
$this->setHeader('Content-Type', $mime);
318-
if (! empty($charset))
265+
if ($charset !== '')
319266
{
320267
$this->charset = $charset;
321268
}
@@ -339,28 +286,7 @@ public function noCache(): self
339286
//--------------------------------------------------------------------
340287

341288
/**
342-
* A shortcut method that allows the developer to set all of the
343-
* cache-control headers in one method call.
344-
*
345-
* The options array is used to provide the cache-control directives
346-
* for the header. It might look something like:
347-
*
348-
* $options = [
349-
* 'max-age' => 300,
350-
* 's-maxage' => 900
351-
* 'etag' => 'abcde',
352-
* ];
353-
*
354-
* Typical options are:
355-
* - etag
356-
* - last-modified
357-
* - max-age
358-
* - s-maxage
359-
* - private
360-
* - public
361-
* - must-revalidate
362-
* - proxy-revalidate
363-
* - no-transform
289+
* Disables cache configuration.
364290
*
365291
* @param array $options
366292
*
@@ -371,46 +297,14 @@ public function setCache(array $options = [])
371297
throw DownloadException::forCannotSetCache();
372298
}
373299

374-
//--------------------------------------------------------------------
375-
376-
/**
377-
* {@inheritDoc}
378-
*/
379-
public function setLastModified($date)
380-
{
381-
if ($date instanceof DateTime)
382-
{
383-
$date->setTimezone(new DateTimeZone('UTC'));
384-
$this->setHeader('Last-Modified', $date->format('D, d M Y H:i:s') . ' GMT');
385-
}
386-
elseif (is_string($date))
387-
{
388-
$this->setHeader('Last-Modified', $date);
389-
}
390-
391-
return $this;
392-
}
393-
394-
//--------------------------------------------------------------------
395300
//--------------------------------------------------------------------
396301
// Output Methods
397302
//--------------------------------------------------------------------
398303

399-
/**
400-
* For unit testing, don't actually send headers.
401-
*
402-
* @param boolean $pretend
403-
* @return $this
404-
*/
405-
public function pretend(bool $pretend = true)
406-
{
407-
$this->pretend = $pretend;
408-
409-
return $this;
410-
}
411-
412304
/**
413305
* {@inheritDoc}
306+
*
307+
* @todo Do downloads need CSP or Cookies? Compare with ResponseTrait::send()
414308
*/
415309
public function send()
416310
{
@@ -438,39 +332,6 @@ public function buildHeaders()
438332
$this->noCache();
439333
}
440334

441-
/**
442-
* Sends the headers of this HTTP request to the browser.
443-
*
444-
* @return DownloadResponse
445-
*/
446-
public function sendHeaders()
447-
{
448-
// Have the headers already been sent?
449-
if ($this->pretend || headers_sent())
450-
{
451-
return $this;
452-
}
453-
454-
// Per spec, MUST be sent with each request, if possible.
455-
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
456-
if (! isset($this->headers['Date']))
457-
{
458-
$this->setDate(DateTime::createFromFormat('U', (string) time()));
459-
}
460-
461-
// HTTP Status
462-
header(sprintf('HTTP/%s %s %s', $this->getProtocolVersion(), $this->getStatusCode(), $this->getReason()), true,
463-
$this->getStatusCode());
464-
465-
// Send all of our headers
466-
foreach ($this->getHeaders() as $name => $values)
467-
{
468-
header($name . ': ' . $this->getHeaderLine($name), false, $this->getStatusCode());
469-
}
470-
471-
return $this;
472-
}
473-
474335
/**
475336
* output download file text.
476337
*

0 commit comments

Comments
 (0)