diff --git a/Documents/Diagramme/Annalyse/MLD.png b/Documents/Diagramme/Annalyse/MLD.png new file mode 100644 index 00000000..02e33c6c Binary files /dev/null and b/Documents/Diagramme/Annalyse/MLD.png differ diff --git a/Sources/composer.json b/Sources/composer.json index 7373dd27..a0ad4bd1 100644 --- a/Sources/composer.json +++ b/Sources/composer.json @@ -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": "*" }, diff --git a/Sources/composer.lock b/Sources/composer.lock index cc0599dd..7fff66e3 100644 --- a/Sources/composer.lock +++ b/Sources/composer.lock @@ -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", diff --git a/Sources/public/index.php b/Sources/public/index.php index bd6fe353..292f954c 100644 --- a/Sources/public/index.php +++ b/Sources/public/index.php @@ -1,9 +1,30 @@ (); + $app = $appFactory->registerService('',''); + + $appFactory.errorProvider(class:: or port) + + // connexion string + +// var connectionString = builder.Configuration.GetConnectionString("LolDatabase"); +// builder.Services.AddDbContext(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(); } diff --git a/Sources/src/app/App.php b/Sources/src/app/App.php new file mode 100644 index 00000000..48929fbe --- /dev/null +++ b/Sources/src/app/App.php @@ -0,0 +1,84 @@ +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; + // } +} + +?> \ No newline at end of file diff --git a/Sources/src/app/AppCreator.php b/Sources/src/app/AppCreator.php new file mode 100644 index 00000000..7d26082c --- /dev/null +++ b/Sources/src/app/AppCreator.php @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/Sources/src/app/DI/Container.php b/Sources/src/app/DI/Container.php new file mode 100644 index 00000000..e2e9e464 --- /dev/null +++ b/Sources/src/app/DI/Container.php @@ -0,0 +1,108 @@ +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; + } +} diff --git a/Sources/src/app/HttpClient.php b/Sources/src/app/HttpClient.php new file mode 100644 index 00000000..50307c0a --- /dev/null +++ b/Sources/src/app/HttpClient.php @@ -0,0 +1,65 @@ +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"; + \ No newline at end of file diff --git a/Sources/src/app/controller/AthleteController.php b/Sources/src/app/controller/AthleteController.php new file mode 100644 index 00000000..b6d4b92c --- /dev/null +++ b/Sources/src/app/controller/AthleteController.php @@ -0,0 +1,45 @@ +container = $di; + } + + // private ILogger $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)); + } + +} + + +?> \ No newline at end of file diff --git a/Sources/src/app/controller/FrontController.php b/Sources/src/app/controller/FrontController.php new file mode 100644 index 00000000..4b1d9925 --- /dev/null +++ b/Sources/src/app/controller/FrontController.php @@ -0,0 +1,63 @@ +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"; + } +} + +?> + + + + \ No newline at end of file diff --git a/Sources/src/app/router/Route.php b/Sources/src/app/router/Route.php new file mode 100644 index 00000000..18d4700e --- /dev/null +++ b/Sources/src/app/router/Route.php @@ -0,0 +1,22 @@ +path = $params; + $this->callable = $callable; + $this->name = $name; + } + + + + +} diff --git a/Sources/src/app/router/Router.php b/Sources/src/app/router/Router.php new file mode 100644 index 00000000..abbcbb5b --- /dev/null +++ b/Sources/src/app/router/Router.php @@ -0,0 +1,77 @@ + + + + + +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 + + +} + + +?> \ No newline at end of file diff --git a/Sources/src/app/router/exception/HttpException.php b/Sources/src/app/router/exception/HttpException.php new file mode 100644 index 00000000..4113c58d --- /dev/null +++ b/Sources/src/app/router/exception/HttpException.php @@ -0,0 +1,33 @@ +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; + } +} \ No newline at end of file diff --git a/Sources/src/app/router/exception/NotFoundException.php b/Sources/src/app/router/exception/NotFoundException.php new file mode 100644 index 00000000..bb5a8161 --- /dev/null +++ b/Sources/src/app/router/exception/NotFoundException.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + + +/** + * @author Fabien Potencier + */ +class NotFoundHttpException extends HttpException +{ + public function __construct(string $message = '', \Throwable $previous = null, int $code = 0, array $headers = []) + { + parent::__construct(404, $message, $previous, $headers, $code); + } +} \ No newline at end of file diff --git a/Sources/src/app/router/exception/ValidationException.php b/Sources/src/app/router/exception/ValidationException.php new file mode 100644 index 00000000..73399f64 --- /dev/null +++ b/Sources/src/app/router/exception/ValidationException.php @@ -0,0 +1,24 @@ +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)); + } +} diff --git a/Sources/src/app/router/middleware/HttpValidationMiddleware.php b/Sources/src/app/router/middleware/HttpValidationMiddleware.php new file mode 100644 index 00000000..c6da37dd --- /dev/null +++ b/Sources/src/app/router/middleware/HttpValidationMiddleware.php @@ -0,0 +1,36 @@ +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 +]; \ No newline at end of file diff --git a/Sources/src/app/router/middleware/IHttpMiddleware.php b/Sources/src/app/router/middleware/IHttpMiddleware.php new file mode 100644 index 00000000..8273d43d --- /dev/null +++ b/Sources/src/app/router/middleware/IHttpMiddleware.php @@ -0,0 +1,5 @@ +getMethod()}, URI: {$request->getRequestUri()}\n"; + return parent::handle($request, $next); + } +} \ No newline at end of file diff --git a/Sources/src/app/router/middleware/Middleware.php b/Sources/src/app/router/middleware/Middleware.php new file mode 100644 index 00000000..20465660 --- /dev/null +++ b/Sources/src/app/router/middleware/Middleware.php @@ -0,0 +1,19 @@ +next = $nextMiddleware; + } + + public function handle(IRequest $request, callable $next) { + if ($this->next !== null) { + return $this->next->handle($request, $next); + } + return $next($request); + } +} diff --git a/Sources/src/app/router/request/ContentStrategy.php b/Sources/src/app/router/request/ContentStrategy.php new file mode 100644 index 00000000..0d0f4a4b --- /dev/null +++ b/Sources/src/app/router/request/ContentStrategy.php @@ -0,0 +1,5 @@ + 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; + } +} \ No newline at end of file diff --git a/Sources/src/app/router/request/FormContentStrategy.php b/Sources/src/app/router/request/FormContentStrategy.php new file mode 100644 index 00000000..015b6d52 --- /dev/null +++ b/Sources/src/app/router/request/FormContentStrategy.php @@ -0,0 +1,8 @@ +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; + } + +} \ No newline at end of file diff --git a/Sources/src/app/router/request/RequestFactory.php b/Sources/src/app/router/request/RequestFactory.php new file mode 100644 index 00000000..ade581b3 --- /dev/null +++ b/Sources/src/app/router/request/RequestFactory.php @@ -0,0 +1,25 @@ + $value) { + if (substr($key, 0, 5) === 'HTTP_') { + $header = str_replace(' ', '-', ucwords(str_replace('_', ' ', strtolower(substr($key, 5))))); + $headers[$header] = $value; + } + } + return $headers; + } +} \ No newline at end of file diff --git a/Sources/src/app/router/responce/IResponce.php b/Sources/src/app/router/responce/IResponce.php new file mode 100644 index 00000000..87b9433a --- /dev/null +++ b/Sources/src/app/router/responce/IResponce.php @@ -0,0 +1,10 @@ + + +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; + } +} + + +?> \ No newline at end of file diff --git a/Sources/src/app/views/form/AbstractFiled.php b/Sources/src/app/views/form/AbstractFiled.php new file mode 100644 index 00000000..e69de29b diff --git a/Sources/src/app/views/form/AbstractForm.php b/Sources/src/app/views/form/AbstractForm.php new file mode 100644 index 00000000..e69de29b diff --git a/Sources/src/app/views/form/FieldFactory.php b/Sources/src/app/views/form/FieldFactory.php new file mode 100644 index 00000000..e69de29b diff --git a/Sources/src/app/views/form/FormBuilder.php b/Sources/src/app/views/form/FormBuilder.php new file mode 100644 index 00000000..e69de29b diff --git a/Sources/src/app/views/form/IField.php b/Sources/src/app/views/form/IField.php new file mode 100644 index 00000000..582744a2 --- /dev/null +++ b/Sources/src/app/views/form/IField.php @@ -0,0 +1,5 @@ + +
+ + logo + HearTrack + +
+
+

