Skip to content

Commit 90f7c5b

Browse files
committed
First commit
0 parents  commit 90f7c5b

12 files changed

+939
-0
lines changed

.gitignore

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/.idea/
2+
/.vs/
3+
/.vscode/
4+
/vendor/
5+
/composer.lock
6+
/.phpunit.result.cache
7+
/nbproject/private/
8+
/*.log

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2023 InitPHP
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

+159
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
# InitPHP Queue
2+
3+
This library offers performance and asynchrony by queuing your jobs to be done later.
4+
5+
```
6+
composer require initphp/queue
7+
```
8+
9+
## Create Job
10+
11+
First, start by creating the business class. You can find a simple example below.
12+
13+
```php
14+
require_once __DIR__ . DIRECTORY_SEPARATOR . 'vendor/autoload.php';
15+
16+
namespace App\Jobs;
17+
18+
use InitPHP\Queue\Job;
19+
20+
class MailJob extends Job
21+
{
22+
protected string $channel = 'mailChannel';
23+
24+
protected string $queue = 'mailQueue';
25+
26+
public function handle(): bool
27+
{
28+
$payload = $this->getPayload();
29+
try {
30+
if (mail($payload['to'], $payload['subject'])) {
31+
return true;
32+
} else {
33+
return false;;
34+
}
35+
} catch (\Throwable $e) {
36+
return false;
37+
}
38+
}
39+
}
40+
```
41+
42+
Use the `push()` method to add jobs to the queue;
43+
44+
```php
45+
require_once __DIR__ . DIRECTORY_SEPARATOR . 'vendor/autoload.php';
46+
$adapter = new \InitPHP\Queue\Adapters\RabbitMQAdapter('127.0.0.1', 5267, 'guest', 'guest');
47+
48+
$job = new App\Jobs\MailJob($adapter);
49+
50+
// Add Queue Job
51+
$job->push([
52+
'to' => '[email protected]',
53+
'subject' => 'Subject Mail',
54+
]);
55+
```
56+
57+
Write your code to handle the jobs in the queue.
58+
59+
`consumer.php`
60+
61+
```php
62+
require_once __DIR__ . DIRECTORY_SEPARATOR . 'vendor/autoload.php';
63+
$adapter = new \InitPHP\Queue\Adapters\RabbitMQAdapter('127.0.0.1', 5267, 'guest', 'guest');
64+
65+
$adapter->handle('mailChannel', 'mailQueue');
66+
67+
$adapter->close();
68+
```
69+
70+
Trigger your consumer code.
71+
72+
```
73+
php consumer.php
74+
```
75+
76+
77+
# Adapters
78+
- [x] [PDO (Database) Adapter](#pdo-adapter)
79+
- [x] [RabbitMQ Adapter](#rabbitmq-adapter)
80+
- [ ] Kafka Adapter
81+
82+
## PDO Adapter
83+
84+
- [x] PDO Extension
85+
86+
To initialize the PDO adapter, you need a PDO object and 2 tables.
87+
88+
```php
89+
$pdo = new PDO('mysql:host=localhost;port=3307;dbname=queue_db', 'root', 'root');
90+
$adapter = new \InitPHP\Queue\Adapters\PDOAdapter($pdo, 'queue');
91+
```
92+
93+
The first of these tables is used for those waiting in line and the other for jobs that have errors. The table name into which the failed jobs fall is obtained by adding "`_failed`" as a suffix to the main table name. Accordingly, create your queue tables using the following SQL.
94+
95+
```sql
96+
CREATE TABLE `queue` (
97+
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
98+
`channel` VARCHAR(255) NOT NULL,
99+
`queue` VARCHAR(255) NOT NULL,
100+
`payload` TEXT NULL DEFAULT NULL,
101+
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
102+
`updated_at` DATETIME NULL DEFAULT NULL,
103+
`status` TINYINT(1) NOT NULL DEFAULT '0',
104+
PRIMARY KEY (`id`)
105+
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
106+
CREATE INDEX `channel_queue` ON `queue` (`channel`, `queue`);
107+
108+
CREATE TABLE `queue_failed` (
109+
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
110+
`queue_id` BIGINT UNSIGNED NOT NULL,
111+
`channel` VARCHAR(255) NOT NULL,
112+
`queue` VARCHAR(255) NOT NULL,
113+
`payload` TEXT NULL DEFAULT NULL,
114+
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
115+
`updated_at` DATETIME NULL DEFAULT NULL,
116+
`status` TINYINT(1) NOT NULL DEFAULT '0',
117+
PRIMARY KEY (`id`)
118+
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
119+
```
120+
121+
## RabbitMQ Adapter
122+
123+
- [x] RabbitMQ Server
124+
- [x] "`php-amqplib/php-amqplib`" Library
125+
126+
```
127+
composer require php-amqplib/php-amqplib
128+
```
129+
130+
```php
131+
$adapter = new \InitPHP\Queue\Adapters\RabbitMQAdapter('127.0.0.1', 5267, 'guest', 'guest');
132+
```
133+
134+
# Getting Involved
135+
136+
> All contributions to this project will be published under the MIT License. By submitting a pull request or filing a bug, issue, or feature request, you are agreeing to comply with this waiver of copyright interest.
137+
138+
There are two primary ways to help:
139+
140+
- Using the issue tracker, and
141+
- Changing the code-base.
142+
143+
## Using the issue tracker
144+
145+
Use the issue tracker to suggest feature requests, report bugs, and ask questions. This is also a great way to connect with the developers of the project as well as others who are interested in this solution.
146+
147+
Use the issue tracker to find ways to contribute. Find a bug or a feature, mention in the issue that you will take on that effort, then follow the Changing the code-base guidance below.
148+
149+
## Changing the code-base
150+
151+
Generally speaking, you should fork this repository, make changes in your own fork, and then submit a pull request. All new code should have associated unit tests that validate implemented features and the presence or lack of defects. Additionally, the code should follow any stylistic and architectural guidelines prescribed by the project. In the absence of such guidelines, mimic the styles and patterns in the existing code-base.
152+
153+
# Credits
154+
155+
- [Muhammet ŞAFAK](https://www.muhammetsafak.com.tr) <<[email protected]>>
156+
157+
# License
158+
159+
Copyright &copy; 2022 [MIT License](./LICENSE)

composer.json

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "initphp/queue",
3+
"description": "InitPHP Queue Library",
4+
"type": "library",
5+
"license": "MIT",
6+
"autoload": {
7+
"psr-4": {
8+
"InitPHP\\Queue\\": "src/"
9+
}
10+
},
11+
"authors": [
12+
{
13+
"name": "Muhammet ŞAFAK",
14+
"email": "[email protected]",
15+
"role": "Developer",
16+
"homepage": "https://www.muhammetsafak.com.tr"
17+
}
18+
],
19+
"minimum-stability": "stable",
20+
"require": {
21+
"php": ">=7.4",
22+
"ext-json": "*"
23+
}
24+
}

src/Adapters/PDOAdapter.php

+156
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
<?php
2+
/**
3+
* InitPHP Queue
4+
*
5+
* This file is part of InitPHP Queue.
6+
*
7+
* @author Muhammet ŞAFAK <[email protected]>
8+
* @copyright Copyright © 2023 Muhammet ŞAFAK
9+
* @license ./LICENSE MIT
10+
* @version 1.0
11+
* @link https://www.muhammetsafak.com.tr
12+
*/
13+
14+
namespace InitPHP\Queue\Adapters;
15+
16+
use PDO;
17+
use Throwable;
18+
use InitPHP\Queue\Interfaces\AdapterInterface;
19+
use InitPHP\Queue\Interfaces\JobInterface;
20+
21+
class PDOAdapter implements AdapterInterface
22+
{
23+
24+
private ?PDO $pdo;
25+
26+
private string $table;
27+
28+
public function __construct(PDO $pdo, string $table = 'queue')
29+
{
30+
$this->pdo = $pdo;
31+
$this->table = $table;
32+
}
33+
34+
/**
35+
* @param object $message
36+
* @return bool
37+
*/
38+
public function worker(object $message): bool
39+
{
40+
try {
41+
$payload = json_decode($message->payload, true);
42+
$jobClass = $payload['jobClass'];
43+
$jobObj = $jobClass($this);
44+
if (!($jobObj instanceof JobInterface)) {
45+
return false;
46+
}
47+
$jobObj->setPayload($payload['payload'])
48+
->setId($message->id);
49+
50+
return $jobObj->handle() ? $jobObj->ack() : $jobObj->nack();
51+
} catch (Throwable $e) {
52+
return false;
53+
}
54+
}
55+
56+
/**
57+
* @inheritDoc
58+
*/
59+
public function handle(string $channel, string $queue): bool
60+
{
61+
try {
62+
63+
do {
64+
$stmt = $this->pdo->prepare("SELECT * FROM " . $this->table . " WHERE channel = :channel AND queue = :queue AND status = 0 LIMIT 0, 1");
65+
if (!$stmt) {
66+
return false;
67+
}
68+
$stmt->bindValue(':channel', $channel);
69+
$stmt->bindValue(':queue', $queue);
70+
if (!$stmt->execute()) {
71+
return false;
72+
}
73+
if ($stmt->rowCount() < 1) {
74+
return false;
75+
}
76+
$stmt->setFetchMode(PDO::FETCH_OBJ);
77+
$res = $stmt->fetch();
78+
79+
$update = $this->pdo->prepare("UPDATE " . $this->table . " SET status = 1, updated_at = :updated_at WHERE id = :id");
80+
$update->bindValue(':id', $res->id, PDO::PARAM_INT);
81+
$update->bindValue(':updated_at', date("Y-m-d H:i:s"));
82+
$update->execute();
83+
84+
$this->worker($res);
85+
} while (true);
86+
} catch (Throwable $e) {
87+
return false;
88+
}
89+
}
90+
91+
/**
92+
* @inheritDoc
93+
*/
94+
public function push(string $channel, string $queue, JobInterface $job): bool
95+
{
96+
try {
97+
$insert = $this->pdo->prepare("INSERT INTO " . $this->table . " (channel, queue, payload, created_at, updated_at, status) VALUES (:channel, :queue, :payload, :created_at, NULL, 0);");
98+
99+
$insert->bindValue(':channel', $channel);
100+
$insert->bindValue(':queue', $queue);
101+
$insert->bindValue(':payload', json_encode(['jobClass' => get_class($job), 'payload' => $job->getPayload()], JSON_UNESCAPED_SLASHES));
102+
$insert->bindValue(':created_at', date("Y-m-d H:i:s"));
103+
return $insert->execute();
104+
} catch (Throwable $e) {
105+
return false;
106+
}
107+
}
108+
109+
/**
110+
* @inheritDoc
111+
*/
112+
public function ack($id, ?string $message = null): bool
113+
{
114+
try {
115+
$delete = $this->pdo->prepare("DELETE FROM " . $this->table . " WHERE id = :id");
116+
$delete->bindValue(':id', $id);
117+
$delete->execute();
118+
} catch (Throwable $e) {
119+
return false;
120+
}
121+
122+
return true;
123+
}
124+
125+
/**
126+
* @inheritDoc
127+
*/
128+
public function nack($id, ?string $message = null): bool
129+
{
130+
try {
131+
$this->pdo->beginTransaction();
132+
$insert = $this->pdo->prepare("INSERT INTO " . $this->table . "_failed (queue_id, channel, queue, payload, created_at, updated_at, status) SELECT * FROM " . $this->table . " WHERE id = :id");
133+
$insert->bindValue(':id', $id);
134+
$insert->execute();
135+
$delete = $this->pdo->prepare("DELETE FROM " . $this->table . " WHERE id = :id");
136+
$delete->bindValue(':id', $id);
137+
$delete->execute();
138+
139+
return $this->pdo->commit();
140+
} catch (Throwable $e) {
141+
$this->pdo->rollBack();
142+
return false;
143+
}
144+
}
145+
146+
/**
147+
* @inheritDoc
148+
*/
149+
public function close(): bool
150+
{
151+
$this->pdo = null;
152+
153+
return true;
154+
}
155+
156+
}

0 commit comments

Comments
 (0)