tmp
continuous-integration/drone/push Build is passing Details

issue_021_Auth
David D'ALMEIDA 1 year ago
parent 153418181e
commit 0e5e671423

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

@ -21,8 +21,8 @@
},
"require": {
"twig/twig": "^3.0",
"vlucas/phpdotenv": "^5.5"
},
"vlucas/phpdotenv": "^5.5",
"altorouter/altorouter": "1.1.0" },
"require-dev": {
"phpunit/phpunit": "*"
},

@ -4,8 +4,63 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "de5df96e9fd160ca0fc32f83e7a4ee7d",
"content-hash": "8751e5a6dda5bf24fe98fe70a522612f",
"packages": [
{
"name": "altorouter/altorouter",
"version": "v1.1.0",
"source": {
"type": "git",
"url": "https://github.com/dannyvankooten/AltoRouter.git",
"reference": "09d9d946c546bae6d22a7654cdb3b825ffda54b4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dannyvankooten/AltoRouter/zipball/09d9d946c546bae6d22a7654cdb3b825ffda54b4",
"reference": "09d9d946c546bae6d22a7654cdb3b825ffda54b4",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"autoload": {
"classmap": [
"AltoRouter.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Danny van Kooten",
"email": "dannyvankooten@gmail.com",
"homepage": "http://dannyvankooten.com/"
},
{
"name": "Koen Punt",
"homepage": "https://github.com/koenpunt"
},
{
"name": "niahoo",
"homepage": "https://github.com/niahoo"
}
],
"description": "A lightning fast router for PHP",
"homepage": "https://github.com/dannyvankooten/AltoRouter",
"keywords": [
"lightweight",
"router",
"routing"
],
"support": {
"issues": "https://github.com/dannyvankooten/AltoRouter/issues",
"source": "https://github.com/dannyvankooten/AltoRouter/tree/master"
},
"time": "2014-04-16T09:44:40+00:00"
},
{
"name": "graham-campbell/result-type",
"version": "v1.1.1",

@ -1,9 +1,30 @@
<?php
require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../config/config.php';
if (APP_ENV === 'console') {
require_once __DIR__ . '/../src/console/Console.php';
}
elseif (APP_ENV === 'development') {
require_once __DIR__ . 'index.html';
// turn the if in obj mode
// if (APP_ENV === 'console') {
// require_once __DIR__ . '/../src/console/Console.php';
// }
// elseif (APP_ENV === 'development') {
// on pourait aussi gérer le port sois ici comme dans express sois comme dans un fichier de config json...
$appFactory = new AppCreator();
// builder.Services.AddScoped<AuthMiddlewareFliter>();
$app = $appFactory->registerService('','');
$appFactory.errorProvider(class:: or port)
// connexion string
// var connectionString = builder.Configuration.GetConnectionString("LolDatabase");
// builder.Services.AddDbContext<LolDbContext>(options =>
// options.UseSqlite(connectionString), ServiceLifetime.Singleton);
$app->use(new LoggingMiddleware());
$app = $appFactory->create();
$app.addHttpClient(HttpClient::class)
// je veux pas faire sa pour load les controller avec les anotation
$app->RegisterControllers();
$app->
$app->run();
}

@ -0,0 +1,84 @@
<?
class App {
private $appName;
private $version;
private IHttpMiddleware $middlewarePipeline;
private DiContainer $container;
public function __construct($appName, $version, $diContainer) {
$this->appName = $appName;
$this->version = $version;
$this->$diContainer = $diContainer;
}
public function use(IHttpMiddleware $middleware) {
if ($this->middlewarePipeline === null) {
$this->middlewarePipeline = $middleware;
} else {
// Chain the new middleware to the end of the existing pipeline
$currentMiddleware = $this->middlewarePipeline;
while ($currentMiddleware->getNext() !== null) {
$currentMiddleware = $currentMiddleware->getNext();
}
$currentMiddleware->setNext($middleware);
}
}
public function getAppName() {
return $this->appName;
}
public function getVersion() {
return $this->version;
}
public function run() {
echo "Running {$this->appName} version {$this->version}\n";
}
// public function run(HttpRequest $request) {
// if ($this->middlewarePipeline === null) {
// // No middleware defined, return the request as-is
// return $request;
// }
// // Exécutez le middleware en utilisant le pipeline
// return $this->middlewarePipeline->handle($request, function($request) {
// // Logique de gestion principale de la requête ici
// echo "Main Request Handling Logic.\n";
// return $request;
// });
// }
// should not be hese responsibibilty add in DI
public function autoAddMiddlewares() {
$middlewareClasses = $this->findMiddlewareClasses();
foreach ($middlewareClasses as $middlewareClass) {
$this->use(new $middlewareClass());
}
}
// Méthode pour rechercher automatiquement les classes de middleware en utilisant la réflexion
// private function findMiddlewareClasses() {
// $middlewareClasses = [];
// // Utilisez la réflexion pour obtenir toutes les classes disponibles
// $classes = get_declared_classes();
// foreach ($classes as $class) {
// $reflectionClass = new ReflectionClass($class);
// if ($reflectionClass->implementsInterface(MiddlewareInterface::class)) {
// // La classe implémente MiddlewareInterface, ajoutez-la à la liste
// $middlewareClasses[] = $class;
// }
// }
// return $middlewareClasses;
// }
}
?>

@ -0,0 +1,13 @@
<?php
class AppCreator {
public function __construct() {
}
}
?>

@ -0,0 +1,108 @@
<?php
/**
* Class Container
*/
class Container
{
/**
* @var array
*/
protected $instances = [];
/**
* @param $abstract
* @param null $concrete
*/
public function set($abstract, $concrete = NULL)
{
if ($concrete === NULL) {
$concrete = $abstract;
}
$this->instances[$abstract] = $concrete;
}
/**
* @param $abstract
* @param array $parameters
*
* @return mixed|null|object
* @throws Exception
*/
public function get($abstract, $parameters = [])
{
// if we don't have it, just register it
if (!isset($this->instances[$abstract])) {
$this->set($abstract);
}
return $this->resolve($this->instances[$abstract], $parameters);
}
/**
* resolve single
*
* @param $concrete
* @param $parameters
*
* @return mixed|object
* @throws Exception
*/
public function resolve($concrete, $parameters)
{
if ($concrete instanceof Closure) {
return $concrete($this, $parameters);
}
$reflector = new ReflectionClass($concrete);
// check if class is instantiable
if (!$reflector->isInstantiable()) {
throw new Exception("Class {$concrete} is not instantiable");
}
// get class constructor
$constructor = $reflector->getConstructor();
if (is_null($constructor)) {
// get new instance from class
return $reflector->newInstance();
}
// get constructor params
$parameters = $constructor->getParameters();
$dependencies = $this->getDependencies($parameters);
// get new instance with dependencies resolved
return $reflector->newInstanceArgs($dependencies);
}
/**
* get all dependencies resolved
*
* @param $parameters
*
* @return array
* @throws Exception
*/
public function getDependencies($parameters)
{
$dependencies = [];
foreach ($parameters as $parameter) {
// get the type hinted class
$dependency = $parameter->getClass();
if ($dependency === NULL) {
// check if default value for a parameter is available
if ($parameter->isDefaultValueAvailable()) {
// get default value of parameter
$dependencies[] = $parameter->getDefaultValue();
} else {
throw new Exception("Can not resolve class dependency {$parameter->name}");
}
} else {
// get dependency resolved
$dependencies[] = $this->get($dependency->name);
}
}
return $dependencies;
}
}

@ -0,0 +1,65 @@
<?
class MyHttpClient {
private $baseUrl;
private $headers;
public function __construct($baseUrl) {
$this->baseUrl = $baseUrl;
$this->headers = [];
}
public function setHeader($name, $value) {
$this->headers[$name] = $value;
}
public function get($endpoint) {
$url = $this->baseUrl . '/' . $endpoint;
return $this->sendRequest('GET', $url);
}
public function post($endpoint, $data) {
$url = $this->baseUrl . '/' . $endpoint;
return $this->sendRequest('POST', $url, $data);
}
private function sendRequest($method, $url, $data = null) {
$options = [
'http' => [
'method' => $method,
'header' => $this->buildHeaders(),
'content' => $data ? http_build_query($data) : null,
],
];
$context = stream_context_create($options);
$response = file_get_contents($url, false, $context);
// You can add error handling here if needed
return $response;
}
private function buildHeaders() {
$headers = [];
foreach ($this->headers as $name => $value) {
$headers[] = "$name: $value";
}
return implode("\r\n", $headers);
}
}
// Usage
$httpClient = new MyHttpClient('https://api.example.com');
// Add headers (e.g., authentication token)
$httpClient->setHeader('Authorization', 'Bearer your-auth-token-here');
// Example GET request
$getResponse = $httpClient->get('some-endpoint');
echo "GET Response: $getResponse\n";
// Example POST request
$postData = ['key' => 'value'];
$postResponse = $httpClient->post('another-endpoint', $postData);
echo "POST Response: $postResponse\n";

@ -0,0 +1,45 @@
<?php
namespace App\Controllers;
class AthleteController extends BaseController
{
public function __construct()
{
// Initialize UserController specific configurations or dependencies
}
public function index()
{
// Handle the user listing logic here
}
public function show($id)
{
// Handle displaying a specific user by ID
}
public function create()
{
// Handle user creation logic here
}
public function store()
{
// Handle storing a new user in the database
}
public function edit($id)
{
// Handle user editing logic here
}
public function update($id)
{
// Handle updating a user in the database
}
public function destroy($id)
{
// Handle user deletion logic here
}
}

@ -0,0 +1,101 @@
<?php
namespace Controllers;
use Responce\{RedirectResponse, Response};
/**
* BaseController is a abstract class that embede all based function of a controller in this app
* Responsabilité du contrôleur :
* - Gérer les demandes HTTP et coordonner l'exécution de l'action appropriée.
* - Exposer des méthodes/actions qui sont spécifiques à l'interface utilisateur.
* !!! Aucune logique métier ici !!!
* Contôle l'intégrité des données et valid les action ( les deux doivent être vérifier)
* Ses responsabilités sont de garantir que les données de la demande sont valides et de choisir la vue à retourner.
* Un Controller à la responsibilité de lire les information d'un Request(obj) et créer une {Response}.
*/
abstract class BaseController{
private DI $container;
public function __construct(DI $di){
$this->container = $di;
}
// private ILogger<T> $logger;
protected function redirect(string $url, int $status = 302): RedirectResponse
{
return new RedirectResponse($url, $status);
}
protected function redirectToRoute(string $route, array $parameters = [], int $status = 302): RedirectResponse
{
return $this->redirect($this->generateUrl($route, $parameters), $status);
}
protected function renderView(string $view, array $parameters = []): string
{
return $this->doRenderView($view, null, $parameters, __FUNCTION__);
}
protected function render(string $view, array $parameters = [], Response $response = null): Response
{
return $this->doRender($view, null, $parameters, $response, __FUNCTION__);
}
private function doRenderView(string $view, ?string $block, array $parameters, string $method): string
{
if (!$this->container->has('twig')) {
throw new \LogicException(sprintf('You cannot use the "%s" method if the Twig Bundle is not available. Try running "composer require symfony/twig-bundle".', $method));
}
foreach ($parameters as $k => $v) {
if ($v instanceof FormInterface) {
$parameters[$k] = $v->createView();
}
}
if (null !== $block) {
return $this->container->get('twig')->load($view)->renderBlock($block, $parameters);
}
}
private function doRender(string $view, ?string $block, array $parameters, ?Response $response, string $method): Response
{
$content = $this->doRenderView($view, $block, $parameters, $method);
$response ??= new Response();
if (200 === $response->getStatusCode()) {
foreach ($parameters as $v) {
if ($v instanceof FormInterface && $v->isSubmitted() && !$v->isValid()) {
$response->setStatusCode(422);
break;
}
}
}
$response->setContent($content);
return $response;
}
abstract public function index();
public function __call($method, $parameters)
{
throw new BadMethodCallException(sprintf(
'Method %s::%s does not exist.', static::class, $method
));
}
/**
* Execute an action on the controller.
*
* @param string $method
* @param array $parameters
* @return \Symfony\Component\HttpFoundation\Response
*/
public function callAction($method, $parameters)
{
return $this->{$method}(...array_values($parameters));
}
}
?>

@ -0,0 +1,63 @@
<?php
// App etre sur que la persone a le droit defaire
class FrontController {
private $router;
public function __construct(Router $router) {
$this->router = $router;
}
public function dispatch(IRequest $request) {
$match = $this->router->match($request);
if ($match) {
$controllerName = $match['controller'];
$actionName = $match['action'];
// Utilisez l'injection de dépendances pour créer le contrôleur
$controller = $this->createController($controllerName);
if ($controller) {
// Appeler l'action correspondante
$controller->$actionName();
} else {
// Gérer l'erreur, le contrôleur n'existe pas
$this->handleError();
}
} else {
// Gérer l'erreur, aucune route correspondante
$this->handleError();
}
}
private function createController($controllerName) {
// Utilisez un conteneur d'injection de dépendances pour créer le contrôleur
return DependencyContainer::create($controllerName);
}
private function handleError() {// composant twig
header("HTTP/1.0 404 Not Found");
echo "Page not found";
}
}
?>
<!--
Point d'entrée unique : Le front controller est le point d'entrée unique de toutes les requêtes HTTP entrantes dans une application Symfony. Toutes les requêtes sont dirigées vers ce contrôleur, qui est généralement un fichier PHP, tel que public/index.php.
Initialisation : Le front controller initialise l'application en chargeant les dépendances, en configurant l'environnement, en définissant les paramètres globaux, et en préparant l'infrastructure de base nécessaire pour gérer les requêtes HTTP.
Gestion de la requête : Une fois qu'une requête HTTP arrive, le front controller crée un objet Request à partir de cette requête entrante. Il s'occupe également de la gestion de la session, de la gestion des cookies, et de l'analyse des en-têtes HTTP.
Routage vers le contrôleur : Le front controller interagit avec le router pour déterminer quel contrôleur doit être utilisé pour traiter la requête. Il envoie la requête au router pour faire correspondre l'URL demandée à une route et obtenir des informations sur le contrôleur et l'action associés.
Résolution du contrôleur : Une fois que le router a identifié le contrôleur et l'action à appeler, le front controller résout le contrôleur en créant une instance de l'objet contrôleur approprié. Il extrait également l'action à appeler.
Exécution de l'action : Le front controller appelle l'action du contrôleur avec les paramètres nécessaires, généralement en passant l'objet Request. L'action effectue le traitement spécifique à la page, tel que la récupération de données, la manipulation des données, etc.
Génération de la réponse : Après avoir exécuté l'action, le contrôleur génère une réponse HTTP sous la forme d'un objet Response. Cette réponse peut contenir des données à afficher dans le navigateur du client.
Envoi de la réponse : Le front controller envoie finalement la réponse HTTP générée par le contrôleur au client, qui l'affiche dans le navigateur -->
<!-- c'est App -->

@ -0,0 +1,22 @@
<?php
class Route
{
private string $name;
private array $parrams;
private $callable;
public function __construct(array $params, callable $callable, string $name = null)
{
$this->path = $params;
$this->callable = $callable;
$this->name = $name;
}
}

@ -0,0 +1,77 @@
<!-- Correspondance des URLs : Le rôle principal du router est de faire correspondre les URLs entrantes aux contrôleurs et aux actions qui doivent les gérer.
Il examine l'URL demandée par le client (par exemple, "/contact") et détermine quelle route correspondante peut être utilisée.
Ces règles peuvent être définies à l'aide d'annotations dans les contrôleurs.
Gestion des paramètres d'URL : Le router extrait également les paramètres d'URL de l'URL demandée. Par exemple, si l'URL est "/articles/123", le router extraira l'identifiant "123" en tant que paramètre pour être transmis au contrôleur.
Gestion des noms de route : Le router attribue des noms aux routes définies, ce qui permet aux développeurs de générer des URLs à partir de noms de route dans leurs vues ou leurs contrôleurs. Cela simplifie la génération d'URLs dans l'application.
Gestion des erreurs de routage : En cas de route non trouvée (par exemple, lorsque l'URL demandée ne correspond à aucune route définie), le router peut générer une réponse HTTP appropriée, généralement une réponse "404 Not Found".
Configuration du contexte de routage : Le router tient compte du contexte de la requête actuelle, ce qui signifie qu'il peut adapter la recherche de routes en fonction des paramètres de la requête, tels que la langue, la méthode HTTP, etc. -->
<!--
match: Méthode pour faire correspondre une URL à une route et renvoyer les informations de route.
generate: Méthode pour générer une URL à partir du nom de la route et des paramètres.
getContext: Récupère le contexte de la requête.
setContext: Définit le contexte de la requête.
getRouteCollection: Récupère la collection de routes.
getMatcher: Récupère l'objet UrlMatcher utilisé pour la correspondance des URLs -->
<!-- Création de l'objet Request : Le contrôleur frontal crée un objet Request à partir de la requête HTTP entrante en utilisant la classe Symfony\Component\HttpFoundation\Request.
Configuration du router : Il configure et utilise le router (objet Router) pour faire correspondre l'URL à une route.
Résolution du contrôleur : Le contrôleur frontal résout le contrôleur en utilisant le contrôleur résolveur (généralement une instance de Symfony\Component\HttpKernel\Controller\ControllerResolver).
Exécution de l'action : Il appelle l'action du contrôleur en fonction de la route correspondante et exécute l'action spécifique à la page.
Création de la réponse : Le contrôleur frontal crée un objet Response pour contenir la réponse HTTP générée par l'action du contrôleur.
Envoi de la réponse : Il envoie finalement la réponse HTTP au client en utilisant la méthode send de l'objet Response -->
<?php
// Just a url matcher
class Router {
private string $path;
// routes collection
private AltoRouter $routes;
// public static $verbs = ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'];
public function __construct(string $path = "/PHP/project/index.php") {
$this->path = $path;
$this->routes = new AltoRouter($this->$path);
}
public function add(Route $route, $name) {}
// extrait également les paramètres d'URL de l'URL demandée
public function extractParams(string $path) {}
public function get(string $path, callable $callable, $name) {
$this->router->map('GET', $path, $callable, $name);
}
public function post(string $path, callable $callable, $name) {
$this->router->map('POST', $path, $callable, $name);
}
public function put(string $path, callable $callable, $name) {
$this->router->map('PUT', $path, $callable, $name);
}
public function match (IRequest $request): ?Route {
$result = $this->routes->match($request->getRequestUri(), $request->getMethod());
if ($result) {
return new Route($result['params'],$result['target'],$result['name']) ;
}
return null;
}
public function
}
?>

@ -0,0 +1,33 @@
<?php
class HttpException extends \RuntimeException
{
private int $statusCode;
private array $headers;
public function __construct(int $statusCode, string $message = '', \Throwable $previous = null, array $headers = [], int $code = 0)
{
$this->statusCode = $statusCode;
$this->headers = $headers;
parent::__construct($message, $code, $previous);
}
public function getStatusCode(): int
{
return $this->statusCode;
}
public function getHeaders(): array
{
return $this->headers;
}
/**
* @return void
*/
public function setHeaders(array $headers)
{
$this->headers = $headers;
}
}

@ -0,0 +1,22 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* @author Fabien Potencier <fabien@symfony.com>
*/
class NotFoundHttpException extends HttpException
{
public function __construct(string $message = '', \Throwable $previous = null, int $code = 0, array $headers = [])
{
parent::__construct(404, $message, $previous, $headers, $code);
}
}

@ -0,0 +1,24 @@
<?php
class ValidationException extends Exception {
protected $errors;
public function __construct(array $errors, $message = "Validation errors occurred", $code = 0, Exception $previous = null) {
parent::__construct($message, $code, $previous);
$this->errors = $errors;
}
public function getErrors() {
return $this->errors;
}
public function __toString() {
return __CLASS__ . ": [{$this->code}]: {$this->message}\n" . $this->formatErrors();
}
protected function formatErrors() {
return implode("\n", array_map(function ($error) {
return "- {$error}";
}, $this->errors));
}
}

@ -0,0 +1,36 @@
<?php
class RequestValidationMiddleware extends Middleware {
private $validator;
private $rules;
public function __construct(Validator $validator, array $rules) {
$this->validator = $validator;
$this->rules = $rules;
}
public function handle(IRequest $request, callable $next) {
$this->validateRequest($request);
return parent::handle($request, $next);
}
private function validateRequest(IRequest $request) {
foreach ($this->rules as $param => $ruleSet) {
foreach ($ruleSet as $rule) {
$this->validator->rule($param, $rule['callback'], $rule['message']);
}
}
$requestData = array_merge($request->getQueryParameters(), $request->getRequestParameters());
$this->validator->assert($requestData);
}
}
$validationRules = [
'email' => [
['callback' => Validator::required(), 'message' => 'Email is required.'],
['callback' => Validator::email(), 'message' => 'Email must be a valid email address.']
],
// Add more rules as needed
];

@ -0,0 +1,5 @@
<?php
interface IHttpMiddleware {
public function handle(IRequest $request, callable $next);
}

@ -0,0 +1,8 @@
<?php
class LoggingMiddleware extends Middleware {
public function handle(IRequest $request, callable $next) {
// Logique de journalisation
echo "LoggingMiddleware: Log request - Method: {$request->getMethod()}, URI: {$request->getRequestUri()}\n";
return parent::handle($request, $next);
}
}

@ -0,0 +1,19 @@
<?php
// Chain of Responsibility
// Dans l'app, les requêtes HTTP traversent une série de couches de traitement appelées "middleware".
// Chaque middleware peut accepter ou refuser la requête. Une fois qu'une requête
// passe avec succès à travers tous les middleware, elle est finalement traitée par l'application elle-même.
abstract class Middleware implements IHttpMiddleware {
protected $next;
public function setNext(IHttpMiddleware $nextMiddleware) {
$this->next = $nextMiddleware;
}
public function handle(IRequest $request, callable $next) {
if ($this->next !== null) {
return $this->next->handle($request, $next);
}
return $next($request);
}
}

@ -0,0 +1,5 @@
<?php
interface ContentStrategy {
public function getContent(): array;
}

@ -0,0 +1,21 @@
<?php
// should maybe change this
class ContentStrategyFactory {
private static $strategyMap = [
'application/json' => JsonContentStrategy::class,
// Format...
];
public static function createContentStrategy(string $contentType, string $requestMethod): ContentStrategy {
foreach (self::$strategyMap as $type => $className) {
if ($contentType === $type || in_array($requestMethod, ['PUT', 'PATCH', 'DELETE'])) {
return new $className();
}
}
return new FormContentStrategy();
}
public static function registerStrategy(string $contentType, string $className): void {
self::$strategyMap[$contentType] = $className;
}
}

@ -0,0 +1,8 @@
<?php
class FormContentStrategy implements ContentStrategy {
public function getContent(): array {
return $_POST;
}
}

@ -0,0 +1,11 @@
<?php
interface IRequest
{
public function getRequestUri();
public function getHeaders();
public function getMethod();
public function getQueryParameters(): array;
public function getRequestParameters(): array;
}

@ -0,0 +1,7 @@
<?php
class JsonContentStrategy implements ContentStrategy {
public function getContent(): array {
$rawContent = file_get_contents('php://input');
return json_decode($rawContent, true) ?? [];
}
}

@ -0,0 +1,43 @@
<?php
class HttpRequest implements IRequest {
private $queryParameters;
private $requestParameters;
private $method;
private $requestUri;
private $headers;
public function __construct(
array $query,
array $server,
array $headers,
ContentStrategy $contentStrategy
) {
$this->queryParameters = $query;
$this->requestUri = $server['REQUEST_URI'] ?? '';
$this->method = strtoupper($server['REQUEST_METHOD'] ?? 'GET');
$this->headers = $headers;
$this->requestParameters = $contentStrategy->getContent();
}
public function getQueryParameters(): array {
return $this->queryParameters;
}
public function getRequestParameters(): array {
return $this->requestParameters;
}
public function getMethod(): string {
return $this->method;
}
public function getRequestUri(): string {
return $this->requestUri;
}
public function getHeaders(): array {
return $this->headers;
}
}

@ -0,0 +1,25 @@
<?php
class RequestFactory {
public static function createFromGlobals(): IRequest {
$query = $_GET;
$server = $_SERVER;
$headers = self::getRequestHeaders();
$contentType = $headers['Content-Type'] ?? '';
$contentStrategy = ContentStrategyFactory::createContentStrategy($contentType, $server['REQUEST_METHOD']);
return new HttpRequest($query, $server, $headers, $contentStrategy);
}
// should not be heare
private static function getRequestHeaders(): array {
$headers = [];
foreach ($_SERVER as $key => $value) {
if (substr($key, 0, 5) === 'HTTP_') {
$header = str_replace(' ', '-', ucwords(str_replace('_', ' ', strtolower(substr($key, 5)))));
$headers[$header] = $value;
}
}
return $headers;
}
}

@ -0,0 +1,10 @@
<?php
interface ResponseInterface {
public function getContent(): string;
public function setContent(string $content): void;
public function getStatusCode(): int;
public function setStatusCode(int $statusCode): void;
public function getHeaders(): array;
public function setHeader(string $key, string $value): void;
public function send(): void;
}

@ -0,0 +1,50 @@
<!-- its can be a HTML page, JSON, XML, a file download, a redirect, a 404 error or anything else -->
<?php
class Response implements IResponce {
private string $content;
private int $statusCode;
private array $headers;
public function __construct(string $content = "", int $statusCode = 200, array $headers = []) {
$this->content = $content;
$this->statusCode = $statusCode;
$this->headers = $headers;
}
public function getContent(): string {
return $this->content;
}
public function setContent(string $content): void {
$this->content = $content;
}
public function getStatusCode(): int {
return $this->statusCode;
}
public function setStatusCode(int $statusCode): void {
$this->statusCode = $statusCode;
}
public function getHeaders(): array {
return $this->headers;
}
public function setHeader(string $key, string $value): void {
$this->headers[$key] = $value;
}
public function send(): void {
foreach ($this->headers as $key => $value) {
header("{$key}: {$value}");
}
http_response_code($this->statusCode);
echo $this->content;
}
}
?>

@ -0,0 +1,5 @@
<?php
interface FieldInterface {
public function render();
public function validate($value);
}

@ -0,0 +1,40 @@
<section class="bg-gray-50 dark:bg-gray-900">
<div class="flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0">
<a href="#" class="flex items-center mb-6 text-2xl font-semibold text-gray-900 dark:text-white">
<img class="w-8 h-8 mr-2" src="https://flowbite.s3.amazonaws.com/blocks/marketing-ui/logo.svg" alt="logo">
HearTrack
</a>
<div class="w-full bg-white rounded-lg shadow dark:border md:mt-0 sm:max-w-md xl:p-0 dark:bg-gray-800 dark:border-gray-700">
<div class="p-6 space-y-4 md:space-y-6 sm:p-8">
<h1 class="text-xl font-bold leading-tight tracking-tight text-gray-900 md:text-2xl dark:text-white">
Sign in to your account
</h1>
<form class="space-y-4 md:space-y-6" action="#">
<div>
<label for="email" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Your email</label>
<input type="email" name="email" id="email" class="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="name@company.com" required="">
</div>
<div>
<label for="password" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Password</label>
<input type="password" name="password" id="password" placeholder="••••••••" class="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" required="">
</div>
<div class="flex items-center justify-between">
<div class="flex items-start">
<div class="flex items-center h-5">
<input id="remember" aria-describedby="remember" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-primary-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-primary-600 dark:ring-offset-gray-800" required="">
</div>
<div class="ml-3 text-sm">
<label for="remember" class="text-gray-500 dark:text-gray-300">Remember me</label>
</div>
</div>
<a href="#" class="text-sm font-medium text-primary-600 hover:underline dark:text-primary-500">Forgot password?</a>
</div>
<button type="submit" class="w-full text-white bg-primary-600 hover:bg-primary-700 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800">Sign in</button>
<p class="text-sm font-light text-gray-500 dark:text-gray-400">
Dont have an account yet? <a href="#" class="font-medium text-primary-600 hover:underline dark:text-primary-500">Sign up</a>
</p>
</form>
</div>
</div>
</div>
</section>

@ -0,0 +1,23 @@
{# templates/login/index.html.twig #}
{% extends 'base.html.twig' %}
{# ... #}
{% block body %}
{% if error %}
<div>{{ error.messageKey|trans(error.messageData, 'security') }}</div>
{% endif %}
<form action="{{ path('app_login') }}" method="post">
<label for="username">Email:</label>
<input type="text" id="username" name="_username" value="{{ last_username }}">
<label for="password">Password:</label>
<input type="password" id="password" name="_password">
{# If you want to control the URL the user is redirected to on success
<input type="hidden" name="_target_path" value="/account"> #}
<button type="submit">login</button>
</form>
{% endblock %}

@ -105,7 +105,7 @@ function displaySettingsMenu()
// Modifier le profil du athlète et coach
echo "2. Personnaliser le profil public\n";
echo "3. Configurer les alertes\n";
echo "4. Supprimer mon compte"
echo "4. Supprimer mon compte";
echo "0. Retour au menu principal\n";
echo "Choisissez une option: ";
}

@ -0,0 +1,21 @@
<?php
namespace Console;
class Application extends Shared\Application{
private $dataManager;
private Menu $currentMenu;
public function __construct(DataManager $dataManager) {
$this->dataManager = $dataManager;
$this->currentMenu = new AuthMenu();
}
public function run()
{
while (true) {
if (!$this->authenticationController->isLoggedIn()) {
$this->authenticationController->displayAuthMenu();
} else {
$this->menuController->displayMainMenu();
}
}
}
}

@ -0,0 +1,16 @@
<?php
class AuthMenu extends Menu {
public function display() {
$this->clearScreen();
echo "\n\n";
echo " +--------------------------+\n";
echo " | Authentification |\n";
echo " +--------------------------+\n";
echo " | 1. Se connecter |\n";
echo " | 2. S'inscrire |\n";
echo " | 0. Quitter |\n";
echo " +--------------------------+\n";
echo " Choisissez une option: ";
}
}

@ -0,0 +1,8 @@
<?php
class ClearScreenCommand {
public function execute() {
system('clear || cls');
}
}

@ -0,0 +1,4 @@
<?php
interface IPrinter {
public function display();
}

@ -0,0 +1,18 @@
<?php
abstract class Menu implements IPrinter {
protected ClearScreenCommand $clearScreenCommand;
public function __construct()
{
$this->clearScreenCommand = new ClearScreenCommand();
}
protected function clearScreen()
{
$this->clearScreenCommand->execute();
}
abstract public function display();
}

@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
namespace Silex\Security;
use Silex\Gateway\UserGateway;
use Silex\Model\User;
class Security
{
const tokenSession = 'tokenSession';
private array $session;
private UserGateway $userGateway;
private ?User $user = null;
public function __construct(UserGateway $userGateway, array &$session)
{
$this->userGateway = $userGateway;
$this->session = &$session;
}
public function initLogin(string $login, string $rawPassword): bool
{
$user = $this->userGateway->getByLogin($login);
if ($user === null || !password_verify($rawPassword, $user->getPasswordHash())) {
return false;
}
$this->session[tokenSession] = $user->getId();
$this->user = $user;
return true;
}
public function logout(): bool
{
if(session_unset()){
return true;
}
$this->user = null;
session_unset();
session_destroy();
$_SESSION['role'] = "";
$_SESSION=array();
unset($this->session[tokenSession]);
return true;
}
public function getCurrentUser(): ?User
{
if (!empty($this->session[tokenSession]) && $this->user === null) {
$this->user = $this->userGateway->getById($this->session[tokenSession]);
}
return $this->user;
}
}

@ -1,8 +1,11 @@
<?php
namespace Model;
class Coach extends Role {
// Attributs spécifiques au Coach si nécessaire
private $idCoach;
private array $athletes;
}
?>

@ -8,8 +8,10 @@ use Shared\Validation;
// c'est le modéle
// should be here try catch ??
// should have a IUserManager
class UserManager
{
Auth $auth; ou $ user :Obersable;
private IAuthService $authService;
public function __construct(IAuthService $authService)
{

@ -28,6 +28,7 @@ class AuthService implements IAuthService {
return false;
}
// Check if user is already in the
public function register(string $loginUser, string $password, $data): bool
{

@ -0,0 +1,71 @@
<?php
class Validator {
protected $rules = [];
public function rule($field, callable $callback, $message = '') {
$this->rules[$field][] = [
'callback' => $callback,
'message' => $message
];
return $this;
}
public function validate(array $data) {
$errors = [];
foreach ($this->rules as $field => $fieldRules) {
foreach ($fieldRules as $rule) {
if (!array_key_exists($field, $data) || !call_user_func($rule['callback'], $data[$field])) {
$errors[$field][] = $rule['message'] ?: "The field {$field} is invalid.";
}
}
}
return $errors;
}
public function assert(array $data) {
$errors = $this->validate($data);
if (!empty($errors)) {
throw new ValidationException($errors);
}
}
// Static helper methods for common validations
public static function required() {
return function ($value) {
return !empty($value);
};
}
public static function email() {
return function ($value) {
return filter_var($value, FILTER_VALIDATE_EMAIL) !== false;
};
}
public static function minLength($length) {
return function ($value) use ($length) {
return mb_strlen($value) >= $length;
};
}
public static function maxLength($length) {
return function ($value) use ($length) {
return mb_strlen($value) <= $length;
};
}
public static function regex($pattern) {
return function ($value) use ($pattern) {
return preg_match($pattern, $value) === 1;
};
}
public static function number() {
return function ($value) {
return filter_var($value, FILTER_VALIDATE_FLOAT) !== false;
};
}
}

@ -3,7 +3,7 @@ const { fontFamily } = require("tailwindcss/defaultTheme")
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: ["class"],
content: ["./src/**/*.{html,js,php,twig}","./public/**/*.{html,js,php}"],
content: ["./src/**/*.{html,js,php,twig,html.twig}","./public/**/*.{html,js,php}"],
theme: {
container: {
center: true,

@ -0,0 +1,7 @@
language: php
php:
- 5.3
- 5.4
- 5.5
script: phpunit --coverage-text ./

@ -0,0 +1,270 @@
<?php
class AltoRouter {
protected $routes = array();
protected $namedRoutes = array();
protected $basePath = '';
protected $matchTypes = array(
'i' => '[0-9]++',
'a' => '[0-9A-Za-z]++',
'h' => '[0-9A-Fa-f]++',
'*' => '.+?',
'**' => '.++',
'' => '[^/\.]++'
);
/**
* Create router in one call from config.
*
* @param array $routes
* @param string $basePath
* @param array $matchTypes
*/
public function __construct( $routes = array(), $basePath = '', $matchTypes = array() ) {
$this->addRoutes($routes);
$this->setBasePath($basePath);
$this->addMatchTypes($matchTypes);
}
/**
* Add multiple routes at once from array in the following format:
*
* $routes = array(
* array($method, $route, $target, $name)
* );
*
* @param array $routes
* @return void
* @author Koen Punt
*/
public function addRoutes($routes){
if(!is_array($routes) && !$routes instanceof Traversable) {
throw new \Exception('Routes should be an array or an instance of Traversable');
}
foreach($routes as $route) {
call_user_func_array(array($this, 'map'), $route);
}
}
/**
* Set the base path.
* Useful if you are running your application from a subdirectory.
*/
public function setBasePath($basePath) {
$this->basePath = $basePath;
}
/**
* Add named match types. It uses array_merge so keys can be overwritten.
*
* @param array $matchTypes The key is the name and the value is the regex.
*/
public function addMatchTypes($matchTypes) {
$this->matchTypes = array_merge($this->matchTypes, $matchTypes);
}
/**
* Map a route to a target
*
* @param string $method One of 4 HTTP Methods, or a pipe-separated list of multiple HTTP Methods (GET|POST|PUT|DELETE)
* @param string $route The route regex, custom regex must start with an @. You can use multiple pre-set regex filters, like [i:id]
* @param mixed $target The target where this route should point to. Can be anything.
* @param string $name Optional name of this route. Supply if you want to reverse route this url in your application.
*/
public function map($method, $route, $target, $name = null) {
$this->routes[] = array($method, $route, $target, $name);
if($name) {
if(isset($this->namedRoutes[$name])) {
throw new \Exception("Can not redeclare route '{$name}'");
} else {
$this->namedRoutes[$name] = $route;
}
}
return;
}
/**
* Reversed routing
*
* Generate the URL for a named route. Replace regexes with supplied parameters
*
* @param string $routeName The name of the route.
* @param array @params Associative array of parameters to replace placeholders with.
* @return string The URL of the route with named parameters in place.
*/
public function generate($routeName, array $params = array()) {
// Check if named route exists
if(!isset($this->namedRoutes[$routeName])) {
throw new \Exception("Route '{$routeName}' does not exist.");
}
// Replace named parameters
$route = $this->namedRoutes[$routeName];
// prepend base path to route url again
$url = $this->basePath . $route;
if (preg_match_all('`(/|\.|)\[([^:\]]*+)(?::([^:\]]*+))?\](\?|)`', $route, $matches, PREG_SET_ORDER)) {
foreach($matches as $match) {
list($block, $pre, $type, $param, $optional) = $match;
if ($pre) {
$block = substr($block, 1);
}
if(isset($params[$param])) {
$url = str_replace($block, $params[$param], $url);
} elseif ($optional) {
$url = str_replace($pre . $block, '', $url);
}
}
}
return $url;
}
/**
* Match a given Request Url against stored routes
* @param string $requestUrl
* @param string $requestMethod
* @return array|boolean Array with route information on success, false on failure (no match).
*/
public function match($requestUrl = null, $requestMethod = null) {
$params = array();
$match = false;
// set Request Url if it isn't passed as parameter
if($requestUrl === null) {
$requestUrl = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/';
}
// strip base path from request url
$requestUrl = substr($requestUrl, strlen($this->basePath));
// Strip query string (?a=b) from Request Url
if (($strpos = strpos($requestUrl, '?')) !== false) {
$requestUrl = substr($requestUrl, 0, $strpos);
}
// set Request Method if it isn't passed as a parameter
if($requestMethod === null) {
$requestMethod = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET';
}
// Force request_order to be GP
// http://www.mail-archive.com/internals@lists.php.net/msg33119.html
$_REQUEST = array_merge($_GET, $_POST);
foreach($this->routes as $handler) {
list($method, $_route, $target, $name) = $handler;
$methods = explode('|', $method);
$method_match = false;
// Check if request method matches. If not, abandon early. (CHEAP)
foreach($methods as $method) {
if (strcasecmp($requestMethod, $method) === 0) {
$method_match = true;
break;
}
}
// Method did not match, continue to next route.
if(!$method_match) continue;
// Check for a wildcard (matches all)
if ($_route === '*') {
$match = true;
} elseif (isset($_route[0]) && $_route[0] === '@') {
$match = preg_match('`' . substr($_route, 1) . '`u', $requestUrl, $params);
} else {
$route = null;
$regex = false;
$j = 0;
$n = isset($_route[0]) ? $_route[0] : null;
$i = 0;
// Find the longest non-regex substring and match it against the URI
while (true) {
if (!isset($_route[$i])) {
break;
} elseif (false === $regex) {
$c = $n;
$regex = $c === '[' || $c === '(' || $c === '.';
if (false === $regex && false !== isset($_route[$i+1])) {
$n = $_route[$i + 1];
$regex = $n === '?' || $n === '+' || $n === '*' || $n === '{';
}
if (false === $regex && $c !== '/' && (!isset($requestUrl[$j]) || $c !== $requestUrl[$j])) {
continue 2;
}
$j++;
}
$route .= $_route[$i++];
}
$regex = $this->compileRoute($route);
$match = preg_match($regex, $requestUrl, $params);
}
if(($match == true || $match > 0)) {
if($params) {
foreach($params as $key => $value) {
if(is_numeric($key)) unset($params[$key]);
}
}
return array(
'target' => $target,
'params' => $params,
'name' => $name
);
}
}
return false;
}
/**
* Compile the regex for a given route (EXPENSIVE)
*/
private function compileRoute($route) {
if (preg_match_all('`(/|\.|)\[([^:\]]*+)(?::([^:\]]*+))?\](\?|)`', $route, $matches, PREG_SET_ORDER)) {
$matchTypes = $this->matchTypes;
foreach($matches as $match) {
list($block, $pre, $type, $param, $optional) = $match;
if (isset($matchTypes[$type])) {
$type = $matchTypes[$type];
}
if ($pre === '.') {
$pre = '\.';
}
//Older versions of PCRE require the 'P' in (?P<named>)
$pattern = '(?:'
. ($pre !== '' ? $pre : null)
. '('
. ($param !== '' ? "?P<$param>" : null)
. $type
. '))'
. ($optional !== '' ? '?' : null);
$route = str_replace($block, $pattern, $route);
}
}
return "`^$route$`u";
}
}

@ -0,0 +1,423 @@
<?php
require 'AltoRouter.php';
class AltoRouterDebug extends AltoRouter{
public function getNamedRoutes(){
return $this->namedRoutes;
}
public function getRoutes(){
return $this->routes;
}
public function getBasePath(){
return $this->basePath;
}
}
class SimpleTraversable implements Iterator{
protected $_position = 0;
protected $_data = array(
array('GET', '/foo', 'foo_action', null),
array('POST', '/bar', 'bar_action', 'second_route')
);
public function current(){
return $this->_data[$this->_position];
}
public function key(){
return $this->_position;
}
public function next(){
++$this->_position;
}
public function rewind(){
$this->_position = 0;
}
public function valid(){
return isset($this->_data[$this->_position]);
}
}
/**
* Generated by PHPUnit_SkeletonGenerator 1.2.1 on 2013-07-14 at 17:47:46.
*/
class AltoRouterTest extends PHPUnit_Framework_TestCase
{
/**
* @var AltoRouter
*/
protected $router;
/**
* Sets up the fixture, for example, opens a network connection.
* This method is called before a test is executed.
*/
protected function setUp()
{
$this->router = new AltoRouterDebug;
}
/**
* Tears down the fixture, for example, closes a network connection.
* This method is called after a test is executed.
*/
protected function tearDown()
{
}
/**
* @covers AltoRouter::addRoutes
*/
public function testAddRoutes()
{
$method = 'POST';
$route = '/[:controller]/[:action]';
$target = function(){};
$this->router->addRoutes(array(
array($method, $route, $target),
array($method, $route, $target, 'second_route')
));
$routes = $this->router->getRoutes();
$this->assertEquals(array($method, $route, $target, null), $routes[0]);
$this->assertEquals(array($method, $route, $target, 'second_route'), $routes[1]);
}
/**
* @covers AltoRouter::addRoutes
*/
public function testAddRoutesAcceptsTraverable()
{
$traversable = new SimpleTraversable();
$this->router->addRoutes($traversable);
$traversable->rewind();
$first = $traversable->current();
$traversable->next();
$second = $traversable->current();
$routes = $this->router->getRoutes();
$this->assertEquals($first, $routes[0]);
$this->assertEquals($second, $routes[1]);
}
/**
* @covers AltoRouter::addRoutes
* @expectedException Exception
*/
public function testAddRoutesThrowsExceptionOnInvalidArgument()
{
$this->router->addRoutes(new stdClass);
}
/**
* @covers AltoRouter::setBasePath
*/
public function testSetBasePath()
{
$basePath = $this->router->setBasePath('/some/path');
$this->assertEquals('/some/path', $this->router->getBasePath());
$basePath = $this->router->setBasePath('/some/path');
$this->assertEquals('/some/path', $this->router->getBasePath());
}
/**
* @covers AltoRouter::map
*/
public function testMap()
{
$method = 'POST';
$route = '/[:controller]/[:action]';
$target = function(){};
$this->router->map($method, $route, $target);
$routes = $this->router->getRoutes();
$this->assertEquals(array($method, $route, $target, null), $routes[0]);
}
/**
* @covers AltoRouter::map
*/
public function testMapWithName()
{
$method = 'POST';
$route = '/[:controller]/[:action]';
$target = function(){};
$name = 'myroute';
$this->router->map($method, $route, $target, $name);
$routes = $this->router->getRoutes();
$this->assertEquals(array($method, $route, $target, $name), $routes[0]);
$named_routes = $this->router->getNamedRoutes();
$this->assertEquals($route, $named_routes[$name]);
try{
$this->router->map($method, $route, $target, $name);
$this->fail('Should not be able to add existing named route');
}catch(Exception $e){
$this->assertEquals("Can not redeclare route '{$name}'", $e->getMessage());
}
}
/**
* @covers AltoRouter::generate
*/
public function testGenerate()
{
$params = array(
'controller' => 'test',
'action' => 'someaction'
);
$this->router->map('GET', '/[:controller]/[:action]', function(){}, 'foo_route');
$this->assertEquals('/test/someaction',
$this->router->generate('foo_route', $params));
$params = array(
'controller' => 'test',
'action' => 'someaction',
'type' => 'json'
);
$this->assertEquals('/test/someaction',
$this->router->generate('foo_route', $params));
}
public function testGenerateWithOptionalUrlParts()
{
$this->router->map('GET', '/[:controller]/[:action].[:type]?', function(){}, 'bar_route');
$params = array(
'controller' => 'test',
'action' => 'someaction'
);
$this->assertEquals('/test/someaction',
$this->router->generate('bar_route', $params));
$params = array(
'controller' => 'test',
'action' => 'someaction',
'type' => 'json'
);
$this->assertEquals('/test/someaction.json',
$this->router->generate('bar_route', $params));
}
public function testGenerateWithNonexistingRoute()
{
try{
$this->router->generate('nonexisting_route');
$this->fail('Should trigger an exception on nonexisting named route');
}catch(Exception $e){
$this->assertEquals("Route 'nonexisting_route' does not exist.", $e->getMessage());
}
}
/**
* @covers AltoRouter::match
* @covers AltoRouter::compileRoute
*/
public function testMatch()
{
$this->router->map('GET', '/foo/[:controller]/[:action]', 'foo_action', 'foo_route');
$this->assertEquals(array(
'target' => 'foo_action',
'params' => array(
'controller' => 'test',
'action' => 'do'
),
'name' => 'foo_route'
), $this->router->match('/foo/test/do', 'GET'));
$this->assertFalse($this->router->match('/foo/test/do', 'POST'));
$this->assertEquals(array(
'target' => 'foo_action',
'params' => array(
'controller' => 'test',
'action' => 'do'
),
'name' => 'foo_route'
), $this->router->match('/foo/test/do?param=value', 'GET'));
}
public function testMatchWithFixedParamValues()
{
$this->router->map('POST','/users/[i:id]/[delete|update:action]', 'usersController#doAction', 'users_do');
$this->assertEquals(array(
'target' => 'usersController#doAction',
'params' => array(
'id' => 1,
'action' => 'delete'
),
'name' => 'users_do'
), $this->router->match('/users/1/delete', 'POST'));
$this->assertFalse($this->router->match('/users/1/delete', 'GET'));
$this->assertFalse($this->router->match('/users/abc/delete', 'POST'));
$this->assertFalse($this->router->match('/users/1/create', 'GET'));
}
public function testMatchWithServerVars()
{
$this->router->map('GET', '/foo/[:controller]/[:action]', 'foo_action', 'foo_route');
$_SERVER['REQUEST_URI'] = '/foo/test/do';
$_SERVER['REQUEST_METHOD'] = 'GET';
$this->assertEquals(array(
'target' => 'foo_action',
'params' => array(
'controller' => 'test',
'action' => 'do'
),
'name' => 'foo_route'
), $this->router->match());
}
public function testMatchWithOptionalUrlParts()
{
$this->router->map('GET', '/bar/[:controller]/[:action].[:type]?', 'bar_action', 'bar_route');
$this->assertEquals(array(
'target' => 'bar_action',
'params' => array(
'controller' => 'test',
'action' => 'do',
'type' => 'json'
),
'name' => 'bar_route'
), $this->router->match('/bar/test/do.json', 'GET'));
}
public function testMatchWithWildcard()
{
$this->router->map('GET', '/a', 'foo_action', 'foo_route');
$this->router->map('GET', '*', 'bar_action', 'bar_route');
$this->assertEquals(array(
'target' => 'bar_action',
'params' => array(),
'name' => 'bar_route'
), $this->router->match('/everything', 'GET'));
}
public function testMatchWithCustomRegexp()
{
$this->router->map('GET', '@^/[a-z]*$', 'bar_action', 'bar_route');
$this->assertEquals(array(
'target' => 'bar_action',
'params' => array(),
'name' => 'bar_route'
), $this->router->match('/everything', 'GET'));
$this->assertFalse($this->router->match('/some-other-thing', 'GET'));
}
public function testMatchWithUnicodeRegex()
{
$pattern = '/(?<path>[^';
// Arabic characters
$pattern .= '\x{0600}-\x{06FF}';
$pattern .= '\x{FB50}-\x{FDFD}';
$pattern .= '\x{FE70}-\x{FEFF}';
$pattern .= '\x{0750}-\x{077F}';
// Alphanumeric, /, _, - and space characters
$pattern .= 'a-zA-Z0-9\/_-\s';
// 'ZERO WIDTH NON-JOINER'
$pattern .= '\x{200C}';
$pattern .= ']+)';
$this->router->map('GET', '@' . $pattern, 'unicode_action', 'unicode_route');
$this->assertEquals(array(
'target' => 'unicode_action',
'name' => 'unicode_route',
'params' => array(
'path' => '大家好'
)
), $this->router->match('/大家好', 'GET'));
$this->assertFalse($this->router->match('/﷽‎', 'GET'));
}
/**
* @covers AltoRouter::addMatchTypes
*/
public function testMatchWithCustomNamedRegex()
{
$this->router->addMatchTypes(array('cId' => '[a-zA-Z]{2}[0-9](?:_[0-9]++)?'));
$this->router->map('GET', '/bar/[cId:customId]', 'bar_action', 'bar_route');
$this->assertEquals(array(
'target' => 'bar_action',
'params' => array(
'customId' => 'AB1',
),
'name' => 'bar_route'
), $this->router->match('/bar/AB1', 'GET'));
$this->assertEquals(array(
'target' => 'bar_action',
'params' => array(
'customId' => 'AB1_0123456789',
),
'name' => 'bar_route'
), $this->router->match('/bar/AB1_0123456789', 'GET'));
$this->assertFalse($this->router->match('/some-other-thing', 'GET'));
}
public function testMatchWithCustomNamedUnicodeRegex()
{
$pattern = '[^';
// Arabic characters
$pattern .= '\x{0600}-\x{06FF}';
$pattern .= '\x{FB50}-\x{FDFD}';
$pattern .= '\x{FE70}-\x{FEFF}';
$pattern .= '\x{0750}-\x{077F}';
$pattern .= ']+';
$this->router->addMatchTypes(array('nonArabic' => $pattern));
$this->router->map('GET', '/bar/[nonArabic:string]', 'non_arabic_action', 'non_arabic_route');
$this->assertEquals(array(
'target' => 'non_arabic_action',
'name' => 'non_arabic_route',
'params' => array(
'string' => 'some-path'
)
), $this->router->match('/bar/some-path', 'GET'));
$this->assertFalse($this->router->match('/﷽‎', 'GET'));
}
}

@ -0,0 +1,92 @@
# AltoRouter [![Build Status](https://api.travis-ci.org/dannyvankooten/AltoRouter.png)](http://travis-ci.org/dannyvankooten/AltoRouter)
AltoRouter is a small but powerful routing class for PHP 5.3+, heavily inspired by [klein.php](https://github.com/chriso/klein.php/).
* Dynamic routing with named parameters
* Reversed routing
* Flexible regular expression routing (inspired by [Sinatra](http://www.sinatrarb.com/))
* Custom regexes
## Getting started
1. PHP 5.3.x is required
2. Install AltoRouter using Composer or manually
2. Setup URL rewriting so that all requests are handled by **index.php**
3. Create an instance of AltoRouter, map your routes and match a request.
4. Have a look at the basic example in the `examples` directory for a better understanding on how to use AltoRouter.
## Routing
```php
$router = new AltoRouter();
$router->setBasePath('/AltoRouter'); // (optional) the subdir AltoRouter lives in
// mapping routes
$router->map('GET|POST','/', 'home#index', 'home');
$router->map('GET','/users', array('c' => 'UserController', 'a' => 'ListAction'));
$router->map('GET','/users/[i:id]', 'users#show', 'users_show');
$router->map('POST','/users/[i:id]/[delete|update:action]', 'usersController#doAction', 'users_do');
// reversed routing
$router->generate('users_show', array('id' => 5));
```
**You can use the following limits on your named parameters. AltoRouter will create the correct regexes for you.**
```php
* // Match all request URIs
[i] // Match an integer
[i:id] // Match an integer as 'id'
[a:action] // Match alphanumeric characters as 'action'
[h:key] // Match hexadecimal characters as 'key'
[:action] // Match anything up to the next / or end of the URI as 'action'
[create|edit:action] // Match either 'create' or 'edit' as 'action'
[*] // Catch all (lazy, stops at the next trailing slash)
[*:trailing] // Catch all as 'trailing' (lazy)
[**:trailing] // Catch all (possessive - will match the rest of the URI)
.[:format]? // Match an optional parameter 'format' - a / or . before the block is also optional
```
**Some more complicated examples**
```php
@/(?[A-Za-z]{2}_[A-Za-z]{2})$ // custom regex, matches language codes like "en_us" etc.
/posts/[*:title][i:id] // Matches "/posts/this-is-a-title-123"
/output.[xml|json:format]? // Matches "/output", "output.xml", "output.json"
/[:controller]?/[:action]? // Matches the typical /controller/action format
```
**The character before the colon (the 'match type') is a shortcut for one of the following regular expressions**
```php
'i' => '[0-9]++'
'a' => '[0-9A-Za-z]++'
'h' => '[0-9A-Fa-f]++'
'*' => '.+?'
'**' => '.++'
'' => '[^/\.]++'
```
**New match types can be added using the `addMatchTypes()` method**
```php
$router->addMatchTypes(array('cId' => '[a-zA-Z]{2}[0-9](?:_[0-9]++)?'));
```
## Contributors
- [Danny van Kooten](https://github.com/dannyvankooten)
- [Koen Punt](https://github.com/koenpunt)
- [John Long](https://github.com/adduc)
- [Niahoo Osef](https://github.com/niahoo)
## License
(MIT License)
Copyright (c) 2012-2013 Danny van Kooten <hi@dannyvankooten.com>
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.

@ -0,0 +1,28 @@
{
"name": "altorouter/altorouter",
"description": "A lightning fast router for PHP",
"keywords": ["router", "routing", "lightweight"],
"homepage": "https://github.com/dannyvankooten/AltoRouter",
"license": "MIT",
"authors": [
{
"name": "Danny van Kooten",
"email": "dannyvankooten@gmail.com",
"homepage": "http://dannyvankooten.com/"
},
{
"name": "Koen Punt",
"homepage": "https://github.com/koenpunt"
},
{
"name": "niahoo",
"homepage": "https://github.com/niahoo"
}
],
"require": {
"php": ">=5.3.0"
},
"autoload": {
"classmap": ["AltoRouter.php"]
}
}

@ -0,0 +1,3 @@
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . index.php [L]

@ -0,0 +1,27 @@
<?php
require '../../AltoRouter.php';
$router = new AltoRouter();
$router->setBasePath('/AltoRouter/examples/basic');
$router->map('GET|POST','/', 'home#index', 'home');
$router->map('GET','/users/', array('c' => 'UserController', 'a' => 'ListAction'));
$router->map('GET','/users/[i:id]', 'users#show', 'users_show');
$router->map('POST','/users/[i:id]/[delete|update:action]', 'usersController#doAction', 'users_do');
// match current request
$match = $router->match();
?>
<h1>AltoRouter</h1>
<h3>Current request: </h3>
<pre>
Target: <?php var_dump($match['target']); ?>
Params: <?php var_dump($match['params']); ?>
Name: <?php var_dump($match['name']); ?>
</pre>
<h3>Try these requests: </h3>
<p><a href="<?php echo $router->generate('home'); ?>">GET <?php echo $router->generate('home'); ?></a></p>
<p><a href="<?php echo $router->generate('users_show', array('id' => 5)); ?>">GET <?php echo $router->generate('users_show', array('id' => 5)); ?></a></p>
<p><form action="<?php echo $router->generate('users_do', array('id' => 10, 'action' => 'update')); ?>" method="post"><button type="submit"><?php echo $router->generate('users_do', array('id' => 10, 'action' => 'update')); ?></button></form></p>

@ -6,6 +6,7 @@ $vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'AltoRouter' => $vendorDir . '/altorouter/altorouter/AltoRouter.php',
'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
'PHPUnit\\Exception' => $vendorDir . '/phpunit/phpunit/src/Exception.php',

@ -161,6 +161,7 @@ class ComposerStaticInitb084bad56d99d613841073027e5f5e7e
);
public static $classMap = array (
'AltoRouter' => __DIR__ . '/..' . '/altorouter/altorouter/AltoRouter.php',
'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
'PHPUnit\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Exception.php',

@ -1,5 +1,63 @@
{
"packages": [
{
"name": "altorouter/altorouter",
"version": "v1.1.0",
"version_normalized": "1.1.0.0",
"source": {
"type": "git",
"url": "https://github.com/dannyvankooten/AltoRouter.git",
"reference": "09d9d946c546bae6d22a7654cdb3b825ffda54b4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dannyvankooten/AltoRouter/zipball/09d9d946c546bae6d22a7654cdb3b825ffda54b4",
"reference": "09d9d946c546bae6d22a7654cdb3b825ffda54b4",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2014-04-16T09:44:40+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"classmap": [
"AltoRouter.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Danny van Kooten",
"email": "dannyvankooten@gmail.com",
"homepage": "http://dannyvankooten.com/"
},
{
"name": "Koen Punt",
"homepage": "https://github.com/koenpunt"
},
{
"name": "niahoo",
"homepage": "https://github.com/niahoo"
}
],
"description": "A lightning fast router for PHP",
"homepage": "https://github.com/dannyvankooten/AltoRouter",
"keywords": [
"lightweight",
"router",
"routing"
],
"support": {
"issues": "https://github.com/dannyvankooten/AltoRouter/issues",
"source": "https://github.com/dannyvankooten/AltoRouter/tree/master"
},
"install-path": "../altorouter/altorouter"
},
{
"name": "doctrine/instantiator",
"version": "1.5.0",

@ -3,13 +3,22 @@
'name' => 'hearttrack/package',
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => 'd4345678992503b9eb56ef4afd00ff13f5d7531a',
'reference' => '153418181ec743052fd1f05b54d45da0f66d37ef',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev' => true,
),
'versions' => array(
'altorouter/altorouter' => array(
'pretty_version' => 'v1.1.0',
'version' => '1.1.0.0',
'reference' => '09d9d946c546bae6d22a7654cdb3b825ffda54b4',
'type' => 'library',
'install_path' => __DIR__ . '/../altorouter/altorouter',
'aliases' => array(),
'dev_requirement' => false,
),
'doctrine/instantiator' => array(
'pretty_version' => '1.5.0',
'version' => '1.5.0.0',
@ -31,7 +40,7 @@
'hearttrack/package' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => 'd4345678992503b9eb56ef4afd00ff13f5d7531a',
'reference' => '153418181ec743052fd1f05b54d45da0f66d37ef',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),

Loading…
Cancel
Save