From a9fee790bbb6d5b20cb7577913e86a1a42c0c5a1 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Tue, 19 Sep 2023 17:50:06 +0000 Subject: [PATCH 01/15] 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 02/15] 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 03/15] 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 04/15] 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 05/15] 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 06/15] 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 07/15] 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 08/15] 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 09/15] 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 10/15] 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 11/15] 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 12/15] 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 13/15] 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 14/15] 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 15/15] 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");