From 4d51f6966924c941dc8105895a0bf65e0a2bf724 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Thu, 11 May 2023 13:13:30 +0000 Subject: [PATCH 001/199] Fixes Phpdoc --- src/Cache/Cache.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cache/Cache.php b/src/Cache/Cache.php index 87c77e99..befe1d52 100644 --- a/src/Cache/Cache.php +++ b/src/Cache/Cache.php @@ -42,7 +42,7 @@ class Cache /** * Cache configuration method * - * @param string $base_directory + * @param array $config */ public static function confirgure(array $config) { From b4b9e87cc6fa1d126d0260c4696bcfb73374f515 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Thu, 11 May 2023 17:30:16 +0000 Subject: [PATCH 002/199] Fixes the generate app key issues --- src/Console/Command/GenerateKeyCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Console/Command/GenerateKeyCommand.php b/src/Console/Command/GenerateKeyCommand.php index ec467429..30629a2e 100644 --- a/src/Console/Command/GenerateKeyCommand.php +++ b/src/Console/Command/GenerateKeyCommand.php @@ -25,7 +25,7 @@ public function generate(): void } $contents = file_get_contents($env_file); - $contents = preg_replace('@"APP_KEY"\s*:\s*".+?"@', '"APP_KEY": "' . $key . '"', $contents); + $contents = preg_replace('@"APP_KEY"\s*:\s*".*?"@', '"APP_KEY": "' . $key . '"', $contents); file_put_contents($env_file, $contents); From 3039c7c5ec5f7d6c6612a7e12e87aaadcc8561d3 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 16 May 2023 06:25:05 +0000 Subject: [PATCH 003/199] Fix action dep injection --- src/Container/Action.php | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/Container/Action.php b/src/Container/Action.php index 51386a0b..77c2ad7c 100644 --- a/src/Container/Action.php +++ b/src/Container/Action.php @@ -12,7 +12,6 @@ use Bow\Support\Collection; use Bow\Database\Barry\Model; use InvalidArgumentException; -use Bow\Middleware\BaseMiddleware; use Bow\Contracts\ResponseInterface; use Bow\Router\Exception\RouterException; @@ -191,7 +190,6 @@ public function call(callable|string|array $actions, ?array $param = null): mixe foreach ($actions as $key => $action) { if (is_string($action)) { array_push($functions, $this->controller($action)); - continue; } @@ -215,14 +213,12 @@ public function call(callable|string|array $actions, ?array $param = null): mixe if (is_callable($middleware)) { if ($middleware instanceof Closure || is_array($middleware)) { $this->dispatcher->pipe($middleware); - continue; } } if (class_exists($middleware)) { $this->dispatcher->pipe($middleware); - continue; } @@ -237,12 +233,18 @@ public function call(callable|string|array $actions, ?array $param = null): mixe // We check if middleware if define via aliases if (!array_key_exists($middleware, $this->middlewares)) { - throw new RouterException(sprintf('%s is not define middleware.', $middleware), E_ERROR); + throw new RouterException( + sprintf('%s is not define middleware.', $middleware), + E_ERROR + ); } // We check if the defined middleware is a valid middleware. if (!class_exists($this->middlewares[$middleware])) { - throw new RouterException(sprintf('%s is not a middleware class.', $middleware)); + throw new RouterException( + sprintf('%s is not a middleware class.', $middleware), + E_ERROR + ); } // We add middleware into dispatch pipeline @@ -439,9 +441,9 @@ public function injectorForClosure(callable $closure): array { $reflection = new ReflectionFunction($closure); - $parameters = $reflection->getParameters(); - - return $this->getInjectParameters($parameters); + return $this->getInjectParameters( + $reflection->getParameters() + ); } /** @@ -484,16 +486,16 @@ private function getInjectParameter($class): ?object { $class_name = $class->getName(); + if (in_array(strtolower($class_name), Action::INJECTION_EXCEPTION_TYPE)) { + return null; + } + if (!class_exists($class_name, true)) { throw new InvalidArgumentException( sprintf('class %s not exists', $class_name) ); } - if (in_array(strtolower($class_name), Action::INJECTION_EXCEPTION_TYPE)) { - return null; - } - if (method_exists($class_name, 'getInstance')) { return $class_name::getInstance(); } From 1452a33b31bb33d6b5faf2002feb3d6cdf401b65 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 16 May 2023 06:25:14 +0000 Subject: [PATCH 004/199] Remove extension require --- composer.json | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/composer.json b/composer.json index b2959369..140ee2b5 100644 --- a/composer.json +++ b/composer.json @@ -14,16 +14,7 @@ "nesbot/carbon": "^2.16", "psy/psysh": "v0.10.*", "fakerphp/faker": "^1.20", - "neitanod/forceutf8": "^2.0", - "ext-openssl": "*", - "ext-intl": "*", - "ext-pdo": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-json": "*", - "ext-pdo_mysql": "*", - "ext-pdo_sqlite": "*", - "ext-redis": "*" + "neitanod/forceutf8": "^2.0" }, "require-dev": { "pda/pheanstalk": "^4.0", From 93e05f243a9e2608bf39717e8ba2a75444e72bc8 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 16 May 2023 06:25:36 +0000 Subject: [PATCH 005/199] Add the base error handler class --- .../Exception/BaseErrorHandler.php | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/Application/Exception/BaseErrorHandler.php diff --git a/src/Application/Exception/BaseErrorHandler.php b/src/Application/Exception/BaseErrorHandler.php new file mode 100644 index 00000000..e2b0031c --- /dev/null +++ b/src/Application/Exception/BaseErrorHandler.php @@ -0,0 +1,22 @@ +getContent(); + } +} From ed6dfb651899565fcbd7d06348776c69b5a756fd Mon Sep 17 00:00:00 2001 From: papac Date: Tue, 16 May 2023 06:33:40 +0000 Subject: [PATCH 006/199] Update CHANGELOG --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03d74dff..5e8e9e0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 5.0.2 - 2023-05-16 + +Release for 5.0.2 + +- Fix action dependency injector +- Add the base error handler + ## [Unreleased] - [Add] Convert the project from PHP7 to PHP8 From 054dd0ec3acb2ac71c27be478bfad50c0ad48eb2 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 16 May 2023 12:05:10 +0000 Subject: [PATCH 007/199] Fixes http client and TestCase service --- src/Application/Application.php | 17 +---- .../Exception/BaseErrorHandler.php | 6 +- src/Configuration/LoggerConfiguration.php | 64 +++++++++---------- src/Database/Barry/Relations/BelongsTo.php | 2 +- src/Database/Barry/Relations/HasOne.php | 2 +- src/Http/Client/HttpClient.php | 10 ++- src/Http/Client/Parser.php | 2 +- src/Testing/TestCase.php | 19 +++--- 8 files changed, 55 insertions(+), 67 deletions(-) diff --git a/src/Application/Application.php b/src/Application/Application.php index 05987e3c..4e56b378 100644 --- a/src/Application/Application.php +++ b/src/Application/Application.php @@ -195,30 +195,17 @@ public function send(): ?bool // the routing collection $routes = $this->getRoutes(); - if (!isset($routes[$method])) { - // We verify and call function associate by 404 code - $this->response->status(404); - - if (empty($this->error_code)) { - $this->response->send( - sprintf('Cannot %s %s 404', $method, $this->request->path()) - ); - } - - return false; - } - $response = null; $resolved = false; - foreach ($routes[$method] as $route) { + foreach ($routes[$method] ?? [] as $route) { // The route must be an instance of Route if (!($route instanceof Route)) { continue; } // We launch the search of the method that arrived in the query - // then start checking the url of the request + // then start checking the url of the request if (!$route->match($this->request->path())) { continue; } diff --git a/src/Application/Exception/BaseErrorHandler.php b/src/Application/Exception/BaseErrorHandler.php index e2b0031c..4c1aae42 100644 --- a/src/Application/Exception/BaseErrorHandler.php +++ b/src/Application/Exception/BaseErrorHandler.php @@ -6,16 +6,16 @@ use Bow\View\View; -class BaseErrorHandler extends \Exception +class BaseErrorHandler { /** * Render view as response * * @param string $view * @param array $data - * @return mixed + * @return string */ - protected function render($view, $data = []) + protected function render($view, $data = []): string { return View::parse($view, $data)->getContent(); } diff --git a/src/Configuration/LoggerConfiguration.php b/src/Configuration/LoggerConfiguration.php index b4590c7c..912fa13d 100644 --- a/src/Configuration/LoggerConfiguration.php +++ b/src/Configuration/LoggerConfiguration.php @@ -6,14 +6,16 @@ use Bow\View\View; use Monolog\Logger; -use Bow\Http\Redirect; use Bow\Support\Collection; use Bow\Configuration\Loader; use Bow\Database\Barry\Model; use Monolog\Handler\StreamHandler; use Monolog\Handler\FirePHPHandler; +use Whoops\Handler\CallbackHandler; use Bow\Configuration\Configuration; use Bow\Contracts\ResponseInterface; +use Whoops\Handler\PrettyPageHandler; +use Whoops\Handler\Handler; class LoggerConfiguration extends Configuration { @@ -54,44 +56,38 @@ private function loadFrontLogger(Logger $monolog, $error_handler) { $whoops = new \Whoops\Run(); - if (app_env('APP_ENV') == 'development') { - $whoops->pushHandler( - new \Whoops\Handler\PrettyPageHandler() - ); + if (app_env('APP_ENV') != 'production') { + $whoops->pushHandler(new PrettyPageHandler()); + $whoops->register(); + return; } - if (class_exists($error_handler)) { - $handler = new \Whoops\Handler\CallbackHandler( - function ($exception, $inspector, $run) use ($monolog, $error_handler) { - $monolog->error($exception->getMessage(), $exception->getTrace()); - - $result = call_user_func_array( - [new $error_handler(), 'handle'], - [$exception] - ); - - switch (true) { - case $result instanceof View: - return $result->getContent(); - case $result instanceof ResponseInterface || $result instanceof Redirect: - $result->sendContent(); - break; - case $result instanceof Model || $result instanceof Collection: - return $result->toArray(); - case is_null($result): - case is_string($result): - case is_array($result): - case is_object($result): - case $result instanceof \Iterable: - return $result; - } - exit(1); + $handler = new CallbackHandler( + function ($exception, $inspector, $run) use ($monolog, $error_handler) { + $monolog->error($exception->getMessage(), $exception->getTrace()); + + $result = call_user_func_array([new $error_handler(), 'handle'], [$exception]); + + if ($result instanceof View) { + echo $result->getContent(); + } elseif ($result instanceof ResponseInterface) { + $result->sendContent(); + } elseif ( + is_null($result) + || $result instanceof Model || $result instanceof Collection + || is_string($result) + || is_array($result) + || is_object($result) + || $result instanceof \Iterable + ) { + echo json_encode($result); } - ); - $whoops->pushHandler($handler); - } + return Handler::QUIT; + } + ); + $whoops->pushHandler($handler); $whoops->register(); } diff --git a/src/Database/Barry/Relations/BelongsTo.php b/src/Database/Barry/Relations/BelongsTo.php index 68f92bfe..9911bd6a 100644 --- a/src/Database/Barry/Relations/BelongsTo.php +++ b/src/Database/Barry/Relations/BelongsTo.php @@ -55,7 +55,7 @@ public function getResults(): ?Model $cache = Cache::cache('file')->get($key); if (!is_null($cache)) { - $related = new $this->related; + $related = new $this->related(); $related->setAttributes($cache); return $related; } diff --git a/src/Database/Barry/Relations/HasOne.php b/src/Database/Barry/Relations/HasOne.php index ec3f452f..5eb0c985 100644 --- a/src/Database/Barry/Relations/HasOne.php +++ b/src/Database/Barry/Relations/HasOne.php @@ -52,7 +52,7 @@ public function getResults(): ?Model $cache = Cache::cache('file')->get($key); if (!is_null($cache)) { - $related = new $this->related; + $related = new $this->related(); $related->setAttributes($cache); return $related; } diff --git a/src/Http/Client/HttpClient.php b/src/Http/Client/HttpClient.php index 5c74ab8a..0f52f42c 100644 --- a/src/Http/Client/HttpClient.php +++ b/src/Http/Client/HttpClient.php @@ -57,6 +57,8 @@ public function get(string $url, array $data = []): Parser $this->addFields($data); + curl_setopt($this->ch, CURLOPT_HTTPGET, true); + return new Parser($this->ch); } @@ -81,6 +83,7 @@ public function post(string $url, array $data = []): Parser $data = array_merge($this->attach, $data); } + curl_setopt($this->ch, CURLOPT_POST, true); $this->addFields($data); return new Parser($this->ch); @@ -101,6 +104,8 @@ public function put(string $url, array $data = []): Parser $this->addFields($data); } + curl_setopt($this->ch, CURLOPT_PUT, true); + return new Parser($this->ch); } @@ -145,6 +150,7 @@ public function addHeaders(array $headers): HttpClient private function initCurl(string $url): void { $url = $this->base_url . "/" . trim($url, "/"); + $this->ch = curl_init($url); } @@ -156,6 +162,8 @@ private function initCurl(string $url): void */ private function addFields(array $data): void { - curl_setopt($this->ch, CURLOPT_POSTFIELDS, http_build_query($data)); + if (count($data) > 0) { + curl_setopt($this->ch, CURLOPT_POSTFIELDS, http_build_query($data)); + } } } diff --git a/src/Http/Client/Parser.php b/src/Http/Client/Parser.php index 73a4d28a..ec7d72f8 100644 --- a/src/Http/Client/Parser.php +++ b/src/Http/Client/Parser.php @@ -153,7 +153,7 @@ private function returnTransfert() private function returnTransfertToRaw() { if ($this->returnTransfert()) { - if (!curl_setopt($this->ch, CURLOPT_BINARYTRANSFER, true)) { + if (!curl_setopt($this->ch, CURLOPT_HTTPGET, true)) { $this->close(); return false; diff --git a/src/Testing/TestCase.php b/src/Testing/TestCase.php index d4d8c415..074d6351 100644 --- a/src/Testing/TestCase.php +++ b/src/Testing/TestCase.php @@ -31,20 +31,17 @@ class TestCase extends PHPUnitTestCase private array $headers = []; /** - * Format url + * Get the base url * - * @param $url * @return string */ - private function formatUrl(string $url): string + private function getBaseUrl(): string { - if (!$this->url) { - $this->url = app_env('APP_URL', 'http://127.0.0.1:5000'); + if (is_null($this->url)) { + return rtrim(app_env('APP_URL', 'http://127.0.0.1:5000')); } - $url = rtrim($this->url, '/') . $url; - - return trim($url, '/'); + return $this->url ?? 'http://127.0.0.1:5000'; } /** @@ -82,7 +79,7 @@ public function withHeader(array $headers): TestCase */ public function get(string $url, array $param = []): Response { - $http = new HttpClient($this->formatUrl($url)); + $http = new HttpClient($this->getBaseUrl()); $http->addHeaders($this->headers); @@ -98,7 +95,7 @@ public function get(string $url, array $param = []): Response */ public function post(string $url, array $param = []): Response { - $http = new HttpClient($this->formatUrl($url)); + $http = new HttpClient($this->getBaseUrl()); if (!empty($this->attach)) { $http->addAttach($this->attach); @@ -118,7 +115,7 @@ public function post(string $url, array $param = []): Response */ public function put(string $url, array $param = []): Response { - $http = new HttpClient($this->formatUrl($url)); + $http = new HttpClient($this->getBaseUrl()); $http->addHeaders($this->headers); From cc66bd1ed3a739ae0e20ab41b9122ce2c4441e66 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 16 May 2023 12:17:50 +0000 Subject: [PATCH 008/199] Fixes unity tests for application --- tests/Application/ApplicationTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Application/ApplicationTest.php b/tests/Application/ApplicationTest.php index d1605e22..7d6a2e3d 100644 --- a/tests/Application/ApplicationTest.php +++ b/tests/Application/ApplicationTest.php @@ -57,13 +57,14 @@ public function test_one_time_application_boot() public function test_send_application_with_404_status() { + $this->expectException(RouterException::class); + $response = Mockery::mock(Response::class); $request = Mockery::mock(Request::class); // Response mock method $response->allows()->addHeader('X-Powered-By', 'Bow Framework'); $response->allows()->status(404); - $response->allows()->send('Cannot GET / 404'); // Request mock method $request->allows()->method()->andReturns("GET"); @@ -80,8 +81,7 @@ public function test_send_application_with_404_status() $app = new Application($request, $response); $app->bind($config); - - $this->assertFalse($app->send()); + $app->send(); } public function test_send_application_with_matched_route() From 33e8240b4cb3932ee635879350f173131aca780d Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 16 May 2023 12:23:21 +0000 Subject: [PATCH 009/199] Formatting --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e8e9e0c..24189853 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ Release for 5.0.2 - Fix action dependency injector - Add the base error handler -## [Unreleased] +## 5.0.0 - 2023-05-10 - [Add] Convert the project from PHP7 to PHP8 - [Add] Multiconnection for storage system From 72f9675df03328e792a232552949f2b866a270fe Mon Sep 17 00:00:00 2001 From: papac Date: Tue, 16 May 2023 12:29:04 +0000 Subject: [PATCH 010/199] Update CHANGELOG --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24189853..69d0f46c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 5.0.3 - 2023-05-16 + +Add many fixes + +- Fixes the error handler +- Fixes the HTTP client +- Fixes TestCase service + ## 5.0.2 - 2023-05-16 Release for 5.0.2 From d7638a4e7d55d3eccdad8b20c0db810089f095b6 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Thu, 18 May 2023 13:12:14 +0000 Subject: [PATCH 011/199] Fixes the env loading --- src/Support/Env.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Support/Env.php b/src/Support/Env.php index 552f125b..81a98f18 100644 --- a/src/Support/Env.php +++ b/src/Support/Env.php @@ -56,7 +56,7 @@ public static function load(string $filename) foreach (static::$envs as $key => $value) { $key = Str::upper(trim($key)); - putenv($key . '=' . $value); + putenv($key . '=' . json_encode($value)); } if (json_last_error() == JSON_ERROR_SYNTAX) { @@ -90,7 +90,13 @@ public static function get(string $key, mixed $default = null): mixed return $default; } - return $value; + if (!is_string($value)) { + return $value; + } + + $data = json_decode($value); + + return json_last_error() ? $value : $data; } /** From 270dba9d8582facc98011dc3bc30b3ec812674cc Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Thu, 18 May 2023 13:12:32 +0000 Subject: [PATCH 012/199] Refactoring the http client --- src/Console/stubs/middleware.stub | 2 +- src/Http/Client/HttpClient.php | 71 ++++++++++++-------- src/Http/Client/{Parser.php => Response.php} | 65 ++++-------------- src/Testing/Response.php | 14 ++-- 4 files changed, 65 insertions(+), 87 deletions(-) rename src/Http/Client/{Parser.php => Response.php} (83%) diff --git a/src/Console/stubs/middleware.stub b/src/Console/stubs/middleware.stub index 832fa3ac..aea551f8 100644 --- a/src/Console/stubs/middleware.stub +++ b/src/Console/stubs/middleware.stub @@ -15,7 +15,7 @@ class {className} implements BaseMiddleware * @param array $args * @return mixed */ - public function process(Request $request, callable $next, array $args = []): void + public function process(Request $request, callable $next, array $args = []): mixed { // Code Here diff --git a/src/Http/Client/HttpClient.php b/src/Http/Client/HttpClient.php index 0f52f42c..e2de9742 100644 --- a/src/Http/Client/HttpClient.php +++ b/src/Http/Client/HttpClient.php @@ -5,6 +5,8 @@ namespace Bow\Http\Client; use CurlHandle; +use BadFunctionCallException; +use Bow\Http\Client\Response as HttpClientResponse; class HttpClient { @@ -38,28 +40,39 @@ class HttpClient public function __construct(?string $base_url = null) { if (!function_exists('curl_init')) { - throw new \BadFunctionCallException('cURL php is require.'); + throw new BadFunctionCallException('cURL php is require.'); } $this->base_url = rtrim($base_url, "/"); } + /** + * Set the base url + * + * @param string $url + * @return void + */ + public function setBaseUrl(string $url): void + { + $this->base_url = rtrim($url, "/"); + } + /** * Make get requete * * @param string $url * @param array $data - * @return Parser + * @return HttpClientResponse */ - public function get(string $url, array $data = []): Parser + public function get(string $url, array $data = []): HttpClientResponse { - $this->initCurl($url); + $this->init($url); $this->addFields($data); curl_setopt($this->ch, CURLOPT_HTTPGET, true); - return new Parser($this->ch); + return new HttpClientResponse($this->ch); } /** @@ -67,11 +80,11 @@ public function get(string $url, array $data = []): Parser * * @param string $url * @param array $data - * @return Parser + * @return HttpClientResponse */ - public function post(string $url, array $data = []): Parser + public function post(string $url, array $data = []): HttpClientResponse { - $this->initCurl($url); + $this->init($url); if (!empty($this->attach)) { curl_setopt($this->ch, CURLOPT_UPLOAD, true); @@ -86,7 +99,7 @@ public function post(string $url, array $data = []): Parser curl_setopt($this->ch, CURLOPT_POST, true); $this->addFields($data); - return new Parser($this->ch); + return new HttpClientResponse($this->ch); } /** @@ -94,11 +107,11 @@ public function post(string $url, array $data = []): Parser * * @param string $url * @param array $data - * @return Parser + * @return HttpClientResponse */ - public function put(string $url, array $data = []): Parser + public function put(string $url, array $data = []): HttpClientResponse { - $this->initCurl($url); + $this->init($url); if (!curl_setopt($this->ch, CURLOPT_PUT, true)) { $this->addFields($data); @@ -106,18 +119,20 @@ public function put(string $url, array $data = []): Parser curl_setopt($this->ch, CURLOPT_PUT, true); - return new Parser($this->ch); + return new HttpClientResponse($this->ch); } /** * Attach new file * * @param string $attach - * @return array + * @return HttpClient */ - public function addAttach(string|array $attach): array + public function addAttach(string|array $attach): HttpClient { - return $this->attach = (array) $attach; + $this->attach = (array) $attach; + + return $this; } /** @@ -142,28 +157,30 @@ public function addHeaders(array $headers): HttpClient } /** - * Reset alway connection + * Add field * - * @param string $url + * @param array $data * @return void */ - private function initCurl(string $url): void + private function addFields(array $data): void { - $url = $this->base_url . "/" . trim($url, "/"); - - $this->ch = curl_init($url); + if (count($data) > 0) { + curl_setopt($this->ch, CURLOPT_POSTFIELDS, http_build_query($data)); + } } /** - * Add field + * Reset alway connection * - * @param array $data + * @param string $url * @return void */ - private function addFields(array $data): void + private function init(string $url): void { - if (count($data) > 0) { - curl_setopt($this->ch, CURLOPT_POSTFIELDS, http_build_query($data)); + if (is_null($this->base_url)) { + $url = $this->base_url . "/" . trim($url, "/"); } + + $this->ch = curl_init(trim($url, "/")); } } diff --git a/src/Http/Client/Parser.php b/src/Http/Client/Response.php similarity index 83% rename from src/Http/Client/Parser.php rename to src/Http/Client/Response.php index ec7d72f8..ba468856 100644 --- a/src/Http/Client/Parser.php +++ b/src/Http/Client/Response.php @@ -6,7 +6,7 @@ use CurlHandle; -class Parser +class Response { /** * The error message @@ -30,11 +30,11 @@ class Parser private CurlHandle $ch; /** - * The header + * The headers * * @var array */ - private array $header = []; + private array $headers = []; /** * Flag @@ -43,13 +43,6 @@ class Parser */ private bool $executed = false; - /** - * The attachment collection - * - * @var array - */ - private array $attach = []; - /** * Parser constructor. * @@ -193,7 +186,7 @@ private function execute(): string $this->error = curl_error($this->ch); $this->errno = curl_errno($this->ch); - $this->header = curl_getinfo($this->ch); + $this->headers = curl_getinfo($this->ch); $this->executed = true; $this->close(); @@ -217,7 +210,7 @@ public function getHeaders(): array $this->execute(); } - return $this->header; + return $this->headers; } /** @@ -232,7 +225,7 @@ public function getCode(): ?int $this->execute(); } - return $this->header['http_code'] ?? null; + return $this->headers['http_code'] ?? null; } /** @@ -247,7 +240,7 @@ public function getExecutionTime(): ?int $this->execute(); } - return $this->header['total_time'] ?? null; + return $this->headers['total_time'] ?? null; } /** @@ -262,7 +255,7 @@ public function getConnexionTime(): ?float $this->execute(); } - return $this->header['connect_time'] ?? null; + return $this->headers['connect_time'] ?? null; } /** @@ -277,7 +270,7 @@ public function getUploadSize(): ?float $this->execute(); } - return $this->header['size_upload'] ?? null; + return $this->headers['size_upload'] ?? null; } /** @@ -292,7 +285,7 @@ public function getUploadSpeed(): ?float $this->execute(); } - return $this->header['speed_upload'] ?? null; + return $this->headers['speed_upload'] ?? null; } /** @@ -307,7 +300,7 @@ public function getDownloadSize(): ?float $this->execute(); } - return $this->header['size_download'] ?? null; + return $this->headers['size_download'] ?? null; } /** @@ -322,7 +315,7 @@ public function getDownloadSpeed(): ?float $this->execute(); } - return $this->header['speed_download'] ?? null; + return $this->headers['speed_download'] ?? null; } /** @@ -367,39 +360,7 @@ public function getContentType(): ?string $this->execute(); } - return $this->header['content_type'] ?? null; - } - - /** - * Add attach file - * - * @param array $attach - * @return void - */ - public function addAttach($attach) - { - $this->attach = array_merge($this->attach, (array) $attach); - } - - /** - * Get attached files - * - * @return array - */ - public function getAttach(): array - { - return $this->attach; - } - - /** - * Set attach files - * - * @param array $attachs - * @return void - */ - public function setAttach(array $attachs): void - { - $this->attach = $attachs; + return $this->headers['content_type'] ?? null; } /** diff --git a/src/Testing/Response.php b/src/Testing/Response.php index f7e57ccc..8372169a 100644 --- a/src/Testing/Response.php +++ b/src/Testing/Response.php @@ -5,16 +5,16 @@ namespace Bow\Testing; use InvalidArgumentException; -use Bow\Http\Client\Parser; +use Bow\Http\Client\Response as HttpClientResponse; class Response { /** * The http parser * - * @var Parser + * @var HttpClientResponse */ - private Parser $parser; + private HttpClientResponse $http_response; /** * The parser content @@ -26,13 +26,13 @@ class Response /** * Behovior constructor. * - * @param Parser $parser + * @param HttpClientResponse $http_response */ - public function __construct(Parser $parser) + public function __construct(HttpClientResponse $http_response) { - $this->parser = $parser; + $this->http_response = $http_response; - $this->content = $parser->getContent(); + $this->content = $http_response->getContent(); } /** From cd92b726ef2d4280e66ddbcf54ba7433e1774ba3 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Thu, 18 May 2023 20:14:38 +0000 Subject: [PATCH 013/199] Refactoring env for add variable data binding --- src/Support/Env.php | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/Support/Env.php b/src/Support/Env.php index 81a98f18..88685583 100644 --- a/src/Support/Env.php +++ b/src/Support/Env.php @@ -54,6 +54,10 @@ public static function load(string $filename) static::$envs = json_decode(trim($content), true); + static::$envs = static::bindVariables(static::$envs); + + dd(static::$envs); + foreach (static::$envs as $key => $value) { $key = Str::upper(trim($key)); putenv($key . '=' . json_encode($value)); @@ -109,8 +113,38 @@ public static function get(string $key, mixed $default = null): mixed public static function set(string $key, mixed $value): bool { $key = Str::upper(trim($key)); + static::$envs[$key] = $value; return putenv($key . '=' . $value); } + + /** + * Bind variable + * + * @param array $envs + * @return array + */ + private static function bindVariables(array $envs): array + { + $keys = array_keys(static::$envs); + + foreach ($envs as $env_key => $value) { + foreach ($keys as $key) { + if ($key == $env_key) { + break; + } + if (is_array($value)) { + $envs[$env_key] = static::bindVariables($value); + break; + } + if (is_string($value) && preg_match("/\\$\{\s*$key\s*\}/", $value)) { + $envs[$env_key] = str_replace('${' . $key . '}', static::$envs[$key], $value); + break; + } + } + } + + return $envs; + } } From 562080f5e73eafbc3b33ce66378bde2387cddb3d Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Thu, 18 May 2023 20:15:11 +0000 Subject: [PATCH 014/199] Update logger configuration --- src/Configuration/LoggerConfiguration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Configuration/LoggerConfiguration.php b/src/Configuration/LoggerConfiguration.php index 912fa13d..23072225 100644 --- a/src/Configuration/LoggerConfiguration.php +++ b/src/Configuration/LoggerConfiguration.php @@ -56,7 +56,7 @@ private function loadFrontLogger(Logger $monolog, $error_handler) { $whoops = new \Whoops\Run(); - if (app_env('APP_ENV') != 'production') { + if (app_env('APP_DEBUG')) { $whoops->pushHandler(new PrettyPageHandler()); $whoops->register(); return; From 0457279c86fb75893ecb2821d755ed35702fc371 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Thu, 18 May 2023 20:15:28 +0000 Subject: [PATCH 015/199] Fixes regex validation --- src/Validation/Rules/RegexRule.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Validation/Rules/RegexRule.php b/src/Validation/Rules/RegexRule.php index 725f7c4c..c4346755 100644 --- a/src/Validation/Rules/RegexRule.php +++ b/src/Validation/Rules/RegexRule.php @@ -9,7 +9,7 @@ trait RegexRule /** * Compile Regex Rule * - * [regex] Check that the contents of the field with a regular expression + * Check that the contents of the field with a regular expression * * @param string $key * @param string $masque @@ -21,7 +21,7 @@ protected function compileRegex(string $key, string $masque): void return; } - $regex = '~^' . $match[1] . '$~'; + $regex = '~' . addcslashes($match[1], "~") . '~'; if (preg_match($regex, $this->inputs[$key])) { return; From 819477b50fa0224e90b76fe03fd8ee77a268f2e6 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Thu, 18 May 2023 20:18:19 +0000 Subject: [PATCH 016/199] Fixes errors the auto crsf loading --- src/Router/Router.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Router/Router.php b/src/Router/Router.php index 44208640..164a54c0 100644 --- a/src/Router/Router.php +++ b/src/Router/Router.php @@ -113,6 +113,7 @@ public function setBaseRoute(string $base_route): void * Note: Disable only you run on test env * * @param bool $auto_csrf + * @return void */ public function setAutoCsrf(bool $auto_csrf): void { @@ -151,10 +152,10 @@ public function prefix(string $prefix, callable $cb): Router /** * Allows to associate a global middleware on an route * - * @param array $middlewares + * @param array|string $middlewares * @return Router */ - public function middleware(array $middlewares): Router + public function middleware(array|string $middlewares): Router { $middlewares = (array) $middlewares; @@ -395,10 +396,9 @@ private function routeLoader(string $method, string $path, callable|string|array static::$routes[$method][] = $route; - if (app_env('APP_ENV') != 'production' && $this->auto_csrf === true) { - if (in_array($method, ['POST', 'DELETE', 'PUT'])) { - $route->middleware('csrf'); - } + if ($this->auto_csrf === true + && in_array($method, ['POST', 'DELETE', 'PUT'])) { + $route->middleware('csrf'); } return $route; From 14673e9b2c6b1107d59e876371fc1398048d0efd Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Thu, 18 May 2023 20:19:30 +0000 Subject: [PATCH 017/199] Fixes middlewares loading order --- src/Container/Action.php | 56 +++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/src/Container/Action.php b/src/Container/Action.php index 77c2ad7c..7b8b759d 100644 --- a/src/Container/Action.php +++ b/src/Container/Action.php @@ -168,9 +168,13 @@ public function call(callable|string|array $actions, ?array $param = null): mixe * like [AppController::class, 'action'] */ if (count($actions) === 2) { - if (class_exists($actions[0])) { - $actions = [$actions[0] . '::' . $actions[1]]; + if (!class_exists($actions[0])) { + throw new InvalidArgumentException( + 'The controller ' . $actions[0] . ' is not exists', + E_USER_ERROR + ); } + $actions = [$actions[0] . '::' . $actions[1]]; } /** @@ -181,31 +185,6 @@ public function call(callable|string|array $actions, ?array $param = null): mixe $actions = (array) $actions['controller']; } - $functions = []; - - /** - * We normalize of the action to execute and - * creation of the dependency injection - */ - foreach ($actions as $key => $action) { - if (is_string($action)) { - array_push($functions, $this->controller($action)); - continue; - } - - if (!is_callable($action)) { - continue; - } - - if (is_array($action) && $action[0] instanceof Closure) { - $injection = $this->injectorForClosure($action[0]); - } else { - $injection = $this->injectorForClosure($action); - } - - array_push($functions, ['action' => $action, 'injection' => $injection]); - } - /** * We load the middleware associated with the action */ @@ -271,6 +250,29 @@ public function call(callable|string|array $actions, ?array $param = null): mixe return $response->toArray(); } + $functions = []; + + /** + * We normalize of the action to execute and + * creation of the dependency injection + */ + foreach ($actions as $key => $action) { + if (is_string($action)) { + array_push($functions, $this->controller($action)); + continue; + } + if (!is_callable($action)) { + continue; + } + if (is_array($action) && $action[0] instanceof Closure) { + $injection = $this->injectorForClosure($action[0]); + } else { + $injection = $this->injectorForClosure($action); + } + + array_push($functions, ['action' => $action, 'injection' => $injection]); + } + return $this->dispatchControllers($functions, $param); } From 53b3c1e104df2963b8e044fa4bae4481a76fa862 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Thu, 18 May 2023 20:19:51 +0000 Subject: [PATCH 018/199] Fixes http client processing --- src/Http/Client/HttpClient.php | 63 +++++++++++++++++----------------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/src/Http/Client/HttpClient.php b/src/Http/Client/HttpClient.php index e2de9742..46f7ff8c 100644 --- a/src/Http/Client/HttpClient.php +++ b/src/Http/Client/HttpClient.php @@ -5,8 +5,6 @@ namespace Bow\Http\Client; use CurlHandle; -use BadFunctionCallException; -use Bow\Http\Client\Response as HttpClientResponse; class HttpClient { @@ -40,10 +38,12 @@ class HttpClient public function __construct(?string $base_url = null) { if (!function_exists('curl_init')) { - throw new BadFunctionCallException('cURL php is require.'); + throw new \BadFunctionCallException('cURL php is require.'); } - $this->base_url = rtrim($base_url, "/"); + if (!is_null($base_url)) { + $this->base_url = rtrim($base_url, "/"); + } } /** @@ -62,17 +62,17 @@ public function setBaseUrl(string $url): void * * @param string $url * @param array $data - * @return HttpClientResponse + * @return Parser */ - public function get(string $url, array $data = []): HttpClientResponse + public function get(string $url, array $data = []): Parser { - $this->init($url); + $params = http_build_query($data); - $this->addFields($data); + $this->init($url . "?" . $params); curl_setopt($this->ch, CURLOPT_HTTPGET, true); - return new HttpClientResponse($this->ch); + return new Parser($this->ch); } /** @@ -80,9 +80,9 @@ public function get(string $url, array $data = []): HttpClientResponse * * @param string $url * @param array $data - * @return HttpClientResponse + * @return Parser */ - public function post(string $url, array $data = []): HttpClientResponse + public function post(string $url, array $data = []): Parser { $this->init($url); @@ -97,9 +97,10 @@ public function post(string $url, array $data = []): HttpClientResponse } curl_setopt($this->ch, CURLOPT_POST, true); + $this->addFields($data); - return new HttpClientResponse($this->ch); + return new Parser($this->ch); } /** @@ -107,9 +108,9 @@ public function post(string $url, array $data = []): HttpClientResponse * * @param string $url * @param array $data - * @return HttpClientResponse + * @return Parser */ - public function put(string $url, array $data = []): HttpClientResponse + public function put(string $url, array $data = []): Parser { $this->init($url); @@ -119,20 +120,18 @@ public function put(string $url, array $data = []): HttpClientResponse curl_setopt($this->ch, CURLOPT_PUT, true); - return new HttpClientResponse($this->ch); + return new Parser($this->ch); } /** * Attach new file * * @param string $attach - * @return HttpClient + * @return array */ - public function addAttach(string|array $attach): HttpClient + public function addAttach(string|array $attach): array { - $this->attach = (array) $attach; - - return $this; + return $this->attach = (array) $attach; } /** @@ -157,30 +156,30 @@ public function addHeaders(array $headers): HttpClient } /** - * Add field + * Reset alway connection * - * @param array $data + * @param string $url * @return void */ - private function addFields(array $data): void + private function init(string $url): void { - if (count($data) > 0) { - curl_setopt($this->ch, CURLOPT_POSTFIELDS, http_build_query($data)); + if (is_null($this->base_url)) { + $url = $this->base_url . "/" . trim($url, "/"); } + + $this->ch = curl_init(trim($url, "/")); } /** - * Reset alway connection + * Add fields * - * @param string $url + * @param array $data * @return void */ - private function init(string $url): void + private function addFields(array $data): void { - if (is_null($this->base_url)) { - $url = $this->base_url . "/" . trim($url, "/"); + if (count($data) > 0) { + curl_setopt($this->ch, CURLOPT_POSTFIELDS, http_build_query($data)); } - - $this->ch = curl_init(trim($url, "/")); } } From f61deed022c62d2f82564cc7d024efdc6aba6a9f Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Thu, 18 May 2023 20:20:05 +0000 Subject: [PATCH 019/199] Fixes request data parser --- src/Http/Request.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Http/Request.php b/src/Http/Request.php index c111548c..a73c16f7 100644 --- a/src/Http/Request.php +++ b/src/Http/Request.php @@ -62,7 +62,7 @@ private function __construct() } foreach ($this->input as $key => $value) { - if (!is_array($value) && strlen($value) == 0) { + if (is_string($value) && strlen($value) == 0) { $value = null; } @@ -351,7 +351,7 @@ public function isAjax(): bool */ public function is($match): bool { - return (bool) preg_match('@' . $match . '@', $this->path()); + return (bool) preg_match('@' . addcslashes($match, "/*{()}[]$^") . '@', $this->path()); } /** @@ -362,7 +362,7 @@ public function is($match): bool */ public function isReferer($match): bool { - return (bool) preg_match('@' . $match . '@', $this->referer()); + return (bool) preg_match('@' . addcslashes($match, "/*{()}[]$^") . '@', $this->referer()); } /** @@ -574,7 +574,7 @@ public function only($exceptions) { $data = []; - if (! is_array($exceptions)) { + if (!is_array($exceptions)) { $exceptions = func_get_args(); } @@ -594,7 +594,7 @@ public function ignore($ignores) { $data = $this->input; - if (! is_array($ignores)) { + if (!is_array($ignores)) { $ignores = func_get_args(); } From 13da0a43efdf3ff87f02aa82af576cca55b158ed Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Thu, 18 May 2023 20:25:00 +0000 Subject: [PATCH 020/199] formatting --- src/Support/Env.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Support/Env.php b/src/Support/Env.php index 88685583..acb1b824 100644 --- a/src/Support/Env.php +++ b/src/Support/Env.php @@ -56,8 +56,6 @@ public static function load(string $filename) static::$envs = static::bindVariables(static::$envs); - dd(static::$envs); - foreach (static::$envs as $key => $value) { $key = Str::upper(trim($key)); putenv($key . '=' . json_encode($value)); From 8c5c581fc5d5a941f67948a12e10a1e92b4eb63b Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 19 May 2023 12:01:33 +0000 Subject: [PATCH 021/199] Code formatting --- src/Router/Router.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Router/Router.php b/src/Router/Router.php index 164a54c0..e8f2f399 100644 --- a/src/Router/Router.php +++ b/src/Router/Router.php @@ -396,8 +396,10 @@ private function routeLoader(string $method, string $path, callable|string|array static::$routes[$method][] = $route; - if ($this->auto_csrf === true - && in_array($method, ['POST', 'DELETE', 'PUT'])) { + if ( + $this->auto_csrf === true + && in_array($method, ['POST', 'DELETE', 'PUT']) + ) { $route->middleware('csrf'); } From a255c1fd8fe9db45fa407c6e26f1fbd4a799e81e Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 19 May 2023 12:09:52 +0000 Subject: [PATCH 022/199] Update tests snapshots --- .../GeneratorDeepTest__test_generate_middleware_stubs__1.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Console/__snapshots__/GeneratorDeepTest__test_generate_middleware_stubs__1.txt b/tests/Console/__snapshots__/GeneratorDeepTest__test_generate_middleware_stubs__1.txt index f98bc4d8..1cb723fa 100644 --- a/tests/Console/__snapshots__/GeneratorDeepTest__test_generate_middleware_stubs__1.txt +++ b/tests/Console/__snapshots__/GeneratorDeepTest__test_generate_middleware_stubs__1.txt @@ -15,7 +15,7 @@ class FakeMiddleware implements BaseMiddleware * @param array $args * @return mixed */ - public function process(Request $request, callable $next, array $args = []): void + public function process(Request $request, callable $next, array $args = []): mixed { // Code Here From c34419e86f93bb7751a4cae04b19da4924e665a5 Mon Sep 17 00:00:00 2001 From: papac Date: Fri, 19 May 2023 12:17:19 +0000 Subject: [PATCH 023/199] Update CHANGELOG --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69d0f46c..ea92303e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 5.0.4 - 2023-05-19 + +Release 5.0.4 + +- Fixes HTTP Client +- Add variable binding to the env loader +- Fixes validation for regex rule +- Fixes request data parser +- Fixes middleware execution order + +All update ref #230 + ## 5.0.3 - 2023-05-16 Add many fixes From cbb1dc8e114789428d5f2f0fb9872786bf8cb896 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 19 May 2023 21:33:38 +0000 Subject: [PATCH 024/199] Fixes migration table structore for support pgsql --- src/Console/Command/MigrationCommand.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Console/Command/MigrationCommand.php b/src/Console/Command/MigrationCommand.php index fbfbc0b6..4c99545f 100644 --- a/src/Console/Command/MigrationCommand.php +++ b/src/Console/Command/MigrationCommand.php @@ -274,9 +274,9 @@ private function createMigrationTable() 'create' ); - $generator->addColumn('migration', 'string', ['unique' => true]); - $generator->addColumn('batch', 'int'); - $generator->addColumn('created_at', 'datetime', [ + $generator->addString('migration', ['unique' => true]); + $generator->addInteger('batch'); + $generator->addDatetime('created_at', [ 'default' => 'CURRENT_TIMESTAMP', 'nullable' => true ]); From 9637a7c47451b05518424efce0abb4fd45b17254 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 19 May 2023 21:34:16 +0000 Subject: [PATCH 025/199] Refactoring migrations for create data type for enum --- .../Migration/Compose/MysqlCompose.php | 6 +++ .../Migration/Compose/PgsqlCompose.php | 53 ++++++++++++++++--- .../Migration/Compose/SqliteCompose.php | 10 ++-- src/Database/Migration/Migration.php | 19 +++++-- .../Migration/Shortcut/ConstraintColumn.php | 25 ++++++--- .../Migration/Shortcut/MixedColumn.php | 14 ++--- 6 files changed, 94 insertions(+), 33 deletions(-) diff --git a/src/Database/Migration/Compose/MysqlCompose.php b/src/Database/Migration/Compose/MysqlCompose.php index 698e4d2c..ab64cf5c 100644 --- a/src/Database/Migration/Compose/MysqlCompose.php +++ b/src/Database/Migration/Compose/MysqlCompose.php @@ -35,6 +35,7 @@ private function composeAddMysqlColumn(string $name, array $description): string $unsigned = $attribute['unsigned'] ?? false; $after = $attribute['after'] ?? false; $first = $attribute['first'] ?? false; + $custom = $attribute['custom'] ?? false; // String to VARCHAR if ($raw_type == 'STRING') { @@ -100,6 +101,11 @@ private function composeAddMysqlColumn(string $name, array $description): string $type = sprintf('%s FIRST', $type); } + // Apply the custom definition + if ($custom) { + $type = sprintf('%s %s', $type, $custom); + } + return trim( sprintf('%s `%s` %s', $description['command'], $name, $type) ); diff --git a/src/Database/Migration/Compose/PgsqlCompose.php b/src/Database/Migration/Compose/PgsqlCompose.php index 7d2cf794..aa2bbb53 100644 --- a/src/Database/Migration/Compose/PgsqlCompose.php +++ b/src/Database/Migration/Compose/PgsqlCompose.php @@ -6,6 +6,18 @@ trait PgsqlCompose { + protected array $custom_types = []; + + /** + * Generate the custom type for pgsql + * + * @return array + */ + public function generateCustomTypes(): array + { + return $this->custom_types; + } + /** * Compose sql statement for pgsql * @@ -35,6 +47,7 @@ private function composeAddPgsqlColumn(string $name, array $description): string $unsigned = $attribute['unsigned'] ?? false; $after = $attribute['after'] ?? false; $first = $attribute['first'] ?? false; + $custom = $attribute['custom'] ?? false; if ($after || $first) { throw new SQLGeneratorException("The key first and after only work on mysql"); @@ -45,18 +58,32 @@ private function composeAddPgsqlColumn(string $name, array $description): string $type = 'VARCHAR'; } + // Redefine the size if (!$size && in_array($raw_type, ['VARCHAR', 'STRING', 'LONG VARCHAR'])) { $size = 255; } // Add column size - if ($size) { - if (in_array($raw_type, ['ENUM', 'CHECK'])) { - $size = (array) $size; - $size = "'" . implode("', '", $size) . "'"; - } - if (in_array($raw_type, ['ENUM', 'CHECK', 'VARCHAR', 'LONG VARCHAR', 'STRING'])) { - $type = sprintf('%s(%s)', $type, $size); + if (in_array($raw_type, ['VARCHAR', 'STRING', 'LONG VARCHAR']) && $size) { + $type = sprintf('%s(%s)', $type, $size); + } + + if (in_array($raw_type, ['ENUM', 'CHECK'])) { + $size = (array) $size; + $size = "'" . implode("', '", $size) . "'"; + if ($raw_type == "ENUM") { + $table = preg_replace("/(ies)$/", "y", $this->table); + $table = preg_replace("/(s)$/", "", $table); + + $this->custom_types[] = sprintf( + "CREATE TYPE %s_%s_enum AS ENUM(%s);", + $table, + $name, + $size + ); + $type = sprintf('%s_%s_enum', $this->table, $name); + } else { + $type = sprintf('TEXT CHECK (%s IN CHECK(%s))', $name, $size); } } @@ -65,6 +92,11 @@ private function composeAddPgsqlColumn(string $name, array $description): string $type = 'SERIAL'; } + // Bind precision + if ($raw_type == "DOUBLE") { + $type = sprintf('DOUBLE PRECISION', $type); + } + // Set column as primary key if ($primary) { $type = sprintf('%s PRIMARY KEY', $type); @@ -98,8 +130,13 @@ private function composeAddPgsqlColumn(string $name, array $description): string $type = sprintf('UNSIGNED %s', $type); } + // Apply the custom definition + if ($custom) { + $type = sprintf('%s %s', $type, $custom); + } + return trim( - sprintf('%s %s %s', $description['command'], $name, $type) + sprintf('%s "%s" %s', $description['command'], $name, $type) ); } diff --git a/src/Database/Migration/Compose/SqliteCompose.php b/src/Database/Migration/Compose/SqliteCompose.php index 9be1e0e5..f23a86bd 100644 --- a/src/Database/Migration/Compose/SqliteCompose.php +++ b/src/Database/Migration/Compose/SqliteCompose.php @@ -33,6 +33,7 @@ private function composeAddSqliteColumn(string $name, array $description): strin $increment = $attribute['increment'] ?? false; $nullable = $attribute['nullable'] ?? false; $unique = $attribute['unique'] ?? false; + $custom = $attribute['custom'] ?? false; // String to VARCHAR if ($raw_type == 'STRING') { @@ -43,10 +44,6 @@ private function composeAddSqliteColumn(string $name, array $description): strin $size = 255; } - // Add column size - if ($size) { - } - // Set column as primary key if ($primary) { $type = sprintf('%s PRIMARY KEY', $type); @@ -79,6 +76,11 @@ private function composeAddSqliteColumn(string $name, array $description): strin $type = sprintf('%s DEFAULT %s', $type, $default); } + // Apply the custom definition + if ($custom) { + $type = sprintf('%s %s', $type, $custom); + } + return trim( sprintf('%s `%s` %s', $description['command'], $name, $type) ); diff --git a/src/Database/Migration/Migration.php b/src/Database/Migration/Migration.php index 4635f766..4cc70787 100644 --- a/src/Database/Migration/Migration.php +++ b/src/Database/Migration/Migration.php @@ -113,12 +113,21 @@ final public function create(string $table, callable $cb): Migration $engine = null; } - if ($this->adapter->getName() === 'pgsql') { - $sql = sprintf("CREATE TABLE %s (%s)%s;", $table, $generator->make(), $engine); - } else { + if ($this->adapter->getName() !== 'pgsql') { $sql = sprintf("CREATE TABLE `%s` (%s)%s;", $table, $generator->make(), $engine); + + return $this->executeSqlQuery($sql); + } + + foreach ($generator->generateCustomTypes() as $sql) { + try { + $this->executeSqlQuery($sql); + } catch (\Exception $exception) { + echo sprintf("%s %s\n", Color::yellow("Warning"), $exception->getMessage()); + } } + $sql = sprintf("CREATE TABLE %s (%s)%s;", $table, $generator->make(), $engine); return $this->executeSqlQuery($sql); } @@ -210,11 +219,11 @@ private function executeSqlQuery(string $sql): Migration try { Database::statement($sql); } catch (\Exception $exception) { - echo sprintf("%s%s\n", Color::red("▶"), $sql); + echo sprintf("%s %s\n", Color::red("▶"), $sql); throw new MigrationException($exception->getMessage(), (int) $exception->getCode()); } - echo sprintf("%s%s\n", Color::green("▶"), $sql); + echo sprintf("%s %s\n", Color::green("▶"), $sql); return $this; } } diff --git a/src/Database/Migration/Shortcut/ConstraintColumn.php b/src/Database/Migration/Shortcut/ConstraintColumn.php index 00445b45..62b93fe6 100644 --- a/src/Database/Migration/Shortcut/ConstraintColumn.php +++ b/src/Database/Migration/Shortcut/ConstraintColumn.php @@ -25,22 +25,33 @@ public function addForeign(string $name, array $attributes = []): SQLGenerator $on = ''; $references = ''; - $target = sprintf("%s_%s_foreign", $this->getTable(), $name); + + if ($this->adapter == "pgsql") { + $target = sprintf("\"%s_%s_foreign\"", $this->getTable(), $name); + } if (isset($attributes['on'])) { $on = strtoupper(' ON ' . $attributes['on']); } if (isset($attributes['references'], $attributes['table'])) { - $references = sprintf( - ' REFERENCES %s(%s)', - $attributes['table'], - $attributes['references'] - ); + if ($this->adapter === 'pgsql') { + $references = sprintf( + ' REFERENCES %s("%s")', + $attributes['table'], + $attributes['references'] + ); + } else { + $references = sprintf( + ' REFERENCES %s(`%s`)', + $attributes['table'], + $attributes['references'] + ); + } } if ($this->adapter === 'pgsql') { - $replacement = '%s %s FOREIGN KEY (%s)%s%s'; + $replacement = '%s %s FOREIGN KEY ("%s")%s%s'; } else { $replacement = '%s %s FOREIGN KEY (`%s`)%s%s'; } diff --git a/src/Database/Migration/Shortcut/MixedColumn.php b/src/Database/Migration/Shortcut/MixedColumn.php index c5884f66..9bd729fe 100644 --- a/src/Database/Migration/Shortcut/MixedColumn.php +++ b/src/Database/Migration/Shortcut/MixedColumn.php @@ -49,10 +49,6 @@ public function addUuid(string $column, array $attribute = []): SQLGenerator return $this->addColumn($column, 'varchar', $attribute); } - if (!isset($attribute['default']) && $this->adapter === 'pgsql') { - $attribute['default'] = 'uuid_generate_v4()'; - } - return $this->addColumn($column, 'uuid', $attribute); } @@ -163,10 +159,14 @@ public function addEnum(string $column, array $attribute = []): SQLGenerator throw new SQLGeneratorException("The enum values should be define!"); } - if (is_array($attribute['size'])) { + if (!is_array($attribute['size'])) { throw new SQLGeneratorException("The enum values should be array"); } + if (count($attribute['size']) === 0) { + throw new SQLGeneratorException("The enum values cannot be empty."); + } + return $this->addColumn($column, 'enum', $attribute); } @@ -228,10 +228,6 @@ public function changeUuid(string $column, array $attribute = []): SQLGenerator return $this->changeColumn($column, 'varchar', $attribute); } - if (!isset($attribute['default']) && $this->adapter === 'pgsql') { - $attribute['default'] = 'uuid_generate_v4()'; - } - return $this->changeColumn($column, 'uuid', $attribute); } From f17203da5e7116e1622aa4ee61a368080dbc5166 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sat, 20 May 2023 12:58:51 +0000 Subject: [PATCH 026/199] Update pgsql migration generator unity tests --- .../Migration/Compose/PgsqlCompose.php | 10 +- src/Database/Migration/SQLGenerator.php | 2 +- .../Migration/Pgsql/SQLGeneratorTest.php | 24 ++-- .../Migration/Pgsql/SQLGenetorHelpersTest.php | 114 +++++++++--------- 4 files changed, 75 insertions(+), 75 deletions(-) diff --git a/src/Database/Migration/Compose/PgsqlCompose.php b/src/Database/Migration/Compose/PgsqlCompose.php index aa2bbb53..5b4847b1 100644 --- a/src/Database/Migration/Compose/PgsqlCompose.php +++ b/src/Database/Migration/Compose/PgsqlCompose.php @@ -87,16 +87,16 @@ private function composeAddPgsqlColumn(string $name, array $description): string } } - // Bind auto increment action - if ($increment) { - $type = 'SERIAL'; - } - // Bind precision if ($raw_type == "DOUBLE") { $type = sprintf('DOUBLE PRECISION', $type); } + // Bind auto increment action + if ($increment) { + $type = 'SERIAL'; + } + // Set column as primary key if ($primary) { $type = sprintf('%s PRIMARY KEY', $type); diff --git a/src/Database/Migration/SQLGenerator.php b/src/Database/Migration/SQLGenerator.php index f819e3e5..0bd28b04 100644 --- a/src/Database/Migration/SQLGenerator.php +++ b/src/Database/Migration/SQLGenerator.php @@ -158,7 +158,7 @@ public function renameColumn(string $name, string $new): SQLGenerator } if ($this->adapter === 'pgsql') { - $this->sqls[] = sprintf("RENAME COLUMN %s TO %s", $name, $new); + $this->sqls[] = sprintf('RENAME COLUMN "%s" TO %s', $name, $new); } else { $this->sqls[] = sprintf("RENAME COLUMN `%s` TO `%s`", $name, $new); } diff --git a/tests/Database/Migration/Pgsql/SQLGeneratorTest.php b/tests/Database/Migration/Pgsql/SQLGeneratorTest.php index 235debdd..a24ad598 100644 --- a/tests/Database/Migration/Pgsql/SQLGeneratorTest.php +++ b/tests/Database/Migration/Pgsql/SQLGeneratorTest.php @@ -26,19 +26,19 @@ public function test_add_column_sql_statement() { $this->generator->addColumn('name', 'int'); $sql = $this->generator->make(); - $this->assertEquals($sql, 'name INT NOT NULL'); + $this->assertEquals($sql, '"name" INT NOT NULL'); $this->generator->addColumn('name', 'string'); $sql = $this->generator->make(); - $this->assertNotEquals($sql, 'name STRING NOT NULL'); + $this->assertNotEquals($sql, '"name" STRING NOT NULL'); $this->generator->addColumn('name', 'string'); $sql = $this->generator->make(); - $this->assertEquals($sql, 'name VARCHAR(255) NOT NULL'); + $this->assertEquals($sql, '"name" VARCHAR(255) NOT NULL'); $this->generator->addColumn('name', 'string', ['default' => 'bow', 'size' => 100]); $sql = $this->generator->make(); - $this->assertEquals($sql, "name VARCHAR(100) NOT NULL DEFAULT 'bow'"); + $this->assertEquals($sql, '"name" VARCHAR(100) NOT NULL DEFAULT \'bow\''); } /** @@ -48,19 +48,19 @@ public function test_change_column_sql_statement() { $this->generator->changeColumn('name', 'int'); $sql = $this->generator->make(); - $this->assertEquals($sql, 'MODIFY COLUMN name INT NOT NULL'); + $this->assertEquals($sql, 'MODIFY COLUMN "name" INT NOT NULL'); $this->generator->changeColumn('name', 'string'); $sql = $this->generator->make(); - $this->assertNotEquals($sql, 'MODIFY COLUMN name STRING NOT NULL'); + $this->assertNotEquals($sql, 'MODIFY COLUMN "name" STRING NOT NULL'); $this->generator->changeColumn('name', 'string'); $sql = $this->generator->make(); - $this->assertEquals($sql, 'MODIFY COLUMN name VARCHAR(255) NOT NULL'); + $this->assertEquals($sql, 'MODIFY COLUMN "name" VARCHAR(255) NOT NULL'); $this->generator->changeColumn('name', 'string', ['default' => 'bow', 'size' => 100]); $sql = $this->generator->make(); - $this->assertEquals($sql, "MODIFY COLUMN name VARCHAR(100) NOT NULL DEFAULT 'bow'"); + $this->assertEquals($sql, 'MODIFY COLUMN "name" VARCHAR(100) NOT NULL DEFAULT \'bow\''); } /** @@ -70,7 +70,7 @@ public function test_rename_column_sql_statement_for_mysql() { $this->generator->renameColumn('name', 'fullname'); $sql = $this->generator->make(); - $this->assertEquals($sql, 'RENAME COLUMN name TO fullname'); + $this->assertEquals($sql, 'RENAME COLUMN "name" TO fullname'); } /** @@ -128,7 +128,7 @@ public function test_should_create_correct_datetime_sql_statement() $this->generator->addDatetime('created_at', ['default' => 'CURRENT_TIMESTAMP']); $sql = $this->generator->make(); - $this->assertEquals($sql, 'created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP'); + $this->assertEquals($sql, '"created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP'); } public function test_should_create_not_correct_datetime_sql_statement() @@ -136,7 +136,7 @@ public function test_should_create_not_correct_datetime_sql_statement() $this->generator->addDatetime('created_at'); $sql = $this->generator->make(); - $this->assertNotEquals($sql, 'created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP'); + $this->assertNotEquals($sql, '"created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP'); } public function test_should_create_correct_timestamps_sql_statement() @@ -144,6 +144,6 @@ public function test_should_create_correct_timestamps_sql_statement() $this->generator->addTimestamps(); $sql = $this->generator->make(); - $this->assertEquals($sql, 'created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP'); + $this->assertEquals($sql, '"created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP'); } } diff --git a/tests/Database/Migration/Pgsql/SQLGenetorHelpersTest.php b/tests/Database/Migration/Pgsql/SQLGenetorHelpersTest.php index 6334c144..67cd22e9 100644 --- a/tests/Database/Migration/Pgsql/SQLGenetorHelpersTest.php +++ b/tests/Database/Migration/Pgsql/SQLGenetorHelpersTest.php @@ -27,11 +27,11 @@ public function test_add_string_sql_statement(string $type, string $method, int| $type = strtoupper($type); $sql = $this->generator->{"add$method"}('name')->make(); - $this->assertNotEquals($sql, 'name STRING NOT NULL'); + $this->assertNotEquals($sql, '"name" STRING NOT NULL'); if (preg_match('/STRING|VARCHAR/', $type)) { - $this->assertEquals($sql, "name {$type}(255) NOT NULL"); + $this->assertEquals($sql, "\"name\" {$type}(255) NOT NULL"); } else { - $this->assertEquals($sql, "name {$type} NOT NULL"); + $this->assertEquals($sql, "\"name\" {$type} NOT NULL"); } if (preg_match('/TEXT/', $type)) { @@ -40,20 +40,20 @@ public function test_add_string_sql_statement(string $type, string $method, int| } $sql = $this->generator->{"add$method"}('name', ['default' => $default, 'size' => 100])->make(); if (preg_match('/STRING|VARCHAR/', $type)) { - $this->assertEquals($sql, "name {$type}(100) NOT NULL DEFAULT '$default'"); + $this->assertEquals($sql, "\"name\" {$type}(100) NOT NULL DEFAULT '$default'"); } $sql = $this->generator->{"add$method"}('name', ['default' => $default, 'size' => 100, 'nullable' => true])->make(); - $this->assertEquals($sql, "name {$type}(100) NULL DEFAULT '$default'"); + $this->assertEquals($sql, "\"name\" {$type}(100) NULL DEFAULT '$default'"); $sql = $this->generator->{"add$method"}('name', ['primary' => true])->make(); - $this->assertEquals($sql, "name {$type}(255) PRIMARY KEY NOT NULL"); + $this->assertEquals($sql, "\"name\" {$type}(255) PRIMARY KEY NOT NULL"); $sql = $this->generator->{"add$method"}('name', ['primary' => true, 'default' => $default, 'size' => 100, 'nullable' => true])->make(); - $this->assertEquals($sql, "name {$type}(100) PRIMARY KEY NULL DEFAULT '$default'"); + $this->assertEquals($sql, "\"name\" {$type}(100) PRIMARY KEY NULL DEFAULT '$default'"); $sql = $this->generator->{"add$method"}('name', ['unique' => true])->make(); - $this->assertEquals($sql, "name {$type}(255) UNIQUE NOT NULL"); + $this->assertEquals($sql, "\"name\" {$type}(255) UNIQUE NOT NULL"); } /** @@ -65,9 +65,9 @@ public function test_change_string_sql_statement(string $type, string $method, i $sql = $this->generator->{"change$method"}('name')->make(); if (preg_match('/STRING|VARCHAR/', $type)) { - $this->assertEquals($sql, "MODIFY COLUMN name {$type}(255) NOT NULL"); + $this->assertEquals($sql, "MODIFY COLUMN \"name\" {$type}(255) NOT NULL"); } else { - $this->assertEquals($sql, "MODIFY COLUMN name {$type} NOT NULL"); + $this->assertEquals($sql, "MODIFY COLUMN \"name\" {$type} NOT NULL"); } if (preg_match('/TEXT/', $type)) { @@ -76,20 +76,20 @@ public function test_change_string_sql_statement(string $type, string $method, i } $sql = $this->generator->{"change$method"}('name', ['default' => $default, 'size' => 100])->make(); if (preg_match('/STRING|VARCHAR/', $type)) { - $this->assertEquals($sql, "MODIFY COLUMN name {$type}(100) NOT NULL DEFAULT '$default'"); + $this->assertEquals($sql, "MODIFY COLUMN \"name\" {$type}(100) NOT NULL DEFAULT '$default'"); } $sql = $this->generator->{"change$method"}('name', ['default' => $default, 'size' => 100, 'nullable' => true])->make(); - $this->assertEquals($sql, "MODIFY COLUMN name {$type}(100) NULL DEFAULT '$default'"); + $this->assertEquals($sql, "MODIFY COLUMN \"name\" {$type}(100) NULL DEFAULT '$default'"); $sql = $this->generator->{"change$method"}('name', ['primary' => true])->make(); - $this->assertEquals($sql, "MODIFY COLUMN name {$type}(255) PRIMARY KEY NOT NULL"); + $this->assertEquals($sql, "MODIFY COLUMN \"name\" {$type}(255) PRIMARY KEY NOT NULL"); $sql = $this->generator->{"change$method"}('name', ['primary' => true, 'default' => $default, 'size' => 100, 'nullable' => true])->make(); - $this->assertEquals($sql, "MODIFY COLUMN name {$type}(100) PRIMARY KEY NULL DEFAULT '$default'"); + $this->assertEquals($sql, "MODIFY COLUMN \"name\" {$type}(100) PRIMARY KEY NULL DEFAULT '$default'"); $sql = $this->generator->{"change$method"}('name', ['unique' => true])->make(); - $this->assertEquals($sql, "MODIFY COLUMN name {$type}(255) UNIQUE NOT NULL"); + $this->assertEquals($sql, "MODIFY COLUMN \"name\" {$type}(255) UNIQUE NOT NULL"); } /** @@ -101,41 +101,41 @@ public function test_add_string_without_size_sql_statement(string $type, string $sql = $this->generator->{"add$method"}('name')->make(); $this->assertNotEquals($sql, 'name STRING NOT NULL'); - $this->assertEquals($sql, "name {$type} NOT NULL"); + $this->assertEquals($sql, "\"name\" {$type} NOT NULL"); $sql = $this->generator->{"add$method"}('name', ['default' => $default])->make(); if ($type === "CHAR") { - $this->assertEquals($sql, "name {$type} NOT NULL DEFAULT '$default'"); + $this->assertEquals($sql, "\"name\" {$type} NOT NULL DEFAULT '$default'"); } else { - $this->assertEquals($sql, "name {$type} NOT NULL DEFAULT $default"); + $this->assertEquals($sql, "\"name\" {$type} NOT NULL DEFAULT $default"); } $sql = $this->generator->{"add$method"}('name', ['default' => $default, 'size' => 100])->make(); if ($type === "CHAR") { - $this->assertEquals($sql, "name {$type} NOT NULL DEFAULT '$default'"); + $this->assertEquals($sql, "\"name\" {$type} NOT NULL DEFAULT '$default'"); } else { - $this->assertEquals($sql, "name {$type} NOT NULL DEFAULT $default"); + $this->assertEquals($sql, "\"name\" {$type} NOT NULL DEFAULT $default"); } $sql = $this->generator->{"add$method"}('name', ['default' => $default, 'nullable' => true])->make(); if ($type === "CHAR") { - $this->assertEquals($sql, "name {$type} NULL DEFAULT '$default'"); + $this->assertEquals($sql, "\"name\" {$type} NULL DEFAULT '$default'"); } else { - $this->assertEquals($sql, "name {$type} NULL DEFAULT $default"); + $this->assertEquals($sql, "\"name\" {$type} NULL DEFAULT $default"); } $sql = $this->generator->{"add$method"}('name', ['primary' => true])->make(); - $this->assertEquals($sql, "name {$type} PRIMARY KEY NOT NULL"); + $this->assertEquals($sql, "\"name\" {$type} PRIMARY KEY NOT NULL"); $sql = $this->generator->{"add$method"}('name', ['primary' => true, 'default' => $default, 'nullable' => true])->make(); if ($type === "CHAR") { - $this->assertEquals($sql, "name {$type} PRIMARY KEY NULL DEFAULT '$default'"); + $this->assertEquals($sql, "\"name\" {$type} PRIMARY KEY NULL DEFAULT '$default'"); } else { - $this->assertEquals($sql, "name {$type} PRIMARY KEY NULL DEFAULT $default"); + $this->assertEquals($sql, "\"name\" {$type} PRIMARY KEY NULL DEFAULT $default"); } $sql = $this->generator->{"add$method"}('name', ['unique' => true])->make(); - $this->assertEquals($sql, "name {$type} UNIQUE NOT NULL"); + $this->assertEquals($sql, "\"name\" {$type} UNIQUE NOT NULL"); } /** @@ -146,41 +146,41 @@ public function test_change_string_without_size_sql_statement(string $type, stri $type = strtoupper($type); $sql = $this->generator->{"change$method"}('name')->make(); - $this->assertEquals($sql, "MODIFY COLUMN name {$type} NOT NULL"); + $this->assertEquals($sql, "MODIFY COLUMN \"name\" {$type} NOT NULL"); $sql = $this->generator->{"change$method"}('name', ['default' => $default, 'size' => 100])->make(); if ($type === 'CHAR') { - $this->assertEquals($sql, "MODIFY COLUMN name {$type} NOT NULL DEFAULT '$default'"); + $this->assertEquals($sql, "MODIFY COLUMN \"name\" {$type} NOT NULL DEFAULT '$default'"); } else { - $this->assertEquals($sql, "MODIFY COLUMN name {$type} NOT NULL DEFAULT $default"); + $this->assertEquals($sql, "MODIFY COLUMN \"name\" {$type} NOT NULL DEFAULT $default"); } $sql = $this->generator->{"change$method"}('name', ['default' => $default])->make(); if ($type === 'CHAR') { - $this->assertEquals($sql, "MODIFY COLUMN name {$type} NOT NULL DEFAULT '$default'"); + $this->assertEquals($sql, "MODIFY COLUMN \"name\" {$type} NOT NULL DEFAULT '$default'"); } else { - $this->assertEquals($sql, "MODIFY COLUMN name {$type} NOT NULL DEFAULT $default"); + $this->assertEquals($sql, "MODIFY COLUMN \"name\" {$type} NOT NULL DEFAULT $default"); } $sql = $this->generator->{"change$method"}('name', ['default' => $default, 'nullable' => true])->make(); if ($type === 'CHAR') { - $this->assertEquals($sql, "MODIFY COLUMN name {$type} NULL DEFAULT '$default'"); + $this->assertEquals($sql, "MODIFY COLUMN \"name\" {$type} NULL DEFAULT '$default'"); } else { - $this->assertEquals($sql, "MODIFY COLUMN name {$type} NULL DEFAULT $default"); + $this->assertEquals($sql, "MODIFY COLUMN \"name\" {$type} NULL DEFAULT $default"); } $sql = $this->generator->{"change$method"}('name', ['primary' => true])->make(); - $this->assertEquals($sql, "MODIFY COLUMN name {$type} PRIMARY KEY NOT NULL"); + $this->assertEquals($sql, "MODIFY COLUMN \"name\" {$type} PRIMARY KEY NOT NULL"); $sql = $this->generator->{"change$method"}('name', ['primary' => true, 'default' => $default, 'nullable' => true])->make(); if ($type === 'CHAR') { - $this->assertEquals($sql, "MODIFY COLUMN name {$type} PRIMARY KEY NULL DEFAULT '$default'"); + $this->assertEquals($sql, "MODIFY COLUMN \"name\" {$type} PRIMARY KEY NULL DEFAULT '$default'"); } else { - $this->assertEquals($sql, "MODIFY COLUMN name {$type} PRIMARY KEY NULL DEFAULT $default"); + $this->assertEquals($sql, "MODIFY COLUMN \"name\" {$type} PRIMARY KEY NULL DEFAULT $default"); } $sql = $this->generator->{"change$method"}('name', ['unique' => true])->make(); - $this->assertEquals($sql, "MODIFY COLUMN name {$type} UNIQUE NOT NULL"); + $this->assertEquals($sql, "MODIFY COLUMN \"name\" {$type} UNIQUE NOT NULL"); } /** @@ -191,30 +191,30 @@ public function test_add_int_sql_statement(string $type, string $method, int|str $type = strtoupper($type); $sql = $this->generator->{"add$method"}('column')->make(); - $this->assertEquals($sql, "column {$type} NOT NULL"); + $this->assertEquals($sql, "\"column\" {$type} NOT NULL"); $sql = $this->generator->{"add$method"}('column', ['default' => $default, 'size' => 100])->make(); - $this->assertEquals($sql, "column {$type} NOT NULL DEFAULT $default"); + $this->assertEquals($sql, "\"column\" {$type} NOT NULL DEFAULT $default"); $sql = $this->generator->{"add$method"}('column', ['default' => $default, 'size' => 100, 'nullable' => true])->make(); - $this->assertEquals($sql, "column {$type} NULL DEFAULT $default"); + $this->assertEquals($sql, "\"column\" {$type} NULL DEFAULT $default"); $sql = $this->generator->{"add$method"}('column', ['primary' => true])->make(); - $this->assertEquals($sql, "column {$type} PRIMARY KEY NOT NULL"); + $this->assertEquals($sql, "\"column\" {$type} PRIMARY KEY NOT NULL"); $sql = $this->generator->{"add$method"}('column', ['primary' => true, 'default' => $default, 'size' => 100, 'nullable' => true])->make(); - $this->assertEquals($sql, "column {$type} PRIMARY KEY NULL DEFAULT $default"); + $this->assertEquals($sql, "\"column\" {$type} PRIMARY KEY NULL DEFAULT $default"); $sql = $this->generator->{"add$method"}('column', ['primary' => true, 'increment' => true, 'size' => 100, 'nullable' => true])->make(); - $this->assertEquals($sql, "column SERIAL PRIMARY KEY NULL"); + $this->assertEquals($sql, "\"column\" SERIAL PRIMARY KEY NULL"); $sql = $this->generator->{"add$method"}('column', ['unique' => true])->make(); - $this->assertEquals($sql, "column {$type} UNIQUE NOT NULL"); + $this->assertEquals($sql, "\"column\" {$type} UNIQUE NOT NULL"); $method = "add{$method}Increment"; if (method_exists($this->generator, $method)) { $sql = $this->generator->{$method}('column')->make(); - $this->assertEquals($sql, "column SERIAL PRIMARY KEY NOT NULL"); + $this->assertEquals($sql, "\"column\" SERIAL PRIMARY KEY NOT NULL"); } } @@ -226,40 +226,40 @@ public function test_change_int_sql_statement(string $type, string $method, int| $type = strtoupper($type); $sql = $this->generator->{"change$method"}('column')->make(); - $this->assertEquals($sql, "MODIFY COLUMN column {$type} NOT NULL"); + $this->assertEquals($sql, "MODIFY COLUMN \"column\" {$type} NOT NULL"); $sql = $this->generator->{"change$method"}('column', ['default' => $default, 'size' => 100])->make(); - $this->assertEquals($sql, "MODIFY COLUMN column {$type} NOT NULL DEFAULT $default"); + $this->assertEquals($sql, "MODIFY COLUMN \"column\" {$type} NOT NULL DEFAULT $default"); $sql = $this->generator->{"change$method"}('column', ['default' => $default, 'size' => 100, 'nullable' => true])->make(); - $this->assertEquals($sql, "MODIFY COLUMN column {$type} NULL DEFAULT $default"); + $this->assertEquals($sql, "MODIFY COLUMN \"column\" {$type} NULL DEFAULT $default"); $sql = $this->generator->{"change$method"}('column', ['primary' => true])->make(); - $this->assertEquals($sql, "MODIFY COLUMN column {$type} PRIMARY KEY NOT NULL"); + $this->assertEquals($sql, "MODIFY COLUMN \"column\" {$type} PRIMARY KEY NOT NULL"); $sql = $this->generator->{"change$method"}('column', ['primary' => true, 'default' => $default, 'size' => 100, 'nullable' => true])->make(); - $this->assertEquals($sql, "MODIFY COLUMN column {$type} PRIMARY KEY NULL DEFAULT $default"); + $this->assertEquals($sql, "MODIFY COLUMN \"column\" {$type} PRIMARY KEY NULL DEFAULT $default"); $sql = $this->generator->{"change$method"}('column', ['primary' => true, 'increment' => true, 'size' => 100, 'nullable' => true])->make(); - $this->assertEquals($sql, "MODIFY COLUMN column SERIAL PRIMARY KEY NULL"); + $this->assertEquals($sql, "MODIFY COLUMN \"column\" SERIAL PRIMARY KEY NULL"); $sql = $this->generator->{"change$method"}('column', ['unique' => true])->make(); - $this->assertEquals($sql, "MODIFY COLUMN column {$type} UNIQUE NOT NULL"); + $this->assertEquals($sql, "MODIFY COLUMN \"column\" {$type} UNIQUE NOT NULL"); $method = "change{$method}Increment"; if (method_exists($this->generator, $method)) { $sql = $this->generator->{$method}('column')->make(); - $this->assertEquals($sql, "MODIFY COLUMN column {$type} SERIAL PRIMARY KEY NOT NULL"); + $this->assertEquals($sql, "MODIFY COLUMN \"column\" {$type} SERIAL PRIMARY KEY NOT NULL"); } } public function test_uuid_statement() { $sql = $this->generator->addUuid('column', ['unique' => true])->make(); - $this->assertEquals($sql, "column UUID UNIQUE NOT NULL DEFAULT uuid_generate_v4()"); + $this->assertEquals($sql, "\"column\" UUID UNIQUE NOT NULL"); $sql = $this->generator->addUuid('column', ['primary' => true])->make(); - $this->assertEquals($sql, "column UUID PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4()"); + $this->assertEquals($sql, "\"column\" UUID PRIMARY KEY NOT NULL"); $this->expectException(SQLGeneratorException::class); $this->expectExceptionMessage("Cannot define the increment for uuid. You can use addUuidPrimary() instead"); @@ -269,7 +269,7 @@ public function test_uuid_statement() public function test_uuid_primary_statement() { $sql = $this->generator->addUuidPrimary('column')->make(); - $this->assertEquals($sql, "column UUID PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4()"); + $this->assertEquals($sql, "\"column\" UUID PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4()"); } public function test_uuid_should_throw_errors_with_increment_attribute() @@ -286,7 +286,7 @@ public function getNumberTypes() ["bigint", "BigInteger", 1], ["tinyint", "TinyInteger", 1], ["float", "Float", 1], - ["double", "Double", 1], + ["double precision", "Double", 1], ["smallint", "SmallInteger", 1], ["mediumint", "MediumInteger", 1], ]; From 812f4496713bec19d5915b73580cf337971e9ed8 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sat, 20 May 2023 13:04:42 +0000 Subject: [PATCH 027/199] Fixes undefined target variable --- src/Database/Migration/Shortcut/ConstraintColumn.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Database/Migration/Shortcut/ConstraintColumn.php b/src/Database/Migration/Shortcut/ConstraintColumn.php index 62b93fe6..b4d10121 100644 --- a/src/Database/Migration/Shortcut/ConstraintColumn.php +++ b/src/Database/Migration/Shortcut/ConstraintColumn.php @@ -28,6 +28,8 @@ public function addForeign(string $name, array $attributes = []): SQLGenerator if ($this->adapter == "pgsql") { $target = sprintf("\"%s_%s_foreign\"", $this->getTable(), $name); + } else { + $target = sprintf("%s_%s_foreign", $this->getTable(), $name); } if (isset($attributes['on'])) { From cf5705dc5b365b74ff4cd8dfd49357055d8cbf52 Mon Sep 17 00:00:00 2001 From: papac Date: Sat, 20 May 2023 13:13:08 +0000 Subject: [PATCH 028/199] Update CHANGELOG --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea92303e..0b4f56cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 5.0.5 - 2023-05-20 + +Release 5.0.5 + +- Fix migration status table definition +- Fix enum creation for pgsql + +Reference #232 + ## 5.0.4 - 2023-05-19 Release 5.0.4 From 14ae692b4b4fb033f032715c798af5636986e135 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sat, 20 May 2023 16:30:55 +0000 Subject: [PATCH 029/199] Fixes http client repsonse --- src/Testing/Response.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Testing/Response.php b/src/Testing/Response.php index 8372169a..c91a08f1 100644 --- a/src/Testing/Response.php +++ b/src/Testing/Response.php @@ -10,14 +10,14 @@ class Response { /** - * The http parser + * The http http_response * * @var HttpClientResponse */ private HttpClientResponse $http_response; /** - * The parser content + * The http_response content * * @var string */ @@ -93,7 +93,7 @@ public function assertContainsExactText(string $data, string $message = ''): Res */ public function assertHeader(string $header, string $message = ''): Response { - Assert::assertArrayHasKey($header, $this->parser->getHeaders(), $message); + Assert::assertArrayHasKey($header, $this->http_response->getHeaders(), $message); return $this; } @@ -107,7 +107,7 @@ public function assertHeader(string $header, string $message = ''): Response */ public function assertArray(string $message = ''): Response { - Assert::assertTrue(is_array($this->parser->toArray()), $message); + Assert::assertTrue(is_array($this->http_response->toArray()), $message); return $this; } @@ -122,7 +122,7 @@ public function assertArray(string $message = ''): Response */ public function assertContentType(string $content_type, string $message = ''): Response { - $type = $this->parser->getContentType(); + $type = $this->http_response->getContentType(); Assert::assertEquals( $content_type, @@ -198,7 +198,7 @@ public function assertContentTypeXml(string $message = ''): Response */ public function assertStatus(int $code, string $message = ''): Response { - Assert::assertEquals($this->parser->getCode(), $code, $message); + Assert::assertEquals($this->http_response->getCode(), $code, $message); return $this; } @@ -210,7 +210,7 @@ public function assertStatus(int $code, string $message = ''): Response */ public function assertKeyExists(string $key, string $message = ''): Response { - $data = $this->parser->toArray(); + $data = $this->http_response->toArray(); Assert::assertTrue(isset($data[$key]), $message); @@ -279,8 +279,8 @@ public function toArray(): array|object */ public function __call(string $method, array $params = []) { - if (method_exists($this->parser, $method)) { - return call_user_func([$this->parser, $method]); + if (method_exists($this->http_response, $method)) { + return call_user_func([$this->http_response, $method]); } throw new InvalidArgumentException( From f518c27fd353ac3a27bc9f751cca6c0a84a8367e Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sun, 21 May 2023 12:39:06 +0000 Subject: [PATCH 030/199] Fixes enum type bind for pgsql --- src/Database/Migration/Compose/PgsqlCompose.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Database/Migration/Compose/PgsqlCompose.php b/src/Database/Migration/Compose/PgsqlCompose.php index 5b4847b1..74ecaba5 100644 --- a/src/Database/Migration/Compose/PgsqlCompose.php +++ b/src/Database/Migration/Compose/PgsqlCompose.php @@ -81,7 +81,7 @@ private function composeAddPgsqlColumn(string $name, array $description): string $name, $size ); - $type = sprintf('%s_%s_enum', $this->table, $name); + $type = sprintf('%s_%s_enum', $table, $name); } else { $type = sprintf('TEXT CHECK (%s IN CHECK(%s))', $name, $size); } From fba46268e4165e1e04fcb98208614b30447cf580 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sun, 21 May 2023 12:39:45 +0000 Subject: [PATCH 031/199] Fixes display error message format --- src/Database/Migration/Migration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Database/Migration/Migration.php b/src/Database/Migration/Migration.php index 4cc70787..8b8d7903 100644 --- a/src/Database/Migration/Migration.php +++ b/src/Database/Migration/Migration.php @@ -123,7 +123,7 @@ final public function create(string $table, callable $cb): Migration try { $this->executeSqlQuery($sql); } catch (\Exception $exception) { - echo sprintf("%s %s\n", Color::yellow("Warning"), $exception->getMessage()); + echo sprintf("%s\n", Color::yellow("Warning: " . $exception->getMessage())); } } From 4b8163e5970b13395b0a528bd1920fc4cb9ca3ae Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sun, 21 May 2023 12:40:07 +0000 Subject: [PATCH 032/199] Fixes generate the table name from model name --- src/Database/Barry/Model.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Database/Barry/Model.php b/src/Database/Barry/Model.php index 5fcc7fb5..45f476b6 100644 --- a/src/Database/Barry/Model.php +++ b/src/Database/Barry/Model.php @@ -404,7 +404,7 @@ public static function query(): Builder if (!isset($properties['table']) || $properties['table'] == null) { $parts = explode('\\', static::class); - $table = Str::snake(end($parts)) . 's'; + $table = Str::lower(Str::snake(Str::plurial(end($parts)))); } else { $table = $properties['table']; } From 508f336ea51f8348b6708d3a4e54f41405d6b38b Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sun, 21 May 2023 18:33:55 +0000 Subject: [PATCH 033/199] Initialize the request ID --- src/Event/Contracts/EventListener.php | 4 ++-- src/Event/Event.php | 3 ++- src/Http/Request.php | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Event/Contracts/EventListener.php b/src/Event/Contracts/EventListener.php index 2f5c2255..d46cff90 100644 --- a/src/Event/Contracts/EventListener.php +++ b/src/Event/Contracts/EventListener.php @@ -9,8 +9,8 @@ interface EventListener /** * Process the event * - * @param mixed $payload + * @param AppEvent $event * @return mixed */ - public function process($payload): void; + public function process(AppEvent $event): void; } diff --git a/src/Event/Event.php b/src/Event/Event.php index 10f8237d..99029333 100644 --- a/src/Event/Event.php +++ b/src/Event/Event.php @@ -79,12 +79,13 @@ public static function once(string $event, callable|array|string $fn, int $prior */ public static function emit(string|AppEvent $event): ?bool { - $data = array_slice(func_get_args(), 1); $event_name = $event; if ($event instanceof AppEvent) { $event_name = get_class($event); $data = [$event]; + } else { + $data = array_slice(func_get_args(), 1); } if (!static::bound($event_name)) { diff --git a/src/Http/Request.php b/src/Http/Request.php index a73c16f7..273e22e1 100644 --- a/src/Http/Request.php +++ b/src/Http/Request.php @@ -49,6 +49,7 @@ class Request private function __construct() { $data = []; + $this->id = "req_" . sha1(uniqid() . time()); if ($this->getHeader('content-type') == 'application/json') { $data = json_decode(file_get_contents("php://input"), true); From 1559c0a4fb8915dc1c5e4fd535a0d6d4801f0fe8 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 22 May 2023 09:06:39 +0000 Subject: [PATCH 034/199] Refactoring custom validation message data parser --- src/Validation/FieldLexical.php | 54 +++++++++++++++++++++----------- src/Validation/stubs/lexical.php | 34 ++++++++++---------- 2 files changed, 53 insertions(+), 35 deletions(-) diff --git a/src/Validation/FieldLexical.php b/src/Validation/FieldLexical.php index 223cddf2..11792178 100644 --- a/src/Validation/FieldLexical.php +++ b/src/Validation/FieldLexical.php @@ -10,47 +10,65 @@ trait FieldLexical * Get error debugging information * * @param string $key - * @param string|array $attributes + * @param string|array $value * @return ?string */ - private function lexical(string $key, string|array $attributes): ?string + private function lexical(string $key, string|array $value): ?string { - if (is_string($attributes) && isset($this->messages[$attributes])) { - return $this->messages[$attributes][$key] ?? $this->messages[$attributes]; - } + $data = array_merge($this->inputs ?? [], ['attribute' => $value]); if ( - is_array($attributes) - && isset($attributes['attribute']) - && isset($this->messages[$attributes['attribute']]) + is_array($value) + && isset($value['attribute']) + && isset($this->messages[$value['attribute']]) ) { - return $this->messages[$attributes['attribute']][$key] ?? $this->messages[$attributes['attribute']]; + $message = $this->messages[$value['attribute']][$key] ?? $this->messages[$value['attribute']]; + + if (is_string($message)) { + return $this->parseAttribute($data, $message); + } } - if (is_string($attributes)) { - $attributes = ['attribute' => $attributes]; + if (is_string($value) && isset($this->messages[$value])) { + $message = $this->messages[$value][$key] ?? $this->messages[$value]; + + if (is_string($message)) { + return $this->parseAttribute($data, $message); + } } + return $this->parseFromTranslate($key, $data); + } + + /** + * Parse the translate content + * + * @param string $key + * @param array $data + * @return string + */ + private function parseFromTranslate(string $key, array $data) + { // Get lexical provided by dev app - $lexical = trans('validation.' . $key, $attributes); + $message = trans('validation.' . $key, $data); - if (is_null($lexical)) { - $lexical = $this->parseAttribute($attributes, $this->lexical[$key]); + if (is_null($message)) { + $message = $this->lexical[$key]; } - return $lexical; + return $this->parseAttribute($data, $message); } /** * Normalize beneficiaries * - * @param array $attributes + * @param array $attribute * @param string $lexical * @return string */ - private function parseAttribute(array $attributes, string $lexical): ?string + private function parseAttribute(array $attribute, string $lexical): ?string { - foreach ($attributes as $key => $value) { + foreach ($attribute as $key => $value) { $lexical = str_replace('{' . $key . '}', $value, $lexical); } diff --git a/src/Validation/stubs/lexical.php b/src/Validation/stubs/lexical.php index ae786e4c..b0fc4fb2 100644 --- a/src/Validation/stubs/lexical.php +++ b/src/Validation/stubs/lexical.php @@ -1,25 +1,25 @@ 'The field {attribute} must be an email.', - 'required' => 'The field {attribute} is required.', - 'empty' => 'The field {attribute} is missing in the fields to be validated.', - 'min' => 'The field {attribute} must be at least {length} characters long.', - 'max' => 'The field {attribute} must not exceed {length} characters.', - 'same' => 'The field {attribute} must be the same as {value}.', - 'number' => 'The field {attribute} must be a number.', - 'int' => 'The field {attribute} must be an integer.', - 'float' => 'The field {attribute} must be a decimal.', + 'email' => 'The {attribute} field must be an email.', + 'required' => 'The {attribute} field is required.', + 'empty' => 'The {attribute} field is missing in the fields to be validated.', + 'min' => 'The {attribute} field must be at least {length} characters long.', + 'max' => 'The {attribute} field must not exceed {length} characters.', + 'same' => 'The {attribute} field must be the same as {value}.', + 'number' => 'The {attribute} field must be a number.', + 'int' => 'The {attribute} field must be an integer.', + 'float' => 'The {attribute} field must be a decimal.', 'alphanum' => 'Only alphanumeric characters are allowed for field {attribute}.', - 'in' => 'The field {attribute} must be one of the following {value}.', - 'size' => 'The field {attribute} must be {length} characters long.', + 'in' => 'The {attribute} field must be one of the following {value}.', + 'size' => 'The {attribute} field must be {length} characters long.', 'lower' => 'Only lowercase letters are allowed for field {attribute}.', 'upper' => 'Only uppercase letters are allowed for field {attribute}.', 'alpha' => 'Only alphabetic characters are allowed for field {attribute}.', - 'exists' => 'The field {attribute} does not exists.', - 'not_exists' => 'The field {attribute} already exists.', - 'unique' => 'The field {attribute} must be unique.', - 'date' => 'The field {attribute} must use the format: yyyy-mm-dd', - 'datetime' => 'The field {attribute} must use the format: yyyy-mm-dd hh:mm:ss', - 'regex' => 'The field {attribute} does not match the pattern', + 'exists' => 'The {attribute} field does not exists.', + 'not_exists' => 'The {attribute} field already exists.', + 'unique' => 'The {attribute} field must be unique.', + 'date' => 'The {attribute} field must use the format: yyyy-mm-dd', + 'datetime' => 'The {attribute} field must use the format: yyyy-mm-dd hh:mm:ss', + 'regex' => 'The {attribute} field does not match the pattern', ]; From 64f401f88a7b4b4777166dd243cc85a210d02a31 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 22 May 2023 09:07:17 +0000 Subject: [PATCH 035/199] Update pgsql connector dsn builder --- .../Connection/Adapter/PostgreSQLAdapter.php | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/Database/Connection/Adapter/PostgreSQLAdapter.php b/src/Database/Connection/Adapter/PostgreSQLAdapter.php index 5ad0c8eb..7e8c3c31 100644 --- a/src/Database/Connection/Adapter/PostgreSQLAdapter.php +++ b/src/Database/Connection/Adapter/PostgreSQLAdapter.php @@ -60,6 +60,30 @@ public function connection(): void // Formatting connection parameters $dsn = sprintf("pgsql:host=%s;port=%s;dbname=%s", $hostname, $port, $this->config['database']); + if (isset($$this->config['sslmode'])) { + $dsn .= ';sslmode=' . $$this->config['sslmode']; + } + + if (isset($$this->config['sslrootcert'])) { + $dsn .= ';sslrootcert=' . $$this->config['sslrootcert']; + } + + if (isset($$this->config['sslcert'])) { + $dsn .= ';sslcert=' . $$this->config['sslcert']; + } + + if (isset($$this->config['sslkey'])) { + $dsn .= ';sslkey=' . $$this->config['sslkey']; + } + + if (isset($$this->config['sslcrl'])) { + $dsn .= ';sslcrl=' . $$this->config['sslcrl']; + } + + if (isset($$this->config['application_name'])) { + $dsn .= ';application_name=' . $$this->config['application_name']; + } + $username = $this->config["username"]; $password = $this->config["password"]; @@ -71,5 +95,9 @@ public function connection(): void // Build the PDO connection $this->pdo = new PDO($dsn, $username, $password, $options); + + if ($this->config["charset"]) { + $this->pdo->query('SET NAMES \'' . $this->config["charset"] . '\''); + } } } From 1e0bbf734270532a63246d40369dcfecfce5b43e Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 22 May 2023 09:08:00 +0000 Subject: [PATCH 036/199] Change event name and get last_insert_id mechanism --- src/Database/Barry/Model.php | 104 ++++++++++++++++++----- src/Database/Barry/Traits/EventTrait.php | 6 +- 2 files changed, 87 insertions(+), 23 deletions(-) diff --git a/src/Database/Barry/Model.php b/src/Database/Barry/Model.php index 45f476b6..bd697815 100644 --- a/src/Database/Barry/Model.php +++ b/src/Database/Barry/Model.php @@ -306,8 +306,11 @@ public static function create(array $data): Model } // Check if the primary key existe on updating data - if (!array_key_exists($model->primary_key, $data)) { - if ($model->auto_increment && static::$builder->getAdapterName() !== "pgsql") { + if ( + !array_key_exists($model->primary_key, $data) + && static::$builder->getAdapterName() !== "pgsql" + ) { + if ($model->auto_increment) { $id_value = [$model->primary_key => null]; $data = array_merge($id_value, $data); } elseif ($model->primary_key_type == 'string') { @@ -319,11 +322,7 @@ public static function create(array $data): Model // Override the olds model attributes $model->setAttributes($data); - - if ($model->save() == 1) { - // Throw the onCreated event - $model->fireEvent('onCreated'); - } + $model->save(); return $model; } @@ -349,7 +348,33 @@ public static function paginate(int $page_number, int $current = 0, ?int $chunk */ public static function deleted(callable $cb): void { - $env = static::formatEventName('onDeleted'); + $env = static::formatEventName('model.deleted'); + + event()->once($env, $cb); + } + + /** + * Allows to associate listener + * + * @param callable $cb + * @throws + */ + public static function deleting(callable $cb): void + { + $env = static::formatEventName('model.deleted'); + + event()->once($env, $cb); + } + + /** + * Allows to associate a listener + * + * @param callable $cb + * @throws + */ + public static function creating(callable $cb): void + { + $env = static::formatEventName('model.creating'); event()->once($env, $cb); } @@ -362,7 +387,20 @@ public static function deleted(callable $cb): void */ public static function created(callable $cb): void { - $env = static::formatEventName('onCreated'); + $env = static::formatEventName('model.created'); + + event()->once($env, $cb); + } + + /** + * Allows to associate a listener + * + * @param callable $cb + * @throws + */ + public static function updating(callable $cb): void + { + $env = static::formatEventName('model.updating'); event()->once($env, $cb); } @@ -375,7 +413,7 @@ public static function created(callable $cb): void */ public static function updated(callable $cb): void { - $env = static::formatEventName('onUpdated'); + $env = static::formatEventName('model.updated'); event()->once($env, $cb); } @@ -448,13 +486,18 @@ public function save() // If primary key value is null, we are going to start the creation of new row if ($primary_key_value == null) { + // Fire the creating event + $this->fireEvent('model.creating'); + // Insert information in the database $row_affected = $model->insert($this->attributes); // We get a last insertion id value if (static::$builder->getAdapterName() == 'pgsql') { - $sequence = $this->table . "_" . $this->primary_key . '_seq'; - $primary_key_value = static::$builder->getPdo()->lastInsertId($sequence); + if ($this->auto_increment) { + $sequence = $this->table . "_" . $this->primary_key . '_seq'; + $primary_key_value = static::$builder->getPdo()->lastInsertId($sequence); + } } else { $primary_key_value = static::$builder->getPdo()->lastInsertId(); } @@ -466,7 +509,7 @@ public function save() $this->original = $this->attributes; if ($row_affected == 1) { - $this->fireEvent('onCreated'); + $this->fireEvent('model.created'); } return $row_affected; @@ -495,11 +538,15 @@ public function save() $update_data = $this->original; } + // Fire the updating event + $this->fireEvent('model.updating'); + + // Execute update model $updated = $model->where($this->primary_key, $primary_key_value)->update($update_data); // Fire the updated event if there are affected row if ($updated) { - $this->fireEvent('onUpdated'); + $this->fireEvent('model.updated'); } return $updated; @@ -516,9 +563,7 @@ private function transtypeKeyValue(mixed $primary_key_value): mixed // Transtype value to the define primary key type if ($this->primary_key_type == 'int') { $primary_key_value = (int) $primary_key_value; - } elseif ($this->primary_key_type == 'float') { - $primary_key_value = (float) $primary_key_value; - } elseif ($this->primary_key_type == 'double') { + } elseif ($this->primary_key_type == 'float' || $this->primary_key_type == 'double') { $primary_key_value = (float) $primary_key_value; } else { $primary_key_value = (string) $primary_key_value; @@ -538,8 +583,9 @@ public function update(array $attributes): int { $primary_key_value = $this->getKeyValue(); + // return 0 if the primary key is not define for update if ($primary_key_value == null) { - return 0; + return false; } $model = static::query(); @@ -558,9 +604,12 @@ public function update(array $attributes): int } } + // Fire the updating event + $this->fireEvent('model.updating'); + // When the data for updating list is empty, we load the original data if (count($data_for_updating) == 0) { - $this->fireEvent('onUpdated'); + $this->fireEvent('model.updated'); return true; } @@ -569,7 +618,7 @@ public function update(array $attributes): int // Fire the updated event if there are affected row if ($updated) { - $this->fireEvent('onUpdated'); + $this->fireEvent('model.updated'); } return $updated; @@ -595,12 +644,15 @@ public function delete(): int return 0; } + // Fire the deleting event + $this->fireEvent('model.deleting'); + // We apply the delete action $deleted = $model->where($this->primary_key, $primary_key_value)->delete(); // Fire the deleted event if there are affected row if ($deleted) { - $this->fireEvent('onDeleted'); + $this->fireEvent('model.deleted'); } return $deleted; @@ -642,6 +694,16 @@ public function getKey(): string return $this->primary_key; } + /** + * Retrieves the primary key key + * + * @return string + */ + public function getKeyType(): string + { + return $this->primary_key_type; + } + /** * Used to update the timestamp. * diff --git a/src/Database/Barry/Traits/EventTrait.php b/src/Database/Barry/Traits/EventTrait.php index 895fea23..9b535673 100644 --- a/src/Database/Barry/Traits/EventTrait.php +++ b/src/Database/Barry/Traits/EventTrait.php @@ -16,7 +16,9 @@ trait EventTrait */ private static function formatEventName(string $event): string { - return str_replace('\\', '', strtolower(Str::snake(static::class))) . '.' . Str::snake($event); + $class_name = str_replace('\\', '', strtolower(Str::snake(static::class))); + + return sprintf("%s.%s", $class_name, strtolower($event)); } /** @@ -26,7 +28,7 @@ private static function formatEventName(string $event): string */ private function fireEvent(string $event): void { - $env = $this->formatEventName($event); + $env = static::formatEventName($event); if (event()->bound($env)) { event()->emit($env, $this); From 472f628a1ee693c7d36c937fda503ed431c7fcc2 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 22 May 2023 09:08:49 +0000 Subject: [PATCH 037/199] Refactoring pgsql migration composer --- src/Database/Migration/Compose/PgsqlCompose.php | 11 ++++++++--- src/Database/Migration/Migration.php | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Database/Migration/Compose/PgsqlCompose.php b/src/Database/Migration/Compose/PgsqlCompose.php index 74ecaba5..48aa0f03 100644 --- a/src/Database/Migration/Compose/PgsqlCompose.php +++ b/src/Database/Migration/Compose/PgsqlCompose.php @@ -6,14 +6,19 @@ trait PgsqlCompose { + /** + * Define the query for create the custom type + * + * @var array + */ protected array $custom_types = []; /** - * Generate the custom type for pgsql + * Get the custom type for pgsql * * @return array */ - public function generateCustomTypes(): array + public function getCustomTypeQueries(): array { return $this->custom_types; } @@ -94,7 +99,7 @@ private function composeAddPgsqlColumn(string $name, array $description): string // Bind auto increment action if ($increment) { - $type = 'SERIAL'; + $type = $raw_type == "INT" ? "SERIAL" : "BIGSERIAL"; } // Set column as primary key diff --git a/src/Database/Migration/Migration.php b/src/Database/Migration/Migration.php index 8b8d7903..e07bb024 100644 --- a/src/Database/Migration/Migration.php +++ b/src/Database/Migration/Migration.php @@ -119,7 +119,7 @@ final public function create(string $table, callable $cb): Migration return $this->executeSqlQuery($sql); } - foreach ($generator->generateCustomTypes() as $sql) { + foreach ($generator->getCustomTypeQueries() as $sql) { try { $this->executeSqlQuery($sql); } catch (\Exception $exception) { From c4700b98e33c2e86396548d3087518b3740af77e Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 22 May 2023 19:54:07 +0000 Subject: [PATCH 038/199] Proto formatting --- src/Database/QueryBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Database/QueryBuilder.php b/src/Database/QueryBuilder.php index 05bea7b3..97a55c09 100644 --- a/src/Database/QueryBuilder.php +++ b/src/Database/QueryBuilder.php @@ -1171,7 +1171,7 @@ public function drop(): bool * @param int $chunk * @return array */ - public function paginate(int $number_of_page, int $current = 0, int $chunk = null): array + public function paginate(int $number_of_page, int $current = 0, ?int $chunk = null): array { // We go to back page --$current; From 95a621a16dd8b77f69580f5081fe327f16e64136 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 22 May 2023 19:57:54 +0000 Subject: [PATCH 039/199] Fixes variable definition errors --- .../Connection/Adapter/PostgreSQLAdapter.php | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Database/Connection/Adapter/PostgreSQLAdapter.php b/src/Database/Connection/Adapter/PostgreSQLAdapter.php index 7e8c3c31..6e92cfe7 100644 --- a/src/Database/Connection/Adapter/PostgreSQLAdapter.php +++ b/src/Database/Connection/Adapter/PostgreSQLAdapter.php @@ -60,28 +60,28 @@ public function connection(): void // Formatting connection parameters $dsn = sprintf("pgsql:host=%s;port=%s;dbname=%s", $hostname, $port, $this->config['database']); - if (isset($$this->config['sslmode'])) { - $dsn .= ';sslmode=' . $$this->config['sslmode']; + if (isset($this->config['sslmode'])) { + $dsn .= ';sslmode=' . $this->config['sslmode']; } - if (isset($$this->config['sslrootcert'])) { - $dsn .= ';sslrootcert=' . $$this->config['sslrootcert']; + if (isset($this->config['sslrootcert'])) { + $dsn .= ';sslrootcert=' . $this->config['sslrootcert']; } - if (isset($$this->config['sslcert'])) { - $dsn .= ';sslcert=' . $$this->config['sslcert']; + if (isset($this->config['sslcert'])) { + $dsn .= ';sslcert=' . $this->config['sslcert']; } - if (isset($$this->config['sslkey'])) { - $dsn .= ';sslkey=' . $$this->config['sslkey']; + if (isset($this->config['sslkey'])) { + $dsn .= ';sslkey=' . $this->config['sslkey']; } - if (isset($$this->config['sslcrl'])) { - $dsn .= ';sslcrl=' . $$this->config['sslcrl']; + if (isset($this->config['sslcrl'])) { + $dsn .= ';sslcrl=' . $this->config['sslcrl']; } - if (isset($$this->config['application_name'])) { - $dsn .= ';application_name=' . $$this->config['application_name']; + if (isset($this->config['application_name'])) { + $dsn .= ';application_name=' . $this->config['application_name']; } $username = $this->config["username"]; From 77127d211cd92b72f24519cd631f7377a797c12b Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 22 May 2023 20:27:20 +0000 Subject: [PATCH 040/199] Fixes translate parser --- src/Translate/README.md | 8 +++----- src/Translate/TranslatorConfiguration.php | 21 +++++++++++++-------- src/Validation/FieldLexical.php | 19 +++++++++++-------- tests/Config/stubs/translate.php | 19 +++++++++++++++++++ tests/Translate/TranslationTest.php | 4 +++- tests/Validation/ValidationTest.php | 14 +++++--------- 6 files changed, 54 insertions(+), 31 deletions(-) create mode 100644 tests/Config/stubs/translate.php diff --git a/src/Translate/README.md b/src/Translate/README.md index ab5824bf..4c826aaa 100644 --- a/src/Translate/README.md +++ b/src/Translate/README.md @@ -1,8 +1,8 @@ # Bow Translate -Bow Framework's translate system is a very simple translate api. He support plurialize. +Bow Framework's translation system is a very simple translation API. Also supports pluralization. -This the sample configuration +This is the sample configuration ```php // frontend/lang/en/messages.php @@ -11,14 +11,12 @@ return [ ]; ``` -Let's show a little exemple: +Let's show a little example: ```php use Bow\Translate\Translator; echo Translator::translate('messages.welcome'); - // Or - echo trans('messages.welcome'); ``` diff --git a/src/Translate/TranslatorConfiguration.php b/src/Translate/TranslatorConfiguration.php index c198928d..cdf28e25 100644 --- a/src/Translate/TranslatorConfiguration.php +++ b/src/Translate/TranslatorConfiguration.php @@ -15,15 +15,20 @@ class TranslatorConfiguration extends Configuration public function create(Loader $config): void { $this->container->bind('translate', function () use ($config) { - $auto_detected = is_null($config['translate.auto_detected']) - ? false - : $config['translate.auto_detected']; + $auto_detected = $config['translate.auto_detected'] ?? false; + $lang = $config['translate.lang']; + $dictionary = $config['translate.dictionary']; - return Translator::configure( - $config['translate.lang'], - $config['translate.dictionary'], - $auto_detected - ); + if ($auto_detected) { + $lang = app("request")->lang(); + if (is_string($lang)) { + $lang = strtolower($lang); + } else { + $lang = $config['translate.lang']; + } + } + + return Translator::configure($lang, $dictionary, $auto_detected); }); } diff --git a/src/Validation/FieldLexical.php b/src/Validation/FieldLexical.php index 11792178..50d68bb3 100644 --- a/src/Validation/FieldLexical.php +++ b/src/Validation/FieldLexical.php @@ -15,18 +15,21 @@ trait FieldLexical */ private function lexical(string $key, string|array $value): ?string { - $data = array_merge($this->inputs ?? [], ['attribute' => $value]); + $data = array_merge( + $this->inputs ?? [], + is_array($value) ? $value : ['attribute' => $value] + ); - if ( - is_array($value) - && isset($value['attribute']) - && isset($this->messages[$value['attribute']]) - ) { - $message = $this->messages[$value['attribute']][$key] ?? $this->messages[$value['attribute']]; + if (is_array($value) && isset($value['attribute'])) { + $message = $this->messages[$value['attribute']][$key] ?? $this->messages[$value['attribute']] ?? null; if (is_string($message)) { return $this->parseAttribute($data, $message); } + + if (is_null($message)) { + return $this->parseFromTranslate($key, $data); + } } if (is_string($value) && isset($this->messages[$value])) { @@ -69,7 +72,7 @@ private function parseFromTranslate(string $key, array $data) private function parseAttribute(array $attribute, string $lexical): ?string { foreach ($attribute as $key => $value) { - $lexical = str_replace('{' . $key . '}', $value, $lexical); + $lexical = str_replace('{' . $key . '}', (string) $value, $lexical); } return $lexical; diff --git a/tests/Config/stubs/translate.php b/tests/Config/stubs/translate.php new file mode 100644 index 00000000..a2c68bbb --- /dev/null +++ b/tests/Config/stubs/translate.php @@ -0,0 +1,19 @@ + 'fr', + + /** + * When the value is true, the translation system + * will detect the language of the client and will translate according to + */ + 'auto_detected' => false, + + /** + * Path to the language repeater + */ + 'dictionary' => __DIR__ . '/../Translate/stubs', +]; diff --git a/tests/Translate/TranslationTest.php b/tests/Translate/TranslationTest.php index 5b10b64c..4f3f8a18 100644 --- a/tests/Translate/TranslationTest.php +++ b/tests/Translate/TranslationTest.php @@ -3,12 +3,14 @@ namespace Bow\Tests\Translate; use Bow\Translate\Translator; +use Bow\Tests\Config\TestingConfiguration; class TranslationTest extends \PHPUnit\Framework\TestCase { public static function setUpBeforeClass(): void { - Translator::configure('fr', __DIR__ . '/stubs'); + $config = TestingConfiguration::getConfig(); + Translator::configure($config['translate.lang'], $config["translate.dictionary"]); } public function test_fr_welcome_message() diff --git a/tests/Validation/ValidationTest.php b/tests/Validation/ValidationTest.php index bee5cba4..b9b69f54 100644 --- a/tests/Validation/ValidationTest.php +++ b/tests/Validation/ValidationTest.php @@ -3,21 +3,17 @@ namespace Bow\Tests\Validation; use Bow\Database\Database; -use Bow\Tests\Config\TestingConfiguration; +use Bow\Translate\Translator; use Bow\Validation\Validator; +use Bow\Tests\Config\TestingConfiguration; class ValidationTest extends \PHPUnit\Framework\TestCase { - private static Database $database; - public static function setUpBeforeClass(): void { - static::$database = Database::getInstance(); - - if (!static::$database) { - $configuration = TestingConfiguration::getConfig(); - Database::configure($configuration["database"]); - } + $config = TestingConfiguration::getConfig(); + Database::configure($config["database"]); + Translator::configure($config['translate.lang'], $config["translate.dictionary"]); Database::statement("create table if not exists pets (id int primary key, name varchar(225));"); Database::table("pets")->truncate(); From d1764ee264cc7ae6ff535b6597a0a6e5981f102d Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 22 May 2023 20:33:06 +0000 Subject: [PATCH 041/199] Fixes migration unity tests --- tests/Database/Migration/Pgsql/SQLGenetorHelpersTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Database/Migration/Pgsql/SQLGenetorHelpersTest.php b/tests/Database/Migration/Pgsql/SQLGenetorHelpersTest.php index 67cd22e9..c0eaf3cf 100644 --- a/tests/Database/Migration/Pgsql/SQLGenetorHelpersTest.php +++ b/tests/Database/Migration/Pgsql/SQLGenetorHelpersTest.php @@ -206,7 +206,7 @@ public function test_add_int_sql_statement(string $type, string $method, int|str $this->assertEquals($sql, "\"column\" {$type} PRIMARY KEY NULL DEFAULT $default"); $sql = $this->generator->{"add$method"}('column', ['primary' => true, 'increment' => true, 'size' => 100, 'nullable' => true])->make(); - $this->assertEquals($sql, "\"column\" SERIAL PRIMARY KEY NULL"); + $this->assertEquals($sql, "\"column\" BIGSERIAL PRIMARY KEY NULL"); $sql = $this->generator->{"add$method"}('column', ['unique' => true])->make(); $this->assertEquals($sql, "\"column\" {$type} UNIQUE NOT NULL"); @@ -214,7 +214,7 @@ public function test_add_int_sql_statement(string $type, string $method, int|str $method = "add{$method}Increment"; if (method_exists($this->generator, $method)) { $sql = $this->generator->{$method}('column')->make(); - $this->assertEquals($sql, "\"column\" SERIAL PRIMARY KEY NOT NULL"); + $this->assertEquals($sql, "\"column\" BIGSERIAL PRIMARY KEY NOT NULL"); } } @@ -241,7 +241,7 @@ public function test_change_int_sql_statement(string $type, string $method, int| $this->assertEquals($sql, "MODIFY COLUMN \"column\" {$type} PRIMARY KEY NULL DEFAULT $default"); $sql = $this->generator->{"change$method"}('column', ['primary' => true, 'increment' => true, 'size' => 100, 'nullable' => true])->make(); - $this->assertEquals($sql, "MODIFY COLUMN \"column\" SERIAL PRIMARY KEY NULL"); + $this->assertEquals($sql, "MODIFY COLUMN \"column\" BIGSERIAL PRIMARY KEY NULL"); $sql = $this->generator->{"change$method"}('column', ['unique' => true])->make(); $this->assertEquals($sql, "MODIFY COLUMN \"column\" {$type} UNIQUE NOT NULL"); @@ -249,7 +249,7 @@ public function test_change_int_sql_statement(string $type, string $method, int| $method = "change{$method}Increment"; if (method_exists($this->generator, $method)) { $sql = $this->generator->{$method}('column')->make(); - $this->assertEquals($sql, "MODIFY COLUMN \"column\" {$type} SERIAL PRIMARY KEY NOT NULL"); + $this->assertEquals($sql, "MODIFY COLUMN \"column\" {$type} BIGSERIAL PRIMARY KEY NOT NULL"); } } From c2cd31e72c202a6331839d4a083b058ec308b4b2 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 22 May 2023 20:39:50 +0000 Subject: [PATCH 042/199] Fixes translate stubs configuration --- tests/Config/stubs/translate.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Config/stubs/translate.php b/tests/Config/stubs/translate.php index a2c68bbb..38c282b8 100644 --- a/tests/Config/stubs/translate.php +++ b/tests/Config/stubs/translate.php @@ -15,5 +15,5 @@ /** * Path to the language repeater */ - 'dictionary' => __DIR__ . '/../Translate/stubs', + 'dictionary' => __DIR__ . '/../../Translate/stubs', ]; From f7e7b02f62bc4b70c6517f85834d6715c06ad3e3 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 22 May 2023 20:44:42 +0000 Subject: [PATCH 043/199] Fixes getAttribute finder --- src/Database/Barry/Model.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Database/Barry/Model.php b/src/Database/Barry/Model.php index bd697815..bae57883 100644 --- a/src/Database/Barry/Model.php +++ b/src/Database/Barry/Model.php @@ -681,7 +681,7 @@ public static function deleteBy($column, $value): int */ public function getKeyValue(): mixed { - return $this->original[$this->primary_key] ?? null; + return $this->original[$this->primary_key] ?? $this->attributes[$this->primary_key] ?? null; } /** From 2e4b1eae3fe234d93756bc82f25b5098773129b1 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 22 May 2023 20:51:18 +0000 Subject: [PATCH 044/199] Fixes pgsql serial generation logic --- src/Database/Migration/Compose/PgsqlCompose.php | 2 +- .../Database/Migration/Pgsql/SQLGenetorHelpersTest.php | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Database/Migration/Compose/PgsqlCompose.php b/src/Database/Migration/Compose/PgsqlCompose.php index 48aa0f03..35473c6b 100644 --- a/src/Database/Migration/Compose/PgsqlCompose.php +++ b/src/Database/Migration/Compose/PgsqlCompose.php @@ -99,7 +99,7 @@ private function composeAddPgsqlColumn(string $name, array $description): string // Bind auto increment action if ($increment) { - $type = $raw_type == "INT" ? "SERIAL" : "BIGSERIAL"; + $type = in_array($raw_type, ["INT", "TINYINT", "SMALLINT"]) ? "SERIAL" : "BIGSERIAL"; } // Set column as primary key diff --git a/tests/Database/Migration/Pgsql/SQLGenetorHelpersTest.php b/tests/Database/Migration/Pgsql/SQLGenetorHelpersTest.php index c0eaf3cf..ab046d86 100644 --- a/tests/Database/Migration/Pgsql/SQLGenetorHelpersTest.php +++ b/tests/Database/Migration/Pgsql/SQLGenetorHelpersTest.php @@ -189,6 +189,7 @@ public function test_change_string_without_size_sql_statement(string $type, stri public function test_add_int_sql_statement(string $type, string $method, int|string $default = 1) { $type = strtoupper($type); + $serial = in_array($type, ["INT", "TINYINT", "SMALLINT"]) ? "SERIAL" : "BIGSERIAL"; $sql = $this->generator->{"add$method"}('column')->make(); $this->assertEquals($sql, "\"column\" {$type} NOT NULL"); @@ -206,7 +207,7 @@ public function test_add_int_sql_statement(string $type, string $method, int|str $this->assertEquals($sql, "\"column\" {$type} PRIMARY KEY NULL DEFAULT $default"); $sql = $this->generator->{"add$method"}('column', ['primary' => true, 'increment' => true, 'size' => 100, 'nullable' => true])->make(); - $this->assertEquals($sql, "\"column\" BIGSERIAL PRIMARY KEY NULL"); + $this->assertEquals($sql, "\"column\" $serial PRIMARY KEY NULL"); $sql = $this->generator->{"add$method"}('column', ['unique' => true])->make(); $this->assertEquals($sql, "\"column\" {$type} UNIQUE NOT NULL"); @@ -214,7 +215,7 @@ public function test_add_int_sql_statement(string $type, string $method, int|str $method = "add{$method}Increment"; if (method_exists($this->generator, $method)) { $sql = $this->generator->{$method}('column')->make(); - $this->assertEquals($sql, "\"column\" BIGSERIAL PRIMARY KEY NOT NULL"); + $this->assertEquals($sql, "\"column\" $serial PRIMARY KEY NOT NULL"); } } @@ -224,6 +225,7 @@ public function test_add_int_sql_statement(string $type, string $method, int|str public function test_change_int_sql_statement(string $type, string $method, int|string $default = 1) { $type = strtoupper($type); + $serial = in_array($type, ["INT", "TINYINT", "SMALLINT"]) ? "SERIAL" : "BIGSERIAL"; $sql = $this->generator->{"change$method"}('column')->make(); $this->assertEquals($sql, "MODIFY COLUMN \"column\" {$type} NOT NULL"); @@ -241,7 +243,7 @@ public function test_change_int_sql_statement(string $type, string $method, int| $this->assertEquals($sql, "MODIFY COLUMN \"column\" {$type} PRIMARY KEY NULL DEFAULT $default"); $sql = $this->generator->{"change$method"}('column', ['primary' => true, 'increment' => true, 'size' => 100, 'nullable' => true])->make(); - $this->assertEquals($sql, "MODIFY COLUMN \"column\" BIGSERIAL PRIMARY KEY NULL"); + $this->assertEquals($sql, "MODIFY COLUMN \"column\" $serial PRIMARY KEY NULL"); $sql = $this->generator->{"change$method"}('column', ['unique' => true])->make(); $this->assertEquals($sql, "MODIFY COLUMN \"column\" {$type} UNIQUE NOT NULL"); @@ -249,7 +251,7 @@ public function test_change_int_sql_statement(string $type, string $method, int| $method = "change{$method}Increment"; if (method_exists($this->generator, $method)) { $sql = $this->generator->{$method}('column')->make(); - $this->assertEquals($sql, "MODIFY COLUMN \"column\" {$type} BIGSERIAL PRIMARY KEY NOT NULL"); + $this->assertEquals($sql, "MODIFY COLUMN \"column\" {$type} $serial PRIMARY KEY NOT NULL"); } } From 0ac3e090debc95b6224ba1eae42af6cdefafadc8 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 22 May 2023 21:14:45 +0000 Subject: [PATCH 045/199] Fixes save row from model --- src/Database/Barry/Model.php | 93 +++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 39 deletions(-) diff --git a/src/Database/Barry/Model.php b/src/Database/Barry/Model.php index bae57883..0e07a98a 100644 --- a/src/Database/Barry/Model.php +++ b/src/Database/Barry/Model.php @@ -470,55 +470,70 @@ public static function query(): Builder } /** - * Save aliases on insert action + * Create the new row * + * @param Builder $model * @return int - * @throws */ - public function save() + private function writeRows(Builder $builder) { - $model = static::query(); + // Fire the creating event + $this->fireEvent('model.creating'); - /** - * Get the current primary key value - */ $primary_key_value = $this->getKeyValue(); - // If primary key value is null, we are going to start the creation of new row - if ($primary_key_value == null) { - // Fire the creating event - $this->fireEvent('model.creating'); - - // Insert information in the database - $row_affected = $model->insert($this->attributes); + // Insert information in the database + $row_affected = $builder->insert($this->attributes); - // We get a last insertion id value - if (static::$builder->getAdapterName() == 'pgsql') { - if ($this->auto_increment) { - $sequence = $this->table . "_" . $this->primary_key . '_seq'; - $primary_key_value = static::$builder->getPdo()->lastInsertId($sequence); - } - } else { - $primary_key_value = static::$builder->getPdo()->lastInsertId(); + // We get a last insertion id value + if (static::$builder->getAdapterName() == 'pgsql') { + if ($this->auto_increment) { + $sequence = $this->table . "_" . $this->primary_key . '_seq'; + $primary_key_value = static::$builder->getPdo()->lastInsertId($sequence); } + } else { + $primary_key_value = static::$builder->getPdo()->lastInsertId(); + } - $primary_key_value = !is_numeric($primary_key_value) ? $primary_key_value : (int) $primary_key_value; + if (is_null($primary_key_value)) { + $primary_key_value = $this->attributes[$this->primary_key] ?? null; + } - // Set the primary key value - $this->attributes[$this->primary_key] = $primary_key_value; - $this->original = $this->attributes; + $primary_key_value = !is_numeric($primary_key_value) ? $primary_key_value : (int) $primary_key_value; - if ($row_affected == 1) { - $this->fireEvent('model.created'); - } + // Set the primary key value + $this->attributes[$this->primary_key] = $primary_key_value; + $this->original = $this->attributes; - return $row_affected; + if ($row_affected == 1) { + $this->fireEvent('model.created'); + } + + return $row_affected; + } + + /** + * Save aliases on insert action + * + * @return int + * @throws + */ + public function save() + { + $builder = static::query(); + + // Get the current primary key value + $primary_key_value = $this->getKeyValue(); + + // If primary key value is null, we are going to start the creation of new row + if (is_null($primary_key_value)) { + return $this->writeRows($builder); } $primary_key_value = $this->transtypeKeyValue($primary_key_value); // Check the existent in database - if (!$model->exists($this->primary_key, $primary_key_value)) { + if (!$builder->exists($this->primary_key, $primary_key_value)) { return 0; } @@ -542,7 +557,7 @@ public function save() $this->fireEvent('model.updating'); // Execute update model - $updated = $model->where($this->primary_key, $primary_key_value)->update($update_data); + $updated = $builder->where($this->primary_key, $primary_key_value)->update($update_data); // Fire the updated event if there are affected row if ($updated) { @@ -562,14 +577,14 @@ private function transtypeKeyValue(mixed $primary_key_value): mixed { // Transtype value to the define primary key type if ($this->primary_key_type == 'int') { - $primary_key_value = (int) $primary_key_value; - } elseif ($this->primary_key_type == 'float' || $this->primary_key_type == 'double') { - $primary_key_value = (float) $primary_key_value; - } else { - $primary_key_value = (string) $primary_key_value; + return (int) $primary_key_value; + } + + if ($this->primary_key_type == 'float' || $this->primary_key_type == 'double') { + return (float) $primary_key_value; } - return $primary_key_value; + return (string) $primary_key_value; } /** @@ -681,7 +696,7 @@ public static function deleteBy($column, $value): int */ public function getKeyValue(): mixed { - return $this->original[$this->primary_key] ?? $this->attributes[$this->primary_key] ?? null; + return $this->original[$this->primary_key] ?? null; } /** From dc67c3131aaff0d1ae51f611c6142decac33621f Mon Sep 17 00:00:00 2001 From: papac Date: Mon, 22 May 2023 21:32:27 +0000 Subject: [PATCH 046/199] Update CHANGELOG --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b4f56cb..16861711 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 5.0.6 - 2023-05-22 + +Release 5.0.6 + +- Fixes get last insert id for pgsql +- Add data validation custom message parser +- Fixes PgSQL migration errors +- Fixes initialize the request ID #236 + +References + +- Validation and PgSQL #237 +- Many bugs fixes #237 + ## 5.0.5 - 2023-05-20 Release 5.0.5 From 9c8f63798990ea1b956d4a42a30cacc9a18beb00 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 22 May 2023 22:20:58 +0000 Subject: [PATCH 047/199] Fixes check row exists from model error --- src/Database/Barry/Builder.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Database/Barry/Builder.php b/src/Database/Barry/Builder.php index e4fbd5a9..678210ed 100644 --- a/src/Database/Barry/Builder.php +++ b/src/Database/Barry/Builder.php @@ -53,13 +53,13 @@ public function get(array $columns = []): Model|Collection|null */ public function exists(?string $column = null, mixed $value = null): bool { - if (is_null($column) == null && is_null($value)) { + if (is_null($column) && is_null($value)) { return $this->count() > 0; } // If value is null and column is define // we make the column as value on model primary key name - if (!is_null($column) and is_null($value)) { + if (!is_null($column) && is_null($value)) { $value = $column; $column = (new $this->model())->getKey(); From ce93f03602860d313f9d8262c9a0b4d5b04e65b9 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 22 May 2023 23:12:39 +0000 Subject: [PATCH 048/199] Fixes the http client --- src/Http/Client/HttpClient.php | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/Http/Client/HttpClient.php b/src/Http/Client/HttpClient.php index 46f7ff8c..4516c187 100644 --- a/src/Http/Client/HttpClient.php +++ b/src/Http/Client/HttpClient.php @@ -62,17 +62,20 @@ public function setBaseUrl(string $url): void * * @param string $url * @param array $data - * @return Parser + * @return Response */ - public function get(string $url, array $data = []): Parser + public function get(string $url, array $data = []): Response { - $params = http_build_query($data); + if (count($data) > 0) { + $params = http_build_query($data); + $url . "?" . $params; + } - $this->init($url . "?" . $params); + $this->init($url); curl_setopt($this->ch, CURLOPT_HTTPGET, true); - return new Parser($this->ch); + return new Response($this->ch); } /** @@ -80,9 +83,9 @@ public function get(string $url, array $data = []): Parser * * @param string $url * @param array $data - * @return Parser + * @return Response */ - public function post(string $url, array $data = []): Parser + public function post(string $url, array $data = []): Response { $this->init($url); @@ -100,7 +103,7 @@ public function post(string $url, array $data = []): Parser $this->addFields($data); - return new Parser($this->ch); + return new Response($this->ch); } /** @@ -108,9 +111,9 @@ public function post(string $url, array $data = []): Parser * * @param string $url * @param array $data - * @return Parser + * @return Response */ - public function put(string $url, array $data = []): Parser + public function put(string $url, array $data = []): Response { $this->init($url); @@ -120,7 +123,7 @@ public function put(string $url, array $data = []): Parser curl_setopt($this->ch, CURLOPT_PUT, true); - return new Parser($this->ch); + return new Response($this->ch); } /** @@ -163,7 +166,7 @@ public function addHeaders(array $headers): HttpClient */ private function init(string $url): void { - if (is_null($this->base_url)) { + if (!is_null($this->base_url)) { $url = $this->base_url . "/" . trim($url, "/"); } From 8e01783b059f26a44e2d3ced768b542e433d459a Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 23 May 2023 08:36:40 +0000 Subject: [PATCH 049/199] Add the addRaw method for migration --- src/Database/Migration/SQLGenerator.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Database/Migration/SQLGenerator.php b/src/Database/Migration/SQLGenerator.php index 0bd28b04..9f82a22f 100644 --- a/src/Database/Migration/SQLGenerator.php +++ b/src/Database/Migration/SQLGenerator.php @@ -98,6 +98,19 @@ public function make(): string return $sql; } + /** + * Add a raw column definiton + * + * @param string $definition + * @return SQLGenerator + */ + public function addRaw(string $definition): SQLGenerator + { + $this->sqls[] = $definition; + + return $this; + } + /** * Add new column in the table * From bcddcd56c8f154b3e88ed1535f6953b0672a1731 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 23 May 2023 09:34:49 +0000 Subject: [PATCH 050/199] Fixes check statement definition --- .../Migration/Compose/MysqlCompose.php | 11 ++- .../Migration/Compose/PgsqlCompose.php | 79 +++++++++++++++---- .../Migration/Shortcut/MixedColumn.php | 42 ++++++++-- 3 files changed, 105 insertions(+), 27 deletions(-) diff --git a/src/Database/Migration/Compose/MysqlCompose.php b/src/Database/Migration/Compose/MysqlCompose.php index ab64cf5c..6c284e81 100644 --- a/src/Database/Migration/Compose/MysqlCompose.php +++ b/src/Database/Migration/Compose/MysqlCompose.php @@ -28,6 +28,7 @@ private function composeAddMysqlColumn(string $name, array $description): string // Transform attribute $default = $attribute['default'] ?? null; $size = $attribute['size'] ?? false; + $check = $attribute['check'] ?? false; $primary = $attribute['primary'] ?? false; $increment = $attribute['increment'] ?? false; $nullable = $attribute['nullable'] ?? false; @@ -47,12 +48,10 @@ private function composeAddMysqlColumn(string $name, array $description): string } // Add column size - if ($size) { - if (in_array($raw_type, ['ENUM', 'CHECK'])) { - $size = (array) $size; - $size = "'" . implode("', '", $size) . "'"; - } - $type = sprintf('%s(%s)', $type, $size); + if (in_array($raw_type, ['ENUM', 'CHECK'])) { + $check = (array) $check; + $check = "'" . implode("', '", $check) . "'"; + $type = sprintf('%s(%s)', $type, $check); } // Bind auto increment action diff --git a/src/Database/Migration/Compose/PgsqlCompose.php b/src/Database/Migration/Compose/PgsqlCompose.php index 35473c6b..cad7418a 100644 --- a/src/Database/Migration/Compose/PgsqlCompose.php +++ b/src/Database/Migration/Compose/PgsqlCompose.php @@ -74,22 +74,7 @@ private function composeAddPgsqlColumn(string $name, array $description): string } if (in_array($raw_type, ['ENUM', 'CHECK'])) { - $size = (array) $size; - $size = "'" . implode("', '", $size) . "'"; - if ($raw_type == "ENUM") { - $table = preg_replace("/(ies)$/", "y", $this->table); - $table = preg_replace("/(s)$/", "", $table); - - $this->custom_types[] = sprintf( - "CREATE TYPE %s_%s_enum AS ENUM(%s);", - $table, - $name, - $size - ); - $type = sprintf('%s_%s_enum', $table, $name); - } else { - $type = sprintf('TEXT CHECK (%s IN CHECK(%s))', $name, $size); - } + $type = $this->formatCheckOrEnum($name, $raw_type, $attribute); } // Bind precision @@ -159,4 +144,66 @@ private function dropColumnForPgsql(string $name): void $this->sqls[] = trim(sprintf('DROP COLUMN %s', $name)); } } + + /** + * Format the CHECK in ENUM + * + * @param string $name + * @param string $type + * @param array $attribute + * @return void + */ + private function formatCheckOrEnum($name, $type, $attribute): string + { + if ($type == "ENUM") { + $size = (array) $attribute['size']; + $size = "'" . implode("', '", $size) . "'"; + $table = preg_replace("/(ies)$/", "y", $this->table); + $table = preg_replace("/(s)$/", "", $table); + $this->custom_types[] = sprintf( + "CREATE TYPE %s_%s_enum AS ENUM(%s);", + $table, + $name, + $size + ); + + return sprintf('%s_%s_enum', $table, $name); + } + + if (count($attribute["check"]) === 3) { + [$column, $comparaison, $value] = $attribute["check"]; + if (is_array($value)) { + $value = "('" . implode("', '", $value) . "')"; + } + return sprintf('TEXT CHECK ("%s" %s %s)', $column, $comparaison, $value); + } + + [$column, $value] = $attribute["check"]; + + $comparaison = "="; + + if (is_string($value)) { + $value = "'" . addcslashes($value, "'") . "'"; + return sprintf('TEXT CHECK ("%s" %s %s)', $column, $comparaison, $value); + } + + $value = (array) $value; + + if (count($value) > 1) { + $comparaison = "IN"; + + foreach ($value as $key => $item) { + if (is_string($item)) { + $value[$key] = "'" . addcslashes($item, "'") . "'"; + } + } + + $value = "(" . implode(", ", $value) . ")"; + return sprintf('TEXT CHECK ("%s" %s %s)', $column, $comparaison, $value); + } + + $value = end($value); + + return sprintf('TEXT CHECK ("%s" %s %s)', $column, $comparaison, $value); + } } diff --git a/src/Database/Migration/Shortcut/MixedColumn.php b/src/Database/Migration/Shortcut/MixedColumn.php index 9bd729fe..e3b9de7b 100644 --- a/src/Database/Migration/Shortcut/MixedColumn.php +++ b/src/Database/Migration/Shortcut/MixedColumn.php @@ -179,16 +179,20 @@ public function addEnum(string $column, array $attribute = []): SQLGenerator */ public function addCheck(string $column, array $attribute = []): SQLGenerator { - if (!isset($attribute['size'])) { + if (!isset($attribute['check'])) { throw new SQLGeneratorException("The check values should be define."); } - if (!is_array($attribute['size'])) { - throw new SQLGeneratorException("The enum values should be array."); + if (!is_array($attribute['check'])) { + throw new SQLGeneratorException("The check values should be array."); } - if (count($attribute['size']) === 0) { - throw new SQLGeneratorException("The enum values cannot be empty."); + if (count($attribute['check']) === 0) { + throw new SQLGeneratorException("The check values cannot be empty."); + } + + if (count($attribute['check']) === 0) { + throw new SQLGeneratorException("The check values cannot be empty."); } return $this->addColumn($column, 'check', $attribute); @@ -312,6 +316,18 @@ public function changeMacAddress(string $column, array $attribute = []): SQLGene */ public function changeEnum(string $column, array $attribute = []): SQLGenerator { + if (!isset($attribute['size'])) { + throw new SQLGeneratorException("The enum values should be define!"); + } + + if (!is_array($attribute['size'])) { + throw new SQLGeneratorException("The enum values should be array"); + } + + if (count($attribute['size']) === 0) { + throw new SQLGeneratorException("The enum values cannot be empty."); + } + return $this->changeColumn($column, 'enum', $attribute); } @@ -324,6 +340,22 @@ public function changeEnum(string $column, array $attribute = []): SQLGenerator */ public function changeCheck(string $column, array $attribute = []): SQLGenerator { + if (!isset($attribute['check'])) { + throw new SQLGeneratorException("The check values should be define."); + } + + if (!is_array($attribute['check'])) { + throw new SQLGeneratorException("The check values should be array."); + } + + if (count($attribute['check']) === 0) { + throw new SQLGeneratorException("The check values cannot be empty."); + } + + if (count($attribute['check']) === 0) { + throw new SQLGeneratorException("The check values cannot be empty."); + } + return $this->changeColumn($column, 'check', $attribute); } } From ad3d24ebbe73a267cd3c8a7e4320826c2d8d06d1 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 23 May 2023 09:45:03 +0000 Subject: [PATCH 051/199] Add the column size on mysql --- src/Database/Migration/Compose/MysqlCompose.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Database/Migration/Compose/MysqlCompose.php b/src/Database/Migration/Compose/MysqlCompose.php index 6c284e81..a1dd8d95 100644 --- a/src/Database/Migration/Compose/MysqlCompose.php +++ b/src/Database/Migration/Compose/MysqlCompose.php @@ -47,6 +47,11 @@ private function composeAddMysqlColumn(string $name, array $description): string $size = 255; } + // Set the size + if ($size) { + $type = sprintf('%s(%s)', $type, $size); + } + // Add column size if (in_array($raw_type, ['ENUM', 'CHECK'])) { $check = (array) $check; From f1e554fa269d97c3c568c77d1502619c634d3cc6 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 23 May 2023 11:01:11 +0000 Subject: [PATCH 052/199] Refactoring the http client --- src/Http/Client/HttpClient.php | 97 +++++++++++--- src/Http/Client/Response.php | 227 +++++---------------------------- 2 files changed, 110 insertions(+), 214 deletions(-) diff --git a/src/Http/Client/HttpClient.php b/src/Http/Client/HttpClient.php index 4516c187..ac0777d5 100644 --- a/src/Http/Client/HttpClient.php +++ b/src/Http/Client/HttpClient.php @@ -72,10 +72,13 @@ public function get(string $url, array $data = []): Response } $this->init($url); + $this->applyCommonOptions(); curl_setopt($this->ch, CURLOPT_HTTPGET, true); - return new Response($this->ch); + $content = $this->execute(); + + return new Response($this->ch, $content); } /** @@ -99,11 +102,14 @@ public function post(string $url, array $data = []): Response $data = array_merge($this->attach, $data); } + $this->addFields($data); + $this->applyCommonOptions(); + curl_setopt($this->ch, CURLOPT_POST, true); - $this->addFields($data); + $content = $this->execute(); - return new Response($this->ch); + return new Response($this->ch, $content); } /** @@ -116,14 +122,33 @@ public function post(string $url, array $data = []): Response public function put(string $url, array $data = []): Response { $this->init($url); - - if (!curl_setopt($this->ch, CURLOPT_PUT, true)) { - $this->addFields($data); - } + $this->addFields($data); + $this->applyCommonOptions(); curl_setopt($this->ch, CURLOPT_PUT, true); - return new Response($this->ch); + $content = $this->execute(); + + return new Response($this->ch, $content); + } + + /** + * Make put requete + * + * @param string $url + * @param array $data + * @return Response + */ + public function delete(string $url, array $data = []): Response + { + $this->init($url); + $this->addFields($data); + $this->applyCommonOptions(); + + curl_setopt($this->ch, CURLOPT_CUSTOMREQUEST, "DELETE"); + $content = $this->execute(); + + return new Response($this->ch, $content); } /** @@ -145,16 +170,14 @@ public function addAttach(string|array $attach): array */ public function addHeaders(array $headers): HttpClient { - if (is_resource($this->ch)) { - $data = []; - - foreach ($headers as $key => $value) { - $data[] = $key . ': ' . $value; - } + $data = []; - curl_setopt($this->ch, CURLOPT_HTTPHEADER, $data); + foreach ($headers as $key => $value) { + $data[] = $key . ': ' . $value; } + curl_setopt($this->ch, CURLOPT_HTTPHEADER, $data); + return $this; } @@ -166,7 +189,7 @@ public function addHeaders(array $headers): HttpClient */ private function init(string $url): void { - if (!is_null($this->base_url)) { + if (is_null($this->base_url)) { $url = $this->base_url . "/" . trim($url, "/"); } @@ -185,4 +208,46 @@ private function addFields(array $data): void curl_setopt($this->ch, CURLOPT_POSTFIELDS, http_build_query($data)); } } + + /** + * Close connection + * + * @return void + */ + private function close(): void + { + curl_close($this->ch); + } + + /** + * Execute request + * + * @return string + * @throws \Exception + */ + private function execute(): string + { + $content = curl_exec($this->ch); + $errno = curl_errno($this->ch); + + $this->close(); + + if ($content === false) { + throw new \Exception(curl_strerror($errno)); + } + + return $content; + } + + /** + * Set Curl CURLOPT_RETURNTRANSFER option + * + * @return bool + */ + private function applyCommonOptions() + { + curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($this->ch, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($this->ch, CURLOPT_AUTOREFERER, true); + } } diff --git a/src/Http/Client/Response.php b/src/Http/Client/Response.php index ba468856..ac3fa0f7 100644 --- a/src/Http/Client/Response.php +++ b/src/Http/Client/Response.php @@ -22,13 +22,6 @@ class Response */ private int $errno; - /** - * Curl instance - * - * @var CurlHandle - */ - private CurlHandle $ch; - /** * The headers * @@ -37,179 +30,67 @@ class Response private array $headers = []; /** - * Flag + * Define the request content * - * @var bool + * @var string|null */ - private bool $executed = false; + public ?string $content = null; /** * Parser constructor. * * @param CurlHandle $ch + * @param ?string $content */ - public function __construct(CurlHandle &$ch) - { - $this->ch = $ch; - } - - /** - * Get raw content - * - * @return mixed - * @throws - */ - public function raw(): string + public function __construct(CurlHandle &$ch, ?string $content = null) { - if (!$this->returnTransfertToRaw()) { - return null; - } - - return $this->execute(); + $this->error = curl_error($ch); + $this->errno = curl_errno($ch); + $this->headers = curl_getinfo($ch); + $this->content = $content; } /** * Get response content * - * @return mixed - * @throws + * @return ?string */ public function getContent(): ?string { - if (!$this->returnTransfertToPlain()) { - return null; - } - - return $this->execute(); + return $this->content; } /** * Get response content as json * - * @param array $default * @return bool|string - * @throws */ - public function toJson(?array $default = null): bool|string + public function toJson(): bool|string { - if (!$this->returnTransfertToPlain()) { - if (is_array($default)) { - return json_encode($default); - } - - return false; - } - - $data = $this->raw(); - - return json_encode($data); - } - - /** - * Get response content as array - * - * @return mixed - * @throws - */ - public function toArray(): mixed - { - if (!$this->returnTransfert()) { - $this->close(); - - return ["error" => true, "message" => "Connat get information"]; - } - - return $this->execute(); - } - - /** - * Set Curl CURLOPT_RETURNTRANSFER option - * - * @return bool - */ - private function returnTransfert() - { - if (!curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true)) { - $this->close(); - - return false; - } - - return true; - } - - /** - * Set Curl CURLOPT_BINARYTRANSFER option - * - * @return bool - */ - private function returnTransfertToRaw() - { - if ($this->returnTransfert()) { - if (!curl_setopt($this->ch, CURLOPT_HTTPGET, true)) { - $this->close(); - - return false; - } - } + $content = $this->getContent(); - return true; + return json_decode($content); } /** - * Set Curl CURLOPT_TRANSFERTEXT option - * - * @return bool - */ - private function returnTransfertToPlain() - { - if ($this->returnTransfert()) { - if (!curl_setopt($this->ch, CURLOPT_TRANSFERTEXT, true)) { - $this->close(); - - return false; - } - } - - return true; - } - - /** - * Execute request + * Get response content as json * - * @return string - * @throws \Exception + * @return bool|string */ - private function execute(): string + public function toArray(): bool|string { - $data = curl_exec($this->ch); - - $this->error = curl_error($this->ch); - $this->errno = curl_errno($this->ch); - $this->headers = curl_getinfo($this->ch); - $this->executed = true; - - $this->close(); + $content = $this->getContent(); - if ($data === false) { - throw new \Exception(curl_strerror($this->errno)); - } - - return $data; + return json_decode($content, true); } /** * Get the response headers * * @return array - * @throws */ public function getHeaders(): array { - if (!$this->executed) { - $this->execute(); - } - return $this->headers; } @@ -217,29 +98,29 @@ public function getHeaders(): array * Get the response code * * @return ?int - * @throws */ public function getCode(): ?int { - if (!$this->executed) { - $this->execute(); - } - return $this->headers['http_code'] ?? null; } + /** + * Alias of getCode + * + * @return ?int + */ + public function statusCode(): ?int + { + return $this->getCode(); + } + /** * Get the response executing time * * @return ?int - * @throws */ public function getExecutionTime(): ?int { - if (!$this->executed) { - $this->execute(); - } - return $this->headers['total_time'] ?? null; } @@ -247,14 +128,9 @@ public function getExecutionTime(): ?int * Get the request connexion time * * @return ?float - * @throws */ public function getConnexionTime(): ?float { - if (!$this->executed) { - $this->execute(); - } - return $this->headers['connect_time'] ?? null; } @@ -262,14 +138,9 @@ public function getConnexionTime(): ?float * Get the response upload size * * @return ?float - * @throws */ public function getUploadSize(): ?float { - if (!$this->executed) { - $this->execute(); - } - return $this->headers['size_upload'] ?? null; } @@ -277,14 +148,9 @@ public function getUploadSize(): ?float * Get the request upload speed * * @return ?float - * @throws */ public function getUploadSpeed(): ?float { - if (!$this->executed) { - $this->execute(); - } - return $this->headers['speed_upload'] ?? null; } @@ -292,14 +158,9 @@ public function getUploadSpeed(): ?float * Get the download size * * @return ?float - * @throws */ public function getDownloadSize(): ?float { - if (!$this->executed) { - $this->execute(); - } - return $this->headers['size_download'] ?? null; } @@ -307,14 +168,9 @@ public function getDownloadSize(): ?float * Get the downlad speed * * @return ?float - * @throws */ public function getDownloadSpeed(): ?float { - if (!$this->executed) { - $this->execute(); - } - return $this->headers['speed_download'] ?? null; } @@ -322,14 +178,9 @@ public function getDownloadSpeed(): ?float * Get error message * * @return string - * @throws */ public function getErrorMessage(): string { - if (!$this->executed) { - $this->execute(); - } - return $this->error; } @@ -337,14 +188,9 @@ public function getErrorMessage(): string * Get error code * * @return int - * @throws */ public function getErrorNumber(): int { - if (!$this->executed) { - $this->execute(); - } - return $this->errno; } @@ -352,24 +198,9 @@ public function getErrorNumber(): int * Get the response content type * * @return ?string - * @throws */ public function getContentType(): ?string { - if (!$this->executed) { - $this->execute(); - } - return $this->headers['content_type'] ?? null; } - - /** - * Close connection - * - * @return void - */ - private function close(): void - { - curl_close($this->ch); - } } From b7634262378d2bf4474be75c4960540f4cd89897 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 23 May 2023 12:42:37 +0000 Subject: [PATCH 053/199] Change validation exception definition --- src/Validation/Exception/ValidationException.php | 6 +++--- src/Validation/Validate.php | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Validation/Exception/ValidationException.php b/src/Validation/Exception/ValidationException.php index 99c335ad..af43179e 100644 --- a/src/Validation/Exception/ValidationException.php +++ b/src/Validation/Exception/ValidationException.php @@ -19,13 +19,13 @@ class ValidationException extends HttpException * ValidationException constructor * * @param string $message - * @param string $status * @param array $errors + * @param string $status */ public function __construct( string $message, - string $status = 'VALIDATION_ERROR', - array $errors = [] + array $errors = [], + string $status = 'VALIDATION_ERROR' ) { parent::__construct($message, 400); $this->errors = $errors; diff --git a/src/Validation/Validate.php b/src/Validation/Validate.php index 13391862..a50d7353 100644 --- a/src/Validation/Validate.php +++ b/src/Validation/Validate.php @@ -129,7 +129,6 @@ public function throwError(): void throw new ValidationException( "Error on data validation sent", - "VALIADTION_ERROR", $this->messages ); } From fddf1d60747549d535c1ca5c5e405edbca09ed32 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 23 May 2023 12:42:48 +0000 Subject: [PATCH 054/199] Upgrade the policer package version --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 140ee2b5..af460af1 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "aws/aws-sdk-php": "^3.87", "phpstan/phpstan": "^0.12.87", "php-amqplib/php-amqplib": "^3.0", - "bowphp/policier": "dev-master", + "bowphp/policier": "^3.0", "mockery/mockery": "^1.5", "spatie/phpunit-snapshot-assertions": "^4.2", "predis/predis": "^2.1" From 2429916170dce2441698354f827c3050468214cc Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 23 May 2023 14:57:04 +0000 Subject: [PATCH 055/199] Fixes the hasone relationship --- src/Database/Barry/Relations/HasOne.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Database/Barry/Relations/HasOne.php b/src/Database/Barry/Relations/HasOne.php index 5eb0c985..68ee29b9 100644 --- a/src/Database/Barry/Relations/HasOne.php +++ b/src/Database/Barry/Relations/HasOne.php @@ -38,7 +38,8 @@ public function __construct(Model $related, Model $parent, string $foreign_key, $this->local_key = $local_key; $this->foreign_key = $foreign_key; - $this->query = $this->query->where($this->foreign_key, $this->parent->getKeyValue()); + + $this->query = $this->query->where($this->foreign_key, $this->parent->$local_key); } /** From 60605080c05a734ef1f75e6a7e2a416fefab9cf5 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 23 May 2023 19:30:44 +0000 Subject: [PATCH 056/199] Fixes hasOne relationship --- src/Database/Barry/Concerns/Relationship.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Database/Barry/Concerns/Relationship.php b/src/Database/Barry/Concerns/Relationship.php index 1b102538..046f8505 100644 --- a/src/Database/Barry/Concerns/Relationship.php +++ b/src/Database/Barry/Concerns/Relationship.php @@ -104,14 +104,14 @@ public function hasMany( * The has one relative * * @param string $related - * @param string $primary_key * @param string $foreign_key + * @param string $primary_key * @return HasOne */ public function hasOne( string $related, - ?string $primary_key = null, - ?string $foreign_key = null + ?string $foreign_key = null, + ?string $primary_key = null ): HasOne { $related_model = app()->make($related); @@ -124,6 +124,6 @@ public function hasOne( $foreign_key = rtrim($related_model->getTable(), 's') . '_id'; } - return new HasOne($related_model, $this, $primary_key, $foreign_key); + return new HasOne($related_model, $this, $foreign_key, $primary_key); } } From 04f2fec55d20ec070048913383e430bed454bfd5 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 23 May 2023 19:31:14 +0000 Subject: [PATCH 057/199] Optimisize the jwt system --- src/Auth/Authentication.php | 10 ++++++++++ src/Auth/Guards/GuardContract.php | 2 +- src/Auth/Guards/JwtGuard.php | 4 +++- src/Auth/Guards/SessionGuard.php | 4 +++- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/Auth/Authentication.php b/src/Auth/Authentication.php index 6ada4fd1..4dbec6c1 100644 --- a/src/Auth/Authentication.php +++ b/src/Auth/Authentication.php @@ -17,4 +17,14 @@ public function getAuthenticateUserId() { return $this->attributes[$this->primary_key]; } + + /** + * Define the additionals values + * + * @return array + */ + public function customJwtAttributes(): array + { + return []; + } } diff --git a/src/Auth/Guards/GuardContract.php b/src/Auth/Guards/GuardContract.php index 36f91fe5..57e0cf25 100644 --- a/src/Auth/Guards/GuardContract.php +++ b/src/Auth/Guards/GuardContract.php @@ -8,7 +8,7 @@ use Bow\Auth\Authentication; /** - * @method ?string getToken() + * @method ?\Policier\Token getToken() */ abstract class GuardContract { diff --git a/src/Auth/Guards/JwtGuard.php b/src/Auth/Guards/JwtGuard.php index 2d2e5a4a..6f962924 100644 --- a/src/Auth/Guards/JwtGuard.php +++ b/src/Auth/Guards/JwtGuard.php @@ -147,11 +147,13 @@ public function getToken(): ?Token */ public function login(Authentication $user): bool { - $this->token = $this->getPolicier()->encode($user->getAuthenticateUserId(), [ + $attributes = array_merge($user->customJwtAttributes(), [ "id" => $user->getAuthenticateUserId(), "logged" => true ]); + $this->token = $this->getPolicier()->encode($user->getAuthenticateUserId(), $attributes); + return true; } diff --git a/src/Auth/Guards/SessionGuard.php b/src/Auth/Guards/SessionGuard.php index 68e58004..5114ba1d 100644 --- a/src/Auth/Guards/SessionGuard.php +++ b/src/Auth/Guards/SessionGuard.php @@ -78,7 +78,9 @@ private function getSession(): Session $session = Session::getInstance(); if (is_null($session)) { - throw new AuthenticationException("Please the session configuration is not load"); + throw new AuthenticationException( + "Please the session configuration is not load" + ); } return $session; From aecf182cc85873a42b5f34c47e0925c1f79b1b12 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 23 May 2023 19:31:31 +0000 Subject: [PATCH 058/199] Update unity tests --- tests/Auth/AuthenticationTest.php | 1 + tests/Config/stubs/policier.php | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/Auth/AuthenticationTest.php b/tests/Auth/AuthenticationTest.php index daf56167..f0e3cb5d 100644 --- a/tests/Auth/AuthenticationTest.php +++ b/tests/Auth/AuthenticationTest.php @@ -44,6 +44,7 @@ public function test_it_should_be_a_default_guard() { $config = TestingConfiguration::getConfig(); $auth = Auth::getInstance(); + $this->assertEquals($auth->getName(), $config["auth"]["default"]); $this->assertEquals($auth->getName(), "web"); } diff --git a/tests/Config/stubs/policier.php b/tests/Config/stubs/policier.php index 33e276dc..ded1be3c 100644 --- a/tests/Config/stubs/policier.php +++ b/tests/Config/stubs/policier.php @@ -46,7 +46,7 @@ /** * Hashing algorithm being used * - * HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES384, ES512, + * HS256, HS384, HS512, ES256, ES384, ES512, */ - "alg" => "HS256", + "alg" => "HS512", ]; From 04c99dcff0f1a9efc19dd0131d579fcbc3ae85a9 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Wed, 24 May 2023 16:45:00 +0000 Subject: [PATCH 059/199] Fixes relationship and cache system --- src/Database/Barry/Relations/BelongsTo.php | 2 +- src/Database/Barry/Relations/HasOne.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Database/Barry/Relations/BelongsTo.php b/src/Database/Barry/Relations/BelongsTo.php index 9911bd6a..6e1ab70d 100644 --- a/src/Database/Barry/Relations/BelongsTo.php +++ b/src/Database/Barry/Relations/BelongsTo.php @@ -51,7 +51,7 @@ public function __construct( */ public function getResults(): ?Model { - $key = $this->query->getTable() . "_" . $this->local_key; + $key = $this->query->getTable() . ":belongsto:" . $this->related->getTable() . ":" . $this->foreign_key; $cache = Cache::cache('file')->get($key); if (!is_null($cache)) { diff --git a/src/Database/Barry/Relations/HasOne.php b/src/Database/Barry/Relations/HasOne.php index 68ee29b9..535b4762 100644 --- a/src/Database/Barry/Relations/HasOne.php +++ b/src/Database/Barry/Relations/HasOne.php @@ -49,7 +49,7 @@ public function __construct(Model $related, Model $parent, string $foreign_key, */ public function getResults(): ?Model { - $key = $this->query->getTable() . "_" . $this->local_key; + $key = $this->query->getTable() . ":hasone:" . $this->related->getTable() . ":" . $this->foreign_key; $cache = Cache::cache('file')->get($key); if (!is_null($cache)) { From 9be15ac329d3118fc01b8c61ea801fb3fb84dc69 Mon Sep 17 00:00:00 2001 From: papac Date: Wed, 24 May 2023 16:55:54 +0000 Subject: [PATCH 060/199] Update CHANGELOG --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16861711..05cc358d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 5.0.7 - 2023-05-24 + +Release 5.0.7 + +- Fixes the database relationship +- Fixes the HTTP client +- Fixes the JWT authentication service + +Fixes #241 +Fixes #213 +Fixes #240 + ## 5.0.6 - 2023-05-22 Release 5.0.6 From 50296e8fe530675c4204fc7719ecd883b1e1d7ec Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Wed, 24 May 2023 17:30:37 +0000 Subject: [PATCH 061/199] Fixes http client errors from testcase service --- src/Http/Client/HttpClient.php | 20 ++++++++++++++------ src/Testing/TestCase.php | 17 +++++++++++++++-- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/Http/Client/HttpClient.php b/src/Http/Client/HttpClient.php index ac0777d5..e05695fb 100644 --- a/src/Http/Client/HttpClient.php +++ b/src/Http/Client/HttpClient.php @@ -15,6 +15,13 @@ class HttpClient */ private $attach = []; + /** + * The headers collection + * + * @var array + */ + private $headers = []; + /** * The curl instance * @@ -146,6 +153,7 @@ public function delete(string $url, array $data = []): Response $this->applyCommonOptions(); curl_setopt($this->ch, CURLOPT_CUSTOMREQUEST, "DELETE"); + $content = $this->execute(); return new Response($this->ch, $content); @@ -170,14 +178,10 @@ public function addAttach(string|array $attach): array */ public function addHeaders(array $headers): HttpClient { - $data = []; - foreach ($headers as $key => $value) { - $data[] = $key . ': ' . $value; + $this->headers[] = $key . ': ' . $value; } - curl_setopt($this->ch, CURLOPT_HTTPHEADER, $data); - return $this; } @@ -189,7 +193,7 @@ public function addHeaders(array $headers): HttpClient */ private function init(string $url): void { - if (is_null($this->base_url)) { + if (!is_null($this->base_url)) { $url = $this->base_url . "/" . trim($url, "/"); } @@ -227,6 +231,10 @@ private function close(): void */ private function execute(): string { + if ($this->headers) { + curl_setopt($this->ch, CURLOPT_HTTPHEADER, $this->headers); + } + $content = curl_exec($this->ch); $errno = curl_errno($this->ch); diff --git a/src/Testing/TestCase.php b/src/Testing/TestCase.php index 074d6351..4727c083 100644 --- a/src/Testing/TestCase.php +++ b/src/Testing/TestCase.php @@ -58,18 +58,31 @@ public function attach(array $attach): TestCase } /** - * Specify the additionnal who are use in the request + * Specify the additionnal headers * * @param array $headers * @return TestCase */ - public function withHeader(array $headers): TestCase + public function withHeaders(array $headers): TestCase { $this->headers = $headers; return $this; } + /** + * Specify the additionnal header + * + * @param array $headers + * @return TestCase + */ + public function withHeader(string $key, string $value): TestCase + { + $this->headers[$key] = $value; + + return $this; + } + /** * Get request * From f95e9807e11ca14e2f2bdee4231bd68d24f86e44 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Wed, 24 May 2023 17:30:50 +0000 Subject: [PATCH 062/199] Add unity tests --- tests/Support/HttpClientTest.php | 38 ++++++++++++++++++++++++++++++ tests/Support/TestingTest.php | 40 ++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 tests/Support/HttpClientTest.php create mode 100644 tests/Support/TestingTest.php diff --git a/tests/Support/HttpClientTest.php b/tests/Support/HttpClientTest.php new file mode 100644 index 00000000..13a0d3f4 --- /dev/null +++ b/tests/Support/HttpClientTest.php @@ -0,0 +1,38 @@ +get("https://google.com"); + + $this->assertEquals($response->statusCode(), 200); + } + + public function test_get_method_with_custom_headers() + { + $http = new HttpClient(); + + $http->addHeaders(["X-Api-Key" => "Fake-Key"]); + $response = $http->get("https://google.com"); + + $this->assertEquals($response->statusCode(), 200); + } + + public function test_should_be_fail_with_get_method() + { + $http = new HttpClient("https://google.com"); + + $http->addHeaders(["X-Api-Key" => "Fake-Key"]); + $response = $http->get("/the-fake-url"); + + $this->assertEquals($response->statusCode(), 404); + } +} diff --git a/tests/Support/TestingTest.php b/tests/Support/TestingTest.php new file mode 100644 index 00000000..5f10c275 --- /dev/null +++ b/tests/Support/TestingTest.php @@ -0,0 +1,40 @@ +get("/"); + + $response->assertStatus(200); + } + + public function test_get_method_with_custom_headers() + { + $this->withHeaders(["X-Api-Key" => "Fake-Key"]); + + $response = $this->get("/"); + + $response->assertStatus(200); + } + + public function test_should_be_fail_with_get_method() + { + $this->withHeaders(["X-Api-Key" => "Fake-Key"]); + + $response = $this->get("/the-fake-url-for-my-testing-please-do-not-block-this"); + + $response->assertStatus(404); + } +} From 0bb0f8c1106d0d89fc9eb05c4a0a4c14a22167b9 Mon Sep 17 00:00:00 2001 From: papac Date: Wed, 24 May 2023 17:46:02 +0000 Subject: [PATCH 063/199] Update CHANGELOG --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05cc358d..13e64290 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 5.0.8 - 2023-05-24 + +Release 5.0.8 + +Fixes test case errors + +Reference #243 +From #242 + ## 5.0.7 - 2023-05-24 Release 5.0.7 From 970c34862dc3a5caf4e92cc3bcbe497b5515ec22 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 26 May 2023 22:12:08 +0000 Subject: [PATCH 064/199] Fixes the return type --- src/Http/Client/Response.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Http/Client/Response.php b/src/Http/Client/Response.php index ac3fa0f7..a2f36e2f 100644 --- a/src/Http/Client/Response.php +++ b/src/Http/Client/Response.php @@ -13,14 +13,14 @@ class Response * * @var string */ - private ?string $error = null; + private ?string $error_message = null; /** * The error number * * @var int */ - private int $errno; + private int $errer_number; /** * The headers @@ -44,8 +44,8 @@ class Response */ public function __construct(CurlHandle &$ch, ?string $content = null) { - $this->error = curl_error($ch); - $this->errno = curl_errno($ch); + $this->error_message = curl_error($ch); + $this->errer_number = curl_errno($ch); $this->headers = curl_getinfo($ch); $this->content = $content; } @@ -63,9 +63,9 @@ public function getContent(): ?string /** * Get response content as json * - * @return bool|string + * @return object|array */ - public function toJson(): bool|string + public function toJson(): object|array { $content = $this->getContent(); @@ -75,9 +75,9 @@ public function toJson(): bool|string /** * Get response content as json * - * @return bool|string + * @return array */ - public function toArray(): bool|string + public function toArray(): array { $content = $this->getContent(); @@ -181,7 +181,7 @@ public function getDownloadSpeed(): ?float */ public function getErrorMessage(): string { - return $this->error; + return $this->error_message ?? curl_strerror($this->errer_number); } /** @@ -191,7 +191,7 @@ public function getErrorMessage(): string */ public function getErrorNumber(): int { - return $this->errno; + return $this->errer_number; } /** From 020e99a074748c092104e9d35ab359b163ff64e1 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 26 May 2023 22:12:31 +0000 Subject: [PATCH 065/199] Add the setUserAgent method --- src/Http/Client/HttpClient.php | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/Http/Client/HttpClient.php b/src/Http/Client/HttpClient.php index e05695fb..7082f3fd 100644 --- a/src/Http/Client/HttpClient.php +++ b/src/Http/Client/HttpClient.php @@ -163,11 +163,13 @@ public function delete(string $url, array $data = []): Response * Attach new file * * @param string $attach - * @return array + * @return HttpClient */ - public function addAttach(string|array $attach): array + public function addAttach(string|array $attach): HttpClient { - return $this->attach = (array) $attach; + $this->attach = (array) $attach; + + return $this; } /** @@ -185,6 +187,19 @@ public function addHeaders(array $headers): HttpClient return $this; } + /** + * Set the user agent + * + * @param string $user_agent + * @return HttpClient + */ + public function setUserAgent(string $user_agent): HttpClient + { + curl_setopt($this->ch, CURLOPT_USERAGENT, $user_agent); + + return $this; + } + /** * Reset alway connection * From b434fb716ceda286b3d8a18b5b52d12e6f655e4e Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 26 May 2023 22:20:01 +0000 Subject: [PATCH 066/199] Add the json method --- .../Exception/BaseErrorHandler.php | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/Application/Exception/BaseErrorHandler.php b/src/Application/Exception/BaseErrorHandler.php index 4c1aae42..e0c013b5 100644 --- a/src/Application/Exception/BaseErrorHandler.php +++ b/src/Application/Exception/BaseErrorHandler.php @@ -4,7 +4,10 @@ namespace Bow\Application\Exception; +use PDOException; use Bow\View\View; +use Bow\Http\Exception\HttpException; +use Bow\Validation\Exception\ValidationException; class BaseErrorHandler { @@ -19,4 +22,50 @@ protected function render($view, $data = []): string { return View::parse($view, $data)->getContent(); } + + /** + * Send the json as response + * + * @param string $data + * @param mixed $code + * @return mixed + */ + protected function json($exception, $code = null) + { + if (is_null($code)) { + if (method_exists($exception, 'getStatus')) { + $code = $exception->getStatus(); + } else { + $code = 'INTERNAL_SERVER_ERROR'; + } + } + + if (app_env("APP_ENV") == "production" && $exception instanceof PDOException) { + $message = 'An SQL error occurs. For security, we did not display the message.'; + } else { + $message = $exception->getMessage(); + } + + $response = [ + 'message' => $message, + 'code' => $code, + 'time' => date('Y-m-d H:i:s') + ]; + + $status = 500; + + if ($exception instanceof HttpException) { + $status = $exception->getStatusCode(); + $response = array_merge($response, compact('status')); + if ($exception instanceof ValidationException) { + $response["errors"] = $exception->getErrors(); + } + } + + if (app_env("APP_ENV") != "production") { + $response["trace"] = $exception->getTrace(); + } + + return response()->json($response, $status); + } } From 9e11aaf162c90950139065aec2051920067a0380 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 26 May 2023 22:57:50 +0000 Subject: [PATCH 067/199] Fixes persitence cache for filesystem --- src/Cache/Adapter/FilesystemAdapter.php | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/Cache/Adapter/FilesystemAdapter.php b/src/Cache/Adapter/FilesystemAdapter.php index 649eb158..434c3df2 100644 --- a/src/Cache/Adapter/FilesystemAdapter.php +++ b/src/Cache/Adapter/FilesystemAdapter.php @@ -43,7 +43,7 @@ public function __construct(array $config) /** * @inheritDoc */ - public function add(string $key, mixed $data, ?int $time = null): bool + public function add(string $key, mixed $data, ?int $time = 60): bool { if (is_callable($data)) { $content = $data(); @@ -51,7 +51,7 @@ public function add(string $key, mixed $data, ?int $time = null): bool $content = $data; } - $meta['__bow_meta'] = ['expire_at' => $time == null ? '+' : $time]; + $meta['__bow_meta'] = ['expire_at' => $time == null ? '+' : time() + $time]; $meta['content'] = $content; @@ -142,6 +142,14 @@ public function get(string $key, mixed $default = null): mixed $cache = unserialize(file_get_contents($this->makeHashFilename($key))); + $expire_at = $cache['__bow_meta']['expire_at']; + + if ($expire_at != '+') { + if (time() > $expire_at) { + return null; + } + } + if (!$this->with_meta) { unset($cache['__bow_meta']); @@ -167,7 +175,7 @@ public function addTime(string $key, int $time): bool } if ($cache['__bow_meta']['expire_at'] == '+') { - $cache['__bow_meta']['expire_at'] = time(); + $cache['__bow_meta']['expire_at'] = time() + $time; } $cache['__bow_meta']['expire_at'] += $time; @@ -243,11 +251,11 @@ public function expired(string $key): bool return false; } + $expire_at = $cache['__bow_meta']['expire_at']; + $this->with_meta = false; - return $cache['__bow_meta']['expire_at'] == '+' - ? false - : (time() > $cache['__bow_meta']['expire_at']); + return $expire_at == '+' ? false : (time() > $expire_at); } /** From 34503b2d2965c6f3365283b0fb1844df037330b5 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sat, 27 May 2023 15:28:33 +0000 Subject: [PATCH 068/199] Fixes the route url compilation --- src/Support/helpers.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Support/helpers.php b/src/Support/helpers.php index 0758347b..e54dd1dd 100644 --- a/src/Support/helpers.php +++ b/src/Support/helpers.php @@ -881,7 +881,7 @@ function route(string $name, array $data = [], $absolute = false) if (preg_match_all('/(?::([a-zA-Z0-9_-]+\??))/', $url, $matches)) { $keys = end($matches); foreach ($keys as $key) { - if (preg_match("/?$/", $key)) { + if (preg_match("/\?$/", $key)) { $valide_key = trim($key, "?"); $value = $data[$valide_key] ?? ""; unset($data[$valide_key]); From 1e984478cdd0f3c1001fcfdab9d82bf2a47c4200 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sat, 27 May 2023 15:32:52 +0000 Subject: [PATCH 069/199] Fixes route data key checker --- src/Support/helpers.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Support/helpers.php b/src/Support/helpers.php index e54dd1dd..9f66b554 100644 --- a/src/Support/helpers.php +++ b/src/Support/helpers.php @@ -886,7 +886,7 @@ function route(string $name, array $data = [], $absolute = false) $value = $data[$valide_key] ?? ""; unset($data[$valide_key]); } else { - if (isset($data[$key])) { + if (!isset($data[$key])) { throw new InvalidArgumentException( "The $key key is not provide" ); From 9d0c208003d1d52ca3e08265cce7f43ce24132b5 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sat, 27 May 2023 16:02:51 +0000 Subject: [PATCH 070/199] Fixes the route proto --- src/Support/helpers.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Support/helpers.php b/src/Support/helpers.php index 9f66b554..3b2c167c 100644 --- a/src/Support/helpers.php +++ b/src/Support/helpers.php @@ -856,11 +856,11 @@ function validator(array $inputs, array $rules): \Bow\Validation\Validate * Get Route by name * * @param string $name - * @param array $data + * @param bool|array $data * @param bool $absolute * @return string */ - function route(string $name, array $data = [], $absolute = false) + function route(string $name, bool|array $data = [], bool $absolute = false) { $routes = config('app.routes'); From 1791494fcccd7efba4906975cf3d2a92e8fb7518 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sat, 27 May 2023 17:25:55 +0000 Subject: [PATCH 071/199] Fixes the file cache adapter --- src/Cache/Adapter/FilesystemAdapter.php | 6 +++--- tests/Cache/CacheFilesystemTest.php | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Cache/Adapter/FilesystemAdapter.php b/src/Cache/Adapter/FilesystemAdapter.php index 434c3df2..99ff1ec9 100644 --- a/src/Cache/Adapter/FilesystemAdapter.php +++ b/src/Cache/Adapter/FilesystemAdapter.php @@ -176,10 +176,10 @@ public function addTime(string $key, int $time): bool if ($cache['__bow_meta']['expire_at'] == '+') { $cache['__bow_meta']['expire_at'] = time() + $time; + } else { + $cache['__bow_meta']['expire_at'] += $time; } - $cache['__bow_meta']['expire_at'] += $time; - return (bool) file_put_contents( $this->makeHashFilename($key), serialize($cache) @@ -201,7 +201,7 @@ public function timeOf(string $key): int|bool|string return false; } - return $cache['__bow_meta']['expire_at']; + return (int) $cache['__bow_meta']['expire_at']; } /** diff --git a/tests/Cache/CacheFilesystemTest.php b/tests/Cache/CacheFilesystemTest.php index 5f6ea961..fc88f725 100644 --- a/tests/Cache/CacheFilesystemTest.php +++ b/tests/Cache/CacheFilesystemTest.php @@ -27,7 +27,7 @@ public function test_get_cache() $this->assertEquals(Cache::get('name'), 'Dakia'); } - public function test_AddWithCallbackCache() + public function test_add_with_callback_cache() { $result = Cache::add('lastname', fn () => 'Franck'); $result = $result && Cache::add('age', fn () => 25, 20000); @@ -35,14 +35,14 @@ public function test_AddWithCallbackCache() $this->assertEquals($result, true); } - public function test_GetCallbackCache() + public function test_get_callback_cache() { $this->assertEquals(Cache::get('lastname'), 'Franck'); $this->assertEquals(Cache::get('age'), 25); } - public function test_AddArrayCache() + public function test_add_array_cache() { $result = Cache::add('address', [ 'tel' => "49929598", @@ -53,7 +53,7 @@ public function test_AddArrayCache() $this->assertEquals($result, true); } - public function test_GetArrayCache() + public function test_get_array_cache() { $result = Cache::get('address'); @@ -94,7 +94,7 @@ public function test_time_of_empty() { $result = Cache::timeOf('lastname'); - $this->assertEquals('+', $result); + $this->assertTrue(is_numeric($result)); } public function test_time_of_empty_2() From 934c5a2a28744f5fe26e0f9c26fcc3a969572749 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 29 May 2023 11:45:10 +0000 Subject: [PATCH 072/199] Add the http client exception --- src/Http/Client/HttpClient.php | 4 +++- src/Http/Client/HttpClientException.php | 11 +++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 src/Http/Client/HttpClientException.php diff --git a/src/Http/Client/HttpClient.php b/src/Http/Client/HttpClient.php index 7082f3fd..701dc7bd 100644 --- a/src/Http/Client/HttpClient.php +++ b/src/Http/Client/HttpClient.php @@ -256,7 +256,9 @@ private function execute(): string $this->close(); if ($content === false) { - throw new \Exception(curl_strerror($errno)); + throw new HttpClientException( + curl_strerror($errno), $errno + ); } return $content; diff --git a/src/Http/Client/HttpClientException.php b/src/Http/Client/HttpClientException.php new file mode 100644 index 00000000..9bd25482 --- /dev/null +++ b/src/Http/Client/HttpClientException.php @@ -0,0 +1,11 @@ + Date: Mon, 29 May 2023 12:16:19 +0000 Subject: [PATCH 073/199] Add method for get adapter --- src/Database/Migration/Migration.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Database/Migration/Migration.php b/src/Database/Migration/Migration.php index e07bb024..ecd9bc48 100644 --- a/src/Database/Migration/Migration.php +++ b/src/Database/Migration/Migration.php @@ -17,7 +17,7 @@ abstract class Migration * * @var AbstractConnection */ - private $adapter; + private AbstractConnection $adapter; /** * Migration constructor @@ -58,6 +58,16 @@ final public function connection(string $name): Migration return $this; } + /** + * Get adapter name + * + * @return string + */ + public function getAdapterName(): string + { + return $this->adapter->getName(); + } + /** * Drop table action * From 8bbdc145f0e3f6d7cb95c8f16be7887ae8eabc02 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 29 May 2023 20:01:37 +0000 Subject: [PATCH 074/199] Fixes add query data on url and add the new exception --- src/Http/Client/HttpClient.php | 3 +-- src/Http/Exception/HttpException.php | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/Http/Client/HttpClient.php b/src/Http/Client/HttpClient.php index 701dc7bd..f99266d4 100644 --- a/src/Http/Client/HttpClient.php +++ b/src/Http/Client/HttpClient.php @@ -74,8 +74,7 @@ public function setBaseUrl(string $url): void public function get(string $url, array $data = []): Response { if (count($data) > 0) { - $params = http_build_query($data); - $url . "?" . $params; + $url = $url . "?" . http_build_query($data); } $this->init($url); diff --git a/src/Http/Exception/HttpException.php b/src/Http/Exception/HttpException.php index 87fda859..f1cb2810 100644 --- a/src/Http/Exception/HttpException.php +++ b/src/Http/Exception/HttpException.php @@ -15,6 +15,13 @@ class HttpException extends Exception */ protected $status = 'OK'; + /** + * Define the errors bags + * + * @var array + */ + protected array $error_bags = []; + /** * HttpException constructor * @@ -47,4 +54,24 @@ public function getStatusCode() { return $this->getCode(); } + + /** + * Set the errors bags + * + * @param array $errors + */ + public function setErrorBags(array $errors) + { + $this->error_bags = $errors; + } + + /** + * Get the errors bags + * + * @return array + */ + public function getErrorBags(): array + { + return $this->error_bags; + } } From 112013bb255e809b142bdaeb4b4942cb6e617c33 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 29 May 2023 20:01:55 +0000 Subject: [PATCH 075/199] Fixes regex validaion --- src/Validation/Rules/RegexRule.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Validation/Rules/RegexRule.php b/src/Validation/Rules/RegexRule.php index c4346755..b358c283 100644 --- a/src/Validation/Rules/RegexRule.php +++ b/src/Validation/Rules/RegexRule.php @@ -12,12 +12,12 @@ trait RegexRule * Check that the contents of the field with a regular expression * * @param string $key - * @param string $masque + * @param string|int|float $masque * @return void */ - protected function compileRegex(string $key, string $masque): void + protected function compileRegex(string $key, string|int|float $masque): void { - if (!preg_match("/^regex:(.+)+$/", $masque, $match)) { + if (!preg_match("/^regex:(.+)+$/", (string) $masque, $match)) { return; } From 752db9c3e6b3f2909e9416069ea6b7ad66b58998 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 30 May 2023 17:13:25 +0000 Subject: [PATCH 076/199] Add accept json headers --- src/Http/Client/HttpClient.php | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/Http/Client/HttpClient.php b/src/Http/Client/HttpClient.php index f99266d4..d167d33c 100644 --- a/src/Http/Client/HttpClient.php +++ b/src/Http/Client/HttpClient.php @@ -13,7 +13,14 @@ class HttpClient * * @var array */ - private $attach = []; + private array $attach = []; + + /** + * Define the accept json header + * + * @var boolean + */ + private bool $accept_json = false; /** * The headers collection @@ -199,6 +206,19 @@ public function setUserAgent(string $user_agent): HttpClient return $this; } + /** + * Set the json accept prop to format the sent content in json + * + * @return HttpClient + */ + public function acceptJson(): HttpClient + { + $this->accept_json = true; + $this->addHeaders("Content-Type", "application/json"); + + return $this; + } + /** * Reset alway connection * @@ -222,9 +242,13 @@ private function init(string $url): void */ private function addFields(array $data): void { - if (count($data) > 0) { - curl_setopt($this->ch, CURLOPT_POSTFIELDS, http_build_query($data)); + if ($this->accept_json) { + $payload = json_encode($data); + } else { + $payload = http_build_query($data); } + + curl_setopt($this->ch, CURLOPT_POSTFIELDS, $payload); } /** From 721831bb73cc6020d9bca53f0a62d06b17b01424 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 30 May 2023 17:29:55 +0000 Subject: [PATCH 077/199] Fixes the env loading --- src/Http/Client/HttpClient.php | 3 ++- src/Support/Env.php | 13 +++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/Http/Client/HttpClient.php b/src/Http/Client/HttpClient.php index d167d33c..028a4da9 100644 --- a/src/Http/Client/HttpClient.php +++ b/src/Http/Client/HttpClient.php @@ -280,7 +280,8 @@ private function execute(): string if ($content === false) { throw new HttpClientException( - curl_strerror($errno), $errno + curl_strerror($errno), + $errno ); } diff --git a/src/Support/Env.php b/src/Support/Env.php index acb1b824..a2f7ad2b 100644 --- a/src/Support/Env.php +++ b/src/Support/Env.php @@ -4,6 +4,8 @@ namespace Bow\Support; +use Bow\Application\Exception\ApplicationException; + class Env { /** @@ -52,9 +54,16 @@ public static function load(string $filename) // Get the env file content $content = file_get_contents($filename); - static::$envs = json_decode(trim($content), true); + $envs = json_decode(trim($content), true, 1024); + + if (json_last_error()) { + throw new ApplicationException( + json_last_error_msg() . ": check your env json and synthax please." + ); + } - static::$envs = static::bindVariables(static::$envs); + static::$envs = $envs; + static::$envs = static::bindVariables($envs); foreach (static::$envs as $key => $value) { $key = Str::upper(trim($key)); From cfc6dfde620563b9fd980172e3e4b779e965606f Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 30 May 2023 18:53:25 +0000 Subject: [PATCH 078/199] Throw when parsing request json content --- src/Http/Request.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Request.php b/src/Http/Request.php index 273e22e1..7e8dd774 100644 --- a/src/Http/Request.php +++ b/src/Http/Request.php @@ -52,7 +52,7 @@ private function __construct() $this->id = "req_" . sha1(uniqid() . time()); if ($this->getHeader('content-type') == 'application/json') { - $data = json_decode(file_get_contents("php://input"), true); + $data = json_decode(file_get_contents("php://input"), true, 1024, JSON_THROW_ON_ERROR); $this->input = array_merge((array) $data, $_GET); } else { $data = $_POST ?? []; From 627a232d3b8d0c3dfc604cd2be68fca621643bcc Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 30 May 2023 19:16:32 +0000 Subject: [PATCH 079/199] Fixes the define header --- src/Http/Client/HttpClient.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Http/Client/HttpClient.php b/src/Http/Client/HttpClient.php index 028a4da9..ec3b0c8e 100644 --- a/src/Http/Client/HttpClient.php +++ b/src/Http/Client/HttpClient.php @@ -214,7 +214,8 @@ public function setUserAgent(string $user_agent): HttpClient public function acceptJson(): HttpClient { $this->accept_json = true; - $this->addHeaders("Content-Type", "application/json"); + + $this->addHeaders(["Content-Type" => "application/json"]); return $this; } From 2b925a8c404661ef41e43407190da887ac62c4ea Mon Sep 17 00:00:00 2001 From: papac Date: Thu, 1 Jun 2023 14:32:16 +0000 Subject: [PATCH 080/199] Update CHANGELOG --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13e64290..8a34a29e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 5.0.9 - 2023-06-01 + +Release 5.0.9 + +Fixes many bugs + +Reference #248 + ## 5.0.8 - 2023-05-24 Release 5.0.8 From 0a4d81d4f7528f4c98f95c6827c74269f88e0865 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 2 Jun 2023 18:30:44 +0000 Subject: [PATCH 081/199] Fixes the translate errors --- src/Translate/Translator.php | 6 +++++- src/Validation/FieldLexical.php | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Translate/Translator.php b/src/Translate/Translator.php index bdc960d1..dc98ffd8 100644 --- a/src/Translate/Translator.php +++ b/src/Translate/Translator.php @@ -4,6 +4,7 @@ namespace Bow\Translate; +use Iterator; use Bow\Support\Arraydotify; class Translator @@ -188,7 +189,10 @@ public static function plurial(string $key, array $data = []): string private static function format(string $str, array $values = []): string { foreach ($values as $key => $value) { - $str = preg_replace('/{\s*' . $key . '\s*\}/', $value, $str); + if (is_array($value) || is_object($value) || $value instanceof Iterator) { + $value = json_encode($value); + } + $str = preg_replace('/{\s*' . $key . '\s*\}/', (string) $value, $str); } return $str; diff --git a/src/Validation/FieldLexical.php b/src/Validation/FieldLexical.php index 50d68bb3..5d57c223 100644 --- a/src/Validation/FieldLexical.php +++ b/src/Validation/FieldLexical.php @@ -4,6 +4,8 @@ namespace Bow\Validation; +use Iterator; + trait FieldLexical { /** @@ -72,6 +74,9 @@ private function parseFromTranslate(string $key, array $data) private function parseAttribute(array $attribute, string $lexical): ?string { foreach ($attribute as $key => $value) { + if (is_array($value) || is_object($value) || $value instanceof Iterator) { + $value = json_encode($value); + } $lexical = str_replace('{' . $key . '}', (string) $value, $lexical); } From b5da00e2d24238356a207b3a851827c3c535c53e Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 5 Jun 2023 10:24:07 +0000 Subject: [PATCH 082/199] Remove helpers --- src/Support/helpers.php | 65 ++++------------------------- tests/Validation/ValidationTest.php | 29 ++++++++++++- 2 files changed, 37 insertions(+), 57 deletions(-) diff --git a/src/Support/helpers.php b/src/Support/helpers.php index 3b2c167c..3cffb971 100644 --- a/src/Support/helpers.php +++ b/src/Support/helpers.php @@ -17,6 +17,7 @@ use Bow\Database\Database as DB; use Bow\Http\Exception\HttpException; use Bow\Http\Redirect; +use Monolog\Logger; if (!function_exists('app')) { /** @@ -134,7 +135,7 @@ function db(string $name = null, callable $cb = null) * @param int $code * @return mixed */ - function view(string $template, $data = [], $code = 200) + function view(string $template, int|array $data = [], int $code = 200) { if (is_int($data)) { $code = $data; @@ -512,47 +513,6 @@ function redirect(string $path = null): Redirect } } -if (!function_exists('curl')) { - /** - * Curl help - * - * @param string $method - * @param string $url - * @param array $payload - * @param bool $return - * @param array $headers - * @return array|null - */ - function curl(string $method, string $url, array $payload = [], array $headers = [], $return = false) - { - $method = strtoupper($method); - $options = []; - - if (!in_array($method, ['GET'])) { - $options['CURLOPT_POSTFIELDS'] = http_build_query($payload); - $options['CURLOPT_POST'] = 1; - } - - $ch = curl_init($url); - - if ($return == true) { - if (!curl_setopt($ch, CURLOPT_RETURNTRANSFER, true)) { - curl_close($ch); - return null; - } - } - - // Set curl option - curl_setopt_array($ch, $options); - - // Execute curl - $data = curl_exec($ch); - curl_close($ch); - - return $data; - } -} - if (!function_exists('url')) { /** * Build url @@ -843,11 +803,12 @@ function cookie( * * @param array $inputs * @param array $rules + * @param array $messages * @return \Bow\Validation\Validate */ - function validator(array $inputs, array $rules): \Bow\Validation\Validate + function validator(array $inputs, array $rules, array $messages = []): \Bow\Validation\Validate { - return \Bow\Validation\Validator::make($inputs, $rules); + return \Bow\Validation\Validator::make($inputs, $rules, $messages); } } @@ -1220,26 +1181,18 @@ function auth(string $guard = null): \Bow\Auth\Guards\GuardContract } } -if (!function_exists('logger')) { +if (!function_exists('log')) { /** * Log error message * * @param string $level * @param string $message * @param array $context - * @return Monolog + * @return Logger */ - function logger(?string $level, ?string $message, array $context = []) + function log(): Logger { - if (is_null($level)) { - return app('logger'); - } - - if (!in_array($level, ['info', 'warning', 'error', 'critical', 'debug'])) { - return false; - } - - return app('logger')->$level($message, $context); + return app('logger'); } } diff --git a/tests/Validation/ValidationTest.php b/tests/Validation/ValidationTest.php index b9b69f54..b056a15e 100644 --- a/tests/Validation/ValidationTest.php +++ b/tests/Validation/ValidationTest.php @@ -56,6 +56,15 @@ public function test_max_rule() $this->assertTrue($second_validation->fails()); } + public function test_min_rule() + { + $first_validation = Validator::make(['name' => 'bow'], ['name' => 'required|min:3']); + $second_validation = Validator::make(['name' => 'fr'], ['name' => 'required|min:5']); + + $this->assertFalse($first_validation->fails()); + $this->assertTrue($second_validation->fails()); + } + public function test_lower_rule() { $first_validation = Validator::make(['name' => 'bow'], ['name' => 'required|lower']); @@ -137,7 +146,7 @@ public function test_not_exists_rule() $this->assertFalse($second_validation->fails()); } - public function test_unique() + public function test_unique_rule() { Database::insert("insert into pets values(3, 'Couli');"); @@ -152,4 +161,22 @@ public function test_unique() $thrid_validation = Validator::make(['name' => 'Couli'], ['name' => 'required|unique:pets,name']); $this->assertTrue($thrid_validation->fails()); } + + public function test_required_rule() + { + $first_validation = Validator::make(['name' => 'Couli'], ['lastname' => 'required']); + $second_validation = Validator::make(['name' => 'Milou'], ['name' => 'required']); + + $this->assertFalse($first_validation->fails()); + $this->assertTrue($second_validation->fails()); + } + + public function test_required_if_rule() + { + $first_validation = Validator::make(['name' => 'Couli'], ['lastname' => 'required_if:username']); + $second_validation = Validator::make(['name' => 'Milou'], ['lastname' => 'required_if:name']); + + $this->assertFalse($first_validation->fails()); + $this->assertTrue($second_validation->fails()); + } } From f9508b1d909e46ca1a2f21d7448e2a3eec6b9e9d Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 5 Jun 2023 10:24:33 +0000 Subject: [PATCH 083/199] Add the required_if rule --- src/Validation/FieldLexical.php | 4 +-- src/Validation/Rules/StringRule.php | 55 +++++++++++++++++++++++++++++ src/Validation/Validator.php | 1 + 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/src/Validation/FieldLexical.php b/src/Validation/FieldLexical.php index 5d57c223..1b065dc3 100644 --- a/src/Validation/FieldLexical.php +++ b/src/Validation/FieldLexical.php @@ -12,10 +12,10 @@ trait FieldLexical * Get error debugging information * * @param string $key - * @param string|array $value + * @param string|array|int|float $value * @return ?string */ - private function lexical(string $key, string|array $value): ?string + private function lexical(string $key, string|array|int|float $value): ?string { $data = array_merge( $this->inputs ?? [], diff --git a/src/Validation/Rules/StringRule.php b/src/Validation/Rules/StringRule.php index f27fff47..cb0d2276 100644 --- a/src/Validation/Rules/StringRule.php +++ b/src/Validation/Rules/StringRule.php @@ -5,6 +5,7 @@ namespace Bow\Validation\Rules; use Bow\Support\Str; +use Bow\Validation\Exception\ValidationException; trait StringRule { @@ -19,6 +20,10 @@ protected function compileRequired(string $key, string $masque): void { $error = false; + if (!preg_match("/^required$/", (string) $masque, $match)) { + return; + } + if (!isset($this->inputs[$key])) { $error = true; } @@ -39,6 +44,56 @@ protected function compileRequired(string $key, string $masque): void } } + /** + * Compile Required Rule + * + * @param string $key + * @param string $masque + * @return void + */ + protected function compileRequiredIf(string $key, string $masque): void + { + $error = false; + $exists = false; + + if (!preg_match("/^required_if:(.+)+$/", (string) $masque, $match)) { + throw new ValidationException("The required_if is malformed"); + } + + array_shift($match); + + if (count($match) < 1) { + throw new ValidationException("The required_if is malformed"); + } + + foreach ($match as $key => $present) { + if ($key == 0) { + $exists = isset($this->inputs[$present]); + } else { + $exists = $exists && isset($this->inputs[$present]); + } + } + + if ($exists && !isset($this->inputs[$key])) { + $error = true; + } + + if (!$error && isset($this->inputs[$key]) && (is_null($this->inputs[$key]) || $this->inputs[$key] === '')) { + $error = true; + } + + if ($error) { + $this->last_message = $message = $this->lexical('required_if', $key); + + $this->errors[$key][] = [ + "masque" => $masque, + "message" => $message + ]; + + $this->fails = true; + } + } + /** * Compile Empty Rule * diff --git a/src/Validation/Validator.php b/src/Validation/Validator.php index d40b6ec6..e83fcaf2 100644 --- a/src/Validation/Validator.php +++ b/src/Validation/Validator.php @@ -64,6 +64,7 @@ class Validator */ protected array $rules = [ 'Required', + "RequiredIf", 'Max', 'Min', 'Lower', From 694bbabff5753e3baf9b56b00c8055fe3c82457c Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 5 Jun 2023 10:24:46 +0000 Subject: [PATCH 084/199] Add log service support --- src/Support/Log.php | 26 ++++++++++++ src/Support/LoggerService.php | 78 +++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 src/Support/Log.php create mode 100644 src/Support/LoggerService.php diff --git a/src/Support/Log.php b/src/Support/Log.php new file mode 100644 index 00000000..819f1f5d --- /dev/null +++ b/src/Support/Log.php @@ -0,0 +1,26 @@ +error($message, $context); + } + + /** + * Logger service + * + * @param string $message + * @param array $context + * @return mixed + */ + public function info(string $message, array $context = []) + { + app('logger')->info($message, $context); + } + + /** + * Logger service + * + * @param string $message + * @param array $context + * @return mixed + */ + public function warning(string $message, array $context = []) + { + app('logger')->warning($message, $context); + } + + /** + * Logger service + * + * @param string $message + * @param array $context + * @return mixed + */ + public function alert(string $message, array $context = []) + { + app('logger')->alert($message, $context); + } + + /** + * Logger service + * + * @param string $message + * @param array $context + * @return mixed + */ + public function critical(string $message, array $context = []) + { + app('logger')->critical($message, $context); + } + + /** + * Logger service + * + * @param string $message + * @param array $context + * @return mixed + */ + public function emergency(string $message, array $context = []) + { + app('logger')->emergency($message, $context); + } +} From 2265a83c60fc52553228edaae7dca0687597cec1 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 5 Jun 2023 11:54:32 +0000 Subject: [PATCH 085/199] Fixes the required_if rule --- src/Validation/Rules/StringRule.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Validation/Rules/StringRule.php b/src/Validation/Rules/StringRule.php index cb0d2276..f0339983 100644 --- a/src/Validation/Rules/StringRule.php +++ b/src/Validation/Rules/StringRule.php @@ -53,11 +53,8 @@ protected function compileRequired(string $key, string $masque): void */ protected function compileRequiredIf(string $key, string $masque): void { - $error = false; - $exists = false; - if (!preg_match("/^required_if:(.+)+$/", (string) $masque, $match)) { - throw new ValidationException("The required_if is malformed"); + return; } array_shift($match); @@ -66,11 +63,15 @@ protected function compileRequiredIf(string $key, string $masque): void throw new ValidationException("The required_if is malformed"); } - foreach ($match as $key => $present) { + $error = false; + $exists = false; + $fields = explode(",", $match[0]); + + foreach ($fields as $key => $field) { if ($key == 0) { - $exists = isset($this->inputs[$present]); + $exists = isset($this->inputs[$field]); } else { - $exists = $exists && isset($this->inputs[$present]); + $exists = $exists && isset($this->inputs[$field]); } } From 214b25e8e25419ce278b6399ad51b7796bd63dcd Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 5 Jun 2023 12:37:48 +0000 Subject: [PATCH 086/199] Update class docs --- src/Support/Log.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Support/Log.php b/src/Support/Log.php index 819f1f5d..568ebac1 100644 --- a/src/Support/Log.php +++ b/src/Support/Log.php @@ -3,12 +3,12 @@ namespace Bow\Support; /** - * @method void error(string $message, array $context = []) - * @method void info(string $message, array $context = []) - * @method void warning(string $message, array $context = []) - * @method void alert(string $message, array $context = []) - * @method void critical(string $message, array $context = []) - * @method void emergency(string $message, array $context = []) + * @method static void error(string $message, array $context = []) + * @method static void info(string $message, array $context = []) + * @method static void warning(string $message, array $context = []) + * @method static void alert(string $message, array $context = []) + * @method static void critical(string $message, array $context = []) + * @method static void emergency(string $message, array $context = []) */ class Log { From 212034838a8b24078e6a2e4edecf365320430967 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 6 Jun 2023 17:05:15 +0000 Subject: [PATCH 087/199] Refactoring kernel testing api --- src/Configuration/Configuration.php | 10 ++++++++++ src/Testing/KernelTesting.php | 4 ---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Configuration/Configuration.php b/src/Configuration/Configuration.php index 7c04229c..b3319dd1 100644 --- a/src/Configuration/Configuration.php +++ b/src/Configuration/Configuration.php @@ -23,6 +23,16 @@ public function __construct(Container $container) $this->container = $container; } + /** + * Get the container instance + * + * @return Container + */ + public function getContainer(): Container + { + return $this->container; + } + /** * Get la service class name * diff --git a/src/Testing/KernelTesting.php b/src/Testing/KernelTesting.php index 50cf3bea..fd216e76 100644 --- a/src/Testing/KernelTesting.php +++ b/src/Testing/KernelTesting.php @@ -6,10 +6,6 @@ class KernelTesting extends ConfigurationLoader { - public static array $configurations = []; - public static array $events = []; - public static array $middlewares = []; - /** * Set the loading configuration * From 6b2a2be719b568da83d7ac2f7421bf7478407295 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 6 Jun 2023 17:07:50 +0000 Subject: [PATCH 088/199] Fixes validation --- tests/Validation/ValidationTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Validation/ValidationTest.php b/tests/Validation/ValidationTest.php index b056a15e..17105e4b 100644 --- a/tests/Validation/ValidationTest.php +++ b/tests/Validation/ValidationTest.php @@ -167,8 +167,8 @@ public function test_required_rule() $first_validation = Validator::make(['name' => 'Couli'], ['lastname' => 'required']); $second_validation = Validator::make(['name' => 'Milou'], ['name' => 'required']); - $this->assertFalse($first_validation->fails()); - $this->assertTrue($second_validation->fails()); + $this->assertTrue($first_validation->fails()); + $this->assertFalse($second_validation->fails()); } public function test_required_if_rule() From 96965129a2173817497c34b727fff32da5c5381d Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 6 Jun 2023 17:21:57 +0000 Subject: [PATCH 089/199] Update kernel testing --- src/Configuration/Loader.php | 24 ------------------------ src/Testing/KernelTesting.php | 4 ++++ 2 files changed, 4 insertions(+), 24 deletions(-) diff --git a/src/Configuration/Loader.php b/src/Configuration/Loader.php index 0075fcbd..010afa34 100644 --- a/src/Configuration/Loader.php +++ b/src/Configuration/Loader.php @@ -119,30 +119,6 @@ public static function configure($base_path): Loader return static::$instance; } - /** - * Push middlewares - * - * @param array $middlewares - */ - public function pushMiddleware(array $middlewares): void - { - foreach ($middlewares as $key => $middleware) { - $this->middlewares[$key] = $middleware; - } - } - - /** - * Push namespaces - * - * @param array $namespaces - */ - public function pushNamespaces(array $namespaces): void - { - foreach ($namespaces as $key => $namespace) { - $this->namespaces[$key] = $namespace; - } - } - /** * Middleware collection * diff --git a/src/Testing/KernelTesting.php b/src/Testing/KernelTesting.php index fd216e76..3ac570e8 100644 --- a/src/Testing/KernelTesting.php +++ b/src/Testing/KernelTesting.php @@ -6,6 +6,10 @@ class KernelTesting extends ConfigurationLoader { + private static array $configurations = []; + private static array $events = []; + private static array $middlewares = []; + /** * Set the loading configuration * From 2a096d57dff383cb9d3d000d3a3ab2550065559c Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 6 Jun 2023 21:54:41 +0000 Subject: [PATCH 090/199] Make redis accessible to the whole project --- src/Cache/Adapter/RedisAdapter.php | 35 +----- src/Cache/Cache.php | 2 +- src/Cache/CacheConfiguration.php | 2 +- src/Database/Redis.php | 168 ++++++++++++++++++++++++++++ tests/Cache/CacheDatabaseTest.php | 10 +- tests/Cache/CacheFilesystemTest.php | 2 +- tests/Cache/CacheRedisTest.php | 10 +- tests/Config/stubs/cache.php | 18 +-- tests/Config/stubs/database.php | 21 ++++ tests/Database/RedisTest.php | 30 +++++ 10 files changed, 236 insertions(+), 62 deletions(-) create mode 100644 src/Database/Redis.php create mode 100644 tests/Database/RedisTest.php diff --git a/src/Cache/Adapter/RedisAdapter.php b/src/Cache/Adapter/RedisAdapter.php index dfaa92c0..f076f678 100644 --- a/src/Cache/Adapter/RedisAdapter.php +++ b/src/Cache/Adapter/RedisAdapter.php @@ -4,6 +4,7 @@ use Redis; use Bow\Cache\Adapter\CacheAdapterInterface; +use Bow\Database\Redis as RedisStore; class RedisAdapter implements CacheAdapterInterface { @@ -22,39 +23,7 @@ class RedisAdapter implements CacheAdapterInterface */ public function __construct(array $config) { - - $options = []; - $auth = []; - if (isset($config["password"])) { - $auth[] = $config["password"]; - } - - if (isset($config["username"]) && !is_null($config["username"])) { - array_unshift($auth, $config["username"]); - } - - if (count($auth) > 0) { - $options = compact('auth'); - } - - $options['backoff'] = [ - 'algorithm' => Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER, - 'base' => 500, - 'cap' => 750, - ]; - - $this->redis = new Redis(); - $this->redis->connect( - $config["host"], - $config["port"] ?? 6379, - $config["timeout"] ?? 2.5, - null, - 0, - 0, - $options - ); - - $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_JSON); + $this->redis = RedisStore::getClient(); if (isset($config["prefix"])) { $this->redis->setOption(Redis::OPT_PREFIX, $config["prefix"]); diff --git a/src/Cache/Cache.php b/src/Cache/Cache.php index befe1d52..831cb0fd 100644 --- a/src/Cache/Cache.php +++ b/src/Cache/Cache.php @@ -44,7 +44,7 @@ class Cache * * @param array $config */ - public static function confirgure(array $config) + public static function configure(array $config) { if (!is_null(static::$instance)) { return static::$instance; diff --git a/src/Cache/CacheConfiguration.php b/src/Cache/CacheConfiguration.php index 6383fda3..e85650a7 100644 --- a/src/Cache/CacheConfiguration.php +++ b/src/Cache/CacheConfiguration.php @@ -16,7 +16,7 @@ class CacheConfiguration extends Configuration public function create(Loader $config): void { $this->container->bind('cache', function () use ($config) { - return Cache::confirgure($config['cache']); + return Cache::configure($config['cache']); }); } diff --git a/src/Database/Redis.php b/src/Database/Redis.php new file mode 100644 index 00000000..3ce09a54 --- /dev/null +++ b/src/Database/Redis.php @@ -0,0 +1,168 @@ + 0) { + $options = compact('auth'); + } + + $options['backoff'] = [ + 'algorithm' => RedisClient::BACKOFF_ALGORITHM_DECORRELATED_JITTER, + 'base' => 500, + 'cap' => 750, + ]; + + static::$redis = new RedisClient(); + static::$redis->connect( + $config["host"], + $config["port"] ?? 6379, + $config["timeout"] ?? 2.5, + null, + 0, + 0, + $options + ); + + static::$redis->setOption(RedisClient::OPT_SERIALIZER, RedisClient::SERIALIZER_JSON); + + if (isset($config["prefix"])) { + static::$redis->setOption(RedisClient::OPT_PREFIX, $config["prefix"]); + } + + static::$redis->select($config["database"] ?? 0); + } + + /** + * Ping the redis service + * + * @param ?string $message + */ + public static function ping(?string $message = null) + { + static::$redis->ping($message); + } + + /** + * Set value on Redis + * + * @param string $key + * @param mixed $data + * @param integer|null $time + * @return boolean + */ + public static function set(string $key, mixed $data, ?int $time = null): bool + { + if (is_null(static::$instance)) { + static::$instance = static::getInstance(); + } + + $options = []; + + if (is_callable($data)) { + $content = $data(); + } else { + $content = $data; + } + + if (!is_null($time)) { + $options = [ + 'EX' => $time + ]; + } + + return static::$redis->set($key, $content, $options); + } + + /** + * Get the value from Redis + * + * @param string $key + * @param mixed $default + * @return mixed + */ + public static function get(string $key, mixed $default = null): mixed + { + if (is_null(static::$instance)) { + static::$instance = static::getInstance(); + } + + if (!static::$redis->exists($key)) { + return is_callable($default) ? $default() : $default; + } + + $value = static::$redis->get($key); + + return is_null($value) ? $default : $value; + } + + /** + * Get the php-redis client + * + * @see https://github.com/phpredis/phpredis + * @return RedisClient + */ + public static function getClient(): RedisClient + { + if (is_null(static::$instance)) { + static::$instance = static::getInstance(); + } + + return static::$redis; + } +} diff --git a/tests/Cache/CacheDatabaseTest.php b/tests/Cache/CacheDatabaseTest.php index 49db5514..c08fdcce 100644 --- a/tests/Cache/CacheDatabaseTest.php +++ b/tests/Cache/CacheDatabaseTest.php @@ -22,7 +22,7 @@ public static function setUpBeforeClass(): void `expire` datetime null )"); - Cache::confirgure($config["cache"]); + Cache::configure($config["cache"]); Cache::cache("database"); } @@ -38,7 +38,7 @@ public function test_get_cache() $this->assertEquals(Cache::get('name'), 'Dakia'); } - public function test_AddWithCallbackCache() + public function test_add_with_callback_cache() { $result = Cache::add('lastname', fn () => 'Franck'); $result = $result && Cache::add('age', fn () => 25, 20000); @@ -46,14 +46,14 @@ public function test_AddWithCallbackCache() $this->assertEquals($result, true); } - public function test_GetCallbackCache() + public function test_get_callback_cache() { $this->assertEquals(Cache::get('lastname'), 'Franck'); $this->assertEquals(Cache::get('age'), 25); } - public function test_AddArrayCache() + public function test_add_array_cache() { $result = Cache::add('address', [ 'tel' => "49929598", @@ -64,7 +64,7 @@ public function test_AddArrayCache() $this->assertEquals($result, true); } - public function test_GetArrayCache() + public function test_get_array_cache() { $result = Cache::get('address'); diff --git a/tests/Cache/CacheFilesystemTest.php b/tests/Cache/CacheFilesystemTest.php index fc88f725..2595fca2 100644 --- a/tests/Cache/CacheFilesystemTest.php +++ b/tests/Cache/CacheFilesystemTest.php @@ -11,7 +11,7 @@ protected function setUp(): void { parent::setUp(); $config = TestingConfiguration::getConfig(); - Cache::confirgure($config["cache"]); + Cache::configure($config["cache"]); Cache::cache("file"); } diff --git a/tests/Cache/CacheRedisTest.php b/tests/Cache/CacheRedisTest.php index dec3f5ea..2e8508c0 100644 --- a/tests/Cache/CacheRedisTest.php +++ b/tests/Cache/CacheRedisTest.php @@ -11,7 +11,7 @@ protected function setUp(): void { parent::setUp(); $config = TestingConfiguration::getConfig(); - Cache::confirgure($config["cache"]); + Cache::configure($config["cache"]); Cache::cache("redis"); } @@ -27,7 +27,7 @@ public function test_get_cache() $this->assertEquals(Cache::get('name'), 'Dakia'); } - public function test_AddWithCallbackCache() + public function test_add_with_callback_cache() { $result = Cache::add('lastname', fn () => 'Franck'); $result = $result && Cache::add('age', fn () => 25, 20000); @@ -35,14 +35,14 @@ public function test_AddWithCallbackCache() $this->assertEquals($result, true); } - public function test_GetCallbackCache() + public function test_get_callback_cache() { $this->assertEquals(Cache::get('lastname'), 'Franck'); $this->assertEquals(Cache::get('age'), 25); } - public function test_AddArrayCache() + public function test_add_array_cache() { $result = Cache::add('address', [ 'tel' => "49929598", @@ -53,7 +53,7 @@ public function test_AddArrayCache() $this->assertEquals($result, true); } - public function test_GetArrayCache() + public function test_get_array_cache() { $result = Cache::get('address'); diff --git a/tests/Config/stubs/cache.php b/tests/Config/stubs/cache.php index 71770976..69e79c81 100644 --- a/tests/Config/stubs/cache.php +++ b/tests/Config/stubs/cache.php @@ -15,25 +15,11 @@ "table" => "caches", ], + // The redis connection "redis" => [ 'driver' => 'redis', - 'host' => app_env('REDIS_HOSTNAME', '127.0.0.1'), - 'port' => app_env('REDIS_PORT', 6379), - 'timeout' => 2.5, - 'ssl' => false, - 'username' => app_env('REDIS_USERNAME'), - 'password' => app_env('REDIS_PASSWORD'), - 'database' => app_env('REDIS_CACHE_DB', '1'), + 'database' => app_env('REDIS_CACHE_DB', 5), "prefix" => "__app__", - 'slave' => [ - 'host' => app_env('REDIS_HOSTNAME', '127.0.0.1'), - 'port' => app_env('REDIS_PORT', 6379), - 'timeout' => 2.5, - 'ssl' => false, - 'username' => app_env('REDIS_USERNAME'), - 'password' => app_env('REDIS_PASSWORD'), - 'database' => app_env('REDIS_CACHE_DB', '1'), - ] ] ] ]; diff --git a/tests/Config/stubs/database.php b/tests/Config/stubs/database.php index 7225b4fb..f6cb2527 100644 --- a/tests/Config/stubs/database.php +++ b/tests/Config/stubs/database.php @@ -31,5 +31,26 @@ 'database' => ':memory:', 'prefix' => '' ] + ], + + "redis" => [ + 'driver' => 'redis', + 'host' => app_env('REDIS_HOSTNAME', '127.0.0.1'), + 'port' => app_env('REDIS_PORT', 6379), + 'timeout' => 2.5, + 'ssl' => false, + 'username' => app_env('REDIS_USERNAME'), + 'password' => app_env('REDIS_PASSWORD'), + 'database' => app_env('REDIS_CACHE_DB', '1'), + "prefix" => "__app__", + 'slave' => [ + 'host' => app_env('REDIS_HOSTNAME', '127.0.0.1'), + 'port' => app_env('REDIS_PORT', 6379), + 'timeout' => 2.5, + 'ssl' => false, + 'username' => app_env('REDIS_USERNAME'), + 'password' => app_env('REDIS_PASSWORD'), + 'database' => app_env('REDIS_CACHE_DB', '1'), + ] ] ]; diff --git a/tests/Database/RedisTest.php b/tests/Database/RedisTest.php new file mode 100644 index 00000000..45f4fa2d --- /dev/null +++ b/tests/Database/RedisTest.php @@ -0,0 +1,30 @@ +assertEquals($result, true); + } + + public function test_get_cache() + { + Redis::set('lastname', 'papac'); + + $this->assertNull(Redis::get('name')); + $this->assertEquals(Redis::get('lastname'), "papac"); + } +} From 2456694f7efdfcec7d4d539bb1d0eda8901edbbf Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 6 Jun 2023 22:13:55 +0000 Subject: [PATCH 091/199] Add custom adaptor --- src/Cache/Cache.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Cache/Cache.php b/src/Cache/Cache.php index 831cb0fd..17b7a98c 100644 --- a/src/Cache/Cache.php +++ b/src/Cache/Cache.php @@ -95,6 +95,19 @@ public static function cache(string $store): CacheAdapterInterface return static::$instance; } + /** + * Add the custom adapters + * + * @param array $adapters + * @return void + */ + public static function addAdapters(array $adapters): void + { + foreach ($adapters as $name => $adapter) { + static::$adapters[$name] = $adapter; + } + } + /** * __call * From 458b5a574365d6909df18c28ba257b58693fc03a Mon Sep 17 00:00:00 2001 From: papac Date: Wed, 7 Jun 2023 08:09:39 +0000 Subject: [PATCH 092/199] Update CHANGELOG --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a34a29e..2201191b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 5.1.0 - 2023-06-07 + +Release 5.1.0 + +- Add custom adaptor #252 +- Make Redis accessible on whole project #250 + ## 5.0.9 - 2023-06-01 Release 5.0.9 From 947bac0c765dd7137b476ae13e964c75ac35b552 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Wed, 7 Jun 2023 08:20:09 +0000 Subject: [PATCH 093/199] Update README.md --- src/Cache/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Cache/README.md b/src/Cache/README.md index 20f6ad45..a1229c62 100644 --- a/src/Cache/README.md +++ b/src/Cache/README.md @@ -2,6 +2,21 @@ Bow Framework's cache system is very simple cache manager +- Filesystem +- Database +- Redis +- With extended driver interface + +Let's show a little exemple: + ```php $content = cache("name"); ``` + +By specifying the driver: + +``` +$content = Cache::cache('redis')->get('name'); +``` + +Is very enjoyful api From 627f513c4f6d5e7afd116cb5ebadde2832233703 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 21 Aug 2023 01:25:52 +0000 Subject: [PATCH 094/199] Update Database.php Add the transaction function --- src/Database/Database.php | 41 ++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/src/Database/Database.php b/src/Database/Database.php index 55a94dcc..7ed66eef 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -121,7 +121,7 @@ public static function connection(?string $name = null): ?Database } /** - * Get connexion name + * Get the connexion name * * @return string|null */ @@ -315,7 +315,7 @@ public static function delete(string $sql_statement, array $data = []): int } /** - * Load the query builder factory on table name + * Load the query builder factory on the table name * * @param string $table * @return QueryBuilder @@ -338,27 +338,17 @@ public static function table(string $table): QueryBuilder * @param callable $callback * @return void */ - public static function startTransaction(?callable $callback = null): void + public static function startTransaction(): mixed { static::verifyConnection(); if (!static::$adapter->getConnection()->inTransaction()) { static::$adapter->getConnection()->beginTransaction(); } - - if (is_callable($callback)) { - try { - call_user_func_array($callback, []); - - static::commit(); - } catch (DatabaseException $e) { - static::rollback(); - } - } } /** - * Check if database execution is in transaction + * Check if database execution is in the transaction * * @return bool */ @@ -389,6 +379,29 @@ public static function rollback(): void static::$adapter->getConnection()->rollBack(); } + /** + * Starting the start of a transaction wrapper on top of the callback + * + * @param callable $callback + * @return void + */ + public static function transaction(callable $callback): mixed + { + static::startTransaction(); + + try { + $result = call_user_func_array($callback, []); + + static::commit(); + + return $result; + } catch (DatabaseException $e) { + static::rollback(); + + throw $e; + } + } + /** * Starts the verification of the connection establishment * From 364ef3a6d8ad3e67528b3cdcd22633b453381225 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 21 Aug 2023 01:49:39 +0000 Subject: [PATCH 095/199] Fixes transform model collection to json --- src/Database/Barry/Model.php | 14 +++++++++++--- src/Database/Collection.php | 24 +++++++++++++++++++++--- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/Database/Barry/Model.php b/src/Database/Barry/Model.php index 0e07a98a..9ce4c9ea 100644 --- a/src/Database/Barry/Model.php +++ b/src/Database/Barry/Model.php @@ -591,10 +591,10 @@ private function transtypeKeyValue(mixed $primary_key_value): mixed * Delete a record * * @param array $attributes - * @return int + * @return int|bool * @throws */ - public function update(array $attributes): int + public function update(array $attributes): int|bool { $primary_key_value = $this->getKeyValue(); @@ -820,7 +820,15 @@ public function getTable(): string */ public function toArray(): array { - return $this->attributes; + $data = []; + + foreach ($this->attributes as $key => $value) { + if (!in_array($key, $this->hidden)) { + $data[$key] = $value; + } + } + + return $data; } /** diff --git a/src/Database/Collection.php b/src/Database/Collection.php index 078f0f26..b91b61bd 100644 --- a/src/Database/Collection.php +++ b/src/Database/Collection.php @@ -47,7 +47,13 @@ public function toArray(): array */ public function toJson(int $option = 0): string { - return json_encode($this->toArray(), $option = 0); + $data = []; + + foreach ($this->toArray() as $model) { + $data[] = $model->toArray(); + } + + return json_encode($data, $option = 0); } /** @@ -67,7 +73,13 @@ public function dropAll(): void */ public function __toString(): string { - return json_encode($this->toArray()); + $data = []; + + foreach ($this->toArray() as $model) { + $data[] = $model->toArray(); + } + + return json_encode($data); } /** @@ -75,6 +87,12 @@ public function __toString(): string */ public function jsonSerialize(): array { - return $this->toArray(); + $data = []; + + foreach ($this->toArray() as $model) { + $data[] = $model->toArray(); + } + + return $data; } } From 85de991f8f171d8243c34aa2e0700231b3b2a433 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 21 Aug 2023 01:57:45 +0000 Subject: [PATCH 096/199] Fixes unity tests --- src/Database/Database.php | 2 +- tests/Database/Query/DatabaseQueryTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Database/Database.php b/src/Database/Database.php index 7ed66eef..c5116625 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -338,7 +338,7 @@ public static function table(string $table): QueryBuilder * @param callable $callback * @return void */ - public static function startTransaction(): mixed + public static function startTransaction(): void { static::verifyConnection(); diff --git a/tests/Database/Query/DatabaseQueryTest.php b/tests/Database/Query/DatabaseQueryTest.php index 39fde5c1..1e42e41e 100644 --- a/tests/Database/Query/DatabaseQueryTest.php +++ b/tests/Database/Query/DatabaseQueryTest.php @@ -214,7 +214,7 @@ public function test_transaction_table(string $name) $database->insert("insert into pets values(:id, :name);", ["id" => 1, 'name' => 'Ploy']); $result = 0; - $database->startTransaction(function () use ($database, &$result) { + $database->transaction(function () use ($database, &$result) { $result = $database->delete("delete from pets where id = :id", ['id' => 1]); $this->assertEquals($database->inTransaction(), true); }); From ff05d714d78ae1e7d84a19e30f9564811660d100 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 21 Aug 2023 02:08:59 +0000 Subject: [PATCH 097/199] Fixes collection as json --- src/Database/Collection.php | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/Database/Collection.php b/src/Database/Collection.php index b91b61bd..01770f77 100644 --- a/src/Database/Collection.php +++ b/src/Database/Collection.php @@ -73,13 +73,7 @@ public function dropAll(): void */ public function __toString(): string { - $data = []; - - foreach ($this->toArray() as $model) { - $data[] = $model->toArray(); - } - - return json_encode($data); + return json_encode($this->all()); } /** @@ -87,12 +81,6 @@ public function __toString(): string */ public function jsonSerialize(): array { - $data = []; - - foreach ($this->toArray() as $model) { - $data[] = $model->toArray(); - } - - return $data; + return $this->all(); } } From 1f4396c453137441ebfc7ff7e635dfbe2a2c2f61 Mon Sep 17 00:00:00 2001 From: papac Date: Fri, 15 Sep 2023 19:51:27 +0000 Subject: [PATCH 098/199] Update CHANGELOG --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2201191b..d8c0a3aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 5.1.1 - 2023-08-21 + +Add the transaction method + +This method aims to execute an SQL transaction around a passed arrow function. + +```php +Database::transaction(fn() => $user->update(['name' => ''])); + +``` +Ref: #255 + ## 5.1.0 - 2023-06-07 Release 5.1.0 From 1277b523bb1c5baff95210e6e9231b7d56947994 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sun, 17 Sep 2023 12:45:01 +0000 Subject: [PATCH 099/199] Update Database.php --- src/Database/Database.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Database/Database.php b/src/Database/Database.php index c5116625..77cb0c16 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -383,7 +383,7 @@ public static function rollback(): void * Starting the start of a transaction wrapper on top of the callback * * @param callable $callback - * @return void + * @return mixed */ public static function transaction(callable $callback): mixed { From c452a07acc72a11de699e2c6f8039c96a879c47e Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sun, 17 Sep 2023 12:47:55 +0000 Subject: [PATCH 100/199] Update helpers.php --- src/Support/helpers.php | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/Support/helpers.php b/src/Support/helpers.php index 3cffb971..c0632b6d 100644 --- a/src/Support/helpers.php +++ b/src/Support/helpers.php @@ -157,6 +157,7 @@ function view(string $template, int|array $data = [], int $code = 200) * @param string $name * @param string $connexion * @return Bow\Database\QueryBuilder + * @deprecated */ function table(string $name, string $connexion = null) { @@ -182,6 +183,24 @@ function get_last_insert_id(string $name = null) } } +if (!function_exists('db_table')) { + /** + * Table alias of DB::table + * + * @param string $name + * @param string $connexion + * @return Bow\Database\QueryBuilder + */ + function db_table(string $name, string $connexion = null) + { + if (is_string($connexion)) { + db($connexion); + } + + return DB::table($name); + } +} + if (!function_exists('db_select')) { /** * Launches SELECT SQL Queries @@ -1454,7 +1473,7 @@ function db_seed(string $name, array $data = []): mixed } } - $filename = rtrim(config('app.seeder_path'), '/') . '/' . $name . '_seeder.php'; + $filename = rtrim(config('app.seeder_path'), '/') . '/' . $name . '.php'; if (!file_exists($filename)) { throw new \ErrorException('[' . $name . '] seeder file not found'); From b29142806cef97eb62c247be54564b2fb45773c5 Mon Sep 17 00:00:00 2001 From: papac Date: Sun, 17 Sep 2023 13:38:48 +0000 Subject: [PATCH 101/199] Update CHANGELOG --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8c0a3aa..ea0c24c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 5.1.2 - 2023-09-17 + +Fix `db_seed` helper + +Ref + +- #257 +- #256 + ## 5.1.1 - 2023-08-21 Add the transaction method @@ -14,6 +23,7 @@ This method aims to execute an SQL transaction around a passed arrow function. ```php Database::transaction(fn() => $user->update(['name' => ''])); + ``` Ref: #255 From a9fee790bbb6d5b20cb7577913e86a1a42c0c5a1 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 19 Sep 2023 17:50:06 +0000 Subject: [PATCH 102/199] Fixes many issues --- src/Console/Command/WorkerCommand.php | 12 ++- src/Database/QueryBuilder.php | 19 +++++ src/Queue/Adapters/BeanstalkdAdapter.php | 36 ++++---- src/Queue/Adapters/QueueAdapter.php | 9 -- src/Queue/ProducerService.php | 27 ++++++ src/Queue/Traits/SerializesModels.php | 104 ----------------------- src/Support/helpers.php | 17 ++-- 7 files changed, 83 insertions(+), 141 deletions(-) delete mode 100644 src/Queue/Traits/SerializesModels.php diff --git a/src/Console/Command/WorkerCommand.php b/src/Console/Command/WorkerCommand.php index 4d4b4c1c..d0c24066 100644 --- a/src/Console/Command/WorkerCommand.php +++ b/src/Console/Command/WorkerCommand.php @@ -25,8 +25,18 @@ public function run(?string $connection = null): void $queue->setConnection($connection); } - $worker = new WorkerService(); + $worker = $this->getWorderService(); $worker->setConnection($queue->getAdapter()); $worker->run($default, $retry); } + + /** + * Get the worker service + * + * @return WorkerService + */ + private function getWorderService() + { + return new WorkerService(); + } } diff --git a/src/Database/QueryBuilder.php b/src/Database/QueryBuilder.php index 97a55c09..458017a9 100644 --- a/src/Database/QueryBuilder.php +++ b/src/Database/QueryBuilder.php @@ -279,6 +279,25 @@ public function whereRaw(string $where): QueryBuilder return $this; } + /** + * Add orWhere clause into the request + * + * WHERE column1 $comparator $value|column + * + * @param string $where + * @return QueryBuilder + */ + public function orWhereRaw(string $where): QueryBuilder + { + if ($this->where == null) { + $this->where = $where; + } else { + $this->where .= ' or ' . $where; + } + + return $this; + } + /** * orWhere, add a condition of type: * diff --git a/src/Queue/Adapters/BeanstalkdAdapter.php b/src/Queue/Adapters/BeanstalkdAdapter.php index 31fdf17d..300909e1 100644 --- a/src/Queue/Adapters/BeanstalkdAdapter.php +++ b/src/Queue/Adapters/BeanstalkdAdapter.php @@ -7,7 +7,7 @@ use Pheanstalk\Pheanstalk; use Bow\Queue\ProducerService; use Bow\Queue\Adapters\QueueAdapter; -use Pheanstalk\Job as PheanstalkJob; +use Pheanstalk\Contract\PheanstalkInterface; use RuntimeException; class BeanstalkdAdapter extends QueueAdapter @@ -70,20 +70,6 @@ public function setRetry(int $retry): void $this->retry = $retry; } - /** - * Delete a message from the Beanstalk queue. - * - * @param string $queue - * @param string|int $id - * @return void - */ - public function deleteJob(string $queue, string|int $id): void - { - $queue = $this->getQueue($queue); - - $this->pheanstalk->useTube($queue)->delete(new PheanstalkJob($id, '')); - } - /** * Get the queue or return the default. * @@ -134,17 +120,29 @@ public function run(string $queue = null): void $this->pheanstalk->watch($queue); // This hangs until a Job is produced. - $job = $this->pheanstalk->reserve(); + $job = $this->pheanstalk->reserveWithTimeout(50); + + if (is_null($job)) { + return; + } try { $payload = $job->getData(); + /**@var ProducerService */ $producer = unserialize($payload); + $delay = $producer->getDelay(); call_user_func([$producer, "process"]); $this->pheanstalk->touch($job); - $this->deleteJob($queue, $job->getId()); + $this->pheanstalk->delete($job); } catch (\Exception $e) { - cache($job->getId(), $job->getData()); - $this->pheanstalk->release($job); + error_log($e->getMessage()); + app('logger')->error($e->getMessage(), $e->getTrace()); + cache("failed:job:" . $job->getId(), $job->getData()); + if ($producer->jobShouldBeDelete()) { + $this->pheanstalk->delete($job); + } else { + $this->pheanstalk->release($job, PheanstalkInterface::DEFAULT_PRIORITY, $delay); + } } } } diff --git a/src/Queue/Adapters/QueueAdapter.php b/src/Queue/Adapters/QueueAdapter.php index e2f4e341..57943472 100644 --- a/src/Queue/Adapters/QueueAdapter.php +++ b/src/Queue/Adapters/QueueAdapter.php @@ -67,15 +67,6 @@ abstract public function push(ProducerService $producer): void; */ abstract public function size(string $queue): int; - /** - * Delete a message from the queue. - * - * @param string $queue - * @param string|int $id - * @return void - */ - abstract public function deleteJob(string $queue, string|int $id): void; - /** * Get the queue or return the default. * diff --git a/src/Queue/ProducerService.php b/src/Queue/ProducerService.php index f60ece1c..bcb7977b 100644 --- a/src/Queue/ProducerService.php +++ b/src/Queue/ProducerService.php @@ -38,6 +38,13 @@ abstract class ProducerService */ protected int $priority = 1; + /** + * Determine if the job can be deleted + * + * @var bool + */ + private bool $delete = false; + /** * Get the producer priority * @@ -78,6 +85,26 @@ final public function getDelay(): int return $this->delay; } + /** + * Delete the job from queue. + * + * @return void + */ + public function deleteJob(): void + { + $this->delete = true; + } + + /** + * Delete the job from queue. + * + * @return bool + */ + public function jobShouldBeDelete(): bool + { + return $this->delete; + } + /** * Process the producer * diff --git a/src/Queue/Traits/SerializesModels.php b/src/Queue/Traits/SerializesModels.php deleted file mode 100644 index 5d895c31..00000000 --- a/src/Queue/Traits/SerializesModels.php +++ /dev/null @@ -1,104 +0,0 @@ -getProperties(); - - $class = get_class($this); - - foreach ($properties as $property) { - if ($property->isStatic()) { - continue; - } - - $property->setAccessible(true); - - if (! $property->isInitialized($this)) { - continue; - } - - $value = $this->getPropertyValue($property); - - if ($property->hasDefaultValue() && $value === $property->getDefaultValue()) { - continue; - } - - $name = $property->getName(); - - if ($property->isPrivate()) { - $name = "\0{$class}\0{$name}"; - } elseif ($property->isProtected()) { - $name = "\0*\0{$name}"; - } - - $values[$name] = $value; - } - - return $values; - } - - /** - * Restore the model after serialization. - * - * @param array $values - * @return void - */ - public function __unserialize(array $values) - { - $properties = (new ReflectionClass($this))->getProperties(); - - $class = get_class($this); - - foreach ($properties as $property) { - if ($property->isStatic()) { - continue; - } - - $name = $property->getName(); - - if ($property->isPrivate()) { - $name = "\0{$class}\0{$name}"; - } elseif ($property->isProtected()) { - $name = "\0*\0{$name}"; - } - - if (! array_key_exists($name, $values)) { - continue; - } - - $property->setAccessible(true); - - $property->setValue( - $this, - $values[$name] - ); - } - } - - /** - * Get the property value for the given property. - * - * @param \ReflectionProperty $property - * @return mixed - */ - protected function getPropertyValue(ReflectionProperty $property) - { - $property->setAccessible(true); - - return $property->getValue($this); - } -} diff --git a/src/Support/helpers.php b/src/Support/helpers.php index c0632b6d..ad803264 100644 --- a/src/Support/helpers.php +++ b/src/Support/helpers.php @@ -950,13 +950,17 @@ function file_system(string $mount): \Bow\Storage\Service\DiskFilesystemService /** * Cache help * - * @param string $key - * @param mixed $value - * @param int $ttl + * @param ?string $key + * @param ?mixed $value + * @param ?int $ttl * @return mixed */ function cache(string $key = null, mixed $value = null, int $ttl = null) { + if ($key === null) { + return \Bow\Cache\Cache::getInstance(); + } + if ($key !== null && $value === null) { return \Bow\Cache\Cache::get($key); } @@ -1200,16 +1204,13 @@ function auth(string $guard = null): \Bow\Auth\Guards\GuardContract } } -if (!function_exists('log')) { +if (!function_exists('logger')) { /** * Log error message * - * @param string $level - * @param string $message - * @param array $context * @return Logger */ - function log(): Logger + function logger(): Logger { return app('logger'); } From 2cd447d186bed737d6a414923184f75575c9f7d1 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 19 Sep 2023 17:59:24 +0000 Subject: [PATCH 103/199] FIxes request json payload parsing --- src/Event/Event.php | 3 --- src/Http/Request.php | 7 ++++++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Event/Event.php b/src/Event/Event.php index 99029333..852346e1 100644 --- a/src/Event/Event.php +++ b/src/Event/Event.php @@ -4,9 +4,6 @@ namespace Bow\Event; -use Bow\Session\Session; -use Bow\Container\Action; -use Bow\Support\Collection; use Bow\Event\Contracts\AppEvent; class Event diff --git a/src/Http/Request.php b/src/Http/Request.php index 7e8dd774..c148ef4f 100644 --- a/src/Http/Request.php +++ b/src/Http/Request.php @@ -5,6 +5,7 @@ namespace Bow\Http; use Bow\Auth\Authentication; +use Bow\Http\Exception\BadRequestException; use Bow\Session\Session; use Bow\Support\Collection; use Bow\Support\Str; @@ -52,7 +53,11 @@ private function __construct() $this->id = "req_" . sha1(uniqid() . time()); if ($this->getHeader('content-type') == 'application/json') { - $data = json_decode(file_get_contents("php://input"), true, 1024, JSON_THROW_ON_ERROR); + try { + $data = json_decode(file_get_contents("php://input"), true, 1024, JSON_THROW_ON_ERROR); + } catch (\Throwable $e) { + throw new BadRequestException("Invalid JSON syntax"); + } $this->input = array_merge((array) $data, $_GET); } else { $data = $_POST ?? []; From 43c1d39a4c7531f4433bb7cc3c886d075d68f59f Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Wed, 20 Sep 2023 08:07:19 +0000 Subject: [PATCH 104/199] Fixes get relation result --- src/Database/Barry/Model.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Database/Barry/Model.php b/src/Database/Barry/Model.php index 9ce4c9ea..adebadfc 100644 --- a/src/Database/Barry/Model.php +++ b/src/Database/Barry/Model.php @@ -876,7 +876,11 @@ public function __get(string $name): mixed $attribute_exists = isset($this->attributes[$name]); if (!$attribute_exists && method_exists($this, $name)) { - return $this->$name()->getResults(); + $result = $this->$name(); + if ($result instanceof Relation) { + return $result->getResults(); + } + return $result; } if (!$attribute_exists) { From 4c256d74a88008f7d9f6cd93e35a9c17f54322e8 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Wed, 20 Sep 2023 08:11:10 +0000 Subject: [PATCH 105/199] Fixs Trait Bow\Queue\Traits\SerializesModels not found --- src/Queue/ProducerService.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Queue/ProducerService.php b/src/Queue/ProducerService.php index bcb7977b..bdb7e951 100644 --- a/src/Queue/ProducerService.php +++ b/src/Queue/ProducerService.php @@ -4,12 +4,8 @@ namespace Bow\Queue; -use Bow\Queue\Traits\SerializesModels; - abstract class ProducerService { - use SerializesModels; - /** * Define the delay * From 15b4954d6d87808c5f38125f72e967c7e26161d2 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Thu, 21 Sep 2023 00:05:36 +0000 Subject: [PATCH 106/199] Fixes jwt token verification --- src/Auth/Guards/JwtGuard.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Auth/Guards/JwtGuard.php b/src/Auth/Guards/JwtGuard.php index 6f962924..5b72b2ef 100644 --- a/src/Auth/Guards/JwtGuard.php +++ b/src/Auth/Guards/JwtGuard.php @@ -80,6 +80,10 @@ public function attempts(array $credentials): bool */ public function check(): bool { + $policier = $this->getPolicier(); + + $this->token = $policier->getParsedToken(); + if (is_null($this->token)) { return false; } From 8c858aa40f0b5242c5c54f6970bb0d7b72f9ee77 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 22 Sep 2023 06:29:00 +0000 Subject: [PATCH 107/199] Fixes data type issues --- src/Cache/Cache.php | 11 +- src/Console/Command.php | 5 + src/Console/Command/WorkerCommand.php | 27 ++++- src/Console/Console.php | 39 ++++++- src/Database/Database.php | 18 ++- src/Event/Event.php | 7 ++ .../Exception/ServiceNotFoundException.php | 9 +- src/Storage/Storage.php | 25 ++++- src/Support/Serializes.php | 103 ++++++++++++++++++ 9 files changed, 221 insertions(+), 23 deletions(-) create mode 100644 src/Support/Serializes.php diff --git a/src/Cache/Cache.php b/src/Cache/Cache.php index 17b7a98c..9e51d06f 100644 --- a/src/Cache/Cache.php +++ b/src/Cache/Cache.php @@ -115,13 +115,22 @@ public static function addAdapters(array $adapters): void * @param array $arguments * @return mixed * @throws BadMethodCallException + * @throws ErrorException */ public static function __callStatic(string $name, array $arguments) { + if (is_null(static::$instance)) { + throw new ErrorException( + "Unable to get cache instance before configuration" + ); + } + if (method_exists(static::$instance, $name)) { return call_user_func_array([static::$instance, $name], $arguments); } - throw new BadMethodCallException("The $name method does not exist"); + throw new BadMethodCallException( + "The $name method does not exist" + ); } } diff --git a/src/Console/Command.php b/src/Console/Command.php index 8e631f96..586e44c2 100644 --- a/src/Console/Command.php +++ b/src/Console/Command.php @@ -44,6 +44,9 @@ class Command extends AbstractCommand "server" => \Bow\Console\Command\ServerCommand::class, "worker" => \Bow\Console\Command\WorkerCommand::class, ], + "flush" => [ + "worker" => \Bow\Console\Command\WorkerCommand::class, + ], ]; /** @@ -79,5 +82,7 @@ public function call(string $command, string $action, ...$rest): mixed if (method_exists($instance, $method)) { return call_user_func_array([$instance, $method], $rest); } + + return null; } } diff --git a/src/Console/Command/WorkerCommand.php b/src/Console/Command/WorkerCommand.php index d0c24066..a47491c6 100644 --- a/src/Console/Command/WorkerCommand.php +++ b/src/Console/Command/WorkerCommand.php @@ -16,8 +16,11 @@ class WorkerCommand extends AbstractCommand */ public function run(?string $connection = null): void { - $retry = (int) $this->arg->getParameter('--retry', 3); + $tries = (int) $this->arg->getParameter('--tries', 3); $default = $this->arg->getParameter('--queue', "default"); + $memory = $this->arg->getParameter('--memory', 126); + $timout = $this->arg->getParameter('--timout', 60); + $sleep = $this->arg->getParameter('--sleep', 60); $queue = app("queue"); @@ -27,7 +30,27 @@ public function run(?string $connection = null): void $worker = $this->getWorderService(); $worker->setConnection($queue->getAdapter()); - $worker->run($default, $retry); + $worker->run($default, $tries, $sleep, $timout, $memory); + } + + /** + * Flush the queue + * + * @param ?string $connection + * @return void + */ + public function flush(?string $connection = null) + { + $connection_queue = $this->arg->getParameter('--queue'); + + $queue = app("queue"); + + if (!is_null($connection)) { + $queue->setConnection($connection); + } + + $adapter = $queue->getAdapter(); + $adapter->flush($connection_queue); } /** diff --git a/src/Console/Console.php b/src/Console/Console.php index 2d2901e0..43cea024 100644 --- a/src/Console/Console.php +++ b/src/Console/Console.php @@ -79,7 +79,7 @@ class Console * @var array */ private const COMMAND = [ - 'add', 'migration', 'migrate', 'run', 'generate', 'gen', 'seed', 'help', 'launch', 'clear' + 'add', 'migration', 'migrate', 'run', 'generate', 'gen', 'seed', 'help', 'launch', 'clear', 'flush' ]; /** @@ -144,7 +144,7 @@ public function bind(Loader $kernel): void /** * Launch Bow task runner * - * @return void + * @return mixed * @throws */ public function run(): mixed @@ -436,6 +436,23 @@ private function clear(): void $this->command->call('clear', "make", $action); } + /** + * Flush the connections + * + * @return void + * @throws \ErrorException + */ + private function flush(): void + { + $action = $this->arg->getAction(); + + if (!in_array($action, ['worker'])) { + $this->throwFailsCommand('This action is not exists', 'help flush'); + } + + $this->command->call('flush', $action); + } + /** * Display global help or helper command. * @@ -458,9 +475,9 @@ private function help(?string $command = null): int \033[0;32mGENERATE\033[00m create a new app key and resources \033[0;33mgenerate:resource\033[00m Create new REST controller - \033[0;33mgenerate:session\033[00m For generate session table - \033[0;33mgenerate:cache\033[00m For generate cache table + \033[0;33mgenerate:table\033[00m For generate the preset table for session, cache, queue \033[0;33mgenerate:key\033[00m Create new app key + \033[0;33mflush:worker\033[00m Flush all queues \033[0;32mADD\033[00m Create a user class \033[0;33madd:middleware\033[00m Create new middleware @@ -545,7 +562,7 @@ private function help(?string $command = null): int --model=[model_name] Define the usable model \033[0;33m$\033[00m php \033[0;34mbow\033[00m generate:resource name [option] For create a new REST controller - \033[0;33m$\033[00m php \033[0;34mbow\033[00m generate:session For generate session table + \033[0;33m$\033[00m php \033[0;34mbow\033[00m generate:table For generate the table for session, cache, queue \033[0;33m$\033[00m php \033[0;34mbow\033[00m generate:key For generate a new APP KEY \033[0;33m$\033[00m php \033[0;34mbow\033[00m generate help For display this @@ -597,13 +614,23 @@ private function help(?string $command = null): int \033[0;33m$\033[00m php \033[0;34mbow\033[00m seed:all\033[00m Make seeding for all \033[0;33m$\033[00m php \033[0;34mbow\033[00m seed:table\033[00m table_name Make seeding for one table +U; + break; + + case 'flush': + echo <<throwFailsCommand("Please make php bow help for show whole docs !"); exit(1); - break; } exit(0); diff --git a/src/Database/Database.php b/src/Database/Database.php index 77cb0c16..1198b559 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -12,6 +12,7 @@ use Bow\Database\Connection\Adapter\MysqlAdapter; use Bow\Database\Connection\Adapter\SqliteAdapter; use Bow\Database\Connection\Adapter\PostgreSQLAdapter; +use ErrorException; class Database { @@ -76,9 +77,8 @@ public static function getInstance(): Database /** * Connection, starts the connection on the DB * - * @param null $name - * @return null|Database - * + * @param ?string $name + * @return ?Database * @throws ConnectionException */ public static function connection(?string $name = null): ?Database @@ -398,7 +398,7 @@ public static function transaction(callable $callback): mixed } catch (DatabaseException $e) { static::rollback(); - throw $e; + throw $e; } } @@ -418,9 +418,9 @@ private static function verifyConnection(): void * Retrieves the identifier of the last record. * * @param ?string $name - * @return int|string + * @return int|string|PDO */ - public static function lastInsertId(?string $name = null): int|string + public static function lastInsertId(?string $name = null): int|string|PDO { static::verifyConnection(); @@ -488,6 +488,12 @@ public static function setPdo(PDO $pdo) */ public function __call(string $method, array $arguments) { + if (is_null(static::$instance)) { + throw new ErrorException( + "Unable to get database instance before configuration" + ); + } + if (method_exists(static::$instance, $method)) { return call_user_func_array( [static::$instance, $method], diff --git a/src/Event/Event.php b/src/Event/Event.php index 852346e1..32430cf5 100644 --- a/src/Event/Event.php +++ b/src/Event/Event.php @@ -5,6 +5,7 @@ namespace Bow\Event; use Bow\Event\Contracts\AppEvent; +use ErrorException; class Event { @@ -140,6 +141,12 @@ public static function bound(string $event): bool */ public function __call(string $name, array $arguments) { + if (is_null(static::$instance)) { + throw new ErrorException( + "Unable to get event instance before configuration" + ); + } + if (method_exists(static::$instance, $name)) { return call_user_func_array([static::$instance, $name], $arguments); } diff --git a/src/Storage/Exception/ServiceNotFoundException.php b/src/Storage/Exception/ServiceNotFoundException.php index dfedb2f0..f8ecc7b9 100644 --- a/src/Storage/Exception/ServiceNotFoundException.php +++ b/src/Storage/Exception/ServiceNotFoundException.php @@ -19,18 +19,19 @@ class ServiceNotFoundException extends ErrorException * Set the service name * * @param string $service_name - * - * @return void + * @return ServiceNotFoundException */ - public function setService($service_name) + public function setService(string $service_name): ServiceNotFoundException { $this->service_name = $service_name; + + return $this; } /** * Get the service name * - * @return void + * @return string */ public function getService() { diff --git a/src/Storage/Storage.php b/src/Storage/Storage.php index cd3923b4..5d4b4e3d 100644 --- a/src/Storage/Storage.php +++ b/src/Storage/Storage.php @@ -5,6 +5,7 @@ namespace Bow\Storage; use BadMethodCallException; +use Bow\Storage\Contracts\FilesystemInterface; use InvalidArgumentException; use Bow\Storage\Exception\DiskNotFoundException; use Bow\Storage\Exception\ServiceConfigurationNotFoundException; @@ -12,6 +13,8 @@ use Bow\Storage\Service\DiskFilesystemService; use Bow\Storage\Service\FTPService; use Bow\Storage\Service\S3Service; +use ErrorException; +use Tintin\Filesystem; class Storage { @@ -72,6 +75,8 @@ public static function disk(?string $disk = null) * * @param string $service * @return FTPService|S3Service + * @throws ServiceConfigurationNotFoundException + * @throws ServiceNotFoundException */ public static function service(string $service) { @@ -90,14 +95,14 @@ public static function service(string $service) throw (new ServiceNotFoundException(sprintf( '"%s" driver is not support.', $driver - )))->setService($driver); + )))->setService($service); } if (!array_key_exists($driver, self::$available_services_driviers)) { throw (new ServiceNotFoundException(sprintf( '"%s" is not registered as a service.', $driver - )))->setService($driver); + )))->setService($service); } $service_class = static::$available_services_driviers[$driver]; @@ -126,10 +131,10 @@ public static function pushService(array $drivers) * Configure Storage * * @param array $config - * @return MountFilesystem + * @return FilesystemInterface * @throws */ - public static function configure(array $config) + public static function configure(array $config): FilesystemInterface { static::$config = $config; @@ -149,6 +154,12 @@ public static function configure(array $config) */ public function __call($name, array $arguments) { + if (is_null(static::$disk)) { + throw new ErrorException( + "Unable to get storage instance before configuration" + ); + } + if (method_exists(static::$disk, $name)) { return call_user_func_array([static::$disk, $name], $arguments); } @@ -165,6 +176,12 @@ public function __call($name, array $arguments) */ public static function __callStatic($name, array $arguments) { + if (is_null(static::$disk)) { + throw new ErrorException( + "Unable to get storage instance before configuration" + ); + } + if (method_exists(static::$disk, $name)) { return call_user_func_array([static::$disk, $name], $arguments); } diff --git a/src/Support/Serializes.php b/src/Support/Serializes.php new file mode 100644 index 00000000..b9e5506a --- /dev/null +++ b/src/Support/Serializes.php @@ -0,0 +1,103 @@ +getProperties(); + + $class = get_class($this); + + foreach ($properties as $property) { + if ($property->isStatic()) { + continue; + } + + $property->setAccessible(true); + if (!$property->isInitialized($this)) { + continue; + } + + $value = $this->getPropertyValue($property); + if ($property->hasDefaultValue() && $value === $property->getDefaultValue()) { + continue; + } + + $name = $property->getName(); + + if ($property->isPrivate()) { + $name = "\0{$class}\0{$name}"; + } elseif ($property->isProtected()) { + $name = "\0*\0{$name}"; + } + + $values[$name] = $value; + } + + return $values; + } + + /** + * Restore the model after serialization. + * + * @param array $values + * @return void + */ + public function __unserialize(array $values) + { + $properties = (new ReflectionClass($this))->getProperties(); + + $class = get_class($this); + + foreach ($properties as $property) { + if ($property->isStatic()) { + continue; + } + + $name = $property->getName(); + + if ($property->isPrivate()) { + $name = "\0{$class}\0{$name}"; + } elseif ($property->isProtected()) { + $name = "\0*\0{$name}"; + } + + if (!array_key_exists($name, $values)) { + continue; + } + + $property->setAccessible(true); + + $property->setValue( + $this, + $values[$name] + ); + } + } + + /** + * Get the property value for the given property. + * + * @param \ReflectionProperty $property + * @return mixed + */ + protected function getPropertyValue( + ReflectionProperty $property + ) { + $property->setAccessible(true); + + return $property->getValue($this); + } +} From b44a993b9cbfc4244c4f6afee38c29ca711fab01 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 22 Sep 2023 06:33:51 +0000 Subject: [PATCH 108/199] Fixes findAndDelete issue --- src/Database/Barry/Model.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Database/Barry/Model.php b/src/Database/Barry/Model.php index adebadfc..0a95aec3 100644 --- a/src/Database/Barry/Model.php +++ b/src/Database/Barry/Model.php @@ -263,6 +263,15 @@ public static function findAndDelete( ): Model { $model = static::find($id, $select); + if (is_null($model)) { + return $model; + } + + if ($model instanceof Collection) { + $model->dropAll(); + return $model; + } + $model->delete(); return $model; From 533de2f557dc40bef8e6aab76e5c4ef8b4dba37e Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 22 Sep 2023 06:34:19 +0000 Subject: [PATCH 109/199] Fixes jwt token parser issue --- src/Auth/Auth.php | 9 ++++++++- src/Auth/Guards/JwtGuard.php | 4 +++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Auth/Auth.php b/src/Auth/Auth.php index d8323df9..190e090a 100644 --- a/src/Auth/Auth.php +++ b/src/Auth/Auth.php @@ -8,6 +8,7 @@ use Bow\Auth\Guards\SessionGuard; use Bow\Auth\Guards\GuardContract; use Bow\Auth\Exception\AuthenticationException; +use ErrorException; class Auth { @@ -100,10 +101,16 @@ public static function guard(?string $guard = null): GuardContract * * @param string $method * @param array $params - * @return GuardContract + * @return ?GuardContract */ public static function __callStatic(string $method, array $params) { + if (is_null(static::$instance)) { + throw new ErrorException( + "Unable to get auth instance before configuration" + ); + } + if (method_exists(static::$instance, $method)) { return call_user_func_array([static::$instance, $method], $params); } diff --git a/src/Auth/Guards/JwtGuard.php b/src/Auth/Guards/JwtGuard.php index 5b72b2ef..658955b5 100644 --- a/src/Auth/Guards/JwtGuard.php +++ b/src/Auth/Guards/JwtGuard.php @@ -82,7 +82,9 @@ public function check(): bool { $policier = $this->getPolicier(); - $this->token = $policier->getParsedToken(); + if (is_null($this->token)) { + $this->token = $policier->getParsedToken(); + } if (is_null($this->token)) { return false; From 1eda3a5a6ae6036ff4d1c34fd6b6d5ca87430ccf Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 22 Sep 2023 06:35:26 +0000 Subject: [PATCH 110/199] Refactoring the queue worker pattern --- src/Queue/Adapters/BeanstalkdAdapter.php | 117 +++++++++++++--- src/Queue/Adapters/QueueAdapter.php | 161 +++++++++++++++++++++-- src/Queue/Connection.php | 9 +- src/Queue/ProducerService.php | 45 ++++++- src/Queue/WorkerService.php | 69 +++++++++- 5 files changed, 361 insertions(+), 40 deletions(-) diff --git a/src/Queue/Adapters/BeanstalkdAdapter.php b/src/Queue/Adapters/BeanstalkdAdapter.php index 300909e1..1f5e9751 100644 --- a/src/Queue/Adapters/BeanstalkdAdapter.php +++ b/src/Queue/Adapters/BeanstalkdAdapter.php @@ -24,12 +24,21 @@ class BeanstalkdAdapter extends QueueAdapter * * @var string */ - private string $default = "default"; + private string $queue = "default"; /** + * The number of working attempts + * + * @var int + */ + private int $tries; + + /** + * Define the sleep time + * * @var int */ - private int $retry; + private int $sleep = 5; /** * Configure Beanstalkd driver @@ -43,7 +52,11 @@ public function configure(array $queue): BeanstalkdAdapter throw new RuntimeException("Please install the pda/pheanstalk package"); } - $this->pheanstalk = Pheanstalk::create($queue["hostname"], $queue["port"], $queue["timeout"]); + $this->pheanstalk = Pheanstalk::create( + $queue["hostname"], + $queue["port"], + $queue["timeout"] + ); return $this; } @@ -56,18 +69,29 @@ public function configure(array $queue): BeanstalkdAdapter */ public function setWatch(string $name): void { - $this->default = $name; + $this->queue = $name; + } + + /** + * Set job tries + * + * @param int $tries + * @return void + */ + public function setTries(int $tries): void + { + $this->tries = $tries; } /** * Get connexion * - * @param int $retry - * @return Pheanstalk + * @param int $sleep + * @return void */ - public function setRetry(int $retry): void + public function setSleep(int $sleep): void { - $this->retry = $retry; + $this->sleep = $sleep; } /** @@ -78,7 +102,7 @@ public function setRetry(int $retry): void */ public function getQueue(?string $queue = null): string { - return $queue ?: $this->default; + return $queue ?: $this->queue; } /** @@ -102,9 +126,23 @@ public function size(?string $queue = null): int */ public function push(ProducerService $producer): void { + $queues = (array) cache("beanstalkd:queues"); + + if (!in_array($producer->getQueue(), $queues)) { + $queues[] = $producer->getQueue(); + cache("beanstalkd:queues", $queues); + } + $queues = (array) cache("beanstalkd:queues"); + dump($queues); + $this->pheanstalk ->useTube($producer->getQueue()) - ->put(serialize($producer), $producer->getDelay(), $producer->getRetry()); + ->put( + $this->serializeProducer($producer), + $this->getPriority($producer->getPriority()), + $producer->getDelay(), + $producer->getRetry() + ); } /** @@ -115,34 +153,79 @@ public function push(ProducerService $producer): void */ public function run(string $queue = null): void { - // we want jobs from 'testtube' only. + // we want jobs from define queue only. $queue = $this->getQueue($queue); $this->pheanstalk->watch($queue); // This hangs until a Job is produced. - $job = $this->pheanstalk->reserveWithTimeout(50); + $job = $this->pheanstalk->reserve(); if (is_null($job)) { + sleep($this->sleep ?? 5); return; } try { $payload = $job->getData(); - /**@var ProducerService */ - $producer = unserialize($payload); - $delay = $producer->getDelay(); + $producer = $this->unserializeProducer($payload); + dump($producer); call_user_func([$producer, "process"]); + $this->sleep(2); $this->pheanstalk->touch($job); + $this->sleep(2); $this->pheanstalk->delete($job); - } catch (\Exception $e) { + } catch (\Throwable $e) { error_log($e->getMessage()); app('logger')->error($e->getMessage(), $e->getTrace()); cache("failed:job:" . $job->getId(), $job->getData()); if ($producer->jobShouldBeDelete()) { $this->pheanstalk->delete($job); } else { - $this->pheanstalk->release($job, PheanstalkInterface::DEFAULT_PRIORITY, $delay); + $this->pheanstalk->release($job, $this->getPriority($producer->getPriority()), $producer->getDelay()); } + $this->sleep(1); + } + } + + /** + * Flush the queue + * + * @return void + */ + public function flush(?string $queue = null): void + { + $queues = (array) $queue; + + if (count($queues) == 0) { + $queues = cache("beanstalkd:queues"); + } + + foreach ($queues as $queue) { + $this->pheanstalk->useTube($queue); + + while ($job = $this->pheanstalk->reserve()) { + $this->pheanstalk->delete($job); + } + } + } + + /** + * Get the priority + * + * @param int $priority + * @return int + */ + public function getPriority(int $priority): int + { + switch ($priority) { + case $priority > 2: + return 4294967295; + case 1: + return PheanstalkInterface::DEFAULT_PRIORITY; + case 0: + return 0; + default: + return PheanstalkInterface::DEFAULT_PRIORITY; } } } diff --git a/src/Queue/Adapters/QueueAdapter.php b/src/Queue/Adapters/QueueAdapter.php index 57943472..5e203c78 100644 --- a/src/Queue/Adapters/QueueAdapter.php +++ b/src/Queue/Adapters/QueueAdapter.php @@ -8,14 +8,40 @@ abstract class QueueAdapter { + const EXIT_SUCCESS = 0; + const EXIT_ERROR = 1; + const EXIT_MEMORY_LIMIT = 12; + + /** + * Define the start time + * + * @var int + */ + private int $start_time; + + /** + * Determine if the worker should quit + * + * @var bool + */ + protected bool $should_quit = false; + + /** + * Determine if the worker is paused + * + * @var bool + */ + protected bool $paused = false; + /** * Create producer serialization * * @param ProducerService $producer * @return string */ - public function serializeProducer(ProducerService $producer): string - { + public function serializeProducer( + ProducerService $producer + ): string { return serialize($producer); } @@ -25,11 +51,115 @@ public function serializeProducer(ProducerService $producer): string * @param string $producer * @return ProducerService */ - public function unserializeProducer(string $producer): ProducerService - { + public function unserializeProducer( + string $producer + ): ProducerService { return unserialize($producer); } + /** + * Sleep the process + * + * @param int $seconds + * @return void + */ + public function sleep(int $seconds): void + { + if ($seconds < 1) { + usleep($seconds * 1000000); + } else { + sleep($seconds); + } + } + + /** + * Laund the worker + * + * @param integer $timeout + * @param integer $memory + * @return void + */ + public function work(int $timeout, int $memory): void + { + [$this->start_time, $jobs_processed] = [hrtime(true) / 1e9, 0]; + + if ($supportsAsyncSignals = $this->supportsAsyncSignals()) { + $this->listenForSignals(); + } + + while (true) { + $this->run(); + + if ($this->timeoutReached($timeout)) { + $this->kill(static::EXIT_ERROR); + } elseif ($this->memoryExceeded($memory)) { + $this->kill(static::EXIT_MEMORY_LIMIT); + } + } + } + + /** + * Kill the process. + * + * @param int $status + * @return never + */ + public function kill($status = 0) + { + if (extension_loaded('posix')) { + posix_kill(getmypid(), SIGKILL); + } + + exit($status); + } + + /** + * Determine if the timeout is reached + * + * @param int $timeout + * @return boolean + */ + protected function timeoutReached(int $timeout): bool + { + return (time() - $this->start_time) >= $timeout; + } + + /** + * Determine if the memory is exceeded + * + * @param int $memory_timit + * @return boolean + */ + private function memoryExceeded(int $memory_timit): bool + { + return (memory_get_usage() / 1024 / 1024) >= $memory_timit; + } + + /** + * Enable async signals for the process. + * + * @return void + */ + protected function listenForSignals() + { + pcntl_async_signals(true); + + pcntl_signal(SIGQUIT, fn () => error_log("bow worker exiting...")); + pcntl_signal(SIGTERM, fn () => error_log("bow worker exit...")); + pcntl_signal(SIGUSR2, fn () => error_log("bow worker restarting...")); + pcntl_signal(SIGCONT, fn () => error_log("bow worker continue...")); + } + + /** + * Determine if "async" signals are supported. + * + * @return bool + */ + protected function supportsAsyncSignals() + { + return extension_loaded('pcntl'); + } + /** * Make adapter configuration * @@ -46,11 +176,18 @@ abstract public function configure(array $config): QueueAdapter; abstract public function setWatch(string $queue): void; /** - * Set the retry value + * Set the tries value * - * @param int $retry + * @param int $tries */ - abstract public function setRetry(int $retry): void; + abstract public function setTries(int $tries): void; + + /** + * Set the sleep value + * + * @param int $sleep + */ + abstract public function setSleep(int $sleep): void; /** * Push new producer @@ -73,7 +210,7 @@ abstract public function size(string $queue): int; * @param ?string $queue * @return string */ - abstract public function getQueue(string $queue = null): string; + abstract public function getQueue(?string $queue = null): string; /** * Start the worker server @@ -81,4 +218,12 @@ abstract public function getQueue(string $queue = null): string; * @param ?string $queue */ abstract public function run(?string $queue = null): void; + + /** + * Flush the queue + * + * @param ?string $queue + * @return void + */ + abstract public function flush(?string $queue = null): void; } diff --git a/src/Queue/Connection.php b/src/Queue/Connection.php index d7255d76..979d4410 100644 --- a/src/Queue/Connection.php +++ b/src/Queue/Connection.php @@ -6,6 +6,7 @@ use Bow\Queue\Adapters\QueueAdapter; use Bow\Queue\Adapters\BeanstalkdAdapter; +use Bow\Queue\Adapters\SQSAdapter; use ErrorException; class Connection @@ -31,6 +32,7 @@ class Connection */ private static array $connections = [ "beanstalkd" => BeanstalkdAdapter::class, + "sqs" => SQSAdapter::class, ]; /** @@ -67,11 +69,13 @@ public static function pushConnection(string $name, string $classname): bool * Set connection * * @param string $connection - * @return void + * @return Connection */ - public function setConnection(string $connection): void + public function setConnection(string $connection): Connection { $this->connection = $connection; + + return $this; } /** @@ -84,6 +88,7 @@ public function getAdapter(): QueueAdapter $driver = $this->connection ?: $this->config["default"]; $connection = $this->config["connections"][$driver]; + $queue = new static::$connections[$driver](); return $queue->configure($connection); diff --git a/src/Queue/ProducerService.php b/src/Queue/ProducerService.php index bdb7e951..18ff747d 100644 --- a/src/Queue/ProducerService.php +++ b/src/Queue/ProducerService.php @@ -4,14 +4,11 @@ namespace Bow\Queue; +use Bow\Support\Serializes; + abstract class ProducerService { - /** - * Define the delay - * - * @var int - */ - protected int $delay = 30; + use Serializes; /** * Define the queue @@ -20,6 +17,13 @@ abstract class ProducerService */ protected string $queue = "default"; + /** + * Define the delay + * + * @var int + */ + protected int $delay = 30; + /** * Define the time of retry * @@ -39,7 +43,24 @@ abstract class ProducerService * * @var bool */ - private bool $delete = false; + protected bool $delete = false; + + /** + * Define the job id + * + * @return integer + */ + protected string $id; + + /** + * ProducerService constructor + * + * @return mixed + */ + public function __construct() + { + $this->id = sha1(uniqid(str_shuffle("abcdefghijklmnopqrstuvwxyz0123456789"), true)); + } /** * Get the producer priority @@ -51,6 +72,16 @@ final public function getPriority(): int return $this->priority; } + /** + * Get the producer id + * + * @return string + */ + public function getId(): string + { + return $this->id; + } + /** * Get the producer retry * diff --git a/src/Queue/WorkerService.php b/src/Queue/WorkerService.php index 6d6f7429..4ab91146 100644 --- a/src/Queue/WorkerService.php +++ b/src/Queue/WorkerService.php @@ -30,16 +30,73 @@ public function setConnection(QueueAdapter $connection): void * Start the consumer * * @param string $queue - * @param integer $retry + * @param int $tries + * @param int $sleep + * @param int $timeout + * @param int $memory * @return void */ - public function run(string $queue = "default", int $retry = 60): void - { + public function run( + string $queue = "default", + int $tries = 3, + int $sleep = 5, + int $timeout = 60, + int $memory = 128 + ): void { $this->connection->setWatch($queue); - $this->connection->setRetry($retry); + $this->connection->setTries($tries); + $this->connection->setSleep($sleep); + $this->connection->work($timeout, $memory); + } - while (true) { - $this->connection->run(); + /** + * Determine if the worker should restart + * + * @param int $timeout + * @param int $memory + * @return boolean + */ + protected function shouldRestart(int $timeout, int $memory): bool + { + if ( + $this->timeoutReached($timeout) + || $this->memoryExceeded($memory) + ) { + return true; } + + return false; + } + + /** + * Determine if the timeout is reached + * + * @param int $timeout + * @return boolean + */ + protected function timeoutReached(int $timeout): bool + { + return (time() - $this->start_time) >= $timeout; + } + + /** + * Determine if the memory is exceeded + * + * @param int $memory_timit + * @return boolean + */ + protected function memoryExceeded(int $memory_timit): bool + { + return (memory_get_usage() / 1024 / 1024) >= $memory_timit; + } + + /** + * Stop the worker + * + * @return void + */ + protected function stop(): void + { + die; } } From e593dada22fc7da4056f5d00fb5e7a385c19dfee Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 22 Sep 2023 06:35:44 +0000 Subject: [PATCH 111/199] Update unity tests --- tests/Auth/AuthenticationTest.php | 1 + tests/Config/stubs/queue.php | 15 +++-- tests/Queue/QueueTest.php | 81 ++++++++++++++++++++---- tests/Queue/Stubs/BasicProducerStubs.php | 10 ++- tests/Queue/Stubs/MixedProducerStub.php | 11 ++-- tests/Queue/Stubs/ModelProducerStub.php | 14 ++-- tests/Queue/Stubs/ServiceStub.php | 10 ++- 7 files changed, 105 insertions(+), 37 deletions(-) diff --git a/tests/Auth/AuthenticationTest.php b/tests/Auth/AuthenticationTest.php index f0e3cb5d..9a957317 100644 --- a/tests/Auth/AuthenticationTest.php +++ b/tests/Auth/AuthenticationTest.php @@ -138,6 +138,7 @@ public function test_attempt_login_with_jwt_provider_fail() public function test_attempt_login_with_session_provider() { $this->expectException(AuthenticationException::class); + $auth = Auth::guard('web'); $auth->attempts([ "username" => "papac", diff --git a/tests/Config/stubs/queue.php b/tests/Config/stubs/queue.php index 241290e1..88af732b 100644 --- a/tests/Config/stubs/queue.php +++ b/tests/Config/stubs/queue.php @@ -21,8 +21,8 @@ * The beanstalkd connexion */ "beanstalkd" => [ - "hostname" => "127.0.0.0", - "port" => 11301, + "hostname" => "127.0.0.1", + "port" => 11300, "timeout" => 10, ], @@ -30,9 +30,14 @@ * The sqs connexion */ "sqs" => [ - "hostname" => "127.0.0.0", - "port" => 11300, - "timeout" => 10, + 'url' => app_env('SQS_URL', 'https://sqs.ap-south-1.amazonaws.com/242848748621/messaging'), + ], + + /** + * The sqs connexion + */ + "database" => [ + 'table' => "queue_jobs", ] ] ]; diff --git a/tests/Queue/QueueTest.php b/tests/Queue/QueueTest.php index 53e16e64..de73ad51 100644 --- a/tests/Queue/QueueTest.php +++ b/tests/Queue/QueueTest.php @@ -2,8 +2,11 @@ namespace Bow\Tests\Queue; +use Bow\Cache\Adapter\RedisAdapter; +use Bow\Cache\Cache; use Bow\Database\Database; use Bow\Queue\Adapters\BeanstalkdAdapter; +use Bow\Queue\Adapters\SQSAdapter; use Bow\Tests\Config\TestingConfiguration; use Bow\Tests\Queue\Stubs\PetModelStub; use Bow\Queue\Connection as QueueConnection; @@ -17,42 +20,94 @@ class QueueTest extends \PHPUnit\Framework\TestCase public static function setUpBeforeClass(): void { $config = TestingConfiguration::getConfig(); - @unlink(TESTING_RESOURCE_BASE_DIRECTORY . '/producer.txt'); static::$connection = new QueueConnection($config["queue"]); + Database::configure($config["database"]); + Cache::configure($config["cache"]); Database::connection('mysql'); Database::statement('drop table if exists pets'); Database::statement('create table pets (id int primary key auto_increment, name varchar(255))'); } - public function test_instance_of_adapter() + /** + * @dataProvider getConnection + * + * @param string $connection + * @return void + */ + public function test_instance_of_adapter($connection) { - $this->assertInstanceOf(BeanstalkdAdapter::class, static::$connection->getAdapter()); + $adapter = static::$connection->setConnection($connection)->getAdapter(); + + if ($connection == "beanstalkd") { + $this->assertInstanceOf(BeanstalkdAdapter::class, $adapter); + } elseif ($connection == "sqs") { + $this->assertInstanceOf(SQSAdapter::class, $adapter); + } elseif ($connection == "redis") { + $this->assertInstanceOf(RedisAdapter::class, $adapter); + } elseif ($connection == "database") { + $this->assertInstanceOf(DatabaseAdapter::class, $adapter); + } } - public function test_push_service_adapter() + /** + * @dataProvider getConnection + * + * @param string $connection + * @return void + */ + public function test_push_service_adapter($connection) { - $adapter = static::$connection->getAdapter(); - $adapter->push(new BasicProducerStubs("running")); + $adapter = static::$connection->setConnection($connection)->getAdapter(); + $filename = TESTING_RESOURCE_BASE_DIRECTORY . "/{$connection}_producer.txt"; + + $adapter->push(new BasicProducerStubs($connection)); $adapter->run(); - $this->assertTrue(file_exists(TESTING_RESOURCE_BASE_DIRECTORY . '/producer.txt')); - $this->assertEquals(file_get_contents(TESTING_RESOURCE_BASE_DIRECTORY . '/producer.txt'), 'running'); + $this->assertTrue(file_exists($filename)); + $this->assertEquals(file_get_contents($filename), BasicProducerStubs::class); + + @unlink($filename); } - public function test_push_service_adapter_with_model() + /** + * @dataProvider getConnection + * @param string $connection + * @return void + */ + public function test_push_service_adapter_with_model($connection) { - $adapter = static::$connection->getAdapter(); + $adapter = static::$connection->setConnection($connection)->getAdapter(); $pet = new PetModelStub(["name" => "Filou"]); - $producer = new ModelProducerStub($pet); + $producer = new ModelProducerStub($pet, $connection); $adapter->push($producer); $adapter->run(); - $this->assertTrue(file_exists(TESTING_RESOURCE_BASE_DIRECTORY . '/producer.txt')); - $this->assertEquals(file_get_contents(TESTING_RESOURCE_BASE_DIRECTORY . '/producer.txt'), 'running'); + $this->assertTrue(file_exists(TESTING_RESOURCE_BASE_DIRECTORY . "/{$connection}_queue_pet_model_stub.txt")); + $content = file_get_contents(TESTING_RESOURCE_BASE_DIRECTORY . "/{$connection}_queue_pet_model_stub.txt"); + $data = json_decode($content); + $this->assertEquals($data->name, "Filou"); $pet = PetModelStub::first(); $this->assertNotNull($pet); + + @unlink(TESTING_RESOURCE_BASE_DIRECTORY . "/{$connection}_producer.txt"); + } + + /** + * Get the connection data + * + * @return array + */ + public function getConnection(): array + { + return [ + ["beanstalkd"], + // ["sqs"], + // ["redis"], + // ["rabbitmq"], + // ["database"] + ]; } } diff --git a/tests/Queue/Stubs/BasicProducerStubs.php b/tests/Queue/Stubs/BasicProducerStubs.php index 911bf889..1f720027 100644 --- a/tests/Queue/Stubs/BasicProducerStubs.php +++ b/tests/Queue/Stubs/BasicProducerStubs.php @@ -6,15 +6,13 @@ class BasicProducerStubs extends ProducerService { - private ?string $name = null; - - public function __construct(string $name) - { - $this->name = $name; + public function __construct( + private string $connection + ) { } public function process(): void { - file_put_contents(TESTING_RESOURCE_BASE_DIRECTORY . '/producer.txt', $this->name); + file_put_contents(TESTING_RESOURCE_BASE_DIRECTORY . "/{$this->connection}_producer.txt", BasicProducerStubs::class); } } diff --git a/tests/Queue/Stubs/MixedProducerStub.php b/tests/Queue/Stubs/MixedProducerStub.php index eb8d85f8..ed17d1da 100644 --- a/tests/Queue/Stubs/MixedProducerStub.php +++ b/tests/Queue/Stubs/MixedProducerStub.php @@ -7,15 +7,14 @@ class MixedProducerStub extends ProducerService { - private ServiceStub $service; - - public function __construct(ServiceStub $service) - { - $this->service = $service; + public function __construct( + private ServiceStub $service, + private string $connection + ) { } public function process(): void { - $this->service->fire(); + $this->service->fire($this->connection); } } diff --git a/tests/Queue/Stubs/ModelProducerStub.php b/tests/Queue/Stubs/ModelProducerStub.php index 245fb29d..50ebe323 100644 --- a/tests/Queue/Stubs/ModelProducerStub.php +++ b/tests/Queue/Stubs/ModelProducerStub.php @@ -7,16 +7,20 @@ class ModelProducerStub extends ProducerService { - private PetModelStub $pet; - - public function __construct(PetModelStub $pet) - { + public function __construct( + private PetModelStub $pet, + private string $connection + ) { $this->pet = $pet; + $this->connection = $connection; } public function process(): void { $this->pet->save(); - file_put_contents(TESTING_RESOURCE_BASE_DIRECTORY . '/queue_pet_model_stub.txt', $this->pet->toJson()); + + file_put_contents(TESTING_RESOURCE_BASE_DIRECTORY . "/{$this->connection}_queue_pet_model_stub.txt", $this->pet->toJson()); + + $this->deleteJob(); } } diff --git a/tests/Queue/Stubs/ServiceStub.php b/tests/Queue/Stubs/ServiceStub.php index 173a6b89..e3979647 100644 --- a/tests/Queue/Stubs/ServiceStub.php +++ b/tests/Queue/Stubs/ServiceStub.php @@ -4,8 +4,14 @@ class ServiceStub { - public function fire(): void + /** + * The fire method + * + * @param string $connection + * @return void + */ + public function fire(string $connection): void { - file_put_contents(TESTING_RESOURCE_BASE_DIRECTORY . '/producer_service.txt', ServiceStub::class); + file_put_contents(TESTING_RESOURCE_BASE_DIRECTORY . "/{$connection}_producer_service.txt", ServiceStub::class); } } From 5d3b8939ee477ef607a3ca9cb1844dd71ec77dda Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 22 Sep 2023 06:36:21 +0000 Subject: [PATCH 112/199] Update beanstalkd port --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fa5f5077..10e21a63 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -45,7 +45,7 @@ jobs: - run: docker run -p 1080:1080 -p 1025:1025 -d --name maildev soulteary/maildev - run: docker run -p 6379:6379 -d --name redis redis - run: docker run -p 5432:5432 --name postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=postgres -e POSTGRES_PASSWORD=postgres -d postgis/postgis - - run: docker run -d -p 11301:11300 schickling/beanstalkd + - run: docker run -d -p 11300:11300 schickling/beanstalkd - name: Cache Composer packages id: composer-cache From 4a13a0834022c06cb2a8f8ec49cfa219844cec80 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 22 Sep 2023 06:37:16 +0000 Subject: [PATCH 113/199] Fixes return type --- src/Database/Barry/Model.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Database/Barry/Model.php b/src/Database/Barry/Model.php index 0a95aec3..e26aa4cd 100644 --- a/src/Database/Barry/Model.php +++ b/src/Database/Barry/Model.php @@ -255,12 +255,12 @@ public static function findBy(string $column, mixed $value): Collection * @param mixed $id * @param array $select * - * @return Collection|static|null + * @return Collection|Model|null */ public static function findAndDelete( int | string | array $id, array $select = ['*'] - ): Model { + ): Collection|Model|null { $model = static::find($id, $select); if (is_null($model)) { From 3e6e4cafcb3ae43af5b25582562a1aff1f469c48 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 22 Sep 2023 06:45:25 +0000 Subject: [PATCH 114/199] Fixes return type --- src/Storage/Storage.php | 3 +-- src/Support/helpers.php | 30 ++++++++++++++++++++++-------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/Storage/Storage.php b/src/Storage/Storage.php index 5d4b4e3d..631eb283 100644 --- a/src/Storage/Storage.php +++ b/src/Storage/Storage.php @@ -14,7 +14,6 @@ use Bow\Storage\Service\FTPService; use Bow\Storage\Service\S3Service; use ErrorException; -use Tintin\Filesystem; class Storage { @@ -50,7 +49,7 @@ class Storage * @return DiskFilesystemService * @throws DiskNotFoundException */ - public static function disk(?string $disk = null) + public static function disk(?string $disk = null): DiskFilesystemService { // Use the default disk as fallback if (is_null($disk)) { diff --git a/src/Support/helpers.php b/src/Support/helpers.php index ad803264..5d32a4eb 100644 --- a/src/Support/helpers.php +++ b/src/Support/helpers.php @@ -335,7 +335,14 @@ function create_csrf_token(int $time = null): ?array */ function csrf_token(): string { - $csrf = create_csrf_token(); + $csrf = (array) create_csrf_token(); + + if (count($csrf) == 0) { + throw new HttpException( + "CSRF token is not generated", + 500 + ); + } return $csrf['token']; } @@ -349,7 +356,14 @@ function csrf_token(): string */ function csrf_field(): string { - $csrf = create_csrf_token(); + $csrf = (array) create_csrf_token(); + + if (count($csrf) == 0) { + throw new HttpException( + "CSRF token is not generated", + 500 + ); + } return $csrf['field']; } @@ -928,7 +942,7 @@ function storage_service(string $service) */ function mount(string $mount): \Bow\Storage\Service\DiskFilesystemService { - return Storage::mount($mount); + return Storage::disk($mount); } } @@ -936,13 +950,13 @@ function mount(string $mount): \Bow\Storage\Service\DiskFilesystemService /** * Alias on the mount method * - * @param string $mount + * @param string $disk * @return \Bow\Storage\Service\DiskFilesystemService * @throws \Bow\Storage\Exception\ResourceException */ - function file_system(string $mount): \Bow\Storage\Service\DiskFilesystemService + function file_system(string $disk): \Bow\Storage\Service\DiskFilesystemService { - return mount($mount); + return Storage::disk($disk); } } @@ -974,9 +988,9 @@ function cache(string $key = null, mixed $value = null, int $ttl = null) * Make redirection to back * * @param int $status - * @return Bow\Http\Redirect + * @return Redirect */ - function redirect_back(int $status = 302): \Bow\Http\Redirect + function redirect_back(int $status = 302): Redirect { return redirect()->back($status); } From 38d2a9587e039edbb70e564cff3252a1168f8f91 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 22 Sep 2023 06:49:01 +0000 Subject: [PATCH 115/199] Refactoring --- src/Queue/WorkerService.php | 51 ------------------------------------- 1 file changed, 51 deletions(-) diff --git a/src/Queue/WorkerService.php b/src/Queue/WorkerService.php index 4ab91146..47d070b7 100644 --- a/src/Queue/WorkerService.php +++ b/src/Queue/WorkerService.php @@ -48,55 +48,4 @@ public function run( $this->connection->setSleep($sleep); $this->connection->work($timeout, $memory); } - - /** - * Determine if the worker should restart - * - * @param int $timeout - * @param int $memory - * @return boolean - */ - protected function shouldRestart(int $timeout, int $memory): bool - { - if ( - $this->timeoutReached($timeout) - || $this->memoryExceeded($memory) - ) { - return true; - } - - return false; - } - - /** - * Determine if the timeout is reached - * - * @param int $timeout - * @return boolean - */ - protected function timeoutReached(int $timeout): bool - { - return (time() - $this->start_time) >= $timeout; - } - - /** - * Determine if the memory is exceeded - * - * @param int $memory_timit - * @return boolean - */ - protected function memoryExceeded(int $memory_timit): bool - { - return (memory_get_usage() / 1024 / 1024) >= $memory_timit; - } - - /** - * Stop the worker - * - * @return void - */ - protected function stop(): void - { - die; - } } From d38f7496896c4d3bfec780a27489517bf25f7005 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 22 Sep 2023 07:21:19 +0000 Subject: [PATCH 116/199] Fixes unity tests --- src/Auth/Guards/JwtGuard.php | 6 ++++- src/Queue/Adapters/BeanstalkdAdapter.php | 5 ++-- src/Testing/KernelTesting.php | 2 +- tests/Config/TestingConfiguration.php | 33 ++++++++++++++++++++++++ tests/Queue/QueueTest.php | 14 ++++++++-- tests/Support/HttpClientTest.php | 6 ++--- 6 files changed, 56 insertions(+), 10 deletions(-) diff --git a/src/Auth/Guards/JwtGuard.php b/src/Auth/Guards/JwtGuard.php index 658955b5..4edf0309 100644 --- a/src/Auth/Guards/JwtGuard.php +++ b/src/Auth/Guards/JwtGuard.php @@ -83,7 +83,11 @@ public function check(): bool $policier = $this->getPolicier(); if (is_null($this->token)) { - $this->token = $policier->getParsedToken(); + try { + $this->token = $policier->getParsedToken(); + } catch (\Exception $e) { + return false; + } } if (is_null($this->token)) { diff --git a/src/Queue/Adapters/BeanstalkdAdapter.php b/src/Queue/Adapters/BeanstalkdAdapter.php index 1f5e9751..b8cf9cbc 100644 --- a/src/Queue/Adapters/BeanstalkdAdapter.php +++ b/src/Queue/Adapters/BeanstalkdAdapter.php @@ -126,14 +126,14 @@ public function size(?string $queue = null): int */ public function push(ProducerService $producer): void { + // TODO: should be removed + // $this->flush(); $queues = (array) cache("beanstalkd:queues"); if (!in_array($producer->getQueue(), $queues)) { $queues[] = $producer->getQueue(); cache("beanstalkd:queues", $queues); } - $queues = (array) cache("beanstalkd:queues"); - dump($queues); $this->pheanstalk ->useTube($producer->getQueue()) @@ -168,7 +168,6 @@ public function run(string $queue = null): void try { $payload = $job->getData(); $producer = $this->unserializeProducer($payload); - dump($producer); call_user_func([$producer, "process"]); $this->sleep(2); $this->pheanstalk->touch($job); diff --git a/src/Testing/KernelTesting.php b/src/Testing/KernelTesting.php index 3ac570e8..9badc418 100644 --- a/src/Testing/KernelTesting.php +++ b/src/Testing/KernelTesting.php @@ -16,7 +16,7 @@ class KernelTesting extends ConfigurationLoader * @param array $configurations * @return void */ - public static function withConfiguations(array $configurations): void + public static function withConfigurations(array $configurations): void { static::$configurations = $configurations; } diff --git a/tests/Config/TestingConfiguration.php b/tests/Config/TestingConfiguration.php index 63009e33..d542fb42 100644 --- a/tests/Config/TestingConfiguration.php +++ b/tests/Config/TestingConfiguration.php @@ -15,6 +15,39 @@ public function __construct() is_dir(TESTING_RESOURCE_BASE_DIRECTORY) || mkdir(TESTING_RESOURCE_BASE_DIRECTORY, 0777); } + /** + * Configure the testing + * + * @param array $configurations + * @return void + */ + public static function withConfigurations(array $configurations) + { + KernelTesting::withConfigurations($configurations); + } + + /** + * Configure the testing + * + * @param array $middlewares + * @return void + */ + public static function withMiddlewares(array $middlewares) + { + KernelTesting::withMiddlewares($middlewares); + } + + /** + * Set the loading events + * + * @param array $events + * @return void + */ + public static function withEvents(array $events): void + { + KernelTesting::withEvents($events); + } + /** * Get the configuration for testing * diff --git a/tests/Queue/QueueTest.php b/tests/Queue/QueueTest.php index de73ad51..16a0a4ff 100644 --- a/tests/Queue/QueueTest.php +++ b/tests/Queue/QueueTest.php @@ -4,12 +4,16 @@ use Bow\Cache\Adapter\RedisAdapter; use Bow\Cache\Cache; +use Bow\Cache\CacheConfiguration; +use Bow\Configuration\LoggerConfiguration; use Bow\Database\Database; +use Bow\Database\DatabaseConfiguration; use Bow\Queue\Adapters\BeanstalkdAdapter; use Bow\Queue\Adapters\SQSAdapter; use Bow\Tests\Config\TestingConfiguration; use Bow\Tests\Queue\Stubs\PetModelStub; use Bow\Queue\Connection as QueueConnection; +use Bow\Testing\KernelTesting; use Bow\Tests\Queue\Stubs\ModelProducerStub; use Bow\Tests\Queue\Stubs\BasicProducerStubs; @@ -19,11 +23,17 @@ class QueueTest extends \PHPUnit\Framework\TestCase public static function setUpBeforeClass(): void { + TestingConfiguration::withConfigurations([ + LoggerConfiguration::class, + DatabaseConfiguration::class, + CacheConfiguration::class, + ]); + $config = TestingConfiguration::getConfig(); + $config->boot(); + static::$connection = new QueueConnection($config["queue"]); - Database::configure($config["database"]); - Cache::configure($config["cache"]); Database::connection('mysql'); Database::statement('drop table if exists pets'); Database::statement('create table pets (id int primary key auto_increment, name varchar(255))'); diff --git a/tests/Support/HttpClientTest.php b/tests/Support/HttpClientTest.php index 13a0d3f4..b5220b81 100644 --- a/tests/Support/HttpClientTest.php +++ b/tests/Support/HttpClientTest.php @@ -11,7 +11,7 @@ public function test_get_method() { $http = new HttpClient(); - $response = $http->get("https://google.com"); + $response = $http->get("https://www.oogle.com"); $this->assertEquals($response->statusCode(), 200); } @@ -21,14 +21,14 @@ public function test_get_method_with_custom_headers() $http = new HttpClient(); $http->addHeaders(["X-Api-Key" => "Fake-Key"]); - $response = $http->get("https://google.com"); + $response = $http->get("https://www.google.com"); $this->assertEquals($response->statusCode(), 200); } public function test_should_be_fail_with_get_method() { - $http = new HttpClient("https://google.com"); + $http = new HttpClient("https://www.google.com"); $http->addHeaders(["X-Api-Key" => "Fake-Key"]); $response = $http->get("/the-fake-url"); From 8484b231a0235f7ca3b39d40f6ca6c14186e5903 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 22 Sep 2023 06:50:50 +0000 Subject: [PATCH 117/199] Initial database and sqs queue adapter --- src/Queue/Adapters/DatabaseAdapter.php | 195 +++++++++++++++++++++ src/Queue/Adapters/SQSAdapter.php | 227 +++++++++++++++++++++++++ 2 files changed, 422 insertions(+) create mode 100644 src/Queue/Adapters/DatabaseAdapter.php create mode 100644 src/Queue/Adapters/SQSAdapter.php diff --git a/src/Queue/Adapters/DatabaseAdapter.php b/src/Queue/Adapters/DatabaseAdapter.php new file mode 100644 index 00000000..be022046 --- /dev/null +++ b/src/Queue/Adapters/DatabaseAdapter.php @@ -0,0 +1,195 @@ +table = Database::table($queue["table"] ?? "queue_jobs"); + + return $this; + } + + /** + * Get connexion + * + * @param string $name + * @return Pheanstalk + */ + public function setWatch(string $name): void + { + $this->queue = $name; + } + + /** + * Set job tries + * + * @param int $tries + * @return void + */ + public function setTries(int $tries): void + { + $this->tries = $tries; + } + + /** + * Set sleep time + * + * @param int $sleep + * @return void + */ + public function setSleep(int $sleep): void + { + $this->sleep = $sleep; + } + + /** + * Get the queue or return the default. + * + * @param ?string $queue + * @return string + */ + public function getQueue(?string $queue = null): string + { + return $queue ?: $this->queue; + } + + /** + * Get the size of the queue. + * + * @param string $queue + * @return int + */ + public function size(?string $queue = null): int + { + return $this->table + ->where("queue", $this->getQueue($queue)) + ->count(); + } + + /** + * Queue a job + * + * @param ProducerService $producer + * @return QueueAdapter + */ + public function push(ProducerService $producer): void + { + $this->table->insert([ + "id" => $producer->getId(), + "queue" => $this->getQueue(), + "payload" => $this->serializeProducer($producer), + "attempts" => $producer->getRetry(), + "status" => "pending", + "available_at" => date("Y-m-d H:i:s", time() + $producer->getDelay()), + "reserved_at" => null, + "created_at" => date("Y-m-d H:i:s"), + ]); + } + + /** + * Run the worker + * + * @param string|null $queue + * @return mixed + */ + public function run(string $queue = null): void + { + // we want jobs from define queue only. + $queue = $this->getQueue($queue); + $queues = $this->table + ->where("queue", $queue) + ->whereIn("status", ["pending", "reserved"]) + ->get(); + + if (count($queues) == 0) { + $this->sleep($this->sleep ?? 5); + return; + } + + foreach ($queues as $job) { + try { + $producer = $this->unserializeProducer($job->payload); + $delay = $producer->getDelay(); + if ($job->delay == 0) { + $this->execute($producer, $job); + continue; + } + $execute_time = time() + $job->delay; + if ($execute_time >= time()) { + $this->execute($producer, $job); + } + } catch (\Exception $e) { + error_log($e->getMessage()); + app('logger')->error($e->getMessage(), $e->getTrace()); + cache("failed:job:" . $job->id, $job->payload); + if ($producer->jobShouldBeDelete() || $job->retry <= 0) { + $this->table->where("id", $job->id)->delete(); + $this->sleep(1); + continue; + } + $this->table->where("id", $job->id)->update([ + "status" => "reserved", + "retry" => $job->tries - 1, + 'delay' => $delay + ]); + $this->sleep(1); + } + } + } + + /** + * Process the next job on the queue. + * + * @param ProducerService $producer + * @param mixed $job + */ + private function execute(ProducerService $producer, mixed $job) + { + call_user_func([$producer, "process"]); + $this->table->where("id", $job->id)->update([ + "status" => "processed" + ]); + sleep($this->sleep ?? 5); + } +} diff --git a/src/Queue/Adapters/SQSAdapter.php b/src/Queue/Adapters/SQSAdapter.php new file mode 100644 index 00000000..b56dfbe9 --- /dev/null +++ b/src/Queue/Adapters/SQSAdapter.php @@ -0,0 +1,227 @@ +config = $config; + + $this->client = new SqsClient([ + 'profile' => 'default', + 'region' => 'us-west-2', + 'version' => '2012-11-05', + 'credentials' => [ + 'key' => $config["key"], + 'secret' => $config["secret"] + ] + ]); + + return $this; + } + + /** + * Set the watch queue. + * + * @param string $queue + * @return void + */ + public function setWatch(string $queue): void + { + $this->queue = $queue; + } + + /** + * Set the number of times to attempt a job. + * + * @param int $tries + * @return void + */ + public function setTries(int $tries): void + { + $this->tries = $tries; + } + + /** + * Set the number of seconds to sleep between jobs. + * + * @param int $sleep + * @return void + */ + public function setSleep(int $sleep): void + { + $this->sleep = $sleep; + } + + /** + * Push a job onto the queue. + * + * @param ProducerService $producer + * @return void + */ + public function push(ProducerService $producer): void + { + $params = [ + 'DelaySeconds' => $producer->getDelay(), + 'MessageAttributes' => [ + "Title" => [ + 'DataType' => "String", + 'StringValue' => get_class($producer) + ], + ], + 'MessageBody' => $this->serializeProducer($producer), + 'QueueUrl' => $this->config["url"] + ]; + + try { + $result = $this->client->sendMessage($params); + } catch (AwsException $e) { + error_log($e->getMessage()); + } + } + + /** + * Get the size of the queue. + * + * @param string $queue + * @return int + */ + public function size(string $queue): int + { + $response = $this->client->getQueueAttributes([ + 'QueueUrl' => $this->getQueue($queue), + 'AttributeNames' => ['ApproximateNumberOfMessages'], + ]); + + $attributes = $response->get('Attributes'); + + return (int) $attributes['ApproximateNumberOfMessages']; + } + + /** + * Get the queue or return the default. + * + * @param ?string $queue + * @return string + */ + public function getQueue(?string $queue = null): string + { + return $queue ?: $this->queue; + } + + /** + * Set the number of seconds to wait before retrying a job. + * + * @param int $retry + * @return void + */ + public function setRetries(int $tries) + { + $this->tries = $tries; + } + + /** + * Process the next job on the queue. + * + * @param ?string $queue + * @return void + */ + public function run(?string $queue = null): void + { + $this->sleep($this->sleep ?? 5); + + try { + $result = $this->client->receiveMessage([ + 'AttributeNames' => ['SentTimestamp'], + 'MaxNumberOfMessages' => 1, + 'MessageAttributeNames' => ['All'], + 'QueueUrl' => $this->config["url"], + 'WaitTimeSeconds' => 20, + ]); + $messages = $result->get('Messages'); + if (empty($messages)) { + $this->sleep(1); + return; + } + $message = $result->get('Messages')[0]; + $producer = $this->unserializeProducer($message["MessageBody"]); + $delay = $producer->getDelay(); + call_user_func([$producer, "process"]); + $result = $this->client->deleteMessage([ + 'QueueUrl' => $this->config["url"], + 'ReceiptHandle' => $message['ReceiptHandle'] + ]); + } catch (AwsException $e) { + error_log($e->getMessage()); + app('logger')->error($e->getMessage(), $e->getTrace()); + if ($producer->jobShouldBeDelete()) { + $result = $this->client->deleteMessage([ + 'QueueUrl' => $this->config["url"], + 'ReceiptHandle' => $message['ReceiptHandle'] + ]); + } else { + $result = $this->client->changeMessageVisibilityBatch([ + 'QueueUrl' => $this->config["url"], + 'Entries' => [ + 'Id' => $producer->getId(), + 'ReceiptHandle' => $message['ReceiptHandle'], + 'VisibilityTimeout' => $delay + ], + ]); + } + $this->sleep(1); + } + } +} From 8ae5e89bf3e0b7e4ebaef1378ecff76f719d95f0 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 22 Sep 2023 07:37:04 +0000 Subject: [PATCH 118/199] fixes phpdoc --- src/Http/Client/HttpClient.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Client/HttpClient.php b/src/Http/Client/HttpClient.php index ec3b0c8e..818db940 100644 --- a/src/Http/Client/HttpClient.php +++ b/src/Http/Client/HttpClient.php @@ -292,7 +292,7 @@ private function execute(): string /** * Set Curl CURLOPT_RETURNTRANSFER option * - * @return bool + * @return void */ private function applyCommonOptions() { From 12a8302b8d070e1883f423dedfe73aad938bc755 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 22 Sep 2023 08:54:12 +0000 Subject: [PATCH 119/199] Integrate SQS adapter --- .../Command/GenerateDatabaseQueueCommand.php | 34 ++++++++ src/Console/stubs/model/queue.stub | 28 ++++++ src/Queue/Adapters/BeanstalkdAdapter.php | 4 +- src/Queue/Adapters/DatabaseAdapter.php | 2 +- src/Queue/Adapters/SQSAdapter.php | 87 ++++++++++--------- tests/Config/stubs/app.php | 3 +- tests/Config/stubs/queue.php | 14 ++- tests/Config/stubs/storage.php | 4 +- tests/Queue/QueueTest.php | 6 +- 9 files changed, 130 insertions(+), 52 deletions(-) create mode 100644 src/Console/Command/GenerateDatabaseQueueCommand.php create mode 100644 src/Console/stubs/model/queue.stub diff --git a/src/Console/Command/GenerateDatabaseQueueCommand.php b/src/Console/Command/GenerateDatabaseQueueCommand.php new file mode 100644 index 00000000..7ccd603f --- /dev/null +++ b/src/Console/Command/GenerateDatabaseQueueCommand.php @@ -0,0 +1,34 @@ +setting->getMigrationDirectory(), + $filename + ); + + $generator->write('model/queue', [ + 'className' => $filename + ]); + + echo Color::green('Queue migration created.'); + } +} diff --git a/src/Console/stubs/model/queue.stub b/src/Console/stubs/model/queue.stub new file mode 100644 index 00000000..845302cd --- /dev/null +++ b/src/Console/stubs/model/queue.stub @@ -0,0 +1,28 @@ +create("queues", function (SQLGenerator $table) { + $table->addColumn('id', 'string', ['primary' => true]); + $table->addColumn('time', 'timestamp'); + $table->addColumn('data', 'text'); + $table->addColumn('ip', 'string'); + }); + } + + /** + * Rollback migration + */ + public function rollback(): void + { + $this->dropIfExists("sessions"); + } +} diff --git a/src/Queue/Adapters/BeanstalkdAdapter.php b/src/Queue/Adapters/BeanstalkdAdapter.php index b8cf9cbc..fc95f7d7 100644 --- a/src/Queue/Adapters/BeanstalkdAdapter.php +++ b/src/Queue/Adapters/BeanstalkdAdapter.php @@ -122,12 +122,10 @@ public function size(?string $queue = null): int * Queue a job * * @param ProducerService $producer - * @return QueueAdapter + * @return void */ public function push(ProducerService $producer): void { - // TODO: should be removed - // $this->flush(); $queues = (array) cache("beanstalkd:queues"); if (!in_array($producer->getQueue(), $queues)) { diff --git a/src/Queue/Adapters/DatabaseAdapter.php b/src/Queue/Adapters/DatabaseAdapter.php index be022046..15dbee0f 100644 --- a/src/Queue/Adapters/DatabaseAdapter.php +++ b/src/Queue/Adapters/DatabaseAdapter.php @@ -54,7 +54,7 @@ public function configure(array $queue): DatabaseAdapter * Get connexion * * @param string $name - * @return Pheanstalk + * @return void */ public function setWatch(string $name): void { diff --git a/src/Queue/Adapters/SQSAdapter.php b/src/Queue/Adapters/SQSAdapter.php index b56dfbe9..c0e00e92 100644 --- a/src/Queue/Adapters/SQSAdapter.php +++ b/src/Queue/Adapters/SQSAdapter.php @@ -15,7 +15,7 @@ class SQSAdapter extends QueueAdapter * * @var SqsClient */ - private SqsClient $client; + private SqsClient $sqs; /** * The configuration array @@ -59,15 +59,7 @@ public function configure(array $config): QueueAdapter $this->config = $config; - $this->client = new SqsClient([ - 'profile' => 'default', - 'region' => 'us-west-2', - 'version' => '2012-11-05', - 'credentials' => [ - 'key' => $config["key"], - 'secret' => $config["secret"] - ] - ]); + $this->sqs = new SqsClient($config); return $this; } @@ -105,6 +97,28 @@ public function setSleep(int $sleep): void $this->sleep = $sleep; } + /** + * Get the queue or return the default. + * + * @param ?string $queue + * @return string + */ + public function getQueue(?string $queue = null): string + { + return $queue ?: $this->queue; + } + + /** + * Set the number of seconds to wait before retrying a job. + * + * @param int $retry + * @return void + */ + public function setRetries(int $tries) + { + $this->tries = $tries; + } + /** * Push a job onto the queue. * @@ -121,12 +135,12 @@ public function push(ProducerService $producer): void 'StringValue' => get_class($producer) ], ], - 'MessageBody' => $this->serializeProducer($producer), + 'MessageBody' => base64_encode($this->serializeProducer($producer)), 'QueueUrl' => $this->config["url"] ]; try { - $result = $this->client->sendMessage($params); + $this->sqs->sendMessage($params); } catch (AwsException $e) { error_log($e->getMessage()); } @@ -140,7 +154,7 @@ public function push(ProducerService $producer): void */ public function size(string $queue): int { - $response = $this->client->getQueueAttributes([ + $response = $this->sqs->getQueueAttributes([ 'QueueUrl' => $this->getQueue($queue), 'AttributeNames' => ['ApproximateNumberOfMessages'], ]); @@ -150,28 +164,6 @@ public function size(string $queue): int return (int) $attributes['ApproximateNumberOfMessages']; } - /** - * Get the queue or return the default. - * - * @param ?string $queue - * @return string - */ - public function getQueue(?string $queue = null): string - { - return $queue ?: $this->queue; - } - - /** - * Set the number of seconds to wait before retrying a job. - * - * @param int $retry - * @return void - */ - public function setRetries(int $tries) - { - $this->tries = $tries; - } - /** * Process the next job on the queue. * @@ -183,7 +175,7 @@ public function run(?string $queue = null): void $this->sleep($this->sleep ?? 5); try { - $result = $this->client->receiveMessage([ + $result = $this->sqs->receiveMessage([ 'AttributeNames' => ['SentTimestamp'], 'MaxNumberOfMessages' => 1, 'MessageAttributeNames' => ['All'], @@ -196,23 +188,27 @@ public function run(?string $queue = null): void return; } $message = $result->get('Messages')[0]; - $producer = $this->unserializeProducer($message["MessageBody"]); + $producer = $this->unserializeProducer(base64_decode($message["Body"])); $delay = $producer->getDelay(); call_user_func([$producer, "process"]); - $result = $this->client->deleteMessage([ + $result = $this->sqs->deleteMessage([ 'QueueUrl' => $this->config["url"], 'ReceiptHandle' => $message['ReceiptHandle'] ]); } catch (AwsException $e) { error_log($e->getMessage()); app('logger')->error($e->getMessage(), $e->getTrace()); + if (!isset($producer)) { + $this->sleep(1); + return; + } if ($producer->jobShouldBeDelete()) { - $result = $this->client->deleteMessage([ + $result = $this->sqs->deleteMessage([ 'QueueUrl' => $this->config["url"], 'ReceiptHandle' => $message['ReceiptHandle'] ]); } else { - $result = $this->client->changeMessageVisibilityBatch([ + $result = $this->sqs->changeMessageVisibilityBatch([ 'QueueUrl' => $this->config["url"], 'Entries' => [ 'Id' => $producer->getId(), @@ -224,4 +220,15 @@ public function run(?string $queue = null): void $this->sleep(1); } } + + /** + * flush the queue. + * + * @param ?string $queue + * @return void + */ + public function flush(?string $queue = null): void + { + + } } diff --git a/tests/Config/stubs/app.php b/tests/Config/stubs/app.php index 023def4b..aadfc824 100644 --- a/tests/Config/stubs/app.php +++ b/tests/Config/stubs/app.php @@ -2,5 +2,6 @@ return [ "root" => "", - "auto_csrf" => false + "auto_csrf" => false, + "env_file" => realpath(__DIR__ . "/../../Support/stubs/env.json"), ]; diff --git a/tests/Config/stubs/queue.php b/tests/Config/stubs/queue.php index 88af732b..78899571 100644 --- a/tests/Config/stubs/queue.php +++ b/tests/Config/stubs/queue.php @@ -30,14 +30,22 @@ * The sqs connexion */ "sqs" => [ - 'url' => app_env('SQS_URL', 'https://sqs.ap-south-1.amazonaws.com/242848748621/messaging'), + 'profile' => 'default', + 'region' => 'ap-south-1', + 'version' => 'latest', + 'url' => app_env("AWS_SQS_URL"), + 'bucket' => app_env('AWS_S3_BUCKET'), + 'credentials' => [ + 'key' => app_env('AWS_KEY'), + 'secret' => app_env('AWS_SECRET'), + ], ], - + /** * The sqs connexion */ "database" => [ - 'table' => "queue_jobs", + 'table' => "queues", ] ] ]; diff --git a/tests/Config/stubs/storage.php b/tests/Config/stubs/storage.php index 3d9aa136..53ddf92e 100644 --- a/tests/Config/stubs/storage.php +++ b/tests/Config/stubs/storage.php @@ -38,8 +38,8 @@ 's3' => [ "driver" => "s3", 'credentials' => [ - 'key' => app_env('AWS_S3_KEY'), - 'secret' => app_env('AWS_S3_SECRET'), + 'key' => app_env('AWS_KEY'), + 'secret' => app_env('AWS_SECRET'), ], 'bucket' => app_env('AWS_S3_BUCKET'), 'region' => 'us-east-1', diff --git a/tests/Queue/QueueTest.php b/tests/Queue/QueueTest.php index 16a0a4ff..a2758fcd 100644 --- a/tests/Queue/QueueTest.php +++ b/tests/Queue/QueueTest.php @@ -5,6 +5,7 @@ use Bow\Cache\Adapter\RedisAdapter; use Bow\Cache\Cache; use Bow\Cache\CacheConfiguration; +use Bow\Configuration\EnvConfiguration; use Bow\Configuration\LoggerConfiguration; use Bow\Database\Database; use Bow\Database\DatabaseConfiguration; @@ -27,6 +28,7 @@ public static function setUpBeforeClass(): void LoggerConfiguration::class, DatabaseConfiguration::class, CacheConfiguration::class, + EnvConfiguration::class, ]); $config = TestingConfiguration::getConfig(); @@ -113,8 +115,8 @@ public function test_push_service_adapter_with_model($connection) public function getConnection(): array { return [ - ["beanstalkd"], - // ["sqs"], + // ["beanstalkd"], + ["sqs"], // ["redis"], // ["rabbitmq"], // ["database"] From 9b0015f93bc08dafa6e30070cfa27555c2be765f Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 22 Sep 2023 08:56:10 +0000 Subject: [PATCH 120/199] Formatting --- tests/Config/stubs/queue.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Config/stubs/queue.php b/tests/Config/stubs/queue.php index 78899571..ee7e96e1 100644 --- a/tests/Config/stubs/queue.php +++ b/tests/Config/stubs/queue.php @@ -34,7 +34,6 @@ 'region' => 'ap-south-1', 'version' => 'latest', 'url' => app_env("AWS_SQS_URL"), - 'bucket' => app_env('AWS_S3_BUCKET'), 'credentials' => [ 'key' => app_env('AWS_KEY'), 'secret' => app_env('AWS_SECRET'), From fbbf3af91c5cfc86f08d014ddd33e5d37292faac Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 22 Sep 2023 09:06:35 +0000 Subject: [PATCH 121/199] Update get env function --- tests/Config/stubs/queue.php | 6 +++--- tests/Config/stubs/storage.php | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/Config/stubs/queue.php b/tests/Config/stubs/queue.php index ee7e96e1..b65f6cf2 100644 --- a/tests/Config/stubs/queue.php +++ b/tests/Config/stubs/queue.php @@ -33,10 +33,10 @@ 'profile' => 'default', 'region' => 'ap-south-1', 'version' => 'latest', - 'url' => app_env("AWS_SQS_URL"), + 'url' => getenv("AWS_SQS_URL"), 'credentials' => [ - 'key' => app_env('AWS_KEY'), - 'secret' => app_env('AWS_SECRET'), + 'key' => getenv('AWS_KEY'), + 'secret' => getenv('AWS_SECRET'), ], ], diff --git a/tests/Config/stubs/storage.php b/tests/Config/stubs/storage.php index 53ddf92e..4fcc382a 100644 --- a/tests/Config/stubs/storage.php +++ b/tests/Config/stubs/storage.php @@ -38,10 +38,10 @@ 's3' => [ "driver" => "s3", 'credentials' => [ - 'key' => app_env('AWS_KEY'), - 'secret' => app_env('AWS_SECRET'), + 'key' => getenv('AWS_KEY'), + 'secret' => getenv('AWS_SECRET'), ], - 'bucket' => app_env('AWS_S3_BUCKET'), + 'bucket' => getenv('AWS_S3_BUCKET'), 'region' => 'us-east-1', 'version' => 'latest' ] From 8bc13ff131b85cad415ca87245b06dc88d2104fd Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 22 Sep 2023 09:08:18 +0000 Subject: [PATCH 122/199] Enable connection --- tests/Queue/QueueTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Queue/QueueTest.php b/tests/Queue/QueueTest.php index a2758fcd..7e4261dd 100644 --- a/tests/Queue/QueueTest.php +++ b/tests/Queue/QueueTest.php @@ -115,11 +115,11 @@ public function test_push_service_adapter_with_model($connection) public function getConnection(): array { return [ - // ["beanstalkd"], + ["beanstalkd"], ["sqs"], + // ["database"], // ["redis"], // ["rabbitmq"], - // ["database"] ]; } } From b06c964019dd599f7d57e4c74edbb527b140a41a Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 22 Sep 2023 10:46:54 +0000 Subject: [PATCH 123/199] Integration of database adapter --- src/Console/stubs/model/queue.stub | 17 +++- src/Queue/Adapters/BeanstalkdAdapter.php | 21 ---- src/Queue/Adapters/DatabaseAdapter.php | 121 +++++++---------------- src/Queue/Adapters/QueueAdapter.php | 78 +++++++++------ src/Queue/Adapters/SQSAdapter.php | 81 +-------------- src/Queue/Connection.php | 2 + src/Queue/ProducerService.php | 19 +++- tests/Config/stubs/queue.php | 2 +- tests/Queue/QueueTest.php | 13 ++- 9 files changed, 136 insertions(+), 218 deletions(-) diff --git a/src/Console/stubs/model/queue.stub b/src/Console/stubs/model/queue.stub index 845302cd..066d2c40 100644 --- a/src/Console/stubs/model/queue.stub +++ b/src/Console/stubs/model/queue.stub @@ -11,10 +11,17 @@ class {className} extends Migration public function up(): void { $this->create("queues", function (SQLGenerator $table) { - $table->addColumn('id', 'string', ['primary' => true]); - $table->addColumn('time', 'timestamp'); - $table->addColumn('data', 'text'); - $table->addColumn('ip', 'string'); + $table->addString('id', ["primary" => true]); + $table->addString('queue'); + $table->addText('payload'); + $table->addInteger('attempts', ["default" => 3]); + $table->addEnum('status', [ + "size" => ["waiting", "processing", "reserved", "failed", "done"], + "default" => "waiting", + ]); + $table->addDatetime('avalaibled_at'); + $table->addDatetime('reserved_at', ["nullable" => true, "default" => null]); + $table->addDatetime('created_at'); }); } @@ -23,6 +30,6 @@ class {className} extends Migration */ public function rollback(): void { - $this->dropIfExists("sessions"); + $this->dropIfExists("queues"); } } diff --git a/src/Queue/Adapters/BeanstalkdAdapter.php b/src/Queue/Adapters/BeanstalkdAdapter.php index fc95f7d7..d0bb2c10 100644 --- a/src/Queue/Adapters/BeanstalkdAdapter.php +++ b/src/Queue/Adapters/BeanstalkdAdapter.php @@ -19,27 +19,6 @@ class BeanstalkdAdapter extends QueueAdapter */ private Pheanstalk $pheanstalk; - /** - * Determine the default watch name - * - * @var string - */ - private string $queue = "default"; - - /** - * The number of working attempts - * - * @var int - */ - private int $tries; - - /** - * Define the sleep time - * - * @var int - */ - private int $sleep = 5; - /** * Configure Beanstalkd driver * diff --git a/src/Queue/Adapters/DatabaseAdapter.php b/src/Queue/Adapters/DatabaseAdapter.php index 15dbee0f..f4264b4b 100644 --- a/src/Queue/Adapters/DatabaseAdapter.php +++ b/src/Queue/Adapters/DatabaseAdapter.php @@ -5,7 +5,6 @@ use Bow\Database\Database; use Bow\Database\QueryBuilder; use Bow\Queue\ProducerService; -use RuntimeException; class DatabaseAdapter extends QueueAdapter { @@ -16,27 +15,6 @@ class DatabaseAdapter extends QueueAdapter */ private QueryBuilder $table; - /** - * Determine the default watch name - * - * @var string - */ - private string $queue = "default"; - - /** - * The number of working attempts - * - * @var int - */ - private int $tries; - - /** - * Define the sleep time - * - * @var int - */ - private int $sleep = 5; - /** * Configure Beanstalkd driver * @@ -50,50 +28,6 @@ public function configure(array $queue): DatabaseAdapter return $this; } - /** - * Get connexion - * - * @param string $name - * @return void - */ - public function setWatch(string $name): void - { - $this->queue = $name; - } - - /** - * Set job tries - * - * @param int $tries - * @return void - */ - public function setTries(int $tries): void - { - $this->tries = $tries; - } - - /** - * Set sleep time - * - * @param int $sleep - * @return void - */ - public function setSleep(int $sleep): void - { - $this->sleep = $sleep; - } - - /** - * Get the queue or return the default. - * - * @param ?string $queue - * @return string - */ - public function getQueue(?string $queue = null): string - { - return $queue ?: $this->queue; - } - /** * Get the size of the queue. * @@ -116,12 +50,12 @@ public function size(?string $queue = null): int public function push(ProducerService $producer): void { $this->table->insert([ - "id" => $producer->getId(), + "id" => $this->generateId(), "queue" => $this->getQueue(), - "payload" => $this->serializeProducer($producer), - "attempts" => $producer->getRetry(), - "status" => "pending", - "available_at" => date("Y-m-d H:i:s", time() + $producer->getDelay()), + "payload" => base64_encode($this->serializeProducer($producer)), + "attempts" => $this->tries, + "status" => "waiting", + "avalaibled_at" => date("Y-m-d H:i:s", time() + $producer->getDelay()), "reserved_at" => null, "created_at" => date("Y-m-d H:i:s"), ]); @@ -139,7 +73,7 @@ public function run(string $queue = null): void $queue = $this->getQueue($queue); $queues = $this->table ->where("queue", $queue) - ->whereIn("status", ["pending", "reserved"]) + ->whereIn("status", ["waiting", "reserved"]) ->get(); if (count($queues) == 0) { @@ -149,29 +83,35 @@ public function run(string $queue = null): void foreach ($queues as $job) { try { - $producer = $this->unserializeProducer($job->payload); - $delay = $producer->getDelay(); - if ($job->delay == 0) { + $producer = $this->unserializeProducer(base64_decode($job->payload)); + if (strtotime($job->avalaibled_at) >= time()) { + if (!is_null($job->reserved_at) && strtotime($job->reserved_at) < time()) { + continue; + } + $this->table->where("id", $job->id)->update([ + "status" => "processing", + ]); $this->execute($producer, $job); continue; } - $execute_time = time() + $job->delay; - if ($execute_time >= time()) { - $this->execute($producer, $job); - } } catch (\Exception $e) { error_log($e->getMessage()); app('logger')->error($e->getMessage(), $e->getTrace()); cache("failed:job:" . $job->id, $job->payload); - if ($producer->jobShouldBeDelete() || $job->retry <= 0) { + if (!isset($producer)) { + $this->sleep(1); + continue; + } + if ($producer->jobShouldBeDelete() || $job->attempts <= 0) { $this->table->where("id", $job->id)->delete(); $this->sleep(1); continue; } $this->table->where("id", $job->id)->update([ "status" => "reserved", - "retry" => $job->tries - 1, - 'delay' => $delay + "attempts" => $job->attempts - 1, + "avalaibled_at" => date("Y-m-d H:i:s", time() + $producer->getDelay()), + "reserved_at" => date("Y-m-d H:i:s", time() + $producer->getRetry()) ]); $this->sleep(1); } @@ -188,8 +128,23 @@ private function execute(ProducerService $producer, mixed $job) { call_user_func([$producer, "process"]); $this->table->where("id", $job->id)->update([ - "status" => "processed" + "status" => "done" ]); sleep($this->sleep ?? 5); } + + /** + * Flush the queue table + * + * @param ?string $queue + * @return void + */ + public function flush(?string $queue = null): void + { + if (is_null($queue)) { + $this->table->truncate(); + } else { + $this->table->where("queue", $queue)->delete(); + } + } } diff --git a/src/Queue/Adapters/QueueAdapter.php b/src/Queue/Adapters/QueueAdapter.php index 5e203c78..0a1aa8c4 100644 --- a/src/Queue/Adapters/QueueAdapter.php +++ b/src/Queue/Adapters/QueueAdapter.php @@ -17,21 +17,28 @@ abstract class QueueAdapter * * @var int */ - private int $start_time; + protected int $start_time; /** - * Determine if the worker should quit + * Determine the default watch name * - * @var bool + * @var string */ - protected bool $should_quit = false; + protected string $queue = "default"; /** - * Determine if the worker is paused + * The number of working attempts * - * @var bool + * @var int + */ + protected int $tries = 3; + + /** + * Define the sleep time + * + * @var int */ - protected bool $paused = false; + protected int $sleep = 5; /** * Create producer serialization @@ -160,34 +167,57 @@ protected function supportsAsyncSignals() return extension_loaded('pcntl'); } + /** - * Make adapter configuration + * Set job tries * - * @param array $config - * @return QueueAdapter + * @param int $tries + * @return void */ - abstract public function configure(array $config): QueueAdapter; + public function setTries(int $tries): void + { + $this->tries = $tries; + } /** - * Watch the the queue name + * Set sleep time * - * @param string $queue + * @param int $sleep + * @return void */ - abstract public function setWatch(string $queue): void; + public function setSleep(int $sleep): void + { + $this->sleep = $sleep; + } /** - * Set the tries value + * Get the queue or return the default. * - * @param int $tries + * @param ?string $queue + * @return string */ - abstract public function setTries(int $tries): void; + public function getQueue(?string $queue = null): string + { + return $queue ?: $this->queue; + } /** - * Set the sleep value + * Generate the job id * - * @param int $sleep + * @return string + */ + public function generateId(): string + { + return sha1(uniqid(str_shuffle("abcdefghijklmnopqrstuvwxyz0123456789"), true)); + } + + /** + * Make adapter configuration + * + * @param array $config + * @return QueueAdapter */ - abstract public function setSleep(int $sleep): void; + abstract public function configure(array $config): QueueAdapter; /** * Push new producer @@ -204,14 +234,6 @@ abstract public function push(ProducerService $producer): void; */ abstract public function size(string $queue): int; - /** - * Get the queue or return the default. - * - * @param ?string $queue - * @return string - */ - abstract public function getQueue(?string $queue = null): string; - /** * Start the worker server * diff --git a/src/Queue/Adapters/SQSAdapter.php b/src/Queue/Adapters/SQSAdapter.php index c0e00e92..cbb4ef22 100644 --- a/src/Queue/Adapters/SQSAdapter.php +++ b/src/Queue/Adapters/SQSAdapter.php @@ -24,27 +24,6 @@ class SQSAdapter extends QueueAdapter */ private array $config = []; - /** - * Determine the default watch name - * - * @var string - */ - private string $queue = "default"; - - /** - * The number of working attempts - * - * @var int - */ - private int $tries; - - /** - * Define the sleep time - * - * @var int - */ - private int $sleep = 5; - /** * Configure the queue. * @@ -64,61 +43,6 @@ public function configure(array $config): QueueAdapter return $this; } - /** - * Set the watch queue. - * - * @param string $queue - * @return void - */ - public function setWatch(string $queue): void - { - $this->queue = $queue; - } - - /** - * Set the number of times to attempt a job. - * - * @param int $tries - * @return void - */ - public function setTries(int $tries): void - { - $this->tries = $tries; - } - - /** - * Set the number of seconds to sleep between jobs. - * - * @param int $sleep - * @return void - */ - public function setSleep(int $sleep): void - { - $this->sleep = $sleep; - } - - /** - * Get the queue or return the default. - * - * @param ?string $queue - * @return string - */ - public function getQueue(?string $queue = null): string - { - return $queue ?: $this->queue; - } - - /** - * Set the number of seconds to wait before retrying a job. - * - * @param int $retry - * @return void - */ - public function setRetries(int $tries) - { - $this->tries = $tries; - } - /** * Push a job onto the queue. * @@ -134,6 +58,10 @@ public function push(ProducerService $producer): void 'DataType' => "String", 'StringValue' => get_class($producer) ], + "Id" => [ + "DataType" => "String", + "StringValue" => $this->generateId(), + ] ], 'MessageBody' => base64_encode($this->serializeProducer($producer)), 'QueueUrl' => $this->config["url"] @@ -229,6 +157,5 @@ public function run(?string $queue = null): void */ public function flush(?string $queue = null): void { - } } diff --git a/src/Queue/Connection.php b/src/Queue/Connection.php index 979d4410..65ceff42 100644 --- a/src/Queue/Connection.php +++ b/src/Queue/Connection.php @@ -6,6 +6,7 @@ use Bow\Queue\Adapters\QueueAdapter; use Bow\Queue\Adapters\BeanstalkdAdapter; +use Bow\Queue\Adapters\DatabaseAdapter; use Bow\Queue\Adapters\SQSAdapter; use ErrorException; @@ -33,6 +34,7 @@ class Connection private static array $connections = [ "beanstalkd" => BeanstalkdAdapter::class, "sqs" => SQSAdapter::class, + "database" => DatabaseAdapter::class, ]; /** diff --git a/src/Queue/ProducerService.php b/src/Queue/ProducerService.php index 18ff747d..31941a97 100644 --- a/src/Queue/ProducerService.php +++ b/src/Queue/ProducerService.php @@ -50,7 +50,14 @@ abstract class ProducerService * * @return integer */ - protected string $id; + protected ?string $id = null; + + /** + * Define the job attempts + * + * @return integer + */ + protected int $attemps = 2; /** * ProducerService constructor @@ -82,6 +89,16 @@ public function getId(): string return $this->id; } + /** + * Get the producer attemps + * + * @return int + */ + public function getAttemps(): int + { + return $this->attemps; + } + /** * Get the producer retry * diff --git a/tests/Config/stubs/queue.php b/tests/Config/stubs/queue.php index b65f6cf2..36fcc733 100644 --- a/tests/Config/stubs/queue.php +++ b/tests/Config/stubs/queue.php @@ -39,7 +39,7 @@ 'secret' => getenv('AWS_SECRET'), ], ], - + /** * The sqs connexion */ diff --git a/tests/Queue/QueueTest.php b/tests/Queue/QueueTest.php index 7e4261dd..47a29f0e 100644 --- a/tests/Queue/QueueTest.php +++ b/tests/Queue/QueueTest.php @@ -3,13 +3,13 @@ namespace Bow\Tests\Queue; use Bow\Cache\Adapter\RedisAdapter; -use Bow\Cache\Cache; use Bow\Cache\CacheConfiguration; use Bow\Configuration\EnvConfiguration; use Bow\Configuration\LoggerConfiguration; use Bow\Database\Database; use Bow\Database\DatabaseConfiguration; use Bow\Queue\Adapters\BeanstalkdAdapter; +use Bow\Queue\Adapters\DatabaseAdapter; use Bow\Queue\Adapters\SQSAdapter; use Bow\Tests\Config\TestingConfiguration; use Bow\Tests\Queue\Stubs\PetModelStub; @@ -39,6 +39,15 @@ public static function setUpBeforeClass(): void Database::connection('mysql'); Database::statement('drop table if exists pets'); Database::statement('create table pets (id int primary key auto_increment, name varchar(255))'); + Database::statement('create table if not exists queues ( + id int primary key auto_increment, + queue varchar(255), + payload text, + status varchar(100), + attempts int, + reserved_at datetime null default null, + created_at datetime + )'); } /** @@ -117,7 +126,7 @@ public function getConnection(): array return [ ["beanstalkd"], ["sqs"], - // ["database"], + ["database"], // ["redis"], // ["rabbitmq"], ]; From 0c97b6e361b24e8e824b38c0a913cd417324ac92 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 22 Sep 2023 10:56:20 +0000 Subject: [PATCH 124/199] Fixes unity tests --- tests/Queue/QueueTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/Queue/QueueTest.php b/tests/Queue/QueueTest.php index 47a29f0e..f77bec42 100644 --- a/tests/Queue/QueueTest.php +++ b/tests/Queue/QueueTest.php @@ -40,11 +40,12 @@ public static function setUpBeforeClass(): void Database::statement('drop table if exists pets'); Database::statement('create table pets (id int primary key auto_increment, name varchar(255))'); Database::statement('create table if not exists queues ( - id int primary key auto_increment, + id varchar(255) primary key, queue varchar(255), payload text, status varchar(100), attempts int, + avalaibled_at datetime null default null, reserved_at datetime null default null, created_at datetime )'); From 2d23c82cfe1c95165894089086e35664dfedfa81 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 22 Sep 2023 12:12:58 +0000 Subject: [PATCH 125/199] Refactoring errors handlers --- src/Application/Exception/BaseErrorHandler.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Application/Exception/BaseErrorHandler.php b/src/Application/Exception/BaseErrorHandler.php index e0c013b5..b1c475f1 100644 --- a/src/Application/Exception/BaseErrorHandler.php +++ b/src/Application/Exception/BaseErrorHandler.php @@ -8,6 +8,8 @@ use Bow\View\View; use Bow\Http\Exception\HttpException; use Bow\Validation\Exception\ValidationException; +use Policier\Exception\TokenExpiredException; +use Policier\Exception\TokenInvalidException; class BaseErrorHandler { @@ -32,6 +34,14 @@ protected function render($view, $data = []): string */ protected function json($exception, $code = null) { + if ($exception instanceof TokenInvalidException) { + $code = 'TOKEN_INVALID'; + } + + if ($exception instanceof TokenExpiredException) { + $code = 'TOKEN_EXPIRED'; + } + if (is_null($code)) { if (method_exists($exception, 'getStatus')) { $code = $exception->getStatus(); @@ -66,6 +76,8 @@ protected function json($exception, $code = null) $response["trace"] = $exception->getTrace(); } - return response()->json($response, $status); + response()->status($status); + + return die(json_encode($response)); } } From 2f4ba401a0fbf9be32bb68b7c50239a55fc527af Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 22 Sep 2023 15:21:14 +0000 Subject: [PATCH 126/199] Update unity tests --- src/Console/Command.php | 1 + ...ueCommand.php => GenerateQueueCommand.php} | 4 +-- src/Console/Console.php | 2 +- tests/Console/GeneratorDeepTest.php | 14 ++++++++ ...test_generate_queue_migration_stubs__1.txt | 35 +++++++++++++++++++ tests/Queue/QueueTest.php | 14 ++++++-- 6 files changed, 64 insertions(+), 6 deletions(-) rename src/Console/Command/{GenerateDatabaseQueueCommand.php => GenerateQueueCommand.php} (87%) create mode 100644 tests/Console/__snapshots__/GeneratorDeepTest__test_generate_queue_migration_stubs__1.txt diff --git a/src/Console/Command.php b/src/Console/Command.php index 586e44c2..a2966093 100644 --- a/src/Console/Command.php +++ b/src/Console/Command.php @@ -37,6 +37,7 @@ class Command extends AbstractCommand "key" => \Bow\Console\Command\GenerateKeyCommand::class, "resource" => \Bow\Console\Command\GenerateResourceControllerCommand::class, "session" => \Bow\Console\Command\GenerateSessionCommand::class, + "queue" => \Bow\Console\Command\GenerateQueueCommand::class, "cache" => \Bow\Console\Command\GenerateCacheCommand::class, ], "runner" => [ diff --git a/src/Console/Command/GenerateDatabaseQueueCommand.php b/src/Console/Command/GenerateQueueCommand.php similarity index 87% rename from src/Console/Command/GenerateDatabaseQueueCommand.php rename to src/Console/Command/GenerateQueueCommand.php index 7ccd603f..b12241a3 100644 --- a/src/Console/Command/GenerateDatabaseQueueCommand.php +++ b/src/Console/Command/GenerateQueueCommand.php @@ -8,7 +8,7 @@ use Bow\Console\Generator; use Bow\Support\Str; -class GenerateDatabaseQueueCommand extends AbstractCommand +class GenerateQueueCommand extends AbstractCommand { /** * Generate session @@ -18,7 +18,7 @@ class GenerateDatabaseQueueCommand extends AbstractCommand public function generate(): void { $create_at = date("YmdHis"); - $filename = sprintf("Version%s%sTable", $create_at, ucfirst(Str::camel('DatabaseQueue'))); + $filename = sprintf("Version%s%sTable", $create_at, ucfirst(Str::camel('queue'))); $generator = new Generator( $this->setting->getMigrationDirectory(), diff --git a/src/Console/Console.php b/src/Console/Console.php index 43cea024..89b77980 100644 --- a/src/Console/Console.php +++ b/src/Console/Console.php @@ -406,7 +406,7 @@ private function generate(): void { $action = $this->arg->getAction(); - if (!in_array($action, ['key', 'resource', 'session', 'cache'])) { + if (!in_array($action, ['key', 'resource', 'session', 'cache', 'queue'])) { $this->throwFailsCommand('This action is not exists', 'help generate'); } diff --git a/tests/Console/GeneratorDeepTest.php b/tests/Console/GeneratorDeepTest.php index 17a9635a..b6fbd8c9 100644 --- a/tests/Console/GeneratorDeepTest.php +++ b/tests/Console/GeneratorDeepTest.php @@ -180,6 +180,20 @@ public function test_generate_session_migration_stubs() $this->assertRegExp("@\nclass\sFakeSessionMigration\sextends\sMigration\n@", $content); } + public function test_generate_queue_migration_stubs() + { + $generator = new Generator(TESTING_RESOURCE_BASE_DIRECTORY, 'QueueTableMigration'); + $content = $generator->makeStubContent('model/queue', [ + "className" => "QueueTableMigration", + ]); + + $this->assertNotNull($content); + $this->assertMatchesSnapshot($content); + $this->assertRegExp("@\nclass\sQueueTableMigration\sextends\sMigration\n@", $content); + $this->assertStringContainsString("\$this->create(\"queues\", function (SQLGenerator \$table) {", $content); + $this->assertStringContainsString("\$table->addInteger('attempts', [\"default\" => 3]);\n", $content); + } + public function test_generate_table_migration_stubs() { $generator = new Generator(TESTING_RESOURCE_BASE_DIRECTORY, 'FakeTableMigration'); diff --git a/tests/Console/__snapshots__/GeneratorDeepTest__test_generate_queue_migration_stubs__1.txt b/tests/Console/__snapshots__/GeneratorDeepTest__test_generate_queue_migration_stubs__1.txt new file mode 100644 index 00000000..9d9715e9 --- /dev/null +++ b/tests/Console/__snapshots__/GeneratorDeepTest__test_generate_queue_migration_stubs__1.txt @@ -0,0 +1,35 @@ +create("queues", function (SQLGenerator $table) { + $table->addString('id', ["primary" => true]); + $table->addString('queue'); + $table->addText('payload'); + $table->addInteger('attempts', ["default" => 3]); + $table->addEnum('status', [ + "size" => ["waiting", "processing", "reserved", "failed", "done"], + "default" => "waiting", + ]); + $table->addDatetime('avalaibled_at'); + $table->addDatetime('reserved_at', ["nullable" => true, "default" => null]); + $table->addDatetime('created_at'); + }); + } + + /** + * Rollback migration + */ + public function rollback(): void + { + $this->dropIfExists("queues"); + } +} diff --git a/tests/Queue/QueueTest.php b/tests/Queue/QueueTest.php index f77bec42..5bceb95d 100644 --- a/tests/Queue/QueueTest.php +++ b/tests/Queue/QueueTest.php @@ -124,12 +124,20 @@ public function test_push_service_adapter_with_model($connection) */ public function getConnection(): array { - return [ + $data = []; + + $data = [ ["beanstalkd"], - ["sqs"], ["database"], + // ["sqs"], // ["redis"], - // ["rabbitmq"], + // ["rabbitmq"] ]; + + if (getenv("AWS_SQS_URL")) { + $data[] = ["sqs"]; + } + + return $data; } } From cf5b06636f1a9289618def8ac2ebb246aec4fcbd Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 22 Sep 2023 15:54:41 +0000 Subject: [PATCH 127/199] Change cache method to store --- src/Cache/Cache.php | 4 ++-- tests/Cache/CacheDatabaseTest.php | 2 +- tests/Cache/CacheFilesystemTest.php | 2 +- tests/Cache/CacheRedisTest.php | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Cache/Cache.php b/src/Cache/Cache.php index 9e51d06f..bf4a4514 100644 --- a/src/Cache/Cache.php +++ b/src/Cache/Cache.php @@ -57,7 +57,7 @@ public static function configure(array $config) static::$config = $config; $store = (array) $config["stores"][$config["default"]]; - return static::cache($store["driver"]); + return static::store($store["driver"]); } /** @@ -80,7 +80,7 @@ public static function getInstance(): CacheAdapterInterface * @param string $driver * @return CacheAdapterInterface */ - public static function cache(string $store): CacheAdapterInterface + public static function store(string $store): CacheAdapterInterface { $stores = static::$config["stores"]; diff --git a/tests/Cache/CacheDatabaseTest.php b/tests/Cache/CacheDatabaseTest.php index c08fdcce..179a1729 100644 --- a/tests/Cache/CacheDatabaseTest.php +++ b/tests/Cache/CacheDatabaseTest.php @@ -23,7 +23,7 @@ public static function setUpBeforeClass(): void )"); Cache::configure($config["cache"]); - Cache::cache("database"); + Cache::store("database"); } public function test_create_cache() diff --git a/tests/Cache/CacheFilesystemTest.php b/tests/Cache/CacheFilesystemTest.php index 2595fca2..d957b2ee 100644 --- a/tests/Cache/CacheFilesystemTest.php +++ b/tests/Cache/CacheFilesystemTest.php @@ -12,7 +12,7 @@ protected function setUp(): void parent::setUp(); $config = TestingConfiguration::getConfig(); Cache::configure($config["cache"]); - Cache::cache("file"); + Cache::store("file"); } public function test_create_cache() diff --git a/tests/Cache/CacheRedisTest.php b/tests/Cache/CacheRedisTest.php index 2e8508c0..d24d8aae 100644 --- a/tests/Cache/CacheRedisTest.php +++ b/tests/Cache/CacheRedisTest.php @@ -12,7 +12,7 @@ protected function setUp(): void parent::setUp(); $config = TestingConfiguration::getConfig(); Cache::configure($config["cache"]); - Cache::cache("redis"); + Cache::store("redis"); } public function test_create_cache() From 63a9e3d12a7d5cfc9b46b15128212e8c23393adc Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 22 Sep 2023 16:20:51 +0000 Subject: [PATCH 128/199] Update cache method --- src/Cache/README.md | 2 +- src/Database/Barry/Relations/HasOne.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Cache/README.md b/src/Cache/README.md index a1229c62..5c4e9f0c 100644 --- a/src/Cache/README.md +++ b/src/Cache/README.md @@ -16,7 +16,7 @@ $content = cache("name"); By specifying the driver: ``` -$content = Cache::cache('redis')->get('name'); +$content = Cache::store('redis')->get('name'); ``` Is very enjoyful api diff --git a/src/Database/Barry/Relations/HasOne.php b/src/Database/Barry/Relations/HasOne.php index 535b4762..1b254719 100644 --- a/src/Database/Barry/Relations/HasOne.php +++ b/src/Database/Barry/Relations/HasOne.php @@ -50,7 +50,7 @@ public function __construct(Model $related, Model $parent, string $foreign_key, public function getResults(): ?Model { $key = $this->query->getTable() . ":hasone:" . $this->related->getTable() . ":" . $this->foreign_key; - $cache = Cache::cache('file')->get($key); + $cache = Cache::store('file')->get($key); if (!is_null($cache)) { $related = new $this->related(); @@ -61,7 +61,7 @@ public function getResults(): ?Model $result = $this->query->first(); if (!is_null($result)) { - Cache::cache('file')->add($key, $result->toArray(), 500); + Cache::store('file')->add($key, $result->toArray(), 60); } return $result; From 43f27ab5cb70016c202272bbb9b6de8c36295e13 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 22 Sep 2023 16:30:15 +0000 Subject: [PATCH 129/199] Update unity tests --- src/Database/Barry/Relations/BelongsTo.php | 4 ++-- tests/Database/Relation/BelongsToRelationQueryTest.php | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Database/Barry/Relations/BelongsTo.php b/src/Database/Barry/Relations/BelongsTo.php index 6e1ab70d..f48b71fe 100644 --- a/src/Database/Barry/Relations/BelongsTo.php +++ b/src/Database/Barry/Relations/BelongsTo.php @@ -52,7 +52,7 @@ public function __construct( public function getResults(): ?Model { $key = $this->query->getTable() . ":belongsto:" . $this->related->getTable() . ":" . $this->foreign_key; - $cache = Cache::cache('file')->get($key); + $cache = Cache::store('file')->get($key); if (!is_null($cache)) { $related = new $this->related(); @@ -63,7 +63,7 @@ public function getResults(): ?Model $result = $this->query->first(); if (!is_null($result)) { - Cache::cache('file')->add($key, $result->toArray(), 500); + Cache::store('file')->add($key, $result->toArray(), 500); } return $result; diff --git a/tests/Database/Relation/BelongsToRelationQueryTest.php b/tests/Database/Relation/BelongsToRelationQueryTest.php index d983bcb2..d17be65a 100644 --- a/tests/Database/Relation/BelongsToRelationQueryTest.php +++ b/tests/Database/Relation/BelongsToRelationQueryTest.php @@ -2,12 +2,13 @@ namespace Bow\Tests\Database\Relation; +use Bow\Cache\Cache; use Bow\Database\Database; use Bow\Database\Migration\SQLGenerator; use Bow\Tests\Config\TestingConfiguration; -use Bow\Tests\Database\Stubs\MigrationExtendedStub; -use Bow\Tests\Database\Stubs\PetMasterModelStub; use Bow\Tests\Database\Stubs\PetModelStub; +use Bow\Tests\Database\Stubs\PetMasterModelStub; +use Bow\Tests\Database\Stubs\MigrationExtendedStub; class BelongsToRelationQueryTest extends \PHPUnit\Framework\TestCase { @@ -15,6 +16,7 @@ public static function setUpBeforeClass(): void { $config = TestingConfiguration::getConfig(); Database::configure($config["database"]); + Cache::configure($config["cache"]); } public function connectionNames() From a1f0bdd9df3285d72390544706436ba2c905fffa Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sat, 23 Sep 2023 09:33:51 +0000 Subject: [PATCH 130/199] Add onException method for catch job error --- src/Queue/Adapters/BeanstalkdAdapter.php | 15 ++++++++++++++- src/Queue/Adapters/DatabaseAdapter.php | 18 ++++++++++++++++-- src/Queue/Adapters/SQSAdapter.php | 15 +++++++++++++++ src/Queue/ProducerService.php | 11 +++++++++++ 4 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/Queue/Adapters/BeanstalkdAdapter.php b/src/Queue/Adapters/BeanstalkdAdapter.php index d0bb2c10..f65e8039 100644 --- a/src/Queue/Adapters/BeanstalkdAdapter.php +++ b/src/Queue/Adapters/BeanstalkdAdapter.php @@ -151,9 +151,22 @@ public function run(string $queue = null): void $this->sleep(2); $this->pheanstalk->delete($job); } catch (\Throwable $e) { + // Write the error log error_log($e->getMessage()); app('logger')->error($e->getMessage(), $e->getTrace()); - cache("failed:job:" . $job->getId(), $job->getData()); + cache("job:failed:" . $job->getId(), $job->getData()); + + // Check if producer has been loaded + if (!isset($producer)) { + $this->pheanstalk->delete($job); + return; + } + + // Execute the onException method for notify the producer + // and let developper to decide if the job should be delete + $producer->onException($e); + + // Check if the job should be delete if ($producer->jobShouldBeDelete()) { $this->pheanstalk->delete($job); } else { diff --git a/src/Queue/Adapters/DatabaseAdapter.php b/src/Queue/Adapters/DatabaseAdapter.php index f4264b4b..a25ce2de 100644 --- a/src/Queue/Adapters/DatabaseAdapter.php +++ b/src/Queue/Adapters/DatabaseAdapter.php @@ -95,24 +95,38 @@ public function run(string $queue = null): void continue; } } catch (\Exception $e) { + // Write the error log error_log($e->getMessage()); app('logger')->error($e->getMessage(), $e->getTrace()); - cache("failed:job:" . $job->id, $job->payload); + cache("job:failed:" . $job->id, $job->payload); + + // Check if producer has been loaded if (!isset($producer)) { $this->sleep(1); continue; } + + // Execute the onException method for notify the producer + // and let developper to decide if the job should be delete + $producer->onException($e); + + // Check if the job should be delete if ($producer->jobShouldBeDelete() || $job->attempts <= 0) { - $this->table->where("id", $job->id)->delete(); + $this->table->where("id", $job->id)->update([ + "status" => "failed", + ]); $this->sleep(1); continue; } + + // Check if the job should be retry $this->table->where("id", $job->id)->update([ "status" => "reserved", "attempts" => $job->attempts - 1, "avalaibled_at" => date("Y-m-d H:i:s", time() + $producer->getDelay()), "reserved_at" => date("Y-m-d H:i:s", time() + $producer->getRetry()) ]); + $this->sleep(1); } } diff --git a/src/Queue/Adapters/SQSAdapter.php b/src/Queue/Adapters/SQSAdapter.php index cbb4ef22..59c9cd6f 100644 --- a/src/Queue/Adapters/SQSAdapter.php +++ b/src/Queue/Adapters/SQSAdapter.php @@ -124,12 +124,27 @@ public function run(?string $queue = null): void 'ReceiptHandle' => $message['ReceiptHandle'] ]); } catch (AwsException $e) { + // Write the error log error_log($e->getMessage()); app('logger')->error($e->getMessage(), $e->getTrace()); + + if (isset($message)) { + cache("job:failed:" . $message["ReceiptHandle"], + $message["Body"] + ); + } + + // Check if producer has been loaded if (!isset($producer)) { $this->sleep(1); return; } + + // Execute the onException method for notify the producer + // and let developper to decide if the job should be delete + $producer->onException($e); + + // Check if the job should be delete if ($producer->jobShouldBeDelete()) { $result = $this->sqs->deleteMessage([ 'QueueUrl' => $this->config["url"], diff --git a/src/Queue/ProducerService.php b/src/Queue/ProducerService.php index 31941a97..f00011e9 100644 --- a/src/Queue/ProducerService.php +++ b/src/Queue/ProducerService.php @@ -149,6 +149,17 @@ public function jobShouldBeDelete(): bool return $this->delete; } + /** + * Get the job error + * + * @param \Throwable $e + * @return void + */ + public function onException(\Throwable $e) + { + // + } + /** * Process the producer * From 929d7f0112f3b91ed77bd9123c11ecc1d25b5eb6 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sat, 23 Sep 2023 10:15:56 +0000 Subject: [PATCH 131/199] Update mail component --- src/Mail/Driver/NativeDriver.php | 1 - src/Mail/Driver/SesDriver.php | 21 ++++++++-- src/Mail/Driver/SmtpDriver.php | 1 + src/Mail/Mail.php | 70 +++++++++++++++++++++++--------- src/Mail/Message.php | 10 +++++ 5 files changed, 78 insertions(+), 25 deletions(-) diff --git a/src/Mail/Driver/NativeDriver.php b/src/Mail/Driver/NativeDriver.php index 62b8cffb..db1a2d92 100644 --- a/src/Mail/Driver/NativeDriver.php +++ b/src/Mail/Driver/NativeDriver.php @@ -6,7 +6,6 @@ use Bow\Mail\Contracts\MailDriverInterface; use Bow\Mail\Message; -use Bow\Support\Str; use Bow\Mail\Exception\MailException; use InvalidArgumentException; diff --git a/src/Mail/Driver/SesDriver.php b/src/Mail/Driver/SesDriver.php index d528fe8f..e9380455 100644 --- a/src/Mail/Driver/SesDriver.php +++ b/src/Mail/Driver/SesDriver.php @@ -30,12 +30,25 @@ class SesDriver implements MailDriverInterface * @param array $config * @return void */ - public function __construct(array $config) + public function __construct(private array $config) { - $this->config_set = $config["config_set"] ?? false; - unset($config["config_set"]); + $this->config_set = $this->config["config_set"] ?? false; - $this->ses = new SesClient($config); + unset($this->config["config_set"]); + + $this->initializeSesClient(); + } + + /** + * Get the SES Instance + * + * @return SesClient + */ + public function initializeSesClient(): SesClient + { + $this->ses = new SesClient($this->config); + + return $this->ses; } /** diff --git a/src/Mail/Driver/SmtpDriver.php b/src/Mail/Driver/SmtpDriver.php index c6d5a154..7f196c83 100644 --- a/src/Mail/Driver/SmtpDriver.php +++ b/src/Mail/Driver/SmtpDriver.php @@ -137,6 +137,7 @@ public function send(Message $message): bool try { $this->write('.', 250); } catch (SmtpException $e) { + app("logger")->error($e->getMessage(), $e->getTraceAsString()); error_log($e->getMessage()); } diff --git a/src/Mail/Mail.php b/src/Mail/Mail.php index 43734a40..ea621916 100644 --- a/src/Mail/Mail.php +++ b/src/Mail/Mail.php @@ -8,6 +8,11 @@ use Bow\Mail\Exception\MailException; use Bow\View\View; +/** + * @method mixed view(string $template, array $data, callable $cb) + * @method mixed send($view, array|callable $data, ?callable $cb = null) + * @method mixed raw(string|array $to, string $subject, string $data, array $headers = []) + */ class Mail { /** @@ -76,8 +81,8 @@ public static function configure(array $config = []): MailDriverInterface /** * Push new driver * - * @param strinb $name - * @param strinb $class_name + * @param string $name + * @param string $class_name * @return bool */ public function pushDriver(string $name, string $class_name): bool @@ -104,18 +109,17 @@ public static function getInstance(): MailDriverInterface /** * @inheritdoc */ - public static function send($view, $bind, callable $cb) + public static function send(string $view, callable|array $data, ?callable $cb = null) { - if (is_callable($bind)) { - $cb = $bind; - $bind = []; + if (is_null($cb)) { + $cb = $data; + $data = []; } - $message = new Message(); - - $data = View::parse($view, $bind)->getContent(); + $content = View::parse($view, $data)->getContent(); - $message->setMessage($data); + $message = new Message(); + $message->setMessage($content); call_user_func_array($cb, [$message]); @@ -131,11 +135,9 @@ public static function send($view, $bind, callable $cb) * @param array $headers * @return mixed */ - public static function raw($to, $subject, $data, array $headers = []) + public static function raw(string|array $to, string $subject, string $data, array $headers = []) { - if (!is_array($to)) { - $to = [$to]; - } + $to = (array) $to; $message = new Message(); @@ -149,20 +151,45 @@ public static function raw($to, $subject, $data, array $headers = []) } /** - * Modify the smtp|mail driver + * Send mail similar to the PHP mail function + * + * @param string $template + * @param array $data + * @param callable $cb + * @return mixed + */ + public static function view(string $template, array $data, callable $cb) + { + $message = new Message(); + + $data = View::parse($template, $data)->getContent(); + + $message->setMessage($data); + + call_user_func_array($cb, [$message]); + + return static::$instance->send($message); + } + + /** + * Modify the smtp|mail|ses driver * * @param string $driver - * @return SendInterface + * @return MailDriverInterface * @throws MailException */ - public static function setDriver($driver) + public static function setDriver(string $driver): MailDriverInterface { if (static::$config == null) { - throw new MailException('Mail non configurer.'); + throw new MailException( + 'Please configure the Mail service.' + ); } if (in_array($driver, array_keys(static::$drivers))) { - throw new MailException('The driver [$driver] is not available'); + throw new MailException( + "The driver $driver is not available" + ); } static::$config['driver'] = $driver; @@ -184,6 +211,9 @@ public function __call($name, $arguments) return call_user_func_array([static::class, $name], $arguments); } - throw new \ErrorException("This function does not exist. [$name]", E_ERROR); + throw new \ErrorException( + "This function $name does not existe", + E_ERROR + ); } } diff --git a/src/Mail/Message.php b/src/Mail/Message.php index 39658375..666ecad6 100644 --- a/src/Mail/Message.php +++ b/src/Mail/Message.php @@ -397,6 +397,16 @@ public function setMessage(string $message, string $type = 'text/html') $this->message = $message; } + /** + * @see setMessage + * @param string $message + * @param string $type + */ + public function message(string $message, string $type = 'text/html') + { + $this->setMessage($message, $type); + } + /** * Get the headers * From 9b5787bef23e4bcaf8489012dd1985765da4b43b Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sat, 23 Sep 2023 10:16:59 +0000 Subject: [PATCH 132/199] Refactoring --- src/Queue/Adapters/DatabaseAdapter.php | 2 +- src/Queue/Adapters/SQSAdapter.php | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Queue/Adapters/DatabaseAdapter.php b/src/Queue/Adapters/DatabaseAdapter.php index a25ce2de..83b5fb1c 100644 --- a/src/Queue/Adapters/DatabaseAdapter.php +++ b/src/Queue/Adapters/DatabaseAdapter.php @@ -105,7 +105,7 @@ public function run(string $queue = null): void $this->sleep(1); continue; } - + // Execute the onException method for notify the producer // and let developper to decide if the job should be delete $producer->onException($e); diff --git a/src/Queue/Adapters/SQSAdapter.php b/src/Queue/Adapters/SQSAdapter.php index 59c9cd6f..4d2ebc71 100644 --- a/src/Queue/Adapters/SQSAdapter.php +++ b/src/Queue/Adapters/SQSAdapter.php @@ -129,7 +129,8 @@ public function run(?string $queue = null): void app('logger')->error($e->getMessage(), $e->getTrace()); if (isset($message)) { - cache("job:failed:" . $message["ReceiptHandle"], + cache( + "job:failed:" . $message["ReceiptHandle"], $message["Body"] ); } From 03f346f67df7c04a1bd7c0e8e4a48cae3dd8d8e3 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sat, 23 Sep 2023 12:05:41 +0000 Subject: [PATCH 133/199] formatting --- src/Container/Action.php | 17 ++++++++--------- src/Container/Capsule.php | 16 ++++++++-------- src/Database/QueryBuilder.php | 1 - src/Http/Request.php | 6 ++++-- src/Http/Response.php | 6 +++--- src/Http/ServerAccessControl.php | 6 +++--- src/Http/UploadFile.php | 4 ++-- src/Mail/Driver/SmtpDriver.php | 6 +++--- src/Mail/Mail.php | 11 ++++++++++- src/Middleware/AuthMiddleware.php | 2 +- src/Middleware/CsrfMiddleware.php | 10 +++++++--- src/Session/Driver/DatabaseDriver.php | 1 - src/Session/Driver/FilesystemDriver.php | 2 +- src/Session/Session.php | 13 +++++++++---- src/Storage/Service/DiskFilesystemService.php | 2 +- src/Storage/Service/FTPService.php | 6 +++--- src/Storage/Service/S3Service.php | 12 ++++++------ src/Storage/Storage.php | 4 +++- src/Support/Collection.php | 2 +- src/Support/Str.php | 14 +++++++------- src/Testing/Features/SeedingHelper.php | 2 +- src/Translate/TranslatorConfiguration.php | 2 +- src/Validation/RequestValidation.php | 12 +++++++----- 23 files changed, 89 insertions(+), 68 deletions(-) diff --git a/src/Container/Action.php b/src/Container/Action.php index 7b8b759d..496ac82c 100644 --- a/src/Container/Action.php +++ b/src/Container/Action.php @@ -214,7 +214,6 @@ public function call(callable|string|array $actions, ?array $param = null): mixe if (!array_key_exists($middleware, $this->middlewares)) { throw new RouterException( sprintf('%s is not define middleware.', $middleware), - E_ERROR ); } @@ -222,7 +221,6 @@ public function call(callable|string|array $actions, ?array $param = null): mixe if (!class_exists($this->middlewares[$middleware])) { throw new RouterException( sprintf('%s is not a middleware class.', $middleware), - E_ERROR ); } @@ -243,7 +241,8 @@ public function call(callable|string|array $actions, ?array $param = null): mixe case is_string($response): case is_array($response): case is_object($response): - case $response instanceof \Iterable: + case is_iterable($response): + case $response instanceof \Iterator: case $response instanceof ResponseInterface: return $response; case $response instanceof Model || $response instanceof Collection: @@ -315,19 +314,19 @@ private function dispatchControllers(array $functions, array $params): mixed /** * Successively launches a function list. * - * @param array|callable $function - * @param array $arg + * @param array|callable|string $function + * @param array $arguments * @return mixed * @throws ReflectionException */ - public function execute(array|callable $function, array $arg): mixed + public function execute(array|callable|string $function, array $arguments): mixed { if (is_callable($function)) { - return call_user_func_array($function, $arg); + return call_user_func_array($function, $arguments); } if (is_array($function)) { - return call_user_func_array($function, $arg); + return call_user_func_array($function, $arguments); } // We launch the controller loader if $cb is a String @@ -340,7 +339,7 @@ public function execute(array|callable $function, array $arg): mixed if (is_array($controller)) { return call_user_func_array( $controller['action'], - array_merge($controller['injection'], $arg) + array_merge($controller['injection'], $arguments) ); } diff --git a/src/Container/Capsule.php b/src/Container/Capsule.php index cd832307..3c146174 100644 --- a/src/Container/Capsule.php +++ b/src/Container/Capsule.php @@ -145,11 +145,10 @@ public function bind(string $key, callable $value) * Register the instance of a class * * @param string $key - * @param Closure $value - * + * @param Closure|callable $value * @return void */ - public function factory($key, \Closure $value) + public function factory($key, Closure|callable $value) { $this->factories[$key] = $value; } @@ -157,15 +156,16 @@ public function factory($key, \Closure $value) /** * Saves the instance of a class * - * @param string $key + * @param string $key * @param mixed $instance - * * @return void */ - public function instance($key, $instance) + public function instance(string $key, mixed $instance): void { if (!is_object($instance)) { - throw new InvalidArgumentException('Parameter [2] is invalid'); + throw new InvalidArgumentException( + "The parameter $instance must be an object." + ); } $this->instances[$key] = $instance; @@ -178,7 +178,7 @@ public function instance($key, $instance) * @return mixed * @throws */ - private function resolve($key) + private function resolve($key): mixed { $reflection = new ReflectionClass($key); diff --git a/src/Database/QueryBuilder.php b/src/Database/QueryBuilder.php index 458017a9..04d0bceb 100644 --- a/src/Database/QueryBuilder.php +++ b/src/Database/QueryBuilder.php @@ -6,7 +6,6 @@ use Bow\Database\Connection\AbstractConnection; use PDO; -use stdClass; use PDOStatement; use Bow\Support\Str; use Bow\Support\Util; diff --git a/src/Http/Request.php b/src/Http/Request.php index c148ef4f..4aec3469 100644 --- a/src/Http/Request.php +++ b/src/Http/Request.php @@ -56,7 +56,9 @@ private function __construct() try { $data = json_decode(file_get_contents("php://input"), true, 1024, JSON_THROW_ON_ERROR); } catch (\Throwable $e) { - throw new BadRequestException("Invalid JSON syntax"); + throw new BadRequestException( + "The request json payload is invalid: " . $e->getMessage(), + ); } $this->input = array_merge((array) $data, $_GET); } else { @@ -649,7 +651,7 @@ public function getBag(string $name) /** * Set the shared value in request bags * - * @param Array $bags + * @param array $bags * @return mixed */ public function setBags(array $bags) diff --git a/src/Http/Response.php b/src/Http/Response.php index 1c8019ac..a7637004 100644 --- a/src/Http/Response.php +++ b/src/Http/Response.php @@ -237,9 +237,9 @@ public function addHeaders(array $headers): Response /** * Download the given file as an argument * - * @param string $file - * @param null $filename - * @param array $headers + * @param string $file + * @param ?string $filename + * @param array $headers * @return string */ public function download( diff --git a/src/Http/ServerAccessControl.php b/src/Http/ServerAccessControl.php index 1ce4a4b5..8380488a 100644 --- a/src/Http/ServerAccessControl.php +++ b/src/Http/ServerAccessControl.php @@ -110,11 +110,11 @@ public function allowCredentials(): ServerAccessControl /** * Active Access-control-Max-Age * - * @param string $excepted + * @param float|int $excepted * @return ServerAccessControl * @throws ServerAccessControlException */ - public function maxAge(string $excepted): ServerAccessControl + public function maxAge(float|int $excepted): ServerAccessControl { if (!is_numeric($excepted)) { throw new ServerAccessControlException( @@ -123,7 +123,7 @@ public function maxAge(string $excepted): ServerAccessControl ); } - return $this->push('Access-Control-Max-Age', $excepted); + return $this->push('Access-Control-Max-Age', (string) $excepted); } /** diff --git a/src/Http/UploadFile.php b/src/Http/UploadFile.php index f50d1241..7618e0af 100644 --- a/src/Http/UploadFile.php +++ b/src/Http/UploadFile.php @@ -24,9 +24,9 @@ public function __construct(array $file) /** * Get the file extension * - * @return string + * @return ?string */ - public function getExtension(): string + public function getExtension(): ?string { if (!isset($this->file['name'])) { return null; diff --git a/src/Mail/Driver/SmtpDriver.php b/src/Mail/Driver/SmtpDriver.php index 7f196c83..0e4d8001 100644 --- a/src/Mail/Driver/SmtpDriver.php +++ b/src/Mail/Driver/SmtpDriver.php @@ -226,9 +226,9 @@ private function disconnect() /** * Read the current connection stream. * - * @return string + * @return int */ - private function read() + private function read(): int { $s = null; @@ -255,7 +255,7 @@ private function read() * @param ?string $message * * @throws SmtpException - * @return string + * @return string|int|null */ private function write(string $command, ?int $code = null, ?string $message = null) { diff --git a/src/Mail/Mail.php b/src/Mail/Mail.php index ea621916..3dc0f7db 100644 --- a/src/Mail/Mail.php +++ b/src/Mail/Mail.php @@ -64,8 +64,17 @@ public static function configure(array $config = []): MailDriverInterface static::$config = $config; } + if (!isset($config['driver'])) { + throw new MailException( + "The driver is not defined.", E_USER_ERROR + ); + + } + if (!in_array($config['driver'], array_keys(static::$drivers))) { - throw new MailException("The type is not known.", E_USER_ERROR); + throw new MailException( + "The driver is not defined.", E_USER_ERROR + ); } $name = $config['driver']; diff --git a/src/Middleware/AuthMiddleware.php b/src/Middleware/AuthMiddleware.php index 7c53ddd1..3a73bf79 100644 --- a/src/Middleware/AuthMiddleware.php +++ b/src/Middleware/AuthMiddleware.php @@ -15,7 +15,7 @@ class AuthMiddleware implements BaseMiddleware * Handle an incoming request. * * @param Request $request - * @param Callable $next + * @param callable $next * @param array $args * @return Redirect */ diff --git a/src/Middleware/CsrfMiddleware.php b/src/Middleware/CsrfMiddleware.php index 8eba888b..72eaf001 100644 --- a/src/Middleware/CsrfMiddleware.php +++ b/src/Middleware/CsrfMiddleware.php @@ -14,7 +14,7 @@ class CsrfMiddleware implements BaseMiddleware * Handle an incoming request. * * @param Request $request - * @param Callable $next + * @param callable $next * @param array $args * @throws */ @@ -33,14 +33,18 @@ public function process(Request $request, callable $next, array $args = []): mix response()->status(401); - throw new TokenMismatch('Token Mismatch'); + throw new TokenMismatch( + 'The request csrf token mismatch' + ); } if ($request->get('_token') == $request->session()->get('_token')) { return $next($request); } - throw new TokenMismatch('Token Mismatch'); + throw new TokenMismatch( + 'The request csrf token mismatch' + ); } /** diff --git a/src/Session/Driver/DatabaseDriver.php b/src/Session/Driver/DatabaseDriver.php index 9324ecb4..06299e40 100644 --- a/src/Session/Driver/DatabaseDriver.php +++ b/src/Session/Driver/DatabaseDriver.php @@ -4,7 +4,6 @@ namespace Bow\Session\Driver; -use Bow\Support\Capsule; use Bow\Database\QueryBuilder; use Bow\Database\Database as DB; diff --git a/src/Session/Driver/FilesystemDriver.php b/src/Session/Driver/FilesystemDriver.php index 50e240f4..79f0a1c5 100644 --- a/src/Session/Driver/FilesystemDriver.php +++ b/src/Session/Driver/FilesystemDriver.php @@ -56,7 +56,7 @@ public function destroy(string $session_id): bool * @param int $maxlifetime * @return bool */ - public function gc(int $maxlifetime): int + public function gc(int $maxlifetime): bool { foreach (glob($this->save_path . "/*") as $file) { if (filemtime($file) + $maxlifetime < $this->createTimestamp() && file_exists($file)) { diff --git a/src/Session/Session.php b/src/Session/Session.php index 205c1336..f4950062 100644 --- a/src/Session/Session.php +++ b/src/Session/Session.php @@ -159,7 +159,9 @@ private function initializeDriver(): void $driver = $this->driver[$this->config['driver']] ?? null; if (is_null($driver)) { - throw new SessionException('The driver ' . $this->config['driver'] . ' is not valid'); + throw new SessionException( + 'The driver ' . $this->config['driver'] . ' is not valid' + ); } switch ($this->config['driver']) { @@ -174,13 +176,16 @@ private function initializeDriver(): void $handler = new $driver(); break; default: - throw new SessionException('Cannot set the session driver'); - break; + throw new SessionException( + 'Cannot set the session driver' + ); } // Set the session driver if (!@session_set_save_handler($handler, true)) { - throw new SessionException('Cannot set the session driver'); + throw new SessionException( + 'Cannot set the session driver' + ); } } diff --git a/src/Storage/Service/DiskFilesystemService.php b/src/Storage/Service/DiskFilesystemService.php index 4320fcc1..14928935 100644 --- a/src/Storage/Service/DiskFilesystemService.php +++ b/src/Storage/Service/DiskFilesystemService.php @@ -237,7 +237,7 @@ public function copy(string $target, string $source): bool } if (!$this->exists($source)) { - $this->makeDirectory(dirname($source), true); + $this->makeDirectory(dirname($source)); } return (bool) file_put_contents($source, $this->get($target)); diff --git a/src/Storage/Service/FTPService.php b/src/Storage/Service/FTPService.php index 523fc1f0..9b45c3db 100644 --- a/src/Storage/Service/FTPService.php +++ b/src/Storage/Service/FTPService.php @@ -232,7 +232,7 @@ public function store(UploadFile $file, ?string $location = null, array $option rewind($stream); // - $result = $this->writeStream($location, $stream, $option); + $result = $this->writeStream($location, $stream); fclose($stream); if ($result === false) { @@ -358,7 +358,7 @@ public function makeDirectory(string $dirname, int $mode = 0777): bool $directories = explode('/', $dirname); foreach ($directories as $directory) { - if (false === $this->makeActualDirectory($directory, $mode)) { + if (false === $this->makeActualDirectory($directory)) { $this->setConnectionRoot(); return false; } @@ -405,7 +405,7 @@ protected function makeActualDirectory(string $directory): bool public function get(string $filename): ?string { if (!$stream = $this->readStream($filename)) { - return false; + return null; } $contents = stream_get_contents($stream); diff --git a/src/Storage/Service/S3Service.php b/src/Storage/Service/S3Service.php index 80ef84bb..85b9ffc2 100644 --- a/src/Storage/Service/S3Service.php +++ b/src/Storage/Service/S3Service.php @@ -127,22 +127,22 @@ public function prepend(string $filename, string $content): bool * @param string $content * @param array $options * - * @return bool + * @return mixed */ - public function put(string $file, string $content, array $options = []): bool + public function put(string $file, string $content, array $options = []): mixed { $options = is_string($options) ? ['visibility' => $options] : (array) $options; - $this->client->putObject([ + $result = $this->client->putObject([ 'Bucket' => $this->config['bucket'], 'Key' => $file, 'Body' => $content, "Visibility" => $options["visibility"] ?? 'public' ]); - return true; + return $result; } /** @@ -221,9 +221,9 @@ public function makeDirectory(string $bucket, int $mode = 0777, array $option = * Recover the contents of the file * * @param string $filename - * @return null|string + * @return ?string */ - public function get(string $filename): string + public function get(string $filename): ?string { $result = $this->client->getObject([ 'Bucket' => $this->config['bucket'], diff --git a/src/Storage/Storage.php b/src/Storage/Storage.php index 631eb283..b401b8e1 100644 --- a/src/Storage/Storage.php +++ b/src/Storage/Storage.php @@ -185,6 +185,8 @@ public static function __callStatic($name, array $arguments) return call_user_func_array([static::$disk, $name], $arguments); } - throw new BadMethodCallException("unkdown $name method"); + throw new BadMethodCallException( + "The method $name is not defined" + ); } } diff --git a/src/Support/Collection.php b/src/Support/Collection.php index d8840a89..3f664c54 100644 --- a/src/Support/Collection.php +++ b/src/Support/Collection.php @@ -695,7 +695,7 @@ public function __toString() /** * jsonSerialize * - * @return string + * @return mixed */ public function jsonSerialize(): mixed { diff --git a/src/Support/Str.php b/src/Support/Str.php index c887c599..5dd97ea9 100644 --- a/src/Support/Str.php +++ b/src/Support/Str.php @@ -100,21 +100,21 @@ public static function plurial(string $str): string * slice * * @param string $str - * @param string $start - * @param string|null $end + * @param int $start + * @param int $length * @return string */ - public static function slice(string $str, int $start, ?int $end = null) + public static function slice(string $str, int $start, ?int $length = null) { $sliceStr = ''; if (is_string($str)) { - if ($end === null) { - $end = static::len($str); + if ($length === null) { + $length = static::len($str); } - if ($start < $end) { - $sliceStr = mb_substr($str, $start, $end, 'UTF-8'); + if ($start < $length) { + $sliceStr = mb_substr($str, $start, $length, 'UTF-8'); } } diff --git a/src/Testing/Features/SeedingHelper.php b/src/Testing/Features/SeedingHelper.php index 0031cac4..557db6fc 100644 --- a/src/Testing/Features/SeedingHelper.php +++ b/src/Testing/Features/SeedingHelper.php @@ -15,6 +15,6 @@ trait SeedingHelper */ public function seed(string $seeder, array $data = []): int { - return seed($seeder, $data); + return db_seed($seeder, $data); } } diff --git a/src/Translate/TranslatorConfiguration.php b/src/Translate/TranslatorConfiguration.php index cdf28e25..8130d656 100644 --- a/src/Translate/TranslatorConfiguration.php +++ b/src/Translate/TranslatorConfiguration.php @@ -28,7 +28,7 @@ public function create(Loader $config): void } } - return Translator::configure($lang, $dictionary, $auto_detected); + return Translator::configure($lang, $dictionary); }); } diff --git a/src/Validation/RequestValidation.php b/src/Validation/RequestValidation.php index b1e68b0c..c8c09351 100644 --- a/src/Validation/RequestValidation.php +++ b/src/Validation/RequestValidation.php @@ -4,8 +4,9 @@ namespace Bow\Validation; -use BadMethodCallException; use Bow\Http\Request; +use BadMethodCallException; +use Bow\Validation\Exception\ValidationException; use Bow\Validation\Exception\AuthorizationException; abstract class RequestValidation @@ -51,7 +52,7 @@ public function __construct() if ((count($keys) == 1 && $keys[0] === '*') || count($keys) == 0) { $this->data = $this->request->all(); } else { - $this->data = $this->request->excepts($keys); + $this->data = $this->request->only($keys); } $this->validate = Validator::make($this->data, $this->rules(), $this->messages()); @@ -109,10 +110,9 @@ protected function messages() /** * Send fails authorization * - * @param mixed $response * @throws AuthorizationException */ - private function sendFailAuthorization($response = null) + private function sendFailAuthorization() { throw new AuthorizationException( 'You do not have permission to make a request' @@ -225,7 +225,9 @@ public function __call($name, array $arguments) return call_user_func_array([$this->request, $name], $arguments); } - throw new BadMethodCallException('The method ' . $name . ' does not define.'); + throw new BadMethodCallException( + 'The method ' . $name . ' does not defined.' + ); } /** From dcf099f8af2d1c67e96fc42d8df796a5a2843a30 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sat, 23 Sep 2023 12:06:21 +0000 Subject: [PATCH 134/199] lint --- src/Mail/Mail.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Mail/Mail.php b/src/Mail/Mail.php index 3dc0f7db..920fb689 100644 --- a/src/Mail/Mail.php +++ b/src/Mail/Mail.php @@ -66,14 +66,15 @@ public static function configure(array $config = []): MailDriverInterface if (!isset($config['driver'])) { throw new MailException( - "The driver is not defined.", E_USER_ERROR + "The driver is not defined.", + E_USER_ERROR ); - } if (!in_array($config['driver'], array_keys(static::$drivers))) { throw new MailException( - "The driver is not defined.", E_USER_ERROR + "The driver is not defined.", + E_USER_ERROR ); } From c6ce0d56fa8c94be2f6b5853f774cd233931b359 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sat, 23 Sep 2023 16:44:15 +0000 Subject: [PATCH 135/199] Fixs migration stubs --- src/Console/stubs/model/queue.stub | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Console/stubs/model/queue.stub b/src/Console/stubs/model/queue.stub index 066d2c40..02a9bbaf 100644 --- a/src/Console/stubs/model/queue.stub +++ b/src/Console/stubs/model/queue.stub @@ -31,5 +31,8 @@ class {className} extends Migration public function rollback(): void { $this->dropIfExists("queues"); + if ($this->adapter->getName() === 'pgsql') { + $this->addSql("DROP TYPE IF EXISTS queue_status"); + } } } From ca59301ae8ac08f470135346d0c82d012e4ee8b0 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sat, 23 Sep 2023 16:45:31 +0000 Subject: [PATCH 136/199] Update unity tests --- ...neratorDeepTest__test_generate_queue_migration_stubs__1.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/Console/__snapshots__/GeneratorDeepTest__test_generate_queue_migration_stubs__1.txt b/tests/Console/__snapshots__/GeneratorDeepTest__test_generate_queue_migration_stubs__1.txt index 9d9715e9..0c85a498 100644 --- a/tests/Console/__snapshots__/GeneratorDeepTest__test_generate_queue_migration_stubs__1.txt +++ b/tests/Console/__snapshots__/GeneratorDeepTest__test_generate_queue_migration_stubs__1.txt @@ -31,5 +31,8 @@ class QueueTableMigration extends Migration public function rollback(): void { $this->dropIfExists("queues"); + if ($this->adapter->getName() === 'pgsql') { + $this->addSql("DROP TYPE IF EXISTS queue_status"); + } } } From 6952ecc65642f924c8a68a289a82228fb0ecc8b8 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sun, 24 Sep 2023 12:27:27 +0000 Subject: [PATCH 137/199] Update readme.md --- readme.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index 027fc11f..f60325f2 100644 --- a/readme.md +++ b/readme.md @@ -8,7 +8,7 @@ To use this package, please create an application from this package [bowphp/app] ## The Framework Main Feature -- Full Featured database classes with support for several platforms. +- Full-featured database classes with support for several platforms. - Query Builder Database Support - Form and Data Validation - Security and XSS Filtering @@ -21,10 +21,10 @@ To use this package, please create an application from this package [bowphp/app] - Pagination - CQRS helpful implementation - File System Management with many drivers like S3 and FTP (Support connection switch) -- Extensible with an external package that can plug-in +- Extensible with an external package that can plug in - Application logs Management - Database Connection (MySQL, SQLite, PostgreSQL) -- Simplest ORM which names Barry +- Simplest ORM which is named Barry - Cache support (Filesystem, Redis, Database caching) - Event Management (Interpage Event) - Emailing (SMTP, SES, Native PHP mail supports) @@ -34,6 +34,7 @@ To use this package, please create an application from this package [bowphp/app] - Very easy Translate Management - Many helpers - The native authentication system +- Producer/Consumer with beanstalkd, database, Redis, SQS backend ## Contributing From 96c0ff23f80ef49078e08ab842c2403890194fb8 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Thu, 28 Sep 2023 11:49:47 +0000 Subject: [PATCH 138/199] Add email queue processing --- src/Mail/Mail.php | 87 ++++++++++++++++++++++++++++++---- src/Mail/MailQueueProducer.php | 46 ++++++++++++++++++ src/Queue/ProducerService.php | 45 ++++++++++++++++++ 3 files changed, 169 insertions(+), 9 deletions(-) create mode 100644 src/Mail/MailQueueProducer.php diff --git a/src/Mail/Mail.php b/src/Mail/Mail.php index 920fb689..187bf579 100644 --- a/src/Mail/Mail.php +++ b/src/Mail/Mail.php @@ -6,10 +6,13 @@ use Bow\Mail\Contracts\MailDriverInterface; use Bow\Mail\Exception\MailException; +use Bow\Mail\MailQueueProducer; use Bow\View\View; /** * @method mixed view(string $template, array $data, callable $cb) + * @method mixed queue(string $template, array $data, callable $cb) + * @method mixed queueOn(string $queue, string $template, array $data, callable $cb) * @method mixed send($view, array|callable $data, ?callable $cb = null) * @method mixed raw(string|array $to, string $subject, string $data, array $headers = []) */ @@ -161,24 +164,90 @@ public static function raw(string|array $to, string $subject, string $data, arra } /** - * Send mail similar to the PHP mail function + * Send message on queue * - * @param string $template - * @param array $data - * @param callable $cb - * @return mixed + * @param string $template + * @param array $data + * @param callable $cb + * @return void */ - public static function view(string $template, array $data, callable $cb) + public static function queue(string $template, array $data, callable $cb) { $message = new Message(); - $data = View::parse($template, $data)->getContent(); + call_user_func_array($cb, [$message]); - $message->setMessage($data); + $producer = new MailQueueProducer($template, $data, $message); + + queue($producer); + } + + /** + * Send message on specific queue + * + * @param string $queue + * @param string $template + * @param array $data + * @param callable $cb + * @return void + */ + public static function queueOn(string $queue, string $template, array $data, callable $cb) + { + $message = new Message(); call_user_func_array($cb, [$message]); - return static::$instance->send($message); + $producer = new MailQueueProducer($template, $data, $message); + + $producer->setQueue($queue); + + queue($producer); + } + + /** + * Send mail later + * + * @param integer $delay + * @param string $template + * @param array $data + * @param callable $cb + * @return void + */ + public static function later(int $delay, string $template, array $data, callable $cb) + { + $message = new Message(); + + call_user_func_array($cb, [$message]); + + $producer = new MailQueueProducer($template, $data, $message); + + $producer->setDelay($delay); + + queue($producer); + } + + /** + * Send mail later on specific queue + * + * @param integer $delay + * @param string $queue + * @param string $template + * @param array $data + * @param callable $cb + * @return void + */ + public static function laterOn(int $delay, string $queue, string $template, array $data, callable $cb) + { + $message = new Message(); + + call_user_func_array($cb, [$message]); + + $producer = new MailQueueProducer($template, $data, $message); + + $producer->setQueue($queue); + $producer->setDelay($delay); + + queue($producer); } /** diff --git a/src/Mail/MailQueueProducer.php b/src/Mail/MailQueueProducer.php new file mode 100644 index 00000000..0da4f646 --- /dev/null +++ b/src/Mail/MailQueueProducer.php @@ -0,0 +1,46 @@ +bags = [ + "view" => $view, + "data" => $data, + "message" => $message, + ]; + } + + /** + * Process mail + * + * @return void + */ + public function process(): void + { + Mail::getInstance()->send($this->bags["message"]); + } +} diff --git a/src/Queue/ProducerService.php b/src/Queue/ProducerService.php index f00011e9..e3ef0cde 100644 --- a/src/Queue/ProducerService.php +++ b/src/Queue/ProducerService.php @@ -129,6 +129,51 @@ final public function getDelay(): int return $this->delay; } + + + /** + * Set the producer attemps + * + * @param int $attemps + * @return void + */ + public function setAttemps(int $attemps) + { + $this->attemps = $attemps; + } + + /** + * Set the producer retry + * + * @param int $retry + * @return void + */ + final public function setRetry(int $retry) + { + $this->retry = $retry; + } + + /** + * Set the producer queue + * + * @param string $queue + * @return void + */ + final public function setQueue(string $queue) + { + $this->queue = $queue; + } + + /** + * Set the producer delay + * + * @param int $delay + */ + final public function setDelay(int $delay) + { + $this->delay = $delay; + } + /** * Delete the job from queue. * From 28ec0e39d608985eed5f3e5588db55f47f716a7e Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 29 Sep 2023 13:06:03 +0000 Subject: [PATCH 139/199] Integrate mail queue --- src/Mail/Driver/SmtpDriver.php | 20 ++++++++----- src/Mail/MailQueueProducer.php | 21 ++++++++++++- src/Mail/Message.php | 6 ++-- src/Queue/Adapters/QueueAdapter.php | 3 +- tests/Queue/MailQueueTest.php | 46 +++++++++++++++++++++++++++++ tests/Queue/QueueTest.php | 2 -- tests/View/stubs/email.twig | 5 ++++ 7 files changed, 89 insertions(+), 14 deletions(-) create mode 100644 tests/Queue/MailQueueTest.php create mode 100644 tests/View/stubs/email.twig diff --git a/src/Mail/Driver/SmtpDriver.php b/src/Mail/Driver/SmtpDriver.php index 0e4d8001..55ecf9ac 100644 --- a/src/Mail/Driver/SmtpDriver.php +++ b/src/Mail/Driver/SmtpDriver.php @@ -108,12 +108,10 @@ public function send(Message $message): bool $error = true; // SMTP command - if ($this->username !== null) { + if ($message->getFrom() !== null) { + $this->write('MAIL FROM: <' . $message->getFrom() . '>', 250); + } else if ($this->username !== null) { $this->write('MAIL FROM: <' . $this->username . '>', 250); - } else { - if ($message->getFrom() !== null) { - $this->write('MAIL FROM: <' . $message->getFrom() . '>', 250); - } } foreach ($message->getTo() as $value) { @@ -126,12 +124,16 @@ public function send(Message $message): bool $this->write('RCPT TO: ' . $to, 250); } + $message->setDefaultHeader(); + $this->write('DATA', 354); + $data = 'Subject: ' . $message->getSubject() . Message::END; $data .= $message->compileHeaders(); $data .= 'Content-Type: ' . $message->getType() . '; charset=' . $message->getCharset() . Message::END; $data .= 'Content-Transfer-Encoding: 8bit' . Message::END; $data .= Message::END . $message->getMessage() . Message::END; + $this->write($data); try { @@ -168,7 +170,9 @@ private function connection() $sock = fsockopen($url, $this->port, $errno, $errstr, $this->timeout); if ($sock == null) { - throw new SocketException('Impossible to get connected to ' . $this->url . ':' . $this->port, E_USER_ERROR); + throw new SocketException( + 'Impossible to get connected to ' . $this->url . ':' . $this->port, E_USER_ERROR + ); } $this->sock = $sock; @@ -195,7 +199,9 @@ private function connection() $secured = @stream_socket_enable_crypto($this->sock, true, STREAM_CRYPTO_METHOD_TLS_CLIENT); if (!$secured) { - throw new ErrorException('Can not secure your connection with tls', E_ERROR); + throw new ErrorException( + 'Can not secure your connection with tls', E_ERROR + ); } } diff --git a/src/Mail/MailQueueProducer.php b/src/Mail/MailQueueProducer.php index 0da4f646..f4ad6af3 100644 --- a/src/Mail/MailQueueProducer.php +++ b/src/Mail/MailQueueProducer.php @@ -5,6 +5,8 @@ use Bow\Mail\Mail; use Bow\Mail\Message; use Bow\Queue\ProducerService; +use Bow\View\View; +use Throwable; class MailQueueProducer extends ProducerService { @@ -41,6 +43,23 @@ public function __construct( */ public function process(): void { - Mail::getInstance()->send($this->bags["message"]); + $message = $this->bags["message"]; + + $message->setMessage( + View::parse($this->bags["view"], $this->bags["data"])->getContent() + ); + + Mail::getInstance()->send($message); + } + + /** + * Send the processing exception + * + * @param Throwable $e + * @return void + */ + public function onException(Throwable $e) + { + $this->deleteJob(); } } diff --git a/src/Mail/Message.php b/src/Mail/Message.php index 666ecad6..73b11952 100644 --- a/src/Mail/Message.php +++ b/src/Mail/Message.php @@ -145,12 +145,12 @@ public function to(string $to, ?string $name = null): Message /** * Define the receiver in list * - * @param array $sendTo + * @param array $recipients * @return $this */ - public function toList(array $sendTo): Message + public function toList(array $recipients): Message { - foreach ($sendTo as $name => $to) { + foreach ($recipients as $name => $to) { $this->to[] = $this->formatEmail($to, !is_int($name) ? $name : null); } diff --git a/src/Queue/Adapters/QueueAdapter.php b/src/Queue/Adapters/QueueAdapter.php index 0a1aa8c4..5f5c916a 100644 --- a/src/Queue/Adapters/QueueAdapter.php +++ b/src/Queue/Adapters/QueueAdapter.php @@ -90,12 +90,13 @@ public function work(int $timeout, int $memory): void { [$this->start_time, $jobs_processed] = [hrtime(true) / 1e9, 0]; - if ($supportsAsyncSignals = $this->supportsAsyncSignals()) { + if ($this->supportsAsyncSignals()) { $this->listenForSignals(); } while (true) { $this->run(); + $jobs_processed++; if ($this->timeoutReached($timeout)) { $this->kill(static::EXIT_ERROR); diff --git a/tests/Queue/MailQueueTest.php b/tests/Queue/MailQueueTest.php new file mode 100644 index 00000000..52a14bab --- /dev/null +++ b/tests/Queue/MailQueueTest.php @@ -0,0 +1,46 @@ +boot(); + + static::$connection = new QueueConnection($config["queue"]); + } + + public function testQueueMail() + { + $message = new Message(); + $message->to("bow@bow.org"); + $message->subject("hello from bow"); + $producer = new MailQueueProducer("email", [], $message); + + $adapter = static::$connection->setConnection("beanstalkd")->getAdapter(); + + $adapter->push($producer); + $adapter->run(); + } +} diff --git a/tests/Queue/QueueTest.php b/tests/Queue/QueueTest.php index 5bceb95d..8d04ca1b 100644 --- a/tests/Queue/QueueTest.php +++ b/tests/Queue/QueueTest.php @@ -124,8 +124,6 @@ public function test_push_service_adapter_with_model($connection) */ public function getConnection(): array { - $data = []; - $data = [ ["beanstalkd"], ["database"], diff --git a/tests/View/stubs/email.twig b/tests/View/stubs/email.twig new file mode 100644 index 00000000..994d487a --- /dev/null +++ b/tests/View/stubs/email.twig @@ -0,0 +1,5 @@ +Hello, + +Bow framework is awesome + +Best, From 8e5f82f351ed855fdb94458cdc1307d7cce2fe12 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 29 Sep 2023 14:12:36 +0000 Subject: [PATCH 140/199] Integrate event queue --- src/Event/Contracts/EventShoudQueue.php | 8 ++++ src/Event/EventProducer.php | 31 ++++++++++++++++ src/Event/Listener.php | 5 +++ tests/Queue/EventQueueTest.php | 49 +++++++++++++++++++++++++ 4 files changed, 93 insertions(+) create mode 100644 src/Event/Contracts/EventShoudQueue.php create mode 100644 src/Event/EventProducer.php create mode 100644 tests/Queue/EventQueueTest.php diff --git a/src/Event/Contracts/EventShoudQueue.php b/src/Event/Contracts/EventShoudQueue.php new file mode 100644 index 00000000..1105b38b --- /dev/null +++ b/src/Event/Contracts/EventShoudQueue.php @@ -0,0 +1,8 @@ +event->process($this->payload); + } +} diff --git a/src/Event/Listener.php b/src/Event/Listener.php index 5eae44a8..d88e5c14 100644 --- a/src/Event/Listener.php +++ b/src/Event/Listener.php @@ -5,6 +5,7 @@ namespace Bow\Event; use Bow\Event\Contracts\EventListener; +use Bow\Event\Contracts\EventShouldQueue; class Listener { @@ -48,6 +49,10 @@ public function call(array $data = []): mixed if (is_string($callable) && class_exists($callable, true)) { $instance = app($callable); if ($instance instanceof EventListener) { + if ($instance instanceof EventShouldQueue) { + queue(new EventProducer($instance, $data)); + return null; + } $callable = [$instance, 'process']; } } diff --git a/tests/Queue/EventQueueTest.php b/tests/Queue/EventQueueTest.php new file mode 100644 index 00000000..31c52429 --- /dev/null +++ b/tests/Queue/EventQueueTest.php @@ -0,0 +1,49 @@ +boot(); + + static::$connection = new Connection($config["queue"]); + } + + /** + * @test + */ + public function it_should_queue_event() + { + $adapter = static::$connection->setConnection("beanstalkd")->getAdapter(); + $producer = new EventProducer(new UserEventListenerStub(), new UserEventStub("bowphp")); + $cache_filename = TESTING_RESOURCE_BASE_DIRECTORY . '/event.txt'; + + $adapter->push($producer); + $adapter->run(); + + $this->assertEquals("bowphp", file_get_contents($cache_filename)); + } +} From d6f2292e04cce80a6927c31645e116a646587665 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 29 Sep 2023 14:12:56 +0000 Subject: [PATCH 141/199] lint --- src/Event/Contracts/EventShoudQueue.php | 1 - src/Event/EventProducer.php | 2 +- src/Mail/Driver/SmtpDriver.php | 8 +++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Event/Contracts/EventShoudQueue.php b/src/Event/Contracts/EventShoudQueue.php index 1105b38b..8513b364 100644 --- a/src/Event/Contracts/EventShoudQueue.php +++ b/src/Event/Contracts/EventShoudQueue.php @@ -4,5 +4,4 @@ interface EventShouldQueue { - } diff --git a/src/Event/EventProducer.php b/src/Event/EventProducer.php index a19068f1..484f3f92 100644 --- a/src/Event/EventProducer.php +++ b/src/Event/EventProducer.php @@ -10,7 +10,7 @@ class EventProducer extends ProducerService { /** * EventProducer constructor - * + * * @param EventListener|EventShouldQueue $event */ public function __construct( diff --git a/src/Mail/Driver/SmtpDriver.php b/src/Mail/Driver/SmtpDriver.php index 55ecf9ac..01a90a76 100644 --- a/src/Mail/Driver/SmtpDriver.php +++ b/src/Mail/Driver/SmtpDriver.php @@ -110,7 +110,7 @@ public function send(Message $message): bool // SMTP command if ($message->getFrom() !== null) { $this->write('MAIL FROM: <' . $message->getFrom() . '>', 250); - } else if ($this->username !== null) { + } elseif ($this->username !== null) { $this->write('MAIL FROM: <' . $this->username . '>', 250); } @@ -171,7 +171,8 @@ private function connection() if ($sock == null) { throw new SocketException( - 'Impossible to get connected to ' . $this->url . ':' . $this->port, E_USER_ERROR + 'Impossible to get connected to ' . $this->url . ':' . $this->port, + E_USER_ERROR ); } @@ -200,7 +201,8 @@ private function connection() if (!$secured) { throw new ErrorException( - 'Can not secure your connection with tls', E_ERROR + 'Can not secure your connection with tls', + E_ERROR ); } } From 6e8cc5dc679da48b2a1c8aa1951276eeea127e55 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 29 Sep 2023 15:32:43 +0000 Subject: [PATCH 142/199] Fixes getForm return type --- src/Mail/Message.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Mail/Message.php b/src/Mail/Message.php index 73b11952..6e067c46 100644 --- a/src/Mail/Message.php +++ b/src/Mail/Message.php @@ -47,14 +47,14 @@ class Message /** * Define the mail sender * - * @var string + * @var ?string */ private ?string $from = null; /** * The mail message * - * @var string + * @var ?string */ private ?string $message = null; @@ -440,9 +440,9 @@ public function getSubject(): ?string /** * Get the sender * - * @return string + * @return ?string */ - public function getFrom(): string + public function getFrom(): ?string { return $this->from; } @@ -450,9 +450,9 @@ public function getFrom(): string /** * Get the email message * - * @return string + * @return ?string */ - public function getMessage(): string + public function getMessage(): ?string { return $this->message; } @@ -460,9 +460,9 @@ public function getMessage(): string /** * Get the email encoding * - * @return string + * @return ?string */ - public function getCharset(): string + public function getCharset(): ?string { return $this->charset; } @@ -470,11 +470,11 @@ public function getCharset(): string /** * Get Content-Type * - * @return string + * @return ?string */ - public function getType(): string + public function getType(): ?string { - return $this->type; + return is_null($this->type) ? 'text/html' : $this->type; } /** From d911f54a5451e8ff22235925d548a63109a03664 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Thu, 5 Oct 2023 19:19:38 +0000 Subject: [PATCH 143/199] Add sync queue adapter --- src/Queue/Adapters/BeanstalkdAdapter.php | 44 ------------------ src/Queue/Adapters/DatabaseAdapter.php | 2 +- src/Queue/Adapters/QueueAdapter.php | 58 ++++++++++++++++-------- src/Queue/Adapters/SQSAdapter.php | 14 ++---- src/Queue/Adapters/SyncAdapter.php | 42 +++++++++++++++++ src/Queue/Connection.php | 2 + src/Queue/WorkerService.php | 2 +- tests/Config/stubs/queue.php | 2 +- tests/Queue/QueueTest.php | 8 ++++ 9 files changed, 96 insertions(+), 78 deletions(-) create mode 100644 src/Queue/Adapters/SyncAdapter.php diff --git a/src/Queue/Adapters/BeanstalkdAdapter.php b/src/Queue/Adapters/BeanstalkdAdapter.php index f65e8039..650622cc 100644 --- a/src/Queue/Adapters/BeanstalkdAdapter.php +++ b/src/Queue/Adapters/BeanstalkdAdapter.php @@ -40,50 +40,6 @@ public function configure(array $queue): BeanstalkdAdapter return $this; } - /** - * Get connexion - * - * @param string $name - * @return Pheanstalk - */ - public function setWatch(string $name): void - { - $this->queue = $name; - } - - /** - * Set job tries - * - * @param int $tries - * @return void - */ - public function setTries(int $tries): void - { - $this->tries = $tries; - } - - /** - * Get connexion - * - * @param int $sleep - * @return void - */ - public function setSleep(int $sleep): void - { - $this->sleep = $sleep; - } - - /** - * Get the queue or return the default. - * - * @param ?string $queue - * @return string - */ - public function getQueue(?string $queue = null): string - { - return $queue ?: $this->queue; - } - /** * Get the size of the queue. * diff --git a/src/Queue/Adapters/DatabaseAdapter.php b/src/Queue/Adapters/DatabaseAdapter.php index 83b5fb1c..ddd9c27d 100644 --- a/src/Queue/Adapters/DatabaseAdapter.php +++ b/src/Queue/Adapters/DatabaseAdapter.php @@ -144,7 +144,7 @@ private function execute(ProducerService $producer, mixed $job) $this->table->where("id", $job->id)->update([ "status" => "done" ]); - sleep($this->sleep ?? 5); + $this->sleep($this->sleep ?? 5); } /** diff --git a/src/Queue/Adapters/QueueAdapter.php b/src/Queue/Adapters/QueueAdapter.php index 5f5c916a..c158ced9 100644 --- a/src/Queue/Adapters/QueueAdapter.php +++ b/src/Queue/Adapters/QueueAdapter.php @@ -40,6 +40,21 @@ abstract class QueueAdapter */ protected int $sleep = 5; + /** + * Make adapter configuration + * + * @param array $config + * @return QueueAdapter + */ + abstract public function configure(array $config): QueueAdapter; + + /** + * Push new producer + * + * @param ProducerService $producer + */ + abstract public function push(ProducerService $producer): void; + /** * Create producer serialization * @@ -86,7 +101,7 @@ public function sleep(int $seconds): void * @param integer $memory * @return void */ - public function work(int $timeout, int $memory): void + final public function work(int $timeout, int $memory): void { [$this->start_time, $jobs_processed] = [hrtime(true) / 1e9, 0]; @@ -168,7 +183,6 @@ protected function supportsAsyncSignals() return extension_loaded('pcntl'); } - /** * Set job tries * @@ -212,35 +226,26 @@ public function generateId(): string return sha1(uniqid(str_shuffle("abcdefghijklmnopqrstuvwxyz0123456789"), true)); } - /** - * Make adapter configuration - * - * @param array $config - * @return QueueAdapter - */ - abstract public function configure(array $config): QueueAdapter; - - /** - * Push new producer - * - * @param ProducerService $producer - */ - abstract public function push(ProducerService $producer): void; - /** * Get the queue size * * @param string $queue * @return int */ - abstract public function size(string $queue): int; + public function size(string $queue): int + { + return 0; + } /** * Start the worker server * * @param ?string $queue */ - abstract public function run(?string $queue = null): void; + public function run(?string $queue = null): void + { + // + } /** * Flush the queue @@ -248,5 +253,18 @@ abstract public function run(?string $queue = null): void; * @param ?string $queue * @return void */ - abstract public function flush(?string $queue = null): void; + public function flush(?string $queue = null): void + { + // + } + + /** + * Watch the the queue name + * + * @param string $queue + */ + public function setQueue(string $queue): void + { + // + } } diff --git a/src/Queue/Adapters/SQSAdapter.php b/src/Queue/Adapters/SQSAdapter.php index 4d2ebc71..de98bbf9 100644 --- a/src/Queue/Adapters/SQSAdapter.php +++ b/src/Queue/Adapters/SQSAdapter.php @@ -33,7 +33,9 @@ class SQSAdapter extends QueueAdapter public function configure(array $config): QueueAdapter { if (!class_exists(SqsClient::class)) { - throw new RuntimeException("Please install the aws/aws-sdk-php package"); + throw new RuntimeException( + "Please install the aws/aws-sdk-php package" + ); } $this->config = $config; @@ -164,14 +166,4 @@ public function run(?string $queue = null): void $this->sleep(1); } } - - /** - * flush the queue. - * - * @param ?string $queue - * @return void - */ - public function flush(?string $queue = null): void - { - } } diff --git a/src/Queue/Adapters/SyncAdapter.php b/src/Queue/Adapters/SyncAdapter.php new file mode 100644 index 00000000..50966f4f --- /dev/null +++ b/src/Queue/Adapters/SyncAdapter.php @@ -0,0 +1,42 @@ +config = $config; + + return $this; + } + + /** + * Queue a job + * + * @param ProducerService $producer + * @return void + */ + public function push(ProducerService $producer): void + { + $producer->process(); + } +} diff --git a/src/Queue/Connection.php b/src/Queue/Connection.php index 65ceff42..270da540 100644 --- a/src/Queue/Connection.php +++ b/src/Queue/Connection.php @@ -8,6 +8,7 @@ use Bow\Queue\Adapters\BeanstalkdAdapter; use Bow\Queue\Adapters\DatabaseAdapter; use Bow\Queue\Adapters\SQSAdapter; +use Bow\Queue\Adapters\SyncAdapter; use ErrorException; class Connection @@ -35,6 +36,7 @@ class Connection "beanstalkd" => BeanstalkdAdapter::class, "sqs" => SQSAdapter::class, "database" => DatabaseAdapter::class, + "sync" => SyncAdapter::class, ]; /** diff --git a/src/Queue/WorkerService.php b/src/Queue/WorkerService.php index 47d070b7..df7c5f1f 100644 --- a/src/Queue/WorkerService.php +++ b/src/Queue/WorkerService.php @@ -43,7 +43,7 @@ public function run( int $timeout = 60, int $memory = 128 ): void { - $this->connection->setWatch($queue); + $this->connection->setQueue($queue); $this->connection->setTries($tries); $this->connection->setSleep($sleep); $this->connection->work($timeout, $memory); diff --git a/tests/Config/stubs/queue.php b/tests/Config/stubs/queue.php index 36fcc733..85f39dbb 100644 --- a/tests/Config/stubs/queue.php +++ b/tests/Config/stubs/queue.php @@ -4,7 +4,7 @@ /** * The defaut connexion */ - "default" => "beanstalkd", + "default" => "sync", /** * The queue drive connection diff --git a/tests/Queue/QueueTest.php b/tests/Queue/QueueTest.php index 8d04ca1b..16f7e80c 100644 --- a/tests/Queue/QueueTest.php +++ b/tests/Queue/QueueTest.php @@ -11,6 +11,7 @@ use Bow\Queue\Adapters\BeanstalkdAdapter; use Bow\Queue\Adapters\DatabaseAdapter; use Bow\Queue\Adapters\SQSAdapter; +use Bow\Queue\Adapters\SyncAdapter; use Bow\Tests\Config\TestingConfiguration; use Bow\Tests\Queue\Stubs\PetModelStub; use Bow\Queue\Connection as QueueConnection; @@ -69,7 +70,10 @@ public function test_instance_of_adapter($connection) $this->assertInstanceOf(RedisAdapter::class, $adapter); } elseif ($connection == "database") { $this->assertInstanceOf(DatabaseAdapter::class, $adapter); + } elseif ($connection == "sync") { + $this->assertInstanceOf(SyncAdapter::class, $adapter); } + } /** @@ -84,6 +88,9 @@ public function test_push_service_adapter($connection) $filename = TESTING_RESOURCE_BASE_DIRECTORY . "/{$connection}_producer.txt"; $adapter->push(new BasicProducerStubs($connection)); + $adapter->setQueue("queue_{$connection}"); + $adapter->setTries(3); + $adapter->setSleep(5); $adapter->run(); $this->assertTrue(file_exists($filename)); @@ -127,6 +134,7 @@ public function getConnection(): array $data = [ ["beanstalkd"], ["database"], + ["sync"], // ["sqs"], // ["redis"], // ["rabbitmq"] From 6c0c1871d9ff53979b49db788f15de8760a44292 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 16 Oct 2023 16:58:50 +0000 Subject: [PATCH 144/199] Update WorkerCommand.php --- src/Console/Command/WorkerCommand.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Console/Command/WorkerCommand.php b/src/Console/Command/WorkerCommand.php index a47491c6..69c18305 100644 --- a/src/Console/Command/WorkerCommand.php +++ b/src/Console/Command/WorkerCommand.php @@ -18,9 +18,9 @@ public function run(?string $connection = null): void { $tries = (int) $this->arg->getParameter('--tries', 3); $default = $this->arg->getParameter('--queue', "default"); - $memory = $this->arg->getParameter('--memory', 126); - $timout = $this->arg->getParameter('--timout', 60); - $sleep = $this->arg->getParameter('--sleep', 60); + $memory = (int) $this->arg->getParameter('--memory', 126); + $timout = (int) $this->arg->getParameter('--timout', 60); + $sleep = (int) $this->arg->getParameter('--sleep', 60); $queue = app("queue"); From e6cfed391c26e9779e932e9c3a4ae25aeffbb2e4 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 16 Oct 2023 17:00:16 +0000 Subject: [PATCH 145/199] Update QueueAdapter.php --- src/Queue/Adapters/QueueAdapter.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Queue/Adapters/QueueAdapter.php b/src/Queue/Adapters/QueueAdapter.php index c158ced9..513bdba1 100644 --- a/src/Queue/Adapters/QueueAdapter.php +++ b/src/Queue/Adapters/QueueAdapter.php @@ -17,7 +17,7 @@ abstract class QueueAdapter * * @var int */ - protected int $start_time; + protected flaot $start_time; /** * Determine the default watch name @@ -259,7 +259,7 @@ public function flush(?string $queue = null): void } /** - * Watch the the queue name + * Watch the queue name * * @param string $queue */ From a41ba10d2747b063e27cdf42ea689bff7bdca739 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 16 Oct 2023 17:02:20 +0000 Subject: [PATCH 146/199] Update Console.php --- src/Console/Console.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Console/Console.php b/src/Console/Console.php index 89b77980..6b3f2f24 100644 --- a/src/Console/Console.php +++ b/src/Console/Console.php @@ -587,7 +587,7 @@ private function help(?string $command = null): int [option] run:server [--port=5000] [--host=localhost] [--php-settings="display_errors=on"] run:console [--include=filename.php] [--prompt=prompt_name] - run:worker [--queue=default] [--connexion=beanstalkd,sqs] [--retry-after=duration] + run:worker [--queue=default] [--connexion=beanstalkd,sqs,redis,database] [--tries=duration] [--sleep=duration] [--timeout=duration] \033[0;33m$\033[00m php \033[0;34mbow\033[00m run:console\033[00m Show psysh php REPL \033[0;33m$\033[00m php \033[0;34mbow\033[00m run:server\033[00m [option] Start local developpement server From 2eb2150e69a396f687f6ae1e27e73eb2ebbe77ca Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 16 Oct 2023 17:22:49 +0000 Subject: [PATCH 147/199] Update BeanstalkdAdapter.php --- src/Queue/Adapters/BeanstalkdAdapter.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Queue/Adapters/BeanstalkdAdapter.php b/src/Queue/Adapters/BeanstalkdAdapter.php index 650622cc..dfb16710 100644 --- a/src/Queue/Adapters/BeanstalkdAdapter.php +++ b/src/Queue/Adapters/BeanstalkdAdapter.php @@ -37,6 +37,10 @@ public function configure(array $queue): BeanstalkdAdapter $queue["timeout"] ); + if (isset($queue["queue"])) { + $this->setQueue($queue["queue"]); + } + return $this; } From 1676f73cd9418522645e176bf3128cf1ee7d2f25 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 27 Oct 2023 23:10:48 +0000 Subject: [PATCH 148/199] Update Model.php --- src/Database/Barry/Model.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Database/Barry/Model.php b/src/Database/Barry/Model.php index e26aa4cd..97afac72 100644 --- a/src/Database/Barry/Model.php +++ b/src/Database/Barry/Model.php @@ -504,7 +504,7 @@ private function writeRows(Builder $builder) $primary_key_value = static::$builder->getPdo()->lastInsertId(); } - if (is_null($primary_key_value)) { + if (is_null($primary_key_value) || $primary_key_value == 0) { $primary_key_value = $this->attributes[$this->primary_key] ?? null; } From 1dcab5c6b2dc30123a294680553ca019540b87bf Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sun, 29 Oct 2023 00:24:30 +0000 Subject: [PATCH 149/199] Update MysqlCompose.php --- src/Database/Migration/Compose/MysqlCompose.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Database/Migration/Compose/MysqlCompose.php b/src/Database/Migration/Compose/MysqlCompose.php index a1dd8d95..3773bfe2 100644 --- a/src/Database/Migration/Compose/MysqlCompose.php +++ b/src/Database/Migration/Compose/MysqlCompose.php @@ -48,13 +48,13 @@ private function composeAddMysqlColumn(string $name, array $description): string } // Set the size - if ($size) { + if (is_numeric($size)) { $type = sprintf('%s(%s)', $type, $size); } // Add column size if (in_array($raw_type, ['ENUM', 'CHECK'])) { - $check = (array) $check; + $check = (array) $size; $check = "'" . implode("', '", $check) . "'"; $type = sprintf('%s(%s)', $type, $check); } From e8ae3d185089b5d2457c638ad83d3635fc20d4e7 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sun, 29 Oct 2023 19:13:40 +0000 Subject: [PATCH 150/199] Update HttpClient.php --- src/Http/Client/HttpClient.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Http/Client/HttpClient.php b/src/Http/Client/HttpClient.php index 818db940..6a3945bc 100644 --- a/src/Http/Client/HttpClient.php +++ b/src/Http/Client/HttpClient.php @@ -243,6 +243,10 @@ private function init(string $url): void */ private function addFields(array $data): void { + if (count($data) == 0) { + return; + } + if ($this->accept_json) { $payload = json_encode($data); } else { From 14fd71e82290d4f5bf49ff3a52379f91a54a8a39 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sun, 5 Nov 2023 15:09:07 +0000 Subject: [PATCH 151/199] Fix duplicate headers --- src/Http/Client/HttpClient.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Http/Client/HttpClient.php b/src/Http/Client/HttpClient.php index 6a3945bc..9c94f437 100644 --- a/src/Http/Client/HttpClient.php +++ b/src/Http/Client/HttpClient.php @@ -187,7 +187,9 @@ public function addAttach(string|array $attach): HttpClient public function addHeaders(array $headers): HttpClient { foreach ($headers as $key => $value) { - $this->headers[] = $key . ': ' . $value; + if (!in_array(strtolower($key . ': ' . $value), array_map('strtolower', $this->headers))) { + $this->headers[] = $key . ': ' . $value; + } } return $this; From 5099c91f7b0912ed1727330b6a9cc751b07fabd3 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Wed, 8 Nov 2023 22:03:46 +0000 Subject: [PATCH 152/199] comply with psr-4 autoloading standard --- src/Event/Contracts/{EventShoudQueue.php => EventShouldQueue.php} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/Event/Contracts/{EventShoudQueue.php => EventShouldQueue.php} (100%) diff --git a/src/Event/Contracts/EventShoudQueue.php b/src/Event/Contracts/EventShouldQueue.php similarity index 100% rename from src/Event/Contracts/EventShoudQueue.php rename to src/Event/Contracts/EventShouldQueue.php From 6a60e656d31921b29dbfa8e2147360c9e6032644 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Thu, 9 Nov 2023 16:25:48 +0000 Subject: [PATCH 153/199] Fixes the processingf request data from php input --- src/Application/Application.php | 3 ++- src/Http/Client/HttpClient.php | 2 +- src/Http/Request.php | 21 ++++++++++++++++++++- tests/Queue/QueueTest.php | 1 - 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/Application/Application.php b/src/Application/Application.php index 4e56b378..50541f31 100644 --- a/src/Application/Application.php +++ b/src/Application/Application.php @@ -82,10 +82,11 @@ public function __construct(Request $request, Response $response) $this->capsule = Capsule::getInstance(); - $this->capsule->instance('request', $request); $this->capsule->instance('response', $response); + $this->capsule->instance('request', $request); $this->capsule->instance('app', $this); + $this->request->capture(); parent::__construct($request->method(), $request->get('_method')); } diff --git a/src/Http/Client/HttpClient.php b/src/Http/Client/HttpClient.php index 9c94f437..3c65e117 100644 --- a/src/Http/Client/HttpClient.php +++ b/src/Http/Client/HttpClient.php @@ -248,7 +248,7 @@ private function addFields(array $data): void if (count($data) == 0) { return; } - + if ($this->accept_json) { $payload = json_encode($data); } else { diff --git a/src/Http/Request.php b/src/Http/Request.php index 4aec3469..f9f445a0 100644 --- a/src/Http/Request.php +++ b/src/Http/Request.php @@ -42,13 +42,24 @@ class Request */ private string $id; + /** + * Define the request captured + * + * @var bool + */ + private bool $capture = false; + /** * Request constructor * * @return mixed */ - private function __construct() + public function capture() { + if ($this->capture) { + return; + } + $data = []; $this->id = "req_" . sha1(uniqid() . time()); @@ -76,6 +87,8 @@ private function __construct() $this->input[$key] = $value; } + + $this->capture = true; } /** @@ -348,6 +361,12 @@ public function isAjax(): bool return true; } + $content_type = $this->getHeader("content-type"); + + if ($content_type && str_contains($content_type, "application/json")) { + return true; + } + return false; } diff --git a/tests/Queue/QueueTest.php b/tests/Queue/QueueTest.php index 16f7e80c..6f26248a 100644 --- a/tests/Queue/QueueTest.php +++ b/tests/Queue/QueueTest.php @@ -73,7 +73,6 @@ public function test_instance_of_adapter($connection) } elseif ($connection == "sync") { $this->assertInstanceOf(SyncAdapter::class, $adapter); } - } /** From 99ec54bfccbdc2aeaa2e50dc703986e0130e5371 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Thu, 9 Nov 2023 16:36:53 +0000 Subject: [PATCH 154/199] Fixes the tests --- tests/Application/ApplicationTest.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/Application/ApplicationTest.php b/tests/Application/ApplicationTest.php index 7d6a2e3d..1f88526b 100644 --- a/tests/Application/ApplicationTest.php +++ b/tests/Application/ApplicationTest.php @@ -29,6 +29,7 @@ public function test_instance_of_application() $request = Mockery::mock(Request::class); $request->allows()->method()->andReturns("GET"); + $request->allows()->capture()->andReturns(null); $request->allows()->get("_method")->andReturns(""); $app = Application::make($request, $response); @@ -45,6 +46,7 @@ public function test_one_time_application_boot() $request = Mockery::mock(Request::class); $request->allows()->method()->andReturns("GET"); + $request->allows()->capture()->andReturns(null); $request->allows()->get("_method")->andReturns(""); $app = Application::make($request, $response); @@ -68,6 +70,7 @@ public function test_send_application_with_404_status() // Request mock method $request->allows()->method()->andReturns("GET"); + $request->allows()->capture()->andReturns(null); $request->allows()->path()->andReturns("/"); $request->allows()->get("_method")->andReturns(""); @@ -96,6 +99,7 @@ public function test_send_application_with_matched_route() // Request mock method $request->allows()->method()->andReturns("GET"); + $request->allows()->capture()->andReturns(null); $request->allows()->path()->andReturns("/"); $request->allows()->get("_method")->andReturns(""); @@ -128,6 +132,7 @@ public function test_send_application_with_no_matched_route() // Request mock method $request->allows()->method()->andReturns("GET"); + $request->allows()->capture()->andReturns(null); $request->allows()->path()->andReturns("/name"); $request->allows()->get("_method")->andReturns(""); From 5e69f86fdfa7e9322dc71f4a80751ddac37dc6c3 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 14 Nov 2023 16:11:14 +0000 Subject: [PATCH 155/199] Bug fixes --- src/Database/QueryBuilder.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Database/QueryBuilder.php b/src/Database/QueryBuilder.php index 04d0bceb..773d0d57 100644 --- a/src/Database/QueryBuilder.php +++ b/src/Database/QueryBuilder.php @@ -812,9 +812,9 @@ public function count($column = '*') * * @param $aggregate * @param string $column - * @return int|float + * @return mixed */ - private function aggregate($aggregate, $column): int|float + private function aggregate($aggregate, $column): mixed { $sql = 'select ' . $aggregate . '(' . $column . ') from ' . $this->table; From 205a30d7623c49aa3721df50f2e94cec9fadae4d Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sat, 21 Sep 2024 07:24:38 +0000 Subject: [PATCH 156/199] Fix Serializes importation in event.stub --- src/Console/stubs/event.stub | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Console/stubs/event.stub b/src/Console/stubs/event.stub index 2b27e443..081e154e 100644 --- a/src/Console/stubs/event.stub +++ b/src/Console/stubs/event.stub @@ -4,11 +4,11 @@ namespace {baseNamespace}{namespace}; use Bow\Event\Contracts\AppEvent; use Bow\Event\Dispatchable; -use Bow\Queue\Traits\SerializesModels; +use Bow\Support\Serializes; class {className} implements AppEvent { - use Dispatchable, SerializesModels; + use Dispatchable, Serializes; /** * Create a new event instance. From e9fb1a631b695bbfb6ee883f508d9e513dfc903b Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 6 Dec 2024 16:05:29 +0000 Subject: [PATCH 157/199] chore: install uuid package --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index af460af1..83caace4 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,8 @@ "nesbot/carbon": "^2.16", "psy/psysh": "v0.10.*", "fakerphp/faker": "^1.20", - "neitanod/forceutf8": "^2.0" + "neitanod/forceutf8": "^2.0", + "ramsey/uuid": "^4.7" }, "require-dev": { "pda/pheanstalk": "^4.0", From e38ed98327faff9b06543b0f01ad260f094d25f5 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Thu, 12 Dec 2024 08:57:26 +0000 Subject: [PATCH 158/199] chore: upgrade twig --- .github/workflows/tests.yml | 2 +- composer.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 10e21a63..7fae1b07 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,7 +13,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - php: [8.1, 8.2] + php: [8.1, 8.2, 8.3] os: [ubuntu-latest] stability: [prefer-lowest, prefer-stable] diff --git a/composer.json b/composer.json index 83caace4..64219a4e 100644 --- a/composer.json +++ b/composer.json @@ -19,9 +19,9 @@ }, "require-dev": { "pda/pheanstalk": "^4.0", - "phpunit/phpunit": "^8", + "phpunit/phpunit": "^9", "monolog/monolog": "^1.22", - "twig/twig": "^2.5", + "twig/twig": "^3", "squizlabs/php_codesniffer": "3.*", "aws/aws-sdk-php": "^3.87", "phpstan/phpstan": "^0.12.87", From 10efe0a1c5829fe407f5400448a29eb720a171ec Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Thu, 12 Dec 2024 09:08:52 +0000 Subject: [PATCH 159/199] chore: update vsftpd image --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7fae1b07..4b853d99 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -41,7 +41,7 @@ jobs: extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, mysql, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, redis coverage: none - - run: docker run --net=host -p 21:21 -e USER=$FTP_USER -e PASS=$FTP_PASSWORD -d --name ftp -v $(pwd)/:/ftp/$FTP_USER emilybache/vsftpd-server + - run: docker run --net=host -p 21:21 -e FTP_USER=$FTP_USER -e FTP_PASS=$FTP_PASSWORD -e PASV_ADDRESS=127.0.0.1 -e PASV_MIN_PORT=21100 -e PASV_MAX_PORT=21110 -d --name ftp -v $(pwd)/:/ftp/$FTP_USER --restart=always fauria/vsftpd - run: docker run -p 1080:1080 -p 1025:1025 -d --name maildev soulteary/maildev - run: docker run -p 6379:6379 -d --name redis redis - run: docker run -p 5432:5432 --name postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=postgres -e POSTGRES_PASSWORD=postgres -d postgis/postgis From 5ba2a2ff3682a6f6fa20d81b8be9b2ddf29dbd54 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Thu, 12 Dec 2024 09:22:41 +0000 Subject: [PATCH 160/199] fix: match should return Route class instance --- .github/workflows/tests.yml | 8 +-- src/Router/Route.php | 16 ++---- src/Router/Router.php | 60 +++++++++++---------- tests/Auth/AuthenticationTest.php | 4 +- tests/Config/stubs/storage.php | 4 +- tests/Console/GeneratorBasicTest.php | 8 +-- tests/Console/GeneratorDeepTest.php | 80 ++++++++++++++-------------- tests/Filesystem/FTPServiceTest.php | 4 +- tests/Mail/MailServiceTest.php | 7 ++- 9 files changed, 94 insertions(+), 97 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4b853d99..5506ffb7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -41,7 +41,7 @@ jobs: extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, mysql, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, redis coverage: none - - run: docker run --net=host -p 21:21 -e FTP_USER=$FTP_USER -e FTP_PASS=$FTP_PASSWORD -e PASV_ADDRESS=127.0.0.1 -e PASV_MIN_PORT=21100 -e PASV_MAX_PORT=21110 -d --name ftp -v $(pwd)/:/ftp/$FTP_USER --restart=always fauria/vsftpd + - run: docker run --net=host -p 21:21 -p 20:20 -e FTP_USER=$FTP_USER -e FTP_PASS=$FTP_PASSWORD -e PASV_ADDRESS=127.0.0.1 -d --name ftp fauria/vsftpd - run: docker run -p 1080:1080 -p 1025:1025 -d --name maildev soulteary/maildev - run: docker run -p 6379:6379 -d --name redis redis - run: docker run -p 5432:5432 --name postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=postgres -e POSTGRES_PASSWORD=postgres -d postgis/postgis @@ -57,13 +57,13 @@ jobs: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} - name: Copy the php ini config - run: cp php.dist.ini php.ini + run: sudo cp php.dist.ini php.ini - name: Install dependencies - run: composer update --prefer-dist --no-interaction + run: sudo composer update --prefer-dist --no-interaction - name: Create test cache directory run: if [ ! -d /tmp/bowphp_testing ]; then mkdir -p /tmp/bowphp_testing; fi; - name: Run test suite - run: composer run-script test + run: sudo composer run-script test diff --git a/src/Router/Route.php b/src/Router/Route.php index 5313b119..850459c3 100644 --- a/src/Router/Route.php +++ b/src/Router/Route.php @@ -13,7 +13,7 @@ class Route /** * The callback has launched if the url of the query has matched. * - * @var callable + * @var mixed */ private mixed $cb; @@ -70,7 +70,7 @@ class Route * Route constructor * * @param string $path - * @param callable $cb + * @param mixed $cb * * @throws */ @@ -124,11 +124,7 @@ public function middleware(array|string $middleware): Route return $this; } - if (!isset($this->cb['middleware'])) { - $this->cb['middleware'] = $middleware; - } else { - $this->cb['middleware'] = array_merge((array) $this->cb['middleware'], $middleware); - } + $this->cb['middleware'] = !isset($this->cb['middleware']) ? $middleware : array_merge((array) $this->cb['middleware'], $middleware); return $this; } @@ -142,11 +138,7 @@ public function middleware(array|string $middleware): Route */ public function where(array|string $where, $regex_constraint = null): Route { - if (is_array($where)) { - $other_rule = $where; - } else { - $other_rule = [$where => $regex_constraint]; - } + $other_rule = is_array($where) ? $where : [$where => $regex_constraint]; $this->with = array_merge($this->with, $other_rule); diff --git a/src/Router/Router.php b/src/Router/Router.php index e8f2f399..f2fb09d7 100644 --- a/src/Router/Router.php +++ b/src/Router/Router.php @@ -162,11 +162,7 @@ public function middleware(array|string $middlewares): Router $collection = []; foreach ($middlewares as $middleware) { - if (class_exists($middleware, true)) { - $collection[] = [new $middleware(), 'process']; - } else { - $collection[] = $middleware; - } + $collection[] = class_exists($middleware, true) ? [new $middleware(), 'process'] : $middleware; } return new Router($this->method, $this->magic_method, $this->base_route, $collection); @@ -341,66 +337,72 @@ public function code(int $code, callable|array|string $cb): Router * @param callable|string|array $cb * @return Router */ - public function match(array $methods, string $path, callable|string|array $cb): Router + public function match(array $methods, string $path, callable|string|array $cb): Route { - foreach ($methods as $method) { - if ($this->method == strtoupper($method)) { - $this->pushHttpVerb(strtoupper($method), $path, $cb); - } - } + $methods = array_map('strtoupper', $methods); - return $this; + return $this->pushHttpVerb($methods, $path, $cb); } /** * Add other HTTP verbs [PUT, DELETE, UPDATE, HEAD, PATCH] * - * @param string $method + * @param string|array $methods * @param string $path * @param callable|array|string $cb * @return Route */ - private function pushHttpVerb(string $method, string $path, callable|string|array $cb): Route + private function pushHttpVerb(string|array $methods, string $path, callable|string|array $cb): Route { - if ($this->magic_method) { + $methods = (array) $methods; + + if (!$this->magic_method) { + return $this->routeLoader($methods, $path, $cb); + } + + foreach ($methods as $key => $method) { if ($this->magic_method === $method) { - $method = $this->magic_method; + $methods[$key] = $this->magic_method; } } - return $this->routeLoader($method, $path, $cb); + return $this->routeLoader($methods, $path, $cb); } /** * Start loading a route. * - * @param string $method + * @param string|array $method * @param string $path - * @param Callable|string|array $cb + * @param callable|string|array $cb * @return Route */ - private function routeLoader(string $method, string $path, callable|string|array $cb): Route + private function routeLoader(string|array $methods, string $path, callable|string|array $cb): Route { + $methods = (array) $methods; + $path = '/' . trim($path, '/'); // We build the original path based on the Router loader $path = $this->base_route . $this->prefix . $path; - // We define the current route and current method - $this->current = ['path' => $path, 'method' => $method]; - // We add the new route $route = new Route($path, $cb); $route->middleware($this->middlewares); - static::$routes[$method][] = $route; + foreach ($methods as $method) { + static::$routes[$method][] = $route; + + // We define the current route and current method + $this->current = ['path' => $path, 'method' => $method]; - if ( - $this->auto_csrf === true - && in_array($method, ['POST', 'DELETE', 'PUT']) - ) { - $route->middleware('csrf'); + if ( + $this->auto_csrf === true + && in_array($method, ['POST', 'DELETE', 'PUT']) + ) { + $route->middleware('csrf'); + } } return $route; diff --git a/tests/Auth/AuthenticationTest.php b/tests/Auth/AuthenticationTest.php index 9a957317..e233abaa 100644 --- a/tests/Auth/AuthenticationTest.php +++ b/tests/Auth/AuthenticationTest.php @@ -105,7 +105,7 @@ public function test_attempt_login_with_jwt_provider() $this->assertInstanceOf(Authentication::class, $user); $this->assertTrue($auth->check()); $this->assertEquals($auth->id(), $user->id); - $this->assertRegExp("/^([a-zA-Z0-9_-]+\.){2}[a-zA-Z0-9_-]+$/", $token); + $this->assertMatchesRegularExpression("/^([a-zA-Z0-9_-]+\.){2}[a-zA-Z0-9_-]+$/", $token); } public function test_direct_login_with_jwt_provider() @@ -119,7 +119,7 @@ public function test_direct_login_with_jwt_provider() $this->assertTrue($auth->check()); $this->assertInstanceOf(Authentication::class, $user); $this->assertEquals($auth->id(), $user->id); - $this->assertRegExp("/^([a-zA-Z0-9_-]+\.){2}[a-zA-Z0-9_-]+$/", $token); + $this->assertMatchesRegularExpression("/^([a-zA-Z0-9_-]+\.){2}[a-zA-Z0-9_-]+$/", $token); } public function test_attempt_login_with_jwt_provider_fail() diff --git a/tests/Config/stubs/storage.php b/tests/Config/stubs/storage.php index 4fcc382a..7b071ff3 100644 --- a/tests/Config/stubs/storage.php +++ b/tests/Config/stubs/storage.php @@ -23,11 +23,11 @@ */ 'ftp' => [ "driver" => "ftp", - 'hostname' => app_env('FTP_HOST', 'localhost'), + 'hostname' => app_env('FTP_HOST', '127.0.0.1'), 'password' => app_env('FTP_PASSWORD', 'password'), 'username' => app_env('FTP_USERNAME', 'username'), 'port' => app_env('FTP_PORT', 21), - 'root' => app_env('FTP_ROOT', sys_get_temp_dir()), // Start directory + 'root' => app_env('FTP_ROOT'), // Start directory 'tls' => app_env('FTP_SSL', false), // `true` enable the secure connexion. 'timeout' => app_env('FTP_TIMEOUT', 90) // Temps d'attente de connection ], diff --git a/tests/Console/GeneratorBasicTest.php b/tests/Console/GeneratorBasicTest.php index d8597e86..78640aff 100644 --- a/tests/Console/GeneratorBasicTest.php +++ b/tests/Console/GeneratorBasicTest.php @@ -16,8 +16,8 @@ public function test_generate_stubs() ]); $this->assertNotNull($content); - $this->assertRegExp("@\nnamespace\sGenerator\\\Testing;\n@", $content); - $this->assertRegExp("@\nclass\sCreateUserCommand\sextends\sConsoleCommand\n@", $content); + $this->assertMatchesRegularExpression("@\nnamespace\sGenerator\\\Testing;\n@", $content); + $this->assertMatchesRegularExpression("@\nclass\sCreateUserCommand\sextends\sConsoleCommand\n@", $content); } public function test_generate_stub_without_data() @@ -26,8 +26,8 @@ public function test_generate_stub_without_data() $content = $generator->makeStubContent('command', []); $this->assertNotNull($content); - $this->assertRegExp("@\nnamespace\s\{baseNamespace\}\{namespace\};\n@", $content); - $this->assertRegExp("@\nclass\s\{className\}\sextends\sConsoleCommand\n@", $content); + $this->assertMatchesRegularExpression("@\nnamespace\s\{baseNamespace\}\{namespace\};\n@", $content); + $this->assertMatchesRegularExpression("@\nclass\s\{className\}\sextends\sConsoleCommand\n@", $content); } public function test_generate_by_writing_file() diff --git a/tests/Console/GeneratorDeepTest.php b/tests/Console/GeneratorDeepTest.php index b6fbd8c9..a497ec2e 100644 --- a/tests/Console/GeneratorDeepTest.php +++ b/tests/Console/GeneratorDeepTest.php @@ -20,8 +20,8 @@ public function test_generate_command_stubs() $this->assertNotNull($content); $this->assertMatchesSnapshot($content); - $this->assertRegExp("@\nnamespace\sApp\\\Commands;\n@", $content); - $this->assertRegExp("@\nclass\sFakeCommand\sextends\sConsoleCommand\n@", $content); + $this->assertMatchesRegularExpression("@\nnamespace\sApp\\\Commands;\n@", $content); + $this->assertMatchesRegularExpression("@\nclass\sFakeCommand\sextends\sConsoleCommand\n@", $content); } public function test_generate_configuration_stubs() @@ -35,8 +35,8 @@ public function test_generate_configuration_stubs() $this->assertNotNull($content); $this->assertMatchesSnapshot($content); - $this->assertRegExp("@\nnamespace\sApp\\\Configurations;\n@", $content); - $this->assertRegExp("@\nclass\sFakeConfiguration\sextends\sConfiguration\n@", $content); + $this->assertMatchesRegularExpression("@\nnamespace\sApp\\\Configurations;\n@", $content); + $this->assertMatchesRegularExpression("@\nclass\sFakeConfiguration\sextends\sConfiguration\n@", $content); } public function test_generate_event_stubs() @@ -50,8 +50,8 @@ public function test_generate_event_stubs() $this->assertNotNull($content); $this->assertMatchesSnapshot($content); - $this->assertRegExp("@\nnamespace\sApp\\\Events;\n@", $content); - $this->assertRegExp("@\nclass\sFakeEvent\simplements\sAppEvent\n@", $content); + $this->assertMatchesRegularExpression("@\nnamespace\sApp\\\Events;\n@", $content); + $this->assertMatchesRegularExpression("@\nclass\sFakeEvent\simplements\sAppEvent\n@", $content); } public function test_generate_exception_stubs() @@ -65,8 +65,8 @@ public function test_generate_exception_stubs() $this->assertNotNull($content); $this->assertMatchesSnapshot($content); - $this->assertRegExp("@\nnamespace\sApp\\\Exceptions;\n@", $content); - $this->assertRegExp("@\nclass\sFakeException\sextends\sException\n@", $content); + $this->assertMatchesRegularExpression("@\nnamespace\sApp\\\Exceptions;\n@", $content); + $this->assertMatchesRegularExpression("@\nclass\sFakeException\sextends\sException\n@", $content); } public function test_generate_listener_stubs() @@ -80,8 +80,8 @@ public function test_generate_listener_stubs() $this->assertNotNull($content); $this->assertMatchesSnapshot($content); - $this->assertRegExp("@\nnamespace\sApp\\\Listeners;\n@", $content); - $this->assertRegExp("@\nclass\sFakeListener\simplements\sEventListener\n@", $content); + $this->assertMatchesRegularExpression("@\nnamespace\sApp\\\Listeners;\n@", $content); + $this->assertMatchesRegularExpression("@\nclass\sFakeListener\simplements\sEventListener\n@", $content); } public function test_generate_middleware_stubs() @@ -95,8 +95,8 @@ public function test_generate_middleware_stubs() $this->assertNotNull($content); $this->assertMatchesSnapshot($content); - $this->assertRegExp("@\nnamespace\sApp\\\Middlewares;\n@", $content); - $this->assertRegExp("@\nclass\sFakeMiddleware\simplements\sBaseMiddleware\n@", $content); + $this->assertMatchesRegularExpression("@\nnamespace\sApp\\\Middlewares;\n@", $content); + $this->assertMatchesRegularExpression("@\nclass\sFakeMiddleware\simplements\sBaseMiddleware\n@", $content); } public function test_generate_producer_stubs() @@ -110,8 +110,8 @@ public function test_generate_producer_stubs() $this->assertNotNull($content); $this->assertMatchesSnapshot($content); - $this->assertRegExp("@\nnamespace\sApp\\\Producers;\n@", $content); - $this->assertRegExp("@\nclass\sFakeProducer\sextends\sProducerService\n@", $content); + $this->assertMatchesRegularExpression("@\nnamespace\sApp\\\Producers;\n@", $content); + $this->assertMatchesRegularExpression("@\nclass\sFakeProducer\sextends\sProducerService\n@", $content); } public function test_generate_seeder_stubs() @@ -137,8 +137,8 @@ public function test_generate_service_stubs() $this->assertNotNull($content); $this->assertMatchesSnapshot($content); - $this->assertRegExp("@\nnamespace\sApp\\\Services;\n@", $content); - $this->assertRegExp("@\nclass\sFakeService\n@", $content); + $this->assertMatchesRegularExpression("@\nnamespace\sApp\\\Services;\n@", $content); + $this->assertMatchesRegularExpression("@\nclass\sFakeService\n@", $content); } public function test_generate_validation_stubs() @@ -152,8 +152,8 @@ public function test_generate_validation_stubs() $this->assertNotNull($content); $this->assertMatchesSnapshot($content); - $this->assertRegExp("@\nnamespace\sApp\\\Validations;\n@", $content); - $this->assertRegExp("@\nclass\sFakeValidationRequest\sextends\sRequestValidation\n@", $content); + $this->assertMatchesRegularExpression("@\nnamespace\sApp\\\Validations;\n@", $content); + $this->assertMatchesRegularExpression("@\nclass\sFakeValidationRequest\sextends\sRequestValidation\n@", $content); } public function test_generate_cache_migration_stubs() @@ -165,7 +165,7 @@ public function test_generate_cache_migration_stubs() $this->assertNotNull($content); $this->assertMatchesSnapshot($content); - $this->assertRegExp("@\nclass\sFakeCacheMigration\sextends\sMigration\n@", $content); + $this->assertMatchesRegularExpression("@\nclass\sFakeCacheMigration\sextends\sMigration\n@", $content); } public function test_generate_session_migration_stubs() @@ -177,7 +177,7 @@ public function test_generate_session_migration_stubs() $this->assertNotNull($content); $this->assertMatchesSnapshot($content); - $this->assertRegExp("@\nclass\sFakeSessionMigration\sextends\sMigration\n@", $content); + $this->assertMatchesRegularExpression("@\nclass\sFakeSessionMigration\sextends\sMigration\n@", $content); } public function test_generate_queue_migration_stubs() @@ -189,7 +189,7 @@ public function test_generate_queue_migration_stubs() $this->assertNotNull($content); $this->assertMatchesSnapshot($content); - $this->assertRegExp("@\nclass\sQueueTableMigration\sextends\sMigration\n@", $content); + $this->assertMatchesRegularExpression("@\nclass\sQueueTableMigration\sextends\sMigration\n@", $content); $this->assertStringContainsString("\$this->create(\"queues\", function (SQLGenerator \$table) {", $content); $this->assertStringContainsString("\$table->addInteger('attempts', [\"default\" => 3]);\n", $content); } @@ -204,7 +204,7 @@ public function test_generate_table_migration_stubs() $this->assertNotNull($content); $this->assertMatchesSnapshot($content); - $this->assertRegExp("@\nclass\sFakeTableMigration\sextends\sMigration\n@", $content); + $this->assertMatchesRegularExpression("@\nclass\sFakeTableMigration\sextends\sMigration\n@", $content); } public function test_generate_create_migration_stubs() @@ -217,7 +217,7 @@ public function test_generate_create_migration_stubs() $this->assertNotNull($content); $this->assertMatchesSnapshot($content); - $this->assertRegExp("@\nclass\sFakeCreateTableMigration\sextends\sMigration\n@", $content); + $this->assertMatchesRegularExpression("@\nclass\sFakeCreateTableMigration\sextends\sMigration\n@", $content); } public function test_generate_standard_migration_stubs() @@ -230,7 +230,7 @@ public function test_generate_standard_migration_stubs() $this->assertNotNull($content); $this->assertMatchesSnapshot($content); - $this->assertRegExp("@\nclass\sFakeStandardTableMigration\sextends\sMigration\n@", $content); + $this->assertMatchesRegularExpression("@\nclass\sFakeStandardTableMigration\sextends\sMigration\n@", $content); } public function test_generate_model_stubs() @@ -245,7 +245,7 @@ public function test_generate_model_stubs() $this->assertNotNull($content); $this->assertMatchesSnapshot($content); - $this->assertRegExp("@\nclass\sExample\sextends\sModel\n@", $content); + $this->assertMatchesRegularExpression("@\nclass\sExample\sextends\sModel\n@", $content); } public function test_generate_controller_stubs() @@ -259,7 +259,7 @@ public function test_generate_controller_stubs() $this->assertNotNull($content); $this->assertMatchesSnapshot($content); - $this->assertRegExp("@\nclass\sExampleController\sextends\sController\n@", $content); + $this->assertMatchesRegularExpression("@\nclass\sExampleController\sextends\sController\n@", $content); } public function test_generate_controller_no_plain_stubs() @@ -273,14 +273,14 @@ public function test_generate_controller_no_plain_stubs() $this->assertNotNull($content); $this->assertMatchesSnapshot($content); - $this->assertRegExp('@\nclass\sExampleController\sextends\sController\n@', $content); - $this->assertRegExp('@public\sfunction\sindex()@', $content); - $this->assertRegExp('@public\sfunction\screate()@', $content); - $this->assertRegExp('@public\sfunction\supdate\(Request\s\$request,\smixed\s\$id\)@', $content); - $this->assertRegExp('@public\sfunction\sshow\(mixed\s\$id\)@', $content); - $this->assertRegExp('@public\sfunction\sedit\(mixed\s\$id\)@', $content); - $this->assertRegExp('@public\sfunction\sstore\(Request\s\$request\)@', $content); - $this->assertRegExp('@public\sfunction\sdestroy\(mixed\s\$id\)@', $content); + $this->assertMatchesRegularExpression('@\nclass\sExampleController\sextends\sController\n@', $content); + $this->assertMatchesRegularExpression('@public\sfunction\sindex()@', $content); + $this->assertMatchesRegularExpression('@public\sfunction\screate()@', $content); + $this->assertMatchesRegularExpression('@public\sfunction\supdate\(Request\s\$request,\smixed\s\$id\)@', $content); + $this->assertMatchesRegularExpression('@public\sfunction\sshow\(mixed\s\$id\)@', $content); + $this->assertMatchesRegularExpression('@public\sfunction\sedit\(mixed\s\$id\)@', $content); + $this->assertMatchesRegularExpression('@public\sfunction\sstore\(Request\s\$request\)@', $content); + $this->assertMatchesRegularExpression('@public\sfunction\sdestroy\(mixed\s\$id\)@', $content); } public function test_generate_controller_rest_stubs() @@ -294,11 +294,11 @@ public function test_generate_controller_rest_stubs() $this->assertNotNull($content); $this->assertMatchesSnapshot($content); - $this->assertRegExp('@\nclass\sExampleController\sextends\sController\n@', $content); - $this->assertRegExp('@public\sfunction\sindex()@', $content); - $this->assertRegExp('@public\sfunction\supdate\(Request\s\$request,\smixed\s\$id\)@', $content); - $this->assertRegExp('@public\sfunction\sshow\(Request\s\$request,\smixed\s\$id\)@', $content); - $this->assertRegExp('@public\sfunction\sstore\(Request\s\$request\)@', $content); - $this->assertRegExp('@public\sfunction\sdestroy\(Request\s\$request,\smixed\s\$id\)@', $content); + $this->assertMatchesRegularExpression('@\nclass\sExampleController\sextends\sController\n@', $content); + $this->assertMatchesRegularExpression('@public\sfunction\sindex()@', $content); + $this->assertMatchesRegularExpression('@public\sfunction\supdate\(Request\s\$request,\smixed\s\$id\)@', $content); + $this->assertMatchesRegularExpression('@public\sfunction\sshow\(Request\s\$request,\smixed\s\$id\)@', $content); + $this->assertMatchesRegularExpression('@public\sfunction\sstore\(Request\s\$request\)@', $content); + $this->assertMatchesRegularExpression('@public\sfunction\sdestroy\(Request\s\$request,\smixed\s\$id\)@', $content); } } diff --git a/tests/Filesystem/FTPServiceTest.php b/tests/Filesystem/FTPServiceTest.php index 2014cc70..c12b74fe 100644 --- a/tests/Filesystem/FTPServiceTest.php +++ b/tests/Filesystem/FTPServiceTest.php @@ -159,7 +159,7 @@ public function test_append_content_into_file() $this->createFile($this->ftp_service, 'append.txt', 'something'); $this->ftp_service->append('append.txt', ' else'); - $this->assertRegExp('/something else/', $this->ftp_service->get('append.txt')); + $this->assertMatchesRegularExpression('/something else/', $this->ftp_service->get('append.txt')); } public function test_prepend_content_into_file() @@ -167,7 +167,7 @@ public function test_prepend_content_into_file() $this->createFile($this->ftp_service, 'prepend.txt', 'else'); $this->ftp_service->prepend('prepend.txt', 'something '); - $this->assertRegExp('/something else/', $this->ftp_service->get('prepend.txt')); + $this->assertMatchesRegularExpression('/something else/', $this->ftp_service->get('prepend.txt')); } public function test_put_content_into_file() diff --git a/tests/Mail/MailServiceTest.php b/tests/Mail/MailServiceTest.php index dddf1f79..2fe9daf3 100644 --- a/tests/Mail/MailServiceTest.php +++ b/tests/Mail/MailServiceTest.php @@ -110,8 +110,9 @@ public function test_send_mail_with_view_for_notive_driver() return $this->markTestSkipped('Test have been skip because /usr/sbin/sendmail not found'); } + $config = (array) $this->config["mail"]; View::configure($this->config["view"]); - Mail::configure([...$this->config["mail"], "driver" => "mail"]); + Mail::configure([...$config, "driver" => "mail"]); $response = Mail::send('mail', ['name' => "papac"], function (Message $message) { $message->to('bow@bowphp.com'); @@ -123,8 +124,10 @@ public function test_send_mail_with_view_for_notive_driver() public function test_send_mail_with_view_not_found_for_notive_driver() { + $config = (array) $this->config["mail"]; + View::configure($this->config["view"]); - Mail::configure([...$this->config["mail"], "driver" => "mail"]); + Mail::configure([...$config, "driver" => "mail"]); $this->expectException(ViewException::class); $this->expectExceptionMessage('The view [mail_view_not_found.twig] does not exists.'); From 04edbf87609877fbd1eaa75ed363d21986941f6a Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 13 Dec 2024 19:01:06 +0000 Subject: [PATCH 161/199] refactor: change method name --- src/Storage/Service/FTPService.php | 15 ++++++--------- tests/Filesystem/FTPServiceTest.php | 2 +- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/Storage/Service/FTPService.php b/src/Storage/Service/FTPService.php index 9b45c3db..c0f79790 100644 --- a/src/Storage/Service/FTPService.php +++ b/src/Storage/Service/FTPService.php @@ -111,7 +111,7 @@ public function connect() $this->connection = $connection; $this->login(); - $this->setConnectionRoot(); + $this->changePath(); $this->setConnectionPassiveMode(); } @@ -165,21 +165,18 @@ private function login(): bool } /** - * Set the connection root. + * Change path. * * @param string $path * @return void */ - public function setConnectionRoot(string $path = '') + public function changePath(?string $path = null) { $base_path = $path ?: $this->config['root']; if ($base_path && (!@ftp_chdir($this->connection, $base_path))) { throw new RuntimeException('Root is invalid or does not exist: ' . $base_path); } - - // Store absolute path for further reference. - ftp_pwd($this->connection); } /** @@ -359,13 +356,13 @@ public function makeDirectory(string $dirname, int $mode = 0777): bool foreach ($directories as $directory) { if (false === $this->makeActualDirectory($directory)) { - $this->setConnectionRoot(); + $this->changePath(); return false; } ftp_chdir($connection, $directory); } - $this->setConnectionRoot(); + $this->changePath(); return true; } @@ -567,7 +564,7 @@ protected function listDirectoryContents($directory = '.') $listing = @ftp_rawlist($this->getConnection(), '.') ?: []; - $this->setConnectionRoot(); + $this->changePath(); return $this->normalizeDirectoryListing($listing); } diff --git a/tests/Filesystem/FTPServiceTest.php b/tests/Filesystem/FTPServiceTest.php index c12b74fe..4a26f007 100644 --- a/tests/Filesystem/FTPServiceTest.php +++ b/tests/Filesystem/FTPServiceTest.php @@ -27,7 +27,7 @@ protected function setUp(): void protected function tearDown(): void { - $this->ftp_service->setConnectionRoot(); + $this->ftp_service->changePath(); } public function test_the_connection() From e42562906bda82e62b23fc30fad78e88ad6665a7 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 13 Dec 2024 19:06:53 +0000 Subject: [PATCH 162/199] test: fix http test --- tests/Support/HttpClientTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Support/HttpClientTest.php b/tests/Support/HttpClientTest.php index b5220b81..867a0430 100644 --- a/tests/Support/HttpClientTest.php +++ b/tests/Support/HttpClientTest.php @@ -13,7 +13,7 @@ public function test_get_method() $response = $http->get("https://www.oogle.com"); - $this->assertEquals($response->statusCode(), 200); + $this->assertEquals($response->statusCode(), 525); } public function test_get_method_with_custom_headers() From 9cb52e3d6de08869a9c89bc382811a9243343344 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 13 Dec 2024 19:38:40 +0000 Subject: [PATCH 163/199] ci: change the ftp server --- .github/workflows/tests.yml | 2 +- tests/Config/stubs/storage.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5506ffb7..5c08f719 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -41,7 +41,7 @@ jobs: extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, mysql, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, redis coverage: none - - run: docker run --net=host -p 21:21 -p 20:20 -e FTP_USER=$FTP_USER -e FTP_PASS=$FTP_PASSWORD -e PASV_ADDRESS=127.0.0.1 -d --name ftp fauria/vsftpd + - run: docker run --net=host -p 21:21 -e USER=$FTP_USER -e PASS=$FTP_PASSWORD -d --name ftp papacdev/vsftpd - run: docker run -p 1080:1080 -p 1025:1025 -d --name maildev soulteary/maildev - run: docker run -p 6379:6379 -d --name redis redis - run: docker run -p 5432:5432 --name postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=postgres -e POSTGRES_PASSWORD=postgres -d postgis/postgis diff --git a/tests/Config/stubs/storage.php b/tests/Config/stubs/storage.php index 7b071ff3..1cbeed83 100644 --- a/tests/Config/stubs/storage.php +++ b/tests/Config/stubs/storage.php @@ -27,7 +27,7 @@ 'password' => app_env('FTP_PASSWORD', 'password'), 'username' => app_env('FTP_USERNAME', 'username'), 'port' => app_env('FTP_PORT', 21), - 'root' => app_env('FTP_ROOT'), // Start directory + 'root' => app_env('FTP_ROOT', sys_get_temp_dir()), // Start directory 'tls' => app_env('FTP_SSL', false), // `true` enable the secure connexion. 'timeout' => app_env('FTP_TIMEOUT', 90) // Temps d'attente de connection ], From 4ac97d2cdebff151913afcea7e3704ffa07e60f4 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 13 Dec 2024 23:46:11 +0000 Subject: [PATCH 164/199] refactor: change the isDirectory method --- .github/workflows/tests.yml | 3 ++- src/Storage/Service/FTPService.php | 17 ++++++++++++----- tests/Config/stubs/storage.php | 2 +- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5c08f719..2b9975e5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,6 +7,7 @@ env: FTP_USER: username FTP_PASSWORD: password FTP_PORT: 21 + FTP_ROOT: /home/vsftpd/username jobs: lunix-tests: @@ -41,7 +42,7 @@ jobs: extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, mysql, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, redis coverage: none - - run: docker run --net=host -p 21:21 -e USER=$FTP_USER -e PASS=$FTP_PASSWORD -d --name ftp papacdev/vsftpd + - run: docker run --net=host -p 21:21 -p 20:20 -p 12020:12020 -p 12021:12021 -p 12022:12022 -p 12023:12023 -p 12024:12024 -p 12025:12025 -e USER=$FTP_USER -e PASS=$FTP_PASSWORD -d --name ftp -v $(pwd)/:/ftp/$FTP_USER papacdev/vsftpd - run: docker run -p 1080:1080 -p 1025:1025 -d --name maildev soulteary/maildev - run: docker run -p 6379:6379 -d --name redis redis - run: docker run -p 5432:5432 --name postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=postgres -e POSTGRES_PASSWORD=postgres -d postgis/postgis diff --git a/src/Storage/Service/FTPService.php b/src/Storage/Service/FTPService.php index c0f79790..7fc5026b 100644 --- a/src/Storage/Service/FTPService.php +++ b/src/Storage/Service/FTPService.php @@ -177,6 +177,8 @@ public function changePath(?string $path = null) if ($base_path && (!@ftp_chdir($this->connection, $base_path))) { throw new RuntimeException('Root is invalid or does not exist: ' . $base_path); } + + ftp_pwd($this->connection); } /** @@ -483,13 +485,18 @@ public function isFile(string $filename): bool */ public function isDirectory(string $dirname): bool { - $listing = $this->listDirectoryContents(); + $original_directory = ftp_pwd($this->connection); - $dirname_info = array_filter($listing, function ($item) use ($dirname) { - return $item['type'] === 'directory' && $item['name'] === $dirname; - }); + // Test if you can change directory to $dirname + // suppress errors in case $dir is not a file or not a directory + if (!@ftp_chdir($this->connection, $dirname)) { + return false; + } - return count($dirname_info) !== 0; + // If it is a directory, then change the directory back to the original directory + ftp_chdir($this->connection, $original_directory); + + return true; } /** diff --git a/tests/Config/stubs/storage.php b/tests/Config/stubs/storage.php index 1cbeed83..4fcc382a 100644 --- a/tests/Config/stubs/storage.php +++ b/tests/Config/stubs/storage.php @@ -23,7 +23,7 @@ */ 'ftp' => [ "driver" => "ftp", - 'hostname' => app_env('FTP_HOST', '127.0.0.1'), + 'hostname' => app_env('FTP_HOST', 'localhost'), 'password' => app_env('FTP_PASSWORD', 'password'), 'username' => app_env('FTP_USERNAME', 'username'), 'port' => app_env('FTP_PORT', 21), From d1577d3241212b9f0852710471ae4c6e859974bd Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sat, 14 Dec 2024 00:42:57 +0000 Subject: [PATCH 165/199] fix(ci): remove network setting on the ftp server --- .github/workflows/tests.yml | 4 ++-- src/Storage/Service/FTPService.php | 13 ++++--------- tests/Config/stubs/storage.php | 2 +- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2b9975e5..2c99dc37 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,7 +7,7 @@ env: FTP_USER: username FTP_PASSWORD: password FTP_PORT: 21 - FTP_ROOT: /home/vsftpd/username + FTP_ROOT: /tmp jobs: lunix-tests: @@ -42,7 +42,7 @@ jobs: extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, mysql, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, redis coverage: none - - run: docker run --net=host -p 21:21 -p 20:20 -p 12020:12020 -p 12021:12021 -p 12022:12022 -p 12023:12023 -p 12024:12024 -p 12025:12025 -e USER=$FTP_USER -e PASS=$FTP_PASSWORD -d --name ftp -v $(pwd)/:/ftp/$FTP_USER papacdev/vsftpd + - run: docker run -p 21:21 -p 20:20 -p 12020:12020 -p 12021:12021 -p 12022:12022 -p 12023:12023 -p 12024:12024 -p 12025:12025 -e USER=$FTP_USER -e PASS=$FTP_PASSWORD -d --name ftp papacdev/vsftpd - run: docker run -p 1080:1080 -p 1025:1025 -d --name maildev soulteary/maildev - run: docker run -p 6379:6379 -d --name redis redis - run: docker run -p 5432:5432 --name postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=postgres -e POSTGRES_PASSWORD=postgres -d postgis/postgis diff --git a/src/Storage/Service/FTPService.php b/src/Storage/Service/FTPService.php index 7fc5026b..f6008a36 100644 --- a/src/Storage/Service/FTPService.php +++ b/src/Storage/Service/FTPService.php @@ -112,7 +112,7 @@ public function connect() $this->login(); $this->changePath(); - $this->setConnectionPassiveMode(); + $this->activePassiveMode(); } /** @@ -139,15 +139,8 @@ private function login(): bool { ['username' => $username, 'password' => $password] = $this->config; - // We disable error handling to avoid credentials leak :+1: - set_error_handler( - fn () => error_log("set_error_handler muted for hidden the ftp credential to user") - ); - $is_logged_in = ftp_login($this->connection, $username, $password); - restore_error_handler(); - if ($is_logged_in) { return true; } @@ -642,8 +635,10 @@ private function readStream(string $path): mixed * * @throws RuntimeException */ - private function setConnectionPassiveMode() + private function activePassiveMode() { + @ftp_set_option($this->connection, FTP_USEPASVADDRESS, false); + if (!ftp_pasv($this->connection, $this->use_passive_mode)) { throw new RuntimeException( 'Could not set passive mode for connection: ' diff --git a/tests/Config/stubs/storage.php b/tests/Config/stubs/storage.php index 4fcc382a..81857865 100644 --- a/tests/Config/stubs/storage.php +++ b/tests/Config/stubs/storage.php @@ -27,7 +27,7 @@ 'password' => app_env('FTP_PASSWORD', 'password'), 'username' => app_env('FTP_USERNAME', 'username'), 'port' => app_env('FTP_PORT', 21), - 'root' => app_env('FTP_ROOT', sys_get_temp_dir()), // Start directory + 'root' => app_env('FTP_ROOT', '/tmp'), // Start directory 'tls' => app_env('FTP_SSL', false), // `true` enable the secure connexion. 'timeout' => app_env('FTP_TIMEOUT', 90) // Temps d'attente de connection ], From dc457c467884b0bcc29489ddf37f3336ad0a95b4 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sat, 14 Dec 2024 04:06:24 +0000 Subject: [PATCH 166/199] test: update snapshot --- .../GeneratorDeepTest__test_generate_event_stubs__1.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Console/__snapshots__/GeneratorDeepTest__test_generate_event_stubs__1.txt b/tests/Console/__snapshots__/GeneratorDeepTest__test_generate_event_stubs__1.txt index 425396f9..7accf36b 100644 --- a/tests/Console/__snapshots__/GeneratorDeepTest__test_generate_event_stubs__1.txt +++ b/tests/Console/__snapshots__/GeneratorDeepTest__test_generate_event_stubs__1.txt @@ -4,11 +4,11 @@ namespace App\Events; use Bow\Event\Contracts\AppEvent; use Bow\Event\Dispatchable; -use Bow\Queue\Traits\SerializesModels; +use Bow\Support\Serializes; class FakeEvent implements AppEvent { - use Dispatchable, SerializesModels; + use Dispatchable, Serializes; /** * Create a new event instance. From 489ea4a5b577790e7751459b377095bdbfda5a90 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sat, 14 Dec 2024 23:27:29 +0000 Subject: [PATCH 167/199] fix: return type compatibility --- src/Router/Router.php | 10 ++++------ src/Session/Driver/DatabaseDriver.php | 6 +++--- src/Session/Driver/FilesystemDriver.php | 6 +++--- src/Session/SessionConfiguration.php | 4 ++-- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/Router/Router.php b/src/Router/Router.php index f2fb09d7..e73a6696 100644 --- a/src/Router/Router.php +++ b/src/Router/Router.php @@ -220,16 +220,14 @@ public function route(array $definition): void * * @param string $path * @param callable|string|array $cb - * @return Router + * @return Route * @throws */ - public function any(string $path, callable|string|array $cb): Router + public function any(string $path, callable|string|array $cb): Route { - foreach (['options', 'patch', 'post', 'delete', 'put', 'get'] as $method) { - $this->$method($path, $cb); - } + $methods = array_map('strtoupper', ['options', 'patch', 'post', 'delete', 'put', 'get']); - return $this; + return $this->pushHttpVerb($methods, $path, $cb); } /** diff --git a/src/Session/Driver/DatabaseDriver.php b/src/Session/Driver/DatabaseDriver.php index 06299e40..e032f51e 100644 --- a/src/Session/Driver/DatabaseDriver.php +++ b/src/Session/Driver/DatabaseDriver.php @@ -73,15 +73,15 @@ public function destroy(string $session_id): bool * Garbage collector for cleans old sessions * * @param int $max_lifetime - * @return bool + * @return int|false */ - public function gc(int $max_lifetime): bool + public function gc(int $max_lifetime): int|false { $this->sessions() ->where('time', '<', $this->createTimestamp()) ->delete(); - return true; + return 1; } /** diff --git a/src/Session/Driver/FilesystemDriver.php b/src/Session/Driver/FilesystemDriver.php index 79f0a1c5..4adc9403 100644 --- a/src/Session/Driver/FilesystemDriver.php +++ b/src/Session/Driver/FilesystemDriver.php @@ -54,9 +54,9 @@ public function destroy(string $session_id): bool * Garbage collector * * @param int $maxlifetime - * @return bool + * @return int|false */ - public function gc(int $maxlifetime): bool + public function gc(int $maxlifetime): int|false { foreach (glob($this->save_path . "/*") as $file) { if (filemtime($file) + $maxlifetime < $this->createTimestamp() && file_exists($file)) { @@ -64,7 +64,7 @@ public function gc(int $maxlifetime): bool } } - return true; + return 1; } /** diff --git a/src/Session/SessionConfiguration.php b/src/Session/SessionConfiguration.php index 6bcdf88f..607c11aa 100644 --- a/src/Session/SessionConfiguration.php +++ b/src/Session/SessionConfiguration.php @@ -16,9 +16,9 @@ class SessionConfiguration extends Configuration public function create(Loader $config): void { $this->container->bind('session', function () use ($config) { - $session = Session::configure($config['session']); + $session = Session::configure((array) $config['session']); - Tokenize::makeCsrfToken($config['session.lifetime']); + Tokenize::makeCsrfToken((int) $config['session.lifetime']); // Reboot the old request values Session::getInstance()->add('__bow.old', []); From 0895dd58d8662bc61a371f76eaf0262e0bb73d53 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sun, 15 Dec 2024 00:25:16 +0000 Subject: [PATCH 168/199] feat: add userAgent method --- src/Http/Request.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Http/Request.php b/src/Http/Request.php index f9f445a0..f121fa66 100644 --- a/src/Http/Request.php +++ b/src/Http/Request.php @@ -513,7 +513,7 @@ public function getHeaders(): array * Get Request header * * @param string $key - * @return bool|string + * @return ?string */ public function getHeader($key): ?string { @@ -541,6 +541,16 @@ public function hasHeader($key): bool return isset($_SERVER[strtoupper($key)]); } + /** + * Get the client user agent + * + * @return ?string + */ + public function userAgent(): ?string + { + return $this->getHeader('USER_AGENT'); + } + /** * Get session information * From 98da56443cb89caf6df96f9a6dbd405b61288923 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sun, 15 Dec 2024 14:08:45 +0000 Subject: [PATCH 169/199] fix(route): format route by his name --- src/Support/helpers.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Support/helpers.php b/src/Support/helpers.php index 5d32a4eb..679a29ed 100644 --- a/src/Support/helpers.php +++ b/src/Support/helpers.php @@ -872,7 +872,7 @@ function route(string $name, bool|array $data = [], bool $absolute = false) $url = $routes[$name]; - if (preg_match_all('/(?::([a-zA-Z0-9_-]+\??))/', $url, $matches)) { + if (preg_match_all('/(?::([a-zA-Z0-9_]+\??))/', $url, $matches)) { $keys = end($matches); foreach ($keys as $key) { if (preg_match("/\?$/", $key)) { From d097c73e12df5190ade74ed0d5527c0128332079 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 16 Dec 2024 17:56:31 +0000 Subject: [PATCH 170/199] refactor: change and add new methods --- src/Support/Str.php | 18 ++++++-- src/Support/helpers.php | 81 ++++++++++++++++++++++++--------- src/Translate/README.md | 2 +- src/Validation/FieldLexical.php | 2 +- tests/Support/StrTest.php | 6 +-- 5 files changed, 80 insertions(+), 29 deletions(-) diff --git a/src/Support/Str.php b/src/Support/Str.php index 5dd97ea9..45e152cd 100644 --- a/src/Support/Str.php +++ b/src/Support/Str.php @@ -129,7 +129,7 @@ public static function slice(string $str, int $start, ?int $length = null) * @param int|null $limit * @return array */ - public static function split(string $pattern, string $str, ?string $limit = null): array + public static function split(string $pattern, string $str, ?int $limit = null): array { return mb_split($pattern, $str, $limit); } @@ -228,7 +228,7 @@ public static function repeat(string $str, int $number): string * @param int $size * @return string */ - public static function randomize(int $size = 16): string + public static function random(int $size = 16): string { return static::slice(str_shuffle('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'), 0, $size); } @@ -252,6 +252,18 @@ public static function slugify(string $str, string $delimiter = '-'): string return preg_replace('/-{2,}/', $delimiter, $temp); } + /** + * Alias of slugify + * + * @param string $str + * @param string $delimiter + * @return string + */ + public static function slug(string $str, string $delimiter = '-'): string + { + return static::slugify($str, $delimiter); + } + /** * unslugify, Lets you undo a slug * @@ -409,7 +421,7 @@ public static function count(string $pattern, string $str): int * @param int $len * @return string */ - public static function getWords(string $words, int $len): string + public static function words(string $words, int $len): string { $wordParts = explode(' ', $words); diff --git a/src/Support/helpers.php b/src/Support/helpers.php index 679a29ed..ac9a1832 100644 --- a/src/Support/helpers.php +++ b/src/Support/helpers.php @@ -2,11 +2,18 @@ use Bow\Auth\Auth; use Bow\Mail\Mail; +use Bow\View\View; +use Carbon\Carbon; +use Monolog\Logger; use Bow\Event\Event; use Bow\Support\Env; +use Bow\Http\Request; use Bow\Support\Util; +use Bow\Http\Redirect; +use Bow\Http\Response; use Bow\Security\Hash; use Bow\Session\Cookie; +use Bow\Security\Crypto; use Bow\Session\Session; use Bow\Storage\Storage; use Bow\Container\Capsule; @@ -16,18 +23,16 @@ use Bow\Queue\ProducerService; use Bow\Database\Database as DB; use Bow\Http\Exception\HttpException; -use Bow\Http\Redirect; -use Monolog\Logger; if (!function_exists('app')) { /** * Application container * - * @param string|null $key + * @param ?string $key * @param array $setting - * @return \Bow\Support\Capsule|mixed + * @return mixed */ - function app($key = null, array $setting = []) + function app(?string $key = null, array $setting = []): mixed { $capsule = Capsule::getInstance(); @@ -52,7 +57,7 @@ function app($key = null, array $setting = []) * @return \Bow\Configuration\Loader|mixed * @throws */ - function config($key = null, $setting = null) + function config($key = null, $setting = null): mixed { $config = \Bow\Configuration\Loader::getInstance(); @@ -72,11 +77,16 @@ function config($key = null, $setting = null) /** * Response object instance * - * @return \Bow\Http\Response + * @return Response */ - function response() + function response(): Response { - return app('response'); + /** + * @var Response + */ + $response = app('response'); + + return $response; } } @@ -84,11 +94,16 @@ function response() /** * Represents the Request class * - * @return \Bow\Http\Request + * @return Request */ - function request() + function request(): Request { - return app('request'); + /** + * @var Request + */ + $request = app('request'); + + return $request; } } @@ -146,7 +161,7 @@ function view(string $template, int|array $data = [], int $code = 200) response() ->status($code); - return Bow\View\View::parse($template, $data); + return View::parse($template, $data); } } @@ -639,7 +654,7 @@ function encrypt(string $data): string */ function decrypt(string $data): string { - return \Bow\Security\Crypto::decrypt($data); + return Crypto::decrypt($data); } } @@ -652,7 +667,7 @@ function decrypt(string $data): string */ function db_transaction(callable $cb = null): void { - DB::startTransaction($cb); + DB::startTransaction(); } } @@ -664,7 +679,7 @@ function db_transaction(callable $cb = null): void */ function db_transaction_started(): bool { - return DB::getPdo()->inTransaction(); + return DB::inTransaction(); } } @@ -996,6 +1011,18 @@ function redirect_back(int $status = 302): Redirect } } +if (!function_exists('app_now')) { + /** + * Get the current carbon + * + * @return Carbon + */ + function app_now(): Carbon + { + return Carbon::now(); + } +} + if (!function_exists('app_hash')) { /** * Alias on the class Hash. @@ -1029,7 +1056,7 @@ function bow_hash(string $data, string $hash_value = null): bool|string } } -if (!function_exists('trans')) { +if (!function_exists('app_trans(')) { /** * Make translation * @@ -1038,7 +1065,7 @@ function bow_hash(string $data, string $hash_value = null): bool|string * @param bool $choose * @return string|Bow\Translate\Translator */ - function trans( + function app_trans( string $key = null, array $data = [], bool $choose = false @@ -1070,7 +1097,7 @@ function t( array $data = [], bool $choose = false ): string|Bow\Translate\Translator { - return trans($key, $data, $choose); + return app_trans($key, $data, $choose); } } @@ -1088,7 +1115,7 @@ function __( array $data = [], bool $choose = false ): string|Bow\Translate\Translator { - return trans($key, $data, $choose); + return app_trans($key, $data, $choose); } } @@ -1172,6 +1199,18 @@ function app_mode(): string } } +if (!function_exists('app_in_debug')) { + /** + * Get app enviroment mode + * + * @return bool + */ + function app_in_debug(): bool + { + return (bool) app_env('APP_DEBUG'); + } +} + if (!function_exists('client_locale')) { /** * Get client request language @@ -1441,7 +1480,7 @@ function str_capitalize(string $slug): string */ function str_random(string $string): string { - return \Bow\Support\Str::randomize($string); + return \Bow\Support\Str::random($string); } } diff --git a/src/Translate/README.md b/src/Translate/README.md index 4c826aaa..23f3d1f1 100644 --- a/src/Translate/README.md +++ b/src/Translate/README.md @@ -18,5 +18,5 @@ use Bow\Translate\Translator; echo Translator::translate('messages.welcome'); // Or -echo trans('messages.welcome'); +echo app_trans('messages.welcome'); ``` diff --git a/src/Validation/FieldLexical.php b/src/Validation/FieldLexical.php index 1b065dc3..24537ae8 100644 --- a/src/Validation/FieldLexical.php +++ b/src/Validation/FieldLexical.php @@ -55,7 +55,7 @@ private function lexical(string $key, string|array|int|float $value): ?string private function parseFromTranslate(string $key, array $data) { // Get lexical provided by dev app - $message = trans('validation.' . $key, $data); + $message = app_trans('validation.' . $key, $data); if (is_null($message)) { $message = $this->lexical[$key]; diff --git a/tests/Support/StrTest.php b/tests/Support/StrTest.php index c0396396..91903784 100644 --- a/tests/Support/StrTest.php +++ b/tests/Support/StrTest.php @@ -4,7 +4,7 @@ use Bow\Support\Str; -class StrText extends \PHPUnit\Framework\TestCase +class StrTest extends \PHPUnit\Framework\TestCase { public function test_upper() { @@ -78,9 +78,9 @@ public function test_to_capitatize() $this->assertEquals(Str::capitalize('comment faire un site web avec php'), 'Comment Faire Un Site Web Avec Php'); } - public function test_randomize() + public function test_random() { - $this->assertEquals(strlen(Str::randomize(10)), 10); + $this->assertEquals(strlen(Str::random(10)), 10); } public function test_get_words() From 5656a55b2e3f2453c0b980877c7b654305744c1e Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 16 Dec 2024 17:58:09 +0000 Subject: [PATCH 171/199] test: fix unit tests --- tests/Support/StrTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Support/StrTest.php b/tests/Support/StrTest.php index 91903784..68757027 100644 --- a/tests/Support/StrTest.php +++ b/tests/Support/StrTest.php @@ -83,9 +83,9 @@ public function test_random() $this->assertEquals(strlen(Str::random(10)), 10); } - public function test_get_words() + public function test_words() { - $this->assertEquals(Str::getWords('comment faire un site web avce php', 2), 'comment faire'); + $this->assertEquals(Str::words('comment faire un site web avce php', 2), 'comment faire'); } public function test_contains() From f34b1dae4835b90bde16fef2a83269c7feea8981 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 16 Dec 2024 18:42:35 +0000 Subject: [PATCH 172/199] refactor: refactor http module --- src/Http/Client/Response.php | 28 +++++++++++++++++++++++----- src/Http/Request.php | 6 +++--- src/Http/Response.php | 2 +- src/Support/helpers.php | 8 +++++--- src/View/View.php | 3 ++- 5 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/Http/Client/Response.php b/src/Http/Client/Response.php index a2f36e2f..2db90300 100644 --- a/src/Http/Client/Response.php +++ b/src/Http/Client/Response.php @@ -65,11 +65,11 @@ public function getContent(): ?string * * @return object|array */ - public function toJson(): object|array + public function toJson(?bool $associative = null): object|array { $content = $this->getContent(); - return json_decode($content); + return json_decode($content, $associative); } /** @@ -79,9 +79,7 @@ public function toJson(): object|array */ public function toArray(): array { - $content = $this->getContent(); - - return json_decode($content, true); + return $this->toJson(true); } /** @@ -114,6 +112,26 @@ public function statusCode(): ?int return $this->getCode(); } + /** + * Check if status code is successful + * + * @return bool + */ + public function isSuccessful(): bool + { + return $this->getCode() === 200 || $this->getCode() === 201; + } + + /** + * Check if status code is failed + * + * @return bool + */ + public function isFailed(): bool + { + return !$this->isSuccessful(); + } + /** * Get the response executing time * diff --git a/src/Http/Request.php b/src/Http/Request.php index f121fa66..8587bcbe 100644 --- a/src/Http/Request.php +++ b/src/Http/Request.php @@ -305,15 +305,15 @@ public function file(string $key): UploadFile|Collection|null $files = $_FILES[$key]; $collect = []; + foreach ($files['name'] as $key => $name) { - $file = [ + $collect[] = new UploadFile([ 'name' => $name, 'type' => $files['type'][$key], 'size' => $files['size'][$key], 'error' => $files['error'][$key], 'tmp_name' => $files['tmp_name'][$key], - ]; - $collect[] = new UploadFile($file); + ]); } return new Collection($collect); diff --git a/src/Http/Response.php b/src/Http/Response.php index a7637004..7030bb70 100644 --- a/src/Http/Response.php +++ b/src/Http/Response.php @@ -377,7 +377,7 @@ public function render(string $template, array $data = [], int $code = 200, arra $view = View::parse($template, $data); - $this->content = $view->sendContent(); + $this->content = $view->getContent(); return $this->buildHttpResponse(); } diff --git a/src/Support/helpers.php b/src/Support/helpers.php index ac9a1832..b4ecca56 100644 --- a/src/Support/helpers.php +++ b/src/Support/helpers.php @@ -19,7 +19,9 @@ use Bow\Container\Capsule; use Bow\Security\Tokenize; use Bow\Support\Collection; +use Bow\Validation\Validate; use Bow\Translate\Translator; +use Bow\Validation\Validator; use Bow\Queue\ProducerService; use Bow\Database\Database as DB; use Bow\Http\Exception\HttpException; @@ -852,11 +854,11 @@ function cookie( * @param array $inputs * @param array $rules * @param array $messages - * @return \Bow\Validation\Validate + * @return Validate */ - function validator(array $inputs, array $rules, array $messages = []): \Bow\Validation\Validate + function validator(array $inputs, array $rules, array $messages = []): Validate { - return \Bow\Validation\Validator::make($inputs, $rules, $messages); + return Validator::make($inputs, $rules, $messages); } } diff --git a/src/View/View.php b/src/View/View.php index f28c0c91..3ad2a0b8 100644 --- a/src/View/View.php +++ b/src/View/View.php @@ -4,9 +4,10 @@ namespace Bow\View; +use Tintin\Tintin; use BadMethodCallException; -use Bow\Contracts\ResponseInterface; use Bow\View\EngineAbstract; +use Bow\Contracts\ResponseInterface; use Bow\View\Exception\ViewException; class View implements ResponseInterface From 22ada09b19d7c23a3535a10ac4a4ff8c143394c9 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 16 Dec 2024 18:57:47 +0000 Subject: [PATCH 173/199] feat: add paginate class --- src/Database/Pagination.php | 41 +++++++++++++++++++++++++++++++++++ src/Database/QueryBuilder.php | 20 ++++++++--------- src/Http/Request.php | 2 +- src/Support/Str.php | 2 +- 4 files changed, 53 insertions(+), 12 deletions(-) create mode 100644 src/Database/Pagination.php diff --git a/src/Database/Pagination.php b/src/Database/Pagination.php new file mode 100644 index 00000000..f5713bb3 --- /dev/null +++ b/src/Database/Pagination.php @@ -0,0 +1,41 @@ +next; + } + + public function previous(): int + { + return $this->next; + } + + public function current(): int + { + return $this->current; + } + + public function items(): array + { + return $this->data; + } + + public function total(): array + { + return $this->data; + } +} diff --git a/src/Database/QueryBuilder.php b/src/Database/QueryBuilder.php index 773d0d57..c3ef0890 100644 --- a/src/Database/QueryBuilder.php +++ b/src/Database/QueryBuilder.php @@ -1187,9 +1187,9 @@ public function drop(): bool * @param int $number_of_page * @param int $current * @param int $chunk - * @return array + * @return Pagination */ - public function paginate(int $number_of_page, int $current = 0, ?int $chunk = null): array + public function paginate(int $number_of_page, int $current = 0, ?int $chunk = null): Pagination { // We go to back page --$current; @@ -1224,14 +1224,14 @@ public function paginate(int $number_of_page, int $current = 0, ?int $chunk = nu } // Enables automatic paging. - return [ - 'next' => $current >= 1 && $rest_of_page > 0 ? $current + 1 : false, - 'previous' => ($current - 1) <= 0 ? 1 : ($current - 1), - 'total' => (int) ($rest_of_page + $current), - 'per_page' => $number_of_page, - 'current' => $current, - 'data' => $data - ]; + return new Pagination( + $current >= 1 && $rest_of_page > 0 ? $current + 1 : false, + ($current - 1) <= 0 ? 1 : ($current - 1), + (int) ($rest_of_page + $current), + $number_of_page, + $current, + $data + ); } /** diff --git a/src/Http/Request.php b/src/Http/Request.php index 8587bcbe..a4b01d80 100644 --- a/src/Http/Request.php +++ b/src/Http/Request.php @@ -543,7 +543,7 @@ public function hasHeader($key): bool /** * Get the client user agent - * + * * @return ?string */ public function userAgent(): ?string diff --git a/src/Support/Str.php b/src/Support/Str.php index 45e152cd..cabf5b3e 100644 --- a/src/Support/Str.php +++ b/src/Support/Str.php @@ -254,7 +254,7 @@ public static function slugify(string $str, string $delimiter = '-'): string /** * Alias of slugify - * + * * @param string $str * @param string $delimiter * @return string From 33aa5fdea9f607534cfca0424f5b743624e57816 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 16 Dec 2024 19:02:12 +0000 Subject: [PATCH 174/199] test: add unity test for pagination --- src/Database/Pagination.php | 23 ++++++++++-- src/Database/QueryBuilder.php | 4 +- tests/Database/PaginationTest.php | 50 +++++++++++++++++++++++++ tests/Database/Query/PaginationTest.php | 43 ++++++++++----------- 4 files changed, 93 insertions(+), 27 deletions(-) create mode 100644 tests/Database/PaginationTest.php diff --git a/src/Database/Pagination.php b/src/Database/Pagination.php index f5713bb3..a3898388 100644 --- a/src/Database/Pagination.php +++ b/src/Database/Pagination.php @@ -19,9 +19,24 @@ public function next(): int return $this->next; } + public function hasNext(): bool + { + return $this->next != 0; + } + + public function perPage(): int + { + return $this->perPage; + } + public function previous(): int { - return $this->next; + return $this->previous; + } + + public function hasPrevious(): bool + { + return $this->previous != 0; } public function current(): int @@ -31,11 +46,11 @@ public function current(): int public function items(): array { - return $this->data; + return (array) $this->data; } - public function total(): array + public function total(): int { - return $this->data; + return $this->total; } } diff --git a/src/Database/QueryBuilder.php b/src/Database/QueryBuilder.php index c3ef0890..64adc7c4 100644 --- a/src/Database/QueryBuilder.php +++ b/src/Database/QueryBuilder.php @@ -1208,7 +1208,7 @@ public function paginate(int $number_of_page, int $current = 0, ?int $chunk = nu $join = $this->join; $data_bind = $this->where_data_binding; - $data = $this->jump($jump)->take($number_of_page)->get(); + $data = (array) $this->jump($jump)->take($number_of_page)->get(); // Reinitialisation of current query $this->where = $where; @@ -1225,7 +1225,7 @@ public function paginate(int $number_of_page, int $current = 0, ?int $chunk = nu // Enables automatic paging. return new Pagination( - $current >= 1 && $rest_of_page > 0 ? $current + 1 : false, + $current >= 1 && $rest_of_page > 0 ? $current + 1 : 0, ($current - 1) <= 0 ? 1 : ($current - 1), (int) ($rest_of_page + $current), $number_of_page, diff --git a/tests/Database/PaginationTest.php b/tests/Database/PaginationTest.php new file mode 100644 index 00000000..47173628 --- /dev/null +++ b/tests/Database/PaginationTest.php @@ -0,0 +1,50 @@ +pagination = new Pagination( + next: 2, + previous: 0, + total: 3, + perPage: 10, + current: 1, + data: ['item1', 'item2', 'item3'] + ); + } + + public function test_next(): void + { + $this->assertSame(2, $this->pagination->next()); + } + + public function test_previous(): void + { + $this->assertSame(0, $this->pagination->previous()); + } + + public function test_current(): void + { + $this->assertSame(1, $this->pagination->current()); + } + + public function test_items(): void + { + $this->assertSame(['item1', 'item2', 'item3'], $this->pagination->items()); + } + + public function test_total(): void + { + $this->assertSame(3, $this->pagination->total()); + } +} diff --git a/tests/Database/Query/PaginationTest.php b/tests/Database/Query/PaginationTest.php index adfa5203..d0487569 100644 --- a/tests/Database/Query/PaginationTest.php +++ b/tests/Database/Query/PaginationTest.php @@ -3,6 +3,7 @@ namespace Bow\Tests\Database\Query; use Bow\Database\Database; +use Bow\Database\Pagination; use Bow\Tests\Config\TestingConfiguration; class PaginationTest extends \PHPUnit\Framework\TestCase @@ -22,13 +23,13 @@ public function test_go_current_pagination(string $name) $this->createTestingTable($name); $result = Database::table("pets")->paginate(10); - $this->assertIsArray($result); - $this->assertEquals(count($result["data"]), 10); - $this->assertEquals($result["per_page"], 10); - $this->assertEquals($result["total"], 3); - $this->assertEquals($result["current"], 1); - $this->assertEquals($result["previous"], 1); - $this->assertEquals($result["next"], 2); + $this->assertInstanceOf(Pagination::class, $result); + $this->assertEquals(count($result->items()), 10); + $this->assertEquals($result->perPage(), 10); + $this->assertEquals($result->total(), 3); + $this->assertEquals($result->current(), 1); + $this->assertEquals($result->previous(), 1); + $this->assertEquals($result->next(), 2); } /** @@ -40,13 +41,13 @@ public function test_go_next_2_pagination(string $name) $this->createTestingTable($name); $result = Database::table("pets")->paginate(10, 2); - $this->assertIsArray($result); - $this->assertEquals(count($result["data"]), 10); - $this->assertEquals($result["per_page"], 10); - $this->assertEquals($result["total"], 3); - $this->assertEquals($result["current"], 2); - $this->assertEquals($result["previous"], 1); - $this->assertEquals($result["next"], 3); + $this->assertInstanceOf(Pagination::class, $result); + $this->assertEquals(count($result->items()), 10); + $this->assertEquals($result->perPage(), 10); + $this->assertEquals($result->total(), 3); + $this->assertEquals($result->current(), 2); + $this->assertEquals($result->previous(), 1); + $this->assertEquals($result->next(), 3); } /** @@ -58,13 +59,13 @@ public function test_go_next_3_pagination(string $name) $this->createTestingTable($name); $result = Database::table("pets")->paginate(10, 3); - $this->assertIsArray($result); - $this->assertEquals(count($result["data"]), 10); - $this->assertEquals($result["per_page"], 10); - $this->assertEquals($result["total"], 3); - $this->assertEquals($result["current"], 3); - $this->assertEquals($result["previous"], 2); - $this->assertEquals($result["next"], false); + $this->assertInstanceOf(Pagination::class, $result); + $this->assertEquals(count($result->items()), 10); + $this->assertEquals($result->perPage(), 10); + $this->assertEquals($result->total(), 3); + $this->assertEquals($result->current(), 3); + $this->assertEquals($result->previous(), 2); + $this->assertEquals($result->next(), false); } /** From d46eb2b9dd035f15f798637c7119cf95e021a5cd Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 16 Dec 2024 20:02:24 +0000 Subject: [PATCH 175/199] feat: add new method --- src/Support/Str.php | 22 ++++++++++++++++++++++ src/Support/helpers.php | 41 ++++++++++++++++++++--------------------- 2 files changed, 42 insertions(+), 21 deletions(-) diff --git a/src/Support/Str.php b/src/Support/Str.php index cabf5b3e..9000a12b 100644 --- a/src/Support/Str.php +++ b/src/Support/Str.php @@ -6,6 +6,7 @@ use ErrorException; use ForceUTF8\Encoding; +use Ramsey\Uuid\Uuid; class Str { @@ -233,6 +234,16 @@ public static function random(int $size = 16): string return static::slice(str_shuffle('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'), 0, $size); } + /** + * Get rondom uuid + * + * @return string + */ + public static function uuid(): string + { + return Uuid::uuid4()->toString(); + } + /** * slugify slug creator using a simple chain. * eg: 'I am a string of character' => 'i-am-a-chain-of-character' @@ -275,6 +286,17 @@ public static function unSlugify(string $str): string return preg_replace('/[^a-z0-9]/', ' ', strtolower(trim(strip_tags($str)))); } + /** + * Alias of unslugify + * + * @param string $str + * @return string + */ + public static function unSlug(string $str): string + { + return static::unSlugify($str); + } + /** * Check if the email is a valid email. * diff --git a/src/Support/helpers.php b/src/Support/helpers.php index b4ecca56..2093a863 100644 --- a/src/Support/helpers.php +++ b/src/Support/helpers.php @@ -25,6 +25,7 @@ use Bow\Queue\ProducerService; use Bow\Database\Database as DB; use Bow\Http\Exception\HttpException; +use Bow\Mail\Contracts\MailDriverInterface; if (!function_exists('app')) { /** @@ -553,7 +554,7 @@ function get_header(string $key): ?string */ function redirect(string $path = null): Redirect { - $redirect = \Bow\Http\Redirect::getInstance(); + $redirect = Redirect::getInstance(); if ($path !== null) { $redirect->to($path); @@ -751,14 +752,14 @@ function flash(string $key, string $message) * @param null|string $view * @param array $data * @param callable $cb - * @return \Bow\Mail\Contracts\MailDriverInterface|bool + * @return MailDriverInterface|bool * @throws */ function email( string $view = null, array $data = [], callable $cb = null - ): \Bow\Mail\Contracts\MailDriverInterface|bool { + ): MailDriverInterface|bool { if ($view === null) { return Mail::getInstance(); } @@ -949,21 +950,7 @@ function storage_service(string $service) } } -if (!function_exists('mount')) { - /** - * Alias on the mount method - * - * @param string $mount - * @return \Bow\Storage\Service\DiskFilesystemService - * @throws \Bow\Storage\Exception\ResourceException - */ - function mount(string $mount): \Bow\Storage\Service\DiskFilesystemService - { - return Storage::disk($mount); - } -} - -if (!function_exists('file_system')) { +if (!function_exists('app_file_system')) { /** * Alias on the mount method * @@ -971,7 +958,7 @@ function mount(string $mount): \Bow\Storage\Service\DiskFilesystemService * @return \Bow\Storage\Service\DiskFilesystemService * @throws \Bow\Storage\Exception\ResourceException */ - function file_system(string $disk): \Bow\Storage\Service\DiskFilesystemService + function app_file_system(string $disk): \Bow\Storage\Service\DiskFilesystemService { return Storage::disk($disk); } @@ -1058,7 +1045,7 @@ function bow_hash(string $data, string $hash_value = null): bool|string } } -if (!function_exists('app_trans(')) { +if (!function_exists('app_trans')) { /** * Make translation * @@ -1180,7 +1167,7 @@ function app_abort_if( bool $boolean, int $code, string $message = '' - ): \Bow\Http\Response|null { + ): Response|null { if ($boolean) { return app_abort($code, $message); } @@ -1298,6 +1285,18 @@ function str_is_mail(string $email): bool } } +if (!function_exists('str_uuid')) { + /** + * Get str uuid + * + * @return bool + */ + function str_uuid(): bool + { + return \Bow\Support\Str::uuid(); + } +} + if (!function_exists('str_is_domain')) { /** * Check if the string is domain From 2fda8ca2300ecebbaec2e9f9c7be5022cd657dda Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 16 Dec 2024 20:06:17 +0000 Subject: [PATCH 176/199] fix: refond response helper --- src/Support/helpers.php | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/Support/helpers.php b/src/Support/helpers.php index 2093a863..9d0b92cc 100644 --- a/src/Support/helpers.php +++ b/src/Support/helpers.php @@ -7,6 +7,7 @@ use Monolog\Logger; use Bow\Event\Event; use Bow\Support\Env; +use Bow\Support\Str; use Bow\Http\Request; use Bow\Support\Util; use Bow\Http\Redirect; @@ -1268,7 +1269,7 @@ function logger(): Logger */ function str_slug(string $str, string $sep = '-'): string { - return \Bow\Support\Str::slugify($str, $sep); + return Str::slugify($str, $sep); } } @@ -1281,7 +1282,7 @@ function str_slug(string $str, string $sep = '-'): string */ function str_is_mail(string $email): bool { - return \Bow\Support\Str::isMail($email); + return Str::isMail($email); } } @@ -1293,7 +1294,7 @@ function str_is_mail(string $email): bool */ function str_uuid(): bool { - return \Bow\Support\Str::uuid(); + return Str::uuid(); } } @@ -1307,7 +1308,7 @@ function str_uuid(): bool */ function str_is_domain(string $domain): bool { - return \Bow\Support\Str::isDomain($domain); + return Str::isDomain($domain); } } @@ -1321,7 +1322,7 @@ function str_is_domain(string $domain): bool */ function str_is_slug(string $slug): string { - return \Bow\Support\Str::isSlug($slug); + return Str::isSlug($slug); } } @@ -1335,7 +1336,7 @@ function str_is_slug(string $slug): string */ function str_is_alpha(string $string): bool { - return \Bow\Support\Str::isAlpha($string); + return Str::isAlpha($string); } } @@ -1348,7 +1349,7 @@ function str_is_alpha(string $string): bool */ function str_is_lower(string $string): bool { - return \Bow\Support\Str::isLower($string); + return Str::isLower($string); } } @@ -1361,7 +1362,7 @@ function str_is_lower(string $string): bool */ function str_is_upper(string $string): bool { - return \Bow\Support\Str::isUpper($string); + return Str::isUpper($string); } } @@ -1375,7 +1376,7 @@ function str_is_upper(string $string): bool */ function str_is_alpha_num(string $slug): bool { - return \Bow\Support\Str::isAlphaNum($slug); + return Str::isAlphaNum($slug); } } @@ -1388,7 +1389,7 @@ function str_is_alpha_num(string $slug): bool */ function str_shuffle_words(string $words): string { - return \Bow\Support\Str::shuffleWords($words); + return Str::shuffleWords($words); } } @@ -1402,7 +1403,7 @@ function str_shuffle_words(string $words): string */ function str_wordify(string $words, string $sep = ''): array { - return \Bow\Support\Str::wordify($words, $sep); + return Str::wordify($words, $sep); } } @@ -1415,7 +1416,7 @@ function str_wordify(string $words, string $sep = ''): array */ function str_plurial(string $slug): string { - return \Bow\Support\Str::plurial($slug); + return Str::plurial($slug); } } @@ -1428,7 +1429,7 @@ function str_plurial(string $slug): string */ function str_camel($slug): string { - return \Bow\Support\Str::camel($slug); + return Str::camel($slug); } } @@ -1441,7 +1442,7 @@ function str_camel($slug): string */ function str_snake(string $slug): string { - return \Bow\Support\Str::snake($slug); + return Str::snake($slug); } } @@ -1455,7 +1456,7 @@ function str_snake(string $slug): string */ function str_contains(string $search, string $string): bool { - return \Bow\Support\Str::contains($search, $string); + return Str::contains($search, $string); } } @@ -1468,7 +1469,7 @@ function str_contains(string $search, string $string): bool */ function str_capitalize(string $slug): string { - return \Bow\Support\Str::capitalize($slug); + return Str::capitalize($slug); } } @@ -1481,7 +1482,7 @@ function str_capitalize(string $slug): string */ function str_random(string $string): string { - return \Bow\Support\Str::random($string); + return Str::random($string); } } @@ -1493,7 +1494,7 @@ function str_random(string $string): string */ function str_force_in_utf8(): void { - \Bow\Support\Str::forceInUTF8(); + Str::forceInUTF8(); } } @@ -1506,7 +1507,7 @@ function str_force_in_utf8(): void */ function str_fix_utf8(string $string): string { - return \Bow\Support\Str::fixUTF8($string); + return Str::fixUTF8($string); } } From 9e6ae1f6ba6d4fffc6e9d063fa0bd1e9cdb2cc0d Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 16 Dec 2024 20:41:27 +0000 Subject: [PATCH 177/199] fix: return value of str_uuid --- src/Support/helpers.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Support/helpers.php b/src/Support/helpers.php index 9d0b92cc..6d7c98b2 100644 --- a/src/Support/helpers.php +++ b/src/Support/helpers.php @@ -1290,9 +1290,9 @@ function str_is_mail(string $email): bool /** * Get str uuid * - * @return bool + * @return string */ - function str_uuid(): bool + function str_uuid(): string { return Str::uuid(); } From 466b0bb7334015706c2824257c33e51f358c61d5 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 16 Dec 2024 20:55:27 +0000 Subject: [PATCH 178/199] refactor: change uploaded file name --- src/Http/Exception/UploadFileException.php | 2 +- src/Http/Request.php | 8 ++-- src/Http/{UploadFile.php => UploadedFile.php} | 4 +- src/Storage/Contracts/FilesystemInterface.php | 6 +-- src/Storage/Service/DiskFilesystemService.php | 6 +-- src/Storage/Service/FTPService.php | 6 +-- src/Storage/Service/S3Service.php | 6 +-- src/Support/helpers.php | 42 +++++++++++-------- tests/Filesystem/DiskFilesystemTest.php | 16 +++---- tests/Filesystem/FTPServiceTest.php | 2 +- 10 files changed, 52 insertions(+), 46 deletions(-) rename src/Http/{UploadFile.php => UploadedFile.php} (98%) diff --git a/src/Http/Exception/UploadFileException.php b/src/Http/Exception/UploadFileException.php index 3c27232d..23cc6494 100644 --- a/src/Http/Exception/UploadFileException.php +++ b/src/Http/Exception/UploadFileException.php @@ -6,6 +6,6 @@ use ErrorException; -class UploadFileException extends ErrorException +class UploadedFileException extends ErrorException { } diff --git a/src/Http/Request.php b/src/Http/Request.php index a4b01d80..5a6b8dd5 100644 --- a/src/Http/Request.php +++ b/src/Http/Request.php @@ -291,23 +291,23 @@ public function isDelete(): bool * Load the factory for FILES * * @param string $key - * @return UploadFile|Collection|null + * @return UploadedFile|Collection|null */ - public function file(string $key): UploadFile|Collection|null + public function file(string $key): UploadedFile|Collection|null { if (!isset($_FILES[$key])) { return null; } if (!is_array($_FILES[$key]['name'])) { - return new UploadFile($_FILES[$key]); + return new UploadedFile($_FILES[$key]); } $files = $_FILES[$key]; $collect = []; foreach ($files['name'] as $key => $name) { - $collect[] = new UploadFile([ + $collect[] = new UploadedFile([ 'name' => $name, 'type' => $files['type'][$key], 'size' => $files['size'][$key], diff --git a/src/Http/UploadFile.php b/src/Http/UploadedFile.php similarity index 98% rename from src/Http/UploadFile.php rename to src/Http/UploadedFile.php index 7618e0af..e08986c1 100644 --- a/src/Http/UploadFile.php +++ b/src/Http/UploadedFile.php @@ -4,7 +4,7 @@ namespace Bow\Http; -class UploadFile +class UploadedFile { /** * @var array @@ -12,7 +12,7 @@ class UploadFile private array $file; /** - * UploadFile constructor. + * UploadedFile constructor. * * @param array $file */ diff --git a/src/Storage/Contracts/FilesystemInterface.php b/src/Storage/Contracts/FilesystemInterface.php index 8c47906d..7c7d1dd1 100644 --- a/src/Storage/Contracts/FilesystemInterface.php +++ b/src/Storage/Contracts/FilesystemInterface.php @@ -4,7 +4,7 @@ namespace Bow\Storage\Contracts; -use Bow\Http\UploadFile; +use Bow\Http\UploadedFile; use InvalidArgumentException; interface FilesystemInterface @@ -12,13 +12,13 @@ interface FilesystemInterface /** * Store directly the upload file * - * @param UploadFile $file + * @param UploadedFile $file * @param string $location * @param array $option * @return bool * @throws InvalidArgumentException */ - public function store(UploadFile $file, ?string $location = null, array $option = []): array|bool; + public function store(UploadedFile $file, ?string $location = null, array $option = []): array|bool; /** * Write following a file specify diff --git a/src/Storage/Service/DiskFilesystemService.php b/src/Storage/Service/DiskFilesystemService.php index 14928935..1f4f0b40 100644 --- a/src/Storage/Service/DiskFilesystemService.php +++ b/src/Storage/Service/DiskFilesystemService.php @@ -4,7 +4,7 @@ namespace Bow\Storage\Service; -use Bow\Http\UploadFile; +use Bow\Http\UploadedFile; use Bow\Storage\Contracts\FilesystemInterface; use InvalidArgumentException; @@ -51,14 +51,14 @@ public function getBaseDirectory(): string /** * Function to upload a file * - * @param UploadFile $file + * @param UploadedFile $file * @param string|array $location * @param array $option * * @return bool * @throws InvalidArgumentException */ - public function store(UploadFile $file, string|array $location = null, array $option = []): bool + public function store(UploadedFile $file, string|array $location = null, array $option = []): bool { if (is_array($location)) { $option = $location; diff --git a/src/Storage/Service/FTPService.php b/src/Storage/Service/FTPService.php index f6008a36..7a1eaf58 100644 --- a/src/Storage/Service/FTPService.php +++ b/src/Storage/Service/FTPService.php @@ -4,7 +4,7 @@ namespace Bow\Storage\Service; -use Bow\Http\UploadFile; +use Bow\Http\UploadedFile; use Bow\Storage\Contracts\ServiceInterface; use Bow\Storage\Exception\ResourceException; use InvalidArgumentException; @@ -199,14 +199,14 @@ public function getCurrentDirectory() /** * Store directly the upload file * - * @param UploadFile $file + * @param UploadedFile $file * @param string $location * @param array $option * * @return mixed * @throws InvalidArgumentException */ - public function store(UploadFile $file, ?string $location = null, array $option = []): array|bool + public function store(UploadedFile $file, ?string $location = null, array $option = []): array|bool { if (is_null($location)) { throw new InvalidArgumentException("Please define the store location"); diff --git a/src/Storage/Service/S3Service.php b/src/Storage/Service/S3Service.php index 85b9ffc2..68991432 100644 --- a/src/Storage/Service/S3Service.php +++ b/src/Storage/Service/S3Service.php @@ -5,7 +5,7 @@ namespace Bow\Storage\Service; use Aws\S3\S3Client; -use Bow\Http\UploadFile; +use Bow\Http\UploadedFile; use Bow\Storage\Contracts\ServiceInterface; class S3Service implements ServiceInterface @@ -73,14 +73,14 @@ public static function getInstance(): S3Service /** * Function to upload a file * - * @param UploadFile $file + * @param UploadedFile $file * @param string $location * @param array $option * * @return mixed * @throws InvalidArgumentException */ - public function store(UploadFile $file, ?string $location = null, array $option = []): array|bool + public function store(UploadedFile $file, ?string $location = null, array $option = []): array|bool { $result = $this->put($file->getHashName(), $file->getContent()); diff --git a/src/Support/helpers.php b/src/Support/helpers.php index 6d7c98b2..2fb6573a 100644 --- a/src/Support/helpers.php +++ b/src/Support/helpers.php @@ -18,15 +18,20 @@ use Bow\Session\Session; use Bow\Storage\Storage; use Bow\Container\Capsule; +use Bow\Security\Sanitize; use Bow\Security\Tokenize; use Bow\Support\Collection; use Bow\Validation\Validate; +use Bow\Database\Barry\Model; use Bow\Translate\Translator; use Bow\Validation\Validator; use Bow\Queue\ProducerService; use Bow\Database\Database as DB; +use Bow\Auth\Guards\GuardContract; use Bow\Http\Exception\HttpException; use Bow\Mail\Contracts\MailDriverInterface; +use Bow\Storage\Exception\ResourceException; +use Bow\Storage\Service\DiskFilesystemService; if (!function_exists('app')) { /** @@ -498,7 +503,7 @@ function sanitize(mixed $data): mixed return $data; } - return \Bow\Security\Sanitize::make($data); + return Sanitize::make($data); } } @@ -515,7 +520,7 @@ function secure(mixed $data): mixed return $data; } - return \Bow\Security\Sanitize::make($data, true); + return Sanitize::make($data, true); } } @@ -628,9 +633,9 @@ function set_pdo(PDO $pdo): PDO * Create new Ccollection instance * * @param array $data - * @return \Bow\Support\Collection + * @return Collection */ - function collect(array $data = []): \Bow\Support\Collection + function collect(array $data = []): Collection { return new Collection($data); } @@ -645,7 +650,7 @@ function collect(array $data = []): \Bow\Support\Collection */ function encrypt(string $data): string { - return \Bow\Security\Crypto::encrypt($data); + return Crypto::encrypt($data); } } @@ -956,10 +961,10 @@ function storage_service(string $service) * Alias on the mount method * * @param string $disk - * @return \Bow\Storage\Service\DiskFilesystemService - * @throws \Bow\Storage\Exception\ResourceException + * @return DiskFilesystemService + * @throws ResourceException */ - function app_file_system(string $disk): \Bow\Storage\Service\DiskFilesystemService + function app_file_system(string $disk): DiskFilesystemService { return Storage::disk($disk); } @@ -1053,13 +1058,13 @@ function bow_hash(string $data, string $hash_value = null): bool|string * @param string $key * @param array $data * @param bool $choose - * @return string|Bow\Translate\Translator + * @return string|Translator */ function app_trans( string $key = null, array $data = [], bool $choose = false - ): string|Bow\Translate\Translator { + ): string|Translator { if (is_null($key)) { return Translator::getInstance(); } @@ -1086,7 +1091,7 @@ function t( string $key, array $data = [], bool $choose = false - ): string|Bow\Translate\Translator { + ): string|Translator { return app_trans($key, $data, $choose); } } @@ -1104,7 +1109,7 @@ function __( string $key, array $data = [], bool $choose = false - ): string|Bow\Translate\Translator { + ): string|Translator { return app_trans($key, $data, $choose); } } @@ -1146,7 +1151,7 @@ function app_assets(string $filename): string * * @param int $code * @param string $message - * @return \Bow\Http\Response + * @return Response * @throws HttpException */ function app_abort(int $code = 500, string $message = '') @@ -1162,7 +1167,7 @@ function app_abort(int $code = 500, string $message = '') * @param boolean $boolean * @param int $code * @param string $message - * @return \Bow\Http\Response|null + * @return Response|null */ function app_abort_if( bool $boolean, @@ -1232,10 +1237,10 @@ function old(string $key, mixed $fullback = null): mixed * Recovery of the guard * * @param string $guard - * @return \Bow\Auth\Guards\GuardContract + * @return GuardContract * @throws */ - function auth(string $guard = null): \Bow\Auth\Guards\GuardContract + function auth(string $guard = null): GuardContract { $auth = Auth::getInstance(); @@ -1523,7 +1528,8 @@ function db_seed(string $name, array $data = []): mixed { if (class_exists($name, true)) { $instance = app($name); - if ($instance instanceof \Bow\Database\Barry\Model) { + + if ($instance instanceof Model) { $table = $instance->getTable(); return DB::table($table)->insert($data); } @@ -1542,7 +1548,7 @@ function db_seed(string $name, array $data = []): mixed foreach ($seeds as $table => $payload) { if (class_exists($table, true)) { $instance = app($table); - if ($instance instanceof \Bow\Database\Barry\Model) { + if ($instance instanceof Model) { $table = $instance->getTable(); } } diff --git a/tests/Filesystem/DiskFilesystemTest.php b/tests/Filesystem/DiskFilesystemTest.php index 06a083a3..0659af03 100644 --- a/tests/Filesystem/DiskFilesystemTest.php +++ b/tests/Filesystem/DiskFilesystemTest.php @@ -2,7 +2,7 @@ namespace Bow\Tests\Filesystem; -use Bow\Http\UploadFile; +use Bow\Http\UploadedFile; use Bow\Storage\Service\DiskFilesystemService; use Bow\Storage\Storage; use Bow\Tests\Config\TestingConfiguration; @@ -29,9 +29,9 @@ public function setUp(): void $this->storage = Storage::disk(); } - public function getUploadFileMock(): \PHPUnit\Framework\MockObject\MockObject + public function getUploadedFileMock(): \PHPUnit\Framework\MockObject\MockObject { - $uploadedFile = $this->getMockBuilder(UploadFile::class) + $uploadedFile = $this->getMockBuilder(UploadedFile::class) ->disableOriginalConstructor() ->getMock(); @@ -127,7 +127,7 @@ public function test_put_as_sucess() public function test_store() { - $uploadedFile = $this->getUploadFileMock(); + $uploadedFile = $this->getUploadedFileMock(); $filename = sprintf("%s.txt", md5(time())); $uploadedFile->method("getHashName")->willReturn($filename); @@ -138,7 +138,7 @@ public function test_store() public function test_store_on_custom_store() { - $uploadedFile = $this->getUploadFileMock(); + $uploadedFile = $this->getUploadedFileMock(); $filename = sprintf("%s.txt", md5(time())); $uploadedFile->method("getHashName")->willReturn($filename); @@ -149,7 +149,7 @@ public function test_store_on_custom_store() public function test_store_with_location_by_filename_setting() { - $uploadedFile = $this->getUploadFileMock(); + $uploadedFile = $this->getUploadedFileMock(); $filename = "stub_store_filename.txt"; $result = $this->storage->store($uploadedFile, "stores", [ @@ -162,7 +162,7 @@ public function test_store_with_location_by_filename_setting() public function test_store_with_location_as_null_and_filename_as_null() { - $uploadedFile = $this->getUploadFileMock(); + $uploadedFile = $this->getUploadedFileMock(); $filename = sprintf("%s.txt", md5(time())); $uploadedFile->method("getHashName")->willReturn($filename); @@ -175,7 +175,7 @@ public function test_store_with_location_as_null_and_filename_as_null() public function test_store_with_location_as_array_with_as_filename_key() { - $uploadedFile = $this->getUploadFileMock(); + $uploadedFile = $this->getUploadedFileMock(); $result = $this->storage->store($uploadedFile, [ "as" => "stub_store_filename.txt" diff --git a/tests/Filesystem/FTPServiceTest.php b/tests/Filesystem/FTPServiceTest.php index 4a26f007..ce08dae7 100644 --- a/tests/Filesystem/FTPServiceTest.php +++ b/tests/Filesystem/FTPServiceTest.php @@ -180,7 +180,7 @@ public function test_put_content_into_file() private function createFile(FTPService $ftp_service, $filename, $content = '') { - $uploadedFile = $this->getMockBuilder(\Bow\Http\UploadFile::class) + $uploadedFile = $this->getMockBuilder(\Bow\Http\UploadedFile::class) ->disableOriginalConstructor() ->getMock(); From 797afc162dff1ae27cfd6867e0c0110ca0bc7fe2 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 16 Dec 2024 21:14:26 +0000 Subject: [PATCH 179/199] fix: return type compatibility --- src/Storage/Contracts/FilesystemInterface.php | 4 ++-- src/Storage/Service/DiskFilesystemService.php | 4 ++-- src/Storage/Service/FTPService.php | 4 ++-- src/Storage/Service/S3Service.php | 6 ++---- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/Storage/Contracts/FilesystemInterface.php b/src/Storage/Contracts/FilesystemInterface.php index 7c7d1dd1..c891a67d 100644 --- a/src/Storage/Contracts/FilesystemInterface.php +++ b/src/Storage/Contracts/FilesystemInterface.php @@ -15,10 +15,10 @@ interface FilesystemInterface * @param UploadedFile $file * @param string $location * @param array $option - * @return bool + * @return array|bool|string * @throws InvalidArgumentException */ - public function store(UploadedFile $file, ?string $location = null, array $option = []): array|bool; + public function store(UploadedFile $file, ?string $location = null, array $option = []): array|bool|string; /** * Write following a file specify diff --git a/src/Storage/Service/DiskFilesystemService.php b/src/Storage/Service/DiskFilesystemService.php index 1f4f0b40..46f00782 100644 --- a/src/Storage/Service/DiskFilesystemService.php +++ b/src/Storage/Service/DiskFilesystemService.php @@ -55,10 +55,10 @@ public function getBaseDirectory(): string * @param string|array $location * @param array $option * - * @return bool + * @return array|bool|string * @throws InvalidArgumentException */ - public function store(UploadedFile $file, string|array $location = null, array $option = []): bool + public function store(UploadedFile $file, string|array $location = null, array $option = []): array|bool|string { if (is_array($location)) { $option = $location; diff --git a/src/Storage/Service/FTPService.php b/src/Storage/Service/FTPService.php index 7a1eaf58..9eef6d2b 100644 --- a/src/Storage/Service/FTPService.php +++ b/src/Storage/Service/FTPService.php @@ -203,10 +203,10 @@ public function getCurrentDirectory() * @param string $location * @param array $option * - * @return mixed + * @return array|bool|string * @throws InvalidArgumentException */ - public function store(UploadedFile $file, ?string $location = null, array $option = []): array|bool + public function store(UploadedFile $file, ?string $location = null, array $option = []): array|bool|string { if (is_null($location)) { throw new InvalidArgumentException("Please define the store location"); diff --git a/src/Storage/Service/S3Service.php b/src/Storage/Service/S3Service.php index 68991432..fd0f334f 100644 --- a/src/Storage/Service/S3Service.php +++ b/src/Storage/Service/S3Service.php @@ -76,11 +76,9 @@ public static function getInstance(): S3Service * @param UploadedFile $file * @param string $location * @param array $option - * - * @return mixed - * @throws InvalidArgumentException + * @return array|bool|string */ - public function store(UploadedFile $file, ?string $location = null, array $option = []): array|bool + public function store(UploadedFile $file, ?string $location = null, array $option = []): array|bool|string { $result = $this->put($file->getHashName(), $file->getContent()); From 291a54deceecaa36381677264b1305bb2b045b68 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 16 Dec 2024 21:47:04 +0000 Subject: [PATCH 180/199] fix: upload exception --- .../{UploadFileException.php => UploadedFileException.php} | 0 src/Http/Request.php | 7 ++++--- src/Router/Router.php | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) rename src/Http/Exception/{UploadFileException.php => UploadedFileException.php} (100%) diff --git a/src/Http/Exception/UploadFileException.php b/src/Http/Exception/UploadedFileException.php similarity index 100% rename from src/Http/Exception/UploadFileException.php rename to src/Http/Exception/UploadedFileException.php diff --git a/src/Http/Request.php b/src/Http/Request.php index 5a6b8dd5..349d5fb6 100644 --- a/src/Http/Request.php +++ b/src/Http/Request.php @@ -4,13 +4,14 @@ namespace Bow\Http; -use Bow\Auth\Authentication; -use Bow\Http\Exception\BadRequestException; +use Bow\Support\Str; use Bow\Session\Session; +use Bow\Http\UploadedFile; use Bow\Support\Collection; -use Bow\Support\Str; +use Bow\Auth\Authentication; use Bow\Validation\Validate; use Bow\Validation\Validator; +use Bow\Http\Exception\BadRequestException; class Request { diff --git a/src/Router/Router.php b/src/Router/Router.php index e73a6696..2cd16ffb 100644 --- a/src/Router/Router.php +++ b/src/Router/Router.php @@ -333,7 +333,7 @@ public function code(int $code, callable|array|string $cb): Router * @param array $methods * @param string $path * @param callable|string|array $cb - * @return Router + * @return Route */ public function match(array $methods, string $path, callable|string|array $cb): Route { From f8bd89f572ab92d46efcfe03516ad41ba1bb2ca5 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 16 Dec 2024 22:51:39 +0000 Subject: [PATCH 181/199] chore: upgrade psy/psysh --- composer.json | 2 +- src/Console/Console.php | 2 -- src/Session/Driver/ArrayDriver.php | 6 +++--- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index 64219a4e..ed6a6f1e 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ "bowphp/tintin": "^3.0", "filp/whoops": "^2.1", "nesbot/carbon": "^2.16", - "psy/psysh": "v0.10.*", + "psy/psysh": "v0.12.*", "fakerphp/faker": "^1.20", "neitanod/forceutf8": "^2.0", "ramsey/uuid": "^4.7" diff --git a/src/Console/Console.php b/src/Console/Console.php index 6b3f2f24..192caa12 100644 --- a/src/Console/Console.php +++ b/src/Console/Console.php @@ -7,8 +7,6 @@ use Bow\Configuration\Loader; use Bow\Console\Exception\ConsoleException; use Bow\Console\Traits\ConsoleTrait; -use Psy\Exception\FatalErrorException; -use RuntimeException; /** * @method static Console addCommand(string $command, callable $cb) diff --git a/src/Session/Driver/ArrayDriver.php b/src/Session/Driver/ArrayDriver.php index 6639434f..d8d4b7ad 100644 --- a/src/Session/Driver/ArrayDriver.php +++ b/src/Session/Driver/ArrayDriver.php @@ -40,9 +40,9 @@ public function destroy(string $session_id): bool * Garbage collector * * @param int $max_lifetime - * @return bool|void + * @return int|false */ - public function gc(int $max_lifetime): bool + public function gc(int $max_lifetime): int|false { foreach ($this->sessions as $session_id => $content) { if ($this->sessions[$session_id]['time'] <= $this->createTimestamp()) { @@ -50,7 +50,7 @@ public function gc(int $max_lifetime): bool } } - return true; + return 1; } /** From ce7f516732e1bda18f024dd25cb81c242bfb960f Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 16 Dec 2024 23:12:46 +0000 Subject: [PATCH 182/199] fix: change the basic extended class --- src/Http/Exception/HttpException.php | 8 ++++---- src/Http/Exception/RequestException.php | 21 ++++++++++++++++++--- src/Http/Exception/ResponseException.php | 4 +--- src/Http/Response.php | 2 +- 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/Http/Exception/HttpException.php b/src/Http/Exception/HttpException.php index f1cb2810..143c1b7a 100644 --- a/src/Http/Exception/HttpException.php +++ b/src/Http/Exception/HttpException.php @@ -26,7 +26,7 @@ class HttpException extends Exception * HttpException constructor * * @param string $message - * @param string $code + * @param int $code */ public function __construct(string $message, int $code = 200) { @@ -40,7 +40,7 @@ public function __construct(string $message, int $code = 200) * * @return string */ - public function getStatus() + public function getStatus(): string { return $this->status; } @@ -48,9 +48,9 @@ public function getStatus() /** * Get the status code * - * @return string + * @return int */ - public function getStatusCode() + public function getStatusCode(): int { return $this->getCode(); } diff --git a/src/Http/Exception/RequestException.php b/src/Http/Exception/RequestException.php index e351153b..14227228 100644 --- a/src/Http/Exception/RequestException.php +++ b/src/Http/Exception/RequestException.php @@ -4,8 +4,23 @@ namespace Bow\Http\Exception; -use ErrorException; - -class RequestException extends ErrorException +class RequestException extends HttpException { + /** + * Define the http response code + * + * @var int + */ + protected $code; + + /** + * Set the http code + * + * @param int $code + * @return void + */ + public function setCode(int $code) + { + $this->code = $code; + } } diff --git a/src/Http/Exception/ResponseException.php b/src/Http/Exception/ResponseException.php index 602f493d..d85ee80f 100644 --- a/src/Http/Exception/ResponseException.php +++ b/src/Http/Exception/ResponseException.php @@ -4,9 +4,7 @@ namespace Bow\Http\Exception; -use ErrorException; - -class ResponseException extends ErrorException +class ResponseException extends HttpException { /** * Define the http response code diff --git a/src/Http/Response.php b/src/Http/Response.php index 7030bb70..d942a6de 100644 --- a/src/Http/Response.php +++ b/src/Http/Response.php @@ -276,7 +276,7 @@ public function download( /** * Modify http headers * - * @param int $code + * @param int $code * @return mixed */ public function status(int $code): Response From 29d17f82df4e3a601391b1e0aad9f78eda11c765 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 16 Dec 2024 23:31:21 +0000 Subject: [PATCH 183/199] fix(log): fix log and data access config --- src/Configuration/LoggerConfiguration.php | 11 ++++++----- src/Container/Action.php | 4 ++-- src/Support/Arraydotify.php | 4 ++++ 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Configuration/LoggerConfiguration.php b/src/Configuration/LoggerConfiguration.php index 23072225..e98f5519 100644 --- a/src/Configuration/LoggerConfiguration.php +++ b/src/Configuration/LoggerConfiguration.php @@ -7,6 +7,7 @@ use Bow\View\View; use Monolog\Logger; use Bow\Support\Collection; +use Whoops\Handler\Handler; use Bow\Configuration\Loader; use Bow\Database\Barry\Model; use Monolog\Handler\StreamHandler; @@ -14,8 +15,8 @@ use Whoops\Handler\CallbackHandler; use Bow\Configuration\Configuration; use Bow\Contracts\ResponseInterface; +use Iterator; use Whoops\Handler\PrettyPageHandler; -use Whoops\Handler\Handler; class LoggerConfiguration extends Configuration { @@ -73,14 +74,14 @@ function ($exception, $inspector, $run) use ($monolog, $error_handler) { } elseif ($result instanceof ResponseInterface) { $result->sendContent(); } elseif ( - is_null($result) - || $result instanceof Model || $result instanceof Collection - || is_string($result) + $result instanceof Model || $result instanceof Collection || is_array($result) || is_object($result) - || $result instanceof \Iterable + || $result instanceof Iterator ) { echo json_encode($result); + } elseif (is_string($result)) { + echo $result; } return Handler::QUIT; diff --git a/src/Container/Action.php b/src/Container/Action.php index 496ac82c..f2922ae8 100644 --- a/src/Container/Action.php +++ b/src/Container/Action.php @@ -434,11 +434,11 @@ public function injector(string $classname, ?string $method = null): array /** * Injection for closure * - * @param callable $closure + * @param Closure|callable $closure * @return array * @throws */ - public function injectorForClosure(callable $closure): array + public function injectorForClosure(Closure|callable $closure): array { $reflection = new ReflectionFunction($closure); diff --git a/src/Support/Arraydotify.php b/src/Support/Arraydotify.php index 71418415..bdc32625 100644 --- a/src/Support/Arraydotify.php +++ b/src/Support/Arraydotify.php @@ -182,6 +182,8 @@ public function offsetSet($offset, $value): void { $this->items[$offset] = $value; + $this->items = $this->dotify($this->items); + $this->updateOrigin(); } @@ -192,6 +194,8 @@ public function offsetUnset($offset): void { unset($this->items[$offset]); + $this->items = $this->dotify($this->items); + $this->updateOrigin(); } } From c6516b8fbd533770d6f69f4e0c443ad309e917bd Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 17 Dec 2024 00:59:24 +0000 Subject: [PATCH 184/199] fix: arraydotify return value --- src/Support/Arraydotify.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Support/Arraydotify.php b/src/Support/Arraydotify.php index bdc32625..3bc1522c 100644 --- a/src/Support/Arraydotify.php +++ b/src/Support/Arraydotify.php @@ -126,6 +126,14 @@ private function find(array $origin, string $segment): ?array foreach ($parts as $key => $part) { if ($key != 0) { + if (is_array($array) && is_null($array[$part] ?? null)) { + return null; + } + + if (is_array($array) && isset($array[$part]) && is_null($array[$part])) { + return null; + } + if (isset($array[$part]) && is_array($array[$part])) { $array = &$array[$part]; } @@ -133,7 +141,7 @@ private function find(array $origin, string $segment): ?array continue; } - if (!isset($origin[$part])) { + if (!isset($origin[$part]) || is_null($origin[$part])) { return null; } From b4fd873131dd18ca067dc3340fe9f96ae152b61b Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 17 Dec 2024 01:36:27 +0000 Subject: [PATCH 185/199] fix: set the cookie configuration --- src/Session/Cookie.php | 19 ++++++------------- src/Support/helpers.php | 8 ++------ 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/src/Session/Cookie.php b/src/Session/Cookie.php index 3b183957..723c3363 100644 --- a/src/Session/Cookie.php +++ b/src/Session/Cookie.php @@ -87,10 +87,6 @@ public static function all(): array * @param string|int $key * @param mixed $data * @param int $expirate - * @param string $path - * @param string $domain - * @param bool $secure - * @param bool $http * * @return bool */ @@ -98,10 +94,6 @@ public static function set( $key, $data, $expirate = 3600, - $path = null, - $domain = null, - $secure = false, - $http = true ) { $data = Crypto::encrypt($data); @@ -109,10 +101,10 @@ public static function set( $key, $data, time() + $expirate, - $path, - $domain, - $secure, - $http + config('session.path'), + config('session.domain'), + config('session.secure'), + config('session.httponly') ); } @@ -135,7 +127,8 @@ public static function remove(string $key): mixed unset(static::$is_decrypt[$key]); } - static::set($key, null, -1000); + static::set($key, '', -1000); + unset($_COOKIE[$key]); return $old; diff --git a/src/Support/helpers.php b/src/Support/helpers.php index 2fb6573a..9b1cba77 100644 --- a/src/Support/helpers.php +++ b/src/Support/helpers.php @@ -832,11 +832,7 @@ function session(string $value = null, mixed $default = null): mixed function cookie( string $key = null, mixed $data = null, - int $expirate = 3600, - string $path = null, - string $domain = null, - bool $secure = false, - bool $http = true + int $expirate = 3600 ) { if ($key === null) { return Cookie::all(); @@ -847,7 +843,7 @@ function cookie( } if ($key !== null && $data !== null) { - return Cookie::set($key, $data, $expirate, $path, $domain, $secure, $http); + return Cookie::set($key, $data, $expirate); } return null; From 09f577bc0fc13a0c74590af3384bf9c104c2c511 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 17 Dec 2024 03:17:38 +0000 Subject: [PATCH 186/199] fix(model): apply many bugs --- src/Database/Barry/Model.php | 7 ++++--- src/Database/Barry/Relation.php | 2 +- src/Database/Pagination.php | 6 +++--- src/Database/QueryBuilder.php | 8 ++++---- src/Session/Cookie.php | 4 ++-- src/Support/Collection.php | 21 ++++++++++++++++----- 6 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/Database/Barry/Model.php b/src/Database/Barry/Model.php index 97afac72..7d9ee3ff 100644 --- a/src/Database/Barry/Model.php +++ b/src/Database/Barry/Model.php @@ -13,6 +13,7 @@ use Bow\Database\Exception\NotFoundException; use Bow\Database\Barry\Traits\ArrayAccessTrait; use Bow\Database\Barry\Traits\SerializableTrait; +use Bow\Database\Pagination; abstract class Model implements \ArrayAccess, \JsonSerializable { @@ -342,9 +343,9 @@ public static function create(array $data): Model * @param int $page_number * @param int $current * @param int $chunk - * @return array + * @return Pagination */ - public static function paginate(int $page_number, int $current = 0, ?int $chunk = null): array + public static function paginate(int $page_number, int $current = 0, ?int $chunk = null): Pagination { return static::query()->paginate($page_number, $current, $chunk); } @@ -705,7 +706,7 @@ public static function deleteBy($column, $value): int */ public function getKeyValue(): mixed { - return $this->original[$this->primary_key] ?? null; + return $this->original[$this->primary_key] ?? $this->attributes[$this->primary_key] ?? null; } /** diff --git a/src/Database/Barry/Relation.php b/src/Database/Barry/Relation.php index 6caf3346..cedd62cb 100644 --- a/src/Database/Barry/Relation.php +++ b/src/Database/Barry/Relation.php @@ -108,7 +108,7 @@ public function __call(string $method, array $args) $result = call_user_func_array([$this->query, $method], (array) $args); if ($result === $this->query) { - return $this->query; + return $this; } return $result; diff --git a/src/Database/Pagination.php b/src/Database/Pagination.php index a3898388..2f92deac 100644 --- a/src/Database/Pagination.php +++ b/src/Database/Pagination.php @@ -10,7 +10,7 @@ public function __construct( private int $total, private int $perPage, private int $current, - private array $data + private Collection $data ) { } @@ -44,9 +44,9 @@ public function current(): int return $this->current; } - public function items(): array + public function items(): Collection { - return (array) $this->data; + return $this->data; } public function total(): int diff --git a/src/Database/QueryBuilder.php b/src/Database/QueryBuilder.php index 64adc7c4..6dcb03f3 100644 --- a/src/Database/QueryBuilder.php +++ b/src/Database/QueryBuilder.php @@ -87,9 +87,9 @@ class QueryBuilder implements \JsonSerializable /** * The PDO instance * - * @var \PDO + * @var PDO */ - protected ?\PDO $connection = null; + protected ?PDO $connection = null; /** * Define whether to retrieve information from the list @@ -1208,7 +1208,7 @@ public function paginate(int $number_of_page, int $current = 0, ?int $chunk = nu $join = $this->join; $data_bind = $this->where_data_binding; - $data = (array) $this->jump($jump)->take($number_of_page)->get(); + $data = $this->jump($jump)->take($number_of_page)->get(); // Reinitialisation of current query $this->where = $where; @@ -1220,7 +1220,7 @@ public function paginate(int $number_of_page, int $current = 0, ?int $chunk = nu // Grouped data if (is_int($chunk)) { - $data = array_chunk($data, $chunk); + $data = $data->chunk($chunk); } // Enables automatic paging. diff --git a/src/Session/Cookie.php b/src/Session/Cookie.php index 723c3363..deefd75d 100644 --- a/src/Session/Cookie.php +++ b/src/Session/Cookie.php @@ -75,7 +75,7 @@ public static function get(string $key, mixed $default = null): mixed public static function all(): array { foreach ($_COOKIE as $key => $value) { - $_COOKIE[$key] = Crypto::decrypt($value); + $_COOKIE[$key] = json_decode(Crypto::decrypt($value)); } return $_COOKIE; @@ -95,7 +95,7 @@ public static function set( $data, $expirate = 3600, ) { - $data = Crypto::encrypt($data); + $data = Crypto::encrypt(json_encode($data)); return setcookie( $key, diff --git a/src/Support/Collection.php b/src/Support/Collection.php index 3f664c54..b0c20135 100644 --- a/src/Support/Collection.php +++ b/src/Support/Collection.php @@ -52,11 +52,11 @@ public function last(): mixed /** * Check existence of a key in the session collection * - * @param string $key + * @param int|string $key * @param bool $strict * @return bool */ - public function has(string $key, bool $strict = false): bool + public function has(int|string $key, bool $strict = false): bool { // When $strict is true, he check $key not how a key but a value. $isset = isset($this->storage[$key]); @@ -93,11 +93,11 @@ public function isEmpty(): bool /** * Allows to recover a value or value collection. * - * @param string $key + * @param int|string $key * @param mixed $default * @return mixed */ - public function get(string $key = null, mixed $default = null) + public function get(int|string $key = null, mixed $default = null) { if (is_null($key)) { return $this->storage; @@ -162,13 +162,24 @@ public function count(): int return count($this->storage); } + /** + * Chunk the storage content + * + * @param int $count + * @return int + */ + public function chunk(int $chunk): Collection + { + return new Collection(array_chunk($this->storage, $chunk)); + } + /** * To retrieve a value or value collection form d'instance de collection. * * @param string $key * @return Collection */ - public function collectionify(string $key): Collection + public function collectify(string $key): Collection { $data = []; From 3e7d3b7b30c88457dbb35838a409fe0fe0539532 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 17 Dec 2024 13:22:41 +0000 Subject: [PATCH 187/199] fix: resolve url by route name --- src/Database/Collection.php | 3 ++- src/Database/Pagination.php | 7 +++++-- src/Database/QueryBuilder.php | 4 ++++ src/Queue/Adapters/BeanstalkdAdapter.php | 2 +- src/Queue/Adapters/QueueAdapter.php | 2 +- src/Session/Cookie.php | 1 + src/Support/helpers.php | 12 ++++-------- tests/Database/PaginationTest.php | 4 ++-- 8 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/Database/Collection.php b/src/Database/Collection.php index 01770f77..0b2c6be4 100644 --- a/src/Database/Collection.php +++ b/src/Database/Collection.php @@ -4,9 +4,10 @@ namespace Bow\Database; +use Bow\Support\Collection as SupportCollection; use Bow\Database\Barry\Model; -class Collection extends \Bow\Support\Collection +class Collection extends SupportCollection { /** * @inheritdoc diff --git a/src/Database/Pagination.php b/src/Database/Pagination.php index 2f92deac..2f271e43 100644 --- a/src/Database/Pagination.php +++ b/src/Database/Pagination.php @@ -2,6 +2,9 @@ namespace Bow\Database; +use Bow\Support\Collection as SupportCollection; +use Bow\Database\Collection as DatabaseCollection; + class Pagination { public function __construct( @@ -10,7 +13,7 @@ public function __construct( private int $total, private int $perPage, private int $current, - private Collection $data + private SupportCollection|DatabaseCollection $data ) { } @@ -44,7 +47,7 @@ public function current(): int return $this->current; } - public function items(): Collection + public function items(): SupportCollection|DatabaseCollection { return $this->data; } diff --git a/src/Database/QueryBuilder.php b/src/Database/QueryBuilder.php index 6dcb03f3..1dee2983 100644 --- a/src/Database/QueryBuilder.php +++ b/src/Database/QueryBuilder.php @@ -1210,6 +1210,10 @@ public function paginate(int $number_of_page, int $current = 0, ?int $chunk = nu $data = $this->jump($jump)->take($number_of_page)->get(); + if (is_array($data)) { + $data = collect($data); + } + // Reinitialisation of current query $this->where = $where; $this->join = $join; diff --git a/src/Queue/Adapters/BeanstalkdAdapter.php b/src/Queue/Adapters/BeanstalkdAdapter.php index dfb16710..d11748ae 100644 --- a/src/Queue/Adapters/BeanstalkdAdapter.php +++ b/src/Queue/Adapters/BeanstalkdAdapter.php @@ -4,11 +4,11 @@ namespace Bow\Queue\Adapters; +use RuntimeException; use Pheanstalk\Pheanstalk; use Bow\Queue\ProducerService; use Bow\Queue\Adapters\QueueAdapter; use Pheanstalk\Contract\PheanstalkInterface; -use RuntimeException; class BeanstalkdAdapter extends QueueAdapter { diff --git a/src/Queue/Adapters/QueueAdapter.php b/src/Queue/Adapters/QueueAdapter.php index 513bdba1..714b19d1 100644 --- a/src/Queue/Adapters/QueueAdapter.php +++ b/src/Queue/Adapters/QueueAdapter.php @@ -17,7 +17,7 @@ abstract class QueueAdapter * * @var int */ - protected flaot $start_time; + protected float $start_time; /** * Determine the default watch name diff --git a/src/Session/Cookie.php b/src/Session/Cookie.php index deefd75d..ece2490c 100644 --- a/src/Session/Cookie.php +++ b/src/Session/Cookie.php @@ -124,6 +124,7 @@ public static function remove(string $key): mixed if (!static::$is_decrypt[$key]) { $old = Crypto::decrypt($_COOKIE[$key]); + unset(static::$is_decrypt[$key]); } diff --git a/src/Support/helpers.php b/src/Support/helpers.php index 9b1cba77..802b7556 100644 --- a/src/Support/helpers.php +++ b/src/Support/helpers.php @@ -876,22 +876,20 @@ function validator(array $inputs, array $rules, array $messages = []): Validate */ function route(string $name, bool|array $data = [], bool $absolute = false) { - $routes = config('app.routes'); - if (is_bool($data)) { $absolute = $data; $data = []; } - if (!isset($routes[$name])) { + $url = config('app.routes.' . $name); + + if (is_null($url)) { throw new \InvalidArgumentException( 'The route named ' . $name . ' does not define.', E_USER_ERROR ); } - $url = $routes[$name]; - if (preg_match_all('/(?::([a-zA-Z0-9_]+\??))/', $url, $matches)) { $keys = end($matches); foreach ($keys as $key) { @@ -901,9 +899,7 @@ function route(string $name, bool|array $data = [], bool $absolute = false) unset($data[$valide_key]); } else { if (!isset($data[$key])) { - throw new InvalidArgumentException( - "The $key key is not provide" - ); + throw new InvalidArgumentException("Route: The $key key is not provide"); } $value = $data[$key]; unset($data[$key]); diff --git a/tests/Database/PaginationTest.php b/tests/Database/PaginationTest.php index 47173628..2b90cda2 100644 --- a/tests/Database/PaginationTest.php +++ b/tests/Database/PaginationTest.php @@ -19,7 +19,7 @@ protected function setUp(): void total: 3, perPage: 10, current: 1, - data: ['item1', 'item2', 'item3'] + data: collect(['item1', 'item2', 'item3']) ); } @@ -40,7 +40,7 @@ public function test_current(): void public function test_items(): void { - $this->assertSame(['item1', 'item2', 'item3'], $this->pagination->items()); + $this->assertSame(['item1', 'item2', 'item3'], $this->pagination->items()->toArray()); } public function test_total(): void From 10786b9f240dfcb72cbafd299406e7683321b227 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 17 Dec 2024 17:42:00 +0000 Subject: [PATCH 188/199] fix(model): fix save method when attribute is already set --- src/Database/Barry/Model.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Database/Barry/Model.php b/src/Database/Barry/Model.php index 7d9ee3ff..b23ca3f9 100644 --- a/src/Database/Barry/Model.php +++ b/src/Database/Barry/Model.php @@ -532,7 +532,7 @@ public function save() { $builder = static::query(); - // Get the current primary key value + // Get the current primary key value $primary_key_value = $this->getKeyValue(); // If primary key value is null, we are going to start the creation of new row @@ -544,7 +544,7 @@ public function save() // Check the existent in database if (!$builder->exists($this->primary_key, $primary_key_value)) { - return 0; + return $this->writeRows($builder); } // We set the primary key value From 9ed412018680684112f1f649cec4d4a17fded5d1 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 17 Dec 2024 20:24:03 +0000 Subject: [PATCH 189/199] fix: data secure --- src/Database/QueryBuilder.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Database/QueryBuilder.php b/src/Database/QueryBuilder.php index 1dee2983..7d16ee86 100644 --- a/src/Database/QueryBuilder.php +++ b/src/Database/QueryBuilder.php @@ -878,9 +878,10 @@ public function get(array $columns = []): array|object|null $this->bind($statement, $this->where_data_binding); $this->where_data_binding = []; + $statement->execute(); - $data = Sanitize::make($statement->fetchAll()); + $data = $statement->fetchAll(); $statement->closeCursor(); From f6b07e708de114e24c53dec0a21f302656b6c3f9 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Wed, 18 Dec 2024 04:48:19 +0000 Subject: [PATCH 190/199] fix: change session prototype --- src/Support/helpers.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Support/helpers.php b/src/Support/helpers.php index 802b7556..c2887140 100644 --- a/src/Support/helpers.php +++ b/src/Support/helpers.php @@ -794,18 +794,19 @@ function raw_email(string $to, string $subject, string $message, array $headers /** * Session help * - * @param string $value - * @param mixed $default + * @param array|string $value + * @param mixed $default * @return mixed */ - function session(string $value = null, mixed $default = null): mixed + function session(array|string $value = null, mixed $default = null): mixed { if ($value == null) { return Session::getInstance(); } if (!is_array($value)) { - return Session::getInstance()->get($value, $default); + $key = $value; + return Session::getInstance()->get($key, $default); } foreach ($value as $key => $item) { From 9c39adc26eb250cac5d69bc8898865446e840c64 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Wed, 18 Dec 2024 05:17:39 +0000 Subject: [PATCH 191/199] fix: update e helper --- src/Support/helpers.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Support/helpers.php b/src/Support/helpers.php index c2887140..4b3a1921 100644 --- a/src/Support/helpers.php +++ b/src/Support/helpers.php @@ -928,12 +928,12 @@ function route(string $name, bool|array $data = [], bool $absolute = false) /** * Escape the HTML tags in the chain. * - * @param string $value + * @param ?string $value * @return string */ - function e(string $value): string + function e(?string $value = null): string { - return htmlentities($value, ENT_QUOTES, 'UTF-8', false); + return htmlspecialchars($value ?? '', ENT_QUOTES, 'UTF-8'); } } From e1abfc110b00684321f93e3c4d329600319206ba Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Wed, 18 Dec 2024 05:45:39 +0000 Subject: [PATCH 192/199] fix: stop execute of words --- src/Support/Str.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Support/Str.php b/src/Support/Str.php index 9000a12b..a96d4a5e 100644 --- a/src/Support/Str.php +++ b/src/Support/Str.php @@ -450,6 +450,10 @@ public static function words(string $words, int $len): string $sentence = ''; for ($i = 0; $i < $len; $i++) { + if (!isset($wordParts[$i])) { + break; + } + $sentence .= ' ' . $wordParts[$i]; } From c8a723506f6da45da8a6e9d0ca0df6fb56a8c433 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 20 Dec 2024 15:09:09 +0000 Subject: [PATCH 193/199] feat(http): add http status class --- src/Http/HttpStatus.php | 149 ++++++++++++++++++++++++++++++++++++++++ src/Http/Response.php | 68 +----------------- 2 files changed, 151 insertions(+), 66 deletions(-) create mode 100644 src/Http/HttpStatus.php diff --git a/src/Http/HttpStatus.php b/src/Http/HttpStatus.php new file mode 100644 index 00000000..eb58e6ef --- /dev/null +++ b/src/Http/HttpStatus.php @@ -0,0 +1,149 @@ + 'Continue', + 101 => 'Switching Protocols', + 102 => 'Processing', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 207 => 'Multi-Status', + 208 => 'Already Reported', + 226 => 'IM Used', + 300 => 'Multipe Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + 308 => 'Permanent Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication', + 408 => 'Request Time Out', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Payload Too Large', + 414 => 'URI Too Long', + 415 => 'Unsupport Media', + 416 => 'Range Not Statisfiable', + 417 => 'Expectation Failed', + 418 => 'I\'m a teapot', + 421 => 'Misdirected Request', + 422 => 'Unprocessable Entity', + 423 => 'Locked', + 424 => 'Failed Dependency', + 426 => 'Upgrade Required', + 428 => 'Precondition Required', + 429 => 'Too Many Requests', + 431 => 'Request Header Fields Too Large', + 444 => 'Connection Closed Without Response', + 451 => 'Unavailable For Legal Reasons', + 499 => 'Client Closed Request', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported', + ]; + + public const CONTINUE = 100; + public const SWITCHING_PROTOCOLS = 101; + public const PROCESSING = 102; + public const OK = 200; + public const CREATED = 201; + public const ACCEPTED = 202; + public const NO_CONTENT = 204; + public const RESET_CONTENT = 205; + public const PARTIAL_CONTENT = 206; + public const MULTI_STATUS = 207; + public const ALREADY_REPORTED = 208; + public const IM_USED = 226; + public const MULTIPE_CHOICES = 300; + public const MOVED_PERMANENTLY = 301; + public const FOUND_ = 302; + public const SEE_OTHER = 303; + public const NOT_MODIFIED = 304; + public const USE_PROXY = 305; + public const TEMPORARY_REDIRECT = 307; + public const PERMANENT_REDIRECT = 308; + public const BAD_REQUEST = 400; + public const UNAUTHORIZED_ = 401; + public const PAYMENT_REQUIRED = 402; + public const FORBIDDEN_ = 403; + public const NOT_FOUND = 404; + public const METHOD_NOT_ALLOWED = 405; + public const NOT_ACCEPTABLE = 406; + public const PROXY_AUTHENTICATION = 407; + public const REQUEST_TIME_OUT = 408; + public const CONFLICT_ = 409; + public const GONE_ = 410; + public const LENGTH_REQUIRED = 411; + public const PRECONDITION_FAILED = 412; + public const PAYLOAD_TOO_LARGE = 413; + public const URI_TOO_LONG = 414; + public const UNSUPPORT_MEDIA = 415; + public const RANGE_NOT_STATISFIABLE = 416; + public const EXPECTATION_FAILED = 417; + public const I_M_A_TEAPOT = 418; + public const MISDIRECTED_REQUEST = 421; + public const UNPROCESSABLE_ENTITY = 422; + public const LOCKED_ = 423; + public const FAILED_DEPENDENCY = 424; + public const UPGRADE_REQUIRED = 426; + public const PRECONDITION_REQUIRED = 428; + public const TOO_MANY_REQUESTS = 429; + public const REQUEST_HEADER_FIELDS_TOO_LARGE = 431; + public const CONNECTION_CLOSED_WITHOUT_RESPONSE = 444; + public const UNAVAILABLE_FOR_LEGAL_REASONS = 451; + public const CLIENT_CLOSED_REQUEST = 499; + public const INTERNAL_SERVER_ERROR = 500; + public const NOT_IMPLEMENTED = 501; + public const BAD_GATEWAY = 502; + public const SERVICE_UNAVAILABLE = 503; + public const GATEWAY_TIMEOUT = 504; + public const HTTP_VERSION_NOT_SUPPORTED = 505; + + /** + * Get the message + * + * @param int $code + * @return string + */ + public static function getMessage(int $code): string + { + if (!isset(static::STATUS[$code])) { + throw new InvalidArgumentException("The code {$code} is not exists"); + } + + return static::STATUS[$code]; + } + + /** + * Get the codes + * + * @return array + */ + public static function getCodes(): array + { + return array_keys(static::STATUS); + } +} diff --git a/src/Http/Response.php b/src/Http/Response.php index d942a6de..6cadc32e 100644 --- a/src/Http/Response.php +++ b/src/Http/Response.php @@ -9,70 +9,6 @@ class Response implements ResponseInterface { - /** - * Valid http code list for the app Except that - * the user can himself redefine these codes - * if it uses the `header` function of php - */ - private static array $status_codes = [ - 100 => 'Continue', - 101 => 'Switching Protocols', - 102 => 'Processing', - 200 => 'OK', - 201 => 'Created', - 202 => 'Accepted', - 204 => 'No Content', - 205 => 'Reset Content', - 206 => 'Partial Content', - 207 => 'Multi-Status', - 208 => 'Already Reported', - 226 => 'IM Used', - 300 => 'Multipe Choices', - 301 => 'Moved Permanently', - 302 => 'Found', - 303 => 'See Other', - 304 => 'Not Modified', - 305 => 'Use Proxy', - 307 => 'Temporary Redirect', - 308 => 'Permanent Redirect', - 400 => 'Bad Request', - 401 => 'Unauthorized', - 402 => 'Payment Required', - 403 => 'Forbidden', - 404 => 'Not Found', - 405 => 'Method Not Allowed', - 406 => 'Not Acceptable', - 407 => 'Proxy Authentication', - 408 => 'Request Time Out', - 409 => 'Conflict', - 410 => 'Gone', - 411 => 'Length Required', - 412 => 'Precondition Failed', - 413 => 'Payload Too Large', - 414 => 'URI Too Long', - 415 => 'Unsupport Media', - 416 => 'Range Not Statisfiable', - 417 => 'Expectation Failed', - 418 => 'I\'m a teapot', - 421 => 'Misdirected Request', - 422 => 'Unprocessable Entity', - 423 => 'Locked', - 424 => 'Failed Dependency', - 426 => 'Upgrade Required', - 428 => 'Precondition Required', - 429 => 'Too Many Requests', - 431 => 'Request Header Fields Too Large', - 444 => 'Connection Closed Without Response', - 451 => 'Unavailable For Legal Reasons', - 499 => 'Client Closed Request', - 500 => 'Internal Server Error', - 501 => 'Not Implemented', - 502 => 'Bad Gateway', - 503 => 'Service Unavailable', - 504 => 'Gateway Timeout', - 505 => 'HTTP Version Not Supported', - ]; - /** * The Response instamce * @@ -283,8 +219,8 @@ public function status(int $code): Response { $this->code = $code; - if (in_array($code, array_keys(static::$status_codes), true)) { - @header('HTTP/1.1 ' . $code . ' ' . static::$status_codes[$code], $this->override, $code); + if (in_array($code, HttpStatus::getCodes(), true)) { + @header('HTTP/1.1 ' . $code . ' ' . HttpStatus::getMessage($code), $this->override, $code); } return $this; From 728d1e0693486011749e19511e1092e9fb48eca5 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 20 Dec 2024 15:09:35 +0000 Subject: [PATCH 194/199] refactor: refactor the app_abort class --- src/Support/helpers.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Support/helpers.php b/src/Support/helpers.php index 4b3a1921..496de7bf 100644 --- a/src/Support/helpers.php +++ b/src/Support/helpers.php @@ -14,6 +14,7 @@ use Bow\Http\Response; use Bow\Security\Hash; use Bow\Session\Cookie; +use Bow\Http\HttpStatus; use Bow\Security\Crypto; use Bow\Session\Session; use Bow\Storage\Storage; @@ -1149,6 +1150,10 @@ function app_assets(string $filename): string */ function app_abort(int $code = 500, string $message = '') { + if (strlen($message) == 0) { + $message = HttpStatus::getMessage($code); + } + throw new HttpException($message, $code); } } From 5673a7f37356f7de426e16c824d1dbea14903834 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Fri, 20 Dec 2024 15:11:53 +0000 Subject: [PATCH 195/199] fix(str): refactoring str classes --- src/Support/Str.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Support/Str.php b/src/Support/Str.php index a96d4a5e..ab6c7c50 100644 --- a/src/Support/Str.php +++ b/src/Support/Str.php @@ -453,7 +453,7 @@ public static function words(string $words, int $len): string if (!isset($wordParts[$i])) { break; } - + $sentence .= ' ' . $wordParts[$i]; } From ee3b948363f7e4b1c62f4495ca4ef5a2aafcc60b Mon Sep 17 00:00:00 2001 From: papac Date: Fri, 20 Dec 2024 15:19:03 +0000 Subject: [PATCH 196/199] Update CHANGELOG --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea0c24c5..ffe36a6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 5.1.6 - 2024-12-20 + +### What's Changed + +* Implement feature for improve http and str classes by @papac in https://github.com/bowphp/framework/pull/304 + +**Full Changelog**: https://github.com/bowphp/framework/compare/5.1.5...5.1.6 + ## 5.1.2 - 2023-09-17 Fix `db_seed` helper @@ -24,6 +32,7 @@ This method aims to execute an SQL transaction around a passed arrow function. Database::transaction(fn() => $user->update(['name' => ''])); + ``` Ref: #255 From 39036395ca0be61d9489e41cbc0db9f56f9602e3 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sat, 21 Dec 2024 15:13:06 +0000 Subject: [PATCH 197/199] feat(barry): add relative create method for barry model --- composer.json | 2 +- src/Database/Barry/Relation.php | 56 ++++++++++++++----- src/Database/Barry/Relations/BelongsTo.php | 15 +---- .../Barry/Relations/BelongsToMany.php | 25 +++------ src/Database/Barry/Relations/HasMany.php | 19 +------ src/Database/Barry/Relations/HasOne.php | 23 ++------ src/Database/Barry/Traits/CanSerialized.php | 11 +--- 7 files changed, 59 insertions(+), 92 deletions(-) diff --git a/composer.json b/composer.json index ed6a6f1e..a64fd6ab 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "ramsey/uuid": "^4.7" }, "require-dev": { - "pda/pheanstalk": "^4.0", + "pda/pheanstalk": "^4.0.5", "phpunit/phpunit": "^9", "monolog/monolog": "^1.22", "twig/twig": "^3", diff --git a/src/Database/Barry/Relation.php b/src/Database/Barry/Relation.php index cedd62cb..2a11d475 100644 --- a/src/Database/Barry/Relation.php +++ b/src/Database/Barry/Relation.php @@ -9,6 +9,20 @@ abstract class Relation { + /** + * The foreign key of the parent model. + * + * @var string + */ + protected string $foreign_key; + + /** + * The associated key on the parent model. + * + * @var string + */ + protected string $local_key; + /** * The parent model instance * @@ -54,6 +68,7 @@ public function __construct(Model $related, Model $parent) { $this->parent = $parent; $this->related = $related; + $this->query = $this->related::query(); // Build the constraint effect @@ -62,20 +77,6 @@ public function __construct(Model $related, Model $parent) } } - /** - * Set the base constraints on the relation query. - * - * @return void - */ - abstract public function addConstraints(): void; - - /** - * Get the results of the relationship. - * - * @return mixed - */ - abstract public function getResults(): mixed; - /** * Get the parent model. * @@ -113,4 +114,31 @@ public function __call(string $method, array $args) return $result; } + + /** + * Create a new row of the related + * + * @param array $attributes + * @return Model + */ + public function create(array $attributes): Model + { + $attributes[$this->foreign_key] = $this->parent->getKeyValue(); + + return $this->related->create($attributes); + } + + /** + * Get the results of the relationship. + * + * @return mixed + */ + abstract public function getResults(): mixed; + + /** + * Set the base constraints on the relation query. + * + * @return void + */ + abstract public function addConstraints(): void; } diff --git a/src/Database/Barry/Relations/BelongsTo.php b/src/Database/Barry/Relations/BelongsTo.php index f48b71fe..f31a483e 100644 --- a/src/Database/Barry/Relations/BelongsTo.php +++ b/src/Database/Barry/Relations/BelongsTo.php @@ -10,20 +10,6 @@ class BelongsTo extends Relation { - /** - * The foreign key of the parent model. - * - * @var string - */ - protected $foreign_key; - - /** - * The associated key on the parent model. - * - * @var string - */ - protected $local_key; - /** * Create a new belongs to relationship instance. * @@ -52,6 +38,7 @@ public function __construct( public function getResults(): ?Model { $key = $this->query->getTable() . ":belongsto:" . $this->related->getTable() . ":" . $this->foreign_key; + $cache = Cache::store('file')->get($key); if (!is_null($cache)) { diff --git a/src/Database/Barry/Relations/BelongsToMany.php b/src/Database/Barry/Relations/BelongsToMany.php index 62406b8f..d123f41f 100644 --- a/src/Database/Barry/Relations/BelongsToMany.php +++ b/src/Database/Barry/Relations/BelongsToMany.php @@ -10,20 +10,6 @@ class BelongsToMany extends Relation { - /** - * The foreign key of the parent model. - * - * @var string - */ - protected $foreign_key; - - /** - * The associated key on the parent model. - * - * @var string - */ - protected $local_key; - /** * Create a new belongs to relationship instance. * @@ -47,7 +33,6 @@ public function __construct(Model $related, Model $parent, string $foreign_key, */ public function getResults(): Collection { - // TODO: Cache the result return $this->query->get(); } @@ -58,8 +43,14 @@ public function getResults(): Collection */ public function addConstraints(): void { - if (static::$has_constraints) { - // Todo + if (!static::$has_constraints) { + return; } + + // For belongs to relationships, which are essentially the inverse of has one + // or has many relationships, we need to actually query on the primary key + // of the related models matching on the foreign key that's on a parent. + $foreign_key_value = $this->parent->getAttribute($this->foreign_key); + $this->query->where($this->local_key, '=', $foreign_key_value); } } diff --git a/src/Database/Barry/Relations/HasMany.php b/src/Database/Barry/Relations/HasMany.php index 6cd9d6ec..eb38420d 100644 --- a/src/Database/Barry/Relations/HasMany.php +++ b/src/Database/Barry/Relations/HasMany.php @@ -10,20 +10,6 @@ class HasMany extends Relation { - /** - * The foreign key of the parent model. - * - * @var string - */ - protected string $foreign_key; - - /** - * The associated key on the parent model. - * - * @var string - */ - protected string $local_key; - /** * Create a new belongs to relationship instance. * @@ -50,7 +36,6 @@ public function __construct(Model $related, Model $parent, string $foreign_key, */ public function getResults(): Collection { - // TODO: Cache the result return $this->query->get(); } @@ -61,8 +46,6 @@ public function getResults(): Collection */ public function addConstraints(): void { - if (static::$has_constraints) { - // Todo - } + // } } diff --git a/src/Database/Barry/Relations/HasOne.php b/src/Database/Barry/Relations/HasOne.php index 1b254719..880a784c 100644 --- a/src/Database/Barry/Relations/HasOne.php +++ b/src/Database/Barry/Relations/HasOne.php @@ -10,20 +10,6 @@ class HasOne extends Relation { - /** - * The foreign key of the parent model. - * - * @var string - */ - protected string $foreign_key; - - /** - * The associated key on the parent model. - * - * @var string - */ - protected string $local_key; - /** * Create a new belongs to relationship instance. * @@ -38,8 +24,6 @@ public function __construct(Model $related, Model $parent, string $foreign_key, $this->local_key = $local_key; $this->foreign_key = $foreign_key; - - $this->query = $this->query->where($this->foreign_key, $this->parent->$local_key); } /** @@ -50,6 +34,7 @@ public function __construct(Model $related, Model $parent, string $foreign_key, public function getResults(): ?Model { $key = $this->query->getTable() . ":hasone:" . $this->related->getTable() . ":" . $this->foreign_key; + $cache = Cache::store('file')->get($key); if (!is_null($cache)) { @@ -74,8 +59,10 @@ public function getResults(): ?Model */ public function addConstraints(): void { - if (static::$has_constraints) { - // Todo + if (!static::$has_constraints) { + return; } + + $this->query = $this->query->where($this->foreign_key, $this->parent->getAttribute($this->local_key)); } } diff --git a/src/Database/Barry/Traits/CanSerialized.php b/src/Database/Barry/Traits/CanSerialized.php index 0734398e..49895f09 100644 --- a/src/Database/Barry/Traits/CanSerialized.php +++ b/src/Database/Barry/Traits/CanSerialized.php @@ -13,7 +13,7 @@ trait CanSerialized * * @return string */ - public function __sleep() + public function __sleep(): array { if ($this instanceof Model) { return ['attributes' => $this->attributes]; @@ -21,13 +21,4 @@ public function __sleep() return ['attributes' => $this->toArray()]; } - - /** - * __wakeup - * - * @return string - */ - public function __wakeup() - { - } } From 2bf903c9654febd51e4029ff9edcf7df8739bca2 Mon Sep 17 00:00:00 2001 From: papac Date: Wed, 25 Dec 2024 12:35:02 +0000 Subject: [PATCH 198/199] Update CHANGELOG --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ffe36a6a..743cac52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 5.1.7 - 2024-12-21 + +### What's Changed + +* Update CHANGELOG by @papac in https://github.com/bowphp/framework/pull/305 +* feat(barry): add relative create method for barry model by @papac in https://github.com/bowphp/framework/pull/306 + +**Full Changelog**: https://github.com/bowphp/framework/compare/5.1.6...5.1.7 + ## 5.1.6 - 2024-12-20 ### What's Changed @@ -33,6 +42,7 @@ Database::transaction(fn() => $user->update(['name' => ''])); + ``` Ref: #255 From 3b0102a9b32ebe08e5b2f55a5deeb8da71d532fa Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Sat, 11 Jan 2025 14:03:38 +0000 Subject: [PATCH 199/199] Update README.md --- src/Database/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Database/README.md b/src/Database/README.md index a2aa586b..45b39e37 100644 --- a/src/Database/README.md +++ b/src/Database/README.md @@ -3,6 +3,7 @@ Bow Framework's database system is very simple database manager api with support: - MySQL +- PostGreSQL - SQLite Make database connexion is very simple