Skip to content

Commit

Permalink
Merge branch 'master' into feat/route-compact-syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
n0nag0n authored Sep 28, 2024
2 parents a788c8a + 128b6e3 commit bc001b7
Show file tree
Hide file tree
Showing 14 changed files with 565 additions and 59 deletions.
23 changes: 23 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Pull Request Check
on: [pull_request]

jobs:
unit-test:
name: Unit testing
strategy:
fail-fast: false
matrix:
php: [7.4, 8.0, 8.1, 8.2, 8.3]
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: curl, mbstring
tools: composer:v2
- run: composer install
- run: composer test
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
},
"require-dev": {
"ext-pdo_sqlite": "*",
"flightphp/runway": "^0.2.0",
"flightphp/runway": "^0.2.3 || ^1.0",
"league/container": "^4.2",
"level-2/dice": "^4.0",
"phpstan/extension-installer": "^1.3",
Expand Down
29 changes: 26 additions & 3 deletions flight/Engine.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,10 @@
* @method void jsonp(mixed $data, string $param = 'jsonp', int $code = 200, bool $encode = true, string $charset = 'utf-8', int $option = 0)
* Sends a JSONP response.
*
* # HTTP caching
* # HTTP methods
* @method void etag(string $id, ('strong'|'weak') $type = 'strong') Handles ETag HTTP caching.
* @method void lastModified(int $time) Handles last modified HTTP caching.
* @method void download(string $filePath) Downloads a file
*
* phpcs:disable PSR2.Methods.MethodDeclaration.Underscore
*/
Expand All @@ -78,7 +79,7 @@ class Engine
private const MAPPABLE_METHODS = [
'start', 'stop', 'route', 'halt', 'error', 'notFound',
'render', 'redirect', 'etag', 'lastModified', 'json', 'jsonHalt', 'jsonp',
'post', 'put', 'patch', 'delete', 'group', 'getUrl', 'resource'
'post', 'put', 'patch', 'delete', 'group', 'getUrl', 'download', 'resource'
];

/** @var array<string, mixed> Stored variables. */
Expand Down Expand Up @@ -600,7 +601,10 @@ public function _start(): void
public function _error(Throwable $e): void
{
$msg = sprintf(
<<<HTML



HTML
<h1>500 Internal Server Error</h1>
<h3>%s (%s)</h3>
<pre>%s</pre>
Expand All @@ -612,6 +616,7 @@ public function _error(Throwable $e): void

try {
$this->response()
->cache(0)
->clearBody()
->status(500)
->write($msg)
Expand Down Expand Up @@ -752,6 +757,10 @@ public function _resource(
*/
public function _halt(int $code = 200, string $message = '', bool $actuallyExit = true): void
{
if ($this->response()->getHeader('Cache-Control') === null) {
$this->response()->cache(0);
}

$this->response()
->clearBody()
->status($code)
Expand Down Expand Up @@ -906,6 +915,20 @@ public function _jsonp(
}
}

/**
* Downloads a file
*
* @param string $filePath The path to the file to download
*
* @throws Exception If the file cannot be found
*
* @return void
*/
public function _download(string $filePath): void
{
$this->response()->downloadFile($filePath);
}

/**
* Handles ETag HTTP caching.
*
Expand Down
5 changes: 3 additions & 2 deletions flight/Flight.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,16 +70,17 @@
* @method static void redirect(string $url, int $code = 303) Redirects to another URL.
* @method static void json(mixed $data, int $code = 200, bool $encode = true, string $charset = "utf8", int $encodeOption = 0, int $encodeDepth = 512)
* Sends a JSON response.
* @method void jsonHalt(mixed $data, int $code = 200, bool $encode = true, string $charset = 'utf-8', int $option = 0)
* @method static void jsonHalt(mixed $data, int $code = 200, bool $encode = true, string $charset = 'utf-8', int $option = 0)
* Sends a JSON response and immediately halts the request.
* @method static void jsonp(mixed $data, string $param = 'jsonp', int $code = 200, bool $encode = true, string $charset = "utf8", int $encodeOption = 0, int $encodeDepth = 512)
* Sends a JSONP response.
* @method static void error(Throwable $exception) Sends an HTTP 500 response.
* @method static void notFound() Sends an HTTP 404 response.
*
* # HTTP caching
* # HTTP methods
* @method static void etag(string $id, ('strong'|'weak') $type = 'strong') Performs ETag HTTP caching.
* @method static void lastModified(int $time) Performs last modified HTTP caching.
* @method static void download(string $filePath) Downloads a file
*/
class Flight
{
Expand Down
59 changes: 59 additions & 0 deletions flight/net/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -414,4 +414,63 @@ public static function getScheme(): string

return 'http';
}

/**
* Retrieves the array of uploaded files.
*
* @return array<string, array<string,UploadedFile>|array<string,array<string,UploadedFile>>> The array of uploaded files.
*/
public function getUploadedFiles(): array
{
$files = [];
$correctedFilesArray = $this->reArrayFiles($this->files);
foreach ($correctedFilesArray as $keyName => $files) {
foreach ($files as $file) {
$UploadedFile = new UploadedFile(
$file['name'],
$file['type'],
$file['size'],
$file['tmp_name'],
$file['error']
);
if (count($files) > 1) {
$files[$keyName][] = $UploadedFile;
} else {
$files[$keyName] = $UploadedFile;
}
}
}

return $files;
}

/**
* Re-arranges the files in the given files collection.
*
* @param Collection $filesCollection The collection of files to be re-arranged.
*
* @return array<string, array<int, array<string, mixed>>> The re-arranged files collection.
*/
protected function reArrayFiles(Collection $filesCollection): array
{

$fileArray = [];
foreach ($filesCollection as $fileKeyName => $file) {
$isMulti = is_array($file['name']) === true && count($file['name']) > 1;
$fileCount = $isMulti === true ? count($file['name']) : 1;
$fileKeys = array_keys($file);

for ($i = 0; $i < $fileCount; $i++) {
foreach ($fileKeys as $key) {
if ($isMulti === true) {
$fileArray[$fileKeyName][$i][$key] = $file[$key][$i];
} else {
$fileArray[$fileKeyName][$i][$key] = $file[$key];
}
}
}
}

return $fileArray;
}
}
68 changes: 58 additions & 10 deletions flight/net/Response.php
Original file line number Diff line number Diff line change
Expand Up @@ -286,15 +286,9 @@ public function clear(): self
*/
public function cache($expires): self
{
if ($expires === false) {
if ($expires === false || $expires === 0) {
$this->headers['Expires'] = 'Mon, 26 Jul 1997 05:00:00 GMT';

$this->headers['Cache-Control'] = [
'no-store, no-cache, must-revalidate',
'post-check=0, pre-check=0',
'max-age=0',
];

$this->headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, max-age=0';
$this->headers['Pragma'] = 'no-cache';
} else {
$expires = \is_int($expires) ? $expires : strtotime($expires);
Expand Down Expand Up @@ -437,15 +431,31 @@ public function send(): void
$this->processResponseCallbacks();
}

if (headers_sent() === false) {
$this->sendHeaders(); // @codeCoverageIgnore
if ($this->headersSent() === false) {
// If you haven't set a Cache-Control header, we'll assume you don't want caching
if ($this->getHeader('Cache-Control') === null) {
$this->cache(false);
}

$this->sendHeaders();
}

echo $this->body;

$this->sent = true;
}

/**
* Headers have been sent
*
* @return bool
* @codeCoverageIgnore
*/
public function headersSent(): bool
{
return headers_sent();
}

/**
* Adds a callback to process the response body before it's sent. These are processed in the order
* they are added
Expand All @@ -470,4 +480,42 @@ protected function processResponseCallbacks(): void
$this->body = $callback($this->body);
}
}

/**
* Downloads a file.
*
* @param string $filePath The path to the file to be downloaded.
*
* @return void
*/
public function downloadFile(string $filePath): void
{
if (file_exists($filePath) === false) {
throw new Exception("$filePath cannot be found.");
}

$fileSize = filesize($filePath);

$mimeType = mime_content_type($filePath);
$mimeType = $mimeType !== false ? $mimeType : 'application/octet-stream';

$this->send();
$this->setRealHeader('Content-Description: File Transfer');
$this->setRealHeader('Content-Type: ' . $mimeType);
$this->setRealHeader('Content-Disposition: attachment; filename="' . basename($filePath) . '"');
$this->setRealHeader('Expires: 0');
$this->setRealHeader('Cache-Control: must-revalidate');
$this->setRealHeader('Pragma: public');
$this->setRealHeader('Content-Length: ' . $fileSize);

// // Clear the output buffer
ob_clean();
flush();

// // Read the file and send it to the output buffer
readfile($filePath);
if (empty(getenv('PHPUNIT_TEST'))) {
exit; // @codeCoverageIgnore
}
}
}
Loading

0 comments on commit bc001b7

Please sign in to comment.