Skip to content

Commit 4ef26c5

Browse files
committed
CRUD fix and xdebug
1 parent cd23633 commit 4ef26c5

File tree

8 files changed

+227
-33
lines changed

8 files changed

+227
-33
lines changed

.gitignore

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
/.php-cs-fixer.cache
33
/.phpunit.result.cache
44
/.idea/
5-
/.vscode/
65
/build/
76
ab*.log
8-
*.log
7+
*.log
8+
9+
# vscode
10+
.vscode/*
11+
!.vscode/launch.json

.vscode/launch.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"version": "0.2.0",
3+
"configurations": [
4+
{
5+
"name": "Listen for Xdebug",
6+
"type": "php",
7+
"request": "launch",
8+
"port": 9003,
9+
"stopOnEntry": false,
10+
"log": true,
11+
"pathMappings": {
12+
"/app": "${workspaceFolder}" // container path → local path
13+
}
14+
}
15+
]
16+
}

Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ RUN docker-php-ext-install pdo pdo_mysql pdo_sqlite
3030
RUN pecl install redis \
3131
&& docker-php-ext-enable redis
3232

33+
RUN pecl install xdebug \
34+
&& docker-php-ext-enable xdebug
35+
3336
# --- Composer ---
3437
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
3538

docker-compose.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ services:
44
context: .
55
dockerfile: Dockerfile
66
image: php-swoole-crud-microservice:latest
7+
volumes:
8+
- .:/app
9+
- ./docker/php/xdebug.ini:/usr/local/etc/php/conf.d/xdebug.ini
710
environment:
811
APP_ENV: prod
912
DB_DRIVER: mysql
@@ -19,6 +22,8 @@ services:
1922
SWOOLE_WORKER_NUM: 4
2023
SWOOLE_TASK_WORKER_NUM: 4
2124
SWOOLE_MAX_REQUEST: 10000
25+
XDEBUG_MODE: debug
26+
XDEBUG_CONFIG: client_host=host.docker.internal client_port=9003
2227
ports:
2328
- "9501:9501"
2429
depends_on: [mysql, redis]
@@ -51,8 +56,8 @@ services:
5156
image: prom/mysqld-exporter:latest
5257
container_name: mysqld_exporter
5358
environment:
54-
# DATA_SOURCE_NAME: "root:root@(mysql:3306)/"
55-
- DATA_SOURCE_NAME=exporter:exporter@(mysql:3306)/
59+
DATA_SOURCE_NAME: "root:root@(mysql:3306)/"
60+
# - DATA_SOURCE_NAME=exporter:exporter@(mysql:3306)/
5661
volumes:
5762
- ./docker/mysql/.my.cnf:/etc/mysql/.my.cnf:ro
5863
command:

docker/php/xdebug.ini

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
zend_extension=xdebug.so
2+
3+
[xdebug]
4+
xdebug.mode=debug
5+
xdebug.start_with_request=yes
6+
xdebug.client_host=host.docker.internal ; for Mac/Windows
7+
; for Linux, set to your host IP (e.g., 172.17.0.1)
8+
xdebug.client_port=9003 ; VS Code default
9+
xdebug.log_level=5
10+
xdebug.log=/tmp/xdebug.log
11+
xdebug.discover_client_host=true
12+
; xdebug.idekey=VSCODE
13+
xdebug.max_nesting_level=512
14+
xdebug.output_dir=/tmp
15+
; xdebug.var_display_max_depth=5
16+
; xdebug.var_display_max_children=256
17+
; xdebug.var_display_max_data=1024
18+
; xdebug.profiler_enable_trigger=1
19+
; xdebug.profiler_output_dir=/tmp
20+
; xdebug.trace_output_dir=/tmp
21+
; xdebug.collect_params=4
22+
; xdebug.collect_return=1
23+
; xdebug.collect_vars=1
24+
; xdebug.show_local_vars=1
25+
; xdebug.show_mem_delta=1
26+
; xdebug.cli_color=1
27+
; xdebug.file_link_format="vscode://file/%f:%l"
28+
;xdebug.halt_level=E_WARNING
29+
;xdebug.halt_on_warning=1
30+
xdebug.halt_on_error=1
31+
xdebug.halt_on_exception=1
32+
xdebug.halt_on_fatal_error=1
33+
xdebug.halt_on_parse_error=1

scripts/migrate.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
$cfg = require __DIR__ . "/../config/config.php";
3+
$db = $cfg["db"]["mysql"];
4+
5+
$conn = mysqli_init();
6+
mysqli_real_connect(
7+
$conn,
8+
$db["host"],
9+
$db["user"],
10+
$db["pass"],
11+
$db["dbname"],
12+
$db["port"]
13+
);
14+
15+
if (mysqli_connect_errno()) {
16+
fwrite(STDERR, "MySQL connect error: " . mysqli_connect_error() . PHP_EOL);
17+
exit(1);
18+
}
19+
20+
$migrations = require __DIR__ . "/../config/database.php";
21+
22+
foreach ($migrations as $k => $migs) {
23+
foreach ($migs as $m) {
24+
$sql = file_get_contents($m);
25+
if (!mysqli_multi_query($conn, $sql)) {
26+
fwrite(STDERR, "Error running $m: " . mysqli_error($conn) . PHP_EOL);
27+
exit(1);
28+
}
29+
while (mysqli_more_results($conn) && mysqli_next_result($conn)) {;}
30+
}
31+
}
32+
33+
echo "migrated\n";

src/Repositories/ItemRepository.php

Lines changed: 65 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,88 @@
33
namespace App\Repositories;
44

55
use App\Core\DbContext;
6-
use PDO;
76

87
final class ItemRepository
98
{
109
public function __construct(private DbContext $ctx) {}
10+
1111
public function create(array $d): int
1212
{
13-
$stmt = $this->ctx->conn()->prepare("INSERT INTO items (sku,title,price) VALUES (?,?,?)");
14-
$stmt->execute([$d['sku'], $d['title'], $d['price']]);
15-
return (int)$this->ctx->conn()->lastInsertId();
13+
$conn = $this->ctx->conn(); // returns Swoole\Coroutine\MySQL
14+
15+
$stmt = $conn->prepare("INSERT INTO items (sku, title, price) VALUES (?, ?, ?)");
16+
if ($stmt === false) {
17+
throw new \RuntimeException("Failed to prepare statement: " . $conn->error);
18+
}
19+
20+
$result = $stmt->execute([$d['sku'], $d['title'], (float)$d['price']]);
21+
if ($result === false) {
22+
throw new \RuntimeException("Insert failed: " . $stmt->error);
23+
}
24+
25+
return (int)$conn->insert_id;
1626
}
27+
1728
public function find(int $id): ?array
1829
{
19-
$stmt = $this->ctx->conn()->prepare("SELECT * FROM items WHERE id=?");
20-
$stmt->execute([$id]);
21-
return $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
30+
$conn = $this->ctx->conn();
31+
32+
$stmt = $conn->prepare("SELECT id, sku, title, price, created_at, updated_at FROM items WHERE id=? LIMIT 1");
33+
if ($stmt === false) {
34+
throw new \RuntimeException("Failed to prepare statement: " . $conn->error);
35+
}
36+
37+
$rows = $stmt->execute([$id]);
38+
if ($rows === false) {
39+
throw new \RuntimeException("Query failed: " . $stmt->error);
40+
}
41+
42+
return $rows[0] ?? null;
2243
}
44+
2345
public function list(): array
2446
{
25-
return $this->ctx->conn()->query("SELECT * FROM items ORDER BY id DESC limit 100")->fetchAll(PDO::FETCH_ASSOC);
47+
$conn = $this->ctx->conn();
48+
49+
$rows = $conn->query("SELECT id, sku, title, price, created_at, updated_at FROM items ORDER BY id DESC LIMIT 100");
50+
if ($rows === false) {
51+
throw new \RuntimeException("Query failed: " . $conn->error);
52+
}
53+
54+
return $rows;
2655
}
56+
2757
public function update(int $id, array $d): bool
2858
{
29-
$stmt = $this->ctx->conn()->prepare("UPDATE items SET sku=?, title=?, price=? WHERE id=?");
30-
return $stmt->execute([$d['sku'], $d['title'], $d['price'], $id]);
59+
$conn = $this->ctx->conn();
60+
61+
$stmt = $conn->prepare("UPDATE items SET sku=?, title=?, price=? WHERE id=?");
62+
if ($stmt === false) {
63+
throw new \RuntimeException("Failed to prepare statement: " . $conn->error);
64+
}
65+
66+
$result = $stmt->execute([$d['sku'], $d['title'], (float)$d['price'], $id]);
67+
if ($result === false) {
68+
throw new \RuntimeException("Update failed: " . $stmt->error);
69+
}
70+
71+
return (bool)($result['affected_rows'] ?? 0);
3172
}
73+
3274
public function delete(int $id): bool
3375
{
34-
$stmt = $this->ctx->conn()->prepare("DELETE FROM items WHERE id=?");
35-
return $stmt->execute([$id]);
76+
$conn = $this->ctx->conn();
77+
78+
$stmt = $conn->prepare("DELETE FROM items WHERE id=?");
79+
if ($stmt === false) {
80+
throw new \RuntimeException("Failed to prepare statement: " . $conn->error);
81+
}
82+
83+
$result = $stmt->execute([$id]);
84+
if ($result === false) {
85+
throw new \RuntimeException("Delete failed: " . $stmt->error);
86+
}
87+
88+
return (bool)($result['affected_rows'] ?? 0);
3689
}
3790
}

src/Repositories/UserRepository.php

Lines changed: 65 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,43 +3,91 @@
33
namespace App\Repositories;
44

55
use App\Core\DbContext;
6-
use PDO;
76

87
final class UserRepository
98
{
109
public function __construct(private DbContext $ctx)
1110
{
1211
//
1312
}
13+
1414
public function create(array $d): int
1515
{
16-
$conn = $this->ctx->conn();
17-
$stmt = $conn->prepare("INSERT INTO users (name,email) VALUES (?,?)");
18-
$stmt->execute([$d['name'], $d['email']]);
19-
return (int)$conn->lastInsertId();
16+
$conn = $this->ctx->conn(); // returns Swoole\Coroutine\MySQL
17+
18+
$stmt = $conn->prepare("INSERT INTO users (name, email) VALUES (?, ?)");
19+
if ($stmt === false) {
20+
throw new \RuntimeException("Failed to prepare statement: " . $conn->error);
21+
}
22+
23+
$result = $stmt->execute([$d['name'], $d['email']]);
24+
if ($result === false) {
25+
throw new \RuntimeException("Insert failed: " . $stmt->error);
26+
}
27+
28+
return (int)$conn->insert_id;
2029
}
30+
2131
public function find(int $id): ?array
2232
{
23-
$stmt = $this->ctx->conn()->prepare("SELECT id,name,email,created_at,updated_at FROM users WHERE id=?");
24-
$stmt->execute([$id]);
25-
$row = $stmt->fetch(PDO::FETCH_ASSOC);
26-
return $row ?: null;
33+
$conn = $this->ctx->conn();
34+
35+
$stmt = $conn->prepare("SELECT id, name, email, created_at, updated_at FROM users WHERE id=? LIMIT 1");
36+
if ($stmt === false) {
37+
throw new \RuntimeException("Failed to prepare statement: " . $conn->error);
38+
}
39+
40+
$rows = $stmt->execute([$id]);
41+
if ($rows === false) {
42+
throw new \RuntimeException("Query failed: " . $stmt->error);
43+
}
44+
45+
return $rows[0] ?? null;
2746
}
47+
2848
public function list(): array
2949
{
30-
return $this->ctx
31-
->conn()
32-
->query("SELECT id,name,email,created_at,updated_at FROM users ORDER BY id DESC LIMIT 100")
33-
->fetchAll(PDO::FETCH_ASSOC);
50+
$conn = $this->ctx->conn();
51+
52+
$rows = $conn->query("SELECT id, name, email, created_at, updated_at FROM users ORDER BY id DESC LIMIT 100");
53+
if ($rows === false) {
54+
throw new \RuntimeException("Query failed: " . $conn->error);
55+
}
56+
57+
return $rows;
3458
}
59+
3560
public function update(int $id, array $d): bool
3661
{
37-
$stmt = $this->ctx->conn()->prepare("UPDATE users SET name=?, email=? WHERE id=?");
38-
return $stmt->execute([$d['name'], $d['email'], $id]);
62+
$conn = $this->ctx->conn();
63+
64+
$stmt = $conn->prepare("UPDATE users SET name=?, email=? WHERE id=?");
65+
if ($stmt === false) {
66+
throw new \RuntimeException("Failed to prepare statement: " . $conn->error);
67+
}
68+
69+
$result = $stmt->execute([$d['name'], $d['email'], $id]);
70+
if ($result === false) {
71+
throw new \RuntimeException("Update failed: " . $stmt->error);
72+
}
73+
74+
return (bool)($result['affected_rows'] ?? 0);
3975
}
76+
4077
public function delete(int $id): bool
4178
{
42-
$stmt = $this->ctx->conn()->prepare("DELETE FROM users WHERE id=?");
43-
return $stmt->execute([$id]);
79+
$conn = $this->ctx->conn();
80+
81+
$stmt = $conn->prepare("DELETE FROM users WHERE id=?");
82+
if ($stmt === false) {
83+
throw new \RuntimeException("Failed to prepare statement: " . $conn->error);
84+
}
85+
86+
$result = $stmt->execute([$id]);
87+
if ($result === false) {
88+
throw new \RuntimeException("Delete failed: " . $stmt->error);
89+
}
90+
91+
return (bool)($result['affected_rows'] ?? 0);
4492
}
4593
}

0 commit comments

Comments
 (0)