From 0e5e671423ff00f372107a83060fe9c5c2908c89 Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 14 Nov 2023 11:27:03 +0100 Subject: [PATCH] tmp --- Documents/Diagramme/Annalyse/MLD.png | Bin 0 -> 82890 bytes Sources/composer.json | 4 +- Sources/composer.lock | 57 ++- Sources/public/index.php | 31 +- Sources/src/app/App.php | 84 ++++ Sources/src/app/AppCreator.php | 13 + Sources/src/app/DI/Container.php | 108 +++++ Sources/src/app/HttpClient.php | 65 +++ .../src/app/controller/AthleteController.php | 45 ++ Sources/src/app/controller/BaseController.php | 101 +++++ .../src/app/controller/FrontController.php | 63 +++ Sources/src/app/router/Route.php | 22 + Sources/src/app/router/Router.php | 77 ++++ .../app/router/exception/HttpException.php | 33 ++ .../router/exception/NotFoundException.php | 22 + .../router/exception/ValidationException.php | 24 + .../middleware/HttpValidationMiddleware.php | 36 ++ .../app/router/middleware/IHttpMiddleware.php | 5 + .../router/middleware/LoggingMiddleware.php | 8 + .../src/app/router/middleware/Middleware.php | 19 + .../app/router/request/ContentStrategy.php | 5 + .../router/request/ContentStrategyFactory.php | 21 + .../router/request/FormContentStrategy.php | 8 + Sources/src/app/router/request/IRequest.php | 11 + .../router/request/JsonContentStrategy.php | 7 + Sources/src/app/router/request/Request.php | 43 ++ .../src/app/router/request/RequestFactory.php | 25 ++ Sources/src/app/router/responce/IResponce.php | 10 + Sources/src/app/router/responce/Response.php | 50 +++ Sources/src/app/views/form/AbstractFiled.php | 0 Sources/src/app/views/form/AbstractForm.php | 0 Sources/src/app/views/form/FieldFactory.php | 0 Sources/src/app/views/form/FormBuilder.php | 0 Sources/src/app/views/form/IField.php | 5 + Sources/src/app/views/form/IForm.php | 0 Sources/src/app/views/form/LoginForm.php | 0 Sources/src/app/views/form/TextField.php | 0 .../src/app/views/templates/login/index.html | 40 ++ .../app/views/templates/login/index.html.twig | 23 + Sources/src/console/Console.php | 2 +- Sources/src/console/ConsolePoo.php | 0 Sources/src/console/Poo/Application.php | 21 + Sources/src/console/Poo/AuthMenu.php | 16 + .../src/console/Poo/ClearScreenCommand.php | 8 + Sources/src/console/Poo/IPrinter.php | 4 + Sources/src/console/Poo/Menu.php | 18 + Sources/src/data/core/Auth.php | 57 +++ Sources/src/data/model/Coach.php | 3 + .../src/data/model/manager/AuthManager.php | 0 .../src/data/model/manager/UserManager.php | 2 + Sources/src/data/stub/service/AuthService.php | 1 + Sources/src/shared/Validator.php | 71 +++ Sources/tailwind.config.js | 2 +- .../vendor/altorouter/altorouter/.travis.yml | 7 + .../altorouter/altorouter/AltoRouter.php | 270 +++++++++++ .../altorouter/altorouter/AltoRouterTest.php | 423 ++++++++++++++++++ .../vendor/altorouter/altorouter/README.md | 92 ++++ .../altorouter/altorouter/composer.json | 28 ++ .../altorouter/examples/basic/.htaccess | 3 + .../altorouter/examples/basic/index.php | 27 ++ Sources/vendor/composer/autoload_classmap.php | 1 + Sources/vendor/composer/autoload_static.php | 1 + Sources/vendor/composer/installed.json | 58 +++ Sources/vendor/composer/installed.php | 13 +- 64 files changed, 2181 insertions(+), 12 deletions(-) create mode 100644 Documents/Diagramme/Annalyse/MLD.png create mode 100644 Sources/src/app/App.php create mode 100644 Sources/src/app/AppCreator.php create mode 100644 Sources/src/app/DI/Container.php create mode 100644 Sources/src/app/HttpClient.php create mode 100644 Sources/src/app/controller/AthleteController.php create mode 100644 Sources/src/app/controller/BaseController.php create mode 100644 Sources/src/app/controller/FrontController.php create mode 100644 Sources/src/app/router/Route.php create mode 100644 Sources/src/app/router/Router.php create mode 100644 Sources/src/app/router/exception/HttpException.php create mode 100644 Sources/src/app/router/exception/NotFoundException.php create mode 100644 Sources/src/app/router/exception/ValidationException.php create mode 100644 Sources/src/app/router/middleware/HttpValidationMiddleware.php create mode 100644 Sources/src/app/router/middleware/IHttpMiddleware.php create mode 100644 Sources/src/app/router/middleware/LoggingMiddleware.php create mode 100644 Sources/src/app/router/middleware/Middleware.php create mode 100644 Sources/src/app/router/request/ContentStrategy.php create mode 100644 Sources/src/app/router/request/ContentStrategyFactory.php create mode 100644 Sources/src/app/router/request/FormContentStrategy.php create mode 100644 Sources/src/app/router/request/IRequest.php create mode 100644 Sources/src/app/router/request/JsonContentStrategy.php create mode 100644 Sources/src/app/router/request/Request.php create mode 100644 Sources/src/app/router/request/RequestFactory.php create mode 100644 Sources/src/app/router/responce/IResponce.php create mode 100644 Sources/src/app/router/responce/Response.php create mode 100644 Sources/src/app/views/form/AbstractFiled.php create mode 100644 Sources/src/app/views/form/AbstractForm.php create mode 100644 Sources/src/app/views/form/FieldFactory.php create mode 100644 Sources/src/app/views/form/FormBuilder.php create mode 100644 Sources/src/app/views/form/IField.php create mode 100644 Sources/src/app/views/form/IForm.php create mode 100644 Sources/src/app/views/form/LoginForm.php create mode 100644 Sources/src/app/views/form/TextField.php create mode 100644 Sources/src/app/views/templates/login/index.html create mode 100644 Sources/src/app/views/templates/login/index.html.twig create mode 100644 Sources/src/console/ConsolePoo.php create mode 100644 Sources/src/console/Poo/Application.php create mode 100644 Sources/src/console/Poo/AuthMenu.php create mode 100644 Sources/src/console/Poo/ClearScreenCommand.php create mode 100644 Sources/src/console/Poo/IPrinter.php create mode 100644 Sources/src/console/Poo/Menu.php create mode 100644 Sources/src/data/core/Auth.php create mode 100644 Sources/src/data/model/manager/AuthManager.php create mode 100644 Sources/src/shared/Validator.php create mode 100644 Sources/vendor/altorouter/altorouter/.travis.yml create mode 100644 Sources/vendor/altorouter/altorouter/AltoRouter.php create mode 100644 Sources/vendor/altorouter/altorouter/AltoRouterTest.php create mode 100644 Sources/vendor/altorouter/altorouter/README.md create mode 100644 Sources/vendor/altorouter/altorouter/composer.json create mode 100644 Sources/vendor/altorouter/altorouter/examples/basic/.htaccess create mode 100644 Sources/vendor/altorouter/altorouter/examples/basic/index.php diff --git a/Documents/Diagramme/Annalyse/MLD.png b/Documents/Diagramme/Annalyse/MLD.png new file mode 100644 index 0000000000000000000000000000000000000000..02e33c6c941d52073722cc4fa7319aaadcf4e1ab GIT binary patch literal 82890 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

Cnsmnreo{yiklJ7hlY1R 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 literal 0 HcmV?d00001 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(),