+ Sign in to your account +

+
+
+ + +
+
+ + +
+
+
+
+ +
+
+ +
+
+ Forgot password? +
+ +

+ Don’t have an account yet? Sign up +

+
+
+
+
+ \ No newline at end of file diff --git a/Sources/src/app/views/templates/login/index.html.twig b/Sources/src/app/views/templates/login/index.html.twig new file mode 100644 index 00000000..b6419ee2 --- /dev/null +++ b/Sources/src/app/views/templates/login/index.html.twig @@ -0,0 +1,23 @@ +{# templates/login/index.html.twig #} +{% extends 'base.html.twig' %} + +{# ... #} + +{% block body %} + {% if error %} +
{{ error.messageKey|trans(error.messageData, 'security') }}
+ {% endif %} + +
+ + + + + + + {# If you want to control the URL the user is redirected to on success + #} + + +
+{% endblock %} \ No newline at end of file diff --git a/Sources/src/console/Console.php b/Sources/src/console/Console.php index 889bff35..f24fe0a6 100755 --- a/Sources/src/console/Console.php +++ b/Sources/src/console/Console.php @@ -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: "; } diff --git a/Sources/src/console/ConsolePoo.php b/Sources/src/console/ConsolePoo.php new file mode 100644 index 00000000..e69de29b diff --git a/Sources/src/console/Poo/Application.php b/Sources/src/console/Poo/Application.php new file mode 100644 index 00000000..d3b4a441 --- /dev/null +++ b/Sources/src/console/Poo/Application.php @@ -0,0 +1,21 @@ +dataManager = $dataManager; + $this->currentMenu = new AuthMenu(); + } + public function run() + { + while (true) { + if (!$this->authenticationController->isLoggedIn()) { + $this->authenticationController->displayAuthMenu(); + } else { + $this->menuController->displayMainMenu(); + } + } + } +} diff --git a/Sources/src/console/Poo/AuthMenu.php b/Sources/src/console/Poo/AuthMenu.php new file mode 100644 index 00000000..2be196a7 --- /dev/null +++ b/Sources/src/console/Poo/AuthMenu.php @@ -0,0 +1,16 @@ +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: "; + } +} diff --git a/Sources/src/console/Poo/ClearScreenCommand.php b/Sources/src/console/Poo/ClearScreenCommand.php new file mode 100644 index 00000000..323052d9 --- /dev/null +++ b/Sources/src/console/Poo/ClearScreenCommand.php @@ -0,0 +1,8 @@ +clearScreenCommand = new ClearScreenCommand(); + } + + protected function clearScreen() + { + $this->clearScreenCommand->execute(); + } + + abstract public function display(); + +} diff --git a/Sources/src/data/core/Auth.php b/Sources/src/data/core/Auth.php new file mode 100644 index 00000000..a0503af6 --- /dev/null +++ b/Sources/src/data/core/Auth.php @@ -0,0 +1,57 @@ +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; + } +} diff --git a/Sources/src/data/model/Coach.php b/Sources/src/data/model/Coach.php index 9f470448..5f471303 100644 --- a/Sources/src/data/model/Coach.php +++ b/Sources/src/data/model/Coach.php @@ -1,8 +1,11 @@ \ No newline at end of file diff --git a/Sources/src/data/model/manager/AuthManager.php b/Sources/src/data/model/manager/AuthManager.php new file mode 100644 index 00000000..e69de29b diff --git a/Sources/src/data/model/manager/UserManager.php b/Sources/src/data/model/manager/UserManager.php index f426e5bf..a2417acc 100644 --- a/Sources/src/data/model/manager/UserManager.php +++ b/Sources/src/data/model/manager/UserManager.php @@ -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) { diff --git a/Sources/src/data/stub/service/AuthService.php b/Sources/src/data/stub/service/AuthService.php index 6f5fa8ef..1a3c7583 100644 --- a/Sources/src/data/stub/service/AuthService.php +++ b/Sources/src/data/stub/service/AuthService.php @@ -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 { diff --git a/Sources/src/shared/Validator.php b/Sources/src/shared/Validator.php new file mode 100644 index 00000000..77229848 --- /dev/null +++ b/Sources/src/shared/Validator.php @@ -0,0 +1,71 @@ +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; + }; + } + +} \ No newline at end of file diff --git a/Sources/tailwind.config.js b/Sources/tailwind.config.js index f5ab680c..874e2ff2 100644 --- a/Sources/tailwind.config.js +++ b/Sources/tailwind.config.js @@ -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, diff --git a/Sources/vendor/altorouter/altorouter/.travis.yml b/Sources/vendor/altorouter/altorouter/.travis.yml new file mode 100644 index 00000000..3bf31615 --- /dev/null +++ b/Sources/vendor/altorouter/altorouter/.travis.yml @@ -0,0 +1,7 @@ +language: php +php: + - 5.3 + - 5.4 + - 5.5 + +script: phpunit --coverage-text ./ diff --git a/Sources/vendor/altorouter/altorouter/AltoRouter.php b/Sources/vendor/altorouter/altorouter/AltoRouter.php new file mode 100644 index 00000000..67e76abc --- /dev/null +++ b/Sources/vendor/altorouter/altorouter/AltoRouter.php @@ -0,0 +1,270 @@ + '[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) + $pattern = '(?:' + . ($pre !== '' ? $pre : null) + . '(' + . ($param !== '' ? "?P<$param>" : null) + . $type + . '))' + . ($optional !== '' ? '?' : null); + + $route = str_replace($block, $pattern, $route); + } + + } + return "`^$route$`u"; + } +} diff --git a/Sources/vendor/altorouter/altorouter/AltoRouterTest.php b/Sources/vendor/altorouter/altorouter/AltoRouterTest.php new file mode 100644 index 00000000..2462cd82 --- /dev/null +++ b/Sources/vendor/altorouter/altorouter/AltoRouterTest.php @@ -0,0 +1,423 @@ +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 = '/(?[^'; + // 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')); + } +} diff --git a/Sources/vendor/altorouter/altorouter/README.md b/Sources/vendor/altorouter/altorouter/README.md new file mode 100644 index 00000000..cb2acb3f --- /dev/null +++ b/Sources/vendor/altorouter/altorouter/README.md @@ -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 + +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. diff --git a/Sources/vendor/altorouter/altorouter/composer.json b/Sources/vendor/altorouter/altorouter/composer.json new file mode 100644 index 00000000..58e86c4c --- /dev/null +++ b/Sources/vendor/altorouter/altorouter/composer.json @@ -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"] + } +} diff --git a/Sources/vendor/altorouter/altorouter/examples/basic/.htaccess b/Sources/vendor/altorouter/altorouter/examples/basic/.htaccess new file mode 100644 index 00000000..d7e13207 --- /dev/null +++ b/Sources/vendor/altorouter/altorouter/examples/basic/.htaccess @@ -0,0 +1,3 @@ +RewriteEngine On +RewriteCond %{REQUEST_FILENAME} !-f +RewriteRule . index.php [L] diff --git a/Sources/vendor/altorouter/altorouter/examples/basic/index.php b/Sources/vendor/altorouter/altorouter/examples/basic/index.php new file mode 100644 index 00000000..83266953 --- /dev/null +++ b/Sources/vendor/altorouter/altorouter/examples/basic/index.php @@ -0,0 +1,27 @@ +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(); +?> +

