Full Code of nezamy/route for AI

master cd683aef81c1 cached
38 files
87.1 KB
23.1k tokens
208 symbols
1 requests
Download .txt
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';
Download .txt
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
Download .txt
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.

Copied to clipboard!