Skip to content

HTTP Phase 1 #3946

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Dec 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

- `CodeIgniter\Database\ModelFactory` is now deprecated in favor of `CodeIgniter\Config\Factories::models()`
- `CodeIgniter\Config\Config` is now deprecated in favor of `CodeIgniter\Config\Factories::config()`
- 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)

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

Expand Down
2 changes: 0 additions & 2 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,10 @@ parameters:
- '#Access to an undefined property CodeIgniter\\Database\\Forge::\$dropConstraintStr#'
- '#Access to an undefined property CodeIgniter\\Database\\BaseConnection::\$mysqli|\$schema#'
- '#Access to an undefined property CodeIgniter\\Database\\ConnectionInterface::(\$DBDriver|\$connID|\$likeEscapeStr|\$likeEscapeChar|\$escapeChar|\$protectIdentifiers|\$schema)#'
- '#Access to an undefined property CodeIgniter\\HTTP\\Request::\$uri#'
- '#Access to protected property CodeIgniter\\Database\\BaseConnection::(\$DBDebug|\$DBPrefix|\$swapPre|\$charset|\$DBCollat|\$database)#'
- '#Call to an undefined method CodeIgniter\\Database\\BaseConnection::_(disable|enable)ForeignKeyChecks\(\)#'
- '#Call to an undefined method CodeIgniter\\Database\\BaseConnection::supportsForeignKeys\(\)#'
- '#Call to an undefined method CodeIgniter\\Database\\ConnectionInterface::(tableExists|protectIdentifiers|setAliasedTables|escapeIdentifiers|affectedRows|addTableAlias|getIndexData)\(\)#'
- '#Call to an undefined method CodeIgniter\\HTTP\\Request::(getPath|getSegments|getMethod|setLocale|getPost)\(\)#'
- '#Call to an undefined method CodeIgniter\\Router\\RouteCollectionInterface::(getDefaultNamespace|isFiltered|getFilterForRoute|getRoutesOptions)\(\)#'
- '#Cannot access property [\$a-z_]+ on ((bool\|)?object\|resource)#'
- '#Cannot call method [a-zA-Z_]+\(\) on ((bool\|)?object\|resource)#'
Expand Down
2 changes: 1 addition & 1 deletion system/CLI/CLI.php
Original file line number Diff line number Diff line change
Expand Up @@ -869,7 +869,7 @@ public static function wrap(string $string = null, int $max = 0, int $padLeft =
*/
protected static function parseCommandLine()
{
$args = $_SERVER['argv'];
$args = $_SERVER['argv'] ?? [];
array_shift($args); // scrap invoking program
$optionValue = false;

Expand Down
28 changes: 11 additions & 17 deletions system/CodeIgniter.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class CodeIgniter
/**
* Main application configuration
*
* @var \Config\App
* @var App
*/
protected $config;

Expand All @@ -76,7 +76,7 @@ class CodeIgniter
/**
* Current request.
*
* @var HTTP\Request|HTTP\IncomingRequest|CLIRequest
* @var Request|HTTP\IncomingRequest|CLIRequest
*/
protected $request;

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

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

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

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

//--------------------------------------------------------------------
Expand Down Expand Up @@ -745,24 +743,20 @@ public function getPerformanceStats(): array
*/
protected function generateCacheName(Cache $config): string
{
if (get_class($this->request) === CLIRequest::class)
if ($this->request instanceof CLIRequest)
{
return md5($this->request->getPath());
}

$uri = $this->request->uri;
$uri = $this->request->getUri();

if ($config->cacheQueryString)
{
$name = URI::createURIString(
$uri->getScheme(), $uri->getAuthority(), $uri->getPath(), $uri->getQuery()
);
$name = URI::createURIString($uri->getScheme(), $uri->getAuthority(), $uri->getPath(), $uri->getQuery());
}
else
{
$name = URI::createURIString(
$uri->getScheme(), $uri->getAuthority(), $uri->getPath()
);
$name = URI::createURIString($uri->getScheme(), $uri->getAuthority(), $uri->getPath());
}

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

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

if (method_exists($class, '_remap'))
{
Expand Down Expand Up @@ -1107,7 +1101,7 @@ public function spoofRequestMethod()
return;
}

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

if (empty($method))
{
Expand Down
181 changes: 21 additions & 160 deletions system/HTTP/DownloadResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
/**
* HTTP response when a download is requested.
*/
class DownloadResponse extends Message implements ResponseInterface
class DownloadResponse extends Response
{
/**
* Download file name
Expand Down Expand Up @@ -51,25 +51,25 @@ class DownloadResponse extends Message implements ResponseInterface
private $binary;

/**
* Download reason
* Download charset
*
* @var string
*/
private $reason = 'OK';
private $charset = 'UTF-8';

/**
* Download charset
* Download reason
*
* @var string
*/
private $charset = 'UTF-8';
protected $reason = 'OK';

/**
* pretend
* The current status code for this response.
*
* @var boolean
* @var integer
*/
private $pretend = false;
protected $statusCode = 200;

/**
* Constructor.
Expand All @@ -79,8 +79,13 @@ class DownloadResponse extends Message implements ResponseInterface
*/
public function __construct(string $filename, bool $setMime)
{
parent::__construct(config('App'));

$this->filename = $filename;
$this->setMime = $setMime;

// Make sure the content type is either specified or detected
$this->removeHeader('Content-Type');
}

/**
Expand Down Expand Up @@ -227,29 +232,13 @@ private function getContentDisposition() : string
return $result;
}

/**
* {@inheritDoc}
*/
public function getStatusCode(): int
{
return 200;
}

//--------------------------------------------------------------------

/**
* Return an instance with the specified status code and, optionally, reason phrase.
* Disallows status changing.
*
* If no reason phrase is specified, will default recommended reason phrase for
* the response's status code.
*
* @see http://tools.ietf.org/html/rfc7231#section-6
* @see http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
*
* @param integer $code The 3-digit integer result code to set.
* @param string $reason The reason phrase to use with the
* provided status code; if none is provided, will
* default to the IANA name.
* @param integer $code
* @param string $reason
*
* @throws DownloadException
*/
Expand All @@ -260,42 +249,6 @@ public function setStatusCode(int $code, string $reason = '')

//--------------------------------------------------------------------

/**
* Gets the response response phrase associated with the status code.
*
* @see http://tools.ietf.org/html/rfc7231#section-6
* @see http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
*
* @return string
*/
public function getReason(): string
{
return $this->reason;
}

//--------------------------------------------------------------------
//--------------------------------------------------------------------
// Convenience Methods
//--------------------------------------------------------------------

/**
* Sets the date header
*
* @param DateTime $date
*
* @return ResponseInterface
*/
public function setDate(DateTime $date)
{
$date->setTimezone(new DateTimeZone('UTC'));

$this->setHeader('Date', $date->format('D, d M Y H:i:s') . ' GMT');

return $this;
}

//--------------------------------------------------------------------

/**
* Sets the Content Type header for this response with the mime type
* and, optionally, the charset.
Expand All @@ -307,15 +260,9 @@ public function setDate(DateTime $date)
*/
public function setContentType(string $mime, string $charset = 'UTF-8')
{
// add charset attribute if not already there and provided as parm
if ((strpos($mime, 'charset=') < 1) && ! empty($charset))
{
$mime .= '; charset=' . $charset;
}
parent::setContentType($mime, $charset);

$this->removeHeader('Content-Type'); // replace existing content type
$this->setHeader('Content-Type', $mime);
if (! empty($charset))
if ($charset !== '')
{
$this->charset = $charset;
}
Expand All @@ -339,28 +286,7 @@ public function noCache(): self
//--------------------------------------------------------------------

/**
* A shortcut method that allows the developer to set all of the
* cache-control headers in one method call.
*
* The options array is used to provide the cache-control directives
* for the header. It might look something like:
*
* $options = [
* 'max-age' => 300,
* 's-maxage' => 900
* 'etag' => 'abcde',
* ];
*
* Typical options are:
* - etag
* - last-modified
* - max-age
* - s-maxage
* - private
* - public
* - must-revalidate
* - proxy-revalidate
* - no-transform
* Disables cache configuration.
*
* @param array $options
*
Expand All @@ -371,46 +297,14 @@ public function setCache(array $options = [])
throw DownloadException::forCannotSetCache();
}

//--------------------------------------------------------------------

/**
* {@inheritDoc}
*/
public function setLastModified($date)
{
if ($date instanceof DateTime)
{
$date->setTimezone(new DateTimeZone('UTC'));
$this->setHeader('Last-Modified', $date->format('D, d M Y H:i:s') . ' GMT');
}
elseif (is_string($date))
{
$this->setHeader('Last-Modified', $date);
}

return $this;
}

//--------------------------------------------------------------------
//--------------------------------------------------------------------
// Output Methods
//--------------------------------------------------------------------

/**
* For unit testing, don't actually send headers.
*
* @param boolean $pretend
* @return $this
*/
public function pretend(bool $pretend = true)
{
$this->pretend = $pretend;

return $this;
}

/**
* {@inheritDoc}
*
* @todo Do downloads need CSP or Cookies? Compare with ResponseTrait::send()
*/
public function send()
{
Expand Down Expand Up @@ -438,39 +332,6 @@ public function buildHeaders()
$this->noCache();
}

/**
* Sends the headers of this HTTP request to the browser.
*
* @return DownloadResponse
*/
public function sendHeaders()
{
// Have the headers already been sent?
if ($this->pretend || headers_sent())
{
return $this;
}

// Per spec, MUST be sent with each request, if possible.
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
if (! isset($this->headers['Date']))
{
$this->setDate(DateTime::createFromFormat('U', (string) time()));
}

// HTTP Status
header(sprintf('HTTP/%s %s %s', $this->getProtocolVersion(), $this->getStatusCode(), $this->getReason()), true,
$this->getStatusCode());

// Send all of our headers
foreach ($this->getHeaders() as $name => $values)
{
header($name . ': ' . $this->getHeaderLine($name), false, $this->getStatusCode());
}

return $this;
}

/**
* output download file text.
*
Expand Down
Loading