Repository: nezamy/route
Branch: master
Commit: cd683aef81c1
Files: 38
Total size: 87.1 KB
Directory structure:
gitextract_dakvb4o0/
├── .gitignore
├── .php_cs
├── LICENSE
├── README.md
├── composer.json
├── phpunit.xml
├── src/
│ ├── DataType/
│ │ └── Uri.php
│ ├── Http/
│ │ ├── Auth/
│ │ │ ├── AuthInterface.php
│ │ │ ├── Basic.php
│ │ │ └── Digest.php
│ │ ├── Auth.php
│ │ ├── GlobalRequest.php
│ │ ├── Middleware.php
│ │ ├── Request.php
│ │ ├── Response.php
│ │ └── Session.php
│ ├── Prototype/
│ │ ├── ArrayPrototype.php
│ │ ├── ConvertObject.php
│ │ ├── Getter.php
│ │ ├── GetterObject.php
│ │ ├── ObjectStore.php
│ │ ├── Setter.php
│ │ └── StringPrototype.php
│ ├── Route.php
│ ├── Routing/
│ │ ├── Route.php
│ │ ├── RouteHandler.php
│ │ ├── RouteHandlerInterface.php
│ │ ├── RouteParser.php
│ │ ├── RouteParserInterface.php
│ │ └── Router.php
│ ├── Support/
│ │ ├── ArrayTrait.php
│ │ └── Regex.php
│ └── functions.php
└── tests/
├── AuthTest.php
├── DummyRequest.php
├── RouteParserTest.php
├── RouteTest.php
└── bootstrap.php
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
Thumbs.db
ehthumbs.db
.DS_Store
._*
.idea
.idea/vcs.xml
vendor
.phpunit.*
================================================
FILE: .php_cs
================================================
<?php
$header = <<<'EOF'
This file is part of Just.
@license https://github.com/just-framework/php/blob/master/LICENSE MIT License
@link https://justframework.com/php/
@author Mahmoud Elnezamy <mahmoud@nezamy.com>
@package Just
EOF;
return PhpCsFixer\Config::create()
->setRiskyAllowed(true)
->setRules([
'@PSR2' => true,
'@Symfony' => true,
'@DoctrineAnnotation' => true,
'@PhpCsFixer' => true,
'header_comment' => [
'commentType' => 'PHPDoc',
'header' => $header,
'separate' => 'none',
'location' => 'after_declare_strict',
],
'array_syntax' => [
'syntax' => 'short'
],
'list_syntax' => [
'syntax' => 'short'
],
'concat_space' => [
'spacing' => 'one'
],
'blank_line_before_statement' => [
'statements' => [
'declare',
],
],
'ordered_imports' => [
'imports_order' => [
'class', 'function', 'const',
],
'sort_algorithm' => 'alpha',
],
'single_line_comment_style' => [
'comment_types' => [
],
],
'yoda_style' => [
'always_move_variable' => false,
'equal' => false,
'identical' => false,
],
'phpdoc_align' => [
'align' => 'left',
],
'multiline_whitespace_before_semicolons' => [
'strategy' => 'no_multi_line',
],
'constant_case' => [
'case' => 'lower',
],
'class_attributes_separation' => true,
'combine_consecutive_unsets' => true,
'declare_strict_types' => true,
'linebreak_after_opening_tag' => true,
'lowercase_static_reference' => true,
'no_useless_else' => true,
'no_unused_imports' => true,
'not_operator_with_successor_space' => true,
'not_operator_with_space' => false,
'ordered_class_elements' => true,
'php_unit_strict' => false,
'phpdoc_separation' => false,
'single_quote' => true,
'standardize_not_equals' => true,
'multiline_comment_opening_closing' => true,
'phpdoc_add_missing_param_annotation' => true,
'no_null_property_initialization' => true
])
->setFinder(
PhpCsFixer\Finder::create()
->exclude('public')
->exclude('runtime')
->exclude('vendor')
->in(__DIR__)
)
->setUsingCache(false);
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2016 Mahmoud Elnezamy
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# Route v2.0
Route - Fast, flexible routing for PHP, enabling you to quickly and easily build RESTful web applications.
## Installation
```bash
$ composer require nezamy/route
```
Or if you looking for ready template for using this route Go to https://github.com/nezamy/just
Route requires PHP 7.4.0 or newer.
## Changes list
- Rewrite route based on php 7.4
- Support Swoole extensions
- Support locales to build multi languages website
- Added Auth, Basic, Digest
- Availability to customize route parser and handler
- Smart dependency injection and service container
## Usage
Only if using composer create index.php in root.
Create an index.php file with the following contents:
```php
<?php declare(strict_types=1);
define('DS', DIRECTORY_SEPARATOR);
define('BASE_PATH', __DIR__ . DS);
//Show errors
//===================================
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);
//===================================
require BASE_PATH.'vendor/autoload.php';
$request = new Just\Http\GlobalRequest;
$response = new Just\Http\Response;
$route = new Just\Routing\Router($request, $response);
// let store them to container, to use them as a singleton
container()->set(Just\Http\Request::class, $request);
container()->set(Just\Http\Response::class, $response);
container()->set(Just\Routing\Router::class, $route);
try {
include 'app/routes.php';
$output = $route->run();
foreach ($output->headers->all() as $k => $v) {
header("$k: $v");
}
http_response_code($output->statusCode());
if ($output->hasRedirect()) {
list($url, $code) = $output->getRedirect();
header("Location: $url", true, $code);
}
} catch (\Error $e) {
pre($e, 'Error', 6);
} catch (\Exception $e) {
pre($e, 'Exception', 6);
}
echo response()->body();
```
app/routes.php
```php
<?php
use Just\Route;
Route::get('/', function (){
return 'Welcome to the home page';
});
// Maybe you want to customize 404 page
Route::setNotfound(function (){
return 'Page Not found';
});
```
### Use with [Swoole](https://www.swoole.co.uk)
```php
<?php
declare(strict_types=1);
use Swoole\Http\Server;
use Swoole\Http\Request;
use Swoole\Http\Response;
use Just\Routing\Router;
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);
require __DIR__ . '/vendor/autoload.php';
$http = new Server("0.0.0.0", 9501);
$http->set([
'document_root' => '/var/www/public',
'enable_static_handler' => true,
]);
$http->on("request", function (Request $request, Response $response) {
$request = new Just\Http\Request(
$request->header ?? [],
$request->server ?? [],
$request->cookie ?? [],
$request->get ?? [],
$request->post ?? [],
$request->files ?? [],
$request->tmpfiles ?? []
);
$response = new Just\Http\Response;
$route = new Just\Routing\Router($request, $response);
container()->set(Just\Http\Request::class, $request);
container()->set(Just\Http\Response::class, $response);
container()->set(Router::class, $route);
try {
include __DIR__ .'/app/routes.php';
$output = $route->run();
foreach ($output->headers->all() as $k => $v) {
$response->header($k, $v);
}
$response->setStatusCode($output->statusCode());
if ($output->hasRedirect()) {
list($url, $code) = $output->getRedirect();
$response->redirect($url, $code);
}
} catch (\Error $e) {
pre($e, 'Error', 6);
} catch (\Exception $e) {
pre($e, 'Exception', 6);
}
$response->end(response()->body(true));
});
$http->start();
```
## How it works
Routing is done by matching a URL pattern with a callback function.
### app/routes.php
```php
Route::any('/', function() {
return 'Hello World';
});
Route::post('/contact-us', function(\Just\Http\Request $req) {
pre($req->body, 'Request');
});
```
### The callback can be any object that is callable. So you can use a regular function:
```php
function pages() {
return 'Page Content';
}
Route::get('/', 'pages');
```
### Or a class method:
```php
class home
{
public function pages() {
return 'Home page Content';
}
}
Route::get('/', [home::class, 'pages']);
// OR
Route::get('/', 'home@pages');
```
## Method Routing
```php
Route::any('/', function() {});
Route::get('/', function() {});
Route::post('/', function() {});
Route::put('/', function() {});
Route::patch('/', function() {});
Route::option('/', function() {});
Route::delete('/', function() {});
```
## Parameters
```php
// This example will match any page name
Route::get('/{page}', function($page) {
return "you are in $page";
});
Route::get('/post/{id}', function($id) {
// Will match anything like post/hello or post/5 ...
// But not match /post/5/title
return "post id $id";
});
// more than parameters
Route::get('/post/{id}/{title}', function($id, $title) {
return "post id $id and title $title";
});
// you can get parameter in any order
Route::get('/post/{id}/{title}', function($title, $id) {
return "post id $id and title $title";
});
```
### For “unlimited” optional parameters, you can do this:
```php
// This example will match anything after blog/ - unlimited arguments
Route::get('/blog/{any}:*', function($any) {
pre($any);
});
```
## Regular Expressions
You can validate the args by regular expressions.
```php
// Validate args by regular expressions uses :(your pattern here)
Route::get('/{username}:([0-9a-z_.-]+)/post/{id}:([0-9]+)',
function($username, $id) {
return "author $username post id $id";
});
// You can add named regex pattern in routes
Route::addPlaceholders([
'username' => '([0-9a-z_.-]+)',
'id' => '([0-9]+)'
]);
// Now you can use named regex
Route::get('/{username}:username/post/{id}:id', function($username, $id) {
return "author $username post id $id";
});
//if the parameter name match the placeholder name just ignore placeholder and route will deduct that
Route::get('/{username}/post/{id}', function($username, $id) {
return "author $username post id $id";
});
```
### Some named regex patterns already registered in routes
```php
[
'int' => '/([0-9]+)',
'multiInt' => '/([0-9,]+)',
'title' => '/([a-z_-]+)',
'key' => '/([a-z0-9_]+)',
'multiKey' => '/([a-z0-9_,]+)',
'isoCode2' => '/([a-z]{2})',
'isoCode3' => '/([a-z]{3})',
'multiIsoCode2' => '/([a-z,]{2,})',
'multiIsoCode3' => '/([a-z,]{3,})'
];
```
## Optional parameters
You can specify named parameters that are optional for matching by adding (?)
```php
Route::get('/post/{title}?:title/{date}?',
function($title, $date) {
$content = '';
if ($title) {
$content = "<h1>$title</h1>";
}else{
$content = "<h1>Posts List</h1>";
}
if ($date) {
$content .= "<small>Published $date</small>";
}
return $content;
});
```
## Groups
```php
Route::group('/admin', function()
{
// /admin/
Route::get('/', function() {});
// /admin/settings
Route::get('/settings', function() {});
// nested group
Route::group('/users', function()
{
// /admin/users
Route::get('/', function() {});
// /admin/users/add
Route::get('/add', function() {});
});
// Anything else
Route::any('/{any}:*', function($any) {
pre("Page ( $any ) Not Found", 6);
});
});
```
### Groups with parameters
```php
Route::group('/{module}', function($lang)
{
Route::post('/create', function() {});
Route::put('/update', function() {});
});
```
### Locales
```php
// the first language is the default i.e. ar
// when you hit the site http://localhost on the first time will redirect to http://localhost/ar
Route::locale(['ar','en'], function(){
// will be /ar/
Route::get('/', function($locale){
//get current language
pre($locale);
});
// /ar/contact
Route::get('/contact', function() {});
Route::group('/blog', function() {
// /ar/blog/
Route::get('/', function() {});
});
});
// Also you can write locales like that or whatever you want
Route::locale(['ar-eg','en-us'], function(){
// will be /ar/
Route::get('/', function($locale){
//get current language
list($lang, $country) = explode('-', $locale, 2);
pre("Lang is $lang, Country is $country");
});
});
```
### Auth
#### Basic
```php
$auth = new \Just\Http\Auth\Basic(['users' => [
'user1' => '123456',
'user2' => '987654'
]]);
Route::auth($auth, function (){
Route::get('/secret', function(\Just\Http\Request $req){
pre("Hello {$req->user()->get('username')}, this is a secret page");
});
});
```
#### Digest
```php
$auth = new \Just\Http\Auth\Digest(['users' => [
'user1' => '123456',
'user2' => '987654'
]]);
Route::auth($auth, function (){
Route::get('/secret', function(\Just\Http\Request $req){
pre("Hello {$req->user()->get('username')}, this is a secret page");
});
});
```
### Middleware
#### Global
```php
Route::use(function (\Just\Http\Request $req, $next){
//validate something the call next to continue or return whatever if you want break
if($req->isMobile()){
return 'Please open from a desktop';
}
return $next();
}, function ($next){
// another middleware
$next();
});
// After
Route::use(function ($next){
$response = $next();
// make some action
return $response;
});
```
#### Middleware on groups
```php
// if open from mobile device
Route::middleware(fn(\Just\Http\Request $req, $next) => !$req->isMobile() ? '' : $next())
->group('/mobile-only', function (){
Route::get('/', function(\Just\Http\Request $req){
pre($req->browser());
});
});
```
If you make the middleware as a class, you can pass the class with namespace.
the class should be had a `handle` method.
```php
class MobileOnly{
public function handle(\Just\Http\Request $req, $next){
return !$req->isMobile() ? '' : $next();
}
}
Route::middleware(MobileOnly::class)
->group('/',function (){
Route::get('/', function(\Just\Http\Request $req){
pre($req->browser());
});
});
```
#### Middleware on route
```php
Route::get('/', function(\Just\Http\Request $req){
pre($req->browser());
})->middleware(MobileOnly::class);
```
### Dependency injection
To learn about Dependency injection and service container please visit this [link](https://github.com/nezamy/di)
### Handle and Parser customization
Example of CustomRouteHandler
```php
class CustomRouteHandler implements Just\Routing\RouteHandlerInterface
{
public function call(callable $handler, array $args = [])
{
return call_user_func_array($handler, $args);
}
public function parse($handler): callable
{
if (is_string($handler) && ! function_exists($handler)) {
$handler = explode('@', $handler, 2);
}
return $handler;
}
}
\Just\Route::setHandler(new CustomRouteHandler);
```
```php
class CustomRouteParser implements RouteParserInterface
{
public function parse(string $uri): array
{
$matchedParameter = [];
$matchedPattern = [];
$result = [];
// parse uri here and return array of 3 elements
// /{page}
// /{page}?
return ['parameters' => $matchedParameter, 'patterns' => $matchedPattern, 'result' => $result];
}
}
\Just\Route::setParser(new CustomRouteParser);
```
================================================
FILE: composer.json
================================================
{
"name": "nezamy/route",
"type": "library",
"description": "Route - Fast, flexible routing for PHP, enabling you to quickly and easily build RESTful web applications.",
"keywords": [
"route",
"restful",
"api",
"justframework"
],
"homepage": "https://nezamy.com/route",
"license": "MIT",
"authors": [
{
"name": "Mahmoud Elnezamy",
"email": "mahmoud@nezamy.com",
"homepage": "http://nezamy.com",
"role": "Full Stack Web Developer"
}
],
"autoload": {
"psr-4": {
"Just\\": "src/",
"": ""
},
"files": [
"src/functions.php"
]
},
"autoload-dev": {
"psr-4": {
"Just\\Test\\": "tests/"
}
},
"require": {
"php": ">=7.4",
"ext-json": "*",
"ext-mbstring": "*",
"nezamy/di": "^2.0"
},
"require-dev": {
"phpunit/phpunit": "^7.5 || ^8.5",
"friendsofphp/php-cs-fixer": "^2.16"
},
"scripts": {
"fix": "php-cs-fixer fix ./src",
"test": "phpunit"
}
}
================================================
FILE: phpunit.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="true"
backupStaticAttributes="false"
bootstrap="tests/bootstrap.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
testdox="true">
<testsuites>
<testsuite name="Application Test Suite">
<directory>./tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">app/</directory>
</whitelist>
</filter>
<php>
</php>
</phpunit>
================================================
FILE: src/DataType/Uri.php
================================================
<?php
declare(strict_types=1);
/**
* This file is part of Just.
*
* @license https://github.com/just-framework/php/blob/master/LICENSE MIT License
* @link https://justframework.com/php/
* @author Mahmoud Elnezamy <mahmoud@nezamy.com>
* @package Just
*/
namespace Just\DataType;
use Just\Prototype\StringPrototype;
class Uri extends StringPrototype
{
public function getChunk(?string $uri = null)
{
$uri = $uri ?? $this->data;
$chunk = $uri ? '/' . explode('/', $uri)[1] : '/';
return $this->isStatic($chunk) ? $chunk : '/*';
}
public function isStatic(?string $uri = null)
{
$uri = $uri ?? $this->data;
return ! $uri || $uri && strpbrk('*{}?:()', $uri) === false;
}
public function rtrim($char)
{
$this->data = rtrim($this->data, $char);
}
public function ltrim($char)
{
$this->data = ltrim($this->data, $char);
}
}
================================================
FILE: src/Http/Auth/AuthInterface.php
================================================
<?php
declare(strict_types=1);
/**
* This file is part of Just.
*
* @license https://github.com/just-framework/php/blob/master/LICENSE MIT License
* @link https://justframework.com/php/
* @author Mahmoud Elnezamy <mahmoud@nezamy.com>
* @package Just
*/
namespace Just\Http\Auth;
interface AuthInterface
{
public function check(): bool;
public function validate(array $credentials): bool;
public function redirectToLogin();
public function login(): bool;
public function logout(): void;
public function user();
}
================================================
FILE: src/Http/Auth/Basic.php
================================================
<?php
declare(strict_types=1);
/**
* This file is part of Just.
*
* @license https://github.com/just-framework/php/blob/master/LICENSE MIT License
* @link https://justframework.com/php/
* @author Mahmoud Elnezamy <mahmoud@nezamy.com>
* @package Just
*/
namespace Just\Http\Auth;
class Basic implements AuthInterface
{
public $error = '';
protected $options = [];
private $user = [];
public function __construct($options)
{
$this->options = array_merge([
'users' => [],
'realm' => 'Restricted area',
], $options);
}
public function check(): bool
{
return $this->login();
}
public function redirectToLogin()
{
$this->logout();
if (! $this->login()) {
$this->logout();
response()->end($this->error);
}
}
public function login(array $credentials = []): bool
{
$username = $password = null;
if (request()->server->has('PHP_AUTH_USER') && request()->server->has('PHP_AUTH_PW')) {
$username = request()->server->get('PHP_AUTH_USER');
$password = request()->server->get('PHP_AUTH_PW');
} elseif (request()->headers->has('authorization')) {
$auth = request()->headers->get('authorization');
if (strpos(strtolower($auth), 'basic') === 0) {
[$username, $password] = explode(':', base64_decode(substr($auth, 6)));
}
}
return $this->validate(['username' => $username, 'password' => $password]);
}
public function logout(): void
{
response()->headers->set('WWW-Authenticate', 'Basic realm="' . $this->options['realm'] . '"');
response()->setStatusCode(401);
}
public function validate(array $credentials): bool
{
if ($credentials['username'] && $credentials['password']) {
$username = filter_var($credentials['username'], FILTER_SANITIZE_STRING, FILTER_FLAG_ENCODE_HIGH | FILTER_FLAG_ENCODE_LOW);
$password = filter_var($credentials['password'], FILTER_SANITIZE_STRING, FILTER_FLAG_ENCODE_HIGH | FILTER_FLAG_ENCODE_LOW);
$users = $this->options['users'];
if (isset($users[$username]) && $users[$username] == $password) {
unset($credentials['password']);
$this->user = $credentials;
return true;
}
}
$this->error = 'Authorization Required';
return false;
}
public function user(): array
{
return $this->user;
}
}
================================================
FILE: src/Http/Auth/Digest.php
================================================
<?php
declare(strict_types=1);
/**
* This file is part of Just.
*
* @license https://github.com/just-framework/php/blob/master/LICENSE MIT License
* @link https://justframework.com/php/
* @author Mahmoud Elnezamy <mahmoud@nezamy.com>
* @package Just
*/
namespace Just\Http\Auth;
class Digest implements AuthInterface
{
public $error = '';
protected $options = [];
private $user = [];
public function __construct($options)
{
$this->options = array_merge([
'users' => [],
'realm' => 'Restricted area',
'qop' => 'auth',
'nonce' => uniqid(),
'error' => 'Wrong Credentials!',
'cancel' => 'Authorization Required',
], $options);
}
public function check(): bool
{
return $this->login();
}
public function redirectToLogin()
{
$this->logout();
if (! $this->login()) {
$this->logout();
response()->end($this->error);
}
}
public function login(array $credentials = []): bool
{
$data = [];
if (request()->server->has('PHP_AUTH_DIGEST')) {
$data = $this->digest_parse(request()->server->get('PHP_AUTH_DIGEST'));
} elseif (request()->headers->has('authorization')) {
$data = $this->digest_parse(request()->headers->get('authorization'));
}
return $this->validate($data);
}
public function logout(): void
{
response()->headers->set('WWW-Authenticate', 'Digest realm="' . $this->options['realm'] . '",qop="' . $this->options['qop'] . '",nonce="' . $this->options['nonce'] . '",opaque="' . md5($this->options['realm']) . '"');
response()->setStatusCode(401);
}
public function validate(array $credentials): bool
{
if (! $credentials || ! isset($this->options['users'][$credentials['username']])) {
$this->error = $this->options['error'];
return false;
}
$A1 = md5($credentials['username'] . ':' . $this->options['realm'] . ':' . $this->options['users'][$credentials['username']]);
$A2 = md5(request()->method() . ':' . $credentials['uri']);
$valid_response = md5($A1 . ':' . $credentials['nonce'] . ':' . $credentials['nc'] . ':' . $credentials['cnonce'] . ':' . $credentials['qop'] . ':' . $A2);
if ($credentials['response'] != $valid_response) {
$this->error = $this->options['error'];
return false;
}
return true;
}
public function user(): array
{
return $this->user;
}
private function digest_parse($txt)
{
// protect against missing data
$needed_parts = ['nonce' => 1, 'nc' => 1, 'cnonce' => 1, 'qop' => 1, 'username' => 1, 'uri' => 1, 'response' => 1];
$data = [];
$keys = implode('|', array_keys($needed_parts));
preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $txt, $matches, PREG_SET_ORDER);
foreach ($matches as $m) {
$data[$m[1]] = $m[3] ? $m[3] : $m[4];
unset($needed_parts[$m[1]]);
}
return $needed_parts ? [] : $data;
}
}
================================================
FILE: src/Http/Auth.php
================================================
<?php
declare(strict_types=1);
/**
* This file is part of Just.
*
* @license https://github.com/just-framework/php/blob/master/LICENSE MIT License
* @link https://justframework.com/php/
* @author Mahmoud Elnezamy <mahmoud@nezamy.com>
* @package Just
*/
namespace Just\Http;
use Just\Http\Auth\AuthInterface;
/**
* Class Auth.
* @method bool check()
* @method bool validate(array $credentials)
* @method void redirectToLogin()
* @method bool login()
* @method void logout()
* @method array user()
*/
class Auth
{
private AuthInterface $auth;
public function __construct(AuthInterface $auth)
{
$this->auth = $auth;
if (! $auth->check()) {
$auth->redirectToLogin();
}
}
public function __call($method, $args)
{
if (method_exists($this->auth, $method)) {
return call_user_func_array([$this->auth, $method], $args);
}
throw new \LogicException("{$method} Not Exists on Just\\Http\\Auth");
}
}
================================================
FILE: src/Http/GlobalRequest.php
================================================
<?php
declare(strict_types=1);
/**
* This file is part of Just.
*
* @license https://github.com/just-framework/php/blob/master/LICENSE MIT License
* @link https://justframework.com/php/
* @author Mahmoud Elnezamy <mahmoud@nezamy.com>
* @package Just
*/
namespace Just\Http;
class GlobalRequest extends Request
{
public function __construct()
{
$headers = [];
$server = [];
foreach ($_SERVER as $key => $value) {
if (substr($key, 0, 5) === 'HTTP_') {
$key = substr($key, 5);
// $key = $this->parseHeaderKey($key);
$headers[$key] = $value;
continue;
}
$key = strtolower($key);
$server[$key] = $value;
}
if (! isset($headers['authorization'])) {
if (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
$headers['authorization'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
} elseif (isset($_SERVER['PHP_AUTH_USER'])) {
$basic_pass = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : '';
$headers['authorization'] = 'Basic ' . base64_encode($_SERVER['PHP_AUTH_USER'] . ':' . $basic_pass);
} elseif (isset($_SERVER['PHP_AUTH_DIGEST'])) {
$headers['authorization'] = $_SERVER['PHP_AUTH_DIGEST'];
}
}
if (isset($server['content_type']) && $server['content_type'] == 'application/x-www-form-urlencoded') {
parse_str(file_get_contents('php://input'), $input);
} else {
$input = json_decode(file_get_contents('php://input'), true);
}
$post = array_merge($input ?? [], $_POST);
parent::__construct($headers, $server, $_COOKIE, $_GET, $post, $_FILES);
}
}
================================================
FILE: src/Http/Middleware.php
================================================
<?php
declare(strict_types=1);
/**
* This file is part of Just.
*
* @license https://github.com/just-framework/php/blob/master/LICENSE MIT License
* @link https://justframework.com/php/
* @author Mahmoud Elnezamy <mahmoud@nezamy.com>
* @package Just
*/
namespace Just\Http;
use Just\Routing\RouteHandlerInterface;
class Middleware
{
private array $layers = [];
private RouteHandlerInterface $handler;
public function __construct(RouteHandlerInterface $handler)
{
$this->handler = $handler;
}
public function add(array $layers): void
{
$this->layers = array_merge($this->layers, $layers);
}
public function handle(callable $core)
{
$layers = array_reverse($this->layers);
$final = array_reduce($layers, function ($nextLayer, $layer) {
return $this->createLayer($nextLayer, $layer);
}, function () use ($core) {
return $this->handler->call($core);
});
return $this->handler->call($final);
}
private function createLayer($nextLayer, $layer): callable
{
return function () use ($nextLayer, $layer) {
container()->setVar('next', $nextLayer);
if(is_string($layer) && class_exists($layer)){
$layer = [$layer, 'handle'];
}
$layer = $this->handler->parse($layer);
return $this->handler->call($layer);
};
}
}
================================================
FILE: src/Http/Request.php
================================================
<?php
declare(strict_types=1);
/**
* This file is part of Just.
*
* @license https://github.com/just-framework/php/blob/master/LICENSE MIT License
* @link https://justframework.com/php/
* @author Mahmoud Elnezamy <mahmoud@nezamy.com>
* @package Just
*/
namespace Just\Http;
use Just\Prototype\GetterObject;
/**
* Class Request.
* @method array arguments()
* @method string uri()
* @method string method()
* @method string currentUrl()
* @method string protocol()
* @method string scheme()
* @method array user()
*/
class Request
{
public GetterObject $headers;
public GetterObject $server;
public GetterObject $cookies;
public GetterObject $query;
public GetterObject $body;
public GetterObject $files;
public GetterObject $tmp_files;
protected $_arguments = [];
protected $_uri;
protected $_method;
protected $_url;
protected $_currentUrl;
protected $_protocol;
protected $_scheme;
protected GetterObject $_user;
public function __construct(array $headers, array $server, array $cookies, array $query, array $body, array $files, $tmp_files = [])
{
$this->headers = new class($headers) extends GetterObject {
protected function getterTransformKey($key): string
{
return str_replace([' ', '_'], '-', strtolower($key));
}
};
$this->server = new class($server) extends GetterObject {
protected function getterTransformKey($key): string
{
return strtolower($key);
}
};
$this->cookies = new GetterObject($cookies);
$this->query = new GetterObject($query);
$this->body = new GetterObject($body);
$this->files = new GetterObject($files);
$this->tmp_files = new GetterObject($tmp_files);
$this->_protocol = isset($server['server_protocol']) ? str_replace('HTTP/', '', $server['server_protocol']) : '1.1';
$this->_scheme = isset($server['https']) && $server['https'] === 'on' || isset($headers['x-forwarded-proto']) && $headers['x-forwarded-proto'] == 'https' ? 'https' : 'http';
$this->_uri = urldecode(parse_url($server['request_uri'] ?? '', PHP_URL_PATH) ?? '') ?? '/';
$this->_url = $this->_scheme . '://' . $this->serverName();
$this->_currentUrl = $this->url(rtrim($this->_uri, '/') ?? '');
$this->_method = $server['request_method'] ?? 'GET';
}
public function __call($method, $args)
{
if (isset($this->{'_' . $method})) {
return $this->{'_' . $method};
}
throw new \LogicException("{$method} Not Exists on Just\\Http\\Request");
}
public function url($uri = ''): string
{
return $this->_url . $uri;
}
public function serverName(): string
{
return $this->server->get('server_name', $this->headers->get('host', 'localhost'));
}
public function setArguments(array $args): void
{
$this->_arguments = $args;
}
public function setUser(array $user): void
{
$this->_user = new GetterObject($user);
}
public function isJson(): bool
{
if ($this->headers->get('X_REQUESTED_WITH') == 'XMLHttpRequest' || strpos($this->headers->get('ACCEPT'), '/json') !== false) {
return true;
}
return false;
}
public function ip(): string
{
if ($this->headers->has('client_ip')) {
$ip = $this->headers->get('client_ip');
} elseif ($this->headers->has('x_forwarded_for')) {
$ip = $this->headers->get('x_forwarded_for');
} elseif ($this->headers->has('x_forwarded')) {
$ip = $this->headers->get('x_forwarded');
} elseif ($this->headers->has('forwarded_for')) {
$ip = $this->headers->get('forwarded_for');
} elseif ($this->headers->has('forwarded')) {
$ip = $this->headers->get('forwarded');
} elseif ($this->server->has('remote_addr')) {
$ip = $this->server->get('remote_addr');
} else {
$ip = getenv('REMOTE_ADDR');
}
if (! filter_var($ip, FILTER_VALIDATE_IP)) {
return 'unknown';
}
return $ip;
}
public function browser(): string
{
$user_agent = $this->headers->get('user-agent');
if (strpos($user_agent, 'Opera') || strpos($user_agent, 'OPR/')) {
return 'Opera';
}
if (strpos($user_agent, 'Edge')) {
return 'Edge';
}
if (strpos($user_agent, 'Chrome')) {
return 'Chrome';
}
if (strpos($user_agent, 'Safari')) {
return 'Safari';
}
if (strpos($user_agent, 'Firefox')) {
return 'Firefox';
}
if (strpos($user_agent, 'MSIE') || strpos($user_agent, 'Trident/7')) {
return 'Internet Explorer';
}
return 'unknown';
}
public function platform(): string
{
$user_agent = $this->headers->get('user-agent');
if (preg_match('/linux/i', $user_agent)) {
return 'linux';
}
if (preg_match('/macintosh|mac os x/i', $user_agent)) {
return 'mac';
}
if (preg_match('/windows|win32/i', $user_agent)) {
return 'windows';
}
return 'unknown';
}
public function isMobile(): bool
{
$aMobileUA = [
'/iphone/i' => 'iPhone',
'/ipod/i' => 'iPod',
'/ipad/i' => 'iPad',
'/android/i' => 'Android',
'/blackberry/i' => 'BlackBerry',
'/webos/i' => 'Mobile',
];
// Return true if mobile User Agent is detected.
foreach ($aMobileUA as $sMobileKey => $sMobileOS) {
if (preg_match($sMobileKey, $this->headers->get('user-agent'))) {
return true;
}
}
// Otherwise, return false.
return false;
}
}
================================================
FILE: src/Http/Response.php
================================================
<?php
declare(strict_types=1);
/**
* This file is part of Just.
*
* @license https://github.com/just-framework/php/blob/master/LICENSE MIT License
* @link https://justframework.com/php/
* @author Mahmoud Elnezamy <mahmoud@nezamy.com>
* @package Just
*/
namespace Just\Http;
use Just\Prototype\ObjectStore;
class Response
{
public $headers;
protected $_body = '';
protected $_statusCode = 200;
protected $_redirect = [];
private $_end = false;
public function __construct()
{
$this->headers = new ObjectStore();
}
public function body($clean = false): string
{
$body = $this->_body;
if ($clean) {
$this->_body = '';
}
return $body;
}
public function write($body): void
{
if (! $this->_end) {
$this->_body .= $body;
}
}
public function end(string $body = ''): void
{
if (! $this->_end) {
$this->_body = $body;
$this->_end = true;
}
}
public function isEnded(): bool
{
return $this->_end;
}
public function setStatusCode(int $statusCode): void
{
$this->_statusCode = $statusCode;
}
public function statusCode(): int
{
return $this->_statusCode;
}
public function redirectTo(string $location, int $status_code = 302)
{
$this->_redirect = [$location, $status_code];
}
public function hasRedirect(): bool
{
return count($this->_redirect) > 0;
}
public function getRedirect()
{
return $this->_redirect;
}
}
================================================
FILE: src/Http/Session.php
================================================
<?php
declare(strict_types=1);
/**
* This file is part of Just.
*
* @license https://github.com/just-framework/php/blob/master/LICENSE MIT License
* @link https://justframework.com/php/
* @author Mahmoud Elnezamy <mahmoud@nezamy.com>
* @package Just
*/
namespace Just\Http;
class Session
{
private $options = [];
public function __construct(array $options)
{
$this->options = array_merge([
'storage',
], $options);
}
}
================================================
FILE: src/Prototype/ArrayPrototype.php
================================================
<?php
declare(strict_types=1);
/**
* This file is part of Just.
*
* @license https://github.com/just-framework/php/blob/master/LICENSE MIT License
* @link https://justframework.com/php/
* @author Mahmoud Elnezamy <mahmoud@nezamy.com>
* @package Just
*/
namespace Just\Prototype;
class ArrayPrototype
{
use Getter;
use ConvertObject;
use Setter;
public function __construct(array $data = [])
{
$this->replace($data);
return $this;
}
// public function set(string $key, $value): void {
// if(!$value instanceof StringPrototype){
// $type = gettype($value);
// switch ($type) {
// case 'string':
// $value = new StringPrototype($value);
// break;
// case 'array':
// case 'object':
// $value = new ArrayPrototype((array)$value);
// }
// }
// $this->_set($key, $value);
// }
}
================================================
FILE: src/Prototype/ConvertObject.php
================================================
<?php
declare(strict_types=1);
/**
* This file is part of Just.
*
* @license https://github.com/just-framework/php/blob/master/LICENSE MIT License
* @link https://justframework.com/php/
* @author Mahmoud Elnezamy <mahmoud@nezamy.com>
* @package Just
*/
namespace Just\Prototype;
use Just\Storage\KeyValue\KeyValueStoreInterface;
trait ConvertObject
{
public function __toString(): string
{
return $this->toJson();
}
public function toArray($data = null): array
{
$array = [];
$data = $data ?? $this->all();
foreach ((array) $data as $k => $v) {
if (is_object($v)) {
if ($v instanceof StringPrototype) {
$array[$k] = (string) $v;
} else {
$array[$k] = $v instanceof KeyValueStoreInterface ? $this->toArray($v->all()) : $v;
}
} else {
$array[$k] = $v;
}
}
return $array;
}
public function toJson(): string
{
return json_encode($this->toArray(), JSON_PRETTY_PRINT);
}
}
================================================
FILE: src/Prototype/Getter.php
================================================
<?php
declare(strict_types=1);
/**
* This file is part of Just.
*
* @license https://github.com/just-framework/php/blob/master/LICENSE MIT License
* @link https://justframework.com/php/
* @author Mahmoud Elnezamy <mahmoud@nezamy.com>
* @package Just
*/
namespace Just\Prototype;
use ArrayIterator;
trait Getter
{
private $data;
public function get(string $key, $default = '')
{
return $this->data[$this->getterTransformKey($key)] ?? $default;
}
public function has(string $key): bool
{
return isset($this->data[$this->getterTransformKey($key)]);
}
public function all(): array
{
return $this->data;
}
public function only(...$keys): array
{
$results = [];
$keys = $this->variadic(...$keys);
foreach ($keys as $key) {
$results[$key] = $this->get($key);
}
return $results;
}
public function except(...$keys): array
{
$results = $this->data;
$keys = $this->variadic(...$keys);
foreach ($keys as $key) {
unset($results[$key]);
}
return $results;
}
public function keys(): array
{
return array_keys($this->data);
}
public function count(): int
{
return count($this->data);
}
public function last()
{
return array_key_last($this->data);
}
public function getIterator(): ArrayIterator
{
return new ArrayIterator($this->data);
}
public function variadic(...$keys): array
{
if (is_array($keys[0])) {
$shift = array_shift($keys);
$keys = array_merge($shift, $keys);
}
return $keys;
}
protected function getterTransformKey($key): string
{
return $key;
}
}
================================================
FILE: src/Prototype/GetterObject.php
================================================
<?php
declare(strict_types=1);
/**
* This file is part of Just.
*
* @license https://github.com/just-framework/php/blob/master/LICENSE MIT License
* @link https://justframework.com/php/
* @author Mahmoud Elnezamy <mahmoud@nezamy.com>
* @package Just
*/
namespace Just\Prototype;
class GetterObject
{
use Getter;
public function __construct(array $data = [])
{
foreach ($data as $k => $v) {
$this->set($k, $v);
}
}
private function set(string $key, $value): void
{
$this->data[$this->getterTransformKey($key)] = $value;
}
}
================================================
FILE: src/Prototype/ObjectStore.php
================================================
<?php
declare(strict_types=1);
/**
* This file is part of Just.
*
* @license https://github.com/just-framework/php/blob/master/LICENSE MIT License
* @link https://justframework.com/php/
* @author Mahmoud Elnezamy <mahmoud@nezamy.com>
* @package Just
*/
namespace Just\Prototype;
class ObjectStore
{
use Setter;
use Getter;
use ConvertObject;
public function __construct(array $data = [])
{
$this->replace($data);
}
}
================================================
FILE: src/Prototype/Setter.php
================================================
<?php
declare(strict_types=1);
/**
* This file is part of Just.
*
* @license https://github.com/just-framework/php/blob/master/LICENSE MIT License
* @link https://justframework.com/php/
* @author Mahmoud Elnezamy <mahmoud@nezamy.com>
* @package Just
*/
namespace Just\Prototype;
trait Setter
{
private $data;
public function set(string $key, $value): void
{
$this->data[$this->setterTransformKey($key)] = $this->setterTransformValue($value);
}
public function push(string $key, $value): void
{
$key = $this->setterTransformKey($key);
if (! isset($this->data[$key])) {
$this->data[$key] = [];
} elseif (! is_array($this->data[$key])) {
$this->data[$key] = (array) $this->data[$key];
}
$this->data[$key][] = $this->setterTransformValue($value);
}
public function pop(string $key)
{
$key = $this->setterTransformKey($key);
if (! isset($this->data[$key])) {
throw new \LogicException(__CLASS__ . " | The value of [key] {$this->data[$key]} is not defined");
}
if (! is_array($this->data[$key])) {
throw new \LogicException(__CLASS__ . " | The value of [key] {$this->data[$key]} is not array");
}
return array_pop($this->data[$key]);
}
public function increment(string $key, int $by = 1): int
{
$key = $this->setterTransformKey($key);
if (isset($this->data[$key]) && (string) $this->data[$key] !== ((string) (int) $this->data[$key])) {
throw new \LogicException(__CLASS__ . " | The value of [key] {$this->data[$key]} is not integer");
}
return $this->data[$key] = (int) ($this->data[$key] ?? 0) + $by;
}
public function decrement(string $key, int $by = 1): int
{
$key = $this->setterTransformKey($key);
if (isset($this->data[$key]) && (string) $this->data[$key] !== ((string) (int) $this->data[$key])) {
throw new \LogicException(__CLASS__ . " | the value of [key] {$this->data[$key]} is not integer");
}
return $this->data[$key] = (int) ($this->data[$key] ?? 0) - $by;
}
public function add(array $data): void
{
foreach ($data as $k => $v) {
$this->set($k, $v);
}
}
public function replace(array $data): void
{
$this->clear();
foreach ($data as $k => $v) {
$this->set((string) $k, $v);
}
}
public function remove(string $key): void
{
unset($this->data[$this->setterTransformKey($key)]);
}
public function clear(): void
{
$this->data = [];
}
protected function setterTransformKey($key): string
{
return $key;
}
protected function setterTransformValue($value)
{
return $value;
}
}
================================================
FILE: src/Prototype/StringPrototype.php
================================================
<?php
declare(strict_types=1);
/**
* This file is part of Just.
*
* @license https://github.com/just-framework/php/blob/master/LICENSE MIT License
* @link https://justframework.com/php/
* @author Mahmoud Elnezamy <mahmoud@nezamy.com>
* @package Just
*/
namespace Just\Prototype;
class StringPrototype
{
protected string $data;
public function __construct(string $value)
{
$this->data = $value;
}
public function __toString(): string
{
return $this->data;
}
public function set($newValue)
{
$this->data = $newValue;
}
public function eq(string $string)
{
return $this->data === $string;
}
public function match(string $string, &$matches)
{
return preg_match($string, $this->data, $matches);
}
public function ends(string $string)
{
return substr($this->data, -strlen($string)) === $string;
}
public function startsWith(string $string)
{
return substr($this->data, 0, strlen($string)) === $string;
}
public function limit(int $limit, $trimMarker = '')
{
return mb_strimwidth($this->data, 0, $limit, $trimMarker);
}
/**
* @return bool|int
*/
public function contain(string $string)
{
return strpos($this->data, $string);
}
/**
* @return $this
*/
public function trim(string $character_mask = " \t\n\r\0\x0B")
{
$this->data = trim($this->data, $character_mask);
return $this;
}
public function append($value): void
{
$this->data .= $value;
}
public function prepend($value): void
{
$this->data = $value . $this->data;
}
}
================================================
FILE: src/Route.php
================================================
<?php
declare(strict_types=1);
/**
* This file is part of Just.
*
* @license https://github.com/just-framework/php/blob/master/LICENSE MIT License
* @link https://justframework.com/php/
* @author Mahmoud Elnezamy <mahmoud@nezamy.com>
* @package Just
*/
namespace Just;
use Just\Http\Auth\AuthInterface;
use Just\Routing\RouteHandlerInterface;
use Just\Routing\RouteParserInterface;
/**
* Class App.
*
* @method static \Just\Routing\Route any(string $uri, mixed $handler)
* @method static \Just\Routing\Route get(string $uri, mixed $handler)
* @method static \Just\Routing\Route head(string $uri, mixed $handler)
* @method static \Just\Routing\Route post(string $uri, mixed $handler)
* @method static \Just\Routing\Route put(string $uri, mixed $handler)
* @method static \Just\Routing\Route patch(string $uri, mixed $handler)
* @method static \Just\Routing\Route options(string $uri, mixed $handler)
* @method static \Just\Routing\Route delete(string $uri, mixed $handler)
* @method static \Just\Routing\Router middleware(...$middleware)
* @method static \Just\Routing\Route auth(AuthInterface $auth, callable $callback)
* @method static \Just\Routing\Route locale(array $locales, callable $callback)
* @method static void addPlaceholders(array $patterns)
* @method static void setNotfound($handler)
* @method static void group($prefix, callable $callback)
* @method static void use(...$middleware)
* @method static void setHandler(RouteHandlerInterface $handler)
* @method static void setParser(RouteParserInterface $parser)
*
*/
class Route
{
public static function __callStatic($method, $args)
{
$instance = container()->get(\Just\Routing\Router::class);
return $instance->{$method}(...$args);
}
}
================================================
FILE: src/Routing/Route.php
================================================
<?php
declare(strict_types=1);
/**
* This file is part of Just.
*
* @license https://github.com/just-framework/php/blob/master/LICENSE MIT License
* @link https://justframework.com/php/
* @author Mahmoud Elnezamy <mahmoud@nezamy.com>
* @package Just
*/
namespace Just\Routing;
use Just\DataType\Uri;
use Just\Http\Auth\AuthInterface;
use Just\Prototype\ArrayPrototype;
class Route
{
public ArrayPrototype $options;
private string $method;
private Uri $uri;
/**
* @var callable
*/
private $handler;
private array $args = [];
private array $middleware = [];
public function __construct(string $method, Uri $uri, $handler, array $options = [])
{
$this->method = $method;
$this->uri = $uri;
$this->handler = $handler;
$this->options = new ArrayPrototype($options);
}
public function getMethod(): string
{
return $this->method;
}
public function getUri(): Uri
{
return $this->uri;
}
// parse before get
public function getHandler(RouteHandlerInterface $handler): callable
{
return $handler->parse($this->handler);
}
public function addNamespaceToHandler()
{
if (is_string($this->handler)) {
$this->handler = $this->options->get('namespace') . '\\' . $this->handler;
} elseif (is_array($this->handler)) {
$this->handler[0] = $this->options->get('namespace') . '\\' . $this->handler[0];
}
}
public function getArgs(): array
{
return $this->args;
}
public function setArgs(array $args): void
{
$this->args = $args;
}
public function middleware(...$middleware)
{
$this->middleware = array_merge($this->middleware, $middleware);
return $this;
}
public function getMiddleware()
{
return $this->middleware;
}
public function hasMiddleware()
{
return count($this->middleware) > 0;
}
public function withoutMiddleware(...$middleware)
{
$this->middleware = array_filter($this->middleware, fn ($var) => ! in_array($var, $middleware));
return $this;
}
}
================================================
FILE: src/Routing/RouteHandler.php
================================================
<?php
declare(strict_types=1);
/**
* This file is part of Just.
*
* @license https://github.com/just-framework/php/blob/master/LICENSE MIT License
* @link https://justframework.com/php/
* @author Mahmoud Elnezamy <mahmoud@nezamy.com>
* @package Just
*/
namespace Just\Routing;
use Just\DI\Resolver;
use LogicException;
class RouteHandler implements RouteHandlerInterface
{
private Resolver $resolver;
public function __construct()
{
$this->resolver = new Resolver();
}
public function call(callable $handler, array $args = [])
{
return $this->resolver->resolve($handler);
}
public function parse($handler): callable
{
if (is_string($handler) && ! function_exists($handler)) {
$handler = str_replace('::', '@', $handler);
$handler = explode('@', $handler, 2);
}
if (is_callable($handler)) {
if (is_array($handler)) {
$handler = $this->resolver->prepare($handler);
}
return $handler;
}
if (! is_callable($handler)) {
$method = isset($handler[1]) ? '::' . $handler[1] : '';
throw new LogicException("[{$handler[0]}{$method}] is not callable");
}
return $handler;
}
}
================================================
FILE: src/Routing/RouteHandlerInterface.php
================================================
<?php
declare(strict_types=1);
/**
* This file is part of Just.
*
* @license https://github.com/just-framework/php/blob/master/LICENSE MIT License
* @link https://justframework.com/php/
* @author Mahmoud Elnezamy <mahmoud@nezamy.com>
* @package Just
*/
namespace Just\Routing;
interface RouteHandlerInterface
{
public function call(callable $handler, array $args = []);
public function parse($handler): callable;
}
================================================
FILE: src/Routing/RouteParser.php
================================================
<?php
declare(strict_types=1);
/**
* This file is part of Just.
*
* @license https://github.com/just-framework/php/blob/master/LICENSE MIT License
* @link https://justframework.com/php/
* @author Mahmoud Elnezamy <mahmoud@nezamy.com>
* @package Just
*/
namespace Just\Routing;
use Just\Support\Regex;
class RouteParser implements RouteParserInterface
{
public function parse(string $uri): array
{
$matchedParameter = [];
$matchedPattern = [];
$result = preg_replace_callback('/\/\{([a-z-0-9@]+)\}\??((:\(?[^\/]+\)?)?)/i', function ($match) use (&$matchedParameter, &$matchedPattern) {
[$full, $parameter, $namedPattern] = $match;
$pattern = '/' . Regex::get('?');
if (! empty($namedPattern)) {
$replace = substr($namedPattern, 1);
if (Regex::has($replace)) {
$pattern = '/' . Regex::get($replace);
} elseif (substr($replace, 0, 1) == '(' && substr($replace, -1, 1) == ')') {
$pattern = '/' . $replace;
}
} elseif (Regex::has($parameter)) {
$pattern = '/' . Regex::get($parameter);
}
// Check whether parameter is optional.
if (strpos($full, '?') !== false) {
$pattern = str_replace(['/(', '|'], ['(/', '|/'], $pattern) . '?';
}
$matchedParameter[] = $parameter;
$matchedPattern[] = $pattern;
return $pattern;
}, trim($uri));
return ['parameters' => $matchedParameter, 'patterns' => $matchedPattern, 'result' => $result];
}
}
================================================
FILE: src/Routing/RouteParserInterface.php
================================================
<?php
declare(strict_types=1);
/**
* This file is part of Just.
*
* @license https://github.com/just-framework/php/blob/master/LICENSE MIT License
* @link https://justframework.com/php/
* @author Mahmoud Elnezamy <mahmoud@nezamy.com>
* @package Just
*/
namespace Just\Routing;
interface RouteParserInterface
{
/**
* @param string $uri i.e '/{username}:([0-9a-z_.-]+)/post/{id}:([0-9]+)'
* @return array [
* 'parameters' => ['username', 'id'],
* 'patterns' => ['/([0-9a-z_.-]+)', '/([0-9]+)'],
* 'result' => '/([0-9a-z_.-]+)/post/([0-9]+)'
* ]
*/
public function parse(string $uri): array;
}
================================================
FILE: src/Routing/Router.php
================================================
<?php
declare(strict_types=1);
/**
* This file is part of Just.
*
* @license https://github.com/just-framework/php/blob/master/LICENSE MIT License
* @link https://justframework.com/php/
* @author Mahmoud Elnezamy <mahmoud@nezamy.com>
* @package Just
*/
namespace Just\Routing;
use Just\DataType\Uri;
use Just\Http\Auth;
use Just\Http\Auth\AuthInterface;
use Just\Http\Middleware;
use Just\Http\Request;
use Just\Http\Response;
use Just\Prototype\ArrayPrototype;
use Just\Support\Regex;
/**
* Class Route.
* @method Route any(string $uri, mixed $handler, array $options = [])
* @method Route get(string $uri, mixed $handler, array $options = [])
* @method Route head(string $uri, mixed $handler, array $options = [])
* @method Route post(string $uri, mixed $handler, array $options = [])
* @method Route put(string $uri, mixed $handler, array $options = [])
* @method Route patch(string $uri, mixed $handler, array $options = [])
* @method Route options(string $uri, mixed $handler, array $options = [])
* @method Route delete(string $uri, mixed $handler, array $options = [])
*/
class Router
{
protected array $allowedMethods = ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE', 'ANY'];
protected array $allowedContentType = ['html' => 'text/html', 'json' => 'application/json', 'jsonp' => 'application/javascript'];
protected Route $matched;
private Request $request;
private Response $response;
private RouteHandlerInterface $handler;
private RouteParserInterface $parser;
private array $middleware_list = [];
private array $globalMiddleware = [];
private array $routes = [];
private string $currentGroupPrefix = '';
private array $currentGroupOptions = [];
private array $nextGroupOptions = [];
private array $groupOptions = [];
private ?string $localeRedirectHandle = null;
private array $auth_list = [];
private ?string $currentAuthId = null;
private \Closure $notfound;
public function __construct(Request $request, Response $response)
{
$this->request = $request;
$this->response = $response;
$this->parser = new RouteParser();
$this->handler = new RouteHandler();
$this->notfound = function () {
pre('Page is not found', '404 Not Found', 6);
};
container()->set(Request::class, $this->request);
container()->set(Response::class, $this->response);
}
public function __call($method, $args)
{
return call_user_func_array([$this, 'add'], array_merge([$method], $args));
}
public function setHandler(RouteHandlerInterface $handler)
{
$this->handler = $handler;
}
public function setParser(RouteParserInterface $parser)
{
$this->parser = $parser;
}
public function setNotfound($handler)
{
$this->notfound = $handler;
}
public function run(): Response
{
$this->match($this->request->method(), $this->request->uri());
return $this->response;
}
public function add($method, $uri, $handler, array $options = []): Route
{
$uri = new Uri($this->currentGroupPrefix . $uri);
$chunk = $uri->getChunk();
if (! isset($this->routes[$chunk])) {
$this->routes[$chunk] = [];
}
if (! in_array($method = strtoupper($method), $this->allowedMethods)) {
throw new \LogicException("[{$method}] Method is not allowed");
}
if($method == 'ANY'){
$method = '';
}
$opt = [];
if ($this->currentAuthId) {
$opt['auth'] = $this->currentAuthId;
}
if ($options) {
$opt = array_merge($opt, $options);
}
if ($this->currentGroupOptions) {
$opt['group'] = $this->currentGroupOptions;
}
$this->routes[$chunk][] = $route = new Route($method, $uri, $handler, $opt);
return $route;
}
public function locale(array $locales, callable $callback): void
{
if (session_status() == PHP_SESSION_NONE) {
@session_start();
}
if (! isset($_SESSION['locale'])) {
$_SESSION['locale'] = $locales[0];
}
$this->group('/{@locale}?:(' . implode('|', $locales) . ')', $callback);
}
public function auth(AuthInterface $auth, callable $callback)
{
$id = uniqid((string) rand());
$this->auth_list[$id] = $auth;
$this->currentAuthId = $id;
$callback($this);
$this->currentAuthId = '';
}
public function group($prefix, callable $callback): void
{
$previousGroupOptions = $this->currentGroupOptions;
if ($this->nextGroupOptions) {
$id = uniqid((string) rand());
$this->groupOptions[$id] = $this->nextGroupOptions;
$this->currentGroupOptions[] = $id;
$this->nextGroupOptions = [];
}
$previousGroupPrefix = $this->currentGroupPrefix;
$this->currentGroupPrefix = $previousGroupPrefix . $prefix;
$callback($this);
$this->currentGroupPrefix = $previousGroupPrefix;
$this->currentGroupOptions = $previousGroupOptions;
}
public function middleware(...$middleware): Router
{
$id = uniqid((string) rand());
$this->middleware_list[$id] = $middleware;
if (! isset($this->nextGroupOptions['middleware'])) {
$this->nextGroupOptions['middleware'] = [];
}
$this->nextGroupOptions['middleware'][] = $id;
return $this;
}
public function use(...$middleware): Router
{
$this->globalMiddleware = array_merge($this->globalMiddleware, $middleware);
return $this;
}
public function addPlaceholders(array $patterns): void
{
foreach ($patterns as $name => $pattern) {
Regex::set($name, $pattern);
}
}
public function match(string $method, string $uri): bool
{
$uri = new Uri($uri);
$uri->rtrim('/');
$chunk = $uri->getChunk();
$matched = null;
if (isset($this->routes[$chunk])) {
$matched = $this->find($method, $uri, $this->routes[$chunk]);
}
if (! $matched && isset($this->routes['/*'])) {
$matched = $this->find($method, $uri, $this->routes['/*']);
}
if ($matched) {
$this->handleOptions($matched);
if ($this->response->isEnded()) {
return false;
}
$args = $matched->getArgs();
if (count($args)) {
container()->importVars($args);
$this->request->setArguments($args);
}
$middleware = new Middleware($this->handler);
$middleware->add($matched->getMiddleware());
$output = $middleware->handle(
$matched->getHandler($this->handler)
);
$this->response->write($output ?? '');
return true;
}
$this->handler->call($this->notfound);
return false;
}
public function handleMiddleware(Route $matched)
{
if ($this->globalMiddleware) {
foreach ($this->globalMiddleware as $item) {
$matched->middleware($item);
}
}
if ($matched->options->has('middleware')) {
$middleware = (array) $matched->options->get('middleware');
foreach (array_reverse($middleware) as $item) {
call_user_func_array([$matched, 'middleware'], $this->middleware_list[$item]);
}
}
}
public function getMatched()
{
return $this->matched;
}
public function export(): array
{
return $this->routes;
}
private function handleOptions(Route $matched)
{
if ($matched->options->has('auth')) {
$this->handleAuth($matched->options->get('auth'));
}
if ($matched->options->has('group')) {
$this->handleGroupOptions($matched->options);
}
$this->handleMiddleware($matched);
if ($matched->options->has('namespace')) {
$matched->addNamespaceToHandler();
}
//Handle Json
if ($matched->options->has('content_type') && isset($this->allowedContentType[$matched->options->get('content_type')])) {
$this->response->headers->set('Content-Type', $this->allowedContentType[$matched->options->get('content_type')]);
}
}
private function handleAuth(string $id)
{
if (! isset($this->auth_list[$id])) {
throw new \LogicException('Auth is not Registered');
}
$auth = new Auth($this->auth_list[$id]);
$this->request->setUser($auth->user());
}
private function handleGroupOptions(ArrayPrototype $options)
{
$final = [];
foreach ($options->get('group') as $id) {
foreach ($this->groupOptions[$id] as $key => $value) {
if ($key == 'namespace') {
$final['namespace'] = (isset($final['namespace']) ? $final['namespace'] . '\\' : '') . $value;
continue;
}
if ($key == 'middleware') {
$final['middleware'] = array_merge($final['middleware'] ?? [], $value);
continue;
}
$final[$key] = $value;
}
}
$options->remove('group');
$options->add($final);
}
private function find(string $requestMethod, Uri $requestUri, $routes): Route
{
$result_args = [];
$matched = false;
foreach ($routes as $route) {
$uri = $route->getUri();
$method = $route->getMethod();
$uri->rtrim('/');
if ($method && $method !== $requestMethod) {
continue;
}
if ($uri->isStatic() && $uri->eq((string) $requestUri)) {
$matched = true;
} else {
$pattern = $this->parser->parse((string) $uri);
if (preg_match('~^' . $pattern['result'] . '$~i', (string) $requestUri, $args)) {
array_shift($args);
if ($args) {
$args = array_map(function ($s) {
return ltrim($s, '/');
}, $args);
}
$result_args = $this->bindArgs($pattern['parameters'], $args);
$matched = true;
}
}
if ($matched) {
$route->setArgs($result_args);
$this->matched = $route;
return $route;
}
}
return new Route('', new Uri('/404'), $this->notfound);
}
private function bindArgs(array $pram, array $args): array
{
$newArgs = [];
if (count($pram) == count($args)) {
$pram = array_map(function ($s) {
return ltrim($s, '@');
}, $pram);
$newArgs = array_combine($pram, $args);
} else {
foreach ($pram as $p) {
$value = array_shift($args);
if ($p == '@locale') {
$p = 'locale';
if (! $value) {
$this->localeRedirect();
break;
}
}
$newArgs[$p] = $value;
}
}
if (isset($newArgs['locale'])) {
$_SESSION['locale'] = $newArgs['locale'];
}
return $newArgs;
}
private function localeRedirect()
{
$value = $_SESSION['locale'];
$this->response->redirectTo('/' . $value . $this->request->uri());
if ($this->localeRedirectHandle && is_callable($this->localeRedirectHandle)) {
$redirect = call_user_func_array($this->localeRedirectHandle, [$this->response->getRedirect(), $this->request->uri()]);
if ($redirect) {
$this->response->redirectTo('/' . $value . $this->request->uri());
}
}
$this->response->end();
}
}
================================================
FILE: src/Support/ArrayTrait.php
================================================
<?php
declare(strict_types=1);
/**
* This file is part of Just.
*
* @license https://github.com/just-framework/php/blob/master/LICENSE MIT License
* @link https://justframework.com/php/
* @author Mahmoud Elnezamy <mahmoud@nezamy.com>
* @package Just
*/
namespace Just\Support;
/**
* ArrTrait.
*
* @since 1.0.0
*/
trait ArrayTrait
{
/**
* Get value from nested array.
*
* @param string $k
* @param string $default
*
* @return mixed
*/
public static function get(array $arr, $k, $default = null)
{
if (isset($arr[$k])) {
return $arr[$k];
}
$nested = explode('.', $k);
foreach ($nested as $part) {
if (isset($arr[$part])) {
$arr = $arr[$part];
continue;
}
$arr = $default;
break;
}
return $arr;
}
/**
* set value to nested array.
*
* @param string $k
* @param mixed $v
*
* @return array
*/
public static function set(array $arr, $k, $v)
{
$nested = ! is_array($k) ? explode('.', $k) : $k;
$count = count($nested);
if ($count == 1) {
return $arr[$k] = $v;
}
if ($count > 1) {
$prev = '';
$loop = 1;
$unshift = $nested;
foreach ($nested as $part) {
if (isset($arr[$part]) && $count > $loop) {
$prev = $part;
array_shift($unshift);
++$loop;
continue;
}
if ($loop > 1 && $loop < $count) {
if (! isset($arr[$prev][$part])) {
$arr[$prev][$part] = [];
}
$arr[$prev] = static::set($arr[$prev], $unshift, $v);
++$loop;
break;
}
if ($loop >= 1 && $loop == $count) {
if (! is_array($arr[$prev])) {
$arr[$prev] = [];
}
if ($part == '') {
$arr[$prev][] = $v;
} else {
$arr[$prev][$part] = $v;
}
} else {
$arr[$part] = [];
$prev = $part;
array_shift($unshift);
++$loop;
}
}
}
return $arr;
}
/**
* Get value if key exists or default value.
*
* @param string $k
* @param string $default
*
* @return mixed
*/
public static function value(array $arr, $k, $default = null)
{
return isset($arr[$k]) ? $arr[$k] : $default;
}
/**
* Get value from string json.
*
* @param string $jsonStr
* @param string $k
* @param string $default
*
* @return mixed
*/
public static function json($jsonStr, $k = null, $default = null)
{
$json = json_decode($jsonStr, true);
if ($k && $json) {
return self::get($json, $k, $default);
}
return $json;
}
// public static function replace_values($arr, $from = null, $to = "")
// {
// $results = [];
// foreach ($arr as $k => $v) {
// $toArr = (array) $v;
// if(count($toArr) > 1 || is_array($v)){
// $results[$k] = nullToString($toArr);
// } else {
// $results[$k] = $v;
// if ($v == $from) {
// $results[$k] = $to;
// }
// }
// }
// return $results;
// }
}
================================================
FILE: src/Support/Regex.php
================================================
<?php
declare(strict_types=1);
/**
* This file is part of Just.
*
* @license https://github.com/just-framework/php/blob/master/LICENSE MIT License
* @link https://justframework.com/php/
* @author Mahmoud Elnezamy <mahmoud@nezamy.com>
* @package Just
*/
namespace Just\Support;
/**
* Class Regex.
* @method static string get(string $name)
* @method static boolean has(string $name)
* @method static void set(string $name, string $pattern)
* @method static void update(string $name, string $pattern)
* @method static array list()
*/
class Regex
{
private static ?Regex $instance = null;
private array $patterns = [
'*' => '(.*)',
'?' => '([^\/]+)',
'int' => '([0-9]+)',
'multiInt' => '([0-9,]+)',
'title' => '([a-z_-]+)',
'key' => '([a-z0-9_]+)',
'multiKey' => '([a-z0-9_,]+)',
'isoCode2' => '([a-z]{2})',
'isoCode3' => '([a-z]{3})',
];
public function __call(string $name, array $arguments)
{
return call_user_func_array([$this, '_' . $name], $arguments);
}
public static function __callStatic(string $name, array $arguments)
{
return call_user_func_array([self::instance(), '_' . $name], $arguments);
}
public static function instance(): self
{
if (static::$instance === null) {
static::$instance = new static();
}
return static::$instance;
}
public function _set(string $name, string $pattern): void
{
if ($this->has($name)) {
throw new \LogicException("{$name} already registered in route patterns");
}
$this->patterns[$name] = $pattern;
}
public function _get(string $name): string
{
return $this->patterns[$name];
}
public function _has(string $name): bool
{
return array_key_exists($name, $this->patterns);
}
public function _update(string $name, string $pattern): void
{
$this->patterns[$name] = $pattern;
}
public function _list(): array
{
return $this->patterns;
}
}
================================================
FILE: src/functions.php
================================================
<?php declare(strict_types=1);
! defined('DS') && define('DS', DIRECTORY_SEPARATOR);
//=============[ pre() is print array ]==============
function pre($arr, $title = null, $theme = 1)
{
ob_start();
switch ($theme) {
case 2: $color = '#e4e7e7'; $background = '#2295bc'; break;
case 3: $color = '#064439'; $background = '#51bba8'; break;
case 4: $color = '#efc75e'; $background = '#324d5b'; break;
case 5: $color = '#000000'; $background = '#b1eea1'; break;
case 6: $color = '#fff'; $background = '#e2574c'; break;
default: $color = '#2295bc'; $background = '#e4e7e7';
}
if ($title) { ?>
<div style="border:1px solid rgba(0,0,0,0.1);border-bottom:0;color:#2e3436;position: relative;padding: 6px 40px;font-weight:500;font-family: Monaco,Consolas, 'Lucida Console',monospace;letter-spacing:1px;font-size:14px;display: inline-block;width: auto;left: 40px;bottom: -30px;"><?php echo $title; ?></div>
<?php } ?>
<pre style="direction: ltr;background:<?php echo $background; ?>;color:<?php echo $color; ?>;
width: calc(100% - 122px);margin: 30px auto;overflow:auto;
font-family: Monaco,Consolas, 'Lucida Console',monospace;font-size: 14px;padding: 20px;border: 1px solid rgba(0,0,0,0.1)"><?php print_r($arr); ?></pre>
<?php
response()->write(ob_get_clean());
}
function dpre()
{
call_user_func_array('pre', func_get_args());
response()->end(response()->body());
}
function container()
{
return \Just\DI\Container::instance();
}
function response(): Just\Http\Response
{
return container()->get(\Just\Http\Response::class);
}
function request(): Just\Http\Request
{
return container()->get(\Just\Http\Request::class);
}
function auth(): Just\Http\Auth
{
return container()->get(\Just\Http\Auth::class);
}
================================================
FILE: tests/AuthTest.php
================================================
<?php
declare(strict_types=1);
namespace Just\Test;
use Just\DI\Container;
use Just\Http\Auth;
use Just\Http\GlobalRequest;
use Just\Http\Request;
use Just\Http\Response;
use PHPUnit\Framework\TestCase;
class AuthTest extends TestCase
{
public function testBasicAuth() : void
{
Container::instance()->set(Request::class, new GlobalRequest);
Container::instance()->set(Response::class, new Response);
$credentials = ['users'=> ['Mahmoud'=> '123258']];
$auth = new Auth(new Auth\Basic($credentials));
$this->assertTrue($auth->validate(['username'=> 'Mahmoud', 'password' => '123258']));
// $auth->
$this->assertTrue(response()->headers->has('WWW-Authenticate'));
}
/**
* Undocumented function
*
* @return void
*/
public function testDigest(): void
{
Container::instance()->set(Request::class, new GlobalRequest);
Container::instance()->set(Response::class, new Response);
new Auth(new Auth\Digest(['users'=> ['Mahmoud'=> '123258']]));
$this->assertTrue(response()->headers->has('WWW-Authenticate'));
}
}
================================================
FILE: tests/DummyRequest.php
================================================
<?php declare(strict_types=1);
use Just\Http\GlobalRequest;
class DummyRequest extends GlobalRequest
{
protected $_uri;
protected $_method;
public function setUri(string $uri)
{
$this->_uri = $uri;
}
public function setMethod(string $method)
{
$this->_method = $method;
}
}
================================================
FILE: tests/RouteParserTest.php
================================================
<?php
declare(strict_types=1);
namespace Just\Test;
use Just\Routing\RouteParser;
use PHPUnit\Framework\TestCase;
class RouteParserTest extends TestCase
{
public function testParameters() : void
{
$parser = new RouteParser();
$results = $parser->parse('/{username}/post/{id}');
$this->assertSame([
'parameters' => [
'username', 'id'
], 'patterns' => [
'/([^\/]+)',
'/([^\/]+)'
], 'result' => '/([^\/]+)/post/([^\/]+)'
], $results);
}
public function testParametersWithRegex() : void
{
$parser = new RouteParser();
$results = $parser->parse('/{username}:([0-9a-z_.-]+)/post/{id}:([0-9]+)');
$this->assertSame([
'parameters' => [
'username', 'id'
], 'patterns' => [
'/([0-9a-z_.-]+)',
'/([0-9]+)'
], 'result' => '/([0-9a-z_.-]+)/post/([0-9]+)'
], $results);
}
public function testParametersWithPlaceholder() : void
{
$parser = new RouteParser();
$results = $parser->parse('/{username}:title/post/{id}:int');
$this->assertSame([
'parameters' => [
'username', 'id'
], 'patterns' => [
'/([a-z_-]+)',
'/([0-9]+)'
], 'result' => '/([a-z_-]+)/post/([0-9]+)'
], $results);
}
public function testParametersWithPlaceholderAll() : void
{
$parser = new RouteParser();
$results = $parser->parse('/{all}:*');
$this->assertSame([
'parameters' => [
'all'
], 'patterns' => [
'/(.*)',
], 'result' => '/(.*)'
], $results);
}
public function testParametersHasSamePlaceholderName() : void
{
$parser = new RouteParser();
$results = $parser->parse('/post/{title}');
$this->assertSame([
'parameters' => [
'title'
], 'patterns' => [
'/([a-z_-]+)',
], 'result' => '/post/([a-z_-]+)'
], $results);
}
public function testOptionalParametersWithPlaceholder() : void
{
$parser = new RouteParser();
$results = $parser->parse('/{username}:title/posts/{id}?:int');
$this->assertSame([
'parameters' => [
'username', 'id'
], 'patterns' => [
'/([a-z_-]+)',
'(/[0-9]+)?'
], 'result' => '/([a-z_-]+)/posts(/[0-9]+)?'
], $results);
}
public function testLocaleParameter() : void
{
$parser = new RouteParser();
$results = $parser->parse('/{@locale}:(ar|en)/posts/{id}?:int');
$this->assertSame([
'parameters' => [
'@locale', 'id'
], 'patterns' => [
'/(ar|en)',
'(/[0-9]+)?'
], 'result' => '/(ar|en)/posts(/[0-9]+)?'
], $results);
}
public function testLocaleOptionalParameter() : void
{
$parser = new RouteParser();
$results = $parser->parse('/{@locale}?:(ar|en)/posts/{id}?:int');
$this->assertSame([
'parameters' => [
'@locale', 'id'
], 'patterns' => [
'(/ar|/en)?',
'(/[0-9]+)?'
], 'result' => '(/ar|/en)?/posts(/[0-9]+)?'
], $results);
}
}
================================================
FILE: tests/RouteTest.php
================================================
<?php
declare(strict_types=1);
namespace Just\Test;
use DummyRequest;
use Just\DataType\Uri;
use Just\Http\GlobalRequest;
use Just\Http\Response;
use Just\Routing\Route;
use Just\Support\Regex;
use PHPUnit\Framework\TestCase;
use Just\Routing\Router;
class controller{
public static function method(){
pre('Methods', 'Controller');
}
}
class RouteTest extends TestCase
{
protected function app(): Router{
return new Router(new GlobalRequest(), new Response());
}
public function testShortcuts() : void
{
$r = $this->app();
$r->add('GET', '/get/users', 'Just\Test\controller::method');
$r->delete('/delete', 'Just\Test\controller::method');
$r->get('/get', 'Just\Test\controller::method');
$r->head('/head', 'Just\Test\controller::method');
$r->patch('/patch', 'Just\Test\controller::method');
$r->post('/post', 'Just\Test\controller::method');
$r->put('/put', 'Just\Test\controller::method');
$r->options('/options', 'Just\Test\controller::method');
$expected = [
'/get' => [
new Route('GET', new Uri('/get/users'), 'Just\Test\controller::method'),
new Route('GET', new Uri('/get'), 'Just\Test\controller::method'),
],
'/delete' => [
new Route('DELETE', new Uri('/delete'), 'Just\Test\controller::method')
],
'/head' => [
new Route('HEAD', new Uri('/head'), 'Just\Test\controller::method')
],
'/patch' => [
new Route('PATCH', new Uri('/patch'), 'Just\Test\controller::method')
],
'/post' => [
new Route('POST', new Uri('/post'), 'Just\Test\controller::method')
],
'/put' => [
new Route('PUT', new Uri('/put'), 'Just\Test\controller::method')
],
'/options' => [
new Route('OPTIONS', new Uri('/options'), 'Just\Test\controller::method')
]
];
self::assertEquals($expected, $r->export());
}
public function testGroups() : void
{
$r = $this->app();
$r->get('/get', 'Just\Test\controller::method');
$r->group('/api/v1', function (Router $r) {
$r->get('/posts', 'Just\Test\controller::method');
$r->post('/posts/create', 'Just\Test\controller::method');
$r->group('/nested-group', function (Router $r) {
$r->get('/posts', 'Just\Test\controller::method');
$r->post('/posts/create', 'Just\Test\controller::method');
});
});
$r->group('/admin', static function (Router $r): void {
$r->get('-some-info', 'Just\Test\controller::method');
});
$r->group('/admin-', static function (Router $r): void {
$r->get('more-info', 'Just\Test\controller::method');
});
$expected = [
'/get' => [
new Route('GET', new Uri('/get'), 'Just\Test\controller::method'),
],
'/api' => [
new Route('GET', new Uri('/api/v1/posts'), 'Just\Test\controller::method'),
new Route('POST', new Uri('/api/v1/posts/create'), 'Just\Test\controller::method'),
new Route('GET', new Uri('/api/v1/nested-group/posts'), 'Just\Test\controller::method'),
new Route('POST', new Uri('/api/v1/nested-group/posts/create'), 'Just\Test\controller::method'),
],
'/admin-some-info' => [
new Route('GET', new Uri('/admin-some-info'), 'Just\Test\controller::method'),
],
'/admin-more-info' => [
new Route('GET', new Uri('/admin-more-info'), 'Just\Test\controller::method'),
]
];
self::assertEquals($expected, $r->export());
}
public function testRouteDynamic() : void
{
$r = $this->app();
$r->get('/{user}/{id}?', 'Just\Test\controller::method');
$r->get('/user/{user}/{id}?', 'Just\Test\controller::method');
self::assertEquals([
'/*' => [
new Route('GET', new Uri('/{user}/{id}?'), 'Just\Test\controller::method')
],
'/user' => [
new Route('GET', new Uri('/user/{user}/{id}?'), 'Just\Test\controller::method')
],
], $r->export());
try {
$r->match('GET', '/username');
} catch (\LogicException $e) {
}
$expect = new Route('GET', new Uri('/{user}/{id}?'), 'Just\Test\controller::method');
$expect->setArgs([
'user' => 'username',
'id' => null
]);
self::assertEquals($expect, $r->getMatched());
try {
$r->match('GET', '/username/10/');
} catch (\LogicException $e) {
}
$expect = new Route('GET', new Uri('/{user}/{id}?'), 'Just\Test\controller::method');
$expect->setArgs([
'user' => 'username',
'id' => 10
]);
self::assertEquals($expect, $r->getMatched());
}
public function testGroupDynamic() : void
{
$r = $this->app();
$r->group('/{lang}:isoCode2', function (Router $r) {
$r->get('/{page}', 'Just\Test\controller::method');
$r->get('/post/{post}', 'Just\Test\controller::method');
});
self::assertEquals([
'/*' => [
new Route('GET', new Uri('/{lang}:isoCode2/{page}'), 'Just\Test\controller::method'),
new Route('GET', new Uri('/{lang}:isoCode2/post/{post}'), 'Just\Test\controller::method')
]
], $r->export());
try {
$r->match('GET', '/ar/page');
} catch (\LogicException $e) {
}
$expect = new Route('GET', new Uri('/{lang}:isoCode2/{page}'), 'Just\Test\controller::method');
$expect->setArgs([
'lang' => 'ar',
'page' => 'page'
]);
self::assertEquals($expect, $r->getMatched());
}
public function testGroupDynamicOptionalParameter() : void
{
$r = $this->app();
$r->group('/{lang}?:isoCode2', function (Router $r) {
$r->get('/{page}', 'Just\Test\controller::method');
$r->get('/post/{post}', 'Just\Test\controller::method');
});
self::assertEquals([
'/*' => [
new Route('GET', new Uri('/{lang}?:isoCode2/{page}'), 'Just\Test\controller::method'),
new Route('GET', new Uri('/{lang}?:isoCode2/post/{post}'), 'Just\Test\controller::method')
]
], $r->export());
try {
$r->match('GET', '/page');
} catch (\LogicException $e) {
}
$expect = new Route('GET', new Uri('/{lang}?:isoCode2/{page}'), 'Just\Test\controller::method');
$expect->setArgs([
'lang' => '',
'page' => 'page'
]);
self::assertEquals($expect, $r->getMatched());
try {
$r->match('GET', '/ar/page');
} catch (\LogicException $e) {
}
$expect = new Route('GET', new Uri('/{lang}?:isoCode2/{page}'), 'Just\Test\controller::method');
$expect->setArgs([
'lang' => 'ar',
'page' => 'page'
]);
self::assertEquals($expect, $r->getMatched());
}
public function testAddPlaceholder() : void
{
$r = $this->app();
$r->addPlaceholders([
'test' => '(test_[a-z_-]+)'
]);
$this->assertSame(Regex::get('test'), '(test_[a-z_-]+)');
}
public function testLocale(): void
{
$r = $this->app();
$r->locale(['ar', 'en'], function (Router $r) {
$r->get('/', 'Just\Test\controller::method');
$r->get('/page', 'Just\Test\controller::method');
$r->get('/page/{page}', 'Just\Test\controller::method');
});
self::assertEquals([
'/*' => [
new Route('GET', new Uri('/{@locale}?:(ar|en)/'), 'Just\Test\controller::method'),
new Route('GET', new Uri('/{@locale}?:(ar|en)/page'), 'Just\Test\controller::method'),
new Route('GET', new Uri('/{@locale}?:(ar|en)/page/{page}'), 'Just\Test\controller::method'),
]
], $r->export());
try {
$r->match('GET', '/en/page');
} catch (\LogicException $e) {
}
$expect = new Route('GET', new Uri('/{@locale}?:(ar|en)/page'), 'Just\Test\controller::method');
$expect->setArgs([
'locale' => 'en',
]);
self::assertEquals($expect, $r->getMatched());
}
public function testLocaleRedirect(): void
{
$req = new DummyRequest();
$req->setUri('/page');
$r = new Router($req, new Response());
$r->locale(['ar', 'en'], function (Router $r) {
$r->get('/', function () {
});
$r->get('/page', function () {
});
});
$match = $r->run();
$this->assertTrue($match->hasRedirect());
$this->assertTrue($match->isEnded());
$this->assertEquals(['/ar/page', 302], $match->getRedirect());
}
}
================================================
FILE: tests/bootstrap.php
================================================
<?php
error_reporting(E_ALL);
// ini_set('error_reporting', E_ALL);
$loader = require __DIR__ . '/../vendor/autoload.php';
$loader = require __DIR__ . '/DummyRequest.php';
gitextract_dakvb4o0/
├── .gitignore
├── .php_cs
├── LICENSE
├── README.md
├── composer.json
├── phpunit.xml
├── src/
│ ├── DataType/
│ │ └── Uri.php
│ ├── Http/
│ │ ├── Auth/
│ │ │ ├── AuthInterface.php
│ │ │ ├── Basic.php
│ │ │ └── Digest.php
│ │ ├── Auth.php
│ │ ├── GlobalRequest.php
│ │ ├── Middleware.php
│ │ ├── Request.php
│ │ ├── Response.php
│ │ └── Session.php
│ ├── Prototype/
│ │ ├── ArrayPrototype.php
│ │ ├── ConvertObject.php
│ │ ├── Getter.php
│ │ ├── GetterObject.php
│ │ ├── ObjectStore.php
│ │ ├── Setter.php
│ │ └── StringPrototype.php
│ ├── Route.php
│ ├── Routing/
│ │ ├── Route.php
│ │ ├── RouteHandler.php
│ │ ├── RouteHandlerInterface.php
│ │ ├── RouteParser.php
│ │ ├── RouteParserInterface.php
│ │ └── Router.php
│ ├── Support/
│ │ ├── ArrayTrait.php
│ │ └── Regex.php
│ └── functions.php
└── tests/
├── AuthTest.php
├── DummyRequest.php
├── RouteParserTest.php
├── RouteTest.php
└── bootstrap.php
SYMBOL INDEX (208 symbols across 31 files)
FILE: src/DataType/Uri.php
class Uri (line 16) | class Uri extends StringPrototype
method getChunk (line 18) | public function getChunk(?string $uri = null)
method isStatic (line 25) | public function isStatic(?string $uri = null)
method rtrim (line 31) | public function rtrim($char)
method ltrim (line 36) | public function ltrim($char)
FILE: src/Http/Auth.php
class Auth (line 25) | class Auth
method __construct (line 29) | public function __construct(AuthInterface $auth)
method __call (line 37) | public function __call($method, $args)
FILE: src/Http/Auth/AuthInterface.php
type AuthInterface (line 14) | interface AuthInterface
method check (line 16) | public function check(): bool;
method validate (line 18) | public function validate(array $credentials): bool;
method redirectToLogin (line 20) | public function redirectToLogin();
method login (line 22) | public function login(): bool;
method logout (line 24) | public function logout(): void;
method user (line 26) | public function user();
FILE: src/Http/Auth/Basic.php
class Basic (line 14) | class Basic implements AuthInterface
method __construct (line 22) | public function __construct($options)
method check (line 30) | public function check(): bool
method redirectToLogin (line 35) | public function redirectToLogin()
method login (line 44) | public function login(array $credentials = []): bool
method logout (line 61) | public function logout(): void
method validate (line 67) | public function validate(array $credentials): bool
method user (line 83) | public function user(): array
FILE: src/Http/Auth/Digest.php
class Digest (line 14) | class Digest implements AuthInterface
method __construct (line 22) | public function __construct($options)
method check (line 34) | public function check(): bool
method redirectToLogin (line 39) | public function redirectToLogin()
method login (line 48) | public function login(array $credentials = []): bool
method logout (line 60) | public function logout(): void
method validate (line 66) | public function validate(array $credentials): bool
method user (line 83) | public function user(): array
method digest_parse (line 88) | private function digest_parse($txt)
FILE: src/Http/GlobalRequest.php
class GlobalRequest (line 14) | class GlobalRequest extends Request
method __construct (line 16) | public function __construct()
FILE: src/Http/Middleware.php
class Middleware (line 16) | class Middleware
method __construct (line 21) | public function __construct(RouteHandlerInterface $handler)
method add (line 26) | public function add(array $layers): void
method handle (line 31) | public function handle(callable $core)
method createLayer (line 43) | private function createLayer($nextLayer, $layer): callable
FILE: src/Http/Request.php
class Request (line 26) | class Request
method __construct (line 58) | public function __construct(array $headers, array $server, array $cook...
method __call (line 86) | public function __call($method, $args)
method url (line 94) | public function url($uri = ''): string
method serverName (line 99) | public function serverName(): string
method setArguments (line 104) | public function setArguments(array $args): void
method setUser (line 109) | public function setUser(array $user): void
method isJson (line 114) | public function isJson(): bool
method ip (line 122) | public function ip(): string
method browser (line 147) | public function browser(): string
method platform (line 171) | public function platform(): string
method isMobile (line 186) | public function isMobile(): bool
FILE: src/Http/Response.php
class Response (line 16) | class Response
method __construct (line 28) | public function __construct()
method body (line 33) | public function body($clean = false): string
method write (line 42) | public function write($body): void
method end (line 49) | public function end(string $body = ''): void
method isEnded (line 57) | public function isEnded(): bool
method setStatusCode (line 62) | public function setStatusCode(int $statusCode): void
method statusCode (line 67) | public function statusCode(): int
method redirectTo (line 72) | public function redirectTo(string $location, int $status_code = 302)
method hasRedirect (line 77) | public function hasRedirect(): bool
method getRedirect (line 82) | public function getRedirect()
FILE: src/Http/Session.php
class Session (line 14) | class Session
method __construct (line 18) | public function __construct(array $options)
FILE: src/Prototype/ArrayPrototype.php
class ArrayPrototype (line 14) | class ArrayPrototype
method __construct (line 20) | public function __construct(array $data = [])
FILE: src/Prototype/ConvertObject.php
type ConvertObject (line 16) | trait ConvertObject
method __toString (line 18) | public function __toString(): string
method toArray (line 23) | public function toArray($data = null): array
method toJson (line 42) | public function toJson(): string
FILE: src/Prototype/Getter.php
type Getter (line 16) | trait Getter
method get (line 20) | public function get(string $key, $default = '')
method has (line 25) | public function has(string $key): bool
method all (line 30) | public function all(): array
method only (line 35) | public function only(...$keys): array
method except (line 45) | public function except(...$keys): array
method keys (line 55) | public function keys(): array
method count (line 60) | public function count(): int
method last (line 65) | public function last()
method getIterator (line 70) | public function getIterator(): ArrayIterator
method variadic (line 75) | public function variadic(...$keys): array
method getterTransformKey (line 84) | protected function getterTransformKey($key): string
FILE: src/Prototype/GetterObject.php
class GetterObject (line 14) | class GetterObject
method __construct (line 18) | public function __construct(array $data = [])
method set (line 25) | private function set(string $key, $value): void
FILE: src/Prototype/ObjectStore.php
class ObjectStore (line 14) | class ObjectStore
method __construct (line 20) | public function __construct(array $data = [])
FILE: src/Prototype/Setter.php
type Setter (line 14) | trait Setter
method set (line 18) | public function set(string $key, $value): void
method push (line 23) | public function push(string $key, $value): void
method pop (line 34) | public function pop(string $key)
method increment (line 47) | public function increment(string $key, int $by = 1): int
method decrement (line 56) | public function decrement(string $key, int $by = 1): int
method add (line 65) | public function add(array $data): void
method replace (line 72) | public function replace(array $data): void
method remove (line 80) | public function remove(string $key): void
method clear (line 85) | public function clear(): void
method setterTransformKey (line 90) | protected function setterTransformKey($key): string
method setterTransformValue (line 95) | protected function setterTransformValue($value)
FILE: src/Prototype/StringPrototype.php
class StringPrototype (line 14) | class StringPrototype
method __construct (line 18) | public function __construct(string $value)
method __toString (line 23) | public function __toString(): string
method set (line 28) | public function set($newValue)
method eq (line 33) | public function eq(string $string)
method match (line 38) | public function match(string $string, &$matches)
method ends (line 43) | public function ends(string $string)
method startsWith (line 48) | public function startsWith(string $string)
method limit (line 53) | public function limit(int $limit, $trimMarker = '')
method contain (line 61) | public function contain(string $string)
method trim (line 69) | public function trim(string $character_mask = " \t\n\r\0\x0B")
method append (line 75) | public function append($value): void
method prepend (line 80) | public function prepend($value): void
FILE: src/Route.php
class Route (line 40) | class Route
method __callStatic (line 42) | public static function __callStatic($method, $args)
FILE: src/Routing/Route.php
class Route (line 18) | class Route
method __construct (line 35) | public function __construct(string $method, Uri $uri, $handler, array ...
method getMethod (line 43) | public function getMethod(): string
method getUri (line 48) | public function getUri(): Uri
method getHandler (line 55) | public function getHandler(RouteHandlerInterface $handler): callable
method addNamespaceToHandler (line 60) | public function addNamespaceToHandler()
method getArgs (line 69) | public function getArgs(): array
method setArgs (line 74) | public function setArgs(array $args): void
method middleware (line 79) | public function middleware(...$middleware)
method getMiddleware (line 85) | public function getMiddleware()
method hasMiddleware (line 90) | public function hasMiddleware()
method withoutMiddleware (line 95) | public function withoutMiddleware(...$middleware)
FILE: src/Routing/RouteHandler.php
class RouteHandler (line 17) | class RouteHandler implements RouteHandlerInterface
method __construct (line 21) | public function __construct()
method call (line 26) | public function call(callable $handler, array $args = [])
method parse (line 31) | public function parse($handler): callable
FILE: src/Routing/RouteHandlerInterface.php
type RouteHandlerInterface (line 14) | interface RouteHandlerInterface
method call (line 16) | public function call(callable $handler, array $args = []);
method parse (line 18) | public function parse($handler): callable;
FILE: src/Routing/RouteParser.php
class RouteParser (line 16) | class RouteParser implements RouteParserInterface
method parse (line 18) | public function parse(string $uri): array
FILE: src/Routing/RouteParserInterface.php
type RouteParserInterface (line 14) | interface RouteParserInterface
method parse (line 24) | public function parse(string $uri): array;
FILE: src/Routing/Router.php
class Router (line 34) | class Router
method __construct (line 72) | public function __construct(Request $request, Response $response)
method __call (line 86) | public function __call($method, $args)
method setHandler (line 91) | public function setHandler(RouteHandlerInterface $handler)
method setParser (line 96) | public function setParser(RouteParserInterface $parser)
method setNotfound (line 101) | public function setNotfound($handler)
method run (line 106) | public function run(): Response
method add (line 112) | public function add($method, $uri, $handler, array $options = []): Route
method locale (line 143) | public function locale(array $locales, callable $callback): void
method auth (line 154) | public function auth(AuthInterface $auth, callable $callback)
method group (line 163) | public function group($prefix, callable $callback): void
method middleware (line 180) | public function middleware(...$middleware): Router
method use (line 191) | public function use(...$middleware): Router
method addPlaceholders (line 197) | public function addPlaceholders(array $patterns): void
method match (line 204) | public function match(string $method, string $uri): bool
method handleMiddleware (line 246) | public function handleMiddleware(Route $matched)
method getMatched (line 262) | public function getMatched()
method export (line 267) | public function export(): array
method handleOptions (line 272) | private function handleOptions(Route $matched)
method handleAuth (line 293) | private function handleAuth(string $id)
method handleGroupOptions (line 302) | private function handleGroupOptions(ArrayPrototype $options)
method find (line 322) | private function find(string $requestMethod, Uri $requestUri, $routes)...
method bindArgs (line 359) | private function bindArgs(array $pram, array $args): array
method localeRedirect (line 386) | private function localeRedirect()
FILE: src/Support/ArrayTrait.php
type ArrayTrait (line 19) | trait ArrayTrait
method get (line 29) | public static function get(array $arr, $k, $default = null)
method set (line 55) | public static function set(array $arr, $k, $v)
method value (line 112) | public static function value(array $arr, $k, $default = null)
method json (line 126) | public static function json($jsonStr, $k = null, $default = null)
FILE: src/Support/Regex.php
class Regex (line 22) | class Regex
method __call (line 38) | public function __call(string $name, array $arguments)
method __callStatic (line 43) | public static function __callStatic(string $name, array $arguments)
method instance (line 48) | public static function instance(): self
method _set (line 56) | public function _set(string $name, string $pattern): void
method _get (line 64) | public function _get(string $name): string
method _has (line 69) | public function _has(string $name): bool
method _update (line 74) | public function _update(string $name, string $pattern): void
method _list (line 79) | public function _list(): array
FILE: src/functions.php
function pre (line 5) | function pre($arr, $title = null, $theme = 1)
function dpre (line 26) | function dpre()
function container (line 32) | function container()
function response (line 37) | function response(): Just\Http\Response
function request (line 42) | function request(): Just\Http\Request
function auth (line 47) | function auth(): Just\Http\Auth
FILE: tests/AuthTest.php
class AuthTest (line 13) | class AuthTest extends TestCase
method testBasicAuth (line 15) | public function testBasicAuth() : void
method testDigest (line 31) | public function testDigest(): void
FILE: tests/DummyRequest.php
class DummyRequest (line 5) | class DummyRequest extends GlobalRequest
method setUri (line 9) | public function setUri(string $uri)
method setMethod (line 13) | public function setMethod(string $method)
FILE: tests/RouteParserTest.php
class RouteParserTest (line 9) | class RouteParserTest extends TestCase
method testParameters (line 11) | public function testParameters() : void
method testParametersWithRegex (line 25) | public function testParametersWithRegex() : void
method testParametersWithPlaceholder (line 39) | public function testParametersWithPlaceholder() : void
method testParametersWithPlaceholderAll (line 53) | public function testParametersWithPlaceholderAll() : void
method testParametersHasSamePlaceholderName (line 66) | public function testParametersHasSamePlaceholderName() : void
method testOptionalParametersWithPlaceholder (line 79) | public function testOptionalParametersWithPlaceholder() : void
method testLocaleParameter (line 93) | public function testLocaleParameter() : void
method testLocaleOptionalParameter (line 107) | public function testLocaleOptionalParameter() : void
FILE: tests/RouteTest.php
class controller (line 14) | class controller{
method method (line 15) | public static function method(){
class RouteTest (line 20) | class RouteTest extends TestCase
method app (line 22) | protected function app(): Router{
method testShortcuts (line 27) | public function testShortcuts() : void
method testGroups (line 66) | public function testGroups() : void
method testRouteDynamic (line 108) | public function testRouteDynamic() : void
method testGroupDynamic (line 145) | public function testGroupDynamic() : void
method testGroupDynamicOptionalParameter (line 172) | public function testGroupDynamicOptionalParameter() : void
method testAddPlaceholder (line 212) | public function testAddPlaceholder() : void
method testLocale (line 221) | public function testLocale(): void
method testLocaleRedirect (line 247) | public function testLocaleRedirect(): void
Condensed preview — 38 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (95K chars).
[
{
"path": ".gitignore",
"chars": 82,
"preview": "Thumbs.db\r\nehthumbs.db\r\n.DS_Store\r\n._*\r\n.idea\r\n.idea/vcs.xml\r\nvendor\r\n.phpunit.*\r\n"
},
{
"path": ".php_cs",
"chars": 2612,
"preview": "<?php\n\n$header = <<<'EOF'\nThis file is part of Just.\n\n@license https://github.com/just-framework/php/blob/master/LICENS"
},
{
"path": "LICENSE",
"chars": 1094,
"preview": "MIT License\r\n\r\nCopyright (c) 2016 Mahmoud Elnezamy\r\n\r\nPermission is hereby granted, free of charge, to any person obtain"
},
{
"path": "README.md",
"chars": 11708,
"preview": "# Route v2.0\nRoute - Fast, flexible routing for PHP, enabling you to quickly and easily build RESTful web applications.\n"
},
{
"path": "composer.json",
"chars": 1050,
"preview": "{\r\n \"name\": \"nezamy/route\",\r\n \"type\": \"library\",\r\n \"description\": \"Route - Fast, flexible routing for PHP, enabling y"
},
{
"path": "phpunit.xml",
"chars": 695,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit backupGlobals=\"true\"\n backupStaticAttributes=\"false\"\n bo"
},
{
"path": "src/DataType/Uri.php",
"chars": 941,
"preview": "<?php\n\ndeclare(strict_types=1);\n/**\n * This file is part of Just.\n *\n * @license https://github.com/just-framework/php/"
},
{
"path": "src/Http/Auth/AuthInterface.php",
"chars": 558,
"preview": "<?php\n\ndeclare(strict_types=1);\n/**\n * This file is part of Just.\n *\n * @license https://github.com/just-framework/php/"
},
{
"path": "src/Http/Auth/Basic.php",
"chars": 2598,
"preview": "<?php\n\ndeclare(strict_types=1);\n/**\n * This file is part of Just.\n *\n * @license https://github.com/just-framework/php/"
},
{
"path": "src/Http/Auth/Digest.php",
"chars": 3212,
"preview": "<?php\n\ndeclare(strict_types=1);\n/**\n * This file is part of Just.\n *\n * @license https://github.com/just-framework/php/"
},
{
"path": "src/Http/Auth.php",
"chars": 1016,
"preview": "<?php\n\ndeclare(strict_types=1);\n/**\n * This file is part of Just.\n *\n * @license https://github.com/just-framework/php/"
},
{
"path": "src/Http/GlobalRequest.php",
"chars": 1818,
"preview": "<?php\n\ndeclare(strict_types=1);\n/**\n * This file is part of Just.\n *\n * @license https://github.com/just-framework/php/"
},
{
"path": "src/Http/Middleware.php",
"chars": 1451,
"preview": "<?php\n\ndeclare(strict_types=1);\n/**\n * This file is part of Just.\n *\n * @license https://github.com/just-framework/php/"
},
{
"path": "src/Http/Request.php",
"chars": 6037,
"preview": "<?php\n\ndeclare(strict_types=1);\n/**\n * This file is part of Just.\n *\n * @license https://github.com/just-framework/php/"
},
{
"path": "src/Http/Response.php",
"chars": 1640,
"preview": "<?php\n\ndeclare(strict_types=1);\n/**\n * This file is part of Just.\n *\n * @license https://github.com/just-framework/php/"
},
{
"path": "src/Http/Session.php",
"chars": 481,
"preview": "<?php\n\ndeclare(strict_types=1);\n/**\n * This file is part of Just.\n *\n * @license https://github.com/just-framework/php/"
},
{
"path": "src/Prototype/ArrayPrototype.php",
"chars": 994,
"preview": "<?php\n\ndeclare(strict_types=1);\n/**\n * This file is part of Just.\n *\n * @license https://github.com/just-framework/php/"
},
{
"path": "src/Prototype/ConvertObject.php",
"chars": 1120,
"preview": "<?php\n\ndeclare(strict_types=1);\n/**\n * This file is part of Just.\n *\n * @license https://github.com/just-framework/php/"
},
{
"path": "src/Prototype/Getter.php",
"chars": 1826,
"preview": "<?php\n\ndeclare(strict_types=1);\n/**\n * This file is part of Just.\n *\n * @license https://github.com/just-framework/php/"
},
{
"path": "src/Prototype/GetterObject.php",
"chars": 606,
"preview": "<?php\n\ndeclare(strict_types=1);\n/**\n * This file is part of Just.\n *\n * @license https://github.com/just-framework/php/"
},
{
"path": "src/Prototype/ObjectStore.php",
"chars": 467,
"preview": "<?php\n\ndeclare(strict_types=1);\n/**\n * This file is part of Just.\n *\n * @license https://github.com/just-framework/php/"
},
{
"path": "src/Prototype/Setter.php",
"chars": 2867,
"preview": "<?php\n\ndeclare(strict_types=1);\n/**\n * This file is part of Just.\n *\n * @license https://github.com/just-framework/php/"
},
{
"path": "src/Prototype/StringPrototype.php",
"chars": 1727,
"preview": "<?php\n\ndeclare(strict_types=1);\n/**\n * This file is part of Just.\n *\n * @license https://github.com/just-framework/php/"
},
{
"path": "src/Route.php",
"chars": 1767,
"preview": "<?php\n\ndeclare(strict_types=1);\n/**\n * This file is part of Just.\n *\n * @license https://github.com/just-framework/php/"
},
{
"path": "src/Routing/Route.php",
"chars": 2213,
"preview": "<?php\n\ndeclare(strict_types=1);\n/**\n * This file is part of Just.\n *\n * @license https://github.com/just-framework/php/"
},
{
"path": "src/Routing/RouteHandler.php",
"chars": 1299,
"preview": "<?php\n\ndeclare(strict_types=1);\n/**\n * This file is part of Just.\n *\n * @license https://github.com/just-framework/php/"
},
{
"path": "src/Routing/RouteHandlerInterface.php",
"chars": 441,
"preview": "<?php\n\ndeclare(strict_types=1);\n/**\n * This file is part of Just.\n *\n * @license https://github.com/just-framework/php/"
},
{
"path": "src/Routing/RouteParser.php",
"chars": 1666,
"preview": "<?php\n\ndeclare(strict_types=1);\n/**\n * This file is part of Just.\n *\n * @license https://github.com/just-framework/php/"
},
{
"path": "src/Routing/RouteParserInterface.php",
"chars": 707,
"preview": "<?php\n\ndeclare(strict_types=1);\n/**\n * This file is part of Just.\n *\n * @license https://github.com/just-framework/php/"
},
{
"path": "src/Routing/Router.php",
"chars": 12283,
"preview": "<?php\n\ndeclare(strict_types=1);\n/**\n * This file is part of Just.\n *\n * @license https://github.com/just-framework/php/"
},
{
"path": "src/Support/ArrayTrait.php",
"chars": 3753,
"preview": "<?php\n\ndeclare(strict_types=1);\n/**\n * This file is part of Just.\n *\n * @license https://github.com/just-framework/php/"
},
{
"path": "src/Support/Regex.php",
"chars": 2108,
"preview": "<?php\n\ndeclare(strict_types=1);\n/**\n * This file is part of Just.\n *\n * @license https://github.com/just-framework/php/"
},
{
"path": "src/functions.php",
"chars": 1834,
"preview": "<?php declare(strict_types=1);\n! defined('DS') && define('DS', DIRECTORY_SEPARATOR);\n\n//=============[ pre() is print ar"
},
{
"path": "tests/AuthTest.php",
"chars": 1150,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Just\\Test;\n\nuse Just\\DI\\Container;\nuse Just\\Http\\Auth;\nuse Just\\Http\\GlobalReq"
},
{
"path": "tests/DummyRequest.php",
"chars": 324,
"preview": "<?php declare(strict_types=1);\n\nuse Just\\Http\\GlobalRequest;\n\nclass DummyRequest extends GlobalRequest\n{\n protected $"
},
{
"path": "tests/RouteParserTest.php",
"chars": 3504,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Just\\Test;\n\nuse Just\\Routing\\RouteParser;\nuse PHPUnit\\Framework\\TestCase;\n\ncla"
},
{
"path": "tests/RouteTest.php",
"chars": 9321,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Just\\Test;\n\nuse DummyRequest;\nuse Just\\DataType\\Uri;\nuse Just\\Http\\GlobalReque"
},
{
"path": "tests/bootstrap.php",
"chars": 174,
"preview": "<?php\nerror_reporting(E_ALL);\n\n// ini_set('error_reporting', E_ALL);\n\n$loader = require __DIR__ . '/../vendor/autoload.p"
}
]
About this extraction
This page contains the full source code of the nezamy/route GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 38 files (87.1 KB), approximately 23.1k tokens, and a symbol index with 208 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.