Skip to content

Commit c15c14f

Browse files
committed
🎉 created base project
0 parents  commit c15c14f

5 files changed

Lines changed: 278 additions & 0 deletions

File tree

.github/FUNDING.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# These are supported funding model platforms
2+
3+
open_collective: leaf

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
test
2+
Experimental
3+
vendor
4+
composer.lock

README.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<!-- markdownlint-disable no-inline-html -->
2+
<p align="center">
3+
<br><br>
4+
<img src="https://leafphp.netlify.app/assets/img/leaf3-logo.png" height="100"/>
5+
<h1 align="center">Leaf Cors Module</h1>
6+
<br><br>
7+
</p>
8+
9+
# Leaf PHP
10+
11+
[![Latest Stable Version](https://poser.pugx.org/leafs/cors/v/stable)](https://packagist.org/packages/leafs/cors)
12+
[![Total Downloads](https://poser.pugx.org/leafs/cors/downloads)](https://packagist.org/packages/leafs/cors)
13+
[![License](https://poser.pugx.org/leafs/cors/license)](https://packagist.org/packages/leafs/cors)
14+
15+
This is a [Leaf PHP](https://leafphp.netlify.app/) module used to enable and configure [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing) with various options. This module can be used both in and out of Leaf and so can be considered a general module. It is also inspired by the [ExpressJS](https://github.com/expressjs/express) [cors package](https://github.com/expressjs/cors).
16+
17+
## Installation
18+
19+
You can easily install it using [Composer](https://getcomposer.org/):
20+
21+
```bash
22+
composer require leafs/cors
23+
```
24+
25+
## Usage
26+
27+
After installing the cors module, the cors module is automatically linked to the leaf app and can be used directly without referencing it anywhere.
28+
29+
### Simple Usage (Enable *All* CORS Requests)
30+
31+
```php
32+
require __DIR__ . "/vendor/autoload.php";
33+
34+
$app = new Leaf\App;
35+
36+
$app->cors();
37+
38+
$app->get('/products/{id}', function () use($app) {
39+
$app->response()->json({["message" => "This is CORS-enabled for all origins!"]);
40+
});
41+
42+
$app->run();
43+
```
44+
45+
You can alternatively call `Leaf\Http\Cors::config()` instead of `$app->cors()` in the example above.
46+
47+
### Configuring CORS
48+
49+
```php
50+
require __DIR__ . "/vendor/autoload.php";
51+
52+
$app = new Leaf\App;
53+
54+
$app->cors([
55+
"origin" => 'http://example.com',
56+
"optionsSuccessStatus" => 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
57+
]);
58+
59+
$app->get('/products/{id}', function () use($app) {
60+
$app->response()->json({["message" => "This is CORS-enabled for all origins!"]);
61+
});
62+
63+
$app->run();
64+
```
65+
66+
## Configuration Options
67+
68+
* `origin`: Configures the **Access-Control-Allow-Origin** CORS header. Possible values:
69+
* `String` - set `origin` to a specific origin. For example if you set it to `"http://example.com"` only requests from "http://example.com" will be allowed.
70+
* `RegExp` - set `origin` to a regular expression pattern which will be used to test the request origin. If it's a match, the request origin will be reflected. For example the pattern `/example\.com$/` will reflect any request that is coming from an origin ending with "example.com".
71+
* `Array` - set `origin` to an array of valid origins. Each origin can be a `String` or a `RegExp`. For example `["http://example1.com", /\.example2\.com$/]` will accept any request from "http://example1.com" or from a subdomain of "example2.com".
72+
* `Function` - set `origin` to a function implementing some custom logic. The function takes the request origin as the first parameter and a callback (called as `callback(err, origin)`, where `origin` is a non-function value of the `origin` option) as the second.
73+
* `methods`: Configures the **Access-Control-Allow-Methods** CORS header. Expects a comma-delimited string (ex: 'GET,PUT,POST') or an array (ex: `['GET', 'PUT', 'POST']`).
74+
* `allowedHeaders`: Configures the **Access-Control-Allow-Headers** CORS header. Expects a comma-delimited string (ex: 'Content-Type,Authorization') or an array (ex: `['Content-Type', 'Authorization']`). If not specified, defaults to reflecting the headers specified in the request's **Access-Control-Request-Headers** header.
75+
* `exposedHeaders`: Configures the **Access-Control-Expose-Headers** CORS header. Expects a comma-delimited string (ex: 'Content-Range,X-Content-Range') or an array (ex: `['Content-Range', 'X-Content-Range']`). If not specified, no custom headers are exposed.
76+
* `credentials`: Configures the **Access-Control-Allow-Credentials** CORS header. Set to `true` to pass the header, otherwise it is omitted.
77+
* `maxAge`: Configures the **Access-Control-Max-Age** CORS header. Set to an integer to pass the header, otherwise it is omitted.
78+
* `preflightContinue`: Pass the CORS preflight response to the next handler.
79+
* `optionsSuccessStatus`: Provides a status code to use for successful `OPTIONS` requests, since some legacy browsers (IE11, various SmartTVs) choke on `204`.
80+
81+
The default configuration is the equivalent of:
82+
83+
```json
84+
{
85+
"origin": "*",
86+
"methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
87+
"allowedHeaders": "*",
88+
"exposedHeaders": "",
89+
"credentials": false,
90+
"maxAge": null,
91+
"preflightContinue": false,
92+
"optionsSuccessStatus": 204,
93+
}
94+
```
95+
96+
## View Leaf's docs [here](https://leafphp.netlify.app/#/)
97+
98+
Built with ❤ by [**Mychi Darko**](https://mychi.netlify.app)

composer.json

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"name": "leafs/cors",
3+
"description": "Leaf PHP cors config",
4+
"keywords": [
5+
"cors",
6+
"cross origin resource sharing",
7+
"origin",
8+
"leaf",
9+
"php",
10+
"framework"
11+
],
12+
"homepage": "https://leafphp.netlify.app/#/",
13+
"type": "library",
14+
"license": "MIT",
15+
"authors": [
16+
{
17+
"name": "Michael Darko",
18+
"email": "mickdd22@gmail.com",
19+
"homepage": "https://mychi.netlify.app",
20+
"role": "Developer"
21+
}
22+
],
23+
"autoload": {
24+
"psr-4": {
25+
"Leaf\\Http\\": "src"
26+
}
27+
},
28+
"minimum-stability": "stable"
29+
}

src/Cors.php

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
<?php
2+
3+
namespace Leaf\Http;
4+
5+
/**
6+
* Leaf CORS Module
7+
* -------
8+
* CORS simplified. Enable CORS with various options.
9+
* Inspired by Express js's CORS package.
10+
*
11+
* @version 1.0
12+
* @since 3.0-beta
13+
*/
14+
class Cors
15+
{
16+
protected static $config = [];
17+
18+
protected static $defaultConfig = [
19+
"origin" => "*",
20+
"methods" => "GET,HEAD,PUT,PATCH,POST,DELETE",
21+
"allowedHeaders" => "*",
22+
"exposedHeaders" => "",
23+
"credentials" => false,
24+
"maxAge" => null,
25+
"preflightContinue" => false,
26+
"optionsSuccessStatus" => 204,
27+
];
28+
29+
public static function config($config = [])
30+
{
31+
static::$config = array_merge(static::$defaultConfig, $config);
32+
33+
if (Request::getMethod() === "OPTIONS") {
34+
if (static::$config["preflightContinue"]) {
35+
// skip to code
36+
} else {
37+
static::configureOrigin();
38+
static::configureHeaders();
39+
static::configureExposedHeaders();
40+
static::configureMaxAge();
41+
static::configureCredentials();
42+
static::configureMethods();
43+
44+
Response::throwErr(
45+
"",
46+
static::$config["optionsSuccessStatus"]
47+
);
48+
}
49+
}
50+
}
51+
52+
protected static function configureMethods()
53+
{
54+
if (is_array(static::$config["methods"])) {
55+
static::$config["methods"] = implode(",", static::$config["methods"]);
56+
}
57+
58+
Headers::accessControl("Allow-Methods", static::$config["methods"]);
59+
}
60+
61+
protected static function configureOrigin()
62+
{
63+
$origin = static::$config["origin"];
64+
65+
// Safari (and potentially other browsers) need content-length 0,
66+
// for 204 or they just hang waiting for a body
67+
Headers::set("Content-Length", "0");
68+
Headers::accessControl(
69+
"Allow-Origin",
70+
static::isOriginAllowed($origin) ? $_SERVER['HTTP_ORIGIN'] : false
71+
);
72+
73+
if ($origin !== "*") {
74+
Headers::set("Vary", "Origin");
75+
}
76+
}
77+
78+
protected static function configureHeaders()
79+
{
80+
$headers = static::$config["allowedHeaders"];
81+
82+
if (!$headers) {
83+
// .headers wasn't specified, so reflect the request headers
84+
$headers = Headers::get("access-control-request-headers");
85+
Headers::set("Vary", "Access-Control-Request-Headers");
86+
}
87+
88+
if ($headers) {
89+
Headers::accessControl(
90+
"Allow-Headers",
91+
is_array($headers) ? implode(", ", $headers) : (strlen($headers) ? $headers : "*")
92+
);
93+
}
94+
}
95+
96+
protected static function configureExposedHeaders()
97+
{
98+
$headers = static::$config["exposedHeaders"];
99+
100+
if ($headers) {
101+
Headers::accessControl(
102+
"Expose-Headers",
103+
is_array($headers) ? implode(", ", $headers) : $headers
104+
);
105+
}
106+
}
107+
108+
protected static function configureMaxAge()
109+
{
110+
if (is_int(static::$config["maxAge"])) {
111+
Headers::accessControl([
112+
"Max-Age" => static::$config["maxAge"],
113+
]);
114+
}
115+
}
116+
117+
protected static function configureCredentials()
118+
{
119+
if (static::$config["credentials"] === true) {
120+
Headers::accessControl("Allow-Credentials", "true");
121+
}
122+
}
123+
124+
protected static function isOriginAllowed($allowedOrigin)
125+
{
126+
$origin = $_SERVER['HTTP_ORIGIN'];
127+
128+
if (is_array($allowedOrigin)) {
129+
for ($i = 0; $i < count($allowedOrigin); $i++) {
130+
if (static::isOriginAllowed($origin, $allowedOrigin[$i])) {
131+
return true;
132+
}
133+
}
134+
135+
return false;
136+
} else if (is_string($allowedOrigin)) {
137+
return $origin === $allowedOrigin;
138+
} else if (@preg_match($allowedOrigin, null) === false) {
139+
return !!preg_match($allowedOrigin, $origin);
140+
} else {
141+
return !!$allowedOrigin;
142+
}
143+
}
144+
}

0 commit comments

Comments
 (0)