AltoRouter

+ +

Current request:

+
+	Target: 
+	Params: 
+	Name: 	
+
+ +

Try these requests:

+

GET generate('home'); ?>

+

GET generate('users_show', array('id' => 5)); ?>

+

diff --git a/Sources/vendor/composer/autoload_classmap.php b/Sources/vendor/composer/autoload_classmap.php index 46399bb5..b851eef9 100644 --- a/Sources/vendor/composer/autoload_classmap.php +++ b/Sources/vendor/composer/autoload_classmap.php @@ -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', diff --git a/Sources/vendor/composer/autoload_static.php b/Sources/vendor/composer/autoload_static.php index fef10b85..151d8c2a 100644 --- a/Sources/vendor/composer/autoload_static.php +++ b/Sources/vendor/composer/autoload_static.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', diff --git a/Sources/vendor/composer/installed.json b/Sources/vendor/composer/installed.json index 9da4e7aa..1151c322 100644 --- a/Sources/vendor/composer/installed.json +++ b/Sources/vendor/composer/installed.json @@ -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", diff --git a/Sources/vendor/composer/installed.php b/Sources/vendor/composer/installed.php index 770e13da..2de551b5 100644 --- a/Sources/vendor/composer/installed.php +++ b/Sources/vendor/composer/installed.php @@ -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(),