Skip to content

Commit 90800c0

Browse files
committed
refactor: reduce complexity hot paths and streamline README
1 parent 6211570 commit 90800c0

6 files changed

Lines changed: 343 additions & 282 deletions

File tree

README.md

Lines changed: 49 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -1,197 +1,100 @@
1-
# PSFS
2-
[![Build Status](https://scrutinizer-ci.com/g/psfs/core/badges/build.png?b=master)](https://scrutinizer-ci.com/g/psfs/core/build-status/master)
3-
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/psfs/core/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/psfs/core/?branch=master)
4-
[![Code Coverage](https://scrutinizer-ci.com/g/psfs/core/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/psfs/core/?branch=master)
1+
# PSFS Core
2+
53
[![Packagist Stable Version](https://img.shields.io/packagist/v/psfs/core)](https://packagist.org/packages/psfs/core)
64
[![Development Line](https://img.shields.io/badge/dev--master-2.2.x--dev-0A7BBB)](https://github.com/psfs/core/tree/master)
75
[![PHP 8.3](https://img.shields.io/badge/PHP-8.3-777BB4.svg?logo=php&logoColor=white)](https://www.php.net/releases/8.3/en.php)
86

9-
## PHP Simple Fast & Secure Framework
10-
11-
PSFS is a lightweight PHP framework focused on MVC/API applications with Twig, Propel, and Symfony components.
7+
PSFS is a lightweight PHP framework for MVC/API applications (Twig + Propel + Symfony components).
128

139
## Runtime baseline
1410

15-
- Project execution and validation run with Docker Compose.
16-
- Runtime PHP version is **8.3**.
17-
- Default host port is defined in the root `.env` via `HOST_PORT=8008`.
18-
- Main local services in `docker-compose.yml` are `php`, `redis`, and `db`.
19-
20-
Direct PHP requirements declared in `composer.json`:
21-
22-
- `php >=8`
23-
- `ext-json`
24-
- `ext-curl`
25-
- `ext-gmp`
26-
27-
Core package constraints currently used by the framework:
28-
29-
```text
30-
psfs/propel: dev-master
31-
symfony/console: ^7.4
32-
symfony/finder: ^7.4
33-
symfony/translation: ^7.4
34-
twig/twig: ^3.24
35-
monolog/monolog: ^3.10
36-
matthiasmullie/minify: ^1.3
37-
firebase/php-jwt: ^7.0
38-
```
39-
40-
## Local development
11+
- Execution and validation use Docker Compose.
12+
- Target PHP runtime: **8.3**.
13+
- Main services: `php`, `redis`, `db`.
14+
- Host port is configured via `.env` (`HOST_PORT=8008` by default).
4115

42-
Start the stack:
16+
## Quick start
4317

4418
```bash
4519
docker compose up -d
4620
docker compose ps
4721
```
4822

49-
The PHP container is usually named `core-php-1`. If needed, discover it with:
50-
51-
```bash
52-
docker compose ps
53-
docker ps --format '{{.Names}}'
54-
```
55-
56-
Run project commands from the PHP container:
23+
Run project commands inside the PHP container:
5724

5825
```bash
59-
docker exec <php_container> php -v
60-
docker exec <php_container> composer install
61-
docker exec <php_container> php vendor/bin/phpunit
26+
docker exec core-php-1 php -v
27+
docker exec core-php-1 composer install
28+
docker exec core-php-1 php vendor/bin/phpunit --no-coverage
6229
```
6330

64-
The application server exposed by Docker runs:
31+
If your PHP container name differs:
6532

6633
```bash
67-
php -S 0.0.0.0:8080 -t ./html
34+
docker compose ps
35+
docker ps --format '{{.Names}}'
6836
```
6937

70-
and is published on the host as `${HOST_PORT}:8080`.
38+
## Swoole runtime
7139

72-
## Swoole runtime (event worker mode)
73-
74-
PSFS now includes Swoole runtime commands:
40+
Check and run Swoole commands through `src/bin/psfs`:
7541

7642
```bash
77-
docker exec <php_container> php /var/www/src/bin/psfs psfs:swoole:check
78-
docker exec <php_container> php /var/www/src/bin/psfs psfs:swoole:start --host=0.0.0.0 --port=8080
79-
docker exec <php_container> php /var/www/src/bin/psfs psfs:swoole:status
80-
docker exec <php_container> php /var/www/src/bin/psfs psfs:swoole:reload
81-
docker exec <php_container> php /var/www/src/bin/psfs psfs:swoole:stop
43+
docker exec core-php-1 php /var/www/src/bin/psfs psfs:swoole:check
44+
docker exec core-php-1 php /var/www/src/bin/psfs psfs:swoole:start --host=0.0.0.0 --port=8080
45+
docker exec core-php-1 php /var/www/src/bin/psfs psfs:swoole:status
46+
docker exec core-php-1 php /var/www/src/bin/psfs psfs:swoole:reload
47+
docker exec core-php-1 php /var/www/src/bin/psfs psfs:swoole:stop
8248
```
8349

84-
Optional compose profile (keeps baseline `php -S` untouched):
50+
Optional compose profile:
8551

8652
```bash
8753
docker compose --profile swoole up -d php-swoole
8854
docker compose --profile swoole ps
8955
```
9056

91-
## Consumer install
92-
93-
If you want to use PSFS as a Composer dependency in another project:
94-
95-
```bash
96-
composer init
97-
composer require psfs/core
98-
./vendor/bin/psfs psfs:create:root
99-
```
100-
101-
The `psfs:create:root` command generates the document root structure.
102-
103-
Published package note:
104-
105-
- Packagist stable release is currently `2.0.1`.
106-
- Repository head is on the `2.2.x-dev` development line.
107-
108-
## Validation
109-
110-
Mandatory baseline before review:
111-
112-
```bash
113-
docker compose up -d
114-
docker compose ps
115-
docker exec <php_container> php -v
116-
docker exec <php_container> php vendor/bin/phpunit
117-
```
118-
119-
Optional coverage when Xdebug is available:
120-
121-
```bash
122-
docker exec -e XDEBUG_MODE=coverage <php_container> php vendor/bin/phpunit --coverage-text
123-
```
124-
125-
Do not run `php`, `composer`, or `phpunit` directly on the host for project validation.
126-
127-
## Benchmark (`php -S` vs Swoole direct)
128-
129-
Run 5 benchmark iterations per mode and aggregate median/IQR:
130-
131-
```bash
132-
chmod +x tools/benchmark/run-benchmark.sh
133-
tools/benchmark/run-benchmark.sh
134-
```
135-
136-
Artifacts:
137-
138-
- `cache/benchmark/baseline/run-*.json`
139-
- `cache/benchmark/swoole/run-*.json`
140-
- `cache/benchmark/baseline-summary.json`
141-
- `cache/benchmark/swoole-summary.json`
142-
143-
## Security/Auth contract (v2)
57+
## Security baseline (v2)
14458

145-
- Auth/cookies are versioned as **v2** with **legacy fallback in read-only mode**.
146-
- Expected result for invalid auth is `null/null` and the request flow must be stopped.
147-
- Compatibility policy: active fallbacks are temporary and can be removed only with explicit user approval.
148-
- Target cookie policy:
59+
- Auth/cookies are versioned as `v2`.
60+
- Legacy fallback remains read-only until explicit removal approval.
61+
- Invalid auth must result in `null/null` and stop request flow.
62+
- Cookie policy target:
14963
- `HttpOnly=true`
150-
- `Secure=true` when running on HTTPS
151-
- `SameSite=Lax` or `SameSite=Strict`
64+
- `Secure=true` on HTTPS
65+
- `SameSite=Lax|Strict`
15266
- `Path=/`
15367
- coherent `Domain`
154-
- TTL aligned with auth/session policy
68+
- TTL aligned with session/auth policy
15569

156-
## Review and commit policy
70+
## CI/CD security gates
15771

158-
- Changes must be reviewed by a human before commit.
159-
- Do not auto-commit after automated changes or agent execution.
72+
Security pipeline blocks merge/release when:
16073

161-
## Environment variables
74+
- a `must_pass` security control test fails,
75+
- any high/critical finding is unresolved,
76+
- hardening or quality gate returns non-pass.
16277

163-
Relevant Docker/runtime variables:
78+
Local pre-check:
16479

165-
```text
166-
APP_ENVIRONMENT=(local|dev|...|prod)
167-
HOST_PORT=8008
168-
DEBUG=-xdebug
169-
PHP_TIMEZONE=Europe/Madrid
170-
PHP_OPCACHE=0
171-
MYSQL_USER=psfs
172-
MYSQL_PASSWORD=psfs
173-
MYSQL_ROOT_PASSWORD=psfs
174-
MYSQL_DATABASE=psfs
80+
```bash
81+
act push --container-architecture linux/amd64
17582
```
17683

177-
`DEBUG=-xdebug` loads the Xdebug image variant. An empty value uses the default PHP image.
178-
179-
## Versioning
84+
## Install as dependency
18085

181-
- Packagist stable release: `2.0.1`
182-
- Active development line: `dev-master -> 2.2.x-dev`
183-
- Release/tag policy is documented in `doc/VERSIONING.md`
86+
```bash
87+
composer require psfs/core
88+
./vendor/bin/psfs psfs:create:root
89+
```
18490

18591
## Documentation
18692

187-
- [General information and contracts](./doc/CONTRACTS.md)
93+
- [Contracts](./doc/CONTRACTS.md)
18894
- [Versioning policy](./doc/VERSIONING.md)
189-
- [Async queue and connector contracts](./doc/contracts/async-jobs-connectors-contracts.md)
190-
- [Security policy](./SECURITY.md)
95+
- [Async jobs and connectors contracts](./doc/contracts/async-jobs-connectors-contracts.md)
19196

192-
## Roadmap
97+
## Notes
19398

194-
- Framework documentation
195-
- PhpDoc for all files
196-
- Testing
197-
- 100% tests coverage
99+
- Do not run `php`, `composer`, or `phpunit` directly on host for project validation.
100+
- Human review is required before committing automated/agent-driven changes.

src/base/queue/FileJobQueue.php

Lines changed: 65 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -54,54 +54,30 @@ public function dequeue(string $queue): ?array
5454
if (false === $input) {
5555
return null;
5656
}
57+
5758
$tmpPath = $this->createTempQueuePath($queueFile);
5859
$output = @fopen($tmpPath, 'wb');
5960
if (false === $output) {
6061
@fclose($input);
6162
return $this->dequeueUsingFullRead($queueFile);
6263
}
6364

64-
$first = null;
65-
$hasRemaining = false;
66-
try {
67-
while (($line = fgets($input)) !== false) {
68-
$line = trim($line);
69-
if ('' === $line) {
70-
continue;
71-
}
72-
if (null === $first) {
73-
$first = $line;
74-
continue;
75-
}
76-
if (false === @fwrite($output, $line . PHP_EOL)) {
77-
return null;
78-
}
79-
$hasRemaining = true;
80-
}
81-
} finally {
82-
@fclose($input);
83-
@fclose($output);
65+
$result = $this->dequeueIntoTemp($input, $output);
66+
if ($result === null) {
67+
@unlink($tmpPath);
68+
return null;
8469
}
8570

86-
if (null === $first) {
71+
if ($result['first'] === null) {
8772
@unlink($tmpPath);
8873
return null;
8974
}
9075

91-
if ($hasRemaining) {
92-
if (!@rename($tmpPath, $queueFile)) {
93-
$remaining = @file_get_contents($tmpPath);
94-
@unlink($tmpPath);
95-
if (false === $remaining || !FileHelper::writeFileAtomic($queueFile, $remaining)) {
96-
return null;
97-
}
98-
}
99-
} else {
100-
FileHelper::deleteFile($queueFile);
101-
@unlink($tmpPath);
76+
if (!$this->commitDequeuedState($queueFile, $tmpPath, $result['has_remaining'])) {
77+
return null;
10278
}
10379

104-
$decoded = json_decode($first, true);
80+
$decoded = json_decode($result['first'], true);
10581
return is_array($decoded) ? $decoded : null;
10682
});
10783
}
@@ -166,4 +142,60 @@ private function createTempQueuePath(string $queueFile): string
166142
$random = uniqid('tmp_', true);
167143
return $queueFile . '.' . $random;
168144
}
145+
146+
/**
147+
* @param resource $input
148+
* @param resource $output
149+
* @return array{first:?string,has_remaining:bool}|null
150+
*/
151+
private function dequeueIntoTemp($input, $output): ?array
152+
{
153+
$first = null;
154+
$hasRemaining = false;
155+
156+
try {
157+
while (($line = fgets($input)) !== false) {
158+
$line = trim($line);
159+
if ($line === '') {
160+
continue;
161+
}
162+
163+
if ($first === null) {
164+
$first = $line;
165+
continue;
166+
}
167+
168+
if (false === @fwrite($output, $line . PHP_EOL)) {
169+
return null;
170+
}
171+
$hasRemaining = true;
172+
}
173+
} finally {
174+
@fclose($input);
175+
@fclose($output);
176+
}
177+
178+
return [
179+
'first' => $first,
180+
'has_remaining' => $hasRemaining,
181+
];
182+
}
183+
184+
private function commitDequeuedState(string $queueFile, string $tmpPath, bool $hasRemaining): bool
185+
{
186+
if (!$hasRemaining) {
187+
FileHelper::deleteFile($queueFile);
188+
@unlink($tmpPath);
189+
return true;
190+
}
191+
192+
if (@rename($tmpPath, $queueFile)) {
193+
return true;
194+
}
195+
196+
$remaining = @file_get_contents($tmpPath);
197+
@unlink($tmpPath);
198+
199+
return false !== $remaining && FileHelper::writeFileAtomic($queueFile, $remaining);
200+
}
169201
}

0 commit comments

Comments
 (0)