chubbyphp / petstore
A simple skeleton to build api's based on the chubbyphp-framework.
Requires
- php: ^8.3
- chubbyphp/chubbyphp-api: ^1.2
- chubbyphp/chubbyphp-clean-directories: ^1.5.2
- chubbyphp/chubbyphp-cors: ^1.7.2
- chubbyphp/chubbyphp-decode-encode: ^1.4.1
- chubbyphp/chubbyphp-framework: ^6.0.3
- chubbyphp/chubbyphp-framework-router-fastroute: ^2.3.4
- chubbyphp/chubbyphp-http-exception: ^1.3.3
- chubbyphp/chubbyphp-laminas-config: ^1.5.2
- chubbyphp/chubbyphp-laminas-config-doctrine: ^3.1.3
- chubbyphp/chubbyphp-laminas-config-factory: ^1.5.2
- chubbyphp/chubbyphp-negotiation: ^2.3.2
- chubbyphp/chubbyphp-oidc: ^1.0
- chubbyphp/chubbyphp-parsing: ^3.0
- doctrine/orm: ^3.6.8
- guzzlehttp/guzzle: ^7.10
- monolog/monolog: ^3.10
- ramsey/uuid: ^4.9.3
- slim/psr7: ^1.8
- symfony/console: ^7.4.16|^8.1.4
- symfony/var-exporter: ^7.4.16|^8.1.4
Requires (Dev)
- chubbyphp/chubbyphp-dev-helper: dev-master
- chubbyphp/chubbyphp-mock: ^2.2.1
- dg/bypass-finals: ^1.11
- infection/infection: ^0.34.2
- php-coveralls/php-coveralls: ^2.9.1
- phpstan/extension-installer: ^1.4.3
- phpstan/phpstan: ^2.2.8
- phpunit/phpunit: ^12.5.33
This package is auto-updated.
Last update: 2026-08-18 17:52:50 UTC
README
Description
A simple skeleton to build api's based on the chubbyphp-framework.
Requirements
- php: ^8.3
- chubbyphp/chubbyphp-api: ^1.2
- chubbyphp/chubbyphp-clean-directories: ^1.5.2
- chubbyphp/chubbyphp-cors: ^1.7.2
- chubbyphp/chubbyphp-decode-encode: ^1.4.1
- chubbyphp/chubbyphp-framework: ^6.0.3
- chubbyphp/chubbyphp-framework-router-fastroute: ^2.3.4
- chubbyphp/chubbyphp-http-exception: ^1.3.3
- chubbyphp/chubbyphp-laminas-config: ^1.5.2
- chubbyphp/chubbyphp-laminas-config-doctrine: ^3.1.3
- chubbyphp/chubbyphp-laminas-config-factory: ^1.5.2
- chubbyphp/chubbyphp-negotiation: ^2.3.2
- chubbyphp/chubbyphp-oidc: ^1.0
- chubbyphp/chubbyphp-parsing: ^3.0
- doctrine/orm: ^3.6.8
- guzzlehttp/guzzle: ^7.10
- monolog/monolog: ^3.10
- ramsey/uuid: ^4.9.3
- slim/psr7: ^1.8
- symfony/console: ^7.4.16|^8.1.4
- symfony/var-exporter: ^7.4.16|^8.1.4
Environment
Add the following environment variable to your system, for example within ~/.bashrc or ~/.zshrc:
export USER_ID=$(id -u) export GROUP_ID=$(id -g)
Mount points
bash
touch ~/.bash_docker touch ~/.bash_history
zsh
touch ~/.zsh_docker touch ~/.zsh_history
git
touch ~/.gitconfig touch ~/.gitignore
npm
touch ~/.npmrc
Coding agents
Claude
if [ ! -f ~/.claude.json ]; then cat > ~/.claude.json <<'EOF' {} EOF fi mkdir -p ~/.claude if [ ! -f ~/.claude/.credentials.json ]; then cat > ~/.claude/.credentials.json <<'EOF' {} EOF fi if [ ! -f ~/.claude/settings.json ]; then cat > ~/.claude/settings.json <<'EOF' { "fileCheckpointingEnabled": false, "permissions": { "defaultMode": "bypassPermissions" }, "skipDangerousModePermissionPrompt": true, "spinnerTipsEnabled": false, "switchModelsOnFlag": false, "theme": "auto" } EOF fi chmod 600 \ ~/.claude/.credentials.json \ ~/.claude/settings.json
Codex
mkdir -p ~/.codex if [ ! -f ~/.codex/auth.json ]; then cat > ~/.codex/auth.json <<'EOF' {} EOF fi if [ ! -f ~/.codex/config.toml ]; then cat > ~/.codex/config.toml <<'EOF' approval_policy = "never" sandbox_mode = "danger-full-access" [notice] hide_full_access_warning = true EOF fi chmod 600 \ ~/.codex/auth.json ~/.codex/config.toml
Opencode
mkdir -p ~/.config/opencode ~/.local/share/opencode if [ ! -f ~/.config/opencode/opencode.jsonc ]; then cat > ~/.config/opencode/opencode.jsonc <<'EOF' { "$schema": "https://opencode.ai/config.json", "permission": { "*": "allow" } } EOF fi if [ ! -f ~/.config/opencode/tui.json ]; then cat > ~/.config/opencode/tui.json <<'EOF' { "$schema": "https://opencode.ai/tui.json", "theme": "system", "tips": false } EOF fi if [ ! -f ~/.local/share/opencode/auth.json ]; then printf '{}\n' > ~/.local/share/opencode/auth.json fi chmod 600 \ ~/.config/opencode/opencode.jsonc \ ~/.config/opencode/tui.json \ ~/.local/share/opencode/auth.json
PI
mkdir -p ~/.pi/agent [ ! -f ~/.pi/agent/auth.json ] && echo '{}' > ~/.pi/agent/auth.json
llama.cpp
llama-server \
-hf lmstudio-community/Qwen3.6-35B-A3B-GGUF:Q4_K_M \
-c 32768 \
-ngl 999 \
--flash-attn on \
--host 0.0.0.0 \
--port 9931
Docker
docker-compose up -d
docker-compose exec php bash
Setup
composer install composer setup:dev
Urls
Pet (oidc protected)
- GET https://localhost/api/pets?sort[name]=asc
- POST https://localhost/api/pets
- GET https://localhost/api/pets/019c201f-6a83-7696-9899-50fbf7b2278d
- PUT https://localhost/api/pets/019c201f-6a83-7696-9899-50fbf7b2278d
- DELETE https://localhost/api/pets/019c201f-6a83-7696-9899-50fbf7b2278d
Database
psql "postgresql://petstore:4aAUfBjDACcdZxNwJgJ6@localhost:5432/petstore"
Oidc (keycloak)
All routes below /api are protected by chubbyphp/chubbyphp-oidc, only /ping and /openapi are public.
The keycloak container acts as the identity provider,
the realm petstore gets imported from docker/development/keycloak/import/petstore-realm.json on startup
(delete and recreate the keycloak container to reimport after changes) and contains two users:
john.doe(password:johndoe1234): a regular end user, meant to log in via the browser based frontend (petstore-frontendclient, see below).petstore(password:GBanBPatEBRZ7hf7cAxKn8Ptt): a technical user for requesting tokens via password grant while testing (see the curl example below).
and two clients:
petstore-frontend: public client for a separate (browser based) frontend codebase, which authenticates against keycloak via authorization code flow + PKCE (S256) and sends the resulting access token asAuthorization: Bearer <token>header to this api. The cors setup allows theAuthorizationheader for localhost origins in development.petstore(secret:5FbFAgTAWyVAWSQtDPqCLZzY): confidential client for backend integrations and for requesting tokens via password grant while testing.
Both clients use an audience mapper, so that the access token contains aud: petstore, which this api requires.
Admin console: http://keycloak:8080 (admin / TCUJyCbLtLbBc4eXYYzD9ecm). Keycloak is configured with the fixed
hostname keycloak, so that the issuer claim is always http://keycloak:8080/realms/petstore; requests via
http://localhost:8080 get redirected to that hostname. Add 127.0.0.1 keycloak to /etc/hosts on the host to use
the admin console or to request tokens from the host:
ACCESS_TOKEN=$(curl -s http://keycloak:8080/realms/petstore/protocol/openid-connect/token \ -d 'grant_type=password' \ -d 'client_id=petstore' \ -d 'client_secret=5FbFAgTAWyVAWSQtDPqCLZzY' \ -d 'username=petstore' \ -d 'password=GBanBPatEBRZ7hf7cAxKn8Ptt' | sed -E 's/.*"access_token":"([^"]+)".*/\1/') curl --insecure -H "Authorization: Bearer ${ACCESS_TOKEN}" -H 'Accept: application/json' https://localhost/api/pets
The integration tests run against keycloak as well (no auth mocking): tests/Helper/AuthHelper.php waits for the
discovery endpoint of OIDC_ISSUER to be reachable and requests tokens via password grant with the petstore
client and user. Within the php container keycloak is reachable as keycloak, in ci a keycloak container gets
started and published on the docker bridge gateway (see .github/workflows/ci.yml).
Structure
Collection
Collections are sortable, filterable paginated lists of models.
Dto
A DTO, or Data Transfer Object, is a simple object used to transport data between software application components.
Model
Models, entities, documents what ever fits your purpose the best.
ORM
ORM Mapping definitions.
Parsing
Parses and validates data against predefined schemas, ensuring that incoming data conforms to expected structures and criteria.
Repository
Repositories get data from storages like databases, elasticsearch, redis or whereever your models are stored or cached.
RequestHandler
RequestHandler alias Controller, or Controller actions to be more precise. There is a directory with generic crud controllers. If you like the idea adapt them for your generic use case, if not drop them. I highly recommend to not extend them.
ServiceFactory
Service factories are the glue code of the dependeny injection container.
Opensearch
Policy to delete logstash formatted indicies after 14 days.
curl -XPUT 'https://localhost:9200/_plugins/_ism/policies/logstash-policy' \ -u 'admin:98T722Eqw99oqFCSJCnB' \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -d '{ "policy": { "description": "Logstash", "default_state": "hot", "states": [ { "name": "hot", "actions": [], "transitions": [ { "state_name": "delete", "conditions": { "min_index_age": "14d" } } ] }, { "name": "delete", "actions": [ { "delete": {} } ] } ], "ism_template": { "index_patterns" : ["logstash-*"], "priority": 100 } } }' \ --insecure
Dashboard
Before you start, produce at least one error, produce a 404.
- Username: admin
- Password: 98T722Eqw99oqFCSJCnB
- Index pattern name: logstash-*
- Time field: @timestamp
Copyright
2026 Dominik Zogg