Compare commits

...

130 Commits

Author SHA1 Message Date
Kévin Dunglas
79e3a98a11 fix 2025-06-05 18:08:52 +02:00
Kévin Dunglas
afc4463ab3 feat: use frankenphp.dev as Go module path 2025-06-05 18:01:54 +02:00
Marc
6749ddbde5 ci: remove leading v from parsed version (#1626) 2025-06-03 15:01:24 +02:00
Kévin Dunglas
82ba882a4e chore: prepare release 1.7.0 2025-06-03 10:04:05 +02:00
Kévin Dunglas
4b1679e70f chore: bump deps 2025-06-02 17:36:51 +02:00
David Buchmann
75ce2e22c2 docs: clarify Mercure URLs (#916)
* clarify mercure URLs

* Update docs/mercure.md

Co-authored-by: David Buchmann <david@liip.ch>

* Update mercure.md

---------

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
2025-06-02 16:04:56 +02:00
Marc
5a43e9f4de feat: make frankenphp directive optional in Caddyfile (#1601)
* make frankenphp directive optional, thanks @francislavoie

* get rid of global variable

* update workers when adding to app

* suggestions

* goto instead of continue outer?

* remove empty frankenphp directives

* update config to reflect the optional frankenphp directive

* AI translations

* restore eol newlines

* don't double check for duplicate worker name

* add short form for php_server worker too

* translations

* AI hates EOL newlines now?

* suggestion to check for nil

* suggestion to use else if block
2025-06-02 15:55:55 +02:00
Alexandre Daubois
5542044376 feat: allow omitting value with the --watch flag of php-server (#1595) 2025-06-02 15:54:01 +02:00
Marc
52b65311c2 ci: get package tag version from binary instead (#1606)
* get package tag version from binary instead

* capture output for better debugging instead
2025-06-01 23:55:50 +02:00
MaximAL
2dc8048ad2 docs: optimize PNG images losslessly: 2 → 1.3 MiB (−36%) (#1623) 2025-06-01 21:47:08 +02:00
Rob Landers
a59b649dac fix: headers before flushing (#1622)
* add tests

* fix test

* attempt to send headers when flushing

* Update testdata/only-headers.php

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>

---------

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
2025-06-01 14:58:36 +02:00
Indra Gunawan
68a4548bf4 skip worker name default value assignment on unmarshal (#1607) 2025-05-31 10:25:47 +02:00
Kévin Dunglas
6f049f9a9c ci: minor cleanup (#1619)
* ci: minor cleanup

* add .golangci.yaml
2025-05-31 08:01:38 +02:00
Kévin Dunglas
340b1fd1c2 docs: improve compilation instructions 2025-05-30 14:05:08 +02:00
wu
c9329bd717 docs: fix typo in French laravel.md (#1617) 2025-05-30 14:04:29 +02:00
Kévin Dunglas
f54a1fa85e fix: prevent cert install warning in Docker images 2025-05-30 14:03:50 +02:00
Kévin Dunglas
b4115ca9a2 fix: linking on OpenBSD 2025-05-29 08:23:17 +02:00
Kévin Dunglas
14469d4a0a chore: fix typo in test comment 2025-05-27 10:10:39 +02:00
Kévin Dunglas
ee394756b1 chore: prepare release 1.6.2 2025-05-23 10:41:10 +02:00
Laury S.
5a260c430a chore: improve style of the default index.php file (#1598)
* feat: improve style of index.php file

* feat: remove assets folder

* Update index.php

* Update index.php

---------

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
2025-05-23 10:24:57 +02:00
Kévin Dunglas
b6fcab5a95 ci: always login to Docker if not a Pull Request (#1599) 2025-05-23 00:53:03 +02:00
Kévin Dunglas
1e49586b0e chore: prepare release 1.6.1 2025-05-22 16:49:58 +02:00
Kévin Dunglas
b27cd1c986 ci: fix packages building (#1596)
* ci: fix packages building

* fix groupdel
2025-05-22 16:44:09 +02:00
Kévin Dunglas
c6483088c5 fix(docker): command to create /etc/frankenphp 2025-05-22 00:57:01 +02:00
Kévin Dunglas
5a9785d0d9 fix(docker): prevent BC break with the new Caddyfile path 2025-05-21 01:19:58 +02:00
Kévin Dunglas
c522b52804 fix: exit(), die() and uncaught exceptions must stop the worker 2025-05-21 01:19:22 +02:00
Kévin Dunglas
9a8ad979e0 ci: don't login to the Docker hub for PRs 2025-05-21 01:18:33 +02:00
Alexandre Daubois
663aff7cc4 chore: improve Homebrew compatibility 2025-05-20 20:40:14 +02:00
Kévin Dunglas
79f2b2347b chore: reduce write error level to warn in logs 2025-05-20 20:38:49 +02:00
Kévin Dunglas
bf5c98410b chore: log thread (#1589) 2025-05-20 10:10:46 +02:00
Kévin Dunglas
cf7541fde6 chore: add more logs for the worker 2025-05-19 22:43:54 +02:00
dependabot[bot]
25491068df ci: bump super-linter/super-linter in the github-actions group
Bumps the github-actions group with 1 update: [super-linter/super-linter](https://github.com/super-linter/super-linter).


Updates `super-linter/super-linter` from 7.3.0 to 7.4.0
- [Release notes](https://github.com/super-linter/super-linter/releases)
- [Changelog](https://github.com/super-linter/super-linter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/super-linter/super-linter/compare/v7.3.0...v7.4.0)

---
updated-dependencies:
- dependency-name: super-linter/super-linter
  dependency-version: 7.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-19 13:49:56 +02:00
Kévin Dunglas
d72751b9fd ci: always login to the Docker Hub to mitigate rate limiting issues 2025-05-19 13:46:55 +02:00
Kévin Dunglas
8820e53819 ci: fix typo in dev Dockerfile 2025-05-16 12:00:04 +02:00
Kévin Dunglas
d2b6f9e723 chore: prepare release 1.6.0 2025-05-16 10:16:35 +02:00
Kévin Dunglas
13fbe126ea fix: automatically change cwd when embedding an app 2025-05-16 10:10:01 +02:00
Kévin Dunglas
afa7dafe1c chore: bump deps 2025-05-16 09:22:00 +02:00
Marc
39e22bd5e0 fix: use sudo to build packages (#1568) 2025-05-15 23:12:38 +02:00
Marc
bbbfdb31b5 ci: build .rpm and .deb packages (#1497)
* add ./create-rpm.sh file to build a "frankenphp" rpm package

* also build a deb package

* renamed to build-packages

* linter...

* add depends

* linter again?

* linter number 3

* linter number 4

* set default locations for ini file, conf files and extensions

* set unified path for modules that should be ok on all dists

* add default content into "package" folder

* make file executable

* worker is in public folder

* what on earth did I do x)

* use same FRANKENPHP_VERSION and make sure to let pr's run the rpm generation too (version 0.0.0) to see issues

* install ruby, fpm and rpm-build

* move to after changing base urls because it would fail with packages not found

* ruby 3 build needs gcc 10

* rpm-build is necessary too...

* and I forgot to link the package folder

* create directories if they don't exist

* copy out all frankenphp* files?

* lint fix

* only copy frankenphp-* files

* only copy frankenphp-* files

* the .deb file is name frankenphp_1.5.0... - create output folder instead and upload all things inside that
will simplify things when later adding xdebug.so and ffi.so

* update the last two steps to use the gh-output directory

* add post install script to set frankenphp able to bind to port 80 for non-root users

* dnf over yum, I think the yum alias was removed in RH 9.5

* newlines

* newlines

* add text what missing libcap means

* copy php.ini-production from php-src, linter, update ruby version

* move Caddyfile to /etc/frankenphp/Caddyfile

* linter

* fix a copy and paste error

* better describe fallback to 0.0.0

* linter

* copy installation scripts from official caddy packages, change user to frankenphp too

* bombombom

* make files executable

* tabs

* linter

* linter again

* use empty directory for three different destinations instead of keeping three empty local directories

* caddy says the file is incorrectly formatted without these spaces

* remove wildcard matcher from root directive

* Apply suggestions from code review

commit suggested changes to preinstall/postinstall scripts

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>

* Update dev.Dockerfile

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>

* remove misleading comment

* update documentation for paths

* update documentation for paths some more

* fix musl opcache-jit issue

* markdown linter

* the damn tab

* Apply suggestions from code review

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>

* drop dev.Dockerfile php location from config.md

* add php config note to CONTRIBUTING.md

* dashes instead of asterisks in chinese docs

* fix package building

* create frankenphp user in case it doesn't exist for deb packages

* create users if they don't exist, delete them again if they didn't exist

* satisfy linter

* create the user with the same commands as the postinst/preinstall scripts

* Removes toolchain requirements.

* trigger

* Removes explicit calls to go get

* trigger

* setcap by default

* simplify example project

* bring page more in line with the caddy / apache / nginx default page

* update to html 5

* oopsies

* revert style to original

* remove https:// (caddy uses http:// on RHEL, :80 on Debian)

---------

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
Co-authored-by: Alliballibaba <alliballibaba@gmail.com>
2025-05-14 07:33:05 +02:00
Alexander Stecher
0b83602575 fix: makes response writer error a debug message. (#1549)
* Makes response writer error a debug message.

* trigger

* log at warn level

* Update frankenphp.go

---------

Co-authored-by: Alliballibaba <alliballibaba@gmail.com>
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
2025-05-13 17:34:10 +02:00
Kévin Dunglas
ecca9dc01d ci: use latest stable Go version for the mostly static binary (#1558)
* ci: use latest stable Go version for the mostly static binary

* fix
2025-05-13 16:10:02 +02:00
Kévin Dunglas
eb40c03a21 chore: use strings.ContainsAny() for needReplacement() 2025-05-13 10:27:35 +02:00
Alexander Stecher
c2390e7c3b fix: php-cli flag parsing conflicts (#1559)
* Fixes flag parsing.

* trigger

* trigger

* Fixes flag parsing.

---------

Co-authored-by: Alliballibaba <alliballibaba@gmail.com>
2025-05-13 10:24:59 +02:00
Kévin Dunglas
0d12a5162d fix: use local Go toolchain (#1546) 2025-05-11 22:30:19 +02:00
Alexander Stecher
a48db9422d fix: go toolchain versioning (#1545)
* Removes toolchain requirements.

* trigger

* Removes explicit calls to go get

* trigger

* Update static-builder-musl.Dockerfile

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>

* Update static-builder-musl.Dockerfile

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>

* Update static-builder-gnu.Dockerfile

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>

* Update alpine.Dockerfile

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>

* Update Dockerfile

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>

* Update Dockerfile

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>

* Update alpine.Dockerfile

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>

* trigger

* trigger

---------

Co-authored-by: Alliballibaba <alliballibaba@gmail.com>
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
2025-05-11 09:18:45 +02:00
Alexander Stecher
ab0fcd80de Fixes metrics also with regular request timeouts. (#1550) 2025-05-10 14:31:58 +02:00
Tolsee
2f7b987198 feat: dequeue worker request on timeout (#1540) 2025-05-09 18:01:49 +02:00
Marc
1d74b2caa8 feat: define domain specific workers in php_server and php blocks (#1509)
* add module (php_server directive) based workers

* refactor moduleID to uintptr for faster comparisons

* let workers inherit environment variables and root from php_server

* caddy can shift FrankenPHPModules in memory for some godforsaken reason, can't rely on them staying the same

* remove debugging statement

* fix tests

* refactor moduleID to uint64 for faster comparisons

* actually allow multiple workers per script filename

* remove logging

* utility function

* reuse existing worker with same filename and environment when calling newWorker with a filepath that already has a suitable worker, simply add number of threads

* no cleanup happens between tests, so restore old global worker overwriting logic

* add test, use getWorker(ForContext) function in frankenphp.go as well

* bring error on second global worker with the same filename again

* refactor to using name instead of moduleID

* nicer name

* nicer name

* add more tests

* remove test case already covered by previous test

* revert back to single variable, moduleIDs no longer relevant

* update comment

* figure out the worker to use in FrankenPHPModule::ServeHTTP

* add caddy/config_tests, add --retry 5 to download

* add caddy/config_tests

* sum up logic a bit, put worker thread addition into moduleWorkers parsing, before workers are actually created

* implement suggestions as far as possible

* fixup

* remove tags

* feat: download the mostly static binary when possible (#1467)

* feat: download the mostly static binary when possible

* cs

* docs: remove wildcard matcher from root directive (#1513)

* docs: update README with additional documentation links

Add link to classic mode, efficiently serving large static files and monitoring FrankenPHP

Signed-off-by: Romain Bastide <romain.bastide@orange.com>

* ci: combine dependabot updates for one group to 1 pull-request

* feat: compatibility with libphp.dylib on macOS

* feat: upgrade to Caddy 2.10

* feat: upgrade to Caddy 2.10

* chore: run prettier

* fix: build-static.sh consecutive builds (#1496)

* fix consecutive builds

* use minor version in PHP_VERSION

* install jq in centos container

* fix "arm64" download arch for spc binary

* jq is not available as a rpm download

* linter

* specify php 8.4 default

specify 8.4 so we manually switch to 8.5 when we make sure it works
allows to run without jq installed

* Apply suggestions from code review

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>

---------

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>

* chore: update Go and toolchain version (#1526)

* apply suggestions one be one - scriptpath only

* generate unique worker names by filename and number

* support worker config from embedded apps

* rename back to make sure we don't accidentally add FrankenPHPApp workers to the slice

* fix test after changing error message

* use 🧩 for module workers

* use 🌍 for global workers :)

* revert 1c414cebbc

* revert 4cc8893ced

* apply suggestions

* add dynamic config loading test of module worker

* fix test

* minor changes

---------

Signed-off-by: Romain Bastide <romain.bastide@orange.com>
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
Co-authored-by: Indra Gunawan <hello@indra.my.id>
Co-authored-by: Romain Bastide <romain.bastide@orange.com>
2025-05-05 16:14:19 +02:00
Kévin Dunglas
92e92335e3 docs: fix warning markup 2025-05-05 15:12:04 +02:00
dependabot[bot]
8f5f9e4c8b ci: bump golangci/golangci-lint-action in the github-actions group
Bumps the github-actions group with 1 update: [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action).


Updates `golangci/golangci-lint-action` from 7 to 8
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v7...v8)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-version: '8'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-05 14:28:35 +02:00
Kévin Dunglas
5b7fc5ec52 chore: make the linter happy (#1537) 2025-05-02 11:43:54 +02:00
Marc
05220de0e3 typo snuck into last pr (#1536) 2025-05-01 16:31:18 +02:00
Alexander Stecher
3741782330 feat: '-r' option for php-cli (#1482) 2025-05-01 02:06:31 +02:00
Indra Gunawan
a6e1d3554d fix negative frankenphp_ready_workers metrics (#1491) 2025-05-01 02:05:23 +02:00
Kévin Dunglas
6f1b4f3bae ci: fix GNU manifest (#1535) 2025-04-30 14:52:40 +02:00
Thomas Cochard
cd540bda11 Fix -d / --wait arguments (#1531) 2025-04-29 16:36:23 +02:00
Alexander Stecher
8125993001 fix: disallow 2 workers with same filename (#1492)
* Disallows 2 workers with the same filename.

* Adds test.

* Prevent duplicate names.

---------

Co-authored-by: a.stecher <a.stecher@sportradar.com>
Co-authored-by: Alliballibaba <alliballibaba@gmail.com>
2025-04-29 10:18:24 +02:00
Kévin Dunglas
8583afd83e chore: add context to logs to make the linter happy (#1533) 2025-04-29 01:08:15 +02:00
Kévin Dunglas
d10a243f86 ci: fix GNU manifest (#1534) 2025-04-29 01:07:37 +02:00
Indra Gunawan
1ec37f6cc9 feat: replace zap with slog (#1527) 2025-04-26 11:04:46 +02:00
Kévin Dunglas
4ad5e870ec ci: fix static GNU binary copy (#1528) 2025-04-26 11:03:36 +02:00
Kévin Dunglas
49d2e62996 chore: bump Mercure and downgrade cbrotli (#1525)
* chore: bump Mercure

* downgrade cbrotli
2025-04-23 14:01:33 +02:00
Indra Gunawan
8febee71af chore: update Go and toolchain version (#1526) 2025-04-23 11:02:37 +02:00
Marc
16814581f9 fix: build-static.sh consecutive builds (#1496)
* fix consecutive builds

* use minor version in PHP_VERSION

* install jq in centos container

* fix "arm64" download arch for spc binary

* jq is not available as a rpm download

* linter

* specify php 8.4 default

specify 8.4 so we manually switch to 8.5 when we make sure it works
allows to run without jq installed

* Apply suggestions from code review

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>

---------

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
2025-04-23 09:44:02 +02:00
Kévin Dunglas
ffa52f7c8d chore: run prettier 2025-04-23 01:02:44 +02:00
Kévin Dunglas
4550027de4 feat: upgrade to Caddy 2.10 2025-04-22 17:34:11 +02:00
Kévin Dunglas
7f8e43fd62 feat: upgrade to Caddy 2.10 2025-04-22 16:15:11 +02:00
Kévin Dunglas
254c0a8a55 feat: compatibility with libphp.dylib on macOS 2025-04-22 16:07:07 +02:00
Indra Gunawan
22cf94d556 ci: combine dependabot updates for one group to 1 pull-request 2025-04-22 16:06:31 +02:00
Romain Bastide
a4dc93f831 docs: update README with additional documentation links
Add link to classic mode, efficiently serving large static files and monitoring FrankenPHP

Signed-off-by: Romain Bastide <romain.bastide@orange.com>
2025-04-22 16:05:06 +02:00
Indra Gunawan
c276a3f434 docs: remove wildcard matcher from root directive (#1513) 2025-04-22 11:27:29 +02:00
Kévin Dunglas
02a1c92b88 feat: download the mostly static binary when possible (#1467)
* feat: download the mostly static binary when possible

* cs
2025-04-18 14:22:58 +02:00
Kévin Dunglas
8092f4a35c chore!: update to golangci-lint-action 7 (#1508) 2025-04-17 20:33:22 +02:00
David Legrand
b250bd9a07 docs: add instructions to run Caddyfile from static binary (#1501) 2025-04-17 15:31:29 +02:00
Pierre du Plessis
99064ee3e1 fix: build-static.sh (#1474)
* Fixes build-static script

* Add composer to gnu image

* Fix syntax
2025-04-17 14:56:33 +02:00
Marc
58a728b790 docs: add configuration note about the ominous php directive (#1495)
* add note about the `php` directive in the configuration page

* Update config.md

* Update config.md

---------

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
2025-04-17 14:54:58 +02:00
Marc
66aa975d47 fix: disable -march-native in case that lead to the illegal instruction in de265_init->init_scan_orders #1460 (#1493) 2025-04-15 15:29:51 +02:00
Kévin Dunglas
5e1ad5d797 docs: efficiently serving large static files (X-Sendfile/X-Accel-Redirect) (#896)
* docs: X-Sendfile/X-Accel-Redirect

* lint

* fix
2025-04-14 17:18:50 +02:00
Romain Bastide
96dd739064 docs: sync French docs with English (#1475)
* docs: update configuration options for frankenphp and add file watching details

* docs: add classic mode usage in french documentation

Signed-off-by: Romain Bastide <romain.bastide@orange.com>

* docs: add French metrics documentation

Signed-off-by: Romain Bastide <romain.bastide@orange.com>

* docs: improve formatting and clarity in configuration documentation

Signed-off-by: Romain Bastide <romain.bastide@orange.com>

* docs: update contributing guide with build instructions and debugging tips

Signed-off-by: Romain Bastide <romain.bastide@orange.com>

* docs: fix link formatting in classic mode documentation

Signed-off-by: Romain Bastide <romain.bastide@orange.com>

* docs: enhance Docker documentation with tag pattern for FrankenPHP image and usage details

Signed-off-by: Romain Bastide <romain.bastide@orange.com>

* docs: enhance embed documentation with PHP extensions customization

Signed-off-by: Romain Bastide <romain.bastide@orange.com>

* docs: add static binary packaging steps

Signed-off-by: Romain Bastide <romain.bastide@orange.com>

* docs: add troubleshooting for TLS/SSL issues with static binaries

Signed-off-by: Romain Bastide <romain.bastide@orange.com>

* docs: add max_threads and try_files configuration details to performance documentation

Signed-off-by: Romain Bastide <romain.bastide@orange.com>

* docs: update Docker run command syntax for clarity

Signed-off-by: Romain Bastide <romain.bastide@orange.com>

* docs: add optional dependencies and build tags for Brotli and file watcher features

Signed-off-by: Romain Bastide <romain.bastide@orange.com>

* docs: enhance static binary documentation with build instructions and performance tips

Signed-off-by: Romain Bastide <romain.bastide@orange.com>

* docs: add file change watch and manual worker restart instructions to worker documentation

Signed-off-by: Romain Bastide <romain.bastide@orange.com>

* docs: typo

* docs: remove english text in french doc

* docs: last missing translations for worker failures

* docs: typo

* docs: typo

* docs: fix lint errors

* docs: add max_wait_time configuration details and clarify thread scaling

Signed-off-by: Romain Bastide <romain.bastide@orange.com>

* docs: add missing translations for thread pool and max_threads configuration

Signed-off-by: Romain Bastide <romain.bastide@orange.com>

---------

Signed-off-by: Romain Bastide <romain.bastide@orange.com>
2025-04-08 11:01:37 +02:00
Pierre Tondereau
729cf9bba1 fix: module reload on request startup (#1476) 2025-04-01 20:54:24 +02:00
Alexander Stecher
c5752f9e3b docs: max_wait_time (#1465) 2025-04-01 20:53:04 +02:00
Pierre du Plessis
ba36f92a35 fix: remove extra -gnu suffic in static build images (#1472) 2025-04-01 08:33:09 +02:00
Kévin Dunglas
d3589f9770 chore: prepare release 1.5.0 2025-03-25 20:29:55 +01:00
Marc
8e6a183bda refactor: simplify using mimalloc (#1454)
* simplify using mimalloc

* fix the duplication issue of mimalloc.o since the linker deduplicates archives automatically, but it's slightly suboptimal. better would be to prevent cgo from duplicating it in the first place.

* only set stack size for musl

* Update build-static.sh
2025-03-25 15:22:46 +01:00
Indra Gunawan
855b3f93b1 metrics: register prometheus collectors only if enabled (#1457)
* collect metrics only if enabled

* Update caddy/caddy.go

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>

* Update caddy/caddy.go

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>

---------

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
2025-03-25 11:38:54 +01:00
Marc
f85ca1c2d2 docs: glibc-based mostly static builds and loading extensions (#1453)
* add glibc based static builder to documentation

* english docs for gnu/extensions

* remove source again

* lint fixes

* why is there no .editorconfig :(

* apply suggestions

* Update docs/static.md

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>

* remove list

* Update performance.md

* Update static.md

* Update static.md

---------

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
2025-03-24 12:00:12 +01:00
Kévin Dunglas
a30ed2e9d3 ci: use latest version of watcher (#1456) 2025-03-24 11:58:00 +01:00
Kévin Dunglas
565b3a9629 chore: bump deps (#1455) 2025-03-24 11:56:20 +01:00
Gina Peter Banyard
3701516e5e refactor: call opcache_reset PHP function directly (#1401)
* Call opcache_reset PHP function directly

* prevent warning

* cleanup

* remove frankenphp_execute_php_function

* cs

---------

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
2025-03-24 11:29:13 +01:00
Alexander Stecher
f36bd51163 perf(metrics): use WithLabelValues instead (#1450)
* Uses WithMetricLabels instead.

* trigger build

---------

Co-authored-by: Alliballibaba <alliballibaba@gmail.com>
2025-03-24 10:14:02 +01:00
Ian
45bba2101f docs: update linking to binary (#1452) 2025-03-23 07:53:28 +01:00
Indra Gunawan
87315a19ae feat: introduces worker name option, use label on worker metrics instead (#1376)
* add worker name option and use it in logs and metrics, update tests

* fix missing reference for collector

* update tests

* update docs

* fix conflict

* add missing allowedDirectives

* update tests
2025-03-22 12:32:59 +01:00
Jerry Ma
3bc426482a feat: add glibc-based static binary (#1438)
* Add gnu static binary build support

* Remove --libc option

* configure ./build-static.sh to allow extension loading with glibc

* use tabs everywhere

* do not use prebuilt sources for glibc build

* ffi does not work with musl builds

* remove unnecessary tabs

* disable opcache jit on musl

* disable opcache jit on musl again

* err, build command, not download command

* cs fixes

* spellcheck

* even more cs fixes

* fix ar removing .a libs

* disable ffi extension for now

* add gnu static action

* add gnu-static target

* skip CHECKOV 2 and 3

* rename static-builder to static-builder-musl, gnu-static to static-builder-gnu
run arm64 gnu job on ubuntu-arm

* rename build-linux to build-linux-musl

* rename job description to specify musl

* higher optimisation flags

* Update docker-bake.hcl

---------

Co-authored-by: DubbleClick <m@pyc.ac>
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
2025-03-22 11:41:47 +01:00
Kévin Dunglas
341b0240c9 ci: include version in BuildInfo and Prometheus metrics (#1418) 2025-03-19 13:27:28 +01:00
Alexander Stecher
432824edf1 fix: ensure env is not in an invalid state on shutdown (#1442) 2025-03-19 13:22:06 +01:00
Alexander Stecher
9cca12858b feat: maximum wait times (#1445) 2025-03-19 13:21:37 +01:00
Alexander Stecher
cc473ee03e fix: better max_threads calculation (#1446) 2025-03-19 13:21:10 +01:00
Alexander Stecher
93266dfcad feat(watcher): log last changed file (#1447)
* logs last changed file.

* Fixes race condition.

---------

Co-authored-by: Alliballibaba <alliballibaba@gmail.com>
2025-03-19 13:10:02 +01:00
dependabot[bot]
6203d207fa chore(caddy): bump github.com/caddyserver/certmagic in /caddy
Bumps [github.com/caddyserver/certmagic](https://github.com/caddyserver/certmagic) from 0.21.7 to 0.22.0.
- [Release notes](https://github.com/caddyserver/certmagic/releases)
- [Commits](https://github.com/caddyserver/certmagic/compare/v0.21.7...v0.22.0)

---
updated-dependencies:
- dependency-name: github.com/caddyserver/certmagic
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-12 15:30:22 +01:00
Kévin Dunglas
424ca426cb fix: timeouts handling on macOS (#1435)
* ci: run tests on macOS

* debug

* debug

* fix

* nobrotli

* install brotli

* fix

* fix

* Also registers php.ini if ZEND_MAX_EXECUTION_TIMERS is disabled.

* Removes max_execution_time from tests (it gets overwritten on mac)

* tiny refacto

* fix free

* cs

---------

Co-authored-by: Alliballibaba <alliballibaba@gmail.com>
2025-03-11 17:34:49 +01:00
Alexander Stecher
a9cf944b62 ci: env test remediation (#1436)
* nbParallell

* trigger build

* Update frankenphp_test.go

---------

Co-authored-by: Alliballibaba <alliballibaba@gmail.com>
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
2025-03-10 22:59:18 +01:00
Alexander Stecher
8d9ce15849 fix: log worker failures (#1437)
* Small fixes on error.

* Adds comments.

---------

Co-authored-by: Alliballibaba <alliballibaba@gmail.com>
2025-03-10 22:49:58 +01:00
Kévin Dunglas
409c0fdf5f chore: bump deps (#1434) 2025-03-10 15:35:17 +01:00
Alexander Stecher
f50248a7d2 refactor: removes context on the C side (#1404) 2025-03-10 08:44:03 +01:00
Alexander Stecher
09b8219ad4 fix(caddy): stricter configuration handling (#1424)
* Adds warnings.

* trigger build

* Errors on wrong configuration.

---------

Co-authored-by: Alliballibaba <alliballibaba@gmail.com>
2025-03-10 08:43:37 +01:00
Alexander Stecher
f2bae25a78 chore: update static build cli PHP version to 8.4 (#1425) 2025-03-09 17:04:06 +01:00
dependabot[bot]
3dd90a3071 ci: bump super-linter/super-linter from 7.2.1 to 7.3.0
Bumps [super-linter/super-linter](https://github.com/super-linter/super-linter) from 7.2.1 to 7.3.0.
- [Release notes](https://github.com/super-linter/super-linter/releases)
- [Changelog](https://github.com/super-linter/super-linter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/super-linter/super-linter/compare/v7.2.1...v7.3.0)

---
updated-dependencies:
- dependency-name: super-linter/super-linter
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-03 23:22:37 +01:00
Alexander Stecher
c57f741d83 fix: concurrent env access (#1409) 2025-03-01 14:45:04 +01:00
Alexander Stecher
3ba4e257a1 fix: only drain workers on graceful shutdown (#1405)
* Only drains workers on shutdown.

* trigger build

* Marks func as experimental.

---------

Co-authored-by: Alliballibaba <alliballibaba@gmail.com>
2025-02-28 12:10:00 +01:00
Alexander Stecher
619c903386 perf: nocallback and noescape cgo flags (#1406) 2025-02-28 12:08:08 +01:00
Kévin Dunglas
78824107f0 docs: Homebrew installation instructions 2025-02-27 17:17:10 +01:00
Kévin Dunglas
f64c0f948e chore: remove unused executePHPFunction (#1398) 2025-02-21 19:09:54 +01:00
Alexander Stecher
db3e1a047c fix: race condition revealed by tests (#1403)
* Resolves a race condition

* Removes unused code.

* trigger build

* Removes accidental files.

---------

Co-authored-by: Alliballibaba <alliballibaba@gmail.com>
2025-02-21 19:09:08 +01:00
Kévin Dunglas
80f13f07ea docs: fix typos (#1399) 2025-02-21 13:55:37 +01:00
Alliballibaba2
072151dfee feat: Adds automatic thread scaling at runtime and php_ini configuration in Caddyfile (#1266)
Adds option to scale threads at runtime

Adds php_ini configuration in Caddyfile
2025-02-19 20:39:33 +01:00
Kévin Dunglas
965fa6570c chore: prepare release 1.4.4 2025-02-19 12:43:26 +01:00
Kévin Dunglas
251567a617 fix: Mercure duplicate metrics panic (#1393)
* fix: Mercure duplicate metrics panic

* tidy

* ci: clang-format
2025-02-19 12:40:59 +01:00
Indra Gunawan
57e7747b9b fix: duplicate metrics collector registration attempted panic 2025-02-19 12:18:40 +01:00
Niels Dossche
f109f0403b perf: avoid redundant work in frankenphp_release_temporary_streams()
Persistent streams are of type le_pstream, not le_stream. Therefore, the
persistent check will always be false. We can thus replace that check
with an assertion.

`zend_list_delete` removes the entry from the regular_list table, and
calls `zend_resource_dtor` via the table destructor.
When the refcount is 1, `zend_list_close` calls `zend_resource_dtor`,
but keeps the entry in the table.
Multiple calls to `zend_resource_dtor` have no effect: the destructor is
only called once.
Therefore, the `zend_list_close` operation is redundant because it is
fully included in the work done by `zend_list_delete`.
2025-02-19 00:16:00 +01:00
Kévin Dunglas
d407dbd498 chore: prepare release 1.4.3 2025-02-18 09:19:00 +01:00
Kévin Dunglas
d970309544 ci: upgrade watcher to the latest stable version (#1385)
* ci: workaround to compile the latest version of watcher

* remove workaround
2025-02-18 09:17:44 +01:00
Niels Dossche
30bf69cbe5 perf: avoid extra string allocation in get_full_env() (#1382)
* Avoid extra string allocation in get_full_env()

We can pass the key directly to add_assoc_str().

* Use add_assoc_str_ex
2025-02-18 09:11:23 +01:00
Kévin Dunglas
f61bc180c4 chore: upgrade to Go 1.24 2025-02-18 07:33:36 +01:00
Alexander Stecher
9f5e7a9eaa fix(watcher): handles associated events (#1379)
* Handles associated events.

* triggers pipeline

* Adjusts comment.

* Uses fixed version.

* Update watch_pattern_test.go

---------

Co-authored-by: Alliballibaba <alliballibaba@gmail.com>
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
2025-02-17 23:47:27 +01:00
Kévin Dunglas
a5ca60da44 chore: fix markdown linter (#1384) 2025-02-17 23:46:11 +01:00
Indra Gunawan
1c097a6fdf feat(caddy): use logger from Caddy context (#1369) 2025-02-17 10:32:15 +01:00
Indra Gunawan
233753ca6b docs: update docs for first-time contributor (#1368) 2025-02-17 10:31:33 +01:00
Indra Gunawan
9dd05b0b1b docs: link metrics docs to website (#1370) 2025-02-17 10:30:58 +01:00
Indra Gunawan
4c92633396 fix: missing metrics with Caddy 2.9 (#1366)
* fix missing metrics

* update tests

* use interface instead
2025-02-12 12:55:53 +01:00
Zhanbolat Yerkinbay
be2e4714f5 docs: translate to RU (#1325)
* README.md

* worker.md

* early-hints.md

* config.md

* docker.md

* production.md

* fix

* mercure.md

* performance.md

* embed.md

* compile.md

* static.md

* laravel.md

* known-issues.md

* fix links

* github-actions.md

* metrics.md

* CONTRIBUTING.md

* fix

* fix

* fix

* main review fix

---------

Co-authored-by: zhanbolat <z.yerkinbay@slotegrator.space>
2025-01-29 18:09:48 +01:00
176 changed files with 11726 additions and 4198 deletions

View File

@@ -1,14 +1,11 @@
# ignored
**/*
# authorized
!**/Caddyfile
!**/*.go
!**/go.*
!**/*.c
!**/*.h
!testdata/*.php
!testdata/*.txt
!build-static.sh
!app.tar
!app_checksum.txt
/caddy/frankenphp/frankenphp
/internal/testserver/testserver
/internal/testcli/testcli
/dist
.DS_Store
.idea/
.vscode/
__debug_bin
frankenphp.dev.test
caddy/frankenphp/Build
*.log

View File

@@ -7,20 +7,27 @@ updates:
interval: weekly
commit-message:
prefix: chore
groups:
go-modules:
patterns:
- "*"
- package-ecosystem: gomod
directory: /caddy
schedule:
interval: weekly
commit-message:
prefix: chore(caddy)
# These packages must be in sync with versions
# used by github.com/caddyserver/caddy/v2
ignore:
- dependency-name: github.com/google/cel-go
- dependency-name: github.com/quic-go/*
groups:
go-modules:
patterns:
- "*"
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
commit-message:
prefix: ci
groups:
github-actions:
patterns:
- "*"

View File

@@ -133,8 +133,8 @@ jobs:
with:
platforms: ${{ matrix.platform }}
- name: Login to DockerHub
if: fromJson(needs.prepare.outputs.push)
uses: docker/login-action@v3
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
with:
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
@@ -223,6 +223,7 @@ jobs:
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v3
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
with:
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}

View File

@@ -21,10 +21,8 @@ jobs:
with:
fetch-depth: 0
- name: Lint Code Base
uses: super-linter/super-linter/slim@v7.2.1
uses: super-linter/super-linter/slim@v7.4.0
env:
VALIDATE_ALL_CODEBASE: true
DEFAULT_BRANCH: main
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
LINTER_RULES_PATH: /
MARKDOWN_CONFIG_FILE: .markdown-lint.yaml

View File

@@ -13,6 +13,8 @@ on:
- "docs/**"
permissions:
contents: read
env:
GOTOOLCHAIN: local
jobs:
# Adapted from https://github.com/beberlei/hdrhistogram-php
sanitizers:
@@ -36,7 +38,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: "1.22"
go-version: "1.24"
cache-dependency-path: |
go.sum
caddy/go.sum
@@ -99,4 +101,4 @@ jobs:
- name: Compile tests
run: go test ${{ matrix.sanitizer == 'msan' && '-tags=nowatcher' || '' }} -${{ matrix.sanitizer }} -v -x -c
- name: Run tests
run: ./frankenphp.test -test.v
run: ./frankenphp.dev.test -test.v

View File

@@ -29,6 +29,7 @@ permissions:
attestations: write
env:
IMAGE_NAME: ${{ (github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.version) || startsWith(github.ref, 'refs/tags/')) && 'dunglas/frankenphp' || 'dunglas/frankenphp-dev' }}
GOTOOLCHAIN: local
jobs:
prepare:
runs-on: ubuntu-24.04
@@ -36,6 +37,7 @@ jobs:
push: ${{ toJson((steps.check.outputs.ref || (github.event_name == 'workflow_dispatch' && inputs.version) || startsWith(github.ref, 'refs/tags/') || (github.ref == 'refs/heads/main' && github.event_name != 'pull_request')) && true || false) }}
platforms: ${{ steps.matrix.outputs.platforms }}
metadata: ${{ steps.matrix.outputs.metadata }}
gnu_metadata: ${{ steps.matrix.outputs.gnu_metadata }}
ref: ${{ steps.check.outputs.ref }}
steps:
- name: Get version
@@ -58,15 +60,17 @@ jobs:
- name: Create platforms matrix
id: matrix
run: |
METADATA="$(docker buildx bake --print static-builder | jq -c)"
METADATA="$(docker buildx bake --print static-builder-musl | jq -c)"
GNU_METADATA="$(docker buildx bake --print static-builder-gnu | jq -c)"
{
echo metadata="${METADATA}"
echo platforms="$(jq -c 'first(.target[]) | .platforms' <<< "${METADATA}")"
echo gnu_metadata="${GNU_METADATA}"
} >> "${GITHUB_OUTPUT}"
env:
SHA: ${{ github.sha }}
VERSION: ${{ steps.check.outputs.ref || 'dev' }}
build-linux:
build-linux-musl:
strategy:
fail-fast: false
matrix:
@@ -79,7 +83,7 @@ jobs:
debug: true
- platform: linux/amd64
mimalloc: true
name: Build ${{ matrix.platform }} static binary${{ matrix.debug && ' (debug)' || '' }}${{ matrix.mimalloc && ' (mimalloc)' || '' }}
name: Build ${{ matrix.platform }} static musl binary${{ matrix.debug && ' (debug)' || '' }}${{ matrix.mimalloc && ' (mimalloc)' || '' }}
runs-on: ${{ startsWith(matrix.platform, 'linux/arm') && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }}
needs: [prepare]
steps:
@@ -96,8 +100,8 @@ jobs:
with:
platforms: ${{ matrix.platform }}
- name: Login to DockerHub
if: ${{ fromJson(needs.prepare.outputs.push) && !matrix.debug && !matrix.mimalloc }}
uses: docker/login-action@v3
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
with:
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
@@ -107,16 +111,16 @@ jobs:
with:
pull: true
load: ${{ !fromJson(needs.prepare.outputs.push) || matrix.debug || matrix.mimalloc }}
targets: static-builder
targets: static-builder-musl
set: |
${{ matrix.debug && 'static-builder.args.DEBUG_SYMBOLS=1' || '' }}
${{ matrix.mimalloc && 'static-builder.args.MIMALLOC=1' || '' }}
${{ (github.event_name == 'pull_request' || matrix.platform == 'linux/arm64') && 'static-builder.args.NO_COMPRESS=1' || '' }}
${{ matrix.debug && 'static-builder-musl.args.DEBUG_SYMBOLS=1' || '' }}
${{ matrix.mimalloc && 'static-builder-musl.args.MIMALLOC=1' || '' }}
${{ (github.event_name == 'pull_request' || matrix.platform == 'linux/arm64') && 'static-builder-musl.args.NO_COMPRESS=1' || '' }}
*.tags=
*.platform=${{ matrix.platform }}
*.cache-from=type=gha,scope=${{ needs.prepare.outputs.ref || github.ref }}-static-builder${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }}
*.cache-from=type=gha,scope=refs/heads/main-static-builder${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }}
*.cache-to=type=gha,scope=${{ needs.prepare.outputs.ref || github.ref }}-static-builder${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }},ignore-error=true
*.cache-from=type=gha,scope=${{ needs.prepare.outputs.ref || github.ref }}-static-builder-musl${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }}
*.cache-from=type=gha,scope=refs/heads/main-static-builder-musl${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }}
*.cache-to=type=gha,scope=${{ needs.prepare.outputs.ref || github.ref }}-static-builder-musl${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }},ignore-error=true
${{ (fromJson(needs.prepare.outputs.push) && !matrix.debug && !matrix.mimalloc) && format('*.output=type=image,name={0},push-by-digest=true,name-canonical=true,push=true', env.IMAGE_NAME) || '' }}
env:
SHA: ${{ github.sha }}
@@ -129,7 +133,7 @@ jobs:
mkdir -p /tmp/metadata
# shellcheck disable=SC2086
digest=$(jq -r '."static-builder"."containerimage.digest"' <<< ${METADATA})
digest=$(jq -r '."static-builder-musl"."containerimage.digest"' <<< ${METADATA})
touch "/tmp/metadata/${digest#sha256:}"
env:
METADATA: ${{ steps.build.outputs.metadata }}
@@ -137,16 +141,16 @@ jobs:
if: fromJson(needs.prepare.outputs.push) && !matrix.debug && !matrix.mimalloc
uses: actions/upload-artifact@v4
with:
name: metadata-static-builder-${{ steps.prepare.outputs.sanitized_platform }}
name: metadata-static-builder-musl-${{ steps.prepare.outputs.sanitized_platform }}
path: /tmp/metadata/*
if-no-files-found: error
retention-days: 1
- name: Copy binary
run: |
# shellcheck disable=SC2034
digest=$(jq -r '."static-builder"."${{ (fromJson(needs.prepare.outputs.push) && !matrix.debug && !matrix.mimalloc) && 'containerimage.digest' || 'containerimage.config.digest' }}"' <<< "${METADATA}")
docker create --platform=${{ matrix.platform }} --name static-builder "${{ (fromJson(needs.prepare.outputs.push) && !matrix.debug && !matrix.mimalloc) && '${IMAGE_NAME}@${digest}' || '${digest}' }}"
docker cp "static-builder:/go/src/app/dist/${BINARY}" "${BINARY}${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }}"
digest=$(jq -r '."static-builder-musl"."${{ (fromJson(needs.prepare.outputs.push) && !matrix.debug && !matrix.mimalloc) && 'containerimage.digest' || 'containerimage.config.digest' }}"' <<< "${METADATA}")
docker create --platform=${{ matrix.platform }} --name static-builder-musl "${{ (fromJson(needs.prepare.outputs.push) && !matrix.debug && !matrix.mimalloc) && '${IMAGE_NAME}@${digest}' || '${digest}' }}"
docker cp "static-builder-musl:/go/src/app/dist/${BINARY}" "${BINARY}${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }}"
env:
METADATA: ${{ steps.build.outputs.metadata }}
BINARY: frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}
@@ -168,32 +172,150 @@ jobs:
- name: Run sanity checks
run: |
"${BINARY}" version
"${BINARY}" build-info
"${BINARY}" list-modules | grep frankenphp
"${BINARY}" list-modules | grep http.encoders.br
"${BINARY}" list-modules | grep http.handlers.mercure
"${BINARY}" list-modules | grep http.handlers.mercure
"${BINARY}" list-modules | grep http.handlers.vulcain
"${BINARY}" php-cli -r "echo 'Sanity check passed';"
env:
BINARY: ./frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }}
build-linux-gnu:
strategy:
fail-fast: false
matrix:
platform: ${{ fromJson(needs.prepare.outputs.platforms) }}
name: Build ${{ matrix.platform }} static GNU binary
runs-on: ${{ startsWith(matrix.platform, 'linux/arm') && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }}
needs: [prepare]
steps:
- name: Prepare
id: prepare
run: |
platform=${{ matrix.platform }}
echo "sanitized_platform=${platform//\//-}" >> "${GITHUB_OUTPUT}"
- uses: actions/checkout@v4
with:
ref: ${{ needs.prepare.outputs.ref }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
platforms: ${{ matrix.platform }}
- name: Login to DockerHub
uses: docker/login-action@v3
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
with:
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Build
id: build
uses: docker/bake-action@v6
with:
pull: true
load: ${{ !fromJson(needs.prepare.outputs.push) }}
targets: static-builder-gnu
set: |
${{ (github.event_name == 'pull_request' || matrix.platform == 'linux/arm64') && 'static-builder-gnu.args.NO_COMPRESS=1' || '' }}
static-builder-gnu.args.BUILD_PACKAGES=1
*.tags=
*.platform=${{ matrix.platform }}
*.cache-from=type=gha,scope=${{ needs.prepare.outputs.ref || github.ref }}-static-builder-gnu
*.cache-from=type=gha,scope=refs/heads/main-static-builder-gnu
*.cache-to=type=gha,scope=${{ needs.prepare.outputs.ref || github.ref }}-static-builder-gnu,ignore-error=true
${{ fromJson(needs.prepare.outputs.push) && format('*.output=type=image,name={0},push-by-digest=true,name-canonical=true,push=true', env.IMAGE_NAME) || '' }}
env:
SHA: ${{ github.sha }}
VERSION: ${{ (github.ref_type == 'tag' && github.ref_name) || needs.prepare.outputs.ref || 'dev' }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- # Workaround for https://github.com/actions/runner/pull/2477#issuecomment-1501003600
name: Export metadata
if: fromJson(needs.prepare.outputs.push)
run: |
mkdir -p /tmp/metadata-gnu
# shellcheck disable=SC2086
digest=$(jq -r '."static-builder-gnu"."containerimage.digest"' <<< ${METADATA})
touch "/tmp/metadata-gnu/${digest#sha256:}"
env:
METADATA: ${{ steps.build.outputs.metadata }}
- name: Upload metadata
if: fromJson(needs.prepare.outputs.push)
uses: actions/upload-artifact@v4
with:
name: metadata-static-builder-gnu-${{ steps.prepare.outputs.sanitized_platform }}
path: /tmp/metadata-gnu/*
if-no-files-found: error
retention-days: 1
- name: Copy all frankenphp* files
run: |
# shellcheck disable=SC2034
digest=$(jq -r '."static-builder-gnu"."${{ fromJson(needs.prepare.outputs.push) && 'containerimage.digest' || 'containerimage.config.digest' }}"' <<< "${METADATA}")
container_id=$(docker create --platform=${{ matrix.platform }} "${{ fromJson(needs.prepare.outputs.push) && '${IMAGE_NAME}@${digest}' || '${digest}' }}")
mkdir -p gh-output
cd gh-output
for file in $(docker run --rm "${{ fromJson(needs.prepare.outputs.push) && '${IMAGE_NAME}@${digest}' || '${digest}' }}" sh -c "ls /go/src/app/dist | grep '^frankenphp'"); do
docker cp "${container_id}:/go/src/app/dist/${file}" "./${file}"
done
docker rm "${container_id}"
mv "${BINARY}" "${BINARY}-gnu"
env:
METADATA: ${{ steps.build.outputs.metadata }}
BINARY: frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}
- name: Upload artifact
if: ${{ !fromJson(needs.prepare.outputs.push) }}
uses: actions/upload-artifact@v4
with:
name: frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}-gnu-files
path: gh-output/*
- name: Upload assets
if: fromJson(needs.prepare.outputs.push) && (needs.prepare.outputs.ref || github.ref_type == 'tag')
run: gh release upload "${{ (github.ref_type == 'tag' && github.ref_name) || needs.prepare.outputs.ref }}" gh-output/* --repo dunglas/frankenphp --clobber
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- if: fromJson(needs.prepare.outputs.push) && (needs.prepare.outputs.ref || github.ref_type == 'tag')
uses: actions/attest-build-provenance@v2
with:
subject-path: ${{ github.workspace }}/gh-output/frankenphp-linux-*-gnu
- name: Run sanity checks
run: |
"${BINARY}" version
"${BINARY}" list-modules | grep frankenphp
"${BINARY}" list-modules | grep http.encoders.br
"${BINARY}" list-modules | grep http.handlers.mercure
"${BINARY}" list-modules | grep http.handlers.mercure
"${BINARY}" list-modules | grep http.handlers.vulcain
"${BINARY}" php-cli -r "echo 'Sanity check passed';"
env:
BINARY: ./gh-output/frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}-gnu
# Adapted from https://docs.docker.com/build/ci/github-actions/multi-platform/
push:
runs-on: ubuntu-24.04
needs:
- prepare
- build-linux
- build-linux-musl
- build-linux-gnu
if: fromJson(needs.prepare.outputs.push)
steps:
- name: Download metadata
uses: actions/download-artifact@v4
with:
pattern: metadata-static-builder-*
pattern: metadata-static-builder-musl-*
path: /tmp/metadata
merge-multiple: true
- name: Download GNU metadata
uses: actions/download-artifact@v4
with:
pattern: metadata-static-builder-gnu-*
path: /tmp/metadata-gnu
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v3
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
with:
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
@@ -201,16 +323,30 @@ jobs:
working-directory: /tmp/metadata
run: |
# shellcheck disable=SC2046,SC2086
docker buildx imagetools create $(jq -cr '.target."static-builder".tags | map("-t " + .) | join(" ")' <<< "${METADATA}") \
docker buildx imagetools create $(jq -cr '.target."static-builder-musl".tags | map("-t " + .) | join(" ")' <<< "${METADATA}") \
$(printf "${IMAGE_NAME}@sha256:%s " *)
env:
METADATA: ${{ needs.prepare.outputs.metadata }}
- name: Create GNU manifest list and push
working-directory: /tmp/metadata-gnu
run: |
# shellcheck disable=SC2046,SC2086
docker buildx imagetools create $(jq -cr '.target."static-builder-gnu".tags | map("-t " + .) | join(" ")' <<< "${GNU_METADATA}") \
$(printf "${IMAGE_NAME}@sha256:%s " *)
env:
GNU_METADATA: ${{ needs.prepare.outputs.gnu_metadata }}
- name: Inspect image
run: |
# shellcheck disable=SC2046,SC2086
docker buildx imagetools inspect "$(jq -cr '.target."static-builder".tags | first' <<< "${METADATA}")"
docker buildx imagetools inspect "$(jq -cr '.target."static-builder-musl".tags | first' <<< "${METADATA}")"
env:
METADATA: ${{ needs.prepare.outputs.metadata }}
- name: Inspect GNU image
run: |
# shellcheck disable=SC2046,SC2086
docker buildx imagetools inspect "$(jq -cr '.target."static-builder-gnu".tags | first' <<< "${GNU_METADATA}")-gnu"
env:
GNU_METADATA: ${{ needs.prepare.outputs.gnu_metadata }}
build-mac:
strategy:
@@ -228,9 +364,9 @@ jobs:
ref: ${{ needs.prepare.outputs.ref }}
- uses: actions/setup-go@v5
with:
go-version: "1.22"
go-version: "1.24"
cache-dependency-path: |
go.sum
go.sum
caddy/go.sum
- name: Set FRANKENPHP_VERSION
run: |
@@ -262,10 +398,12 @@ jobs:
- name: Run sanity checks
run: |
"${BINARY}" version
"${BINARY}" build-info
"${BINARY}" list-modules | grep frankenphp
"${BINARY}" list-modules | grep http.encoders.br
"${BINARY}" list-modules | grep http.handlers.mercure
"${BINARY}" list-modules | grep http.handlers.mercure
"${BINARY}" list-modules | grep http.handlers.vulcain
"${BINARY}" php-cli -r "echo 'Sanity check passed';"
env:
BINARY: dist/frankenphp-mac-${{ matrix.platform }}

View File

@@ -13,22 +13,25 @@ on:
- "docs/**"
permissions:
contents: read
env:
GOTOOLCHAIN: local
GOEXPERIMENT: cgocheck2
jobs:
tests:
tests-linux:
name: Tests (Linux, PHP ${{ matrix.php-versions }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php-versions: ["8.2", "8.3", "8.4"]
env:
GOEXPERIMENT: cgocheck2
GOMAXPROCS: 10
LIBRARY_PATH: ${{ github.workspace }}/watcher/target/lib
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: "1.22"
go-version: "1.24"
cache-dependency-path: |
go.sum
caddy/go.sum
@@ -43,7 +46,7 @@ jobs:
debug: true
- name: Install e-dant/watcher
uses: ./.github/actions/watcher
- name: Set Set CGO flags
- name: Set CGO flags
run: echo "CGO_CFLAGS=-I${PWD}/watcher/target/include $(php-config --includes)" >> "${GITHUB_ENV}"
- name: Build
run: go build
@@ -67,7 +70,42 @@ jobs:
- name: Run integrations tests
run: ./reload_test.sh
- name: Lint Go code
uses: golangci/golangci-lint-action@v6
uses: golangci/golangci-lint-action@v8
if: matrix.php-versions == '8.4'
with:
version: latest
tests-mac:
name: Tests (macOS, PHP 8.4)
runs-on: macos-latest
env:
HOMEBREW_NO_AUTO_UPDATE: 1
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: "1.24"
cache-dependency-path: |
go.sum
caddy/go.sum
- uses: shivammathur/setup-php@v2
with:
php-version: 8.4
ini-file: development
coverage: none
tools: none
env:
phpts: ts
debug: true
- name: Set Set CGO flags
run: |
{
echo "CGO_CFLAGS=-I/opt/homebrew/include/ $(php-config --includes)"
echo "CGO_LDFLAGS=-L/opt/homebrew/lib/ $(php-config --ldflags) $(php-config --libs)"
} >> "${GITHUB_ENV}"
- name: Build
run: go build -tags nowatcher
- name: Run library tests
run: go test -tags nowatcher -race -v ./...
- name: Run Caddy module tests
working-directory: caddy/
run: go test -tags nowatcher,nobadger,nomysql,nopgx -race -v ./...

6
.gitignore vendored
View File

@@ -2,7 +2,11 @@
/internal/testserver/testserver
/internal/testcli/testcli
/dist
.DS_Store
.idea/
.vscode/
__debug_bin
frankenphp.test
frankenphp.dev.test
caddy/frankenphp/Build
package/etc/php.ini
*.log

7
.golangci.yaml Normal file
View File

@@ -0,0 +1,7 @@
---
version: "2"
run:
build-tags:
- nobadger
- nomysql
- nopgx

View File

@@ -11,9 +11,13 @@ docker build -t frankenphp-dev -f dev.Dockerfile .
docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -p 8080:8080 -p 443:443 -p 443:443/udp -v $PWD:/go/src/app -it frankenphp-dev
```
The image contains the usual development tools (Go, GDB, Valgrind, Neovim...).
The image contains the usual development tools (Go, GDB, Valgrind, Neovim...) and uses the following php setting locations
If docker version is lower than 23.0, build is failed by dockerignore [pattern issue](https://github.com/moby/moby/pull/42676). Add directories to `.dockerignore`.
- php.ini: `/etc/frankenphp/php.ini` A php.ini file with development presets is provided by default.
- additional configuration files: `/etc/frankenphp/php.d/*.ini`
- php extensions: `/usr/lib/frankenphp/modules/`
If your docker version is lower than 23.0, the build will fail due to dockerignore [pattern issue](https://github.com/moby/moby/pull/42676). Add directories to `.dockerignore`.
```patch
!testdata/*.php
@@ -49,10 +53,13 @@ cd testdata/
../caddy/frankenphp/frankenphp run
```
The server is listening on `127.0.0.1:8080`:
The server is listening on `127.0.0.1:80`:
> [!NOTE]
> if you are using Docker, you will have to either bind container port 80 or execute from inside the container
```console
curl -vk https://localhost/phpinfo.php
curl -vk http://127.0.0.1/phpinfo.php
```
## Minimal test server
@@ -108,22 +115,22 @@ docker buildx bake -f docker-bake.hcl --pull --no-cache --push
1. Download the debug version of the FrankenPHP binary from GitHub or create your custom static build including debug symbols:
```console
docker buildx bake \
--load \
--set static-builder.args.DEBUG_SYMBOLS=1 \
--set "static-builder.platform=linux/amd64" \
static-builder
docker cp $(docker create --name static-builder dunglas/frankenphp:static-builder):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp
```
```console
docker buildx bake \
--load \
--set static-builder.args.DEBUG_SYMBOLS=1 \
--set "static-builder.platform=linux/amd64" \
static-builder
docker cp $(docker create --name static-builder-musl dunglas/frankenphp:static-builder-musl):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp
```
2. Replace your current version of `frankenphp` by the debug FrankenPHP executable
3. Start FrankenPHP as usual (alternatively, you can directly start FrankenPHP with GDB: `gdb --args frankenphp run`)
4. Attach to the process with GDB:
```console
gdb -p `pidof frankenphp`
```
```console
gdb -p `pidof frankenphp`
```
5. If necessary, type `continue` in the GDB shell
6. Make FrankenPHP crash
@@ -135,63 +142,60 @@ docker buildx bake -f docker-bake.hcl --pull --no-cache --push
1. Open `.github/workflows/tests.yml`
2. Enable PHP debug symbols
```patch
- uses: shivammathur/setup-php@v2
# ...
env:
phpts: ts
+ debug: true
```
```patch
- uses: shivammathur/setup-php@v2
# ...
env:
phpts: ts
+ debug: true
```
3. Enable `tmate` to connect to the container
```patch
-
name: Set CGO flags
run: echo "CGO_CFLAGS=$(php-config --includes)" >> "$GITHUB_ENV"
+ -
+ run: |
+ sudo apt install gdb
+ mkdir -p /home/runner/.config/gdb/
+ printf "set auto-load safe-path /\nhandle SIG34 nostop noprint pass" > /home/runner/.config/gdb/gdbinit
+ -
+ uses: mxschmitt/action-tmate@v3
```
```patch
- name: Set CGO flags
run: echo "CGO_CFLAGS=$(php-config --includes)" >> "$GITHUB_ENV"
+ - run: |
+ sudo apt install gdb
+ mkdir -p /home/runner/.config/gdb/
+ printf "set auto-load safe-path /\nhandle SIG34 nostop noprint pass" > /home/runner/.config/gdb/gdbinit
+ - uses: mxschmitt/action-tmate@v3
```
4. Connect to the container
5. Open `frankenphp.go`
6. Enable `cgosymbolizer`
```patch
- //_ "github.com/ianlancetaylor/cgosymbolizer"
+ _ "github.com/ianlancetaylor/cgosymbolizer"
```
```patch
- //_ "github.com/ianlancetaylor/cgosymbolizer"
+ _ "github.com/ianlancetaylor/cgosymbolizer"
```
7. Download the module: `go get`
8. In the container, you can use GDB and the like:
```console
go test -tags watcher -c -ldflags=-w
gdb --args frankenphp.test -test.run ^MyTest$
```
```console
go test -tags watcher -c -ldflags=-w
gdb --args frankenphp.dev.test -test.run ^MyTest$
```
9. When the bug is fixed, revert all these changes
## Misc Dev Resources
* [PHP embedding in uWSGI](https://github.com/unbit/uwsgi/blob/master/plugins/php/php_plugin.c)
* [PHP embedding in NGINX Unit](https://github.com/nginx/unit/blob/master/src/nxt_php_sapi.c)
* [PHP embedding in Go (go-php)](https://github.com/deuill/go-php)
* [PHP embedding in Go (GoEmPHP)](https://github.com/mikespook/goemphp)
* [PHP embedding in C++](https://gist.github.com/paresy/3cbd4c6a469511ac7479aa0e7c42fea7)
* [Extending and Embedding PHP by Sara Golemon](https://books.google.fr/books?id=zMbGvK17_tYC&pg=PA254&lpg=PA254#v=onepage&q&f=false)
* [What the heck is TSRMLS_CC, anyway?](http://blog.golemon.com/2006/06/what-heck-is-tsrmlscc-anyway.html)
* [SDL bindings](https://pkg.go.dev/github.com/veandco/go-sdl2@v0.4.21/sdl#Main)
- [PHP embedding in uWSGI](https://github.com/unbit/uwsgi/blob/master/plugins/php/php_plugin.c)
- [PHP embedding in NGINX Unit](https://github.com/nginx/unit/blob/master/src/nxt_php_sapi.c)
- [PHP embedding in Go (go-php)](https://github.com/deuill/go-php)
- [PHP embedding in Go (GoEmPHP)](https://github.com/mikespook/goemphp)
- [PHP embedding in C++](https://gist.github.com/paresy/3cbd4c6a469511ac7479aa0e7c42fea7)
- [Extending and Embedding PHP by Sara Golemon](https://books.google.fr/books?id=zMbGvK17_tYC&pg=PA254&lpg=PA254#v=onepage&q&f=false)
- [What the heck is TSRMLS_CC, anyway?](http://blog.golemon.com/2006/06/what-heck-is-tsrmlscc-anyway.html)
- [SDL bindings](https://pkg.go.dev/github.com/veandco/go-sdl2@v0.4.21/sdl#Main)
## Docker-Related Resources
* [Bake file definition](https://docs.docker.com/build/customize/bake/file-definition/)
* [docker buildx build](https://docs.docker.com/engine/reference/commandline/buildx_build/)
- [Bake file definition](https://docs.docker.com/build/customize/bake/file-definition/)
- [docker buildx build](https://docs.docker.com/engine/reference/commandline/buildx_build/)
## Useful Command
@@ -210,6 +214,6 @@ follow these steps:
3. Copy the `README.md` and `CONTRIBUTING.md` files from the root directory to the new directory
4. Translate the content of the files, but don't change the filenames, also don't translate strings starting with `> [!` (it's special markup for GitHub)
5. Create a Pull Request with the translations
6. In the [site repository](https://github.com/dunglas/frankenphp-website/tree/main), copy and translate the translation files in the `content/`, `data/` and `i18n/` directories
6. In the [site repository](https://github.com/dunglas/frankenphp-website), copy and translate the translation files in the `content/`, `data/` and `i18n/` directories
7. Translate the values in the created YAML file
8. Open a Pull Request on the site repository

View File

@@ -19,17 +19,19 @@ RUN set -eux; \
/app/public \
/config/caddy \
/data/caddy \
/etc/caddy; \
/etc/caddy \
/etc/frankenphp; \
sed -i 's/php/frankenphp run/g' /usr/local/bin/docker-php-entrypoint; \
echo '<?php phpinfo();' > /app/public/index.php
COPY --link caddy/frankenphp/Caddyfile /etc/caddy/Caddyfile
RUN curl -sSLf \
RUN ln /etc/caddy/Caddyfile /etc/frankenphp/Caddyfile && \
curl -sSLf \
-o /usr/local/bin/install-php-extensions \
https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions && \
chmod +x /usr/local/bin/install-php-extensions
CMD ["--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]
CMD ["--config", "/etc/frankenphp/Caddyfile", "--adapter", "caddyfile"]
HEALTHCHECK CMD curl -f http://localhost:2019/metrics || exit 1
# See https://caddyserver.com/docs/conventions#file-locations for details
@@ -57,11 +59,13 @@ SHELL ["/bin/bash", "-o", "pipefail", "-c"]
COPY --from=golang-base /usr/local/go /usr/local/go
ENV PATH=/usr/local/go/bin:$PATH
ENV GOTOOLCHAIN=local
# This is required to link the FrankenPHP binary to the PHP binary
RUN apt-get update && \
apt-get -y --no-install-recommends install \
cmake \
cmake \
git \
libargon2-dev \
libbrotli-dev \
libcurl4-openssl-dev \
@@ -75,21 +79,6 @@ RUN apt-get update && \
&& \
apt-get clean
WORKDIR /go/src/app
COPY --link go.mod go.sum ./
RUN go mod graph | awk '{if ($1 !~ "@") print $2}' | xargs go get
WORKDIR /go/src/app/caddy
COPY --link caddy/go.mod caddy/go.sum ./
RUN go mod graph | awk '{if ($1 !~ "@") print $2}' | xargs go get
WORKDIR /go/src/app
COPY --link *.* ./
COPY --link caddy caddy
COPY --link internal internal
COPY --link testdata testdata
# Install e-dant/watcher (necessary for file watching)
WORKDIR /usr/local/src/watcher
RUN curl -s https://api.github.com/repos/e-dant/watcher/releases/latest | \
@@ -104,6 +93,18 @@ RUN curl -s https://api.github.com/repos/e-dant/watcher/releases/latest | \
cmake --install build && \
ldconfig
WORKDIR /go/src/app
COPY --link go.mod go.sum ./
RUN go mod download
WORKDIR /go/src/app/caddy
COPY --link caddy/go.mod caddy/go.sum ./
RUN go mod download
WORKDIR /go/src/app
COPY --link . ./
# See https://github.com/docker-library/php/blob/master/8.3/bookworm/zts/Dockerfile#L57-L59 for PHP values
ENV CGO_CFLAGS="-DFRANKENPHP_VERSION=$FRANKENPHP_VERSION $PHP_CFLAGS"
ENV CGO_CPPFLAGS=$PHP_CPPFLAGS
@@ -112,10 +113,11 @@ ENV CGO_LDFLAGS="-L/usr/local/lib -lssl -lcrypto -lreadline -largon2 -lcurl -lon
RUN echo $CGO_LDFLAGS
WORKDIR /go/src/app/caddy/frankenphp
RUN GOBIN=/usr/local/bin go install -tags 'nobadger,nomysql,nopgx' -ldflags "-w -s -X 'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP $FRANKENPHP_VERSION PHP $PHP_VERSION Caddy'" && \
RUN GOBIN=/usr/local/bin go install -tags 'nobadger,nomysql,nopgx' -ldflags "-w -s -X 'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP $FRANKENPHP_VERSION PHP $PHP_VERSION Caddy'" -buildvcs=true && \
setcap cap_net_bind_service=+ep /usr/local/bin/frankenphp && \
cp Caddyfile /etc/caddy/Caddyfile && \
frankenphp version
cp Caddyfile /etc/frankenphp/Caddyfile && \
frankenphp version && \
frankenphp build-info
WORKDIR /go/src/app
@@ -133,4 +135,5 @@ RUN apt-get install -y --no-install-recommends libstdc++6 && \
COPY --from=builder /usr/local/bin/frankenphp /usr/local/bin/frankenphp
RUN setcap cap_net_bind_service=+ep /usr/local/bin/frankenphp && \
frankenphp version
frankenphp version && \
frankenphp build-info

106
README.md
View File

@@ -4,36 +4,21 @@
FrankenPHP is a modern application server for PHP built on top of the [Caddy](https://caddyserver.com/) web server.
FrankenPHP gives superpowers to your PHP apps thanks to its stunning features: [*Early Hints*](https://frankenphp.dev/docs/early-hints/), [worker mode](https://frankenphp.dev/docs/worker/), [real-time capabilities](https://frankenphp.dev/docs/mercure/), automatic HTTPS, HTTP/2, and HTTP/3 support...
FrankenPHP gives superpowers to your PHP apps thanks to its stunning features: [_Early Hints_](https://frankenphp.dev/docs/early-hints/), [worker mode](https://frankenphp.dev/docs/worker/), [real-time capabilities](https://frankenphp.dev/docs/mercure/), automatic HTTPS, HTTP/2, and HTTP/3 support...
FrankenPHP works with any PHP app and makes your Laravel and Symfony projects faster than ever thanks to their official integrations with the worker mode.
FrankenPHP can also be used as a standalone Go library to embed PHP in any app using `net/http`.
[**Learn more** on *frankenphp.dev*](https://frankenphp.dev) and in this slide deck:
[**Learn more** on _frankenphp.dev_](https://frankenphp.dev) and in this slide deck:
<a href="https://dunglas.dev/2022/10/frankenphp-the-modern-php-app-server-written-in-go/"><img src="https://dunglas.dev/wp-content/uploads/2022/10/frankenphp.png" alt="Slides" width="600"></a>
## Getting Started
### Docker
```console
docker run -v .:/app/public \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
Go to `https://localhost`, and enjoy!
> [!TIP]
>
> Do not attempt to use `https://127.0.0.1`. Use `https://localhost` and accept the self-signed certificate.
> Use the [`SERVER_NAME` environment variable](docs/config.md#environment-variables) to change the domain to use.
### Standalone Binary
If you prefer not to use Docker, we provide standalone FrankenPHP binaries for Linux and macOS
We provide static FrankenPHP binaries for Linux and macOS
containing [PHP 8.4](https://www.php.net/releases/8.4/en.php) and most popular PHP extensions.
On Windows, use [WSL](https://learn.microsoft.com/windows/wsl/) to run FrankenPHP.
@@ -58,33 +43,68 @@ You can also run command-line scripts with:
frankenphp php-cli /path/to/your/script.php
```
### Docker
Alternatively, [Docker images](https://frankenphp.dev/docs/docker/) are available:
```console
docker run -v .:/app/public \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
Go to `https://localhost`, and enjoy!
> [!TIP]
>
> Do not attempt to use `https://127.0.0.1`. Use `https://localhost` and accept the self-signed certificate.
> Use the [`SERVER_NAME` environment variable](docs/config.md#environment-variables) to change the domain to use.
### Homebrew
FrankenPHP is also available as a [Homebrew](https://brew.sh) package for macOS and Linux.
To install it:
```console
brew install dunglas/frankenphp/frankenphp
```
To serve the content of the current directory, run:
```console
frankenphp php-server
```
## Docs
* [Classic mode](https://frankenphp.dev/docs/classic/)
* [Worker mode](https://frankenphp.dev/docs/worker/)
* [Early Hints support (103 HTTP status code)](https://frankenphp.dev/docs/early-hints/)
* [Real-time](https://frankenphp.dev/docs/mercure/)
* [Configuration](https://frankenphp.dev/docs/config/)
* [Docker images](https://frankenphp.dev/docs/docker/)
* [Deploy in production](https://frankenphp.dev/docs/production/)
* [Performance optimization](https://frankenphp.dev/docs/performance/)
* [Create **standalone**, self-executable PHP apps](https://frankenphp.dev/docs/embed/)
* [Create static binaries](https://frankenphp.dev/docs/static/)
* [Compile from sources](https://frankenphp.dev/docs/compile/)
* [Laravel integration](https://frankenphp.dev/docs/laravel/)
* [Known issues](https://frankenphp.dev/docs/known-issues/)
* [Demo app (Symfony) and benchmarks](https://github.com/dunglas/frankenphp-demo)
* [Go library documentation](https://pkg.go.dev/github.com/dunglas/frankenphp)
* [Contributing and debugging](https://frankenphp.dev/docs/contributing/)
- [Classic mode](https://frankenphp.dev/docs/classic/)
- [Worker mode](https://frankenphp.dev/docs/worker/)
- [Early Hints support (103 HTTP status code)](https://frankenphp.dev/docs/early-hints/)
- [Real-time](https://frankenphp.dev/docs/mercure/)
- [Efficiently Serving Large Static Files](https://frankenphp.dev/docs/x-sendfile/)
- [Configuration](https://frankenphp.dev/docs/config/)
- [Docker images](https://frankenphp.dev/docs/docker/)
- [Deploy in production](https://frankenphp.dev/docs/production/)
- [Performance optimization](https://frankenphp.dev/docs/performance/)
- [Create **standalone**, self-executable PHP apps](https://frankenphp.dev/docs/embed/)
- [Create static binaries](https://frankenphp.dev/docs/static/)
- [Compile from sources](https://frankenphp.dev/docs/compile/)
- [Monitoring FrankenPHP](https://frankenphp.dev/docs/metrics/)
- [Laravel integration](https://frankenphp.dev/docs/laravel/)
- [Known issues](https://frankenphp.dev/docs/known-issues/)
- [Demo app (Symfony) and benchmarks](https://github.com/dunglas/frankenphp-demo)
- [Go library documentation](https://pkg.go.dev/frankenphp.dev)
- [Contributing and debugging](https://frankenphp.dev/docs/contributing/)
## Examples and Skeletons
* [Symfony](https://github.com/dunglas/symfony-docker)
* [API Platform](https://api-platform.com/docs/symfony)
* [Laravel](https://frankenphp.dev/docs/laravel/)
* [Sulu](https://sulu.io/blog/running-sulu-with-frankenphp)
* [WordPress](https://github.com/StephenMiracle/frankenwp)
* [Drupal](https://github.com/dunglas/frankenphp-drupal)
* [Joomla](https://github.com/alexandreelise/frankenphp-joomla)
* [TYPO3](https://github.com/ochorocho/franken-typo3)
* [Magento2](https://github.com/ekino/frankenphp-magento2)
- [Symfony](https://github.com/dunglas/symfony-docker)
- [API Platform](https://api-platform.com/docs/symfony)
- [Laravel](https://frankenphp.dev/docs/laravel/)
- [Sulu](https://sulu.io/blog/running-sulu-with-frankenphp)
- [WordPress](https://github.com/StephenMiracle/frankenwp)
- [Drupal](https://github.com/dunglas/frankenphp-drupal)
- [Joomla](https://github.com/alexandreelise/frankenphp-joomla)
- [TYPO3](https://github.com/ochorocho/franken-typo3)
- [Magento2](https://github.com/ekino/frankenphp-magento2)

View File

@@ -18,17 +18,20 @@ RUN set -eux; \
/app/public \
/config/caddy \
/data/caddy \
/etc/caddy; \
/etc/caddy \
/etc/frankenphp; \
sed -i 's/php/frankenphp run/g' /usr/local/bin/docker-php-entrypoint; \
echo '<?php phpinfo();' > /app/public/index.php
COPY --link caddy/frankenphp/Caddyfile /etc/caddy/Caddyfile
RUN curl -sSLf \
RUN ln /etc/caddy/Caddyfile /etc/frankenphp/Caddyfile && \
curl -sSLf \
-o /usr/local/bin/install-php-extensions \
https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions && \
chmod +x /usr/local/bin/install-php-extensions
CMD ["--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]
CMD ["--config", "/etc/frankenphp/Caddyfile", "--adapter", "caddyfile"]
HEALTHCHECK CMD curl -f http://localhost:2019/metrics || exit 1
# See https://caddyserver.com/docs/conventions#file-locations for details
@@ -57,6 +60,7 @@ SHELL ["/bin/ash", "-eo", "pipefail", "-c"]
COPY --link --from=golang-base /usr/local/go /usr/local/go
ENV PATH=/usr/local/go/bin:$PATH
ENV GOTOOLCHAIN=local
# hadolint ignore=SC2086
RUN apk add --no-cache --virtual .build-deps \
@@ -82,21 +86,6 @@ RUN apk add --no-cache --virtual .build-deps \
sqlite-dev \
upx
WORKDIR /go/src/app
COPY --link go.mod go.sum ./
RUN go mod graph | awk '{if ($1 !~ "@") print $2}' | xargs go get
WORKDIR /go/src/app/caddy
COPY caddy/go.mod caddy/go.sum ./
RUN go mod graph | awk '{if ($1 !~ "@") print $2}' | xargs go get
WORKDIR /go/src/app
COPY --link *.* ./
COPY --link caddy caddy
COPY --link internal internal
COPY --link testdata testdata
# Install e-dant/watcher (necessary for file watching)
WORKDIR /usr/local/src/watcher
RUN curl -s https://api.github.com/repos/e-dant/watcher/releases/latest | \
@@ -110,16 +99,29 @@ RUN curl -s https://api.github.com/repos/e-dant/watcher/releases/latest | \
cmake --build build && \
cmake --install build
WORKDIR /go/src/app
COPY --link go.mod go.sum ./
RUN go mod download
WORKDIR /go/src/app/caddy
COPY caddy/go.mod caddy/go.sum ./
RUN go mod download
WORKDIR /go/src/app
COPY --link . ./
# See https://github.com/docker-library/php/blob/master/8.3/alpine3.20/zts/Dockerfile#L53-L55
ENV CGO_CFLAGS="-DFRANKENPHP_VERSION=$FRANKENPHP_VERSION $PHP_CFLAGS"
ENV CGO_CPPFLAGS=$PHP_CPPFLAGS
ENV CGO_LDFLAGS="-lssl -lcrypto -lreadline -largon2 -lcurl -lonig -lz $PHP_LDFLAGS"
WORKDIR /go/src/app/caddy/frankenphp
RUN GOBIN=/usr/local/bin go install -tags 'nobadger,nomysql,nopgx' -ldflags "-w -s -extldflags '-Wl,-z,stack-size=0x80000' -X 'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP $FRANKENPHP_VERSION PHP $PHP_VERSION Caddy'" && \
RUN GOBIN=/usr/local/bin go install -tags 'nobadger,nomysql,nopgx' -ldflags "-w -s -extldflags '-Wl,-z,stack-size=0x80000' -X 'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP $FRANKENPHP_VERSION PHP $PHP_VERSION Caddy'" -buildvcs=true && \
setcap cap_net_bind_service=+ep /usr/local/bin/frankenphp && \
([ -z "${NO_COMPRESS}" ] && upx --best /usr/local/bin/frankenphp || true) && \
frankenphp version
frankenphp version && \
frankenphp build-info
WORKDIR /go/src/app
@@ -135,4 +137,5 @@ RUN apk add --no-cache libstdc++ && \
COPY --from=builder /usr/local/bin/frankenphp /usr/local/bin/frankenphp
RUN setcap cap_net_bind_service=+ep /usr/local/bin/frankenphp && \
frankenphp version
frankenphp version && \
frankenphp build-info

119
build-packages.sh Executable file
View File

@@ -0,0 +1,119 @@
#!/bin/bash
set -o errexit
set -x
# Ensure required tools are installed
if ! command -v rpmbuild &>/dev/null; then
echo "Error: rpm-build is required to create RPM packages."
echo "Install it with: sudo dnf install rpm-build"
exit 1
fi
if ! command -v ruby &>/dev/null; then
echo "Error: Ruby is required by FPM."
echo "Install it with: sudo dnf install ruby"
exit 1
fi
if ! command -v fpm &>/dev/null; then
echo "Error: FPM (rubygem-fpm) is required to create RPM packages."
echo "Install it with: sudo gem install fpm"
exit 1
fi
arch="$(uname -m)"
os="$(uname -s | tr '[:upper:]' '[:lower:]')"
bin="frankenphp-${os}-${arch}"
if [ ! -f "dist/$bin" ]; then
echo "Error: dist/$bin not found. Run './build-static.sh' first"
exit 1
fi
version_output="$(dist/"$bin" version)"
frankenphp_version=$(echo "$version_output" | grep -oP 'FrankenPHP\s+\K[^ ]+' || true)
frankenphp_version=${frankenphp_version#v}
if [[ ! "${frankenphp_version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Warning: frankenphp_version must be set to X.Y.Z (e.g. 1.5.1), got '${frankenphp_version}'"
echo "Falling back to non-release version 0.0.0"
frankenphp_version=0.0.0
fi
group_preexists=0
user_preexists=0
if getent group frankenphp >/dev/null; then
group_preexists=1
else
sudo groupadd --system frankenphp
fi
if getent passwd frankenphp >/dev/null; then
user_preexists=1
else
sudo useradd --system \
--gid frankenphp \
--create-home \
--home-dir /var/lib/frankenphp \
--shell /usr/sbin/nologin \
--comment "FrankenPHP web server" \
frankenphp
fi
mkdir -p package/empty
mkdir -p package/etc
[ -f ./dist/static-php-cli/source/php-src/php.ini-production ] && cp -f ./dist/static-php-cli/source/php-src/php.ini-production ./package/etc/php.ini
cd dist
iteration=1
glibc_version=$(ldd -v "$bin" | awk '/GLIBC_/ {gsub(/[()]/, "", $2); print $2}' | grep -v GLIBC_PRIVATE | sort -V | tail -n1)
cxxabi_version=$(strings "$bin" | grep -oP 'CXXABI_\d+\.\d+(\.\d+)?' | sort -V | tail -n1)
fpm -s dir -t rpm -n frankenphp -v "${frankenphp_version}" \
--config-files /etc/frankenphp/Caddyfile \
--config-files /etc/frankenphp/php.ini \
--depends "libc.so.6(${glibc_version})(64bit)" \
--depends "libstdc++.so.6(${cxxabi_version})(64bit)" \
--before-install ../package/rhel/preinstall.sh \
--after-install ../package/rhel/postinstall.sh \
--before-remove ../package/rhel/preuninstall.sh \
--after-remove ../package/rhel/postuninstall.sh \
--iteration "${iteration}" \
--rpm-user frankenphp --rpm-group frankenphp \
"${bin}=/usr/bin/frankenphp" \
"../package/rhel/frankenphp.service=/usr/lib/systemd/system/frankenphp.service" \
"../package/Caddyfile=/etc/frankenphp/Caddyfile" \
"../package/content/=/usr/share/frankenphp" \
"../package/etc/php.ini=/etc/frankenphp/php.ini" \
"../package/empty/=/etc/frankenphp/php.d" \
"../package/empty/=/usr/lib/frankenphp/modules" \
"../package/empty/=/var/lib/frankenphp"
glibc_version=$(ldd -v "$bin" | awk '/GLIBC_/ {gsub(/[()]/, "", $2); print $2}' | grep -v GLIBC_PRIVATE | sed 's/GLIBC_//' | sort -V | tail -n1)
cxxabi_version=$(strings "$bin" | grep -oP 'CXXABI_\d+\.\d+(\.\d+)?' | sed 's/CXXABI_//' | sort -V | tail -n1)
fpm -s dir -t deb -n frankenphp -v "${frankenphp_version}" \
--config-files /etc/frankenphp/Caddyfile \
--config-files /etc/frankenphp/php.ini \
--depends "libc6 (>= ${glibc_version})" \
--depends "libstdc++6 (>= ${cxxabi_version})" \
--after-install ../package/debian/postinst.sh \
--before-remove ../package/debian/prerm.sh \
--after-remove ../package/debian/postrm.sh \
--iteration "${iteration}" \
--deb-user frankenphp --deb-group frankenphp \
"${bin}=/usr/bin/frankenphp" \
"../package/debian/frankenphp.service=/usr/lib/systemd/system/frankenphp.service" \
"../package/Caddyfile=/etc/frankenphp/Caddyfile" \
"../package/content/=/usr/share/frankenphp" \
"../package/etc/php.ini=/etc/frankenphp/php.ini" \
"../package/empty/=/etc/frankenphp/php.d" \
"../package/empty/=/usr/lib/frankenphp/modules" \
"../package/empty/=/var/lib/frankenphp"
[ "$user_preexists" -eq 0 ] && sudo userdel frankenphp
[ "$group_preexists" -eq 0 ] && (sudo groupdel frankenphp || true)
cd ..

View File

@@ -10,8 +10,75 @@ fi
arch="$(uname -m)"
os="$(uname -s | tr '[:upper:]' '[:lower:]')"
# FIXME: re-enable PHP errors when SPC will be compatible with PHP 8.4
spcCommand="php -ddisplay_errors=Off ./bin/spc"
# Supported variables:
# - PHP_VERSION: PHP version to build (default: "8.4")
# - PHP_EXTENSIONS: PHP extensions to build (default: ${defaultExtensions} set below)
# - PHP_EXTENSION_LIBS: PHP extension libraries to build (default: ${defaultExtensionLibs} set below)
# - FRANKENPHP_VERSION: FrankenPHP version (default: current Git commit)
# - EMBED: Path to the PHP app to embed (default: none)
# - DEBUG_SYMBOLS: Enable debug symbols if set to 1 (default: none)
# - MIMALLOC: Use mimalloc as the allocator if set to 1 (default: none)
# - XCADDY_ARGS: Additional arguments to pass to xcaddy
# - RELEASE: [maintainer only] Create a GitHub release if set to 1 (default: none)
# - SPC_REL_TYPE: Release type to download (accept "source" and "binary", default: "source")
# - SPC_OPT_BUILD_ARGS: Additional arguments to pass to spc build
# - SPC_OPT_DOWNLOAD_ARGS: Additional arguments to pass to spc download
# - SPC_LIBC: Set to glibc to build with GNU toolchain (default: musl)
# init spc command, if we use spc binary, just use it instead of fetching source
if [ -z "${SPC_REL_TYPE}" ]; then
SPC_REL_TYPE="source"
fi
# init spc libc
if [ -z "${SPC_LIBC}" ]; then
if [ "${os}" = "linux" ]; then
SPC_LIBC="musl"
fi
fi
# init spc build additional args
if [ -z "${SPC_OPT_BUILD_ARGS}" ]; then
SPC_OPT_BUILD_ARGS=""
fi
if [ "${SPC_LIBC}" = "musl" ] && [[ "${SPC_OPT_BUILD_ARGS}" != *"--disable-opcache-jit"* ]]; then
SPC_OPT_BUILD_ARGS="${SPC_OPT_BUILD_ARGS} --disable-opcache-jit"
fi
# init spc download additional args
if [ -z "${SPC_OPT_DOWNLOAD_ARGS}" ]; then
SPC_OPT_DOWNLOAD_ARGS="--ignore-cache-sources=php-src --retry 5"
if [ "${SPC_LIBC}" = "musl" ]; then
SPC_OPT_DOWNLOAD_ARGS="${SPC_OPT_DOWNLOAD_ARGS} --prefer-pre-built"
fi
fi
# if we need debug symbols, disable strip
if [ -n "${DEBUG_SYMBOLS}" ]; then
SPC_OPT_BUILD_ARGS="${SPC_OPT_BUILD_ARGS} --no-strip"
fi
# php version to build
if [ -z "${PHP_VERSION}" ]; then
get_latest_php_version() {
input="$1"
json=$(curl -s "https://www.php.net/releases/index.php?json&version=$input")
latest=$(echo "$json" | jq -r '.version')
if [[ "$latest" == "$input"* ]]; then
echo "$latest"
else
echo "$input"
fi
}
PHP_VERSION="$(get_latest_php_version "8.4")"
export PHP_VERSION
fi
# default extension set
defaultExtensions="apcu,bcmath,bz2,calendar,ctype,curl,dba,dom,exif,fileinfo,filter,ftp,gd,gmp,gettext,iconv,igbinary,imagick,intl,ldap,mbregex,mbstring,mysqli,mysqlnd,opcache,openssl,parallel,pcntl,pdo,pdo_mysql,pdo_pgsql,pdo_sqlite,pgsql,phar,posix,protobuf,readline,redis,session,shmop,simplexml,soap,sockets,sodium,sqlite3,ssh2,sysvmsg,sysvsem,sysvshm,tidy,tokenizer,xlswriter,xml,xmlreader,xmlwriter,zip,zlib,yaml,zstd"
# if [ "${os}" != "linux" ] || [ "${SPC_LIBC}" = "glibc" ]; then
# defaultExtensions="${defaultExtensions},ffi"
# fi
defaultExtensionLibs="bzip2,freetype,libavif,libjpeg,liblz4,libwebp,libzip,nghttp2"
md5binary="md5sum"
if [ "${os}" = "darwin" ]; then
os="mac"
@@ -35,32 +102,6 @@ else
fpie="-fpie"
fi
if [ -z "${PHP_EXTENSIONS}" ]; then
if [ -n "${EMBED}" ] && [ -f "${EMBED}/composer.json" ]; then
cd "${EMBED}"
# read the composer.json file and extract the required PHP extensions
# remove internal extensions from the list: https://github.com/crazywhalecc/static-php-cli/blob/4b16631d45a57370b4747df15c8f105130e96d03/src/globals/defines.php#L26-L34
PHP_EXTENSIONS="$(composer check-platform-reqs --no-dev 2>/dev/null | grep ^ext | sed -e 's/^ext-core//' -e 's/^ext-hash//' -e 's/^ext-json//' -e 's/^ext-pcre//' -e 's/^ext-reflection//' -e 's/^ext-spl//' -e 's/^ext-standard//' -e 's/^ext-//' -e 's/ .*//' | xargs | tr ' ' ',')"
export PHP_EXTENSIONS
cd -
else
export PHP_EXTENSIONS="apcu,bcmath,bz2,calendar,ctype,curl,dba,dom,exif,fileinfo,filter,ftp,gd,gmp,gettext,iconv,igbinary,imagick,intl,ldap,mbregex,mbstring,mysqli,mysqlnd,opcache,openssl,parallel,pcntl,pdo,pdo_mysql,pdo_pgsql,pdo_sqlite,pgsql,phar,posix,protobuf,readline,redis,session,shmop,simplexml,soap,sockets,sodium,sqlite3,ssh2,sysvmsg,sysvsem,sysvshm,tidy,tokenizer,xlswriter,xml,xmlreader,xmlwriter,zip,zlib,yaml,zstd"
fi
fi
if [ -z "${PHP_EXTENSION_LIBS}" ]; then
export PHP_EXTENSION_LIBS="bzip2,freetype,libavif,libjpeg,liblz4,libwebp,libzip,nghttp2"
fi
# The Brotli library must always be built as it is required by http://github.com/dunglas/caddy-cbrotli
if ! echo "${PHP_EXTENSION_LIBS}" | grep -q "\bbrotli\b"; then
export PHP_EXTENSION_LIBS="${PHP_EXTENSION_LIBS},brotli"
fi
if [ -z "${PHP_VERSION}" ]; then
export PHP_VERSION="8.4"
fi
if [ -z "${FRANKENPHP_VERSION}" ]; then
FRANKENPHP_VERSION="$(git rev-parse --verify HEAD)"
export FRANKENPHP_VERSION
@@ -88,55 +129,90 @@ if [ -n "${CLEAN}" ]; then
go clean -cache
fi
cache_key="${PHP_VERSION}-${PHP_EXTENSIONS}-${PHP_EXTENSION_LIBS}"
mkdir -p dist/
cd dist/
if type "brew" >/dev/null 2>&1; then
if ! type "composer" >/dev/null; then
packages="composer"
fi
if ! type "go" >/dev/null 2>&1; then
packages="${packages} go"
fi
if [ -n "${RELEASE}" ] && ! type "gh" >/dev/null 2>&1; then
packages="${packages} gh"
fi
if [ -n "${packages}" ]; then
# shellcheck disable=SC2086
brew install --formula --quiet ${packages}
fi
fi
if [ "${SPC_REL_TYPE}" = "binary" ]; then
mkdir -p static-php-cli/
cd static-php-cli/
if [[ "${arch}" =~ "arm" ]]; then
dl_arch="aarch64"
else
dl_arch="${arch}"
fi
curl -o spc -fsSL "https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-linux-${dl_arch}"
chmod +x spc
spcCommand="./spc"
elif [ -d "static-php-cli/src" ]; then
cd static-php-cli/
git pull
composer install --no-dev -a --no-interaction
spcCommand="./bin/spc"
else
git clone --depth 1 https://github.com/crazywhalecc/static-php-cli --branch main
cd static-php-cli/
composer install --no-dev -a --no-interaction
spcCommand="./bin/spc"
fi
# Extensions to build
if [ -z "${PHP_EXTENSIONS}" ]; then
# enable EMBED mode, first check if project has dumped extensions
if [ -n "${EMBED}" ] && [ -f "${EMBED}/composer.json" ] && [ -f "${EMBED}/composer.lock" ] && [ -f "${EMBED}/vendor/installed.json" ]; then
cd "${EMBED}"
# read the extensions using spc dump-extensions
PHP_EXTENSIONS=$(${spcCommand} dump-extensions "${EMBED}" --format=text --no-dev --no-ext-output="${defaultExtensions}")
else
PHP_EXTENSIONS="${defaultExtensions}"
fi
fi
# Additional libraries to build
if [ -z "${PHP_EXTENSION_LIBS}" ]; then
PHP_EXTENSION_LIBS="${defaultExtensionLibs}"
fi
# The Brotli library must always be built as it is required by http://github.com/dunglas/caddy-cbrotli
if ! echo "${PHP_EXTENSION_LIBS}" | grep -q "\bbrotli\b"; then
PHP_EXTENSION_LIBS="${PHP_EXTENSION_LIBS},brotli"
fi
# The mimalloc library must be built if MIMALLOC is true
if [ -n "${MIMALLOC}" ]; then
if ! echo "${PHP_EXTENSION_LIBS}" | grep -q "\bmimalloc\b"; then
PHP_EXTENSION_LIBS="${PHP_EXTENSION_LIBS},mimalloc"
fi
fi
# Build libphp if necessary
if [ -f dist/cache_key ] && [ "$(cat dist/cache_key)" = "${cache_key}" ] && [ -f "dist/static-php-cli/buildroot/lib/libphp.a" ]; then
cd dist/static-php-cli
cache_key="${PHP_VERSION}-${PHP_EXTENSIONS}-${PHP_EXTENSION_LIBS}"
if [ -f ../cache_key ] && [ "$(cat ../cache_key)" = "${cache_key}" ] && [ -f "buildroot/lib/libphp.a" ]; then
echo "Hit cache, skipping libphp build."
else
mkdir -p dist/
cd dist/
echo -n "${cache_key}" >cache_key
if [ -d "static-php-cli/" ]; then
cd static-php-cli/
git pull
else
git clone --depth 1 https://github.com/crazywhalecc/static-php-cli
cd static-php-cli/
fi
if type "brew" >/dev/null 2>&1; then
if ! type "composer" >/dev/null; then
packages="composer"
fi
if ! type "go" >/dev/null 2>&1; then
packages="${packages} go"
fi
if [ -n "${RELEASE}" ] && ! type "gh" >/dev/null 2>&1; then
packages="${packages} gh"
fi
if [ -n "${packages}" ]; then
# shellcheck disable=SC2086
brew install --formula --quiet ${packages}
fi
fi
composer install --no-dev -a
if [ "${os}" = "linux" ]; then
extraOpts="--disable-opcache-jit"
fi
if [ -n "${DEBUG_SYMBOLS}" ]; then
extraOpts="${extraOpts} --no-strip"
fi
${spcCommand} doctor --auto-fix
${spcCommand} download --with-php="${PHP_VERSION}" --for-extensions="${PHP_EXTENSIONS}" --for-libs="${PHP_EXTENSION_LIBS}" --ignore-cache-sources=php-src --prefer-pre-built
# shellcheck disable=SC2086
${spcCommand} build --debug --enable-zts --build-embed ${extraOpts} "${PHP_EXTENSIONS}" --with-libs="${PHP_EXTENSION_LIBS}"
${spcCommand} download --with-php="${PHP_VERSION}" --for-extensions="${PHP_EXTENSIONS}" --for-libs="${PHP_EXTENSION_LIBS}" ${SPC_OPT_DOWNLOAD_ARGS}
# shellcheck disable=SC2086
${spcCommand} build --enable-zts --build-embed ${SPC_OPT_BUILD_ARGS} "${PHP_EXTENSIONS}" --with-libs="${PHP_EXTENSION_LIBS}"
echo -n "${cache_key}" >../cache_key
fi
if ! type "go" >/dev/null 2>&1; then
@@ -166,7 +242,12 @@ curl -f --retry 5 "${curlGitHubHeaders[@]}" https://api.github.com/repos/e-dant/
xargs curl -fL --retry 5 "${curlGitHubHeaders[@]}" |
tar xz --strip-components 1
cd watcher-c
cc -c -o libwatcher-c.o ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -Wall -Wextra "${fpic}"
if [ -z "${CC}" ]; then
watcherCC=cc
else
watcherCC="${CC}"
fi
${watcherCC} -c -o libwatcher-c.o ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -Wall -Wextra "${fpic}"
ar rcs libwatcher-c.a libwatcher-c.o
cp libwatcher-c.a ../../buildroot/lib/libwatcher-c.a
mkdir -p ../../buildroot/include/wtr
@@ -188,12 +269,19 @@ if [ "${os}" = "mac" ]; then
elif [ "${os}" = "linux" ] && [ -z "${DEBUG_SYMBOLS}" ]; then
CGO_LDFLAGS="-Wl,-O1 -pie"
fi
if [ "${os}" = "linux" ] && [ "${SPC_LIBC}" = "glibc" ]; then
CGO_LDFLAGS="${CGO_LDFLAGS} -Wl,--allow-multiple-definition -Wl,--export-dynamic"
fi
CGO_LDFLAGS="${CGO_LDFLAGS} ${PWD}/buildroot/lib/libbrotlicommon.a ${PWD}/buildroot/lib/libbrotlienc.a ${PWD}/buildroot/lib/libbrotlidec.a ${PWD}/buildroot/lib/libwatcher-c.a $(${spcCommand} spc-config "${PHP_EXTENSIONS}" --with-libs="${PHP_EXTENSION_LIBS}" --libs)"
if [ "${os}" = "linux" ]; then
if echo "${PHP_EXTENSIONS}" | grep -qE "\b(intl|imagick|grpc|v8js|protobuf|mongodb|tbb)\b"; then
CGO_LDFLAGS="${CGO_LDFLAGS} -lstdc++"
fi
if [[ "$CGO_LDFLAGS" == *"${PWD}/buildroot/lib/mimalloc.o"* ]]; then
CGO_LDFLAGS=${CGO_LDFLAGS//${PWD}\/buildroot\/lib\/mimalloc.o/}
CGO_LDFLAGS="${PWD}/buildroot/lib/libmimalloc.a $CGO_LDFLAGS"
fi
if [ "${os}" = "linux" ] && [ "${SPC_LIBC}" = "glibc" ]; then
CGO_LDFLAGS="${CGO_LDFLAGS//-lphp/-Wl,--whole-archive -lphp -Wl,--no-whole-archive}"
# shellcheck disable=SC2046
ar d "${PWD}/buildroot/lib/libphp.a" $(ar t "${PWD}/buildroot/lib/libphp.a" | grep '\.a$')
fi
export CGO_LDFLAGS
@@ -202,82 +290,6 @@ LIBPHP_VERSION="$(./buildroot/bin/php-config --version)"
cd ../
if [ "${os}" = "linux" ]; then
if [ -n "${MIMALLOC}" ]; then
# Replace musl's mallocng by mimalloc
# The default musl allocator is slow, especially when used by multi-threaded apps,
# and triggers weird bugs
# Adapted from https://www.tweag.io/blog/2023-08-10-rust-static-link-with-mimalloc/
echo 'The USE_MIMALLOC environment variable is EXPERIMENTAL.'
echo 'This option can be removed or its behavior modified at any time.'
if [ ! -f "mimalloc/out/libmimalloc.a" ]; then
if [ -d "mimalloc" ]; then
cd mimalloc/
git reset --hard
git clean -xdf
git fetch --tags
else
git clone https://github.com/microsoft/mimalloc.git
cd mimalloc/
fi
git checkout "$(git describe --tags "$(git rev-list --tags --max-count=1 || true)" || true)"
curl -fL --retry 5 https://raw.githubusercontent.com/tweag/rust-alpine-mimalloc/1a756444a5c1484d26af9cd39187752728416ba8/mimalloc.diff -o mimalloc.diff
patch -p1 <mimalloc.diff
mkdir -p out/
cd out/
if [ -n "${DEBUG_SYMBOLS}" ]; then
cmake \
-DCMAKE_BUILD_TYPE=Debug \
-DMI_BUILD_SHARED=OFF \
-DMI_BUILD_OBJECT=OFF \
-DMI_BUILD_TESTS=OFF \
../
else
cmake \
-DCMAKE_BUILD_TYPE=Release \
-DMI_BUILD_SHARED=OFF \
-DMI_BUILD_OBJECT=OFF \
-DMI_BUILD_TESTS=OFF \
../
fi
make -j"$(nproc || true)"
cd ../../
fi
if [ -n "${DEBUG_SYMBOLS}" ]; then
libmimalloc_path=mimalloc/out/libmimalloc-debug.a
else
libmimalloc_path=mimalloc/out/libmimalloc.a
fi
# Patch musl library to use mimalloc
for libc_path in "/usr/local/musl/lib/libc.a" "/usr/local/musl/$(uname -m)-linux-musl/lib/libc.a" "/usr/lib/libc.a"; do
if [ ! -f "${libc_path}" ] || [ -f "${libc_path}.unpatched" ]; then
continue
fi
{
echo "CREATE libc.a"
echo "ADDLIB ${libc_path}"
echo "DELETE aligned_alloc.lo calloc.lo donate.lo free.lo libc_calloc.lo lite_malloc.lo malloc.lo malloc_usable_size.lo memalign.lo posix_memalign.lo realloc.lo reallocarray.lo valloc.lo"
echo "ADDLIB ${libmimalloc_path}"
echo "SAVE"
} | ar -M
mv "${libc_path}" "${libc_path}.unpatched"
mv libc.a "${libc_path}"
done
fi
# Increase the default stack size to prevents issues with code including many files such as Symfony containers
extraExtldflags="-Wl,-z,stack-size=0x80000"
fi
if [ -z "${DEBUG_SYMBOLS}" ]; then
extraLdflags="-w -s"
fi
@@ -299,17 +311,27 @@ if [ -n "${DEBUG_SYMBOLS}" ]; then
XCADDY_DEBUG=1
fi
if [ "${SPC_LIBC}" = "musl" ]; then
muslStackSizeFix="-Wl,-z,stack-size=0x80000"
fi
go env
cd caddy/
if [ -z "${SPC_LIBC}" ] || [ "${SPC_LIBC}" = "musl" ]; then
xcaddyGoBuildFlags="-buildmode=pie -tags cgo,netgo,osusergo,static_build,nobadger,nomysql,nopgx -ldflags \"-linkmode=external -extldflags '-static-pie ${muslStackSizeFix}' ${extraLdflags} -X 'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP ${FRANKENPHP_VERSION} PHP ${LIBPHP_VERSION} Caddy'\""
elif [ "${SPC_LIBC}" = "glibc" ]; then
xcaddyGoBuildFlags="-buildmode=pie -tags cgo,netgo,osusergo,nobadger,nomysql,nopgx -ldflags \"-linkmode=external -extldflags '-pie' ${extraLdflags} -X 'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP ${FRANKENPHP_VERSION} PHP ${LIBPHP_VERSION} Caddy'\""
fi
# shellcheck disable=SC2086
CGO_ENABLED=1 \
XCADDY_GO_BUILD_FLAGS="-buildmode=pie -tags cgo,netgo,osusergo,static_build,nobadger,nomysql,nopgx -ldflags \"-linkmode=external -extldflags '-static-pie ${extraExtldflags}' ${extraLdflags} -X 'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP ${FRANKENPHP_VERSION} PHP ${LIBPHP_VERSION} Caddy'\"" \
XCADDY_GO_BUILD_FLAGS=${xcaddyGoBuildFlags} \
XCADDY_DEBUG="${XCADDY_DEBUG}" \
${XCADDY_COMMAND} build \
--output "../dist/${bin}" \
${XCADDY_ARGS} \
--with github.com/dunglas/frankenphp=.. \
--with github.com/dunglas/frankenphp/caddy=.
--with frankenphp.dev=.. \
--with frankenphp.dev/caddy=.
cd ..
if [ -d "${EMBED}" ]; then
@@ -322,6 +344,7 @@ if type "upx" >/dev/null 2>&1 && [ -z "${DEBUG_SYMBOLS}" ] && [ -z "${NO_COMPRES
fi
"dist/${bin}" version
"dist/${bin}" build-info
if [ -n "${RELEASE}" ]; then
gh release upload "v${FRANKENPHP_VERSION}" "dist/${bin}" --repo dunglas/frankenphp --clobber

65
caddy/admin.go Normal file
View File

@@ -0,0 +1,65 @@
package caddy
import (
"encoding/json"
"fmt"
"github.com/caddyserver/caddy/v2"
"frankenphp.dev"
"net/http"
)
type FrankenPHPAdmin struct{}
// if the id starts with "admin.api" the module will register AdminRoutes via module.Routes()
func (FrankenPHPAdmin) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "admin.api.frankenphp",
New: func() caddy.Module { return new(FrankenPHPAdmin) },
}
}
// EXPERIMENTAL: These routes are not yet stable and may change in the future.
func (admin FrankenPHPAdmin) Routes() []caddy.AdminRoute {
return []caddy.AdminRoute{
{
Pattern: "/frankenphp/workers/restart",
Handler: caddy.AdminHandlerFunc(admin.restartWorkers),
},
{
Pattern: "/frankenphp/threads",
Handler: caddy.AdminHandlerFunc(admin.threads),
},
}
}
func (admin *FrankenPHPAdmin) restartWorkers(w http.ResponseWriter, r *http.Request) error {
if r.Method != http.MethodPost {
return admin.error(http.StatusMethodNotAllowed, fmt.Errorf("method not allowed"))
}
frankenphp.RestartWorkers()
caddy.Log().Info("workers restarted from admin api")
admin.success(w, "workers restarted successfully\n")
return nil
}
func (admin *FrankenPHPAdmin) threads(w http.ResponseWriter, _ *http.Request) error {
debugState := frankenphp.DebugState()
prettyJson, err := json.MarshalIndent(debugState, "", " ")
if err != nil {
return admin.error(http.StatusInternalServerError, err)
}
return admin.success(w, string(prettyJson))
}
func (admin *FrankenPHPAdmin) success(w http.ResponseWriter, message string) error {
w.WriteHeader(http.StatusOK)
_, err := w.Write([]byte(message))
return err
}
func (admin *FrankenPHPAdmin) error(statusCode int, err error) error {
return caddy.APIError{HTTPStatus: statusCode, Err: err}
}

293
caddy/admin_test.go Normal file
View File

@@ -0,0 +1,293 @@
package caddy_test
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"sync"
"testing"
"frankenphp.dev"
"frankenphp.dev/internal/fastabs"
"github.com/caddyserver/caddy/v2/caddytest"
"github.com/stretchr/testify/assert"
)
func TestRestartWorkerViaAdminApi(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`
{
skip_install_trust
admin localhost:2999
http_port `+testPort+`
frankenphp {
worker ../testdata/worker-with-counter.php 1
}
}
localhost:`+testPort+` {
route {
root ../testdata
rewrite worker-with-counter.php
php
}
}
`, "caddyfile")
tester.AssertGetResponse("http://localhost:"+testPort+"/", http.StatusOK, "requests:1")
tester.AssertGetResponse("http://localhost:"+testPort+"/", http.StatusOK, "requests:2")
assertAdminResponse(t, tester, "POST", "workers/restart", http.StatusOK, "workers restarted successfully\n")
tester.AssertGetResponse("http://localhost:"+testPort+"/", http.StatusOK, "requests:1")
}
func TestShowTheCorrectThreadDebugStatus(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`
{
skip_install_trust
admin localhost:2999
http_port `+testPort+`
frankenphp {
num_threads 3
max_threads 6
worker ../testdata/worker-with-counter.php 1
worker ../testdata/index.php 1
}
}
localhost:`+testPort+` {
route {
root ../testdata
rewrite worker-with-counter.php
php
}
}
`, "caddyfile")
debugState := getDebugState(t, tester)
// assert that the correct threads are present in the thread info
assert.Equal(t, debugState.ThreadDebugStates[0].State, "ready")
assert.Contains(t, debugState.ThreadDebugStates[1].Name, "worker-with-counter.php")
assert.Contains(t, debugState.ThreadDebugStates[2].Name, "index.php")
assert.Equal(t, debugState.ReservedThreadCount, 3)
assert.Len(t, debugState.ThreadDebugStates, 3)
}
func TestAutoScaleWorkerThreads(t *testing.T) {
wg := sync.WaitGroup{}
maxTries := 10
requestsPerTry := 200
tester := caddytest.NewTester(t)
tester.InitServer(`
{
skip_install_trust
admin localhost:2999
http_port `+testPort+`
frankenphp {
max_threads 10
num_threads 2
worker ../testdata/sleep.php 1
}
}
localhost:`+testPort+` {
route {
root ../testdata
rewrite sleep.php
php
}
}
`, "caddyfile")
// spam an endpoint that simulates IO
endpoint := "http://localhost:" + testPort + "/?sleep=2&work=1000"
amountOfThreads := len(getDebugState(t, tester).ThreadDebugStates)
// try to spawn the additional threads by spamming the server
for tries := 0; tries < maxTries; tries++ {
wg.Add(requestsPerTry)
for i := 0; i < requestsPerTry; i++ {
go func() {
tester.AssertGetResponse(endpoint, http.StatusOK, "slept for 2 ms and worked for 1000 iterations")
wg.Done()
}()
}
wg.Wait()
amountOfThreads = len(getDebugState(t, tester).ThreadDebugStates)
if amountOfThreads > 2 {
break
}
}
// assert that there are now more threads than before
assert.NotEqual(t, amountOfThreads, 2)
}
// Note this test requires at least 2x40MB available memory for the process
func TestAutoScaleRegularThreadsOnAutomaticThreadLimit(t *testing.T) {
wg := sync.WaitGroup{}
maxTries := 10
requestsPerTry := 200
tester := caddytest.NewTester(t)
tester.InitServer(`
{
skip_install_trust
admin localhost:2999
http_port `+testPort+`
frankenphp {
max_threads auto
num_threads 1
php_ini memory_limit 40M # a reasonable limit for the test
}
}
localhost:`+testPort+` {
route {
root ../testdata
php
}
}
`, "caddyfile")
// spam an endpoint that simulates IO
endpoint := "http://localhost:" + testPort + "/sleep.php?sleep=2&work=1000"
amountOfThreads := len(getDebugState(t, tester).ThreadDebugStates)
// try to spawn the additional threads by spamming the server
for tries := 0; tries < maxTries; tries++ {
wg.Add(requestsPerTry)
for i := 0; i < requestsPerTry; i++ {
go func() {
tester.AssertGetResponse(endpoint, http.StatusOK, "slept for 2 ms and worked for 1000 iterations")
wg.Done()
}()
}
wg.Wait()
amountOfThreads = len(getDebugState(t, tester).ThreadDebugStates)
if amountOfThreads > 1 {
break
}
}
// assert that there are now more threads present
assert.NotEqual(t, amountOfThreads, 1)
}
func assertAdminResponse(t *testing.T, tester *caddytest.Tester, method string, path string, expectedStatus int, expectedBody string) {
adminUrl := "http://localhost:2999/frankenphp/"
r, err := http.NewRequest(method, adminUrl+path, nil)
assert.NoError(t, err)
if expectedBody == "" {
_ = tester.AssertResponseCode(r, expectedStatus)
return
}
_, _ = tester.AssertResponse(r, expectedStatus, expectedBody)
}
func getAdminResponseBody(t *testing.T, tester *caddytest.Tester, method string, path string) string {
adminUrl := "http://localhost:2999/frankenphp/"
r, err := http.NewRequest(method, adminUrl+path, nil)
assert.NoError(t, err)
resp := tester.AssertResponseCode(r, http.StatusOK)
defer resp.Body.Close()
bytes, err := io.ReadAll(resp.Body)
assert.NoError(t, err)
return string(bytes)
}
func getDebugState(t *testing.T, tester *caddytest.Tester) frankenphp.FrankenPHPDebugState {
threadStates := getAdminResponseBody(t, tester, "GET", "threads")
var debugStates frankenphp.FrankenPHPDebugState
err := json.Unmarshal([]byte(threadStates), &debugStates)
assert.NoError(t, err)
return debugStates
}
func TestAddModuleWorkerViaAdminApi(t *testing.T) {
// Initialize a server with admin API enabled
tester := caddytest.NewTester(t)
tester.InitServer(`
{
skip_install_trust
admin localhost:2999
http_port `+testPort+`
}
localhost:`+testPort+` {
route {
root ../testdata
php
}
}
`, "caddyfile")
// Get initial debug state to check number of workers
initialDebugState := getDebugState(t, tester)
initialWorkerCount := 0
for _, thread := range initialDebugState.ThreadDebugStates {
if thread.Name != "" && thread.Name != "ready" {
initialWorkerCount++
}
}
// Create a Caddyfile configuration with a module worker
workerConfig := `
{
skip_install_trust
admin localhost:2999
http_port ` + testPort + `
}
localhost:` + testPort + ` {
route {
root ../testdata
php {
worker ../testdata/worker-with-counter.php 1
}
}
}
`
// Send the configuration to the admin API
adminUrl := "http://localhost:2999/load"
r, err := http.NewRequest("POST", adminUrl, bytes.NewBufferString(workerConfig))
assert.NoError(t, err)
r.Header.Set("Content-Type", "text/caddyfile")
resp := tester.AssertResponseCode(r, http.StatusOK)
defer resp.Body.Close()
// Get the updated debug state to check if the worker was added
updatedDebugState := getDebugState(t, tester)
updatedWorkerCount := 0
workerFound := false
filename, _ := fastabs.FastAbs("../testdata/worker-with-counter.php")
for _, thread := range updatedDebugState.ThreadDebugStates {
if thread.Name != "" && thread.Name != "ready" {
updatedWorkerCount++
if thread.Name == "Worker PHP Thread - "+filename {
workerFound = true
}
}
}
// Assert that the worker was added
assert.Greater(t, updatedWorkerCount, initialWorkerCount, "Worker count should have increased")
assert.True(t, workerFound, fmt.Sprintf("Worker with name %q should be found", "Worker PHP Thread - "+filename))
// Make a request to the worker to verify it's working
tester.AssertGetResponse("http://localhost:"+testPort+"/worker-with-counter.php", http.StatusOK, "requests:1")
}

View File

@@ -7,12 +7,14 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/dunglas/frankenphp/internal/fastabs"
"github.com/prometheus/client_golang/prometheus"
"log/slog"
"net/http"
"path/filepath"
"strconv"
"strings"
"time"
"frankenphp.dev/internal/fastabs"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig"
@@ -21,15 +23,20 @@ import (
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
"github.com/caddyserver/caddy/v2/modules/caddyhttp/fileserver"
"github.com/caddyserver/caddy/v2/modules/caddyhttp/rewrite"
"github.com/dunglas/frankenphp"
"go.uber.org/zap"
"frankenphp.dev"
)
const defaultDocumentRoot = "public"
const (
defaultDocumentRoot = "public"
defaultWatchPattern = "./**/*.{php,yaml,yml,twig,env}"
)
var iniError = errors.New("'php_ini' must be in the format: php_ini \"<key>\" \"<value>\"")
func init() {
caddy.RegisterModule(FrankenPHPApp{})
caddy.RegisterModule(FrankenPHPModule{})
caddy.RegisterModule(FrankenPHPAdmin{})
httpcaddyfile.RegisterGlobalOption("frankenphp", parseGlobalOption)
@@ -40,9 +47,9 @@ func init() {
httpcaddyfile.RegisterDirectiveOrder("php_server", "before", "file_server")
}
var metrics = frankenphp.NewPrometheusMetrics(prometheus.DefaultRegisterer)
type workerConfig struct {
// Name for the worker. Default: the filename for FrankenPHPApp workers, always prefixed with "m#" for FrankenPHPModule workers.
Name string `json:"name,omitempty"`
// FileName sets the path to the worker script.
FileName string `json:"file_name,omitempty"`
// Num sets the number of workers to start.
@@ -56,8 +63,17 @@ type workerConfig struct {
type FrankenPHPApp struct {
// NumThreads sets the number of PHP threads to start. Default: 2x the number of available CPUs.
NumThreads int `json:"num_threads,omitempty"`
// MaxThreads limits how many threads can be started at runtime. Default 2x NumThreads
MaxThreads int `json:"max_threads,omitempty"`
// Workers configures the worker scripts to start.
Workers []workerConfig `json:"workers,omitempty"`
// Overwrites the default php ini configuration
PhpIni map[string]string `json:"php_ini,omitempty"`
// The maximum amount of time a request may be stalled waiting for a thread
MaxWaitTime time.Duration `json:"max_wait_time,omitempty"`
metrics frankenphp.Metrics
logger *slog.Logger
}
// CaddyModule returns the Caddy module information.
@@ -68,13 +84,69 @@ func (f FrankenPHPApp) CaddyModule() caddy.ModuleInfo {
}
}
// Provision sets up the module.
func (f *FrankenPHPApp) Provision(ctx caddy.Context) error {
f.logger = ctx.Slogger()
if httpApp, err := ctx.AppIfConfigured("http"); err == nil {
if httpApp.(*caddyhttp.App).Metrics != nil {
f.metrics = frankenphp.NewPrometheusMetrics(ctx.GetMetricsRegistry())
}
} else {
// if the http module is not configured (this should never happen) then collect the metrics by default
f.metrics = frankenphp.NewPrometheusMetrics(ctx.GetMetricsRegistry())
}
return nil
}
func (f *FrankenPHPApp) generateUniqueModuleWorkerName(filepath string) string {
var i uint
filepath, _ = fastabs.FastAbs(filepath)
name := "m#" + filepath
retry:
for _, wc := range f.Workers {
if wc.Name == name {
name = fmt.Sprintf("m#%s_%d", filepath, i)
i++
goto retry
}
}
return name
}
func (f *FrankenPHPApp) addModuleWorkers(workers ...workerConfig) ([]workerConfig, error) {
for i := range workers {
w := &workers[i]
if frankenphp.EmbeddedAppPath != "" && filepath.IsLocal(w.FileName) {
w.FileName = filepath.Join(frankenphp.EmbeddedAppPath, w.FileName)
}
if w.Name == "" {
w.Name = f.generateUniqueModuleWorkerName(w.FileName)
} else if !strings.HasPrefix(w.Name, "m#") {
w.Name = "m#" + w.Name
}
f.Workers = append(f.Workers, *w)
}
return workers, nil
}
func (f *FrankenPHPApp) Start() error {
repl := caddy.NewReplacer()
logger := caddy.Log()
opts := []frankenphp.Option{frankenphp.WithNumThreads(f.NumThreads), frankenphp.WithLogger(logger), frankenphp.WithMetrics(metrics)}
for _, w := range f.Workers {
opts = append(opts, frankenphp.WithWorkers(repl.ReplaceKnown(w.FileName, ""), w.Num, w.Env, w.Watch))
opts := []frankenphp.Option{
frankenphp.WithNumThreads(f.NumThreads),
frankenphp.WithMaxThreads(f.MaxThreads),
frankenphp.WithLogger(f.logger),
frankenphp.WithMetrics(f.metrics),
frankenphp.WithPhpIni(f.PhpIni),
frankenphp.WithMaxWaitTime(f.MaxWaitTime),
}
for _, w := range append(f.Workers) {
opts = append(opts, frankenphp.WithWorkers(w.Name, repl.ReplaceKnown(w.FileName, ""), w.Num, w.Env, w.Watch))
}
frankenphp.Shutdown()
@@ -86,104 +158,213 @@ func (f *FrankenPHPApp) Start() error {
}
func (f *FrankenPHPApp) Stop() error {
caddy.Log().Info("FrankenPHP stopped 🐘")
f.logger.Info("FrankenPHP stopped 🐘")
// attempt a graceful shutdown if caddy is exiting
// note: Exiting() is currently marked as 'experimental'
// https://github.com/caddyserver/caddy/blob/e76405d55058b0a3e5ba222b44b5ef00516116aa/caddy.go#L810
if caddy.Exiting() {
frankenphp.Shutdown()
frankenphp.DrainWorkers()
}
// reset configuration so it doesn't bleed into later tests
// reset the configuration so it doesn't bleed into later tests
f.Workers = nil
f.NumThreads = 0
f.MaxWaitTime = 0
return nil
}
func parseWorkerConfig(d *caddyfile.Dispenser) (workerConfig, error) {
wc := workerConfig{}
if d.NextArg() {
wc.FileName = d.Val()
}
if d.NextArg() {
if d.Val() == "watch" {
wc.Watch = append(wc.Watch, defaultWatchPattern)
} else {
v, err := strconv.ParseUint(d.Val(), 10, 32)
if err != nil {
return wc, err
}
wc.Num = int(v)
}
}
if d.NextArg() {
return wc, errors.New(`FrankenPHP: too many "worker" arguments: ` + d.Val())
}
for d.NextBlock(1) {
v := d.Val()
switch v {
case "name":
if !d.NextArg() {
return wc, d.ArgErr()
}
wc.Name = d.Val()
case "file":
if !d.NextArg() {
return wc, d.ArgErr()
}
wc.FileName = d.Val()
case "num":
if !d.NextArg() {
return wc, d.ArgErr()
}
v, err := strconv.ParseUint(d.Val(), 10, 32)
if err != nil {
return wc, err
}
wc.Num = int(v)
case "env":
args := d.RemainingArgs()
if len(args) != 2 {
return wc, d.ArgErr()
}
if wc.Env == nil {
wc.Env = make(map[string]string)
}
wc.Env[args[0]] = args[1]
case "watch":
if !d.NextArg() {
// the default if the watch directory is left empty:
wc.Watch = append(wc.Watch, defaultWatchPattern)
} else {
wc.Watch = append(wc.Watch, d.Val())
}
default:
allowedDirectives := "name, file, num, env, watch"
return wc, wrongSubDirectiveError("worker", allowedDirectives, v)
}
}
if wc.FileName == "" {
return wc, errors.New(`the "file" argument must be specified`)
}
if frankenphp.EmbeddedAppPath != "" && filepath.IsLocal(wc.FileName) {
wc.FileName = filepath.Join(frankenphp.EmbeddedAppPath, wc.FileName)
}
return wc, nil
}
// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
func (f *FrankenPHPApp) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() {
for d.NextBlock(0) {
// when adding a new directive, also update the allowedDirectives error message
switch d.Val() {
case "num_threads":
if !d.NextArg() {
return d.ArgErr()
}
v, err := strconv.Atoi(d.Val())
v, err := strconv.ParseUint(d.Val(), 10, 32)
if err != nil {
return err
}
f.NumThreads = v
case "worker":
wc := workerConfig{}
if d.NextArg() {
wc.FileName = d.Val()
f.NumThreads = int(v)
case "max_threads":
if !d.NextArg() {
return d.ArgErr()
}
if d.NextArg() {
v, err := strconv.Atoi(d.Val())
if d.Val() == "auto" {
f.MaxThreads = -1
continue
}
v, err := strconv.ParseUint(d.Val(), 10, 32)
if err != nil {
return err
}
f.MaxThreads = int(v)
case "max_wait_time":
if !d.NextArg() {
return d.ArgErr()
}
v, err := time.ParseDuration(d.Val())
if err != nil {
return errors.New("max_wait_time must be a valid duration (example: 10s)")
}
f.MaxWaitTime = v
case "php_ini":
parseIniLine := func(d *caddyfile.Dispenser) error {
key := d.Val()
if !d.NextArg() {
return iniError
}
if f.PhpIni == nil {
f.PhpIni = make(map[string]string)
}
f.PhpIni[key] = d.Val()
if d.NextArg() {
return iniError
}
return nil
}
isBlock := false
for d.NextBlock(1) {
isBlock = true
err := parseIniLine(d)
if err != nil {
return err
}
wc.Num = v
}
for d.NextBlock(1) {
v := d.Val()
switch v {
case "file":
if !d.NextArg() {
return d.ArgErr()
}
wc.FileName = d.Val()
case "num":
if !d.NextArg() {
return d.ArgErr()
}
v, err := strconv.Atoi(d.Val())
if err != nil {
return err
}
wc.Num = v
case "env":
args := d.RemainingArgs()
if len(args) != 2 {
return d.ArgErr()
}
if wc.Env == nil {
wc.Env = make(map[string]string)
}
wc.Env[args[0]] = args[1]
case "watch":
if !d.NextArg() {
// the default if the watch directory is left empty:
wc.Watch = append(wc.Watch, "./**/*.{php,yaml,yml,twig,env}")
} else {
wc.Watch = append(wc.Watch, d.Val())
}
if !isBlock {
if !d.NextArg() {
return iniError
}
if wc.FileName == "" {
return errors.New(`the "file" argument must be specified`)
err := parseIniLine(d)
if err != nil {
return err
}
}
if frankenphp.EmbeddedAppPath != "" && filepath.IsLocal(wc.FileName) {
wc.FileName = filepath.Join(frankenphp.EmbeddedAppPath, wc.FileName)
case "worker":
wc, err := parseWorkerConfig(d)
if err != nil {
return err
}
if frankenphp.EmbeddedAppPath != "" && filepath.IsLocal(wc.FileName) {
wc.FileName = filepath.Join(frankenphp.EmbeddedAppPath, wc.FileName)
}
if strings.HasPrefix(wc.Name, "m#") {
return fmt.Errorf(`global worker names must not start with "m#": %q`, wc.Name)
}
// check for duplicate workers
for _, existingWorker := range f.Workers {
if existingWorker.FileName == wc.FileName {
return fmt.Errorf("global workers must not have duplicate filenames: %q", wc.FileName)
}
}
f.Workers = append(f.Workers, wc)
default:
allowedDirectives := "num_threads, max_threads, php_ini, worker, max_wait_time"
return wrongSubDirectiveError("frankenphp", allowedDirectives, d.Val())
}
}
}
if f.MaxThreads > 0 && f.NumThreads > 0 && f.MaxThreads < f.NumThreads {
return errors.New(`"max_threads"" must be greater than or equal to "num_threads"`)
}
return nil
}
@@ -209,11 +390,13 @@ type FrankenPHPModule struct {
ResolveRootSymlink *bool `json:"resolve_root_symlink,omitempty"`
// Env sets an extra environment variable to the given value. Can be specified more than once for multiple environment variables.
Env map[string]string `json:"env,omitempty"`
// Workers configures the worker scripts to start.
Workers []workerConfig `json:"workers,omitempty"`
resolvedDocumentRoot string
preparedEnv frankenphp.PreparedEnv
preparedEnvNeedsReplacement bool
logger *zap.Logger
logger *slog.Logger
}
// CaddyModule returns the Caddy module information.
@@ -226,7 +409,24 @@ func (FrankenPHPModule) CaddyModule() caddy.ModuleInfo {
// Provision sets up the module.
func (f *FrankenPHPModule) Provision(ctx caddy.Context) error {
f.logger = ctx.Logger(f)
f.logger = ctx.Slogger()
app, err := ctx.App("frankenphp")
if err != nil {
return err
}
fapp, ok := app.(*FrankenPHPApp)
if !ok {
return fmt.Errorf(`expected ctx.App("frankenphp") to return *FrankenPHPApp, got %T`, app)
}
if fapp == nil {
return fmt.Errorf(`expected ctx.App("frankenphp") to return *FrankenPHPApp, got nil`)
}
workers, err := fapp.addModuleWorkers(f.Workers...)
if err != nil {
return err
}
f.Workers = workers
if f.Root == "" {
if frankenphp.EmbeddedAppPath == "" {
@@ -285,20 +485,26 @@ func (f *FrankenPHPModule) Provision(ctx caddy.Context) error {
// needReplacement checks if a string contains placeholders.
func needReplacement(s string) bool {
return strings.Contains(s, "{") || strings.Contains(s, "}")
return strings.ContainsAny(s, "{}")
}
// ServeHTTP implements caddyhttp.MiddlewareHandler.
// TODO: Expose TLS versions as env vars, as Apache's mod_ssl: https://github.com/caddyserver/caddy/blob/master/modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go#L298
func (f FrankenPHPModule) ServeHTTP(w http.ResponseWriter, r *http.Request, _ caddyhttp.Handler) error {
func (f *FrankenPHPModule) ServeHTTP(w http.ResponseWriter, r *http.Request, _ caddyhttp.Handler) error {
origReq := r.Context().Value(caddyhttp.OriginalRequestCtxKey).(http.Request)
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
var documentRootOption frankenphp.RequestOption
var documentRoot string
if f.resolvedDocumentRoot == "" {
documentRootOption = frankenphp.WithRequestDocumentRoot(repl.ReplaceKnown(f.Root, ""), *f.ResolveRootSymlink)
documentRoot = repl.ReplaceKnown(f.Root, "")
if documentRoot == "" && frankenphp.EmbeddedAppPath != "" {
documentRoot = frankenphp.EmbeddedAppPath
}
documentRootOption = frankenphp.WithRequestDocumentRoot(documentRoot, *f.ResolveRootSymlink)
} else {
documentRootOption = frankenphp.WithRequestResolvedDocumentRoot(f.resolvedDocumentRoot)
documentRoot = f.resolvedDocumentRoot
documentRootOption = frankenphp.WithRequestResolvedDocumentRoot(documentRoot)
}
env := f.preparedEnv
@@ -309,18 +515,24 @@ func (f FrankenPHPModule) ServeHTTP(w http.ResponseWriter, r *http.Request, _ ca
}
}
fullScriptPath, _ := fastabs.FastAbs(documentRoot + "/" + r.URL.Path)
workerName := ""
for _, w := range f.Workers {
if p, _ := fastabs.FastAbs(w.FileName); p == fullScriptPath {
workerName = w.Name
}
}
fr, err := frankenphp.NewRequestWithContext(
r,
documentRootOption,
frankenphp.WithRequestSplitPath(f.SplitPath),
frankenphp.WithRequestPreparedEnv(env),
frankenphp.WithOriginalRequest(&origReq),
frankenphp.WithWorkerName(workerName),
)
if err != nil {
return caddyhttp.Error(http.StatusInternalServerError, err)
}
if err = frankenphp.ServeHTTP(w, fr); err != nil {
return caddyhttp.Error(http.StatusInternalServerError, err)
}
@@ -330,6 +542,7 @@ func (f FrankenPHPModule) ServeHTTP(w http.ResponseWriter, r *http.Request, _ ca
// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
func (f *FrankenPHPModule) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
// First pass: Parse all directives except "worker"
for d.Next() {
for d.NextBlock(0) {
switch d.Val() {
@@ -361,7 +574,6 @@ func (f *FrankenPHPModule) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
if !d.NextArg() {
continue
}
v, err := strconv.ParseBool(d.Val())
if err != nil {
return err
@@ -369,8 +581,58 @@ func (f *FrankenPHPModule) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
if d.NextArg() {
return d.ArgErr()
}
f.ResolveRootSymlink = &v
case "worker":
for d.NextBlock(1) {
}
for d.NextArg() {
}
// Skip "worker" blocks in the first pass
continue
default:
allowedDirectives := "root, split, env, resolve_root_symlink, worker"
return wrongSubDirectiveError("php or php_server", allowedDirectives, d.Val())
}
}
}
// Second pass: Parse only "worker" blocks
d.Reset()
for d.Next() {
for d.NextBlock(0) {
if d.Val() == "worker" {
wc, err := parseWorkerConfig(d)
if err != nil {
return err
}
// Inherit environment variables from the parent php_server directive
if !filepath.IsAbs(wc.FileName) && f.Root != "" {
wc.FileName = filepath.Join(f.Root, wc.FileName)
}
if f.Env != nil {
if wc.Env == nil {
wc.Env = make(map[string]string)
}
for k, v := range f.Env {
// Only set if not already defined in the worker
if _, exists := wc.Env[k]; !exists {
wc.Env[k] = v
}
}
}
// Check if a worker with this filename already exists in this module
for _, existingWorker := range f.Workers {
if existingWorker.FileName == wc.FileName {
return fmt.Errorf(`workers in a single "php_server" block must not have duplicate filenames: %q`, wc.FileName)
}
}
f.Workers = append(f.Workers, wc)
}
}
}
@@ -380,7 +642,7 @@ func (f *FrankenPHPModule) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
// parseCaddyfile unmarshals tokens from h into a new Middleware.
func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
m := FrankenPHPModule{}
m := &FrankenPHPModule{}
err := m.UnmarshalCaddyfile(h.Dispenser)
return m, err
@@ -617,6 +879,7 @@ func parsePhpServer(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error)
// using the php directive syntax
dispenser.Next() // consume the directive name
err = phpsrv.UnmarshalCaddyfile(dispenser)
if err != nil {
return nil, err
}
@@ -668,9 +931,15 @@ func parsePhpServer(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error)
}, nil
}
// return a nice error message
func wrongSubDirectiveError(module string, allowedDriectives string, wrongValue string) error {
return fmt.Errorf("unknown '%s' subdirective: '%s' (allowed directives are: %s)", module, wrongValue, allowedDriectives)
}
// Interface guards
var (
_ caddy.App = (*FrankenPHPApp)(nil)
_ caddy.Provisioner = (*FrankenPHPApp)(nil)
_ caddy.Provisioner = (*FrankenPHPModule)(nil)
_ caddyhttp.MiddlewareHandler = (*FrankenPHPModule)(nil)
_ caddyfile.Unmarshaler = (*FrankenPHPModule)(nil)

File diff suppressed because it is too large Load Diff

222
caddy/config_test.go Normal file
View File

@@ -0,0 +1,222 @@
package caddy
import (
"testing"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/stretchr/testify/require"
)
func TestModuleWorkerDuplicateFilenamesFail(t *testing.T) {
// Create a test configuration with duplicate worker filenames
configWithDuplicateFilenames := `
{
php {
worker {
file worker-with-env.php
num 1
}
worker {
file worker-with-env.php
num 2
}
}
}`
// Parse the configuration
d := caddyfile.NewTestDispenser(configWithDuplicateFilenames)
module := &FrankenPHPModule{}
// Unmarshal the configuration
err := module.UnmarshalCaddyfile(d)
// Verify that an error was returned
require.Error(t, err, "Expected an error when two workers in the same module have the same filename")
require.Contains(t, err.Error(), "must not have duplicate filenames", "Error message should mention duplicate filenames")
}
func TestModuleWorkersWithDifferentFilenames(t *testing.T) {
// Create a test configuration with different worker filenames
configWithDifferentFilenames := `
{
php {
worker ../testdata/worker-with-env.php
worker ../testdata/worker-with-counter.php
}
}`
// Parse the configuration
d := caddyfile.NewTestDispenser(configWithDifferentFilenames)
module := &FrankenPHPModule{}
// Unmarshal the configuration
err := module.UnmarshalCaddyfile(d)
// Verify that no error was returned
require.NoError(t, err, "Expected no error when two workers in the same module have different filenames")
// Verify that both workers were added to the module
require.Len(t, module.Workers, 2, "Expected two workers to be added to the module")
require.Equal(t, "../testdata/worker-with-env.php", module.Workers[0].FileName, "First worker should have the correct filename")
require.Equal(t, "../testdata/worker-with-counter.php", module.Workers[1].FileName, "Second worker should have the correct filename")
}
func TestModuleWorkersDifferentNamesSucceed(t *testing.T) {
// Create a test configuration with a worker name
configWithWorkerName1 := `
{
php_server {
worker {
name test-worker-1
file ../testdata/worker-with-env.php
num 1
}
}
}`
// Parse the first configuration
d1 := caddyfile.NewTestDispenser(configWithWorkerName1)
app := &FrankenPHPApp{}
module1 := &FrankenPHPModule{}
// Unmarshal the first configuration
err := module1.UnmarshalCaddyfile(d1)
require.NoError(t, err, "First module should be configured without errors")
// Create a second test configuration with a different worker name
configWithWorkerName2 := `
{
php_server {
worker {
name test-worker-2
file ../testdata/worker-with-env.php
num 1
}
}
}`
// Parse the second configuration
d2 := caddyfile.NewTestDispenser(configWithWorkerName2)
module2 := &FrankenPHPModule{}
// Unmarshal the second configuration
err = module2.UnmarshalCaddyfile(d2)
// Verify that no error was returned
require.NoError(t, err, "Expected no error when two workers have different names")
_, err = app.addModuleWorkers(module1.Workers...)
require.NoError(t, err, "Expected no error when adding the first module workers")
_, err = app.addModuleWorkers(module2.Workers...)
require.NoError(t, err, "Expected no error when adding the second module workers")
// Verify that both workers were added
require.Len(t, app.Workers, 2, "Expected two workers in the app")
require.Equal(t, "m#test-worker-1", app.Workers[0].Name, "First worker should have the correct name")
require.Equal(t, "m#test-worker-2", app.Workers[1].Name, "Second worker should have the correct name")
}
func TestModuleWorkerWithEnvironmentVariables(t *testing.T) {
// Create a test configuration with environment variables
configWithEnv := `
{
php {
worker {
file ../testdata/worker-with-env.php
num 1
env APP_ENV production
env DEBUG true
}
}
}`
// Parse the configuration
d := caddyfile.NewTestDispenser(configWithEnv)
module := &FrankenPHPModule{}
// Unmarshal the configuration
err := module.UnmarshalCaddyfile(d)
// Verify that no error was returned
require.NoError(t, err, "Expected no error when configuring a worker with environment variables")
// Verify that the worker was added to the module
require.Len(t, module.Workers, 1, "Expected one worker to be added to the module")
require.Equal(t, "../testdata/worker-with-env.php", module.Workers[0].FileName, "Worker should have the correct filename")
// Verify that the environment variables were set correctly
require.Len(t, module.Workers[0].Env, 2, "Expected two environment variables")
require.Equal(t, "production", module.Workers[0].Env["APP_ENV"], "APP_ENV should be set to production")
require.Equal(t, "true", module.Workers[0].Env["DEBUG"], "DEBUG should be set to true")
}
func TestModuleWorkerWithWatchConfiguration(t *testing.T) {
// Create a test configuration with watch directories
configWithWatch := `
{
php {
worker {
file ../testdata/worker-with-env.php
num 1
watch
watch ./src/**/*.php
watch ./config/**/*.yaml
}
}
}`
// Parse the configuration
d := caddyfile.NewTestDispenser(configWithWatch)
module := &FrankenPHPModule{}
// Unmarshal the configuration
err := module.UnmarshalCaddyfile(d)
// Verify that no error was returned
require.NoError(t, err, "Expected no error when configuring a worker with watch directories")
// Verify that the worker was added to the module
require.Len(t, module.Workers, 1, "Expected one worker to be added to the module")
require.Equal(t, "../testdata/worker-with-env.php", module.Workers[0].FileName, "Worker should have the correct filename")
// Verify that the watch directories were set correctly
require.Len(t, module.Workers[0].Watch, 3, "Expected three watch patterns")
require.Equal(t, "./**/*.{php,yaml,yml,twig,env}", module.Workers[0].Watch[0], "First watch pattern should be the default")
require.Equal(t, "./src/**/*.php", module.Workers[0].Watch[1], "Second watch pattern should match the configuration")
require.Equal(t, "./config/**/*.yaml", module.Workers[0].Watch[2], "Third watch pattern should match the configuration")
}
func TestModuleWorkerWithCustomName(t *testing.T) {
// Create a test configuration with a custom worker name
configWithCustomName := `
{
php {
worker {
file ../testdata/worker-with-env.php
num 1
name custom-worker-name
}
}
}`
// Parse the configuration
d := caddyfile.NewTestDispenser(configWithCustomName)
module := &FrankenPHPModule{}
app := &FrankenPHPApp{}
// Unmarshal the configuration
err := module.UnmarshalCaddyfile(d)
// Verify that no error was returned
require.NoError(t, err, "Expected no error when configuring a worker with a custom name")
// Verify that the worker was added to the module
require.Len(t, module.Workers, 1, "Expected one worker to be added to the module")
require.Equal(t, "../testdata/worker-with-env.php", module.Workers[0].FileName, "Worker should have the correct filename")
// Verify that the worker was added to app.Workers with the m# prefix
module.Workers, err = app.addModuleWorkers(module.Workers...)
require.NoError(t, err, "Expected no error when adding the worker to the app")
require.Equal(t, "m#custom-worker-name", module.Workers[0].Name, "Worker should have the custom name, prefixed with m#")
require.Equal(t, "m#custom-worker-name", app.Workers[0].Name, "Worker should have the custom name, prefixed with m#")
}

View File

@@ -1,8 +1,14 @@
# The Caddyfile is an easy way to configure FrankenPHP and the Caddy web server.
#
# https://frankenphp.dev/docs/config
# https://caddyserver.com/docs/caddyfile
{
skip_install_trust
{$CADDY_GLOBAL_OPTIONS}
frankenphp {
{$FRANKENPHP_CONFIG}
#worker /path/to/your/worker.php
}
}
@@ -19,7 +25,7 @@
#}
root public/
root * public/
encode zstd br gzip
# Uncomment the following lines to enable Mercure and Vulcain modules
#mercure {
@@ -41,5 +47,13 @@
{$CADDY_SERVER_EXTRA_DIRECTIVES}
php_server {
php_server
#worker /path/to/your/worker.php
}
}
# As an alternative to editing the above site block, you can add your own site
# block files in the Caddyfile.d directory, and they will be included as long
# as they use the .caddyfile extension.
import Caddyfile.d/*.caddyfile
import Caddyfile.d/*.caddyfile

View File

@@ -6,7 +6,7 @@ import (
// plug in Caddy modules here.
_ "github.com/caddyserver/caddy/v2/modules/standard"
_ "github.com/dunglas/caddy-cbrotli"
_ "github.com/dunglas/frankenphp/caddy"
_ "frankenphp.dev/caddy"
_ "github.com/dunglas/mercure/caddy"
_ "github.com/dunglas/vulcain/caddy"
)

View File

@@ -1,153 +1,155 @@
module github.com/dunglas/frankenphp/caddy
module frankenphp.dev/caddy
go 1.22.7
go 1.24.0
replace github.com/dunglas/frankenphp => ../
replace frankenphp.dev => ../
retract v1.0.0-rc.1 // Human error
require (
github.com/caddyserver/caddy/v2 v2.9.1
github.com/caddyserver/certmagic v0.21.7
github.com/caddyserver/caddy/v2 v2.10.0
github.com/caddyserver/certmagic v0.23.0
github.com/dunglas/caddy-cbrotli v1.0.0
github.com/dunglas/frankenphp v1.4.2
github.com/dunglas/mercure/caddy v0.18.2
github.com/dunglas/vulcain/caddy v1.1.1
github.com/prometheus/client_golang v1.20.5
github.com/spf13/cobra v1.8.1
frankenphp.dev v1.7.0
github.com/dunglas/mercure/caddy v0.19.2
github.com/dunglas/vulcain/caddy v1.2.0
github.com/prometheus/client_golang v1.22.0
github.com/spf13/cobra v1.9.1
github.com/stretchr/testify v1.10.0
go.uber.org/zap v1.27.0
)
require github.com/smallstep/go-attestation v0.4.4-0.20240109183208-413678f90935 // indirect
require github.com/smallstep/go-attestation v0.4.4-0.20241119153605-2306d5b464ca // indirect
require (
cel.dev/expr v0.19.2 // indirect
dario.cat/mergo v1.0.1 // indirect
cel.dev/expr v0.24.0 // indirect
dario.cat/mergo v1.0.2 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect
github.com/BurntSushi/toml v1.4.0 // indirect
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/KimMachineGun/automemlimit v0.7.2 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.3.1 // indirect
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
github.com/MauriceGit/skiplist v0.0.0-20211105230623-77f5c8d3e145 // indirect
github.com/MicahParks/jwkset v0.8.0 // indirect
github.com/MicahParks/keyfunc/v3 v3.3.10 // indirect
github.com/MicahParks/jwkset v0.9.6 // indirect
github.com/MicahParks/keyfunc/v3 v3.4.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/RoaringBitmap/roaring v1.9.4 // indirect
github.com/alecthomas/chroma/v2 v2.15.0 // indirect
github.com/alecthomas/chroma/v2 v2.18.0 // indirect
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.20.0 // indirect
github.com/bits-and-blooms/bitset v1.22.0 // indirect
github.com/caddyserver/zerossl v0.1.3 // indirect
github.com/ccoveille/go-safecast v1.6.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chzyer/readline v1.5.1 // indirect
github.com/coreos/go-oidc/v3 v3.12.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/coreos/go-oidc/v3 v3.14.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dgraph-io/badger v1.6.2 // indirect
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
github.com/dgraph-io/ristretto v0.2.0 // indirect
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da // indirect
github.com/dlclark/regexp2 v1.11.4 // indirect
github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/dolthub/maphash v0.1.0 // indirect
github.com/dunglas/httpsfv v1.0.2 // indirect
github.com/dunglas/mercure v0.18.2 // indirect
github.com/dunglas/vulcain v1.1.1 // indirect
github.com/dunglas/httpsfv v1.1.0 // indirect
github.com/dunglas/mercure v0.19.2 // indirect
github.com/dunglas/vulcain v1.2.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/francoispqt/gojay v1.2.13 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
github.com/gammazero/deque v1.0.0 // indirect
github.com/getkin/kin-openapi v0.128.0 // indirect
github.com/go-chi/chi/v5 v5.2.0 // indirect
github.com/go-jose/go-jose/v3 v3.0.3 // indirect
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
github.com/getkin/kin-openapi v0.132.0 // indirect
github.com/go-chi/chi/v5 v5.2.1 // indirect
github.com/go-jose/go-jose/v3 v3.0.4 // indirect
github.com/go-jose/go-jose/v4 v4.1.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/go-openapi/jsonpointer v0.21.1 // indirect
github.com/go-openapi/swag v0.23.1 // indirect
github.com/go-sql-driver/mysql v1.9.2 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/gofrs/uuid v4.4.0+incompatible // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/golang/snappy v1.0.0 // indirect
github.com/google/brotli/go/cbrotli v0.0.0-20241111155135-4303850b01d6 // indirect
github.com/google/cel-go v0.23.0 // indirect
github.com/google/certificate-transparency-go v1.3.0 // indirect
github.com/google/go-tpm v0.9.3 // indirect
github.com/google/cel-go v0.25.0 // indirect
github.com/google/certificate-transparency-go v1.3.1 // indirect
github.com/google/go-tpm v0.9.5 // indirect
github.com/google/go-tspi v0.3.0 // indirect
github.com/google/pprof v0.0.0-20250125003558-7fdb3d7e6fa0 // indirect
github.com/google/pprof v0.0.0-20250602020802-c6617b811d0e // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/handlers v1.5.2 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
github.com/hashicorp/golang-lru v1.0.2 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/invopop/yaml v0.3.1 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.7.2 // indirect
github.com/jackc/pgx/v5 v5.7.5 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/kevburnsjr/skipfilter v0.0.1 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/libdns/libdns v0.2.2 // indirect
github.com/magiconair/properties v1.8.9 // indirect
github.com/libdns/libdns v1.1.0 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/manifoldco/promptui v0.9.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/maypok86/otter v1.2.4 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/mholt/acmez/v3 v3.0.1 // indirect
github.com/miekg/dns v1.1.63 // indirect
github.com/mholt/acmez/v3 v3.1.2 // indirect
github.com/miekg/dns v1.1.66 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-ps v1.0.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/mschoch/smat v0.2.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/onsi/ginkgo/v2 v2.22.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
github.com/onsi/ginkgo/v2 v2.23.4 // indirect
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
github.com/pires/go-proxyproto v0.8.0 // indirect
github.com/pires/go-proxyproto v0.8.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.64.0 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.49.0 // indirect
github.com/quic-go/quic-go v0.52.0 // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.7.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sagikazarmark/locafero v0.9.0 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/slackhq/nebula v1.9.4 // indirect
github.com/smallstep/certificates v0.28.1 // indirect
github.com/smallstep/cli-utils v0.10.0 // indirect
github.com/slackhq/nebula v1.9.5 // indirect
github.com/smallstep/certificates v0.28.3 // indirect
github.com/smallstep/cli-utils v0.12.1 // indirect
github.com/smallstep/linkedca v0.23.0 // indirect
github.com/smallstep/nosql v0.7.0 // indirect
github.com/smallstep/pkcs7 v0.1.1 // indirect
github.com/smallstep/scep v0.0.0-20241223071629-a37a330173bc // indirect
github.com/smallstep/pkcs7 v0.2.1 // indirect
github.com/smallstep/scep v0.0.0-20250318231241-a25cabb69492 // indirect
github.com/smallstep/truststore v0.13.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.12.0 // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.19.0 // indirect
github.com/spf13/afero v1.14.0 // indirect
github.com/spf13/cast v1.9.2 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/spf13/viper v1.20.1 // indirect
github.com/stoewer/go-strcase v1.3.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/tailscale/tscert v0.0.0-20240608151842-d3f834017e53 // indirect
@@ -159,47 +161,47 @@ require (
github.com/urfave/cli v1.22.16 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
github.com/yuin/goldmark v1.7.8 // indirect
github.com/yuin/goldmark v1.7.12 // indirect
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc // indirect
github.com/zeebo/blake3 v0.2.4 // indirect
go.etcd.io/bbolt v1.3.11 // indirect
go.etcd.io/bbolt v1.4.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect
go.opentelemetry.io/contrib/propagators/autoprop v0.58.0 // indirect
go.opentelemetry.io/contrib/propagators/aws v1.33.0 // indirect
go.opentelemetry.io/contrib/propagators/b3 v1.33.0 // indirect
go.opentelemetry.io/contrib/propagators/jaeger v1.33.0 // indirect
go.opentelemetry.io/contrib/propagators/ot v1.33.0 // indirect
go.opentelemetry.io/otel v1.33.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 // indirect
go.opentelemetry.io/otel/metric v1.33.0 // indirect
go.opentelemetry.io/otel/sdk v1.33.0 // indirect
go.opentelemetry.io/otel/trace v1.33.0 // indirect
go.opentelemetry.io/proto/otlp v1.4.0 // indirect
go.step.sm/crypto v0.57.0 // indirect
go.step.sm/linkedca v0.22.2 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
go.opentelemetry.io/contrib/propagators/autoprop v0.60.0 // indirect
go.opentelemetry.io/contrib/propagators/aws v1.35.0 // indirect
go.opentelemetry.io/contrib/propagators/b3 v1.35.0 // indirect
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 // indirect
go.opentelemetry.io/contrib/propagators/ot v1.35.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect
go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/sdk v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect
go.opentelemetry.io/proto/otlp v1.6.0 // indirect
go.step.sm/crypto v0.66.0 // indirect
go.uber.org/automaxprocs v1.6.0 // indirect
go.uber.org/mock v0.5.0 // indirect
go.uber.org/mock v0.5.2 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
go.uber.org/zap/exp v0.3.0 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/crypto/x509roots/fallback v0.0.0-20250118192723-a8ea4be81f07 // indirect
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/oauth2 v0.25.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/term v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.9.0 // indirect
golang.org/x/tools v0.29.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250127172529-29210b9bc287 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250127172529-29210b9bc287 // indirect
google.golang.org/grpc v1.70.0 // indirect
google.golang.org/protobuf v1.36.4 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
golang.org/x/crypto v0.38.0 // indirect
golang.org/x/crypto/x509roots/fallback v0.0.0-20250531095911-4f9f0ca9fcfb // indirect
golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b // indirect
golang.org/x/mod v0.24.0 // indirect
golang.org/x/net v0.40.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
golang.org/x/sync v0.14.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/term v0.32.0 // indirect
golang.org/x/text v0.25.0 // indirect
golang.org/x/time v0.11.0 // indirect
golang.org/x/tools v0.33.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect
google.golang.org/grpc v1.72.2 // indirect
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
howett.net/plist v1.0.1 // indirect

View File

@@ -1,26 +1,25 @@
cel.dev/expr v0.19.2 h1:V354PbqIXr9IQdwy4SYA4xa0HXaWq1BUPAGzugBY5V4=
cel.dev/expr v0.19.2/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=
cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE=
cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U=
cloud.google.com/go/auth v0.13.0 h1:8Fu8TZy167JkW8Tj3q7dIkr2v4cndv41ouecJx0PAHs=
cloud.google.com/go/auth v0.13.0/go.mod h1:COOjD9gwfKNKz+IIduatIhYJQIc0mG3H102r/EMxX6Q=
cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU=
cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8=
cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg=
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
cloud.google.com/go/iam v1.2.2 h1:ozUSofHUGf/F4tCNy/mu9tHLTaxZFLOUiKzjcgWHGIA=
cloud.google.com/go/iam v1.2.2/go.mod h1:0Ys8ccaZHdI1dEUilwzqng/6ps2YB6vRsjIe00/+6JY=
cloud.google.com/go/kms v1.20.4 h1:CJ0hMpOg1ANN9tx/a/GPJ+Uxudy8k6f3fvGFuTHiE5A=
cloud.google.com/go/kms v1.20.4/go.mod h1:gPLsp1r4FblUgBYPOcvI/bUPpdMg2Jm1ZVKU4tQUfcc=
cloud.google.com/go/longrunning v0.6.2 h1:xjDfh1pQcWPEvnfjZmwjKQEcHnpz6lHjfy7Fo0MK+hc=
cloud.google.com/go/longrunning v0.6.2/go.mod h1:k/vIs83RN4bE3YCswdXC5PFfWVILjm3hpEUlSko4PiI=
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA=
cloud.google.com/go v0.120.0/go.mod h1:/beW32s8/pGRuj4IILWQNd4uuebeT4dkOhKmkfit64Q=
cloud.google.com/go/auth v0.16.1 h1:XrXauHMd30LhQYVRHLGvJiYeczweKQXZxsTbV9TiguU=
cloud.google.com/go/auth v0.16.1/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI=
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
cloud.google.com/go/compute/metadata v0.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU=
cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo=
cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8=
cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE=
cloud.google.com/go/kms v1.22.0 h1:dBRIj7+GDeeEvatJeTB19oYZNV0aj6wEqSIT/7gLqtk=
cloud.google.com/go/kms v1.22.0/go.mod h1:U7mf8Sva5jpOb4bxYZdtw/9zsbIjrklYwPcvMk34AL8=
cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE=
cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY=
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
@@ -31,8 +30,11 @@ git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGy
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M=
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/KimMachineGun/automemlimit v0.7.2 h1:DyfHI7zLWmZPn2Wqdy2AgTiUvrGPmnYWgwhHXtAegX4=
github.com/KimMachineGun/automemlimit v0.7.2/go.mod h1:QZxpHaGOQoYvFhv/r4u3U0JTC2ZcOwbSr11UZF46UBM=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4=
@@ -42,10 +44,10 @@ github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSC
github.com/MauriceGit/skiplist v0.0.0-20191117202105-643e379adb62/go.mod h1:877WBceefKn14QwVVn4xRFUsHsZb9clICgdeTj4XsUg=
github.com/MauriceGit/skiplist v0.0.0-20211105230623-77f5c8d3e145 h1:1yw6O62BReQ+uA1oyk9XaQTvLhcoHWmoQAgXmDFXpIY=
github.com/MauriceGit/skiplist v0.0.0-20211105230623-77f5c8d3e145/go.mod h1:877WBceefKn14QwVVn4xRFUsHsZb9clICgdeTj4XsUg=
github.com/MicahParks/jwkset v0.8.0 h1:jHtclI38Gibmu17XMI6+6/UB59srp58pQVxePHRK5o8=
github.com/MicahParks/jwkset v0.8.0/go.mod h1:fVrj6TmG1aKlJEeceAz7JsXGTXEn72zP1px3us53JrA=
github.com/MicahParks/keyfunc/v3 v3.3.10 h1:JtEGE8OcNeI297AMrR4gVXivV8fyAawFUMkbwNreJRk=
github.com/MicahParks/keyfunc/v3 v3.3.10/go.mod h1:1TEt+Q3FO7Yz2zWeYO//fMxZMOiar808NqjWQQpBPtU=
github.com/MicahParks/jwkset v0.9.6 h1:Tf8l2/MOby5Kh3IkrqzThPQKfLytMERoAsGZKlyYZxg=
github.com/MicahParks/jwkset v0.9.6/go.mod h1:U2oRhRaLgDCLjtpGL2GseNKGmZtLs/3O7p+OZaL5vo0=
github.com/MicahParks/keyfunc/v3 v3.4.0 h1:g03TXq6NjhZyO/UkODl//abm4KiLLNRi0VhW7vGOHyg=
github.com/MicahParks/keyfunc/v3 v3.4.0/go.mod h1:y6Ed3dMgNKTcpxbaQHD8mmrYDUZWJAxteddA6OQj+ag=
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
@@ -57,8 +59,8 @@ github.com/RoaringBitmap/roaring v1.9.4/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhve
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
github.com/alecthomas/chroma/v2 v2.15.0 h1:LxXTQHFoYrstG2nnV9y2X5O94sOBzf0CIUpSTbpxvMc=
github.com/alecthomas/chroma/v2 v2.15.0/go.mod h1:gUhVLrPDXPtp/f+L1jo9xepo9gL4eLwRuGAunSZMkio=
github.com/alecthomas/chroma/v2 v2.18.0 h1:6h53Q4hW83SuF+jcsp7CVhLsMozzvQvO8HBbKQW+gn4=
github.com/alecthomas/chroma/v2 v2.18.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk=
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
@@ -68,49 +70,51 @@ github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmO
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b h1:uUXgbcPDK3KpW29o4iy7GtuappbWT0l5NaMo9H9pJDw=
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/aws/aws-sdk-go-v2 v1.32.8 h1:cZV+NUS/eGxKXMtmyhtYPJ7Z4YLoI/V8bkTdRZfYhGo=
github.com/aws/aws-sdk-go-v2 v1.32.8/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U=
github.com/aws/aws-sdk-go-v2/config v1.28.10 h1:fKODZHfqQu06pCzR69KJ3GuttraRJkhlC8g80RZ0Dfg=
github.com/aws/aws-sdk-go-v2/config v1.28.10/go.mod h1:PvdxRYZ5Um9QMq9PQ0zHHNdtKK+he2NHtFCUFMXWXeg=
github.com/aws/aws-sdk-go-v2/credentials v1.17.51 h1:F/9Sm6Y6k4LqDesZDPJCLxQGXNNHd/ZtJiWd0lCZKRk=
github.com/aws/aws-sdk-go-v2/credentials v1.17.51/go.mod h1:TKbzCHm43AoPyA+iLGGcruXd4AFhF8tOmLex2R9jWNQ=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.23 h1:IBAoD/1d8A8/1aA8g4MBVtTRHhXRiNAgwdbo/xRM2DI=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.23/go.mod h1:vfENuCM7dofkgKpYzuzf1VT1UKkA/YL3qanfBn7HCaA=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.27 h1:jSJjSBzw8VDIbWv+mmvBSP8ezsztMYJGH+eKqi9AmNs=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.27/go.mod h1:/DAhLbFRgwhmvJdOfSm+WwikZrCuUJiA4WgJG0fTNSw=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.27 h1:l+X4K77Dui85pIj5foXDhPlnqcNRG2QUyvca300lXh8=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.27/go.mod h1:KvZXSFEXm6x84yE8qffKvT3x8J5clWnVFXphpohhzJ8=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.8 h1:cWno7lefSH6Pp+mSznagKCgfDGeZRin66UvYUqAkyeA=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.8/go.mod h1:tPD+VjU3ABTBoEJ3nctu5Nyg4P4yjqSH5bJGGkY4+XE=
github.com/aws/aws-sdk-go-v2/service/kms v1.37.8 h1:KbLZjYqhQ9hyB4HwXiheiflTlYQa0+Fz0Ms/rh5f3mk=
github.com/aws/aws-sdk-go-v2/service/kms v1.37.8/go.mod h1:ANs9kBhK4Ghj9z1W+bsr3WsNaPF71qkgd6eE6Ekol/Y=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.9 h1:YqtxripbjWb2QLyzRK9pByfEDvgg95gpC2AyDq4hFE8=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.9/go.mod h1:lV8iQpg6OLOfBnqbGMBKYjilBlf633qwHnBEiMSPoHY=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.8 h1:6dBT1Lz8fK11m22R+AqfRsFn8320K0T5DTGxxOQBSMw=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.8/go.mod h1:/kiBvRQXBc6xeJTYzhSdGvJ5vm1tjaDEjH+MSeRJnlY=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.6 h1:VwhTrsTuVn52an4mXx29PqRzs2Dvu921NpGk7y43tAM=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.6/go.mod h1:+8h7PZb3yY5ftmVLD7ocEoE98hdc8PoKS0H3wfx1dlc=
github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro=
github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM=
github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
github.com/aws/aws-sdk-go-v2/config v1.29.14 h1:f+eEi/2cKCg9pqKBoAIwRGzVb70MRKqWX4dg1BDcSJM=
github.com/aws/aws-sdk-go-v2/config v1.29.14/go.mod h1:wVPHWcIFv3WO89w0rE10gzf17ZYy+UVS1Geq8Iei34g=
github.com/aws/aws-sdk-go-v2/credentials v1.17.67 h1:9KxtdcIA/5xPNQyZRgUSpYOE6j9Bc4+D7nZua0KGYOM=
github.com/aws/aws-sdk-go-v2/credentials v1.17.67/go.mod h1:p3C44m+cfnbv763s52gCqrjaqyPikj9Sg47kUVaNZQQ=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY=
github.com/aws/aws-sdk-go-v2/service/kms v1.38.3 h1:RivOtUH3eEu6SWnUMFHKAW4MqDOzWn1vGQ3S38Y5QMg=
github.com/aws/aws-sdk-go-v2/service/kms v1.38.3/go.mod h1:cQn6tAF77Di6m4huxovNM7NVAozWTZLsDRp9t8Z/WYk=
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 h1:1Gw+9ajCV1jogloEv1RRnvfRFia2cL6c9cuKV2Ps+G8=
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 h1:hXmVKytPfTy5axZ+fYbR5d0cFmC3JvwLm5kM83luako=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 h1:1XuUZ8mYJw9B6lzAkXhqHlJd/XvaX32evhproijJEZY=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4=
github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ=
github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU=
github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/bits-and-blooms/bitset v1.22.0 h1:Tquv9S8+SGaS3EhyA+up3FXzmkhxPGjQQCkcs2uw7w4=
github.com/bits-and-blooms/bitset v1.22.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/caddyserver/caddy/v2 v2.9.1 h1:OEYiZ7DbCzAWVb6TNEkjRcSCRGHVoZsJinoDR/n9oaY=
github.com/caddyserver/caddy/v2 v2.9.1/go.mod h1:ImUELya2el1FDVp3ahnSO2iH1or1aHxlQEQxd/spP68=
github.com/caddyserver/certmagic v0.21.7 h1:66KJioPFJwttL43KYSWk7ErSmE6LfaJgCQuhm8Sg6fg=
github.com/caddyserver/certmagic v0.21.7/go.mod h1:LCPG3WLxcnjVKl/xpjzM0gqh0knrKKKiO5WVttX2eEI=
github.com/caddyserver/caddy/v2 v2.10.0 h1:fonubSaQKF1YANl8TXqGcn4IbIRUDdfAkpcsfI/vX5U=
github.com/caddyserver/caddy/v2 v2.10.0/go.mod h1:q+dgBS3xtIJJGYI2H5Nyh9+4BvhQQ9yCGmECv4Ubdjo=
github.com/caddyserver/certmagic v0.23.0 h1:CfpZ/50jMfG4+1J/u2LV6piJq4HOfO6ppOnOf7DkFEU=
github.com/caddyserver/certmagic v0.23.0/go.mod h1:9mEZIWqqWoI+Gf+4Trh04MOVPD0tGSxtqsxg87hAIH4=
github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA=
github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4=
github.com/ccoveille/go-safecast v1.6.1 h1:Nb9WMDR8PqhnKCVs2sCB+OqhohwO5qaXtCviZkIff5Q=
github.com/ccoveille/go-safecast v1.6.1/go.mod h1:QqwNjxQ7DAqY0C721OIO9InMk9zCwcsO7tnRuHytad8=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
@@ -127,17 +131,19 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-oidc/v3 v3.12.0 h1:sJk+8G2qq94rDI6ehZ71Bol3oUHy63qNYmkiSjrc/Jo=
github.com/coreos/go-oidc/v3 v3.12.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0=
github.com/coreos/go-oidc/v3 v3.14.1 h1:9ePWwfdwC4QKRlCXsJGou56adA/owXczOzwKdOumLqk=
github.com/coreos/go-oidc/v3 v3.14.1/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
@@ -155,22 +161,23 @@ github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa5
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dolthub/maphash v0.1.0 h1:bsQ7JsF4FkkWyrP3oCnFJgrCUAFbFf3kOl4L/QxPDyQ=
github.com/dolthub/maphash v0.1.0/go.mod h1:gkg4Ch4CdCDu5h6PMriVLawB7koZ+5ijb9puGMV50a4=
github.com/dunglas/caddy-cbrotli v1.0.0 h1:+WNqXBkWyMcIpXB2rVZ3nwcElUbuAzf0kPxNXU4D+u0=
github.com/dunglas/caddy-cbrotli v1.0.0/go.mod h1:KZsUu3fnQBgO0o3YDoQuO3Z61dFgUncr1F8rg8acwQw=
github.com/dunglas/httpsfv v1.0.2 h1:iERDp/YAfnojSDJ7PW3dj1AReJz4MrwbECSSE59JWL0=
github.com/dunglas/httpsfv v1.0.2/go.mod h1:zID2mqw9mFsnt7YC3vYQ9/cjq30q41W+1AnDwH8TiMg=
github.com/dunglas/mercure v0.18.2 h1:WknM1Ags4wwcD6t6D2hjAQhdKte43SLQAKCVX8Cxc3w=
github.com/dunglas/mercure v0.18.2/go.mod h1:4f0YfFbnggYNEoJmCpjzxx6LeybiZlf8smBYOBOCsH4=
github.com/dunglas/mercure/caddy v0.18.2 h1:k7IqRL/kAkOveBBcwC6OC35dmwuOMDk4pNkcKz8gXdc=
github.com/dunglas/mercure/caddy v0.18.2/go.mod h1:vLAxBRWSxlPgvrEK3Q7rswvHQFvlxbKEsVOoedAymSs=
github.com/dunglas/vulcain v1.1.1 h1:nWh6sEhaeSla3IVXpzxQ8tK1nEr4lM1Q/HW8EVdpIgU=
github.com/dunglas/vulcain v1.1.1/go.mod h1:5YslB+KQI6SbghCDB3zrDyGtS2WCLvu+oFBkHi6mo+w=
github.com/dunglas/vulcain/caddy v1.1.1 h1:TT90lkiqnF1rGEyWXwZwhnuTRInNmYgJsFzR9ZH8pbQ=
github.com/dunglas/vulcain/caddy v1.1.1/go.mod h1:6aXBrXh46qlJv9rIBlX5DlxzmRPJyVWpnFp8JMkpBNI=
github.com/dunglas/frankenphp v1.7.0/go.mod h1:BaPr7WO5PmAkMtJvRUhRcmdWNAuEIK1zWLC++c8GhE0=
github.com/dunglas/httpsfv v1.1.0 h1:Jw76nAyKWKZKFrpMMcL76y35tOpYHqQPzHQiwDvpe54=
github.com/dunglas/httpsfv v1.1.0/go.mod h1:zID2mqw9mFsnt7YC3vYQ9/cjq30q41W+1AnDwH8TiMg=
github.com/dunglas/mercure v0.19.2 h1:eBMQhxvzJTenIffL+jlqtWO+TPevCjOQ+DQJb8iB6+s=
github.com/dunglas/mercure v0.19.2/go.mod h1:/8edy/qugBTM4CbEzX4DwykEIh1VWHWlo1e/pHos120=
github.com/dunglas/mercure/caddy v0.19.2 h1:Ug/nALeSn7DH4qMy7I2pImCf1D0H+0vr6K4AWjgmOKg=
github.com/dunglas/mercure/caddy v0.19.2/go.mod h1:Kq+qvO79l8odqokiNcdKbH53aOLurarse8xZ42ALzJQ=
github.com/dunglas/vulcain v1.2.0 h1:RPNYuTe0woh4bGfIMAJ3dCgJDN8VJwhDjucQiCOoUsE=
github.com/dunglas/vulcain v1.2.0/go.mod h1:LhyYeqSAEw9P65l25CIzS1sRwJxkP75Qa7p8lIHZPsc=
github.com/dunglas/vulcain/caddy v1.2.0 h1:2O2R7Hn+kkInv6mrmOk5LLDtgRdPKGlXzdFJUKrb/jE=
github.com/dunglas/vulcain/caddy v1.2.0/go.mod h1:RAbiewGNIyWCmT37C2k4O1hs4IF+QIGVVgSNQuW2FH4=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
@@ -182,43 +189,45 @@ github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiD
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU=
github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/gammazero/deque v1.0.0 h1:LTmimT8H7bXkkCy6gZX7zNLtkbz4NdS2z8LZuor3j34=
github.com/gammazero/deque v1.0.0/go.mod h1:iflpYvtGfM3U8S8j+sZEKIak3SAKYpA5/SQewgfXDKo=
github.com/getkin/kin-openapi v0.128.0 h1:jqq3D9vC9pPq1dGcOCv7yOp1DaEe7c/T1vzcLbITSp4=
github.com/getkin/kin-openapi v0.128.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM=
github.com/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk=
github.com/getkin/kin-openapi v0.132.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-chi/chi/v5 v5.2.0 h1:Aj1EtB0qR2Rdo2dG4O94RIU35w2lvQSj6BRA4+qwFL0=
github.com/go-chi/chi/v5 v5.2.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E=
github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc=
github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY=
github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
github.com/go-jose/go-jose/v4 v4.1.0 h1:cYSYxd3pw5zd2FSXk2vGdn9igQU2PS8MuxrCOCl0FdY=
github.com/go-jose/go-jose/v4 v4.1.0/go.mod h1:GG/vqmYm3Von2nYiB2vGTXzdoNKE5tix5tuc6iAd+sw=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic=
github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=
github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU=
github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@@ -228,45 +237,46 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/brotli/go/cbrotli v0.0.0-20241111155135-4303850b01d6 h1:ygfAzuHUP3wed0um8AamwtZIh022Ie5lnsWHhItj/sY=
github.com/google/brotli/go/cbrotli v0.0.0-20241111155135-4303850b01d6/go.mod h1:nOPhAkwVliJdNTkj3gXpljmWhjc4wCaVqbMJcPKWP4s=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/cel-go v0.23.0 h1:knsnzeUOcREUFo0ZFJqZI8Rk6uEVyobAlir7GEbf5v0=
github.com/google/cel-go v0.23.0/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo=
github.com/google/cel-go v0.25.0 h1:jsFw9Fhn+3y2kBbltZR4VEz5xKkcIFRPDnuEzAGv5GY=
github.com/google/cel-go v0.25.0/go.mod h1:hjEb6r5SuOSlhCHmFoLzu8HGCERvIsDAbxDAyNU/MmI=
github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
github.com/google/certificate-transparency-go v1.3.0 h1:+UhSNQAyA38Ed4CGfwOZeG4sJ030ELQZE4xtMFOxA7U=
github.com/google/certificate-transparency-go v1.3.0/go.mod h1:/xVlT13jyrOuJOXTW5PjCBCrHBtXUq/jT5UeW40xliQ=
github.com/google/certificate-transparency-go v1.3.1 h1:akbcTfQg0iZlANZLn0L9xOeWtyCIdeoYhKrqi5iH3Go=
github.com/google/certificate-transparency-go v1.3.1/go.mod h1:gg+UQlx6caKEDQ9EElFOujyxEQEfOiQzAt6782Bvi8k=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/go-tpm v0.9.3 h1:+yx0/anQuGzi+ssRqeD6WpXjW2L/V0dItUayO0i9sRc=
github.com/google/go-tpm v0.9.3/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
github.com/google/go-tpm-tools v0.4.4 h1:oiQfAIkc6xTy9Fl5NKTeTJkBTlXdHsxAofmQyxBKY98=
github.com/google/go-tpm-tools v0.4.4/go.mod h1:T8jXkp2s+eltnCDIsXR84/MTcVU9Ja7bh3Mit0pa4AY=
github.com/google/go-tpm v0.9.5 h1:ocUmnDebX54dnW+MQWGQRbdaAcJELsa6PqZhJ48KwVU=
github.com/google/go-tpm v0.9.5/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
github.com/google/go-tpm-tools v0.4.5 h1:3fhthtyMDbIZFR5/0y1hvUoZ1Kf4i1eZ7C73R4Pvd+k=
github.com/google/go-tpm-tools v0.4.5/go.mod h1:ktjTNq8yZFD6TzdBFefUfen96rF3NpYwpSb2d8bc+Y8=
github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus=
github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20250125003558-7fdb3d7e6fa0 h1:my2ucqBZmv+cWHIhZNSIYKzgN8EBGyHdC7zD5sASRAg=
github.com/google/pprof v0.0.0-20250125003558-7fdb3d7e6fa0/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM=
github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA=
github.com/google/pprof v0.0.0-20250602020802-c6617b811d0e h1:FJta/0WsADCe1r9vQjdHbd3KuiLPu7Y9WlyLGwMUNyE=
github.com/google/pprof v0.0.0-20250602020802-c6617b811d0e/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw=
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4=
github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
github.com/googleapis/gax-go/v2 v2.14.2 h1:eBLnkZ9635krYIPD+ag1USrOAI0Nr0QYF3+/3GqO0k0=
github.com/googleapis/gax-go/v2 v2.14.2/go.mod h1:ON64QhlJkhVtSqp4v1uaK92VyZ2gmvDQsweuyLV+8+w=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
@@ -274,12 +284,11 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
@@ -288,14 +297,12 @@ github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso=
github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI=
github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
github.com/jackc/pgx/v5 v5.7.5 h1:JHGfMnQY+IEtGM63d+NGMjoRpysB2JBwDr5fsngwmJs=
github.com/jackc/pgx/v5 v5.7.5/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
@@ -308,10 +315,10 @@ github.com/kevburnsjr/skipfilter v0.0.1 h1:EWl1lWUJfIehrKYIEkps0Cl67lCfS2pUM9iZF
github.com/kevburnsjr/skipfilter v0.0.1/go.mod h1:jfaRyFOYVUtIa6IIC+0mB1qiZqhHw+DKvFowCBuijSk=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@@ -323,12 +330,10 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/libdns/libdns v1.1.0 h1:9ze/tWvt7Df6sbhOJRB8jT33GHEHpEQXdtkE3hPthbU=
github.com/libdns/libdns v1.1.0/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
@@ -343,19 +348,17 @@ github.com/maypok86/otter v1.2.4 h1:HhW1Pq6VdJkmWwcZZq19BlEQkHtI8xgsQzBVXJU0nfc=
github.com/maypok86/otter v1.2.4/go.mod h1:mKLfoI7v1HOmQMwFgX4QkRk23mX6ge3RDvjdHOWG4R4=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mholt/acmez/v3 v3.0.1 h1:4PcjKjaySlgXK857aTfDuRbmnM5gb3Ruz3tvoSJAUp8=
github.com/mholt/acmez/v3 v3.0.1/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc=
github.com/mholt/acmez/v3 v3.1.2/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE=
github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -368,20 +371,26 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY=
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw=
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c=
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o=
github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU=
github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
github.com/peterbourgon/diskv/v3 v3.0.1 h1:x06SQA46+PKIUftmEujdwSEpIx8kR+M9eLYsUxeYveU=
github.com/peterbourgon/diskv/v3 v3.0.1/go.mod h1:kJ5Ny7vLdARGU3WUuy6uzO6T0nb/2gWcT1JiBvRmb5o=
github.com/pires/go-proxyproto v0.8.0 h1:5unRmEAPbHXHuLjDg01CxJWf91cw3lKHc/0xzKpXEe0=
github.com/pires/go-proxyproto v0.8.0/go.mod h1:iknsfgnH8EkjrMeMyvfKByp9TiBZCKZM0jx2xmKqnVY=
github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0=
github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -391,21 +400,21 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/common v0.64.0 h1:pdZeA+g617P7oGv1CzdTzyeShxAGrTBsolKNOLQPGO4=
github.com/prometheus/common v0.64.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/quic-go/quic-go v0.49.0 h1:w5iJHXwHxs1QxyBv1EHKuC50GX5to8mJAxvtnttJp94=
github.com/quic-go/quic-go v0.49.0/go.mod h1:s2wDnmCdooUQBmQfpUSTCYBl1/D4FcqbULMMkASvR6s=
github.com/quic-go/quic-go v0.52.0 h1:/SlHrCRElyaU6MaEPKqKr9z83sBg2v4FLLvWM+Z47pA=
github.com/quic-go/quic-go v0.52.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
@@ -413,10 +422,8 @@ github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k=
github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk=
github.com/schollz/jsonstore v1.1.0 h1:WZBDjgezFS34CHI+myb4s8GGpir3UMpy7vWoCeO0n6E=
github.com/schollz/jsonstore v1.1.0/go.mod h1:15c6+9guw8vDRyozGjN3FoILt0wpruJk9Pi66vjaZfg=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
@@ -449,22 +456,24 @@ github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5k
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/slackhq/nebula v1.9.4 h1:p06JxtXT/OBMWt2OQkY7F0phOBb42X93YWNsS1yqC9o=
github.com/slackhq/nebula v1.9.4/go.mod h1:1+4q4wd3dDAjO8rKCttSb9JIVbklQhuJiBp5I0lbIsQ=
github.com/slackhq/nebula v1.9.5 h1:ZrxcvP/lxwFglaijmiwXLuCSkybZMJnqSYI1S8DtGnY=
github.com/slackhq/nebula v1.9.5/go.mod h1:1+4q4wd3dDAjO8rKCttSb9JIVbklQhuJiBp5I0lbIsQ=
github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1/1fApl1A+9VcBk+9dcqGfnePY87LY=
github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc=
github.com/smallstep/certificates v0.28.1 h1:hLYz1It9mecJD0DqQ+qLldQnTUkanFIztuyqmxKRZkY=
github.com/smallstep/certificates v0.28.1/go.mod h1:/xlSParPTewKBdhyp/vHcoOmtwK8AbPiXdmbu6c4J3A=
github.com/smallstep/cli-utils v0.10.0 h1:CfXNvHtIN5pAzGvGP0NEUZoGFcj5epNEB6RSpSfduek=
github.com/smallstep/cli-utils v0.10.0/go.mod h1:jIeNa5ctrVg89lU5TaQKYd6o1eFxi9mtZu1sXSxpEBg=
github.com/smallstep/go-attestation v0.4.4-0.20240109183208-413678f90935 h1:kjYvkvS/Wdy0PVRDUAA0gGJIVSEZYhiAJtfwYgOYoGA=
github.com/smallstep/go-attestation v0.4.4-0.20240109183208-413678f90935/go.mod h1:vNAduivU014fubg6ewygkAvQC0IQVXqdc8vaGl/0er4=
github.com/smallstep/certificates v0.28.3 h1:rcMh1TAs8m2emP3aDJxKLkE9jriAtcFtCuj2gttnpmI=
github.com/smallstep/certificates v0.28.3/go.mod h1:P/IjGTvRCem3YZ7d1XtUxpvK/8dfFsJn7gaVLpMXbJw=
github.com/smallstep/cli-utils v0.12.1 h1:D9QvfbFqiKq3snGZ2xDcXEFrdFJ1mQfPHZMq/leerpE=
github.com/smallstep/cli-utils v0.12.1/go.mod h1:skV2Neg8qjiKPu2fphM89H9bIxNpKiiRTnX9Q6Lc+20=
github.com/smallstep/go-attestation v0.4.4-0.20241119153605-2306d5b464ca h1:VX8L0r8vybH0bPeaIxh4NQzafKQiqvlOn8pmOXbFLO4=
github.com/smallstep/go-attestation v0.4.4-0.20241119153605-2306d5b464ca/go.mod h1:vNAduivU014fubg6ewygkAvQC0IQVXqdc8vaGl/0er4=
github.com/smallstep/linkedca v0.23.0 h1:5W/7EudlK1HcCIdZM68dJlZ7orqCCCyv6bm2l/0JmLU=
github.com/smallstep/linkedca v0.23.0/go.mod h1:7cyRM9soAYySg9ag65QwytcgGOM+4gOlkJ/YA58A9E8=
github.com/smallstep/nosql v0.7.0 h1:YiWC9ZAHcrLCrayfaF+QJUv16I2bZ7KdLC3RpJcnAnE=
github.com/smallstep/nosql v0.7.0/go.mod h1:H5VnKMCbeq9QA6SRY5iqPylfxLfYcLwvUff3onQ8+HU=
github.com/smallstep/pkcs7 v0.1.1 h1:x+rPdt2W088V9Vkjho4KtoggyktZJlMduZAtRHm68LU=
github.com/smallstep/pkcs7 v0.1.1/go.mod h1:dL6j5AIz9GHjVEBTXtW+QliALcgM19RtXaTeyxI+AfA=
github.com/smallstep/scep v0.0.0-20241223071629-a37a330173bc h1:gJ1mkz/iJhKnKUJit5DCFxNRWo9mxIkVm9SI8DiUugI=
github.com/smallstep/scep v0.0.0-20241223071629-a37a330173bc/go.mod h1:bENyEPpujhqigQx115AitJTc11LZmGUNk0ftgyhcNus=
github.com/smallstep/pkcs7 v0.2.1 h1:6Kfzr/QizdIuB6LSv8y1LJdZ3aPSfTNhTLqAx9CTLfA=
github.com/smallstep/pkcs7 v0.2.1/go.mod h1:RcXHsMfL+BzH8tRhmrF1NkkpebKpq3JEM66cOFxanf0=
github.com/smallstep/scep v0.0.0-20250318231241-a25cabb69492 h1:k23+s51sgYix4Zgbvpmy+1ZgXLjr4ZTkBTqXmpnImwA=
github.com/smallstep/scep v0.0.0-20250318231241-a25cabb69492/go.mod h1:QQhwLqCS13nhv8L5ov7NgusowENUtXdEzdytjmJHdZQ=
github.com/smallstep/truststore v0.13.0 h1:90if9htAOblavbMeWlqNLnO9bsjjgVv2hQeQJCi/py4=
github.com/smallstep/truststore v0.13.0/go.mod h1:3tmMp2aLKZ/OA/jnFUB0cYPcho402UG2knuJoPh4j7A=
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
@@ -475,21 +484,21 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA=
github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=
github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -538,8 +547,8 @@ github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zI
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark v1.7.12 h1:YwGP/rrea2/CnCtUHgjuolG/PnMxdQtPMO5PvaE2/nY=
github.com/yuin/goldmark v1.7.12/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ=
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I=
github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
@@ -548,51 +557,49 @@ github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI=
github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE=
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk=
go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q=
go.opentelemetry.io/contrib/propagators/autoprop v0.58.0 h1:pL1MMoBcG/ol6fVsjE1bbOO9A8GMQiN+T73hnmaXDoU=
go.opentelemetry.io/contrib/propagators/autoprop v0.58.0/go.mod h1:EU5uMoCqafsagp4hzFqzu1Eyg/8L23JS5Y1hChoHf7s=
go.opentelemetry.io/contrib/propagators/aws v1.33.0 h1:MefPfPIut0IxEiQRK1qVv5AFADBOwizl189+m7QhpFg=
go.opentelemetry.io/contrib/propagators/aws v1.33.0/go.mod h1:VB6xPo12uW/PezOqtA/cY2/DiAGYshnhID606wC9NEY=
go.opentelemetry.io/contrib/propagators/b3 v1.33.0 h1:ig/IsHyyoQ1F1d6FUDIIW5oYpsuTVtN16AyGOgdjAHQ=
go.opentelemetry.io/contrib/propagators/b3 v1.33.0/go.mod h1:EsVYoNy+Eol5znb6wwN3XQTILyjl040gUpEnUSNZfsk=
go.opentelemetry.io/contrib/propagators/jaeger v1.33.0 h1:Jok/dG8kfp+yod29XKYV/blWgYPlMuRUoRHljrXMF5E=
go.opentelemetry.io/contrib/propagators/jaeger v1.33.0/go.mod h1:ku/EpGk44S5lyVMbtJRK2KFOnXEehxf6SDnhu1eZmjA=
go.opentelemetry.io/contrib/propagators/ot v1.33.0 h1:xj/pQFKo4ROsx0v129KpLgFwaYMgFTu3dAMEEih97cY=
go.opentelemetry.io/contrib/propagators/ot v1.33.0/go.mod h1:/xxHCLhTmaypEFwMViRGROj2qgrGiFrkxIlATt0rddc=
go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw=
go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA=
go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ=
go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M=
go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM=
go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM=
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg=
go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY=
go.step.sm/crypto v0.57.0 h1:YjoRQDaJYAxHLVwjst0Bl0xcnoKzVwuHCJtEo2VSHYU=
go.step.sm/crypto v0.57.0/go.mod h1:+Lwp5gOVPaTa3H/Ul/TzGbxQPXZZcKIUGMS0lG6n9Go=
go.step.sm/linkedca v0.22.2 h1:zmFIyDC77gFHo6FLQJ8OIXYpLYDIsgDWaYqtYs6A9/Q=
go.step.sm/linkedca v0.22.2/go.mod h1:ESY8r5VfhJA8ZVzI6hXIQcEX9LwaY3aoPnT+Hb9jpbw=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/contrib/propagators/autoprop v0.60.0 h1:sevByeAWTtfBdJQT7nkJfK5wOCjNpmDMZGPEBx3l1RA=
go.opentelemetry.io/contrib/propagators/autoprop v0.60.0/go.mod h1:uEhyRPnUTSeUwMjDdrMQnsJ0sQ2mf/fA94hfchemm4A=
go.opentelemetry.io/contrib/propagators/aws v1.35.0 h1:xoXA+5dVwsf5uE5GvSJ3lKiapyMFuIzbEmJwQ0JP+QU=
go.opentelemetry.io/contrib/propagators/aws v1.35.0/go.mod h1:s11Orts/IzEgw9Srw5iRXtk2kM2j3jt/45noUWyf60E=
go.opentelemetry.io/contrib/propagators/b3 v1.35.0 h1:DpwKW04LkdFRFCIgM3sqwTJA/QREHMeMHYPWP1WeaPQ=
go.opentelemetry.io/contrib/propagators/b3 v1.35.0/go.mod h1:9+SNxwqvCWo1qQwUpACBY5YKNVxFJn5mlbXg/4+uKBg=
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 h1:UIrZgRBHUrYRlJ4V419lVb4rs2ar0wFzKNAebaP05XU=
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0/go.mod h1:0ciyFyYZxE6JqRAQvIgGRabKWDUmNdW3GAQb6y/RlFU=
go.opentelemetry.io/contrib/propagators/ot v1.35.0 h1:ZsgYijVvOpju4mq3g4QyqCwLKs2vKenlCpZHbKu50OA=
go.opentelemetry.io/contrib/propagators/ot v1.35.0/go.mod h1:t1ZwtgjEtDH9uW6OlCRVLL2wOgsTJmp0pJwNouUq+HE=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI=
go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc=
go.step.sm/crypto v0.66.0 h1:9TW6BEguOtcS9NIjja9bDQ+j8OjhenU/F6lJfHjbXNU=
go.step.sm/crypto v0.66.0/go.mod h1:anqGyvO/Px05D1mznHq4/a9wwP1I1DmMZvk+TWX5Dzo=
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
@@ -609,14 +616,14 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/crypto/x509roots/fallback v0.0.0-20250118192723-a8ea4be81f07 h1:Tuk3hxOkRoX4Xwph6/tRU1wGumEsVYM2TZfvAC6MllM=
golang.org/x/crypto/x509roots/fallback v0.0.0-20250118192723-a8ea4be81f07/go.mod h1:kNa9WdvYnzFwC79zRpLRMJbdEFlhyM5RPFBBZp/wWH8=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
golang.org/x/crypto/x509roots/fallback v0.0.0-20250531095911-4f9f0ca9fcfb h1:Qr7HsIdEUI8QBvEohd2cTYzTErkEkUXafR9qx9D6QEo=
golang.org/x/crypto/x509roots/fallback v0.0.0-20250531095911-4f9f0ca9fcfb/go.mod h1:lxN5T34bK4Z/i6cMaU7frUU57VkDXFD4Kamfl/cp9oU=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA=
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b h1:QoALfVG9rhQ/M7vYDScfPdWjGL9dlsVVM5VGh7aKoAA=
golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@@ -625,8 +632,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -643,14 +650,14 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -662,8 +669,9 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -685,9 +693,9 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -696,9 +704,9 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -708,12 +716,13 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -724,14 +733,14 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
google.golang.org/api v0.216.0 h1:xnEHy+xWFrtYInWPy8OdGFsyIfWJjtVnO39g7pz2BFY=
google.golang.org/api v0.216.0/go.mod h1:K9wzQMvWi47Z9IU7OgdOofvZuw75Ge3PPITImZR/UyI=
google.golang.org/api v0.234.0 h1:d3sAmYq3E9gdr2mpmiWGbm9pHsA/KJmyiLkwKfHBqU4=
google.golang.org/api v0.234.0/go.mod h1:QpeJkemzkFKe5VCE/PMv7GsUfn9ZF+u+q1Q7w6ckxTg=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -741,27 +750,27 @@ google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoA
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk=
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc=
google.golang.org/genproto/googleapis/api v0.0.0-20250127172529-29210b9bc287 h1:A2ni10G3UlplFrWdCDJTl7D7mJ7GSRm37S+PDimaKRw=
google.golang.org/genproto/googleapis/api v0.0.0-20250127172529-29210b9bc287/go.mod h1:iYONQfRdizDB8JJBybql13nArx91jcUk7zCXEsOofM4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250127172529-29210b9bc287 h1:J1H9f+LEdWAfHcez/4cvaVBox7cOYT+IU6rgqj5x++8=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250127172529-29210b9bc287/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk=
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78=
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk=
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a h1:SGktgSolFCo75dnHJF2yMvnns6jCmHFJ0vE4Vn2JKvQ=
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a/go.mod h1:a77HrdMjoeKbnd2jmgcWdaS++ZLZAEq3orIOAEIKiVw=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8=
google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 h1:F29+wU6Ee6qgu9TddPgooOdaqsxTMunOoj8KA5yuS5A=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zteSqZV6fqFOWBl4Yde8En8MryZA=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=

View File

@@ -6,7 +6,7 @@ import (
"path/filepath"
caddycmd "github.com/caddyserver/caddy/v2/cmd"
"github.com/dunglas/frankenphp"
"frankenphp.dev"
"github.com/spf13/cobra"
)
@@ -25,7 +25,7 @@ Executes a PHP script similarly to the CLI SAPI.`,
})
}
func cmdPHPCLI(caddycmd.Flags) (int, error) {
func cmdPHPCLI(fs caddycmd.Flags) (int, error) {
args := os.Args[2:]
if len(args) < 1 {
return 1, errors.New("the path to the PHP script is required")
@@ -37,7 +37,13 @@ func cmdPHPCLI(caddycmd.Flags) (int, error) {
}
}
status := frankenphp.ExecuteScriptCLI(args[0], args)
var status int
if len(args) >= 2 && args[0] == "-r" {
status = frankenphp.ExecutePHPCode(args[1])
} else {
status = frankenphp.ExecuteScriptCLI(args[0], args)
}
os.Exit(status)
return status, nil

View File

@@ -3,15 +3,14 @@ package caddy
import (
"encoding/json"
"log"
"log/slog"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"time"
mercureModule "github.com/dunglas/mercure/caddy"
"go.uber.org/zap/zapcore"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig"
@@ -21,15 +20,14 @@ import (
"github.com/caddyserver/caddy/v2/modules/caddyhttp/fileserver"
"github.com/caddyserver/caddy/v2/modules/caddyhttp/rewrite"
"github.com/caddyserver/certmagic"
"github.com/dunglas/frankenphp"
"frankenphp.dev"
"github.com/spf13/cobra"
)
func init() {
caddycmd.RegisterCommand(caddycmd.Command{
Name: "php-server",
Usage: "[--domain <example.com>] [--root <path>] [--listen <addr>] [--worker /path/to/worker.php<,nb-workers>] [--watch <paths...>] [--access-log] [--debug] [--no-compress] [--mercure]",
Usage: "[--domain=<example.com>] [--root=<path>] [--listen=<addr>] [--worker=/path/to/worker.php<,nb-workers>] [--watch[=<glob-pattern>]]... [--access-log] [--debug] [--no-compress] [--mercure]",
Short: "Spins up a production-ready PHP server",
Long: `
A simple but production-ready PHP server. Useful for quick deployments,
@@ -48,11 +46,14 @@ For more advanced use cases, see https://github.com/dunglas/frankenphp/blob/main
cmd.Flags().StringP("root", "r", "", "The path to the root of the site")
cmd.Flags().StringP("listen", "l", "", "The address to which to bind the listener")
cmd.Flags().StringArrayP("worker", "w", []string{}, "Worker script")
cmd.Flags().StringArrayP("watch", "", []string{}, "Directory to watch for file changes")
cmd.Flags().StringArray("watch", []string{}, "Glob pattern of directories and files to watch for changes")
cmd.Flags().BoolP("access-log", "a", false, "Enable the access log")
cmd.Flags().BoolP("debug", "v", false, "Enable verbose debug logs")
cmd.Flags().BoolP("mercure", "m", false, "Enable the built-in Mercure.rocks hub")
cmd.Flags().BoolP("no-compress", "", false, "Disable Zstandard, Brotli and Gzip compression")
cmd.Flags().Bool("no-compress", false, "Disable Zstandard, Brotli and Gzip compression")
cmd.Flags().Lookup("watch").NoOptDefVal = defaultWatchPattern
cmd.RunE = caddycmd.WrapCommandFuncForCobra(cmdPHPServer)
},
})
@@ -79,27 +80,30 @@ func cmdPHPServer(fs caddycmd.Flags) (int, error) {
panic(err)
}
if frankenphp.EmbeddedAppPath != "" {
if err := os.Chdir(frankenphp.EmbeddedAppPath); err != nil {
return caddy.ExitCodeFailedStartup, err
}
}
var workersOption []workerConfig
if len(workers) != 0 {
workersOption = make([]workerConfig, 0, len(workers))
for _, worker := range workers {
parts := strings.SplitN(worker, ",", 2)
if frankenphp.EmbeddedAppPath != "" && filepath.IsLocal(parts[0]) {
parts[0] = filepath.Join(frankenphp.EmbeddedAppPath, parts[0])
}
var num int
var num uint64
if len(parts) > 1 {
num, _ = strconv.Atoi(parts[1])
num, _ = strconv.ParseUint(parts[1], 10, 32)
}
workersOption = append(workersOption, workerConfig{FileName: parts[0], Num: num})
workersOption = append(workersOption, workerConfig{FileName: parts[0], Num: int(num)})
}
workersOption[0].Watch = watch
}
if frankenphp.EmbeddedAppPath != "" {
if _, err := os.Stat(filepath.Join(frankenphp.EmbeddedAppPath, "php.ini")); err == nil {
if _, err := os.Stat("php.ini"); err == nil {
iniScanDir := os.Getenv("PHP_INI_SCAN_DIR")
if err := os.Setenv("PHP_INI_SCAN_DIR", iniScanDir+":"+frankenphp.EmbeddedAppPath); err != nil {
@@ -107,8 +111,8 @@ func cmdPHPServer(fs caddycmd.Flags) (int, error) {
}
}
if _, err := os.Stat(filepath.Join(frankenphp.EmbeddedAppPath, "Caddyfile")); err == nil {
config, _, err := caddycmd.LoadConfig(filepath.Join(frankenphp.EmbeddedAppPath, "Caddyfile"), "")
if _, err := os.Stat("Caddyfile"); err == nil {
config, _, err := caddycmd.LoadConfig("Caddyfile", "caddyfile")
if err != nil {
return caddy.ExitCodeFailedStartup, err
}
@@ -121,9 +125,7 @@ func cmdPHPServer(fs caddycmd.Flags) (int, error) {
}
if root == "" {
root = filepath.Join(frankenphp.EmbeddedAppPath, defaultDocumentRoot)
} else if filepath.IsLocal(root) {
root = filepath.Join(frankenphp.EmbeddedAppPath, root)
root = defaultDocumentRoot
}
}
@@ -334,7 +336,7 @@ func cmdPHPServer(fs caddycmd.Flags) (int, error) {
cfg.Logging = &caddy.Logging{
Logs: map[string]*caddy.CustomLog{
"default": {
BaseLog: caddy.BaseLog{Level: zapcore.DebugLevel.CapitalString()},
BaseLog: caddy.BaseLog{Level: slog.LevelDebug.String()},
},
},
}

View File

@@ -19,7 +19,7 @@ func TestWorkerWithInactiveWatcher(t *testing.T) {
frankenphp {
worker {
file ../testdata/worker-with-watcher.php
file ../testdata/worker-with-counter.php
num 1
watch ./**/*.php
}
@@ -28,7 +28,7 @@ func TestWorkerWithInactiveWatcher(t *testing.T) {
localhost:`+testPort+` {
root ../testdata
rewrite worker-with-watcher.php
rewrite worker-with-counter.php
php
}
`, "caddyfile")

29
cgi.go
View File

@@ -1,17 +1,24 @@
package frankenphp
// #cgo nocallback frankenphp_register_bulk
// #cgo nocallback frankenphp_register_variables_from_request_info
// #cgo nocallback frankenphp_register_variable_safe
// #cgo nocallback frankenphp_register_single
// #cgo noescape frankenphp_register_bulk
// #cgo noescape frankenphp_register_variables_from_request_info
// #cgo noescape frankenphp_register_variable_safe
// #cgo noescape frankenphp_register_single
// #include <php_variables.h>
// #include "frankenphp.h"
import "C"
import (
"crypto/tls"
"net"
"net/http"
"path/filepath"
"strings"
"unsafe"
"github.com/dunglas/frankenphp/internal/phpheaders"
"frankenphp.dev/internal/phpheaders"
)
var knownServerKeys = []string{
@@ -48,7 +55,8 @@ var knownServerKeys = []string{
//
// TODO: handle this case https://github.com/caddyserver/caddy/issues/3718
// Inspired by https://github.com/caddyserver/caddy/blob/master/modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go
func addKnownVariablesToServer(thread *phpThread, request *http.Request, fc *FrankenPHPContext, trackVarsArray *C.zval) {
func addKnownVariablesToServer(thread *phpThread, fc *frankenPHPContext, trackVarsArray *C.zval) {
request := fc.request
keys := mainThread.knownServerKeys
// Separate remote IP and port; more lenient than net.SplitHostPort
var ip, port string
@@ -160,8 +168,8 @@ func packCgiVariable(key *C.zend_string, value string) C.ht_key_value_pair {
return C.ht_key_value_pair{key, toUnsafeChar(value), C.size_t(len(value))}
}
func addHeadersToServer(request *http.Request, thread *phpThread, fc *FrankenPHPContext, trackVarsArray *C.zval) {
for field, val := range request.Header {
func addHeadersToServer(fc *frankenPHPContext, trackVarsArray *C.zval) {
for field, val := range fc.request.Header {
if k := mainThread.commonHeaders[field]; k != nil {
v := strings.Join(val, ", ")
C.frankenphp_register_single(k, toUnsafeChar(v), C.size_t(len(v)), trackVarsArray)
@@ -176,7 +184,7 @@ func addHeadersToServer(request *http.Request, thread *phpThread, fc *FrankenPHP
}
}
func addPreparedEnvToServer(fc *FrankenPHPContext, trackVarsArray *C.zval) {
func addPreparedEnvToServer(fc *frankenPHPContext, trackVarsArray *C.zval) {
for k, v := range fc.env {
C.frankenphp_register_variable_safe(toUnsafeChar(k), toUnsafeChar(v), C.size_t(len(v)), trackVarsArray)
}
@@ -186,11 +194,10 @@ func addPreparedEnvToServer(fc *FrankenPHPContext, trackVarsArray *C.zval) {
//export go_register_variables
func go_register_variables(threadIndex C.uintptr_t, trackVarsArray *C.zval) {
thread := phpThreads[threadIndex]
r := thread.getActiveRequest()
fc := r.Context().Value(contextKey).(*FrankenPHPContext)
fc := thread.getRequestContext()
addKnownVariablesToServer(thread, r, fc, trackVarsArray)
addHeadersToServer(r, thread, fc, trackVarsArray)
addKnownVariablesToServer(thread, fc, trackVarsArray)
addHeadersToServer(fc, trackVarsArray)
// The Prepared Environment is registered last and can overwrite any previous values
addPreparedEnvToServer(fc, trackVarsArray)
@@ -201,7 +208,7 @@ func go_register_variables(threadIndex C.uintptr_t, trackVarsArray *C.zval) {
//
// Adapted from https://github.com/caddyserver/caddy/blob/master/modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go
// Copyright 2015 Matthew Holt and The Caddy Authors
func splitPos(fc *FrankenPHPContext, path string) int {
func splitPos(fc *frankenPHPContext, path string) int {
if len(fc.splitPath) == 0 {
return 0
}

166
context.go Normal file
View File

@@ -0,0 +1,166 @@
package frankenphp
import (
"context"
"log/slog"
"net/http"
"os"
"strings"
"time"
)
// frankenPHPContext provides contextual information about the Request to handle.
type frankenPHPContext struct {
documentRoot string
splitPath []string
env PreparedEnv
logger *slog.Logger
request *http.Request
originalRequest *http.Request
docURI string
pathInfo string
scriptName string
scriptFilename string
workerName string
// Whether the request is already closed by us
isDone bool
responseWriter http.ResponseWriter
done chan interface{}
startedAt time.Time
}
// fromContext extracts the frankenPHPContext from a context.
func fromContext(ctx context.Context) (fctx *frankenPHPContext, ok bool) {
fctx, ok = ctx.Value(contextKey).(*frankenPHPContext)
return
}
// NewRequestWithContext creates a new FrankenPHP request context.
func NewRequestWithContext(r *http.Request, opts ...RequestOption) (*http.Request, error) {
fc := &frankenPHPContext{
done: make(chan interface{}),
startedAt: time.Now(),
request: r,
}
for _, o := range opts {
if err := o(fc); err != nil {
return nil, err
}
}
if fc.logger == nil {
fc.logger = logger
}
if fc.documentRoot == "" {
if EmbeddedAppPath != "" {
fc.documentRoot = EmbeddedAppPath
} else {
var err error
if fc.documentRoot, err = os.Getwd(); err != nil {
return nil, err
}
}
}
if fc.splitPath == nil {
fc.splitPath = []string{".php"}
}
if fc.env == nil {
fc.env = make(map[string]string)
}
if splitPos := splitPos(fc, r.URL.Path); splitPos > -1 {
fc.docURI = r.URL.Path[:splitPos]
fc.pathInfo = r.URL.Path[splitPos:]
// Strip PATH_INFO from SCRIPT_NAME
fc.scriptName = strings.TrimSuffix(r.URL.Path, fc.pathInfo)
// Ensure the SCRIPT_NAME has a leading slash for compliance with RFC3875
// Info: https://tools.ietf.org/html/rfc3875#section-4.1.13
if fc.scriptName != "" && !strings.HasPrefix(fc.scriptName, "/") {
fc.scriptName = "/" + fc.scriptName
}
}
// SCRIPT_FILENAME is the absolute path of SCRIPT_NAME
fc.scriptFilename = sanitizedPathJoin(fc.documentRoot, fc.scriptName)
c := context.WithValue(r.Context(), contextKey, fc)
return r.WithContext(c), nil
}
func newDummyContext(requestPath string, opts ...RequestOption) (*frankenPHPContext, error) {
r, err := http.NewRequest(http.MethodGet, requestPath, nil)
if err != nil {
return nil, err
}
fr, err := NewRequestWithContext(r, opts...)
if err != nil {
return nil, err
}
fc, _ := fromContext(fr.Context())
return fc, nil
}
// closeContext sends the response to the client
func (fc *frankenPHPContext) closeContext() {
if fc.isDone {
return
}
close(fc.done)
fc.isDone = true
}
// validate checks if the request should be outright rejected
func (fc *frankenPHPContext) validate() bool {
if !strings.Contains(fc.request.URL.Path, "\x00") {
return true
}
fc.rejectBadRequest("Invalid request path")
return false
}
func (fc *frankenPHPContext) clientHasClosed() bool {
select {
case <-fc.request.Context().Done():
return true
default:
return false
}
}
// reject sends a response with the given status code and message
func (fc *frankenPHPContext) reject(statusCode int, message string) {
if fc.isDone {
return
}
rw := fc.responseWriter
if rw != nil {
rw.WriteHeader(statusCode)
_, _ = rw.Write([]byte(message))
if f, ok := rw.(http.Flusher); ok {
f.Flush()
}
}
fc.closeContext()
}
func (fc *frankenPHPContext) rejectBadRequest(message string) {
fc.reject(http.StatusBadRequest, message)
}

46
debugstate.go Normal file
View File

@@ -0,0 +1,46 @@
package frankenphp
// EXPERIMENTAL: ThreadDebugState prints the state of a single PHP thread - debugging purposes only
type ThreadDebugState struct {
Index int
Name string
State string
IsWaiting bool
IsBusy bool
WaitingSinceMilliseconds int64
}
// EXPERIMENTAL: FrankenPHPDebugState prints the state of all PHP threads - debugging purposes only
type FrankenPHPDebugState struct {
ThreadDebugStates []ThreadDebugState
ReservedThreadCount int
}
// EXPERIMENTAL: DebugState prints the state of all PHP threads - debugging purposes only
func DebugState() FrankenPHPDebugState {
fullState := FrankenPHPDebugState{
ThreadDebugStates: make([]ThreadDebugState, 0, len(phpThreads)),
ReservedThreadCount: 0,
}
for _, thread := range phpThreads {
if thread.state.is(stateReserved) {
fullState.ReservedThreadCount++
continue
}
fullState.ThreadDebugStates = append(fullState.ThreadDebugStates, threadDebugState(thread))
}
return fullState
}
// threadDebugState creates a small jsonable status message for debugging purposes
func threadDebugState(thread *phpThread) ThreadDebugState {
return ThreadDebugState{
Index: thread.threadIndex,
Name: thread.name(),
State: thread.state.name(),
IsWaiting: thread.state.isInWaitingState(),
IsBusy: !thread.state.isInWaitingState(),
WaitingSinceMilliseconds: thread.state.waitTime(),
}
}

View File

@@ -1,8 +1,9 @@
# syntax=docker/dockerfile:1
#checkov:skip=CKV_DOCKER_2
#checkov:skip=CKV_DOCKER_3
FROM golang:1.22-alpine
FROM golang:1.24-alpine
ENV GOTOOLCHAIN=local
ENV CFLAGS="-ggdb3"
ENV PHPIZE_DEPS="\
autoconf \
@@ -50,18 +51,21 @@ WORKDIR /usr/local/src/php
RUN git clone --branch=PHP-8.4 https://github.com/php/php-src.git . && \
# --enable-embed is only necessary to generate libphp.so, we don't use this SAPI directly
./buildconf --force && \
./configure \
EXTENSION_DIR=/usr/lib/frankenphp/modules ./configure \
--enable-embed \
--enable-zts \
--disable-zend-signals \
--enable-zend-max-execution-timers \
--with-config-file-path=/etc/frankenphp/php.ini \
--with-config-file-scan-dir=/etc/frankenphp/php.d \
--enable-debug && \
make -j"$(nproc)" && \
make install && \
ldconfig /etc/ld.so.conf.d && \
cp php.ini-development /usr/local/lib/php.ini && \
echo "zend_extension=opcache.so" >> /usr/local/lib/php.ini && \
echo "opcache.enable=1" >> /usr/local/lib/php.ini && \
mkdir -p /etc/frankenphp/php.d && \
cp php.ini-development /etc/frankenphp/php.ini && \
echo "zend_extension=opcache.so" >> /etc/frankenphp/php.ini && \
echo "opcache.enable=1" >> /etc/frankenphp/php.ini && \
php --version
# Install e-dant/watcher (necessary for file watching)

View File

@@ -1,8 +1,9 @@
# syntax=docker/dockerfile:1
#checkov:skip=CKV_DOCKER_2
#checkov:skip=CKV_DOCKER_3
FROM golang:1.22
FROM golang:1.24
ENV GOTOOLCHAIN=local
ENV CFLAGS="-ggdb3"
ENV PHPIZE_DEPS="\
autoconf \
@@ -46,24 +47,27 @@ RUN apt-get update && \
echo 'set auto-load safe-path /' > /root/.gdbinit && \
echo '* soft core unlimited' >> /etc/security/limits.conf \
&& \
apt-get clean
apt-get clean
WORKDIR /usr/local/src/php
RUN git clone --branch=PHP-8.4 https://github.com/php/php-src.git . && \
# --enable-embed is only necessary to generate libphp.so, we don't use this SAPI directly
./buildconf --force && \
./configure \
EXTENSION_DIR=/usr/lib/frankenphp/modules ./configure \
--enable-embed \
--enable-zts \
--disable-zend-signals \
--enable-zend-max-execution-timers \
--with-config-file-path=/etc/frankenphp/php.ini \
--with-config-file-scan-dir=/etc/frankenphp/php.d \
--enable-debug && \
make -j"$(nproc)" && \
make install && \
ldconfig && \
cp php.ini-development /usr/local/lib/php.ini && \
echo "zend_extension=opcache.so" >> /usr/local/lib/php.ini && \
echo "opcache.enable=1" >> /usr/local/lib/php.ini && \
mkdir -p /etc/frankenphp/php.d && \
cp php.ini-development /etc/frankenphp/php.ini && \
echo "zend_extension=opcache.so" >> /etc/frankenphp/php.ini && \
echo "opcache.enable=1" >> /etc/frankenphp/php.ini && \
php --version
# Install e-dant/watcher (necessary for file watching)
@@ -71,7 +75,9 @@ WORKDIR /usr/local/src/watcher
RUN git clone https://github.com/e-dant/watcher . && \
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release && \
cmake --build build/ && \
cmake --install build
cmake --install build && \
cp build/libwatcher-c.so /usr/local/lib/libwatcher-c.so && \
ldconfig
WORKDIR /go/src/app
COPY . .

View File

@@ -11,7 +11,7 @@ variable "PHP_VERSION" {
}
variable "GO_VERSION" {
default = "1.23"
default = "1.24"
}
variable "SHA" {}
@@ -118,20 +118,20 @@ target "default" {
}
}
target "static-builder" {
target "static-builder-musl" {
contexts = {
golang-base = "docker-image://golang:${GO_VERSION}-alpine"
}
dockerfile = "static-builder.Dockerfile"
dockerfile = "static-builder-musl.Dockerfile"
context = "./"
platforms = [
"linux/amd64",
"linux/arm64",
]
tags = distinct(flatten([
LATEST ? "${IMAGE_NAME}:static-builder" : "",
SHA == "" || VERSION != "dev" ? "" : "${IMAGE_NAME}:static-builder-sha-${substr(SHA, 0, 7)}",
VERSION == "dev" ? [] : [for v in semver(VERSION) : "${IMAGE_NAME}:static-builder-${v}"]
LATEST ? "${IMAGE_NAME}:static-builder-musl" : "",
SHA == "" || VERSION != "dev" ? "" : "${IMAGE_NAME}:static-builder-musl-sha-${substr(SHA, 0, 7)}",
VERSION == "dev" ? [] : [for v in semver(VERSION) : "${IMAGE_NAME}:static-builder-musl-${v}"]
]))
labels = {
"org.opencontainers.image.created" = "${timestamp()}"
@@ -143,3 +143,27 @@ target "static-builder" {
}
secret = ["id=github-token,env=GITHUB_TOKEN"]
}
target "static-builder-gnu" {
dockerfile = "static-builder-gnu.Dockerfile"
context = "./"
platforms = [
"linux/amd64",
"linux/arm64"
]
tags = distinct(flatten([
LATEST ? "${IMAGE_NAME}:static-builder-gnu" : "",
SHA == "" || VERSION != "dev" ? "" : "${IMAGE_NAME}:static-builder-gnu-sha-${substr(SHA, 0, 7)}",
VERSION == "dev" ? [] : [for v in semver(VERSION) : "${IMAGE_NAME}:static-builder-gnu-${v}"]
]))
labels = {
"org.opencontainers.image.created" = "${timestamp()}"
"org.opencontainers.image.version" = VERSION
"org.opencontainers.image.revision" = SHA
}
args = {
FRANKENPHP_VERSION = VERSION
GO_VERSION = GO_VERSION
}
secret = ["id=github-token,env=GITHUB_TOKEN"]
}

View File

@@ -2,8 +2,10 @@
Without any additional configuration, FrankenPHP operates in classic mode. In this mode, FrankenPHP functions like a traditional PHP server, directly serving PHP files. This makes it a seamless drop-in replacement for PHP-FPM or Apache with mod_php.
Similar to Caddy, FrankenPHP accepts an unlimited number of connections and uses a [fixed number of threads](config.md#caddyfile-config) to serve them. The number of accepted and queued connections is limited only by the available system resources. The PHP thread pool operates with a fixed number of threads initialized at startup, comparable to the static mode of PHP-FPM.
Similar to Caddy, FrankenPHP accepts an unlimited number of connections and uses a [fixed number of threads](config.md#caddyfile-config) to serve them. The number of accepted and queued connections is limited only by the available system resources.
The PHP thread pool operates with a fixed number of threads initialized at startup, comparable to the static mode of PHP-FPM. It's also possible to let threads [scale automatically at runtime](performance.md#max_threads), similar to the dynamic mode of PHP-FPM.
Queued connections will wait indefinitely until a PHP thread is available to serve them. To prevent that, set a reasonable [write timeout in Caddy](https://caddyserver.com/docs/caddyfile/options#timeouts).
Queued connections will wait indefinitely until a PHP thread is available to serve them. To prevent that, you can use the `max_wait_time` [configuration](config.md#caddyfile-config) to limit how long a request may wait for a free PHP thread before being rejected.
Additionally, you can set a reasonable [write timeout in Caddy](https://caddyserver.com/docs/caddyfile/options#timeouts).
Each Caddy instance will only spin up one FrankenPHP thread pool, which will be shared across all `php_server` blocks.

View File

@@ -11,9 +11,13 @@ docker build -t frankenphp-dev -f dev.Dockerfile .
docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -p 8080:8080 -p 443:443 -p 443:443/udp -v $PWD:/go/src/app -it frankenphp-dev
```
该镜像包含常用的开发工具Go、GDB、Valgrind、Neovim等
该镜像包含常用的开发工具Go、GDB、Valgrind、Neovim等并使用以下 php 设置位置
如果 docker 版本低于 23.0,则会因为 dockerignore [pattern issue](https://github.com/moby/moby/pull/42676) 而导致构建失败。将目录添加到 `.dockerignore`
- php.ini: `/etc/frankenphp/php.ini` 默认提供了一个带有开发预设的 php.ini 文件
- 附加配置文件: `/etc/frankenphp/php.d/*.ini`
- php 扩展: `/usr/lib/frankenphp/modules/`
如果您的 docker 版本低于 23.0,则会因为 dockerignore [pattern issue](https://github.com/moby/moby/pull/42676) 而导致构建失败。将目录添加到 `.dockerignore`
```patch
!testdata/*.php
@@ -114,7 +118,7 @@ docker buildx bake -f docker-bake.hcl --pull --no-cache --push
--set static-builder.args.DEBUG_SYMBOLS=1 \
--set "static-builder.platform=linux/amd64" \
static-builder
docker cp $(docker create --name static-builder dunglas/frankenphp:static-builder):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp
docker cp $(docker create --name static-builder-musl dunglas/frankenphp:static-builder-musl):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp
```
2. 将当前版本的 `frankenphp` 替换为 debug FrankenPHP 可执行文件
@@ -172,27 +176,27 @@ docker buildx bake -f docker-bake.hcl --pull --no-cache --push
```console
go test -tags watcher -c -ldflags=-w
gdb --args frankenphp.test -test.run ^MyTest$
gdb --args frankenphp.dev.test -test.run ^MyTest$
```
9. 当错误修复后,恢复所有这些更改
## 其他开发资源
* [PHP 嵌入 uWSGI](https://github.com/unbit/uwsgi/blob/master/plugins/php/php_plugin.c)
* [PHP 嵌入 NGINX Unit](https://github.com/nginx/unit/blob/master/src/nxt_php_sapi.c)
* [PHP 嵌入 Go (go-php)](https://github.com/deuill/go-php)
* [PHP 嵌入 Go (GoEmPHP)](https://github.com/mikespook/goemphp)
* [PHP 嵌入 C++](https://gist.github.com/paresy/3cbd4c6a469511ac7479aa0e7c42fea7)
* [扩展和嵌入 PHP 作者Sara Golemon](https://books.google.fr/books?id=zMbGvK17_tYC&pg=PA254&lpg=PA254#v=onepage&q&f=false)
* [TSRMLS_CC到底是什么](http://blog.golemon.com/2006/06/what-heck-is-tsrmlscc-anyway.html)
* [Mac 上的 PHP 嵌入](https://gist.github.com/jonnywang/61427ffc0e8dde74fff40f479d147db4)
* [SDL 绑定](https://pkg.go.dev/github.com/veandco/go-sdl2@v0.4.21/sdl#Main)
- [PHP 嵌入 uWSGI](https://github.com/unbit/uwsgi/blob/master/plugins/php/php_plugin.c)
- [PHP 嵌入 NGINX Unit](https://github.com/nginx/unit/blob/master/src/nxt_php_sapi.c)
- [PHP 嵌入 Go (go-php)](https://github.com/deuill/go-php)
- [PHP 嵌入 Go (GoEmPHP)](https://github.com/mikespook/goemphp)
- [PHP 嵌入 C++](https://gist.github.com/paresy/3cbd4c6a469511ac7479aa0e7c42fea7)
- [扩展和嵌入 PHP 作者Sara Golemon](https://books.google.fr/books?id=zMbGvK17_tYC&pg=PA254&lpg=PA254#v=onepage&q&f=false)
- [TSRMLS_CC到底是什么](http://blog.golemon.com/2006/06/what-heck-is-tsrmlscc-anyway.html)
- [Mac 上的 PHP 嵌入](https://gist.github.com/jonnywang/61427ffc0e8dde74fff40f479d147db4)
- [SDL 绑定](https://pkg.go.dev/github.com/veandco/go-sdl2@v0.4.21/sdl#Main)
## Docker 相关资源
* [Bake 文件定义](https://docs.docker.com/build/customize/bake/file-definition/)
* [docker buildx 构建](https://docs.docker.com/engine/reference/commandline/buildx_build/)
- [Bake 文件定义](https://docs.docker.com/build/customize/bake/file-definition/)
- [docker buildx 构建](https://docs.docker.com/engine/reference/commandline/buildx_build/)
## 有用的命令

View File

@@ -80,7 +80,7 @@ CGO_ENABLED=1 \
XCADDY_GO_BUILD_FLAGS="-ldflags '-w -s'" \
xcaddy build \
--output frankenphp \
--with github.com/dunglas/frankenphp/caddy \
--with frankenphp.dev/caddy \
--with github.com/dunglas/caddy-cbrotli \
--with github.com/dunglas/mercure/caddy \
--with github.com/dunglas/vulcain/caddy

View File

@@ -1,13 +1,18 @@
# 配置
# 配置
FrankenPHPCaddy 以及 Mercure 和 Vulcain 模块可以使用 [Caddy 支持的格式](https://caddyserver.com/docs/getting-started#your-first-config) 进行配置。
在[Docker 映像](docker.md) 中,`Caddyfile` 位于 `/etc/caddy/Caddyfile`
在[Docker 映像](docker.md) 中,`Caddyfile` 位于 `/etc/frankenphp/Caddyfile`
静态二进制文件会在启动时所在的目录中查找 `Caddyfile`
PHP 本身可以[使用 `php.ini` 文件](https://www.php.net/manual/zh/configuration.file.php)进行配置。
默认情况下,随 Docker 映像提供的 PHP 和静态二进制文件中包含的 PHP 将在启动 FrankenPHP 的目录和 `/usr/local/etc/php/` 中查找`php.ini` 文件。它们还会从 `/usr/local/etc/php/conf.d/` 中加载所有以 `.ini` 结尾的文件。
默认情况下没有 `php.ini` 文件,因此应复制 PHP 项目提供的官方模板。
Docker 上,模板在镜像中提供:
PHP 解释器将在以下位置查找:
Docker:
- php.ini: `/usr/local/etc/php/php.ini` 默认情况下不提供 php.ini。
- 附加配置文件: `/usr/local/etc/php/conf.d/*.ini`
- php 扩展: `/usr/local/lib/php/extensions/no-debug-zts-<YYYYMMDD>/`
- 您应该复制 PHP 项目提供的官方模板:
```dockerfile
FROM dunglas/frankenphp
@@ -19,20 +24,26 @@ RUN cp $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini
RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini
```
如果不使用 Docker请复制[PHP 源代码](https://github.com/php/php-src/)中提供的`php.ini-production``php.ini-development`中的一个。
FrankenPHP 安装 (.rpm 或 .deb):
- php.ini: `/etc/frankenphp/php.ini` 默认情况下提供带有生产预设的 php.ini 文件。
- 附加配置文件: `/etc/frankenphp/php.d/*.ini`
- php 扩展: `/usr/lib/frankenphp/modules/`
静态二进制:
- php.ini: 执行 `frankenphp run``frankenphp php-server` 的目录,然后是 `/etc/frankenphp/php.ini`
- 附加配置文件: `/etc/frankenphp/php.d/*.ini`
- php 扩展: 无法加载
- 复制[PHP 源代码](https://github.com/php/php-src/)中提供的`php.ini-production``php.ini-development`中的一个。
## Caddyfile 配置
要注册 FrankenPHP 执行器,必须设置 `frankenphp` [全局选项](https://caddyserver.com/docs/caddyfile/concepts#global-options),然后可以在站点块中使用 `php_server``php` [HTTP 指令](https://caddyserver.com/docs/caddyfile/concepts#directives) 来为您的 PHP 应用程序提供服务。
可以在站点块中使用 `php_server``php` [HTTP 指令](https://caddyserver.com/docs/caddyfile/concepts#directives) 来为您的 PHP 应用程序提供服务。
最小示例:
```caddyfile
{
# 启用 FrankenPHP
frankenphp
}
localhost {
# 启用压缩(可选)
encode zstd br gzip
@@ -41,7 +52,8 @@ localhost {
}
```
或者,可以在全局选项下指定要创建的线程数和要从服务器启动的 [worker 脚本](worker.md)。
您也可以使用全局选项显式配置 FrankenPHP
`frankenphp` [全局选项](https://caddyserver.com/docs/caddyfile/concepts#global-options) 可用于配置 FrankenPHP。
```caddyfile
{
@@ -73,21 +85,18 @@ localhost {
如果在同一服务器上运行多个应用,还可以定义多个 worker
```caddyfile
{
frankenphp {
worker /path/to/app/public/index.php <num>
worker /path/to/other/public/index.php <num>
app.example.com {
php_server {
root /path/to/app/public
worker index.php <num>
}
}
app.example.com {
root * /path/to/app/public
php_server
}
other.example.com {
root * /path/to/other/public
php_server
php_server {
root /path/to/other/public
worker index.php <num>
}
}
# ...
```
@@ -123,9 +132,18 @@ route {
```caddyfile
php_server [<matcher>] {
root <directory> # 设置站点的根目录。默认值:`root` 指令。
split_path <delim...> # 设置用于将 URI 拆分为两部分的子字符串。第一个匹配的子字符串将用于从路径中拆分路径信息。第一个部分以匹配的子字符串为后缀,并将假定为实际资源(CGI 脚本)名称。第二部分将设置为PATH_INFO供脚本使用。默认值`.php`
split_path <delim...> # 设置用于将 URI 拆分为两部分的子字符串。第一个匹配的子字符串将用于从路径中拆分"路径信息"。第一个部分以匹配的子字符串为后缀,并将假定为实际资源(CGI 脚本)名称。第二部分将设置为PATH_INFO供脚本使用。默认值`.php`
resolve_root_symlink false # 禁用将 `root` 目录在符号链接时将其解析为实际值(默认启用)。
env <key> <value> # 设置额外的环境变量,可以设置多个环境变量。
file_server off # 禁用内置的 file_server 指令。
worker { # 创建特定于此服务器的 worker。可以为多个 worker 多次指定。
file <path> # 设置 worker 脚本的路径,可以相对于 php_server 根目录
num <num> # 设置要启动的 PHP 线程数,默认为可用 CPU 数的 2 倍
name <name> # 为 worker 设置名称用于日志和指标。默认值worker 文件的绝对路径。在 php_server 块中定义时始终以 m# 开头。
watch <path> # 设置要监视文件更改的路径。可以为多个路径多次指定。
env <key> <value> # 将额外的环境变量设置为给定值。可以为多个环境变量多次指定。此 worker 的环境变量也从 php_server 父级继承,但可以在此处覆盖。
}
worker <other_file> <num> # 也可以像在全局 frankenphp 块中一样使用简短形式。
}
```
@@ -133,9 +151,9 @@ php_server [<matcher>] {
以下环境变量可用于在 `Caddyfile` 中注入 Caddy 指令,而无需对其进行修改:
* `SERVER_NAME`: 更改 [要监听的地址](https://caddyserver.com/docs/caddyfile/concepts#addresses),提供的主机名也将用于生成的 TLS 证书
* `CADDY_GLOBAL_OPTIONS`: 注入 [全局选项](https://caddyserver.com/docs/caddyfile/options)
* `FRANKENPHP_CONFIG`: 在 `frankenphp` 指令下注入配置
- `SERVER_NAME`: 更改 [要监听的地址](https://caddyserver.com/docs/caddyfile/concepts#addresses),提供的主机名也将用于生成的 TLS 证书
- `CADDY_GLOBAL_OPTIONS`: 注入 [全局选项](https://caddyserver.com/docs/caddyfile/options)
- `FRANKENPHP_CONFIG`: 在 `frankenphp` 指令下注入配置
## PHP 配置

View File

@@ -57,8 +57,8 @@ RUN CGO_ENABLED=1 \
CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \
xcaddy build \
--output /usr/local/bin/frankenphp \
--with github.com/dunglas/frankenphp=./ \
--with github.com/dunglas/frankenphp/caddy=./caddy/ \
--with frankenphp.dev=./ \
--with frankenphp.dev/caddy=./caddy/ \
--with github.com/dunglas/caddy-cbrotli \
# Mercure 和 Vulcain 包含在官方版本中,如果不需要你可以删除它们
--with github.com/dunglas/mercure/caddy \

View File

@@ -27,7 +27,7 @@ docker run -p 80:80 -p 443:443 -p 443:443/udp -v $PWD:/app dunglas/frankenphp
# 服务器的域名
localhost {
# 将 webroot 设置为 public/ 目录
root * public/
root public/
# 启用压缩(可选)
encode zstd br gzip
# 执行当前目录中的 PHP 文件并提供资产

View File

@@ -13,7 +13,7 @@ FrankenPHP 还支持 [将 PHP 应用程序嵌入到静态二进制文件中](emb
```console
docker buildx bake --load static-builder
docker cp $(docker create --name static-builder dunglas/frankenphp:static-builder):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp ; docker rm static-builder
docker cp $(docker create --name static-builder-musl dunglas/frankenphp:static-builder-musl):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp ; docker rm static-builder
```
生成的静态二进制文件名为 `frankenphp`,可在当前目录中找到。

View File

@@ -3,13 +3,30 @@
This document explains how to create a FrankenPHP binary that will load PHP as a dynamic library.
This is the recommended method.
Alternatively, [static builds](static.md) can also be created.
Alternatively, [fully and mostly static builds](static.md) can also be created.
## Install PHP
FrankenPHP is compatible with PHP 8.2 and superior.
First, [get the PHP sources](https://www.php.net/downloads.php) and extract them:
### With Homebrew (Linux and Mac)
The easiest way to install a version of libphp compatible with FrankenPHP is to use the ZTS packages provided by [Homebrew PHP](https://github.com/shivammathur/homebrew-php).
First, if not already done, install [Homebrew](https://brew.sh).
Then, install the ZTS variant of PHP, Brotli (optional, for compression support) and watcher (optional, for file change detection):
```console
brew install shivammathur/php/php-zts brotli watcher
brew link --overwrite --force shivammathur/php/php-zts
```
### By Compiling PHP
Alternatively, you can compile PHP from sources with the options needed by FrankenPHP by following these steps.
~~
~~First, [get the PHP sources](https://www.php.net/downloads.php) and extract them:
```console
tar xf php-*
@@ -19,7 +36,7 @@ cd php-*/
Then, run the `configure` script with the options needed for your platform.
The following `./configure` flags are mandatory, but you can add others, for example, to compile extensions or additional features.
### Linux
#### Linux
```console
./configure \
@@ -29,13 +46,12 @@ The following `./configure` flags are mandatory, but you can add others, for exa
--enable-zend-max-execution-timers
```
### Mac
#### Mac
Use the [Homebrew](https://brew.sh/) package manager to install
`libiconv`, `bison`, `re2c` and `pkg-config`:
Use the [Homebrew](https://brew.sh/) package manager to install the required and optional dependencies:
```console
brew install libiconv bison brotli re2c pkg-config
brew install libiconv bison brotli re2c pkg-config watcher
echo 'export PATH="/opt/homebrew/opt/bison/bin:$PATH"' >> ~/.zshrc
```
@@ -43,16 +59,13 @@ Then run the configure script:
```console
./configure \
--enable-embed=static \
--enable-embed \
--enable-zts \
--disable-zend-signals \
--disable-opcache-jit \
--enable-static \
--enable-shared=no \
--with-iconv=/opt/homebrew/opt/libiconv/
```
## Compile PHP
#### Compile PHP
Finally, compile and install PHP:
@@ -73,17 +86,12 @@ Alternatively, these features can be disabled by passing build tags to the Go co
## Compile the Go App
You can now build the final binary:
```console
curl -L https://github.com/dunglas/frankenphp/archive/refs/heads/main.tar.gz | tar xz
cd frankenphp-main/caddy/frankenphp
CGO_CFLAGS=$(php-config --includes) CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" go build -tags=nobadger,nomysql,nopgx
```
You can now build the final binary.
### Using xcaddy
Alternatively, use [xcaddy](https://github.com/caddyserver/xcaddy) to compile FrankenPHP with [custom Caddy modules](https://caddyserver.com/docs/modules/):
The recommended way is to use [xcaddy](https://github.com/caddyserver/xcaddy) to compile FrankenPHP.
`xcaddy` also allows to easily add [custom Caddy modules](https://caddyserver.com/docs/modules/) and FrankenPHP extensions:
```console
CGO_ENABLED=1 \
@@ -92,10 +100,10 @@ CGO_CFLAGS=$(php-config --includes) \
CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \
xcaddy build \
--output frankenphp \
--with github.com/dunglas/frankenphp/caddy \
--with frankenphp.dev/caddy \
--with github.com/dunglas/mercure/caddy \
--with github.com/dunglas/vulcain/caddy
# Add extra Caddy modules here
# Add extra Caddy modules and FrankenPHP extensions here
```
> [!TIP]
@@ -107,3 +115,13 @@ xcaddy build \
> To do so, change the `XCADDY_GO_BUILD_FLAGS` environment variable to something like
> `XCADDY_GO_BUILD_FLAGS=$'-ldflags "-w -s -extldflags \'-Wl,-z,stack-size=0x80000\'"'`
> (change the stack size value according to your app needs).
### Without xcaddy
Alternatively, it's possible to compile FrankenPHP without `xcaddy` by using the `go` command directly:
```console
curl -L https://github.com/dunglas/frankenphp/archive/refs/heads/main.tar.gz | tar xz
cd frankenphp-main/caddy/frankenphp
CGO_CFLAGS=$(php-config --includes) CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" go build -tags=nobadger,nomysql,nopgx
```

View File

@@ -2,16 +2,20 @@
FrankenPHP, Caddy as well as the Mercure and Vulcain modules can be configured using [the formats supported by Caddy](https://caddyserver.com/docs/getting-started#your-first-config).
In [the Docker images](docker.md), the `Caddyfile` is located at `/etc/caddy/Caddyfile`.
The static binary will look for the `Caddyfile` in the directory in which it is started.
In [the Docker images](docker.md), the `Caddyfile` is located at `/etc/frankenphp/Caddyfile`.
The static binary will also look for the `Caddyfile` in the directory where the `frankenphp run` command is executed.
You can specify a custom path with the `-c` or `--config` option.
PHP itself can be configured [using a `php.ini` file](https://www.php.net/manual/en/configuration.file.php).
By default, PHP supplied with Docker images and the one included in the static binary will look for a `php.ini` file in the directory where FrankenPHP is started and in `/usr/local/etc/php/`. They will also load all files ending in `.ini` from `/usr/local/etc/php/conf.d/`.
Depending on your installation method, the PHP interpreter will look for configuration files in locations described above.
No `php.ini` file is present by default, you should copy an official template provided by the PHP project.
## Docker
On Docker, the templates are provided in the images:
- `php.ini`: `/usr/local/etc/php/php.ini` (no `php.ini` is provided by default)
- additional configuration files: `/usr/local/etc/php/conf.d/*.ini`
- PHP extensions: `/usr/local/lib/php/extensions/no-debug-zts-<YYYYMMDD>/`
- You should copy an official template provided by the PHP project:
```dockerfile
FROM dunglas/frankenphp
@@ -23,20 +27,26 @@ RUN cp $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini
RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini
```
If you don't use Docker, copy one of `php.ini-production` or `php.ini-development` provided [in the PHP sources](https://github.com/php/php-src/).
## RPM and Debian packages
- `php.ini`: `/etc/frankenphp/php.ini` (a `php.ini` file with production presets is provided by default)
- additional configuration files: `/etc/frankenphp/php.d/*.ini`
- PHP extensions: `/usr/lib/frankenphp/modules/`
## Static binary
- `php.ini`: The directory in which `frankenphp run` or `frankenphp php-server` is executed, then `/etc/frankenphp/php.ini`
- additional configuration files: `/etc/frankenphp/php.d/*.ini`
- PHP extensions: cannot be loaded, bundle them in the binary itself
- copy one of `php.ini-production` or `php.ini-development` provided [in the PHP sources](https://github.com/php/php-src/).
## Caddyfile Config
To register the FrankenPHP executor, the `frankenphp` [global option](https://caddyserver.com/docs/caddyfile/concepts#global-options) must be set, then the `php_server` or the `php` [HTTP directives](https://caddyserver.com/docs/caddyfile/concepts#directives) may be used within the site blocks to serve your PHP app.
The `php_server` or the `php` [HTTP directives](https://caddyserver.com/docs/caddyfile/concepts#directives) may be used within the site blocks to serve your PHP app.
Minimal example:
```caddyfile
{
# Enable FrankenPHP
frankenphp
}
localhost {
# Enable compression (optional)
encode zstd br gzip
@@ -45,17 +55,22 @@ localhost {
}
```
Optionally, the number of threads to create and [worker scripts](worker.md) to start with the server can be specified under the global option.
You can also explicitly configure FrankenPHP using the global option:
The `frankenphp` [global option](https://caddyserver.com/docs/caddyfile/concepts#global-options) can be used to configure FrankenPHP.
```caddyfile
{
frankenphp {
num_threads <num_threads> # Sets the number of PHP threads to start. Default: 2x the number of available CPUs.
max_threads <num_threads> # Limits the number of additional PHP threads that can be started at runtime. Default: num_threads. Can be set to 'auto'.
max_wait_time <duration> # Sets the maximum time a request may wait for a free PHP thread before timing out. Default: disabled.
php_ini <key> <value> # Set a php.ini directive. Can be used several times to set multiple directives.
worker {
file <path> # Sets the path to the worker script.
num <num> # Sets the number of PHP threads to start, defaults to 2x the number of available CPUs.
env <key> <value> # Sets an extra environment variable to the given value. Can be specified more than once for multiple environment variables.
watch <path> # Sets the path to watch for file changes. Can be specified more than once for multiple paths.
name <name> # Sets the name of the worker, used in logs and metrics. Default: absolute path of worker file
}
}
}
@@ -78,28 +93,27 @@ Alternatively, you may use the one-line short form of the `worker` option:
You can also define multiple workers if you serve multiple apps on the same server:
```caddyfile
{
frankenphp {
worker /path/to/app/public/index.php <num>
worker /path/to/other/public/index.php <num>
app.example.com {
php_server {
root /path/to/app/public
worker index.php <num>
}
}
app.example.com {
root * /path/to/app/public
php_server
}
other.example.com {
root * /path/to/other/public
php_server
php_server {
root /path/to/other/public
worker index.php <num>
}
}
# ...
```
Using the `php_server` directive is generally what you need,
but if you need full control, you can use the lower level `php` directive:
but if you need full control, you can use the lower-level `php` directive.
The `php` directive passes all input to PHP, instead of first checking whether
it's a PHP file or not. Read more about it in the [performance page](performance.md).
Using the `php_server` directive is equivalent to this configuration:
@@ -133,6 +147,14 @@ php_server [<matcher>] {
resolve_root_symlink false # Disables resolving the `root` directory to its actual value by evaluating a symbolic link, if one exists (enabled by default).
env <key> <value> # Sets an extra environment variable to the given value. Can be specified more than once for multiple environment variables.
file_server off # Disables the built-in file_server directive.
worker { # Creates a worker specific to this server. Can be specified more than once for multiple workers.
file <path> # Sets the path to the worker script, can be relative to the php_server root
num <num> # Sets the number of PHP threads to start, defaults to 2x the number of available
name <name> # Sets the name for the worker, used in logs and metrics. Default: absolute path of worker file. Always starts with m# when defined in a php_server block.
watch <path> # Sets the path to watch for file changes. Can be specified more than once for multiple paths.
env <key> <value> # Sets an extra environment variable to the given value. Can be specified more than once for multiple environment variables. Environment variables for this worker are also inherited from the php_server parent, but can be overwritten here.
}
worker <other_file> <num> # Can also use the short form like in the global frankenphp block.
}
```
@@ -174,10 +196,10 @@ where the FrankenPHP process was started. You can instead also specify one or mo
}
```
* The `**` pattern signifies recursive watching
* Directories can also be relative (to where the FrankenPHP process is started from)
* If you have multiple workers defined, all of them will be restarted when a file changes
* Be wary about watching files that are created at runtime (like logs) since they might cause unwanted worker restarts.
- The `**` pattern signifies recursive watching
- Directories can also be relative (to where the FrankenPHP process is started from)
- If you have multiple workers defined, all of them will be restarted when a file changes
- Be wary about watching files that are created at runtime (like logs) since they might cause unwanted worker restarts.
The file watcher is based on [e-dant/watcher](https://github.com/e-dant/watcher).
@@ -213,9 +235,9 @@ You can find more information about this setting in the [Caddy documentation](ht
The following environment variables can be used to inject Caddy directives in the `Caddyfile` without modifying it:
* `SERVER_NAME`: change [the addresses on which to listen](https://caddyserver.com/docs/caddyfile/concepts#addresses), the provided hostnames will also be used for the generated TLS certificate
* `CADDY_GLOBAL_OPTIONS`: inject [global options](https://caddyserver.com/docs/caddyfile/options)
* `FRANKENPHP_CONFIG`: inject config under the `frankenphp` directive
- `SERVER_NAME`: change [the addresses on which to listen](https://caddyserver.com/docs/caddyfile/concepts#addresses), the provided hostnames will also be used for the generated TLS certificate
- `CADDY_GLOBAL_OPTIONS`: inject [global options](https://caddyserver.com/docs/caddyfile/options)
- `FRANKENPHP_CONFIG`: inject config under the `frankenphp` directive
As for FPM and CLI SAPIs, environment variables are exposed by default in the `$_SERVER` superglobal.
@@ -227,6 +249,23 @@ To load [additional PHP configuration files](https://www.php.net/manual/en/confi
the `PHP_INI_SCAN_DIR` environment variable can be used.
When set, PHP will load all the file with the `.ini` extension present in the given directories.
You can also change the PHP configuration using the `php_ini` directive in the `Caddyfile`:
```caddyfile
{
frankenphp {
php_ini memory_limit 256M
# or
php_ini {
memory_limit 256M
max_execution_time 15
}
}
}
```
## Enable the Debug Mode
When using the Docker image, set the `CADDY_GLOBAL_OPTIONS` environment variable to `debug` to enable the debug mode:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 KiB

After

Width:  |  Height:  |  Size: 87 KiB

View File

@@ -6,8 +6,8 @@ Variants for PHP 8.2, 8.3 and 8.4 are provided.
The tags follow this pattern: `dunglas/frankenphp:<frankenphp-version>-php<php-version>-<os>`
* `<frankenphp-version>` and `<php-version>` are version numbers of FrankenPHP and PHP respectively, ranging from major (e.g. `1`), minor (e.g. `1.2`) to patch versions (e.g. `1.2.3`).
* `<os>` is either `bookworm` (for Debian Bookworm) or `alpine` (for the latest stable version of Alpine).
- `<frankenphp-version>` and `<php-version>` are version numbers of FrankenPHP and PHP respectively, ranging from major (e.g. `1`), minor (e.g. `1.2`) to patch versions (e.g. `1.2.3`).
- `<os>` is either `bookworm` (for Debian Bookworm) or `alpine` (for the latest stable version of Alpine).
[Browse tags](https://hub.docker.com/r/dunglas/frankenphp/tags).
@@ -65,8 +65,8 @@ RUN CGO_ENABLED=1 \
CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \
xcaddy build \
--output /usr/local/bin/frankenphp \
--with github.com/dunglas/frankenphp=./ \
--with github.com/dunglas/frankenphp/caddy=./caddy/ \
--with frankenphp.dev=./ \
--with frankenphp.dev/caddy=./caddy/ \
--with github.com/dunglas/caddy-cbrotli \
# Mercure and Vulcain are included in the official build, but feel free to remove them
--with github.com/dunglas/mercure/caddy \
@@ -193,8 +193,8 @@ Example: `:8000`
The Docker images are built:
* when a new release is tagged
* daily at 4 am UTC, if new versions of the official PHP images are available
- when a new release is tagged
- daily at 4 am UTC, if new versions of the official PHP images are available
## Development Versions

View File

@@ -14,10 +14,10 @@ Before creating the self-contained binary be sure that your app is ready for emb
For instance, you likely want to:
* Install the production dependencies of the app
* Dump the autoloader
* Enable the production mode of your application (if any)
* Strip unneeded files such as `.git` or tests to reduce the size of your final binary
- Install the production dependencies of the app
- Dump the autoloader
- Enable the production mode of your application (if any)
- Strip unneeded files such as `.git` or tests to reduce the size of your final binary
For instance, for a Symfony app, you can use the following commands:
@@ -53,34 +53,34 @@ The easiest way to create a Linux binary is to use the Docker-based builder we p
1. Create a file named `static-build.Dockerfile` in the repository of your app:
```dockerfile
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder
```dockerfile
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder
# Copy your app
WORKDIR /go/src/app/dist/app
COPY . .
# Copy your app
WORKDIR /go/src/app/dist/app
COPY . .
# Build the static binary
WORKDIR /go/src/app/
RUN EMBED=dist/app/ ./build-static.sh
```
# Build the static binary
WORKDIR /go/src/app/
RUN EMBED=dist/app/ ./build-static.sh
```
> [!CAUTION]
>
> Some `.dockerignore` files (e.g. default [Symfony Docker `.dockerignore`](https://github.com/dunglas/symfony-docker/blob/main/.dockerignore))
> will ignore the `vendor/` directory and `.env` files. Be sure to adjust or remove the `.dockerignore` file before the build.
> [!CAUTION]
>
> Some `.dockerignore` files (e.g. default [Symfony Docker `.dockerignore`](https://github.com/dunglas/symfony-docker/blob/main/.dockerignore))
> will ignore the `vendor/` directory and `.env` files. Be sure to adjust or remove the `.dockerignore` file before the build.
2. Build:
```console
docker build -t static-app -f static-build.Dockerfile .
```
```console
docker build -t static-app -f static-build.Dockerfile .
```
3. Extract the binary:
```console
docker cp $(docker create --name static-app-tmp static-app):/go/src/app/dist/frankenphp-linux-x86_64 my-app ; docker rm static-app-tmp
```
```console
docker cp $(docker create --name static-app-tmp static-app):/go/src/app/dist/frankenphp-linux-x86_64 my-app ; docker rm static-app-tmp
```
The resulting binary is the file named `my-app` in the current directory.

View File

@@ -11,9 +11,13 @@ docker build -t frankenphp-dev -f dev.Dockerfile .
docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -p 8080:8080 -p 443:443 -p 443:443/udp -v $PWD:/go/src/app -it frankenphp-dev
```
L'image contient les outils de développement habituels (Go, GDB, Valgrind, Neovim...).
L'image contient les outils de développement habituels (Go, GDB, Valgrind, Neovim...) et utilise les emplacements de configuration PHP suivants
Si la version de Docker est inférieure à 23.0, la construction échoue à cause d'un [problème de pattern](https://github.com/moby/moby/pull/42676) dans `.dockerignore`. Ajoutez les répertoires à `.dockerignore`.
- php.ini: `/etc/frankenphp/php.ini` Un fichier php.ini avec des préréglages de développement est fourni par défaut.
- fichiers de configuration supplémentaires: `/etc/frankenphp/php.d/*.ini`
- extensions php: `/usr/lib/frankenphp/modules/`
Si votre version de Docker est inférieure à 23.0, la construction échouera à cause d'un [problème de pattern](https://github.com/moby/moby/pull/42676) dans `.dockerignore`. Ajoutez les répertoires à `.dockerignore`.
```patch
!testdata/*.php
@@ -38,7 +42,7 @@ Construire Caddy avec le module FrankenPHP :
```console
cd caddy/frankenphp/
go build
go build -tags watcher,brotli,nobadger,nomysql,nopgx
cd ../../
```
@@ -49,10 +53,14 @@ cd testdata/
../caddy/frankenphp/frankenphp run
```
Le serveur est configuré pour écouter à l'adresse `127.0.0.1:8080`:
Le serveur est configuré pour écouter à l'adresse `127.0.0.1:80`:
> [!NOTE]
>
> Si vous utilisez Docker, vous devrez soit lier le port 80 du conteneur, soit exécuter depuis l'intérieur du conteneur.
```console
curl -vk https://localhost/phpinfo.php
curl -vk http://127.0.0.1/phpinfo.php
```
## Serveur de test minimal
@@ -104,69 +112,92 @@ Construire à partir de zéro les images FrankenPHP pour arm64 & amd64 et les po
docker buildx bake -f docker-bake.hcl --pull --no-cache --push
```
## Déboguer les erreurs de segmentation avec les builds statiques
1. Téléchargez la version de débogage du binaire FrankenPHP depuis GitHub ou créez votre propre build statique incluant des symboles de débogage :
```console
docker buildx bake \
--load \
--set static-builder.args.DEBUG_SYMBOLS=1 \
--set "static-builder.platform=linux/amd64" \
static-builder
docker cp $(docker create --name static-builder-musl dunglas/frankenphp:static-builder-musl):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp
```
2. Remplacez votre version actuelle de `frankenphp` par l'exécutable de débogage de FrankenPHP.
3. Démarrez FrankenPHP comme d'habitude (alternativement, vous pouvez directement démarrer FrankenPHP avec GDB : `gdb --args frankenphp run`).
4. Attachez-vous au processus avec GDB :
```console
gdb -p `pidof frankenphp`
```
5. Si nécessaire, tapez `continue` dans le shell GDB
6. Faites planter FrankenPHP.
7. Tapez `bt` dans le shell GDB
8. Copiez la sortie
## Déboguer les erreurs de segmentation dans GitHub Actions
1. Ouvrir `.github/workflows/tests.yml`
2. Activer les symboles de débogage de la bibliothèque PHP
```patch
- uses: shivammathur/setup-php@v2
# ...
env:
phpts: ts
+ debug: true
```
```patch
- uses: shivammathur/setup-php@v2
# ...
env:
phpts: ts
+ debug: true
```
3. Activer `tmate` pour se connecter au conteneur
```patch
-
name: Set CGO flags
run: echo "CGO_CFLAGS=$(php-config --includes)" >> "$GITHUB_ENV"
+ -
+ run: |
+ sudo apt install gdb
+ mkdir -p /home/runner/.config/gdb/
+ printf "set auto-load safe-path /\nhandle SIG34 nostop noprint pass" > /home/runner/.config/gdb/gdbinit
+ -
+ uses: mxschmitt/action-tmate@v3
```
```patch
- name: Set CGO flags
run: echo "CGO_CFLAGS=$(php-config --includes)" >> "$GITHUB_ENV"
+ - run: |
+ sudo apt install gdb
+ mkdir -p /home/runner/.config/gdb/
+ printf "set auto-load safe-path /\nhandle SIG34 nostop noprint pass" > /home/runner/.config/gdb/gdbinit
+ - uses: mxschmitt/action-tmate@v3
```
4. Se connecter au conteneur
5. Ouvrir `frankenphp.go`
6. Activer `cgosymbolizer`
```patch
- //_ "github.com/ianlancetaylor/cgosymbolizer"
+ _ "github.com/ianlancetaylor/cgosymbolizer"
```
```patch
- //_ "github.com/ianlancetaylor/cgosymbolizer"
+ _ "github.com/ianlancetaylor/cgosymbolizer"
```
7. Télécharger le module : `go get`
8. Dans le conteneur, vous pouvez utiliser GDB et similaires :
```console
go test -tags watcher -c -ldflags=-w
gdb --args frankenphp.test -test.run ^MyTest$
```
```console
go test -tags watcher -c -ldflags=-w
gdb --args frankenphp.dev.test -test.run ^MyTest$
```
9. Quand le bug est corrigé, annulez tous les changements
9. Quand le bug est corrigé, annulez tous les changements.
## Ressources Diverses pour le Développement
* [Intégration de PHP dans uWSGI](https://github.com/unbit/uwsgi/blob/master/plugins/php/php_plugin.c)
* [Intégration de PHP dans NGINX Unit](https://github.com/nginx/unit/blob/master/src/nxt_php_sapi.c)
* [Intégration de PHP dans Go (go-php)](https://github.com/deuill/go-php)
* [Intégration de PHP dans Go (GoEmPHP)](https://github.com/mikespook/goemphp)
* [Intégration de PHP dans C++](https://gist.github.com/paresy/3cbd4c6a469511ac7479aa0e7c42fea7)
* [Extending and Embedding PHP par Sara Golemon](https://books.google.fr/books?id=zMbGvK17_tYC&pg=PA254&lpg=PA254#v=onepage&q&f=false)
* [Qu'est-ce que TSRMLS_CC, au juste ?](http://blog.golemon.com/2006/06/what-heck-is-tsrmlscc-anyway.html)
* [Intégration de PHP sur Mac](https://gist.github.com/jonnywang/61427ffc0e8dde74fff40f479d147db4)
* [Bindings SDL](https://pkg.go.dev/github.com/veandco/go-sdl2@v0.4.21/sdl#Main)
- [Intégration de PHP dans uWSGI](https://github.com/unbit/uwsgi/blob/master/plugins/php/php_plugin.c)
- [Intégration de PHP dans NGINX Unit](https://github.com/nginx/unit/blob/master/src/nxt_php_sapi.c)
- [Intégration de PHP dans Go (go-php)](https://github.com/deuill/go-php)
- [Intégration de PHP dans Go (GoEmPHP)](https://github.com/mikespook/goemphp)
- [Intégration de PHP dans C++](https://gist.github.com/paresy/3cbd4c6a469511ac7479aa0e7c42fea7)
- [Extending and Embedding PHP par Sara Golemon](https://books.google.fr/books?id=zMbGvK17_tYC&pg=PA254&lpg=PA254#v=onepage&q&f=false)
- [Qu'est-ce que TSRMLS_CC, au juste ?](http://blog.golemon.com/2006/06/what-heck-is-tsrmlscc-anyway.html)
- [Intégration de PHP sur Mac](https://gist.github.com/jonnywang/61427ffc0e8dde74fff40f479d147db4)
- [Bindings SDL](https://pkg.go.dev/github.com/veandco/go-sdl2@v0.4.21/sdl#Main)
## Ressources Liées à Docker
* [Définition du fichier Bake](https://docs.docker.com/build/customize/bake/file-definition/)
* [docker buildx build](https://docs.docker.com/engine/reference/commandline/buildx_build/)
- [Définition du fichier Bake](https://docs.docker.com/build/customize/bake/file-definition/)
- [docker buildx build](https://docs.docker.com/engine/reference/commandline/buildx_build/)
## Commande utile
@@ -174,3 +205,16 @@ docker buildx bake -f docker-bake.hcl --pull --no-cache --push
apk add strace util-linux gdb
strace -e 'trace=!futex,epoll_ctl,epoll_pwait,tgkill,rt_sigreturn' -p 1
```
## Traduire la documentation
Pour traduire la documentation et le site dans une nouvelle langue, procédez comme suit :
1. Créez un nouveau répertoire nommé avec le code ISO à 2 caractères de la langue dans le répertoire `docs/` de ce dépôt
2. Copiez tous les fichiers `.md` à la racine du répertoire `docs/` dans le nouveau répertoire (utilisez toujours la version anglaise comme source de traduction, car elle est toujours à jour).
3. Copiez les fichiers `README.md` et `CONTRIBUTING.md` du répertoire racine vers le nouveau répertoire.
4. Traduisez le contenu des fichiers, mais ne changez pas les noms de fichiers, ne traduisez pas non plus les chaînes commençant par `> [!` (c'est un balisage spécial pour GitHub).
5. Créez une Pull Request avec les traductions
6. Dans le [référentiel du site](https://github.com/dunglas/frankenphp-website/tree/main), copiez et traduisez les fichiers de traduction dans les répertoires `content/`, `data/` et `i18n/`.
7. Traduire les valeurs dans le fichier YAML créé.
8. Ouvrir une Pull Request sur le dépôt du site.

View File

@@ -4,7 +4,7 @@
FrankenPHP est un serveur d'applications moderne pour PHP construit à partir du serveur web [Caddy](https://caddyserver.com/).
FrankenPHP donne des super-pouvoirs à vos applications PHP grâce à ses fonctionnalités à la pointe : [*Early Hints*](early-hints.md), [mode worker](worker.md), [fonctionnalités en temps réel](mercure.md), HTTPS automatique, prise en charge de HTTP/2 et HTTP/3...
FrankenPHP donne des super-pouvoirs à vos applications PHP grâce à ses fonctionnalités à la pointe : [_Early Hints_](early-hints.md), [mode worker](worker.md), [fonctionnalités en temps réel](mercure.md), HTTPS automatique, prise en charge de HTTP/2 et HTTP/3...
FrankenPHP fonctionne avec n'importe quelle application PHP et rend vos projets Laravel et Symfony plus rapides que jamais grâce à leurs intégrations officielles avec le mode worker.
@@ -16,21 +16,6 @@ Découvrez plus de détails sur ce serveur dapplication dans le replay de cet
## Pour Commencer
### Docker
```console
docker run -v $PWD:/app/public \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
Rendez-vous sur `https://localhost`, c'est parti !
> [!TIP]
>
> Ne tentez pas d'utiliser `https://127.0.0.1`. Utilisez `https://localhost` et acceptez le certificat auto-signé.
> Utilisez [la variable d'environnement `SERVER_NAME`](config.md#variables-denvironnement) pour changer le domaine à utiliser.
### Binaire autonome
Si vous préférez ne pas utiliser Docker, nous fournissons des binaires autonomes de FrankenPHP pour Linux et macOS
@@ -57,32 +42,68 @@ Vous pouvez également exécuter des scripts en ligne de commande avec :
frankenphp php-cli /path/to/your/script.php
```
### Docker
Des [images Docker](https://frankenphp.dev/docs/fr/docker/) sont également disponibles :
```console
docker run -v .:/app/public \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
Rendez-vous sur `https://localhost`, c'est parti !
> [!TIP]
>
> Ne tentez pas d'utiliser `https://127.0.0.1`. Utilisez `https://localhost` et acceptez le certificat auto-signé.
> Utilisez [la variable d'environnement `SERVER_NAME`](config.md#variables-denvironnement) pour changer le domaine à utiliser.
### Homebrew
FrankenPHP est également disponible sous forme de paquet [Homebrew](https://brew.sh) pour macOS et Linux.
Pour l'installer :
```console
brew install dunglas/frankenphp/frankenphp
```
Pour servir le contenu du répertoire courant, exécutez :
```console
frankenphp php-server
```
## Documentation
* [Le mode worker](worker.md)
* [Le support des Early Hints (code de statut HTTP 103)](early-hints.md)
* [Temps réel](mercure.md)
* [Configuration](config.md)
* [Images Docker](docker.md)
* [Déploiement en production](production.md)
* [Optimisation des performances](performance.md)
* [Créer des applications PHP **standalone**, auto-exécutables](embed.md)
* [Créer un build statique](static.md)
* [Compiler depuis les sources](compile.md)
* [Intégration Laravel](laravel.md)
* [Problèmes connus](known-issues.md)
* [Application de démo (Symfony) et benchmarks](https://github.com/dunglas/frankenphp-demo)
* [Documentation de la bibliothèque Go](https://pkg.go.dev/github.com/dunglas/frankenphp)
* [Contribuer et débugger](CONTRIBUTING.md)
- [Le mode classique](classic.md)
- [Le mode worker](worker.md)
- [Le support des Early Hints (code de statut HTTP 103)](early-hints.md)
- [Temps réel](mercure.md)
- [Servir efficacement les fichiers statiques volumineux](x-sendfile.md)
- [Configuration](config.md)
- [Images Docker](docker.md)
- [Déploiement en production](production.md)
- [Optimisation des performances](performance.md)
- [Créer des applications PHP **standalone**, auto-exécutables](embed.md)
- [Créer un build statique](static.md)
- [Compiler depuis les sources](compile.md)
- [Surveillance de FrankenPHP](metrics.md)
- [Intégration Laravel](laravel.md)
- [Problèmes connus](known-issues.md)
- [Application de démo (Symfony) et benchmarks](https://github.com/dunglas/frankenphp-demo)
- [Documentation de la bibliothèque Go](https://pkg.go.dev/github.com/dunglas/frankenphp)
- [Contribuer et débugger](CONTRIBUTING.md)
## Exemples et squelettes
* [Symfony](https://github.com/dunglas/symfony-docker)
* [API Platform](https://api-platform.com/docs/distribution/)
* [Laravel](laravel.md)
* [Sulu](https://sulu.io/blog/running-sulu-with-frankenphp)
* [WordPress](https://github.com/StephenMiracle/frankenwp)
* [Drupal](https://github.com/dunglas/frankenphp-drupal)
* [Joomla](https://github.com/alexandreelise/frankenphp-joomla)
* [TYPO3](https://github.com/ochorocho/franken-typo3)
* [Magento2](https://github.com/ekino/frankenphp-magento2)
- [Symfony](https://github.com/dunglas/symfony-docker)
- [API Platform](https://api-platform.com/docs/distribution/)
- [Laravel](laravel.md)
- [Sulu](https://sulu.io/blog/running-sulu-with-frankenphp)
- [WordPress](https://github.com/StephenMiracle/frankenwp)
- [Drupal](https://github.com/dunglas/frankenphp-drupal)
- [Joomla](https://github.com/alexandreelise/frankenphp-joomla)
- [TYPO3](https://github.com/ochorocho/franken-typo3)
- [Magento2](https://github.com/ekino/frankenphp-magento2)

11
docs/fr/classic.md Normal file
View File

@@ -0,0 +1,11 @@
# Utilisation du mode classique
Sans aucune configuration additionnelle, FrankenPHP fonctionne en mode classique. Dans ce mode, FrankenPHP fonctionne comme un serveur PHP traditionnel, en servant directement les fichiers PHP. Cela en fait un remplaçant parfait à PHP-FPM ou Apache avec mod_php.
Comme Caddy, FrankenPHP accepte un nombre illimité de connexions et utilise un [nombre fixe de threads](config.md#configuration-du-caddyfile) pour les servir. Le nombre de connexions acceptées et en attente n'est limité que par les ressources système disponibles.
Le pool de threads PHP fonctionne avec un nombre fixe de threads initialisés au démarrage, comparable au mode statique de PHP-FPM. Il est également possible de laisser les threads [s'adapter automatiquement à l'exécution](performance.md#max_threads), comme dans le mode dynamique de PHP-FPM.
Les connexions en file d'attente attendront indéfiniment jusqu'à ce qu'un thread PHP soit disponible pour les servir. Pour éviter cela, vous pouvez utiliser la [configuration](config.md#configuration-du-caddyfile) `max_wait_time` pour limiter la durée pendant laquelle une requête peut attendre un thread PHP libre avant d'être rejetée.
En outre, vous pouvez définir un [délai d'écriture dans Caddy](https://caddyserver.com/docs/caddyfile/options#timeouts) raisonnable.
Chaque instance de Caddy n'utilisera qu'un seul pool de threads FrankenPHP, qui sera partagé par tous les blocs `php_server`.

View File

@@ -9,6 +9,23 @@ Alternativement, il est aussi possible de [créer des builds statiques](static.m
FrankenPHP est compatible avec PHP 8.2 et versions ultérieures.
### Avec Homebrew (Linux et Mac)
La manière la plus simple d'installer une version de libphp compatible avec FrankenPHP est d'utiliser les paquets ZTS fournis par [Homebrew PHP](https://github.com/shivammathur/homebrew-php).
Tout d'abord, si ce n'est déjà fait, installez [Homebrew](https://brew.sh).
Ensuite, installez la variante ZTS de PHP, Brotli (facultatif, pour la prise en charge de la compression) et watcher (facultatif, pour la détection des modifications de fichiers) :
```console
brew install shivammathur/php/php-zts brotli watcher
brew link --overwrite --force shivammathur/php/php-zts
```
### En compilant PHP
Vous pouvez également compiler PHP à partir des sources avec les options requises par FrankenPHP en suivant ces étapes.
Tout d'abord, [téléchargez les sources de PHP](https://www.php.net/downloads.php) et extrayez-les :
```console
@@ -32,10 +49,10 @@ Les options de configuration suivantes sont nécessaires pour la compilation, ma
### Mac
Utilisez le gestionnaire de paquets [Homebrew](https://brew.sh/) pour installer `libiconv`, `bison`, `re2c` et `pkg-config` :
Utilisez le gestionnaire de paquets [Homebrew](https://brew.sh/) pour installer les dépendances obligatoires et optionnelles :
```console
brew install libiconv bison re2c pkg-config
brew install libiconv bison brotli re2c pkg-config watcher
echo 'export PATH="/opt/homebrew/opt/bison/bin:$PATH"' >> ~/.zshrc
```
@@ -43,16 +60,14 @@ Puis exécutez le script de configuration :
```console
./configure \
--enable-embed=static \
--enable-embed \
--enable-zts \
--disable-zend-signals \
--disable-opcache-jit \
--enable-static \
--enable-shared=no \
--with-iconv=/opt/homebrew/opt/libiconv/
```
## Compilez PHP
### Compilez PHP
Finalement, compilez et installez PHP :
@@ -61,30 +76,35 @@ make -j"$(getconf _NPROCESSORS_ONLN)"
sudo make install
```
## Installez les dépendances optionnelles
Certaines fonctionnalités de FrankenPHP nécessitent des dépendances optionnelles qui doivent être installées.
Ces fonctionnalités peuvent également être désactivées en passant des tags de compilation au compilateur Go.
| Fonctionnalité | Dépendance | Tag de compilation pour la désactiver |
|---------------------------------------------------------|-----------------------------------------------------------------------|---------------------------------------|
| Compression Brotli | [Brotli](https://github.com/google/brotli) | nobrotli |
| Redémarrage des workers en cas de changement de fichier | [Watcher C](https://github.com/e-dant/watcher/tree/release/watcher-c) | nowatcher |
## Compiler l'application Go
Vous pouvez maintenant compilez FrankenPHP :
```console
curl -L https://github.com/dunglas/frankenphp/archive/refs/heads/main.tar.gz | tar xz
cd frankenphp-main/caddy/frankenphp
CGO_CFLAGS=$(php-config --includes) CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" go build
```
### Utiliser xcaddy
Alternativement, Vous pouvez utiliser [xcaddy](https://github.com/caddyserver/xcaddy) pour compiler FrankenPHP avec [des modules Caddy additionnels](https://caddyserver.com/docs/modules/):
La méthode recommandée consiste à utiliser [xcaddy](https://github.com/caddyserver/xcaddy) pour compiler FrankenPHP.
`xcaddy` permet également d'ajouter facilement des [modules Caddy personnalisés](https://caddyserver.com/docs/modules/) et des extensions FrankenPHP :
```console
CGO_ENABLED=1 \
XCADDY_GO_BUILD_FLAGS="-ldflags '-w -s'" \
XCADDY_GO_BUILD_FLAGS="-ldflags='-w -s' -tags=nobadger,nomysql,nopgx" \
CGO_CFLAGS=$(php-config --includes) \
CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \
xcaddy build \
--output frankenphp \
--with github.com/dunglas/frankenphp/caddy \
--with frankenphp.dev/caddy \
--with github.com/dunglas/caddy-cbrotli \
--with github.com/dunglas/mercure/caddy \
--with github.com/dunglas/vulcain/caddy
# Add extra Caddy modules here
# Ajoutez les modules Caddy supplémentaires et les extensions FrankenPHP ici
```
> [!TIP]
@@ -96,3 +116,13 @@ xcaddy build \
> Pour ce faire, modifiez la variable d'environnement `XCADDY_GO_BUILD_FLAGS` en quelque chose comme
> `XCADDY_GO_BUILD_FLAGS=$'-ldflags "-w -s -extldflags \'-Wl,-z,stack-size=0x80000\'"'`
> (modifiez la valeur de la taille de la pile selon les besoins de votre application).
### Sans xcaddy
Il est également possible de compiler FrankenPHP sans `xcaddy` en utilisant directement la commande `go` :
```console
curl -L https://github.com/dunglas/frankenphp/archive/refs/heads/main.tar.gz | tar xz
cd frankenphp-main/caddy/frankenphp
CGO_CFLAGS=$(php-config --includes) CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" go build -tags=nobadger,nomysql,nopgx
```

View File

@@ -1,15 +1,20 @@
# Configuration
# Configuration
FrankenPHP, Caddy ainsi que les modules Mercure et Vulcain peuvent être configurés en utilisant [les formats pris en charge par Caddy](https://caddyserver.com/docs/getting-started#your-first-config).
Dans [les images Docker](docker.md), le `Caddyfile` est situé dans `/etc/caddy/Caddyfile`.
Dans [les images Docker](docker.md), le `Caddyfile` est situé dans `/etc/frankenphp/Caddyfile`.
Le binaire statique cherchera le `Caddyfile` dans le répertoire dans lequel il est démarré.
PHP lui-même peut être configuré [en utilisant un fichier `php.ini`](https://www.php.net/manual/fr/configuration.file.php).
Par défaut, le PHP fourni avec les images Docker et celui inclus dans le binaire statique cherchera un fichier `php.ini` dans le répertoire où FrankenPHP est démarré et dans `/usr/local/etc/php/`. Ils chargeront également tous les fichiers se terminant par `.ini` dans `/usr/local/etc/php/conf.d/`.
L'interpréteur PHP cherchera dans les emplacements suivants :
Aucun fichier `php.ini` n'est présent par défaut, vous devriez copier un modèle officiel fourni par le projet PHP.
Sur Docker, les modèles sont fournis dans les images :
Docker :
- php.ini : `/usr/local/etc/php/php.ini` Aucun php.ini n'est fourni par défaut.
- fichiers de configuration supplémentaires : `/usr/local/etc/php/conf.d/*.ini`
- extensions php : `/usr/local/lib/php/extensions/no-debug-zts-<YYYYMMDD>/`
- Vous devriez copier un modèle officiel fourni par le projet PHP :
```dockerfile
FROM dunglas/frankenphp
@@ -21,40 +26,52 @@ RUN cp $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini
RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini
```
Si vous n'utilisez pas Docker, copiez l'un des fichiers `php.ini-production` ou `php.ini-development` fournis [dans les sources de PHP](https://github.com/php/php-src/).
Installation de FrankenPHP (.rpm ou .deb) :
- php.ini : `/etc/frankenphp/php.ini` Un fichier php.ini avec des préréglages de production est fourni par défaut.
- fichiers de configuration supplémentaires : `/etc/frankenphp/php.d/*.ini`
- extensions php : `/usr/lib/frankenphp/modules/`
Binaire statique :
- php.ini : Le répertoire dans lequel `frankenphp run` ou `frankenphp php-server` est exécuté, puis `/etc/frankenphp/php.ini`
- fichiers de configuration supplémentaires : `/etc/frankenphp/php.d/*.ini`
- extensions php : ne peuvent pas être chargées
- copiez l'un des fichiers `php.ini-production` ou `php.ini-development` fournis [dans les sources de PHP](https://github.com/php/php-src/).
## Configuration du Caddyfile
Pour enregistrer l'exécutable de FrankenPHP, l'[option globale](https://caddyserver.com/docs/caddyfile/concepts#global-options) `frankenphp` doit être définie, puis les [directives HTTP](https://caddyserver.com/docs/caddyfile/concepts#directives) `php_server` ou `php` peuvent être utilisées dans les blocs de site pour servir votre application PHP.
Les [directives HTTP](https://caddyserver.com/docs/caddyfile/concepts#directives) `php_server` ou `php` peuvent être utilisées dans les blocs de site pour servir votre application PHP.
Exemple minimal :
```caddyfile
{
# Activer FrankenPHP
frankenphp
}
localhost {
# Activer la compression (optionnel)
encode zstd br gzip
# Exécuter les fichiers PHP dans le répertoire courant et servir les assets
php_server
# Activer la compression (optionnel)
encode zstd br gzip
# Exécuter les fichiers PHP dans le répertoire courant et servir les assets
php_server
}
```
En option, le nombre de threads à créer et les [workers](worker.md) à démarrer avec le serveur peuvent être spécifiés sous l'option globale.
Vous pouvez également configurer explicitement FrankenPHP en utilisant l'option globale :
L'[option globale](https://caddyserver.com/docs/caddyfile/concepts#global-options) `frankenphp` peut être utilisée pour configurer FrankenPHP.
```caddyfile
{
frankenphp {
num_threads <num_threads> # Définit le nombre de threads PHP à démarrer. Par défaut : 2x le nombre de CPUs disponibles.
worker {
file <path> # Définit le chemin vers le script worker.
num <num> # Définit le nombre de threads PHP à démarrer, par défaut 2x le nombre de CPUs disponibles.
env <key> <value> # Définit une variable d'environnement supplémentaire avec la valeur donnée. Peut être spécifié plusieurs fois pour régler plusieurs variables d'environnement.
}
}
frankenphp {
num_threads <num_threads> # Définit le nombre de threads PHP à démarrer. Par défaut : 2x le nombre de CPUs disponibles.
max_threads <num_threads> # Limite le nombre de threads PHP supplémentaires qui peuvent être démarrés au moment de l'exécution. Valeur par défaut : num_threads. Peut être mis à 'auto'.
max_wait_time <duration> # Définit le temps maximum pendant lequel une requête peut attendre un thread PHP libre avant d'être interrompue. Valeur par défaut : désactivé.
php_ini <key> <value> Définit une directive php.ini. Peut être utilisé plusieurs fois pour définir plusieurs directives.
worker {
file <path> # Définit le chemin vers le script worker.
num <num> # Définit le nombre de threads PHP à démarrer, par défaut 2x le nombre de CPUs disponibles.
env <key> <value> # Définit une variable d'environnement supplémentaire avec la valeur donnée. Peut être spécifié plusieurs fois pour régler plusieurs variables d'environnement.
watch <path> # Définit le chemin d'accès à surveiller pour les modifications de fichiers. Peut être spécifié plusieurs fois pour plusieurs chemins.
name <name> # Définit le nom du worker, utilisé dans les journaux et les métriques. Défaut : chemin absolu du fichier du worker
}
}
}
# ...
@@ -64,9 +81,9 @@ Vous pouvez également utiliser la forme courte de l'option worker en une seule
```caddyfile
{
frankenphp {
worker <file> <num>
}
frankenphp {
worker <file> <num>
}
}
# ...
@@ -75,21 +92,18 @@ Vous pouvez également utiliser la forme courte de l'option worker en une seule
Vous pouvez aussi définir plusieurs workers si vous servez plusieurs applications sur le même serveur :
```caddyfile
{
frankenphp {
worker /path/to/app/public/index.php <num>
worker /path/to/other/public/index.php <num>
}
}
app.example.com {
root * /path/to/app/public
php_server
php_server {
root /path/to/app/public
worker index.php <num>
}
}
other.example.com {
root * /path/to/other/public
php_server
php_server {
root /path/to/other/public
worker index.php <num>
}
}
# ...
@@ -97,27 +111,29 @@ other.example.com {
L'utilisation de la directive `php_server` est généralement suffisante,
mais si vous avez besoin d'un contrôle total, vous pouvez utiliser la directive `php`, qui permet un plus grand niveau de finesse dans la configuration.
La directive `php` transmet toutes les entrées à PHP, au lieu de vérifier d'abord si
c'est un fichier PHP ou pas. En savoir plus à ce sujet dans la [page performances](performance.md).
Utiliser la directive `php_server` est équivalent à cette configuration :
```caddyfile
route {
# Ajoute un slash final pour les requêtes de répertoire
@canonicalPath {
file {path}/index.php
not path */
}
redir @canonicalPath {path}/ 308
# Si le fichier demandé n'existe pas, essayer les fichiers index
@indexFiles file {
try_files {path} {path}/index.php index.php
split_path .php
}
rewrite @indexFiles {http.matchers.file.relative}
# FrankenPHP!
@phpFiles path *.php
php @phpFiles
file_server
# Ajoute un slash final pour les requêtes de répertoire
@canonicalPath {
file {path}/index.php
not path */
}
redir @canonicalPath {path}/ 308
# Si le fichier demandé n'existe pas, essayer les fichiers index
@indexFiles file {
try_files {path} {path}/index.php index.php
split_path .php
}
rewrite @indexFiles {http.matchers.file.relative}
# FrankenPHP!
@phpFiles path *.php
php @phpFiles
file_server
}
```
@@ -125,20 +141,102 @@ Les directives `php_server` et `php` disposent des options suivantes :
```caddyfile
php_server [<matcher>] {
root <directory> # Définit le dossier racine du le site. Par défaut : valeur de la directive `root` parente.
split_path <delim...> # Définit les sous-chaînes pour diviser l'URI en deux parties. La première sous-chaîne correspondante sera utilisée pour séparer le "path info" du chemin. La première partie est suffixée avec la sous-chaîne correspondante et sera considérée comme le nom réel de la ressource (script CGI). La seconde partie sera définie comme PATH_INFO pour utilisation par le script. Par défaut : `.php`
resolve_root_symlink false # Désactive la résolution du répertoire `root` vers sa valeur réelle en évaluant un lien symbolique, s'il existe (activé par défaut).
env <key> <value> # Définit une variable d'environnement supplémentaire avec la valeur donnée. Peut être spécifié plusieurs fois pour plusieurs variables d'environnement.
root <directory> # Définit le dossier racine du le site. Par défaut : valeur de la directive `root` parente.
split_path <delim...> # Définit les sous-chaînes pour diviser l'URI en deux parties. La première sous-chaîne correspondante sera utilisée pour séparer le "path info" du chemin. La première partie est suffixée avec la sous-chaîne correspondante et sera considérée comme le nom réel de la ressource (script CGI). La seconde partie sera définie comme PATH_INFO pour utilisation par le script. Par défaut : `.php`
resolve_root_symlink false # Désactive la résolution du répertoire `root` vers sa valeur réelle en évaluant un lien symbolique, s'il existe (activé par défaut).
env <key> <value> # Définit une variable d'environnement supplémentaire avec la valeur donnée. Peut être spécifié plusieurs fois pour plusieurs variables d'environnement.
file_server off # Désactive la directive file_server intégrée.
worker { # Crée un worker spécifique à ce serveur. Peut être spécifié plusieurs fois pour plusieurs workers.
file <path> # Définit le chemin vers le script worker, peut être relatif à la racine du php_server
num <num> # Définit le nombre de threads PHP à démarrer, par défaut 2x le nombre de CPUs disponibles
name <name> # Définit le nom du worker, utilisé dans les journaux et les métriques. Défaut : chemin absolu du fichier du worker. Commence toujours par m# lorsqu'il est défini dans un bloc php_server.
watch <path> # Définit le chemin d'accès à surveiller pour les modifications de fichiers. Peut être spécifié plusieurs fois pour plusieurs chemins.
env <key> <value> # Définit une variable d'environnement supplémentaire avec la valeur donnée. Peut être spécifié plusieurs fois pour plusieurs variables d'environnement. Les variables d'environnement pour ce worker sont également héritées du parent php_server, mais peuvent être écrasées ici.
}
worker <other_file> <num> # Peut également utiliser la forme courte comme dans le bloc frankenphp global.
}
```
### Surveillance des modifications de fichier
Vu que les workers ne démarrent votre application qu'une seule fois et la gardent en mémoire, toute modification
apportée à vos fichiers PHP ne sera pas répercutée immédiatement.
Les workers peuvent être redémarrés en cas de changement de fichier via la directive `watch`.
Ceci est utile pour les environnements de développement.
```caddyfile
{
frankenphp {
worker {
file /path/to/app/public/worker.php
watch
}
}
}
```
Si le répertoire `watch` n'est pas précisé, il se rabattra sur `./**/*.{php,yaml,yml,twig,env}`,
qui surveille tous les fichiers `.php`, `.yaml`, `.yml`, `.twig` et `.env` dans le répertoire et les sous-répertoires
où le processus FrankenPHP a été lancé. Vous pouvez également spécifier un ou plusieurs répertoires via une commande
[motif de nom de fichier shell](https://pkg.go.dev/path/filepath#Match) :
```caddyfile
{
frankenphp {
worker {
file /path/to/app/public/worker.php
watch /path/to/app # surveille tous les fichiers dans tous les sous-répertoires de /path/to/app
watch /path/to/app/*.php # surveille les fichiers se terminant par .php dans /path/to/app
watch /path/to/app/**/*.php # surveille les fichiers PHP dans /path/to/app et les sous-répertoires
watch /path/to/app/**/*.{php,twig} # surveille les fichiers PHP et Twig dans /path/to/app et les sous-répertoires
}
}
}
```
- Le motif `**` signifie une surveillance récursive.
- Les répertoires peuvent également être relatifs (depuis l'endroit où le processus FrankenPHP est démarré).
- Si vous avez défini plusieurs workers, ils seront tous redémarrés lorsqu'un fichier est modifié.
- Méfiez-vous des fichiers créés au moment de l'exécution (comme les logs) car ils peuvent provoquer des redémarrages intempestifs du worker.
La surveillance des fichiers est basé sur [e-dant/watcher](https://github.com/e-dant/watcher).
### Full Duplex (HTTP/1)
Lors de l'utilisation de HTTP/1.x, il peut être souhaitable d'activer le mode full-duplex pour permettre l'écriture d'une réponse avant que le corps entier
n'ait été lu. (par exemple : WebSocket, événements envoyés par le serveur, etc.)
Il s'agit d'une configuration optionnelle qui doit être ajoutée aux options globales dans le fichier `Caddyfile` :
```caddyfile
{
servers {
enable_full_duplex
}
}
```
> [!CAUTION]
>
> L'activation de cette option peut entraîner un blocage (deadlock) des anciens clients HTTP/1.x qui ne supportent pas le full-duplex.
> Cela peut aussi être configuré en utilisant la variable d'environnement `CADDY_GLOBAL_OPTIONS` :
```sh
CADDY_GLOBAL_OPTIONS="servers {
enable_full_duplex
}"
```
Vous trouverez plus d'informations sur ce paramètre dans la [documentation Caddy](https://caddyserver.com/docs/caddyfile/options#enable-full-duplex).
## Variables d'environnement
Les variables d'environnement suivantes peuvent être utilisées pour insérer des directives Caddy dans le `Caddyfile` sans le modifier :
* `SERVER_NAME` : change [les adresses sur lesquelles écouter](https://caddyserver.com/docs/caddyfile/concepts#addresses), les noms d'hôte fournis seront également utilisés pour le certificat TLS généré
* `CADDY_GLOBAL_OPTIONS` : injecte [des options globales](https://caddyserver.com/docs/caddyfile/options)
* `FRANKENPHP_CONFIG` : insère la configuration sous la directive `frankenphp`
- `SERVER_NAME` : change [les adresses sur lesquelles écouter](https://caddyserver.com/docs/caddyfile/concepts#addresses), les noms d'hôte fournis seront également utilisés pour le certificat TLS généré
- `CADDY_GLOBAL_OPTIONS` : injecte [des options globales](https://caddyserver.com/docs/caddyfile/options)
- `FRANKENPHP_CONFIG` : insère la configuration sous la directive `frankenphp`
Comme pour les SAPI FPM et CLI, les variables d'environnement ne sont exposées par défaut dans la superglobale `$_SERVER`.
@@ -146,9 +244,27 @@ La valeur `S` de [la directive `variables_order` de PHP](https://www.php.net/man
## Configuration PHP
Pour charger [des fichiers de configuration PHP supplémentaires](https://www.php.net/manual/fr/configuration.file.php#configuration.file.scan), la variable d'environnement `PHP_INI_SCAN_DIR` peut être utilisée.
Pour charger [des fichiers de configuration PHP supplémentaires](https://www.php.net/manual/fr/configuration.file.php#configuration.file.scan),
la variable d'environnement `PHP_INI_SCAN_DIR` peut être utilisée.
Lorsqu'elle est définie, PHP chargera tous les fichiers avec l'extension `.ini` présents dans les répertoires donnés.
Vous pouvez également modifier la configuration de PHP en utilisant la directive `php_ini` dans le fichier `Caddyfile` :
```caddyfile
{
frankenphp {
php_ini memory_limit 256M
# or
php_ini {
memory_limit 256M
max_execution_time 15
}
}
}
```
## Activer le mode debug
Lors de l'utilisation de l'image Docker, définissez la variable d'environnement `CADDY_GLOBAL_OPTIONS` sur `debug` pour activer le mode debug :

View File

@@ -4,6 +4,13 @@ Les images Docker de [FrankenPHP](https://hub.docker.com/r/dunglas/frankenphp) s
Des variantes pour PHP 8.2, 8.3 et 8.4 sont disponibles. [Parcourir les tags](https://hub.docker.com/r/dunglas/frankenphp/tags).
Les tags suivent le pattern suivant: `dunglas/frankenphp:<frankenphp-version>-php<php-version>-<os>`
- `<frankenphp-version>` et `<php-version>` sont repsectivement les numéros de version de FrankenPHP et PHP, allant de majeur (e.g. `1`), mineur (e.g. `1.2`) à des versions correctives (e.g. `1.2.3`).
- `<os>` est soit `bookworm` (pour Debian Bookworm) ou `alpine` (pour la dernière version stable d'Alpine).
[Parcourir les tags](https://hub.docker.com/r/dunglas/frankenphp/tags).
## Comment utiliser les images
Créez un `Dockerfile` dans votre projet :
@@ -42,7 +49,7 @@ RUN install-php-extensions \
FrankenPHP est construit sur Caddy, et tous les [modules Caddy](https://caddyserver.com/docs/modules/) peuvent être utilisés avec FrankenPHP.
La manière la plus simple d'installer des modules Caddy personnalisés est d'utiliser [xcaddy](https://github.com/caddyserver/xcaddy):
La manière la plus simple d'installer des modules Caddy personnalisés est d'utiliser [xcaddy](https://github.com/caddyserver/xcaddy) :
```dockerfile
FROM dunglas/frankenphp:builder AS builder
@@ -58,8 +65,8 @@ RUN CGO_ENABLED=1 \
CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \
xcaddy build \
--output /usr/local/bin/frankenphp \
--with github.com/dunglas/frankenphp=./ \
--with github.com/dunglas/frankenphp/caddy=./caddy/ \
--with frankenphp.dev=./ \
--with frankenphp.dev/caddy=./caddy/ \
--with github.com/dunglas/caddy-cbrotli \
# Mercure et Vulcain sont inclus dans la construction officielle, mais n'hésitez pas à les retirer
--with github.com/dunglas/mercure/caddy \
@@ -167,12 +174,12 @@ FROM dunglas/frankenphp
ARG USER=appuser
RUN
RUN \
# Utiliser "adduser -D ${USER}" pour les distros basées sur Alpine
useradd ${USER};
# Supprimer la capacité par défaut
useradd ${USER}; \
# Supprimer la capacité par défaut \
setcap -r /usr/local/bin/frankenphp; \
# Donner un accès en écriture à /data/caddy et /config/caddy
# Donner un accès en écriture à /data/caddy et /config/caddy \
chown -R ${USER}:${USER} /data/caddy && chown -R ${USER}:${USER} /config/caddy
USER ${USER}
@@ -185,8 +192,8 @@ Exemple `:8000`
Les images Docker sont construites :
* lorsqu'une nouvelle version est taguée
* tous les jours à 4h UTC, si de nouvelles versions des images officielles PHP sont disponibles
- lorsqu'une nouvelle version est taguée
- tous les jours à 4h UTC, si de nouvelles versions des images officielles PHP sont disponibles
## Versions de développement

View File

@@ -6,16 +6,18 @@ Grâce à cette fonctionnalité, les applications PHP peuvent être distribuées
Pour en savoir plus sur cette fonctionnalité, consultez [la présentation faite par Kévin à la SymfonyCon 2023](https://dunglas.dev/2023/12/php-and-symfony-apps-as-standalone-binaries/).
Pour embarquer des applications Laravel, [lisez ce point spécifique de la documentation](laravel.md#les-applications-laravel-en-tant-que-binaires-autonomes).
## Préparer votre application
Avant de créer le binaire autonome, assurez-vous que votre application est prête à être intégrée.
Vous devrez probablement :
* Installer les dépendances de production de l'application
* Dumper l'autoloader
* Activer le mode production de votre application (si disponible)
* Supprimer les fichiers inutiles tels que `.git` ou les tests pour réduire la taille de votre binaire final
- Installer les dépendances de production de l'application
- Dumper l'autoloader
- Activer le mode production de votre application (si disponible)
- Supprimer les fichiers inutiles tels que `.git` ou les tests pour réduire la taille de votre binaire final
Par exemple, pour une application Symfony, lancez les commandes suivantes :
@@ -29,7 +31,8 @@ cd $TMPDIR/my-prepared-app
echo APP_ENV=prod > .env.local
echo APP_DEBUG=0 >> .env.local
# Supprimer les tests
# Supprimer les tests et autres fichiers inutiles pour économiser de l'espace
# Alternativement, ajoutez ces fichiers avec l'attribut export-ignore dans votre fichier .gitattributes
rm -Rf tests/
# Installer les dépendances
@@ -52,36 +55,34 @@ La manière la plus simple de créer un binaire Linux est d'utiliser le builder
1. Créez un fichier nommé `static-build.Dockerfile` dans le répertoire de votre application préparée :
```dockerfile
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder
```dockerfile
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder
# Copy your app
WORKDIR /go/src/app/dist/app
COPY . .
# Copy your app
WORKDIR /go/src/app/dist/app
COPY . .
# Build the static binary, be sure to select only the PHP extensions you want
WORKDIR /go/src/app/
RUN EMBED=dist/app/ \
PHP_EXTENSIONS=ctype,iconv,pdo_sqlite \
./build-static.sh
```
# Build the static binary, be sure to select only the PHP extensions you want
WORKDIR /go/src/app/
RUN EMBED=dist/app/ ./build-static.sh
```
> [!CAUTION]
>
> Certains fichiers `.dockerignore` (par exemple celui fourni par défaut par [Symfony Docker](https://github.com/dunglas/symfony-docker/blob/main/.dockerignore))
> empêchent la copie du dossier `vendor/` et des fichiers `.env`. Assurez-vous d'ajuster ou de supprimer le fichier `.dockerignore` avant le build.
> [!CAUTION]
>
> Certains fichiers `.dockerignore` (par exemple celui fourni par défaut par [Symfony Docker](https://github.com/dunglas/symfony-docker/blob/main/.dockerignore))
> empêchent la copie du dossier `vendor/` et des fichiers `.env`. Assurez-vous d'ajuster ou de supprimer le fichier `.dockerignore` avant le build.
2. Construisez:
```console
docker build -t static-app -f static-build.Dockerfile .
```
```console
docker build -t static-app -f static-build.Dockerfile .
```
3. Extrayez le binaire :
```console
docker cp $(docker create --name static-app-tmp static-app):/go/src/app/dist/frankenphp-linux-x86_64 my-app ; docker rm static-app-tmp
```
```console
docker cp $(docker create --name static-app-tmp static-app):/go/src/app/dist/frankenphp-linux-x86_64 my-app ; docker rm static-app-tmp
```
Le binaire généré sera nommé `my-app` dans le répertoire courant.
@@ -92,9 +93,7 @@ Si vous ne souhaitez pas utiliser Docker, ou souhaitez construire un binaire mac
```console
git clone https://github.com/dunglas/frankenphp
cd frankenphp
EMBED=/path/to/your/app \
PHP_EXTENSIONS=ctype,iconv,pdo_sqlite \
./build-static.sh
EMBED=/path/to/your/app ./build-static.sh
```
Le binaire obtenu est le fichier nommé `frankenphp-<os>-<arch>` dans le répertoire `dist/`.
@@ -127,6 +126,19 @@ Vous pouvez également exécuter les scripts CLI PHP incorporés dans votre bina
./my-app php-cli bin/console
```
## Extensions PHP
Par défaut, le script construira les extensions requises par le fichier `composer.json` de votre projet, s'il y en a.
Si le fichier `composer.json` n'existe pas, les extensions par défaut sont construites, comme documenté dans [Créer un binaire statique](static.md).
Pour personnaliser les extensions, utilisez la variable d'environnement `PHP_EXTENSIONS`.
```console
EMBED=/path/to/your/app \
PHP_EXTENSIONS=ctype,iconv,pdo_sqlite \
./build-static.sh
```
## Personnaliser la compilation
[Consultez la documentation sur la compilation statique](static.md) pour voir comment personnaliser le binaire (extensions, version PHP...).

View File

@@ -5,7 +5,7 @@
Les extensions suivantes sont connues pour ne pas être compatibles avec FrankenPHP :
| Nom | Raison | Alternatives |
|-------------------------------------------------------------------------------------------------------------|-----------------|----------------------------------------------------------------------------------------------------------------------|
| ----------------------------------------------------------------------------------------------------------- | --------------- | -------------------------------------------------------------------------------------------------------------------- |
| [imap](https://www.php.net/manual/en/imap.installation.php) | Non thread-safe | [javanile/php-imap2](https://github.com/javanile/php-imap2), [webklex/php-imap](https://github.com/Webklex/php-imap) |
| [newrelic](https://docs.newrelic.com/docs/apm/agents/php-agent/getting-started/introduction-new-relic-php/) | Non thread-safe | - |
@@ -14,7 +14,7 @@ Les extensions suivantes sont connues pour ne pas être compatibles avec Franken
Les extensions suivantes ont des bugs connus ou des comportements inattendus lorsqu'elles sont utilisées avec FrankenPHP :
| Nom | Problème |
|---------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [ext-openssl](https://www.php.net/manual/fr/book.openssl.php) | Lors de l'utilisation d'une version statique de FrankenPHP (construite avec la libc musl), l'extension OpenSSL peut planter sous de fortes charges. Une solution consiste à utiliser une version liée dynamiquement (comme celle utilisée dans les images Docker). Ce bogue est [suivi par PHP](https://github.com/php/php-src/issues/13648). |
## get_browser
@@ -30,7 +30,7 @@ Le binaire autonome et les images docker basées sur Alpine (`dunglas/frankenphp
Par défaut, FrankenPHP génère un certificat TLS pour `localhost`.
C'est l'option la plus simple et recommandée pour le développement local.
Si vous voulez vraiment utiliser `127.0.0.1` comme hôte, il est possible de configure FrankenPHP pour générer un certificat pour cela en définissant le nom du serveur à `127.0.0.1`.
Si vous voulez vraiment utiliser `127.0.0.1` comme hôte, il est possible de configurer FrankenPHP pour générer un certificat pour cela en définissant le nom du serveur à `127.0.0.1`.
Malheureusement, cela ne suffit pas lors de l'utilisation de Docker à cause de [son système de gestion des réseaux](https://docs.docker.com/network/).
Vous obtiendrez une erreur TLS similaire à `curl: (35) LibreSSL/3.3.6: error:1404B438:SSL routines:ST_CONNECT:tlsv1 alert internal error`.
@@ -47,7 +47,7 @@ docker run \
Le pilote de réseau "hôte" n'est pas pris en charge sur Mac et Windows. Sur ces plateformes, vous devrez deviner l'adresse IP du conteneur et l'inclure dans les noms de serveur.
Exécutez la commande `docker network inspect bridge` et inpectez la clef `Containers` pour identifier la dernière adresse IP attribuée sous la clef `IPv4Address`, puis incrémentez-la de un. Si aucun conteneur n'est en cours d'exécution, la première adresse IP attribuée est généralement `172.17.0.2`.
Exécutez la commande `docker network inspect bridge` et inpectez la clef `Containers` pour identifier la dernière adresse IP attribuée sous la clef `IPv4Address`, puis incrémentez-la d'un. Si aucun conteneur n'est en cours d'exécution, la première adresse IP attribuée est généralement `172.17.0.2`.
Ensuite, incluez ceci dans la variable d'environnement `SERVER_NAME` :
@@ -75,3 +75,69 @@ docker run \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
## Scripts Composer Faisant Références à `@php`
Les [scripts Composer](https://getcomposer.org/doc/articles/scripts.md) peuvent vouloir exécuter un binaire PHP pour certaines tâches, par exemple dans [un projet Laravel](laravel.md) pour exécuter `@php artisan package:discover --ansi`. Cela [echoue actuellement](https://github.com/dunglas/frankenphp/issues/483#issuecomment-1899890915) pour deux raisons :
- Composer ne sait pas comment appeler le binaire FrankenPHP ;
- Composer peut ajouter des paramètres PHP en utilisant le paramètre `-d` dans la commande, ce que FrankenPHP ne supporte pas encore.
Comme solution de contournement, nous pouvons créer un script shell dans `/usr/local/bin/php` qui supprime les paramètres non supportés et appelle ensuite FrankenPHP :
```bash
#!/usr/bin/env bash
args=("$@")
index=0
for i in "$@"
do
if [ "$i" == "-d" ]; then
unset 'args[$index]'
unset 'args[$index+1]'
fi
index=$((index+1))
done
/usr/local/bin/frankenphp php-cli ${args[@]}
```
Ensuite, mettez la variable d'environnement `PHP_BINARY` au chemin de notre script `php` et lancez Composer :
```console
export PHP_BINARY=/usr/local/bin/php
composer install
```
## Résolution des problèmes TLS/SSL avec les binaires statiques
Lorsque vous utilisez les binaires statiques, vous pouvez rencontrer les erreurs suivantes liées à TLS, par exemple lors de l'envoi de courriels utilisant STARTTLS :
```text
Unable to connect with STARTTLS: stream_socket_enable_crypto(): SSL operation failed with code 5. OpenSSL Error messages:
error:80000002:system library::No such file or directory
error:80000002:system library::No such file or directory
error:80000002:system library::No such file or directory
error:0A000086:SSL routines::certificate verify failed
```
Comme le binaire statique ne contient pas de certificats TLS, vous devez indiquer à OpenSSL l'installation de vos certificats CA locaux.
Inspectez la sortie de [`openssl_get_cert_locations()`](https://www.php.net/manual/en/function.openssl-get-cert-locations.php),
pour trouver l'endroit où les certificats CA doivent être installés et stockez-les à cet endroit.
> [!WARNING]
>
> Les contextes Web et CLI peuvent avoir des paramètres différents.
> Assurez-vous d'exécuter `openssl_get_cert_locations()` dans le bon contexte.
[Les certificats CA extraits de Mozilla peuvent être téléchargés sur le site curl](https://curl.se/docs/caextract.html).
Alternativement, de nombreuses distributions, y compris Debian, Ubuntu, et Alpine fournissent des paquets nommés `ca-certificates` qui contiennent ces certificats.
Il est également possible d'utiliser `SSL_CERT_FILE` et `SSL_CERT_DIR` pour indiquer à OpenSSL où chercher les certificats CA :
```console
# Définir les variables d'environnement des certificats TLS
export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
export SSL_CERT_DIR=/etc/ssl/certs
```

View File

@@ -2,8 +2,7 @@
## Docker
Déployer une application web [Laravel](https://laravel.com) avec FrankenPHP est très facile.
Il suffit de monter le projet dans le répertoire `/app` de l'image Docker officielle.
Déployer une application web [Laravel](https://laravel.com) avec FrankenPHP est très facile. Il suffit de monter le projet dans le répertoire `/app` de l'image Docker officielle.
Exécutez cette commande depuis le répertoire principal de votre application Laravel :
@@ -20,21 +19,23 @@ Vous pouvez également exécuter vos projets Laravel avec FrankenPHP depuis votr
1. [Téléchargez le binaire correspondant à votre système](README.md#binaire-autonome)
2. Ajoutez la configuration suivante dans un fichier nommé `Caddyfile` placé dans le répertoire racine de votre projet Laravel :
```caddyfile
{
frankenphp
}
```caddyfile
{
frankenphp
}
# Le nom de domaine de votre serveur
localhost {
# Définir le répertoire racine sur le dossier public/
root * public/
# Autoriser la compression (optionnel)
encode zstd br gzip
# Exécuter les scripts PHP du dossier public/ et servir les assets
php_server
}
```
# Le nom de domaine de votre serveur
localhost {
# Définir le répertoire racine sur le dossier public/
root public/
# Autoriser la compression (optionnel)
encode zstd br gzip
# Exécuter les scripts PHP du dossier public/ et servir les assets
php_server {
try_files {path} index.php
}
}
```
3. Démarrez FrankenPHP depuis le répertoire racine de votre projet Laravel : `frankenphp run`
@@ -60,19 +61,124 @@ php artisan octane:frankenphp
La commande `octane:frankenphp` peut prendre les options suivantes :
* `--host` : L'adresse IP à laquelle le serveur doit se lier (par défaut : `127.0.0.1`)
* `--port` : Le port sur lequel le serveur doit être disponible (par défaut : `8000`)
* `--admin-port` : Le port sur lequel le serveur administratif doit être disponible (par défaut : `2019`)
* `--workers` : Le nombre de workers qui doivent être disponibles pour traiter les requêtes (par défaut : `auto`)
* `--max-requests` : Le nombre de requêtes à traiter avant de recharger le serveur (par défaut : `500`)
* `--caddyfile` : Le chemin vers le fichier `Caddyfile` de FrankenPHP
* `--https` : Activer HTTPS, HTTP/2, et HTTP/3, et générer automatiquement et renouveler les certificats
* `--http-redirect` : Activer la redirection HTTP vers HTTPS (uniquement activé si --https est passé)
* `--watch` : Recharger automatiquement le serveur lorsque l'application est modifiée
* `--poll` : Utiliser le sondage du système de fichiers pendant la surveillance pour surveiller les fichiers sur un réseau
* `--log-level` : Enregistrer les messages au niveau de journalisation spécifié ou au-dessus, en utilisant le logger natif de Caddy
- `--host` : L'adresse IP à laquelle le serveur doit se lier (par défaut : `127.0.0.1`)
- `--port` : Le port sur lequel le serveur doit être disponible (par défaut : `8000`)
- `--admin-port` : Le port sur lequel le serveur administratif doit être disponible (par défaut : `2019`)
- `--workers` : Le nombre de workers qui doivent être disponibles pour traiter les requêtes (par défaut : `auto`)
- `--max-requests` : Le nombre de requêtes à traiter avant de recharger le serveur (par défaut : `500`)
- `--caddyfile` : Le chemin vers le fichier `Caddyfile` de FrankenPHP
- `--https` : Activer HTTPS, HTTP/2, et HTTP/3, et générer automatiquement et renouveler les certificats
- `--http-redirect` : Activer la redirection HTTP vers HTTPS (uniquement activé si --https est passé)
- `--watch` : Recharger automatiquement le serveur lorsque l'application est modifiée
- `--poll` : Utiliser le sondage du système de fichiers pendant la surveillance pour surveiller les fichiers sur un réseau
- `--log-level` : Enregistrer les messages au niveau de journalisation spécifié ou au-dessus, en utilisant le logger natif de Caddy
> [!TIP]
> Pour obtenir des logs structurés en JSON logs (utile quand vous utilisez des solutions d'analyse de logs), passez explicitement l'otpion `--log-level`.
> Pour obtenir des logs structurés en JSON logs (utile quand vous utilisez des solutions d'analyse de logs), passez explicitement l'option `--log-level`.
En savoir plus sur Laravel Octane [dans sa documentation officielle](https://laravel.com/docs/octane).
## Les Applications Laravel En Tant Que Binaires Autonomes
En utilisant la [fonctionnalité d'intégration d'applications de FrankenPHP](embed.md), il est possible de distribuer
les applications Laravel sous forme de binaires autonomes.
Suivez ces étapes pour empaqueter votre application Laravel en tant que binaire autonome pour Linux :
1. Créez un fichier nommé `static-build.Dockerfile` dans le dépôt de votre application :
```dockerfile
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder
# Copiez votre application
WORKDIR /go/src/app/dist/app
COPY . .
# Supprimez les tests et autres fichiers inutiles pour gagner de la place
# Alternativement, ajoutez ces fichiers à un fichier .dockerignore
RUN rm -Rf tests/
# Copiez le fichier .env
RUN cp .env.example .env
# Modifier APP_ENV et APP_DEBUG pour qu'ils soient prêts pour la production
RUN sed -i'' -e 's/^APP_ENV=.*/APP_ENV=production/' -e 's/^APP_DEBUG=.*/APP_DEBUG=false/' .env
# Apportez d'autres modifications à votre fichier .env si nécessaire
# Installez les dépendances
RUN composer install --ignore-platform-reqs --no-dev -a
# Construire le binaire statique
WORKDIR /go/src/app/
RUN EMBED=dist/app/ ./build-static.sh
```
> [!CAUTION]
>
> Certains fichiers `.dockerignore` ignoreront le répertoire `vendor/`
> et les fichiers `.env`. Assurez-vous d'ajuster ou de supprimer le fichier `.dockerignore` avant la construction.
2. Build:
```console
docker build -t static-laravel-app -f static-build.Dockerfile .
```
3. Extraire le binaire
```console
docker cp $(docker create --name static-laravel-app-tmp static-laravel-app):/go/src/app/dist/frankenphp-linux-x86_64 frankenphp ; docker rm static-laravel-app-tmp
```
4. Remplir les caches :
```console
frankenphp php-cli artisan optimize
```
5. Exécutez les migrations de base de données (s'il y en a) :
```console
frankenphp php-cli artisan migrate
```
6. Générer la clé secrète de l'application :
```console
frankenphp php-cli artisan key:generate
```
7. Démarrez le serveur:
```console
frankenphp php-server
```
Votre application est maintenant prête !
Pour en savoir plus sur les options disponibles et sur la construction de binaires pour d'autres systèmes d'exploitation,
consultez la documentation [Applications PHP en tant que binaires autonomes](embed.md).
### Changer le chemin de stockage
Par défaut, Laravel stocke les fichiers téléchargés, les caches, les logs, etc. dans le répertoire `storage/` de l'application.
Ceci n'est pas adapté aux applications embarquées, car chaque nouvelle version sera extraite dans un répertoire temporaire différent.
Définissez la variable d'environnement `LARAVEL_STORAGE_PATH` (par exemple, dans votre fichier `.env`) ou appelez la méthode `Illuminate\Foundation\Application::useStoragePath()` pour utiliser un répertoire en dehors du répertoire temporaire.
### Exécuter Octane avec des binaires autonomes
Il est même possible d'empaqueter les applications Laravel Octane en tant que binaires autonomes !
Pour ce faire, [installez Octane correctement](#laravel-octane) et suivez les étapes décrites dans [la section précédente](#les-applications-laravel-en-tant-que-binaires-autonomes).
Ensuite, pour démarrer FrankenPHP en mode worker via Octane, exécutez :
```console
PATH="$PWD:$PATH" frankenphp php-cli artisan octane:frankenphp
```
> [!CAUTION]
>
> Pour que la commande fonctionne, le binaire autonome **doit** être nommé `frankenphp`
> car Octane a besoin d'un programme nommé `frankenphp` disponible dans le chemin

17
docs/fr/metrics.md Normal file
View File

@@ -0,0 +1,17 @@
# Métriques
Lorsque les [métriques Caddy](https://caddyserver.com/docs/metrics) sont activées, FrankenPHP expose les métriques suivantes :
- `frankenphp_total_threads` : Le nombre total de threads PHP.
- `frankenphp_busy_threads` : Le nombre de threads PHP en cours de traitement d'une requête (les workers en cours d'exécution consomment toujours un thread).
- `frankenphp_queue_depth` : Le nombre de requêtes régulières en file d'attente
- `frankenphp_total_workers{worker=« [nom_du_worker] »}` : Le nombre total de workers.
- `frankenphp_busy_workers{worker=« [nom_du_worker] »}` : Le nombre de workers qui traitent actuellement une requête.
- `frankenphp_worker_request_time{worker=« [nom_du_worker] »}` : Le temps passé à traiter les requêtes par tous les workers.
- `frankenphp_worker_request_count{worker=« [nom_du_worker] »}` : Le nombre de requêtes traitées par tous les workers.
- `frankenphp_ready_workers{worker=« [nom_du_worker] »}` : Le nombre de workers qui ont appelé `frankenphp_handle_request` au moins une fois.
- `frankenphp_worker_crashes{worker=« [nom_du_worker] »}` : Le nombre de fois où un worker s'est arrêté de manière inattendue.
- `frankenphp_worker_restarts{worker=« [nom_du_worker] »}` : Le nombre de fois où un worker a été délibérément redémarré.
- `frankenphp_worker_queue_depth{worker=« [nom_du_worker] »}` : Le nombre de requêtes en file d'attente.
Pour les métriques de worker, le placeholder `[nom_du_worker]` est remplacé par le nom du worker dans le Caddyfile, sinon le chemin absolu du fichier du worker sera utilisé.

View File

@@ -5,7 +5,7 @@ Cependant, il est possible d'améliorer considérablement les performances en ut
## Nombre de threads et de workers
Par défaut, FrankenPHP démarre 2 fois plus de threads et de workers (en mode worker) que le nombre de CPU disponibles.
Par défaut, FrankenPHP démarre deux fois plus de threads et de workers (en mode worker) que le nombre de CPU disponibles.
Les valeurs appropriées dépendent fortement de la manière dont votre application est écrite, de ce qu'elle fait et de votre matériel.
Nous recommandons vivement de modifier ces valeurs.
@@ -16,6 +16,16 @@ Pour trouver les bonnes valeurs, il est souhaitable d'effectuer des tests de cha
Pour configurer le nombre de threads, utilisez l'option `num_threads` des directives `php_server` et `php`.
Pour changer le nombre de travailleurs, utilisez l'option `num` de la section `worker` de la directive `frankenphp`.
### `max_threads`
Bien qu'il soit toujours préférable de savoir exactement à quoi ressemblera votre trafic, les applications réelles
ont tendance à être plus imprévisibles. Le paramètre `max_threads` permet à FrankenPHP de créer automatiquement des threads supplémentaires au moment de l'exécution, jusqu'à la limite spécifiée.
`max_threads` peut vous aider à déterminer le nombre de threads dont vous avez besoin pour gérer votre trafic et peut rendre le serveur plus résistant aux pics de latence.
Si elle est fixée à `auto`, la limite sera estimée en fonction de la valeur de `memory_limit` dans votre `php.ini`. Si ce n'est pas possible,
`auto` prendra par défaut 2x `num_threads`. Gardez à l'esprit que `auto` peut fortement sous-estimer le nombre de threads nécessaires.
`max_threads` est similaire à [pm.max_children](https://www.php.net/manual/en/install.fpm.configuration.php#pm.max-children) de PHP FPM. La principale différence est que FrankenPHP utilise des threads au lieu de
processus et les délègue automatiquement à différents scripts de travail et au `mode classique` selon les besoins.
## Mode worker
Activer [le mode worker](worker.md) améliore considérablement les performances,
@@ -27,7 +37,7 @@ vous devez créer un script worker et vous assurer que l'application n'a pas de
Les binaires statiques que nous fournissons, ainsi que la variante Alpine Linux des images Docker officielles, utilisent [la bibliothèque musl](https://musl.libc.org).
PHP est connu pour être [significativement plus lent](https://gitlab.alpinelinux.org/alpine/aports/-/issues/14381) lorsqu'il utilise cette bibliothèque C alternative au lieu de la bibliothèque GNU traditionnelle,
surtout lorsqu'il est compilé en mode ZTS (*thread-safe*), ce qui est nécessaire pour FrankenPHP.
surtout lorsqu'il est compilé en mode ZTS (_thread-safe_), ce qui est nécessaire pour FrankenPHP.
En outre, [certains bogues ne se produisent que lors de l'utilisation de musl](https://github.com/php/php-src/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3ABug+musl).
@@ -53,7 +63,7 @@ Pour plus de détails, [la page de documentation Go dédiée à ce sujet](https:
## `file_server`
Par défaut, la directive `php_server` met automatiquement en place un serveur de fichiers pour
Par défaut, la directive `php_server` met automatiquement en place un serveur de fichiers
pour servir les fichiers statiques (assets) stockés dans le répertoire racine.
Cette fonctionnalité est pratique, mais a un coût.
@@ -65,16 +75,54 @@ php_server {
}
```
## *Placeholders*
## `try_files`
Vous pouvez utiliser des [*placeholders*](https://caddyserver.com/docs/conventions#placeholders) dans les directives `root` et `env`.
En plus des fichiers statiques et des fichiers PHP, `php_server` essaiera aussi de servir les fichiers d'index
et d'index de répertoire de votre application (`/path/` -> `/path/index.php`). Si vous n'avez pas besoin des index de répertoires,
vous pouvez les désactiver en définissant explicitement `try_files` comme ceci :
```caddyfile
php_server {
try_files {path} index.php
root /root/to/your/app # l'ajout explicite de la racine ici permet une meilleure mise en cache
}
```
Cela permet de réduire considérablement le nombre d'opérations inutiles sur les fichiers.
Une approche alternative avec 0 opérations inutiles sur le système de fichiers serait d'utiliser la directive `php`
et de diviser les fichiers de PHP par chemin. Cette approche fonctionne bien si votre application entière est servie par un seul fichier d'entrée.
Un exemple de [configuration](config.md#configuration-du-caddyfile) qui sert des fichiers statiques derrière un dossier `/assets` pourrait ressembler à ceci :
```caddyfile
route {
@assets {
path /assets/*
}
# tout ce qui se trouve derrière /assets est géré par le serveur de fichiers
file_server @assets {
root /root/to/your/app
}
# tout ce qui n'est pas dans /assets est géré par votre index ou votre fichier PHP worker
rewrite index.php
php {
root /root/to/your/app # l'ajout explicite de la racine ici permet une meilleure mise en cache
}
}
```
## _Placeholders_
Vous pouvez utiliser des [_placeholders_](https://caddyserver.com/docs/conventions#placeholders) dans les directives `root` et `env`.
Cependant, cela empêche la mise en cache de ces valeurs et a un coût important en termes de performances.
Si possible, évitez les *placeholders* dans ces directives.
Si possible, évitez les _placeholders_ dans ces directives.
## `resolve_root_symlink`
Par défaut, si le *document root* est un lien symbolique, il est automatiquement résolu par FrankenPHP (c'est nécessaire pour le bon fonctionnement de PHP).
Par défaut, si le _document root_ est un lien symbolique, il est automatiquement résolu par FrankenPHP (c'est nécessaire pour le bon fonctionnement de PHP).
Si la racine du document n'est pas un lien symbolique, vous pouvez désactiver cette fonctionnalité.
```caddyfile
@@ -83,12 +131,12 @@ php_server {
}
```
Cela améliorera les performances si la directive `root` contient des [*placeholders*](https://caddyserver.com/docs/conventions#placeholders).
Cela améliorera les performances si la directive `root` contient des [_placeholders_](https://caddyserver.com/docs/conventions#placeholders).
Le gain sera négligeable dans les autres cas.
## Journaux
La journalisation est évidemment très utile, mais, par définition, elle nécessite des opérations d'*I/O* et des allocations de mémoire,
La journalisation est évidemment très utile, mais, par définition, elle nécessite des opérations d'_I/O_ et des allocations de mémoire,
ce qui réduit considérablement les performances.
Assurez-vous de [définir le niveau de journalisation](https://caddyserver.com/docs/caddyfile/options#log) correctement,
et de ne journaliser que ce qui est nécessaire.
@@ -100,10 +148,10 @@ Toutes les optimisations de performances habituelles liées à PHP s'appliquent
En particulier :
* vérifiez que [OPcache](https://www.php.net/manual/en/book.opcache.php) est installé, activé et correctement configuré
* activez [les optimisations de l'autoloader de Composer](https://getcomposer.org/doc/articles/autoloader-optimization.md)
* assurez-vous que le cache `realpath` est suffisamment grand pour les besoins de votre application
* utilisez le [pré-chargement](https://www.php.net/manual/en/opcache.preloading.php)
- vérifiez que [OPcache](https://www.php.net/manual/en/book.opcache.php) est installé, activé et correctement configuré
- activez [les optimisations de l'autoloader de Composer](https://getcomposer.org/doc/articles/autoloader-optimization.md)
- assurez-vous que le cache `realpath` est suffisamment grand pour les besoins de votre application
- utilisez le [pré-chargement](https://www.php.net/manual/en/opcache.preloading.php)
Pour plus de détails, lisez [l'entrée de la documentation dédiée de Symfony](https://symfony.com/doc/current/performance.html)
(la plupart des conseils sont utiles même si vous n'utilisez pas Symfony).

View File

@@ -4,20 +4,49 @@ Au lieu d'utiliser une installation locale de la bibliothèque PHP, il est possi
Avec cette méthode, un binaire portable unique contiendra l'interpréteur PHP, le serveur web Caddy et FrankenPHP !
Les exécutables natifs entièrement statiques ne nécessitent aucune dépendance et peuvent même être exécutés sur une [image Docker `scratch`](https://docs.docker.com/build/building/base-images/#create-a-minimal-base-image-using-scratch).
Cependant, ils ne peuvent pas charger les extensions dynamiques de PHP (comme Xdebug) et ont quelques limitations parce qu'ils utilisent la librairie musl.
La plupart des binaires statiques ne nécessitent que la `glibc` et peuvent charger des extensions dynamiques.
Lorsque c'est possible, nous recommandons d'utiliser des binaires statiques basés sur la glibc.
FrankenPHP permet également [d'embarquer l'application PHP dans le binaire statique](embed.md).
## Linux
Nous fournissons une image Docker pour créer un binaire statique pour Linux :
Nous fournissons des images Docker pour créer des binaires statiques pour Linux :
### Build entièrement statique, basé sur musl
Pour un binaire entièrement statique qui fonctionne sur n'importe quelle distribution Linux sans dépendances,
mais qui ne prend pas en charge le chargement dynamique des extensions :
```console
docker buildx bake --load static-builder
docker cp $(docker create --name static-builder dunglas/frankenphp:static-builder):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp ; docker rm static-builder
docker buildx bake --load static-builder-musl
docker cp $(docker create --name static-builder-musl dunglas/frankenphp:static-builder-musl):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp ; docker rm static-builder-musl
```
Le binaire statique résultant est nommé `frankenphp`, et il est disponible dans le répertoire courant.
Pour améliorer les performances dans les scénarios fortement concurrents, envisagez d'utiliser l'allocateur [mimalloc](https://github.com/microsoft/mimalloc).
Si vous souhaitez construire le binaire statique sans Docker, regardez les instructions pour macOS, qui fonctionnent également pour Linux.
```console
docker buildx bake --load --set static-builder-musl.args.MIMALLOC=1 static-builder-musl
```
### Construction principalement statique (avec prise en charge des extensions dynamiques), basé sur la glibc
Pour un binaire qui supporte le chargement dynamique des extensions PHP tout en ayant les extensions sélectionnées compilées statiquement :
```console
docker buildx bake --load static-builder-gnu
docker cp $(docker create --name static-builder-gnu dunglas/frankenphp:static-builder-gnu):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp ; docker rm static-builder-gnu
```
Ce binaire supporte toutes les versions 2.17 et supérieures de la glibc mais ne fonctionne pas sur les systèmes basés sur musl (comme Alpine Linux).
Le binaire résultant, principalement statique (à l'exception de `glibc`), est nommé `frankenphp` et est disponible dans le répertoire courant.
Si vous voulez construire le binaire statique sans Docker, jetez un coup d'œil aux instructions pour macOS, qui fonctionnent aussi pour Linux.
### Extensions personnalisées
@@ -28,7 +57,7 @@ Pour réduire la taille du binaire et diminuer la surface d'attaque, vous pouvez
Par exemple, exécutez la commande suivante pour ne construire que l'extension `opcache` :
```console
docker buildx bake --load --set static-builder.args.PHP_EXTENSIONS=opcache,pdo_sqlite static-builder
docker buildx bake --load --set static-builder-musl.args.PHP_EXTENSIONS=opcache,pdo_sqlite static-builder-musl
# ...
```
@@ -37,9 +66,9 @@ Pour ajouter des bibliothèques permettant des fonctionnalités supplémentaires
```console
docker buildx bake \
--load \
--set static-builder.args.PHP_EXTENSIONS=gd \
--set static-builder.args.PHP_EXTENSION_LIBS=libjpeg,libwebp \
static-builder
--set static-builder-musl.args.PHP_EXTENSIONS=gd \
--set static-builder-musl.args.PHP_EXTENSION_LIBS=libjpeg,libwebp \
static-builder-musl
```
### Modules supplémentaires de Caddy
@@ -49,8 +78,8 @@ Pour ajouter des modules Caddy supplémentaires ou passer d'autres arguments à
```console
docker buildx bake \
--load \
--set static-builder.args.XCADDY_ARGS="--with github.com/darkweak/souin/plugins/caddy --with github.com/dunglas/caddy-cbrotli --with github.com/dunglas/mercure/caddy --with github.com/dunglas/vulcain/caddy" \
static-builder
--set static-builder-musl.args.XCADDY_ARGS="--with github.com/darkweak/souin/plugins/caddy --with github.com/dunglas/caddy-cbrotli --with github.com/dunglas/mercure/caddy --with github.com/dunglas/vulcain/caddy" \
static-builder-musl
```
Dans cet exemple, nous ajoutons le module de cache HTTP [Souin](https://souin.io) pour Caddy ainsi que les modules [cbrotli](https://github.com/dunglas/caddy-cbrotli), [Mercure](https://mercure.rocks) et [Vulcain](https://vulcain.rocks).
@@ -67,7 +96,7 @@ Voir aussi comment [personnaliser la construction](#personnalisation-de-la-const
Si vous atteignez la limite de taux d'appels de l'API GitHub, définissez un jeton d'accès personnel GitHub dans une variable d'environnement nommée `GITHUB_TOKEN` :
```console
GITHUB_TOKEN="xxx" docker --load buildx bake static-builder
GITHUB_TOKEN="xxx" docker --load buildx bake static-builder-musl
# ...
```
@@ -87,14 +116,46 @@ Note : ce script fonctionne également sur Linux (et probablement sur d'autres U
Les variables d'environnement suivantes peuvent être transmises à `docker build` et au script `build-static.sh` pour personnaliser la construction statique :
* `FRANKENPHP_VERSION` : la version de FrankenPHP à utiliser
* `PHP_VERSION` : la version de PHP à utiliser
* `PHP_EXTENSIONS` : les extensions PHP à construire ([liste des extensions prises en charge](https://static-php.dev/en/guide/extensions.html))
* `PHP_EXTENSION_LIBS` : bibliothèques supplémentaires à construire qui ajoutent des fonctionnalités aux extensions
* `XCADDY_ARGS` : arguments à passer à [xcaddy](https://github.com/caddyserver/xcaddy), par exemple pour ajouter des modules Caddy supplémentaires
* `EMBED` : chemin de l'application PHP à intégrer dans le binaire
* `CLEAN` : lorsque défini, `libphp` et toutes ses dépendances sont construites à partir de zéro (pas de cache)
* `DEBUG_SYMBOLS` : lorsque défini, les symboles de débogage ne seront pas supprimés et seront ajoutés dans le binaire
* `NO_COMPRESS`: ne pas compresser le binaire avec UPX
* `MIMALLOC`: (expérimental, Linux seulement) remplace l'allocateur mallocng de musl par [mimalloc](https://github.com/microsoft/mimalloc) pour des performances améliorées
* `RELEASE` : (uniquement pour les mainteneurs) lorsque défini, le binaire résultant sera uploadé sur GitHub
- `FRANKENPHP_VERSION` : la version de FrankenPHP à utiliser
- `PHP_VERSION` : la version de PHP à utiliser
- `PHP_EXTENSIONS` : les extensions PHP à construire ([liste des extensions prises en charge](https://static-php.dev/en/guide/extensions.html))
- `PHP_EXTENSION_LIBS` : bibliothèques supplémentaires à construire qui ajoutent des fonctionnalités aux extensions
- `XCADDY_ARGS` : arguments à passer à [xcaddy](https://github.com/caddyserver/xcaddy), par exemple pour ajouter des modules Caddy supplémentaires
- `EMBED` : chemin de l'application PHP à intégrer dans le binaire
- `CLEAN` : lorsque défini, `libphp` et toutes ses dépendances sont construites à partir de zéro (pas de cache)
- `DEBUG_SYMBOLS` : lorsque défini, les symboles de débogage ne seront pas supprimés et seront ajoutés dans le binaire
- `NO_COMPRESS`: ne pas compresser le binaire avec UPX
- `MIMALLOC`: (expérimental, Linux seulement) remplace l'allocateur mallocng de musl par [mimalloc](https://github.com/microsoft/mimalloc) pour des performances améliorées
- `RELEASE` : (uniquement pour les mainteneurs) lorsque défini, le binaire résultant sera uploadé sur GitHub
## Extensions
Avec la glibc ou les binaires basés sur macOS, vous pouvez charger des extensions PHP dynamiquement. Cependant, ces extensions devront être compilées avec le support ZTS.
Comme la plupart des gestionnaires de paquets ne proposent pas de versions ZTS de leurs extensions, vous devrez les compiler vous-même.
Pour cela, vous pouvez construire et exécuter le conteneur Docker `static-builder-gnu`, vous y connecter à distance et compiler les extensions avec `./configure --with-php-config=/go/src/app/dist/static-php-cli/buildroot/bin/php-config`.
Exemple d'étapes pour [l'extension Xdebug](https://xdebug.org) :
```console
docker build -t gnu-ext -f static-builder-gnu.Dockerfile --build-arg FRANKENPHP_VERSION=1.0 .
docker create --name static-builder-gnu -it gnu-ext /bin/sh
docker start static-builder-gnu
docker exec -it static-builder-gnu /bin/sh
cd /go/src/app/dist/static-php-cli/buildroot/bin
git clone https://github.com/xdebug/xdebug.git && cd xdebug
source scl_source enable devtoolset-10
../phpize
./configure --with-php-config=/go/src/app/dist/static-php-cli/buildroot/bin/php-config
make
exit
docker cp static-builder-gnu:/go/src/app/dist/static-php-cli/buildroot/bin/xdebug/modules/xdebug.so xdebug-zts.so
docker cp static-builder-gnu:/go/src/app/dist/frankenphp-linux-$(uname -m) ./frankenphp
docker stop static-builder-gnu
docker rm static-builder-gnu
docker rmi gnu-ext
```
Cela aura créé `frankenphp` et `xdebug-zts.so` dans le répertoire courant.
Si vous déplacez `xdebug-zts.so` dans votre répertoire d'extension, ajoutez `zend_extension=xdebug-zts.so` à votre php.ini
et lancez FrankenPHP, il chargera Xdebug.

View File

@@ -28,6 +28,13 @@ frankenphp php-server --worker /path/to/your/worker/script.php
Si votre application PHP est [intégrée dans le binaire](embed.md), vous pouvez également ajouter un `Caddyfile` personnalisé dans le répertoire racine de l'application.
Il sera utilisé automatiquement.
Il est également possible de [redémarrer le worker en cas de changement de fichier](config.md#surveillance-des-modifications-de-fichier) avec l'option `--watch`.
La commande suivante déclenchera un redémarrage si un fichier se terminant par `.php` dans le répertoire `/path/to/your/app/` ou ses sous-répertoires est modifié :
```console
frankenphp php-server --worker /path/to/your/worker/script.php --watch="/path/to/your/app/**/*.php"
```
## Runtime Symfony
Le mode worker de FrankenPHP est pris en charge par le [Composant Runtime de Symfony](https://symfony.com/doc/current/components/runtime.html).
@@ -76,14 +83,17 @@ $handler = static function () use ($myApp) {
echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER);
};
for ($nbRequests = 0, $running = true; isset($_SERVER['MAX_REQUESTS']) && ($nbRequests < ((int)$_SERVER['MAX_REQUESTS'])) && $running; ++$nbRequests) {
$running = \frankenphp_handle_request($handler);
$maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0);
for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests) {
$keepRunning = \frankenphp_handle_request($handler);
// Faire quelque chose après l'envoi de la réponse HTTP
$myApp->terminate();
// Exécuter le ramasse-miettes pour réduire les chances qu'il soit déclenché au milieu de la génération d'une page
gc_collect_cycles();
if (!$keepRunning) break;
}
// Nettoyage
@@ -118,13 +128,35 @@ Une solution pour utiliser ce type de code en mode worker est de redémarrer le
Le code du worker précédent permet de configurer un nombre maximal de requêtes à traiter en définissant une variable d'environnement nommée `MAX_REQUESTS`.
### Redémarrer les workers manuellement
Bien qu'il soit possible de redémarrer les workers [en cas de changement de fichier](config.md#surveillance-des-modifications-de-fichier),
il est également possible de redémarrer tous les workers de manière élégante via l'[API Admin de Caddy](https://caddyserver.com/docs/api).
Si l'administration est activée dans votre [Caddyfile](config.md#configuration-du-caddyfile), vous pouvez envoyer un ping
à l'endpoint de redémarrage avec une simple requête POST comme celle-ci :
```console
curl -X POST http://localhost:2019/frankenphp/workers/restart
```
> [!NOTE]
>
> C'est une fonctionnalité expérimentale et peut être modifiée ou supprimée dans le futur.
### Worker Failures
Si un script de worker se plante avec un code de sortie non nul, FrankenPHP le redémarre avec une stratégie de backoff exponentielle.
Si le script worker reste en place plus longtemps que le dernier backoff \* 2, FrankenPHP ne pénalisera pas le script et le redémarrera à nouveau.
Toutefois, si le script de worker continue d'échouer avec un code de sortie non nul dans un court laps de temps
(par exemple, une faute de frappe dans un script), FrankenPHP plantera avec l'erreur : `too many consecutive failures` (trop d'échecs consécutifs).
## Comportement des superglobales
[Les superglobales PHP](https://www.php.net/manual/fr/language.variables.superglobals.php) (`$_SERVER`, `$_ENV`, `$_GET`...)
se comportent comme suit :
* avant le premier appel à `frankenphp_handle_request()`, les superglobales contiennent des valeurs liées au script worker lui-même
* pendant et après l'appel à `frankenphp_handle_request()`, les superglobales contiennent des valeurs générées à partir de la requête HTTP traitée, chaque appel à `frankenphp_handle_request()` change les valeurs des superglobales
- avant le premier appel à `frankenphp_handle_request()`, les superglobales contiennent des valeurs liées au script worker lui-même
- pendant et après l'appel à `frankenphp_handle_request()`, les superglobales contiennent des valeurs générées à partir de la requête HTTP traitée, chaque appel à `frankenphp_handle_request()` change les valeurs des superglobales
Pour accéder aux superglobales du script worker à l'intérieur de la fonction de rappel, vous devez les copier et importer la copie dans le scope de la fonction :

71
docs/fr/x-sendfile.md Normal file
View File

@@ -0,0 +1,71 @@
# Servir efficacement les gros fichiers statiques (`X-Sendfile`/`X-Accel-Redirect`)
Habituellement, les fichiers statiques peuvent être servis directement par le serveur web,
mais parfois, il est nécessaire d'exécuter du code PHP avant de les envoyer :
contrôle d'accès, statistiques, en-têtes HTTP personnalisés...
Malheureusement, utiliser PHP pour servir de gros fichiers statiques est inefficace comparé à
à l'utilisation directe du serveur web (surcharge mémoire, diminution des performances...).
FrankenPHP permet de déléguer l'envoi des fichiers statiques au serveur web
**après** avoir exécuté du code PHP personnalisé.
Pour ce faire, votre application PHP n'a qu'à définir un en-tête HTTP personnalisé
contenant le chemin du fichier à servir. FrankenPHP se chargera du reste.
Cette fonctionnalité est connue sous le nom de **`X-Sendfile`** pour Apache, et **`X-Accel-Redirect`** pour NGINX.
Dans les exemples suivants, nous supposons que le "document root" du projet est le répertoire `public/`
et que nous voulons utiliser PHP pour servir des fichiers stockés en dehors du dossier `public/`,
depuis un répertoire nommé `private-files/`.
## Configuration
Tout d'abord, ajoutez la configuration suivante à votre `Caddyfile` pour activer cette fonctionnalité :
```patch
root public/
# ...
+ # Needed for Symfony, Laravel and other projects using the Symfony HttpFoundation component
+ request_header X-Sendfile-Type x-accel-redirect
+ request_header X-Accel-Mapping ../private-files=/private-files
+
+ intercept {
+ @accel header X-Accel-Redirect *
+ handle_response @accel {
+ root private-files/
+ rewrite * {resp.header.X-Accel-Redirect}
+ method * GET
+
+ # Remove the X-Accel-Redirect header set by PHP for increased security
+ header -X-Accel-Redirect
+
+ file_server
+ }
+ }
php_server
```
## PHP simple
Définissez le chemin relatif du fichier (à partir de `private-files/`) comme valeur de l'en-tête `X-Accel-Redirect` :
```php
header('X-Accel-Redirect: file.txt') ;
```
## Projets utilisant le composant Symfony HttpFoundation (Symfony, Laravel, Drupal...)
Symfony HttpFoundation [supporte nativement cette fonctionnalité](https://symfony.com/doc/current/components/http_foundation.html#serving-files).
Il va automatiquement déterminer la bonne valeur pour l'en-tête `X-Accel-Redirect` et l'ajoutera à la réponse.
```php
use Symfony\Component\HttpFoundation\BinaryFileResponse;
BinaryFileResponse::trustXSendfileTypeHeader();
$response = new BinaryFileResponse(__DIR__.'/../private-files/file.txt');
// ...
```

View File

@@ -5,7 +5,7 @@
The following extensions are known not to be compatible with FrankenPHP:
| Name | Reason | Alternatives |
|-------------------------------------------------------------------------------------------------------------|-----------------|----------------------------------------------------------------------------------------------------------------------|
| ----------------------------------------------------------------------------------------------------------- | --------------- | -------------------------------------------------------------------------------------------------------------------- |
| [imap](https://www.php.net/manual/en/imap.installation.php) | Not thread-safe | [javanile/php-imap2](https://github.com/javanile/php-imap2), [webklex/php-imap](https://github.com/Webklex/php-imap) |
| [newrelic](https://docs.newrelic.com/docs/apm/agents/php-agent/getting-started/introduction-new-relic-php/) | Not thread-safe | - |
@@ -14,7 +14,7 @@ The following extensions are known not to be compatible with FrankenPHP:
The following extensions have known bugs and unexpected behaviors when used with FrankenPHP:
| Name | Problem |
|---------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [ext-openssl](https://www.php.net/manual/en/book.openssl.php) | When using a static build of FrankenPHP (built with the musl libc), the OpenSSL extension may crash under heavy loads. A workaround is to use a dynamically linked build (like the one used in Docker images). This bug is [being tracked by PHP](https://github.com/php/php-src/issues/13648). |
## get_browser
@@ -80,8 +80,8 @@ docker run \
[Composer scripts](https://getcomposer.org/doc/articles/scripts.md) may want to execute a PHP binary for some tasks, e.g. in [a Laravel project](laravel.md) to run `@php artisan package:discover --ansi`. This [currently fails](https://github.com/dunglas/frankenphp/issues/483#issuecomment-1899890915) for two reasons:
* Composer does not know how to call the FrankenPHP binary;
* Composer may add PHP settings using the `-d` flag in the command, which FrankenPHP does not yet support.
- Composer does not know how to call the FrankenPHP binary;
- Composer may add PHP settings using the `-d` flag in the command, which FrankenPHP does not yet support.
As a workaround, we can create a shell script in `/usr/local/bin/php` which strips the unsupported parameters and then calls FrankenPHP:

View File

@@ -16,26 +16,26 @@ And enjoy!
Alternatively, you can run your Laravel projects with FrankenPHP from your local machine:
1. [Download the binary corresponding to your system](../README.md#standalone-binary)
1. [Download the binary corresponding to your system](../#standalone-binary)
2. Add the following configuration to a file named `Caddyfile` in the root directory of your Laravel project:
```caddyfile
{
frankenphp
}
```caddyfile
{
frankenphp
}
# The domain name of your server
localhost {
# Set the webroot to the public/ directory
root * public/
# Enable compression (optional)
encode zstd br gzip
# Execute PHP files from the public/ directory and serve assets
php_server {
try_files {path} index.php
}
}
```
# The domain name of your server
localhost {
# Set the webroot to the public/ directory
root public/
# Enable compression (optional)
encode zstd br gzip
# Execute PHP files from the public/ directory and serve assets
php_server {
try_files {path} index.php
}
}
```
3. Start FrankenPHP from the root directory of your Laravel project: `frankenphp run`
@@ -61,17 +61,17 @@ php artisan octane:frankenphp
The `octane:frankenphp` command can take the following options:
* `--host`: The IP address the server should bind to (default: `127.0.0.1`)
* `--port`: The port the server should be available on (default: `8000`)
* `--admin-port`: The port the admin server should be available on (default: `2019`)
* `--workers`: The number of workers that should be available to handle requests (default: `auto`)
* `--max-requests`: The number of requests to process before reloading the server (default: `500`)
* `--caddyfile`: The path to the FrankenPHP `Caddyfile` file (default: [stubbed `Caddyfile` in Laravel Octane](https://github.com/laravel/octane/blob/2.x/src/Commands/stubs/Caddyfile))
* `--https`: Enable HTTPS, HTTP/2, and HTTP/3, and automatically generate and renew certificates
* `--http-redirect`: Enable HTTP to HTTPS redirection (only enabled if --https is passed)
* `--watch`: Automatically reload the server when the application is modified
* `--poll`: Use file system polling while watching in order to watch files over a network
* `--log-level`: Log messages at or above the specified log level, using the native Caddy logger
- `--host`: The IP address the server should bind to (default: `127.0.0.1`)
- `--port`: The port the server should be available on (default: `8000`)
- `--admin-port`: The port the admin server should be available on (default: `2019`)
- `--workers`: The number of workers that should be available to handle requests (default: `auto`)
- `--max-requests`: The number of requests to process before reloading the server (default: `500`)
- `--caddyfile`: The path to the FrankenPHP `Caddyfile` file (default: [stubbed `Caddyfile` in Laravel Octane](https://github.com/laravel/octane/blob/2.x/src/Commands/stubs/Caddyfile))
- `--https`: Enable HTTPS, HTTP/2, and HTTP/3, and automatically generate and renew certificates
- `--http-redirect`: Enable HTTP to HTTPS redirection (only enabled if --https is passed)
- `--watch`: Automatically reload the server when the application is modified
- `--poll`: Use file system polling while watching in order to watch files over a network
- `--log-level`: Log messages at or above the specified log level, using the native Caddy logger
> [!TIP]
> To get structured JSON logs (useful when using log analytics solutions), explicitly the pass `--log-level` option.
@@ -87,72 +87,72 @@ Follow these steps to package your Laravel app as a standalone binary for Linux:
1. Create a file named `static-build.Dockerfile` in the repository of your app:
```dockerfile
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder
```dockerfile
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder
# Copy your app
WORKDIR /go/src/app/dist/app
COPY . .
# Copy your app
WORKDIR /go/src/app/dist/app
COPY . .
# Remove the tests and other unneeded files to save space
# Alternatively, add these files to a .dockerignore file
RUN rm -Rf tests/
# Remove the tests and other unneeded files to save space
# Alternatively, add these files to a .dockerignore file
RUN rm -Rf tests/
# Copy .env file
RUN cp .env.example .env
# Change APP_ENV and APP_DEBUG to be production ready
RUN sed -i'' -e 's/^APP_ENV=.*/APP_ENV=production/' -e 's/^APP_DEBUG=.*/APP_DEBUG=false/' .env
# Copy .env file
RUN cp .env.example .env
# Change APP_ENV and APP_DEBUG to be production ready
RUN sed -i'' -e 's/^APP_ENV=.*/APP_ENV=production/' -e 's/^APP_DEBUG=.*/APP_DEBUG=false/' .env
# Make other changes to your .env file if needed
# Make other changes to your .env file if needed
# Install the dependencies
RUN composer install --ignore-platform-reqs --no-dev -a
# Install the dependencies
RUN composer install --ignore-platform-reqs --no-dev -a
# Build the static binary
WORKDIR /go/src/app/
RUN EMBED=dist/app/ ./build-static.sh
```
# Build the static binary
WORKDIR /go/src/app/
RUN EMBED=dist/app/ ./build-static.sh
```
> [!CAUTION]
>
> Some `.dockerignore` files
> will ignore the `vendor/` directory and `.env` files. Be sure to adjust or remove the `.dockerignore` file before the build.
> [!CAUTION]
>
> Some `.dockerignore` files
> will ignore the `vendor/` directory and `.env` files. Be sure to adjust or remove the `.dockerignore` file before the build.
2. Build:
```console
docker build -t static-laravel-app -f static-build.Dockerfile .
```
```console
docker build -t static-laravel-app -f static-build.Dockerfile .
```
3. Extract the binary:
```console
docker cp $(docker create --name static-laravel-app-tmp static-laravel-app):/go/src/app/dist/frankenphp-linux-x86_64 frankenphp ; docker rm static-laravel-app-tmp
```
```console
docker cp $(docker create --name static-laravel-app-tmp static-laravel-app):/go/src/app/dist/frankenphp-linux-x86_64 frankenphp ; docker rm static-laravel-app-tmp
```
4. Populate caches:
```console
frankenphp php-cli artisan optimize
```
```console
frankenphp php-cli artisan optimize
```
5. Run database migrations (if any):
```console
frankenphp php-cli artisan migrate
````
```console
frankenphp php-cli artisan migrate
```
6. Generate app's secret key:
```console
frankenphp php-cli artisan key:generate
```
```console
frankenphp php-cli artisan key:generate
```
7. Start the server:
```console
frankenphp php-server
```
```console
frankenphp php-server
```
Your app is now ready!

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 1009 KiB

View File

@@ -1,12 +1,15 @@
# Real-time
FrankenPHP comes with a built-in [Mercure](https://mercure.rocks) hub!
Mercure allows to push events in real-time to all the connected devices: they will receive a JavaScript event instantly.
Mercure allows you to push real-time events to all the connected devices: they will receive a JavaScript event instantly.
No JS library or SDK required!
No JS library or SDK is required!
![Mercure](mercure-hub.png)
To enable the Mercure hub, update the `Caddyfile` as described [on Mercure's site](https://mercure.rocks/docs/hub/config).
To push Mercure updates from your code, we recommend the [Symfony Mercure Component](https://symfony.com/components/Mercure) (you don't need the Symfony full stack framework to use it).
The path of the Mercure hub is `/.well-known/mercure`.
When running FrankenPHP inside Docker, the full send URL would look like `http://php/.well-known/mercure` (with `php` being the container's name running FrankenPHP).
To push Mercure updates from your code, we recommend the [Symfony Mercure Component](https://symfony.com/components/Mercure) (you don't need the Symfony full-stack framework to use it).

View File

@@ -2,14 +2,16 @@
When [Caddy metrics](https://caddyserver.com/docs/metrics) are enabled, FrankenPHP exposes the following metrics:
* `frankenphp_[worker]_total_workers`: The total number of workers.
* `frankenphp_[worker]_busy_workers`: The number of workers currently processing a request.
* `frankenphp_[worker]_worker_request_time`: The time spent processing requests by all workers.
* `frankenphp_[worker]_worker_request_count`: The number of requests processed by all workers.
* `frankenphp_[worker]_ready_workers`: The number of workers that have called `frankenphp_handle_request` at least once.
* `frankenphp_[worker]_worker_crashes`: The number of times a worker has unexpectedly terminated.
* `frankenphp_[worker]_worker_restarts`: The number of times a worker has been deliberately restarted.
* `frankenphp_total_threads`: The total number of PHP threads.
* `frankenphp_busy_threads`: The number of PHP threads currently processing a request (running workers always consume a thread).
- `frankenphp_total_threads`: The total number of PHP threads.
- `frankenphp_busy_threads`: The number of PHP threads currently processing a request (running workers always consume a thread).
- `frankenphp_queue_depth`: The number of regular queued requests
- `frankenphp_total_workers{worker="[worker_name]"}`: The total number of workers.
- `frankenphp_busy_workers{worker="[worker_name]"}`: The number of workers currently processing a request.
- `frankenphp_worker_request_time{worker="[worker_name]"}`: The time spent processing requests by all workers.
- `frankenphp_worker_request_count{worker="[worker_name]"}`: The number of requests processed by all workers.
- `frankenphp_ready_workers{worker="[worker_name]"}`: The number of workers that have called `frankenphp_handle_request` at least once.
- `frankenphp_worker_crashes{worker="[worker_name]"}`: The number of times a worker has unexpectedly terminated.
- `frankenphp_worker_restarts{worker="[worker_name]"}`: The number of times a worker has been deliberately restarted.
- `frankenphp_worker_queue_depth{worker="[worker_name]"}`: The number of queued requests.
For worker metrics, the `[worker]` placeholder is replaced by the worker script path in the Caddyfile.
For worker metrics, the `[worker_name]` placeholder is replaced by the worker name in the Caddyfile, otherwise absolute path of worker file will be used.

View File

@@ -16,6 +16,16 @@ To find the right values, it's best to run load tests simulating real traffic.
To configure the number of threads, use the `num_threads` option of the `php_server` and `php` directives.
To change the number of workers, use the `num` option of the `worker` section of the `frankenphp` directive.
### `max_threads`
While it's always better to know exactly what your traffic will look like, real-life applications tend to be more
unpredictable. The `max_threads` [configuration](config.md#caddyfile-config) allows FrankenPHP to automatically spawn additional threads at runtime up to the specified limit.
`max_threads` can help you figure out how many threads you need to handle your traffic and can make the server more resilient to latency spikes.
If set to `auto`, the limit will be estimated based on the `memory_limit` in your `php.ini`. If not able to do so,
`auto` will instead default to 2x `num_threads`. Keep in mind that `auto` might strongly underestimate the number of threads needed.
`max_threads` is similar to PHP FPM's [pm.max_children](https://www.php.net/manual/en/install.fpm.configuration.php#pm.max-children). The main difference is that FrankenPHP uses threads instead of
processes and automatically delegates them across different worker scripts and 'classic mode' as needed.
## Worker Mode
Enabling [the worker mode](worker.md) dramatically improves performance,
@@ -24,19 +34,18 @@ you need to create a worker script and to be sure that the app is not leaking me
## Don't Use musl
The static binaries we provide and the Alpine Linux variant of the official Docker images
are using [the musl libc](https://musl.libc.org).
The Alpine Linux variant of the official Docker images and the default binaries we provide are using [the musl libc](https://musl.libc.org).
PHP is known to be [significantly slower](https://gitlab.alpinelinux.org/alpine/aports/-/issues/14381) when using this alternative C library instead of the traditional GNU library,
especially when compiled in ZTS mode (thread-safe), which is required for FrankenPHP.
PHP is known to be [slower](https://gitlab.alpinelinux.org/alpine/aports/-/issues/14381) when using this alternative C library instead of the traditional GNU library,
especially when compiled in ZTS mode (thread-safe), which is required for FrankenPHP. The difference can be significant in a heavily threaded environment.
Also, [some bugs only happen when using musl](https://github.com/php/php-src/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3ABug+musl).
In production environments, we strongly recommend to use the glibc.
In production environments, we recommend using FrankenPHP linked against glibc.
This can be achieved by using the Debian Docker images (the default) and [by compiling FrankenPHP from sources](compile.md).
This can be achieved by using the Debian Docker images (the default), downloading the -gnu suffix binary from our [Releases](https://github.com/dunglas/frankenphp/releases), or by [compiling FrankenPHP from sources](compile.md).
Alternatively, we provide static binaries compiled with [the mimalloc allocator](https://github.com/microsoft/mimalloc), which makes FrankenPHP+musl faster (but still slower than FrankenPHP+glibc).
Alternatively, we provide static musl binaries compiled with [the mimalloc allocator](https://github.com/microsoft/mimalloc), which alleviates the problems in threaded scenarios.
## Go Runtime Configuration
@@ -139,10 +148,10 @@ All usual PHP-related performance optimizations apply with FrankenPHP.
In particular:
* check that [OPcache](https://www.php.net/manual/en/book.opcache.php) is installed, enabled and properly configured
* enable [Composer autoloader optimizations](https://getcomposer.org/doc/articles/autoloader-optimization.md)
* ensure that the `realpath` cache is big enough for the needs of your application
* use [preloading](https://www.php.net/manual/en/opcache.preloading.php)
- check that [OPcache](https://www.php.net/manual/en/book.opcache.php) is installed, enabled and properly configured
- enable [Composer autoloader optimizations](https://getcomposer.org/doc/articles/autoloader-optimization.md)
- ensure that the `realpath` cache is big enough for the needs of your application
- use [preloading](https://www.php.net/manual/en/opcache.preloading.php)
For more details, read [the dedicated Symfony documentation entry](https://symfony.com/doc/current/performance.html)
(most tips are useful even if you don't use Symfony).

View File

@@ -124,7 +124,7 @@ git clone git@github.com:<username>/<project-name>.git
Go into the directory containing your project (`<project-name>`), and start the app in production mode:
```console
docker compose up -d --wait
docker compose up --wait
```
Your server is up and running, and an HTTPS certificate has been automatically generated for you.

218
docs/ru/CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,218 @@
# Участие в проекте
## Компиляция PHP
### С помощью Docker (Linux)
Создайте образ Docker для разработки:
```console
docker build -t frankenphp-dev -f dev.Dockerfile .
docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -p 8080:8080 -p 443:443 -p 443:443/udp -v $PWD:/go/src/app -it frankenphp-dev
```
Образ содержит стандартные инструменты для разработки (Go, GDB, Valgrind, Neovim и др.) и использует следующие пути для настроек PHP
- php.ini: `/etc/frankenphp/php.ini` По умолчанию предоставляется файл php.ini с настройками для разработки.
- дополнительные файлы конфигурации: `/etc/frankenphp/php.d/*.ini`
- расширения php: `/usr/lib/frankenphp/modules/`
Если ваша версия Docker ниже 23.0, сборка может завершиться ошибкой из-за [проблемы с шаблонами dockerignore](https://github.com/moby/moby/pull/42676). Добавьте в `.dockerignore` следующие директории:
```patch
!testdata/*.php
!testdata/*.txt
+!caddy
+!internal
```
### Без Docker (Linux и macOS)
[Следуйте инструкциям по компиляции из исходников](https://frankenphp.dev/docs/compile/) и укажите флаг конфигурации `--debug`.
## Запуск тестов
```console
go test -tags watcher -race -v ./...
```
## Модуль Caddy
Соберите Caddy с модулем FrankenPHP:
```console
cd caddy/frankenphp/
go build -tags watcher,brotli,nobadger,nomysql,nopgx
cd ../../
```
Запустите Caddy с модулем FrankenPHP:
```console
cd testdata/
../caddy/frankenphp/frankenphp run
```
Сервер будет доступен по адресу `127.0.0.1:8080`:
```console
curl -vk https://localhost/phpinfo.php
```
## Минимальный тестовый сервер
Соберите минимальный тестовый сервер:
```console
cd internal/testserver/
go build
cd ../../
```
Запустите тестовый сервер:
```console
cd testdata/
../internal/testserver/testserver
```
Сервер будет доступен по адресу `127.0.0.1:8080`:
```console
curl -v http://127.0.0.1:8080/phpinfo.php
```
## Локальная сборка Docker-образов
Выведите план bake:
```console
docker buildx bake -f docker-bake.hcl --print
```
Соберите образы FrankenPHP для amd64 локально:
```console
docker buildx bake -f docker-bake.hcl --pull --load --set "*.platform=linux/amd64"
```
Соберите образы FrankenPHP для arm64 локально:
```console
docker buildx bake -f docker-bake.hcl --pull --load --set "*.platform=linux/arm64"
```
Соберите образы FrankenPHP с нуля для arm64 и amd64 и отправьте их в Docker Hub:
```console
docker buildx bake -f docker-bake.hcl --pull --no-cache --push
```
## Отладка ошибок сегментации с использованием статических сборок
1. Скачайте отладочную версию бинарного файла FrankenPHP с GitHub или создайте собственную статическую сборку с включённым отладочным режимом:
```console
docker buildx bake \
--load \
--set static-builder.args.DEBUG_SYMBOLS=1 \
--set "static-builder.platform=linux/amd64" \
static-builder
docker cp $(docker create --name static-builder-musl dunglas/frankenphp:static-builder-musl):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp
```
2. Замените текущую версию `frankenphp` на бинарный файл с включенным отладочным режимом.
3. Запустите FrankenPHP как обычно (или сразу запустите FrankenPHP с GDB: `gdb --args frankenphp run`).
4. Подключитесь к процессу через GDB:
```console
gdb -p `pidof frankenphp`
```
5. При необходимости введите `continue` в консоли GDB.
6. Вызовите сбой FrankenPHP.
7. Введите `bt` в консоли GDB.
8. Скопируйте вывод.
## Отладка ошибок сегментации в GitHub Actions
1. Откройте файл `.github/workflows/tests.yml`.
2. Включите режим отладки PHP:
```patch
- uses: shivammathur/setup-php@v2
# ...
env:
phpts: ts
+ debug: true
```
3. Настройте `tmate` для удалённого подключения к контейнеру:
```patch
-
name: Set CGO flags
run: echo "CGO_CFLAGS=$(php-config --includes)" >> "$GITHUB_ENV"
+ -
+ run: |
+ sudo apt install gdb
+ mkdir -p /home/runner/.config/gdb/
+ printf "set auto-load safe-path /\nhandle SIG34 nostop noprint pass" > /home/runner/.config/gdb/gdbinit
+ -
+ uses: mxschmitt/action-tmate@v3
```
4. Подключитесь к контейнеру.
5. Откройте файл `frankenphp.go`.
6. Включите `cgosymbolizer`:
```patch
- //_ "github.com/ianlancetaylor/cgosymbolizer"
+ _ "github.com/ianlancetaylor/cgosymbolizer"
```
7. Загрузите модуль: `go get`.
8. В контейнере используйте GDB и другие инструменты:
```console
go test -tags watcher -c -ldflags=-w
gdb --args frankenphp.dev.test -test.run ^MyTest$
```
9. После исправления ошибки откатите все внесенные изменения.
## Дополнительные ресурсы для разработки
- [Встраивание PHP в uWSGI](https://github.com/unbit/uwsgi/blob/master/plugins/php/php_plugin.c)
- [Встраивание PHP в NGINX Unit](https://github.com/nginx/unit/blob/master/src/nxt_php_sapi.c)
- [Встраивание PHP в Go (go-php)](https://github.com/deuill/go-php)
- [Встраивание PHP в Go (GoEmPHP)](https://github.com/mikespook/goemphp)
- [Встраивание PHP в C++](https://gist.github.com/paresy/3cbd4c6a469511ac7479aa0e7c42fea7)
- [Книга "Extending and Embedding PHP" Сары Големан](https://books.google.fr/books?id=zMbGvK17_tYC&pg=PA254&lpg=PA254#v=onepage&q&f=false)
- [Статья: Что такое TSRMLS_CC?](http://blog.golemon.com/2006/06/what-heck-is-tsrmlscc-anyway.html)
- [SDL bindings](https://pkg.go.dev/github.com/veandco/go-sdl2@v0.4.21/sdl#Main)
## Docker-ресурсы
- [Определение файлов bake](https://docs.docker.com/build/customize/bake/file-definition/)
- [Документация по команде `docker buildx build`](https://docs.docker.com/engine/reference/commandline/buildx_build/)
## Полезные команды
```console
apk add strace util-linux gdb
strace -e 'trace=!futex,epoll_ctl,epoll_pwait,tgkill,rt_sigreturn' -p 1
```
## Перевод документации
Чтобы перевести документацию и сайт на новый язык, выполните следующие шаги:
1. Создайте новую директорию с 2-буквенным ISO-кодом языка в папке `docs/`.
2. Скопируйте все `.md` файлы из корня папки `docs/` в новую директорию (используйте английскую версию как основу для перевода).
3. Скопируйте файлы `README.md` и `CONTRIBUTING.md` из корневой директории в новую папку.
4. Переведите содержимое файлов, но не изменяйте имена файлов. Не переводите строки, начинающиеся с `> [!`, это специальная разметка GitHub.
5. Создайте Pull Request с переводом.
6. В [репозитории сайта](https://github.com/dunglas/frankenphp-website/tree/main) скопируйте и переведите файлы в папках `content/`, `data/` и `i18n/`.
7. Переведите значения в созданных YAML-файлах.
8. Откройте Pull Request в репозитории сайта.

86
docs/ru/README.md Normal file
View File

@@ -0,0 +1,86 @@
# FrankenPHP: Современный сервер приложений для PHP
<h1 align="center"><a href="https://frankenphp.dev"><img src="../../frankenphp.png" alt="FrankenPHP" width="600"></a></h1>
**FrankenPHP** — это современный сервер приложений для PHP, построенный на базе веб-сервера [Caddy](https://caddyserver.com/).
FrankenPHP добавляет новые возможности вашим PHP-приложениям благодаря следующим функциям: [_Early Hints_](https://frankenphp.dev/docs/early-hints/), [Worker режим](https://frankenphp.dev/docs/worker/), [Real-time режим](https://frankenphp.dev/docs/mercure/), автоматическая поддержка HTTPS, HTTP/2 и HTTP/3.
FrankenPHP совместим с любыми PHP-приложениями и значительно ускоряет ваши проекты на Laravel и Symfony благодаря их официальной поддержке в worker режиме.
FrankenPHP также может использоваться как автономная Go-библиотека для встраивания PHP в любое приложение с использованием `net/http`.
[**Узнайте больше** на сайте _frankenphp.dev_](https://frankenphp.dev) или из этой презентации:
<a href="https://dunglas.dev/2022/10/frankenphp-the-modern-php-app-server-written-in-go/"><img src="https://dunglas.dev/wp-content/uploads/2022/10/frankenphp.png" alt="Slides" width="600"></a>
## Начало работы
### Docker
```console
docker run -v .:/app/public \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
Перейдите по адресу `https://localhost` и наслаждайтесь!
> [!TIP]
>
> Не используйте `https://127.0.0.1`. Используйте `https://localhost` и настройте самоподписанный сертификат.
> Чтобы изменить используемый домен, настройте переменную окружения [`SERVER_NAME`](config.md#переменные-окружения).
### Автономный бинарный файл
Если вы предпочитаете не использовать Docker, мы предоставляем автономный бинарный файл FrankenPHP для Linux и macOS, включающий [PHP 8.4](https://www.php.net/releases/8.4/en.php) и большинство популярных PHP-расширений.
Для Windows используйте [WSL](https://learn.microsoft.com/windows/wsl/) для запуска FrankenPHP.
[Скачать FrankenPHP](https://github.com/dunglas/frankenphp/releases) или выполните следующую команду для автоматической установки подходящей версии:
```console
curl https://frankenphp.dev/install.sh | sh
mv frankenphp /usr/local/bin/
```
Для запуска содержимого текущей директории выполните:
```console
frankenphp php-server
```
Вы также можете запускать CLI-скрипты:
```console
frankenphp php-cli /path/to/your/script.php
```
## Документация
- [Worker режим](https://frankenphp.dev/docs/worker/)
- [Поддержка Early Hints (103 HTTP статус код)](https://frankenphp.dev/docs/early-hints/)
- [Real-time режим](https://frankenphp.dev/docs/mercure/)
- [Конфигурация](https://frankenphp.dev/docs/config/)
- [Docker-образы](https://frankenphp.dev/docs/docker/)
- [Деплой в продакшен](https://frankenphp.dev/docs/production/)
- [Оптимизация производительности](https://frankenphp.dev/docs/performance/)
- [Создание автономного PHP-приложений](https://frankenphp.dev/docs/embed/)
- [Создание статических бинарных файлов](https://frankenphp.dev/docs/static/)
- [Компиляция из исходников](https://frankenphp.dev/docs/compile/)
- [Интеграция с Laravel](https://frankenphp.dev/docs/laravel/)
- [Известные проблемы](https://frankenphp.dev/docs/known-issues/)
- [Демо-приложение (Symfony) и бенчмарки](https://github.com/dunglas/frankenphp-demo)
- [Документация Go-библиотеки](https://pkg.go.dev/github.com/dunglas/frankenphp)
- [Участие в проекте и отладка](https://frankenphp.dev/docs/contributing/)
## Примеры и шаблоны
- [Symfony](https://github.com/dunglas/symfony-docker)
- [API Platform](https://api-platform.com/docs/symfony)
- [Laravel](https://frankenphp.dev/docs/laravel/)
- [Sulu](https://sulu.io/blog/running-sulu-with-frankenphp)
- [WordPress](https://github.com/StephenMiracle/frankenwp)
- [Drupal](https://github.com/dunglas/frankenphp-drupal)
- [Joomla](https://github.com/alexandreelise/frankenphp-joomla)
- [TYPO3](https://github.com/ochorocho/franken-typo3)

110
docs/ru/compile.md Normal file
View File

@@ -0,0 +1,110 @@
# Компиляция из исходников
Этот документ объясняет, как создать бинарный файл FrankenPHP, который будет загружать PHP как динамическую библиотеку.
Это рекомендуемый способ.
Альтернативно можно создать [статическую сборку](static.md).
## Установка PHP
FrankenPHP совместим с PHP версии 8.2 и выше.
Сначала [загрузите исходники PHP](https://www.php.net/downloads.php) и распакуйте их:
```console
tar xf php-*
cd php-*/
```
Далее выполните скрипт `configure` с параметрами, необходимыми для вашей платформы.
Следующие флаги `./configure` обязательны, но вы можете добавить и другие, например, для компиляции расширений или дополнительных функций.
### Linux
```console
./configure \
--enable-embed \
--enable-zts \
--disable-zend-signals \
--enable-zend-max-execution-timers
```
### Mac
Используйте пакетный менеджер [Homebrew](https://brew.sh/) для установки
`libiconv`, `bison`, `re2c` и `pkg-config`:
```console
brew install libiconv bison brotli re2c pkg-config
echo 'export PATH="/opt/homebrew/opt/bison/bin:$PATH"' >> ~/.zshrc
```
Затем выполните скрипт configure:
```console
./configure \
--enable-embed=static \
--enable-zts \
--disable-zend-signals \
--disable-opcache-jit \
--enable-static \
--enable-shared=no \
--with-iconv=/opt/homebrew/opt/libiconv/
```
## Компиляция PHP
Наконец, скомпилируйте и установите PHP:
```console
make -j"$(getconf _NPROCESSORS_ONLN)"
sudo make install
```
## Установка дополнительных зависимостей
Некоторые функции FrankenPHP зависят от опциональных системных зависимостей.
Альтернативно, эти функции можно отключить, передав соответствующие теги сборки компилятору Go.
| Функция | Зависимость | Тег сборки для отключения |
| ----------------------------------------------- | --------------------------------------------------------------------- | ------------------------- |
| Сжатие Brotli | [Brotli](https://github.com/google/brotli) | nobrotli |
| Перезапуск worker-скриптов при изменении файлов | [Watcher C](https://github.com/e-dant/watcher/tree/release/watcher-c) | nowatcher |
## Компиляция Go-приложения
Теперь можно собрать итоговый бинарный файл:
```console
curl -L https://github.com/dunglas/frankenphp/archive/refs/heads/main.tar.gz | tar xz
cd frankenphp-main/caddy/frankenphp
CGO_CFLAGS=$(php-config --includes) CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" go build -tags=nobadger,nomysql,nopgx
```
### Использование xcaddy
Альтернативно, используйте [xcaddy](https://github.com/caddyserver/xcaddy) для компиляции FrankenPHP с [пользовательскими модулями Caddy](https://caddyserver.com/docs/modules/):
```console
CGO_ENABLED=1 \
XCADDY_GO_BUILD_FLAGS="-ldflags='-w -s' -tags=nobadger,nomysql,nopgx" \
CGO_CFLAGS=$(php-config --includes) \
CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \
xcaddy build \
--output frankenphp \
--with frankenphp.dev/caddy \
--with github.com/dunglas/mercure/caddy \
--with github.com/dunglas/vulcain/caddy
# Добавьте дополнительные модули Caddy здесь
```
> [!TIP]
>
> Если вы используете musl libc (по умолчанию в Alpine Linux) и Symfony,
> возможно, потребуется увеличить размер стека.
> В противном случае вы можете столкнуться с ошибками вроде
> `PHP Fatal error: Maximum call stack size of 83360 bytes reached during compilation. Try splitting expression`.
>
> Для этого измените значение переменной окружения `XCADDY_GO_BUILD_FLAGS`, например:
> `XCADDY_GO_BUILD_FLAGS=$'-ldflags "-w -s -extldflags \'-Wl,-z,stack-size=0x80000\'"'`
> (измените значение размера стека в зависимости от требований вашего приложения).

247
docs/ru/config.md Normal file
View File

@@ -0,0 +1,247 @@
# Конфигурация
FrankenPHP, Caddy, а также модули Mercure и Vulcain могут быть настроены с использованием [конфигурационных форматов, поддерживаемых Caddy](https://caddyserver.com/docs/getting-started#your-first-config).
В [Docker-образах](docker.md) файл `Caddyfile` находится по пути `/etc/frankenphp/Caddyfile`.
Статический бинарный файл будет искать `Caddyfile` в директории запуска.
PHP можно настроить [с помощью файла `php.ini`](https://www.php.net/manual/en/configuration.file.php).
PHP-интерпретатор будет искать в следующих местах:
Docker:
- php.ini: `/usr/local/etc/php/php.ini` По умолчанию php.ini не предоставляется.
- дополнительные файлы конфигурации: `/usr/local/etc/php/conf.d/*.ini`
- расширения php: `/usr/local/lib/php/extensions/no-debug-zts-<YYYYMMDD>/`
- Вы должны скопировать официальный шаблон, предоставляемый проектом PHP:
```dockerfile
FROM dunglas/frankenphp
# Для production:
RUN cp $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini
# Или для development:
RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini
```
Установка FrankenPHP (.rpm или .deb):
- php.ini: `/etc/frankenphp/php.ini` По умолчанию предоставляется файл php.ini с производственными настройками.
- дополнительные файлы конфигурации: `/etc/frankenphp/php.d/*.ini`
- расширения php: `/usr/lib/frankenphp/modules/`
Статический бинарный файл:
- php.ini: Директория, в которой выполняется `frankenphp run` или `frankenphp php-server`, затем `/etc/frankenphp/php.ini`
- дополнительные файлы конфигурации: `/etc/frankenphp/php.d/*.ini`
- расширения php: не могут быть загружены
- скопируйте один из шаблонов `php.ini-production` или `php.ini-development`, предоставленных [в исходниках PHP](https://github.com/php/php-src/).
## Конфигурация Caddyfile
[HTTP-директивы](https://caddyserver.com/docs/caddyfile/concepts#directives) `php_server` или `php` могут быть использованы в блоках сайта для обработки вашего PHP-приложения.
Минимальный пример:
```caddyfile
localhost {
# Включить сжатие (опционально)
encode zstd br gzip
# Выполнять PHP-файлы в текущей директории и обслуживать ресурсы
php_server
}
```
Вы также можете явно настроить FrankenPHP с помощью глобальной опции:
[Глобальная опция](https://caddyserver.com/docs/caddyfile/concepts#global-options) `frankenphp` может быть использована для настройки FrankenPHP.
```caddyfile
{
frankenphp {
num_threads <num_threads> # Указывает количество потоков PHP. По умолчанию: 2x от числа доступных CPU.
worker {
file <path> # Указывает путь к worker-скрипту.
num <num> # Указывает количество потоков PHP. По умолчанию: 2x от числа доступных CPU.
env <key> <value> # Устанавливает дополнительную переменную окружения. Можно указать несколько раз для разных переменных.
watch <path> # Указывает путь для отслеживания изменений файлов.Можно указать несколько раз для разных путей.
}
}
}
# ...
```
В качестве альтернативы можно использовать однострочную краткую форму для опции `worker`:
```caddyfile
{
frankenphp {
worker <file> <num>
}
}
# ...
```
Вы также можете определить несколько workers, если обслуживаете несколько приложений на одном сервере:
```caddyfile
app.example.com {
php_server {
root /path/to/app/public
worker index.php <num>
}
}
other.example.com {
php_server {
root /path/to/other/public
worker index.php <num>
}
}
# ...
```
Использование директивы `php_server` — это то, что нужно в большинстве случаев. Однако если требуется полный контроль, вы можете использовать более низкоуровневую директиву `php`:
Использование директивы `php_server` эквивалентно следующей конфигурации:
```caddyfile
route {
# Добавить слэш в конец запросов к директориям
@canonicalPath {
file {path}/index.php
not path */
}
redir @canonicalPath {path}/ 308
# Если запрошенный файл не существует, попытаться использовать файлы index
@indexFiles file {
try_files {path} {path}/index.php index.php
split_path .php
}
rewrite @indexFiles {http.matchers.file.relative}
# FrankenPHP!
@phpFiles path *.php
php @phpFiles
file_server
}
```
Директивы `php_server` и `php` имеют следующие опции:
```caddyfile
php_server [<matcher>] {
root <directory> # Указывает корневую директорию сайта. По умолчанию: директива `root`.
split_path <delim...> # Устанавливает подстроки для разделения URI на две части. Первая часть будет использована как имя ресурса (CGI-скрипта), вторая часть — как PATH_INFO. По умолчанию: `.php`.
resolve_root_symlink false # Отключает разрешение символьных ссылок для `root` (включено по умолчанию).
env <key> <value> # Устанавливает дополнительные переменные окружения. Можно указать несколько раз для разных переменных.
file_server off # Отключает встроенную директиву file_server.
worker { # Создает worker, специфичный для этого сервера. Можно указать несколько раз для разных workers.
file <path> # Указывает путь к worker-скрипту, может быть относительным к корню php_server
num <num> # Указывает количество потоков PHP. По умолчанию: 2x от числа доступных CPU.
name <name> # Устанавливает имя для worker, используемое в логах и метриках. По умолчанию: абсолютный путь к файлу worker. Всегда начинается с m# при определении в блоке php_server.
watch <path> # Указывает путь для отслеживания изменений файлов. Можно указать несколько раз для разных путей.
env <key> <value> # Устанавливает дополнительную переменную окружения. Можно указать несколько раз для разных переменных. Переменные окружения для этого worker также наследуются от родительского php_server, но могут быть переопределены здесь.
}
worker <other_file> <num> # Также можно использовать краткую форму как в глобальном блоке frankenphp.
}
```
### Отслеживание изменений файлов
Поскольку workers запускают ваше приложение только один раз и держат его в памяти, изменения в PHP-файлах не будут применяться сразу.
Для разработки можно настроить перезапуск workers при изменении файлов с помощью директивы `watch`:
```caddyfile
{
frankenphp {
worker {
file /path/to/app/public/worker.php
watch
}
}
}
```
Если директория для `watch` не указана, по умолчанию будет использоваться путь `./**/*.{php,yaml,yml,twig,env}`,
который отслеживает все файлы с расширениями `.php`, `.yaml`, `.yml`, `.twig` и `.env` в директории, где был запущен процесс FrankenPHP, и во всех её поддиректориях. Вы также можете указать одну или несколько директорий с использованием [шаблона имён файлов](https://pkg.go.dev/path/filepath#Match):
```caddyfile
{
frankenphp {
worker {
file /path/to/app/public/worker.php
watch /path/to/app # отслеживает все файлы во всех поддиректориях /path/to/app
watch /path/to/app/*.php # отслеживает файлы с расширением .php в /path/to/app
watch /path/to/app/**/*.php # отслеживает PHP-файлы в /path/to/app и поддиректориях
watch /path/to/app/**/*.{php,twig} # отслеживает PHP и Twig-файлы в /path/to/app и поддиректориях
}
}
}
```
- Шаблон `**` указывает на рекурсивное отслеживание.
- Директории могут быть указаны относительно директории запуска FrankenPHP.
- Если у вас определено несколько workers, все они будут перезапущены при изменении файлов.
- Избегайте отслеживания файлов, создаваемых во время выполнения (например, логов), так как это может вызвать нежелательные перезапуски.
Механизм отслеживания файлов основан на [e-dant/watcher](https://github.com/e-dant/watcher).
### Полный дуплекс (HTTP/1)
При использовании HTTP/1.x можно включить режим полного дуплекса, чтобы разрешить запись ответа до завершения чтения тела запроса (например, для WebSocket, Server-Sent Events и т.д.).
Эта опция включается вручную и должна быть добавлена в глобальные настройки `Caddyfile`:
```caddyfile
{
servers {
enable_full_duplex
}
}
```
> [!CAUTION]
>
> Включение этой опции может привести к зависанию устаревших HTTP/1.x клиентов, которые не поддерживают полный дуплекс.
> Настройка также доступна через переменную окружения `CADDY_GLOBAL_OPTIONS`:
```sh
CADDY_GLOBAL_OPTIONS="servers {
enable_full_duplex
}"
```
Дополнительную информацию об этой настройке можно найти в [документации Caddy](https://caddyserver.com/docs/caddyfile/options#enable-full-duplex).
## Переменные окружения
Следующие переменные окружения могут быть использованы для добавления директив в `Caddyfile` без его изменения:
- `SERVER_NAME`: изменение [адресов для прослушивания](https://caddyserver.com/docs/caddyfile/concepts#addresses); предоставленные хостнеймы также будут использованы для генерации TLS-сертификата.
- `CADDY_GLOBAL_OPTIONS`: добавление [глобальных опций](https://caddyserver.com/docs/caddyfile/options).
- `FRANKENPHP_CONFIG`: добавление конфигурации в директиву `frankenphp`.
Как и для FPM и CLI SAPIs, переменные окружения по умолчанию доступны в суперглобальной переменной `$_SERVER`.
Значение `S` в [директиве PHP `variables_order`](https://www.php.net/manual/en/ini.core.php#ini.variables-order) всегда эквивалентно `ES`, независимо от того, где расположена `E` в этой директиве.
## Конфигурация PHP
Для загрузки [дополнительных конфигурационных файлов PHP](https://www.php.net/manual/en/configuration.file.php#configuration.file.scan) можно использовать переменную окружения `PHP_INI_SCAN_DIR`.
Если она установлена, PHP загрузит все файлы с расширением `.ini`, находящиеся в указанных директориях.
## Включение режима отладки
При использовании Docker-образа установите переменную окружения `CADDY_GLOBAL_OPTIONS` в `debug`, чтобы включить режим отладки:
```console
docker run -v $PWD:/app/public \
-e CADDY_GLOBAL_OPTIONS=debug \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```

201
docs/ru/docker.md Normal file
View File

@@ -0,0 +1,201 @@
# Создание кастомных Docker-образов
[Docker-образы FrankenPHP](https://hub.docker.com/r/dunglas/frankenphp) основаны на [официальных PHP-образах](https://hub.docker.com/_/php/). Доступны варианты для Debian и Alpine Linux для популярных архитектур. Рекомендуется использовать Debian-варианты.
Доступны версии для PHP 8.2, 8.3 и 8.4.
Теги следуют следующему шаблону: `dunglas/frankenphp:<frankenphp-version>-php<php-version>-<os>`.
- `<frankenphp-version>` и `<php-version>` — версии FrankenPHP и PHP соответственно: от основных (например, `1`) до минорных (например, `1.2`) и патч-версий (например, `1.2.3`).
- `<os>` может быть `bookworm` (для Debian Bookworm) или `alpine` (для последней стабильной версии Alpine).
[Просмотреть доступные теги](https://hub.docker.com/r/dunglas/frankenphp/tags).
## Как использовать образы
Создайте `Dockerfile` в вашем проекте:
```dockerfile
FROM dunglas/frankenphp
COPY . /app/public
```
Затем выполните следующие команды для сборки и запуска Docker-образа:
```console
docker build -t my-php-app .
docker run -it --rm --name my-running-app my-php-app
```
## Как установить дополнительные PHP-расширения
Скрипт [`docker-php-extension-installer`](https://github.com/mlocati/docker-php-extension-installer) включён в базовый образ. Установка дополнительных PHP-расширений осуществляется просто:
```dockerfile
FROM dunglas/frankenphp
# Добавьте дополнительные расширения здесь:
RUN install-php-extensions \
pdo_mysql \
gd \
intl \
zip \
opcache
```
## Как установить дополнительные модули Caddy
FrankenPHP построен на базе Caddy, и все [модули Caddy](https://caddyserver.com/docs/modules/) можно использовать с FrankenPHP.
Самый простой способ установить пользовательские модули Caddy — использовать [xcaddy](https://github.com/caddyserver/xcaddy):
```dockerfile
FROM dunglas/frankenphp:builder AS builder
# Копируем xcaddy в образ сборки
COPY --from=caddy:builder /usr/bin/xcaddy /usr/bin/xcaddy
# Для сборки FrankenPHP необходимо включить CGO
RUN CGO_ENABLED=1 \
XCADDY_SETCAP=1 \
XCADDY_GO_BUILD_FLAGS="-ldflags='-w -s' -tags=nobadger,nomysql,nopgx" \
CGO_CFLAGS=$(php-config --includes) \
CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \
xcaddy build \
--output /usr/local/bin/frankenphp \
--with frankenphp.dev=./ \
--with frankenphp.dev/caddy=./caddy/ \
--with github.com/dunglas/caddy-cbrotli \
# Mercure и Vulcain включены в официальный билд, но вы можете их удалить
--with github.com/dunglas/mercure/caddy \
--with github.com/dunglas/vulcain/caddy
# Добавьте дополнительные модули Caddy здесь
FROM dunglas/frankenphp AS runner
# Заменяем официальный бинарный файл на пользовательский с добавленными модулями
COPY --from=builder /usr/local/bin/frankenphp /usr/local/bin/frankenphp
```
Образ `builder`, предоставляемый FrankenPHP, содержит скомпилированную версию `libphp`.
[Образы builder](https://hub.docker.com/r/dunglas/frankenphp/tags?name=builder) доступны для всех версий FrankenPHP и PHP, как для Debian, так и для Alpine.
> [!TIP]
>
> Если вы используете Alpine Linux и Symfony, возможно, потребуется [увеличить размер стека](compile.md#использование-xcaddy).
## Активировать worker режим по умолчанию
Установите переменную окружения `FRANKENPHP_CONFIG`, чтобы запускать FrankenPHP с Worker-скриптом:
```dockerfile
FROM dunglas/frankenphp
# ...
ENV FRANKENPHP_CONFIG="worker ./public/index.php"
```
## Использование тома в разработке
Для удобной разработки с FrankenPHP смонтируйте директорию с исходным кодом приложения на хосте как том в Docker-контейнере:
```console
docker run -v $PWD:/app/public -p 80:80 -p 443:443 -p 443:443/udp --tty my-php-app
```
> [!TIP]
>
> Опция `--tty` позволяет видеть удобочитаемые логи вместо JSON-формата.
С использованием Docker Compose:
```yaml
# compose.yaml
services:
php:
image: dunglas/frankenphp
# раскомментируйте следующую строку, если хотите использовать собственный Dockerfile
#build: .
# раскомментируйте следующую строку, если вы запускаете это в продакшн среде
# restart: always
ports:
- "80:80" # HTTP
- "443:443" # HTTPS
- "443:443/udp" # HTTP/3
volumes:
- ./:/app/public
- caddy_data:/data
- caddy_config:/config
# закомментируйте следующую строку в продакшн среде, она позволяет получать удобочитаемые логи в режиме разработки
tty: true
# Томы, необходимые для сертификатов и конфигурации Caddy
volumes:
caddy_data:
caddy_config:
```
## Запуск под обычным пользователем
FrankenPHP поддерживает запуск под обычным пользователем в Docker.
Пример `Dockerfile` для этого:
```dockerfile
FROM dunglas/frankenphp
ARG USER=appuser
RUN \
# Для дистрибутивов на основе Alpine используйте "adduser -D ${USER}"
useradd ${USER}; \
# Добавьте возможность привязываться к портам 80 и 443
setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp; \
# Дайте права на запись для /data/caddy и /config/caddy
chown -R ${USER}:${USER} /data/caddy && chown -R ${USER}:${USER} /config/caddy
USER ${USER}
```
### Запуск без дополнительных прав
Даже при запуске без root-прав, FrankenPHP требуется возможность `CAP_NET_BIND_SERVICE` для привязки веб-сервера к зарезервированным портам (80 и 443).
Если вы открываете доступ к FrankenPHP на непривилегированном порту (1024 и выше), можно запустить веб-сервер от имени обычного пользователя без необходимости предоставления дополнительных возможностей:
```dockerfile
FROM dunglas/frankenphp
ARG USER=appuser
RUN \
# Для Alpine-дистрибутивов используйте команду "adduser -D ${USER}"
useradd ${USER}; \
# Удалите стандартные возможности
setcap -r /usr/local/bin/frankenphp; \
# Дайте права на запись для /data/caddy и /config/caddy
chown -R ${USER}:${USER} /data/caddy && chown -R ${USER}:${USER} /config/caddy
USER ${USER}
```
Затем установите переменную окружения `SERVER_NAME`, чтобы использовать непривилегированный порт.
Пример: `:8000`.
## Обновления
Docker-образы обновляются:
- при выпуске новой версии;
- ежедневно в 4 утра UTC, если доступны новые версии официальных PHP-образов.
## Версии для разработки
Версии для разработки доступны в Docker-репозитории [`dunglas/frankenphp-dev`](https://hub.docker.com/repository/docker/dunglas/frankenphp-dev).
Сборка запускается автоматически при каждом коммите в основную ветку GitHub-репозитория
Теги с префиксом `latest*` указывают на актуальное состояние ветки `main`.
Также доступны теги в формате `sha-<git-commit-hash>`.

21
docs/ru/early-hints.md Normal file
View File

@@ -0,0 +1,21 @@
# Early Hints
FrankenPHP изначально поддерживает [Early Hints (103 HTTP статус код)](https://developer.chrome.com/blog/early-hints/).
Использование Early Hints может улучшить время загрузки ваших веб-страниц на 30%.
```php
<?php
header('Link: </style.css>; rel=preload; as=style');
headers_send(103);
// ваши медленные алгоритмы и SQL-запросы 🤪
echo <<<'HTML'
<!DOCTYPE html>
<title>Hello FrankenPHP</title>
<link rel="stylesheet" href="style.css">
HTML;
```
Early Hints поддерживается как в обычном, так и в [worker режиме](worker.md).

140
docs/ru/embed.md Normal file
View File

@@ -0,0 +1,140 @@
# PHP-приложения как автономные бинарные файлы
FrankenPHP позволяет встраивать исходный код и ресурсы PHP-приложений в статический автономный бинарный файл.
Благодаря этой функции PHP-приложения могут распространяться как автономные бинарные файлы, которые содержат само приложение, интерпретатор PHP и Caddy — веб-сервер уровня продакшн.
Подробнее об этой функции [в презентации Кевина на SymfonyCon 2023](https://dunglas.dev/2023/12/php-and-symfony-apps-as-standalone-binaries/).
Для встраивания Laravel-приложений ознакомьтесь с [документацией](laravel.md#laravel-приложения-как-автономные-бинарные-файлы).
## Подготовка приложения
Перед созданием автономного бинарного файла убедитесь, что ваше приложение готово для встраивания.
Например, вам может понадобиться:
- Установить продакшн-зависимости приложения.
- Сгенерировать автозагрузчик.
- Включить продакшн-режим приложения (если он есть).
- Удалить ненужные файлы, такие как `.git` или тесты, чтобы уменьшить размер итогового бинарного файла.
Для приложения на Symfony это может выглядеть так:
```console
# Экспорт проекта, чтобы избавиться от .git/ и других ненужных файлов
mkdir $TMPDIR/my-prepared-app
git archive HEAD | tar -x -C $TMPDIR/my-prepared-app
cd $TMPDIR/my-prepared-app
# Установить соответствующие переменные окружения
echo APP_ENV=prod > .env.local
echo APP_DEBUG=0 >> .env.local
# Удалить тесты и другие ненужные файлы
rm -Rf tests/
# Установить зависимости
composer install --ignore-platform-reqs --no-dev -a
# Оптимизировать .env
composer dump-env prod
```
### Настройка конфигурации
Чтобы настроить [конфигурацию](config.md), вы можете разместить файлы `Caddyfile` и `php.ini` в основной директории приложения (`$TMPDIR/my-prepared-app` в примере выше).
## Создание бинарного файла для Linux
Самый простой способ создать бинарный файл для Linux — использовать предоставленный Docker-билдер.
1. Создайте файл `static-build.Dockerfile` в репозитории вашего приложения:
```dockerfile
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder
# Скопировать приложение
WORKDIR /go/src/app/dist/app
COPY . .
# Сборка статического бинарного файла
WORKDIR /go/src/app/
RUN EMBED=dist/app/ ./build-static.sh
```
> [!CAUTION]
>
> Некоторые `.dockerignore` файлы (например, [Symfony Docker `.dockerignore`](https://github.com/dunglas/symfony-docker/blob/main/.dockerignore))
> игнорируют директорию `vendor/` и файлы `.env`. Перед сборкой убедитесь, что `.dockerignore` файл настроен корректно или удалён.
2. Соберите образ:
```console
docker build -t static-app -f static-build.Dockerfile .
```
3. Извлеките бинарный файл:
```console
docker cp $(docker create --name static-app-tmp static-app):/go/src/app/dist/frankenphp-linux-x86_64 my-app ; docker rm static-app-tmp
```
Созданный бинарный файл сохранится в текущей директории под именем `my-app`.
## Создание бинарного файла для других ОС
Если вы не хотите использовать Docker или хотите собрать бинарный файл для macOS, используйте предоставленный скрипт:
```console
git clone https://github.com/dunglas/frankenphp
cd frankenphp
EMBED=/path/to/your/app ./build-static.sh
```
Итоговый бинарный файл будет находиться в директории `dist/` под именем `frankenphp-<os>-<arch>`.
## Использование бинарного файла
Готово! Файл `my-app` (или `dist/frankenphp-<os>-<arch>` для других ОС) содержит ваше автономное приложение.
Для запуска веб-приложения выполните:
```console
./my-app php-server
```
Если ваше приложение содержит [worker-скрипт](worker.md), запустите его следующим образом:
```console
./my-app php-server --worker public/index.php
```
Чтобы включить HTTPS (Let's Encrypt автоматически создаст сертификат), HTTP/2 и HTTP/3, укажите доменное имя:
```console
./my-app php-server --domain localhost
```
Вы также можете запускать PHP-скрипты CLI, встроенные в бинарный файл:
```console
./my-app php-cli bin/console
```
## PHP-расширения
По умолчанию скрипт собирает расширения, указанные в `composer.json` вашего проекта.
Если файла `composer.json` нет, собираются стандартные расширения, как указано в [документации по статической сборке](static.md).
Чтобы настроить список расширений, используйте переменную окружения `PHP_EXTENSIONS`.
## Настройка сборки
[Ознакомьтесь с документацией по статической сборке](static.md), чтобы узнать, как настроить бинарный файл (расширения, версию PHP и т.д.).
## Распространение бинарного файла
На Linux созданный бинарный файл сжимается с помощью [UPX](https://upx.github.io).
На Mac для уменьшения размера файла перед отправкой его можно сжать. Рекомендуется использовать `xz`.

30
docs/ru/github-actions.md Normal file
View File

@@ -0,0 +1,30 @@
# Использование GitHub Actions
Этот репозиторий автоматически собирает и публикует Docker-образы в [Docker Hub](https://hub.docker.com/r/dunglas/frankenphp) для каждого одобренного pull request или вашего собственного форка после настройки.
## Настройка GitHub Actions
В настройках репозитория, в разделе "Secrets", добавьте следующие секреты:
- `REGISTRY_LOGIN_SERVER`: Docker-реестр, который будет использоваться (например, `docker.io`).
- `REGISTRY_USERNAME`: Имя пользователя для входа в реестр (например, `dunglas`).
- `REGISTRY_PASSWORD`: Пароль для входа в реестр (например, токен доступа).
- `IMAGE_NAME`: Имя образа (например, `dunglas/frankenphp`).
## Сборка и загрузка образа
1. Создайте Pull Request или выполните push в ваш форк.
2. GitHub Actions соберёт образ и выполнит тесты.
3. Если сборка пройдёт успешно, образ будет отправлен в реестр с тегом `pr-x`, где `x` — номер PR.
## Развёртывание образа
1. После слияния Pull Request GitHub Actions выполнит повторные тесты и соберёт новый образ.
2. Если сборка пройдёт успешно, тег `main` будет обновлён в Docker-реестре.
## Релизы
1. Создайте новый тег в репозитории.
2. GitHub Actions соберёт образ и выполнит тесты.
3. Если сборка пройдёт успешно, образ будет отправлен в реестр с именем тега (например, `v1.2.3` и `v1.2` будут созданы).
4. Также будет обновлён тег `latest`.

140
docs/ru/known-issues.md Normal file
View File

@@ -0,0 +1,140 @@
# Известные проблемы
## Неподдерживаемые расширения PHP
Следующие расширения не совместимы с FrankenPHP:
| Название | Причина | Альтернативы |
| ----------------------------------------------------------------------------------------------------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| [imap](https://www.php.net/manual/en/imap.installation.php) | Не поддерживает потокобезопасность | [javanile/php-imap2](https://github.com/javanile/php-imap2), [webklex/php-imap](https://github.com/Webklex/php-imap) |
| [newrelic](https://docs.newrelic.com/docs/apm/agents/php-agent/getting-started/introduction-new-relic-php/) | Не поддерживает потокобезопасность | - |
## Проблемные расширения PHP
Следующие расширения имеют известные ошибки или могут вести себя непредсказуемо при использовании с FrankenPHP:
| Название | Проблема |
| ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [ext-openssl](https://www.php.net/manual/en/book.openssl.php) | При использовании статической сборки FrankenPHP (на базе musl libc) расширение OpenSSL может аварийно завершаться при высокой нагрузке. Решение — использовать динамически связанную сборку (например, ту, что используется в Docker-образах). Ошибка [отслеживается сообществом PHP](https://github.com/php/php-src/issues/13648). |
## `get_browser`
Функция [get_browser()](https://www.php.net/manual/en/function.get-browser.php) начинает работать медленно через некоторое время. Решение — кэшировать результаты для каждого User-Agent, например, с помощью [APCu](https://www.php.net/manual/en/book.apcu.php), так как они статичны.
## Автономные бинарные файлы и образы на базе Alpine
Автономные бинарные файлы и образы на базе Alpine (`dunglas/frankenphp:*-alpine`) используют [musl libc](https://musl.libc.org/) вместо [glibc](https://www.etalabs.net/compare_libcs.html) для уменьшения размера бинарных файлов. Это может вызвать проблемы совместимости. В частности, флаг `GLOB_BRACE` в функции glob [не поддерживается](https://www.php.net/manual/en/function.glob.php).
## Использование `https://127.0.0.1` с Docker
По умолчанию FrankenPHP генерирует TLS-сертификат для `localhost`, что является самым простым и рекомендуемым вариантом для локальной разработки.
Если вы всё же хотите использовать `127.0.0.1`, настройте генерацию сертификата, указав в переменной окружения `SERVER_NAME` значение `127.0.0.1`.
Однако этого может не хватить при использовании Docker из-за [особенностей его сетевой системы](https://docs.docker.com/network/). Возможна ошибка TLS вида:
`curl: (35) LibreSSL/3.3.6: error:1404B438:SSL routines:ST_CONNECT:tlsv1 alert internal error`.
Если вы используете Linux, можно воспользоваться [host-драйвером](https://docs.docker.com/network/network-tutorial-host/):
```console
docker run \
-e SERVER_NAME="127.0.0.1" \
-v $PWD:/app/public \
--network host \
dunglas/frankenphp
```
Host-драйвер не поддерживается на Mac и Windows. На этих платформах нужно определить IP-адрес контейнера и включить его в `SERVER_NAME`.
Выполните команду `docker network inspect bridge`, найдите ключ `Containers` и определите последний присвоенный IP из `IPv4Address`. Увеличьте его на единицу. Если контейнеров нет, первый IP обычно `172.17.0.2`.
Включите этот IP в переменную окружения `SERVER_NAME`:
```console
docker run \
-e SERVER_NAME="127.0.0.1, 172.17.0.3" \
-v $PWD:/app/public \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
> [!CAUTION]
> Обязательно замените `172.17.0.3` на IP, который будет присвоен вашему контейнеру.
Теперь вы должны иметь доступ к `https://127.0.0.1`.
Если это не так, запустите FrankenPHP в режиме отладки:
```console
docker run \
-e CADDY_GLOBAL_OPTIONS="debug" \
-e SERVER_NAME="127.0.0.1" \
-v $PWD:/app/public \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
## Скрипты Composer с использованием `@php`
[Скрипты Composer](https://getcomposer.org/doc/articles/scripts.md) могут вызывать PHP для выполнения задач, например, в [проекте Laravel](laravel.md) для команды `@php artisan package:discover --ansi`.
Это [на данный момент не поддерживается](https://github.com/dunglas/frankenphp/issues/483#issuecomment-1899890915) по двум причинам:
- Composer не знает, как вызывать бинарный файл FrankenPHP;
- Composer может добавлять настройки PHP через флаг `-d`, который FrankenPHP пока не поддерживает.
Решение — создать shell-скрипт в `/usr/local/bin/php`, который удаляет неподдерживаемые параметры и вызывает FrankenPHP:
```bash
#!/usr/bin/env bash
args=("$@")
index=0
for i in "$@"
do
if [ "$i" == "-d" ]; then
unset 'args[$index]'
unset 'args[$index+1]'
fi
index=$((index+1))
done
/usr/local/bin/frankenphp php-cli ${args[@]}
```
Затем установите переменную окружения `PHP_BINARY` на путь к нашему скрипту `php` и запустите Composer:
```console
export PHP_BINARY=/usr/local/bin/php
composer install
```
## TLS/SSL: проблемы со статическими бинарными файлами
При использовании статических бинарных файлов могут возникать следующие ошибки TLS, например, при отправке писем через STARTTLS:
```text
Unable to connect with STARTTLS: stream_socket_enable_crypto(): SSL operation failed with code 5. OpenSSL Error messages:
error:80000002:system library::No such file or directory
error:80000002:system library::No such file or directory
error:80000002:system library::No such file or directory
error:0A000086:SSL routines::certificate verify failed
```
Статический бинарный файл не включает TLS-сертификаты, поэтому необходимо указать OpenSSL местоположение локальных сертификатов CA.
Выполните [`openssl_get_cert_locations()`](https://www.php.net/manual/en/function.openssl-get-cert-locations.php), чтобы определить, где должны находиться сертификаты CA, и поместите их туда.
> [!WARNING]
> Веб и CLI контексты могут иметь разные настройки.
> Запустите `openssl_get_cert_locations()` в нужном контексте.
[Сертификаты CA, извлечённые из Mozilla, можно скачать с сайта curl](https://curl.se/docs/caextract.html).
Кроме того, многие дистрибутивы, такие как Debian, Ubuntu и Alpine, предоставляют пакеты `ca-certificates`, содержащие эти сертификаты.
Также можно использовать переменные `SSL_CERT_FILE` и `SSL_CERT_DIR`, чтобы указать OpenSSL, где искать сертификаты CA:
```console
# Установите переменные окружения для TLS-сертификатов
export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
export SSL_CERT_DIR=/etc/ssl/certs
```

176
docs/ru/laravel.md Normal file
View File

@@ -0,0 +1,176 @@
# Laravel
## Docker
Запустить [Laravel](https://laravel.com) веб-приложение с FrankenPHP очень просто: достаточно смонтировать проект в директорию `/app` официального Docker-образа.
Выполните эту команду из корневой директории вашего Laravel-приложения:
```console
docker run -p 80:80 -p 443:443 -p 443:443/udp -v $PWD:/app dunglas/frankenphp
```
И наслаждайтесь!
## Локальная установка
Вы также можете запустить ваши Laravel-проекты с FrankenPHP на локальной машине:
1. [Скачайте бинарный файл для вашей системы](README.md#автономный-бинарный-файл)
2. Добавьте следующую конфигурацию в файл с именем `Caddyfile` в корневой директории вашего Laravel-проекта:
```caddyfile
{
frankenphp
}
# Доменное имя вашего сервера
localhost {
# Укажите веб-корень как директорию public/
root public/
# Включите сжатие (опционально)
encode zstd br gzip
# Выполняйте PHP-файлы из директории public/ и обслуживайте статические файлы
php_server
}
```
3. Запустите FrankenPHP из корневой директории вашего Laravel-проекта: `frankenphp run`
## Laravel Octane
Octane можно установить с помощью менеджера пакетов Composer:
```console
composer require laravel/octane
```
После установки Octane выполните Artisan-команду `octane:install`, которая создаст конфигурационный файл Octane в вашем приложении:
```console
php artisan octane:install --server=frankenphp
```
Сервер Octane можно запустить с помощью Artisan-команды `octane:frankenphp`:
```console
php artisan octane:frankenphp
```
Команда `octane:frankenphp` поддерживает следующие опции:
- `--host`: IP-адрес, к которому должен привязаться сервер (по умолчанию: `127.0.0.1`)
- `--port`: Порт, на котором сервер будет доступен (по умолчанию: `8000`)
- `--admin-port`: Порт, на котором будет доступен административный сервер (по умолчанию: `2019`)
- `--workers`: Количество worker-скриптов для обработки запросов (по умолчанию: `auto`)
- `--max-requests`: Количество запросов, обрабатываемых перед перезагрузкой сервера (по умолчанию: `500`)
- `--caddyfile`: Путь к файлу `Caddyfile` FrankenPHP (по умолчанию: [stubbed `Caddyfile` в Laravel Octane](https://github.com/laravel/octane/blob/2.x/src/Commands/stubs/Caddyfile))
- `--https`: Включить HTTPS, HTTP/2 и HTTP/3, а также автоматически генерировать и обновлять сертификаты
- `--http-redirect`: Включить редирект с HTTP на HTTPS (включается только при передаче --https)
- `--watch`: Автоматически перезагружать сервер при изменении приложения
- `--poll`: Использовать опрос файловой системы для отслеживания изменений в файлах через сеть
- `--log-level`: Установить уровень логирования, используя встроенный логгер Caddy
> [!TIP]
> Чтобы получить структурированные JSON-логи (полезно при использовании решений для анализа логов), явно укажите опцию `--log-level`.
Подробнее о [Laravel Octane читайте в официальной документации](https://laravel.com/docs/octane).
## Laravel-приложения как автономные бинарные файлы
Используя [возможность встраивания приложений в FrankenPHP](embed.md), можно распространять Laravel-приложения как автономные бинарные файлы.
Следуйте этим шагам, чтобы упаковать ваше Laravel-приложение в автономный бинарный файл для Linux:
1. Создайте файл с именем `static-build.Dockerfile` в репозитории вашего приложения:
```dockerfile
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder
# Скопируйте ваше приложение
WORKDIR /go/src/app/dist/app
COPY . .
# Удалите тесты и другие ненужные файлы, чтобы сэкономить место
# В качестве альтернативы добавьте эти файлы в .dockerignore
RUN rm -Rf tests/
# Скопируйте файл .env
RUN cp .env.example .env
# Измените APP_ENV и APP_DEBUG для продакшна
RUN sed -i'' -e 's/^APP_ENV=.*/APP_ENV=production/' -e 's/^APP_DEBUG=.*/APP_DEBUG=false/' .env
# Внесите другие изменения в файл .env, если необходимо
# Установите зависимости
RUN composer install --ignore-platform-reqs --no-dev -a
# Соберите статический бинарный файл
WORKDIR /go/src/app/
RUN EMBED=dist/app/ ./build-static.sh
```
> [!CAUTION]
>
> Некоторые `.dockerignore` файлы могут игнорировать директорию `vendor/` и файлы `.env`. Убедитесь, что вы скорректировали или удалили `.dockerignore` перед сборкой.
2. Соберите:
```console
docker build -t static-laravel-app -f static-build.Dockerfile .
```
3. Извлеките бинарный файл:
```console
docker cp $(docker create --name static-laravel-app-tmp static-laravel-app):/go/src/app/dist/frankenphp-linux-x86_64 frankenphp ; docker rm static-laravel-app-tmp
```
4. Заполните кеши:
```console
frankenphp php-cli artisan optimize
```
5. Запустите миграции базы данных (если есть):
```console
frankenphp php-cli artisan migrate
```
6. Сгенерируйте секретный ключ приложения:
```console
frankenphp php-cli artisan key:generate
```
7. Запустите сервер:
```console
frankenphp php-server
```
Ваше приложение готово!
Узнайте больше о доступных опциях и о том, как собирать бинарные файлы для других ОС в [документации по встраиванию приложений](embed.md).
### Изменение пути хранения
По умолчанию Laravel сохраняет загруженные файлы, кеши, логи и другие данные в директории `storage/` приложения. Это неудобно для встроенных приложений, так как каждая новая версия будет извлекаться в другую временную директорию.
Установите переменную окружения `LARAVEL_STORAGE_PATH` (например, в вашем `.env` файле) или вызовите метод `Illuminate\Foundation\Application::useStoragePath()`, чтобы использовать директорию за пределами временной директории.
### Запуск Octane как автономный бинарный файл
Можно даже упаковать приложения Laravel Octane как автономный бинарный файл!
Для этого [установите Octane правильно](#laravel-octane) и следуйте шагам, описанным в [предыдущем разделе](#laravel-приложения-как-автономные-бинарные-файлы).
Затем, чтобы запустить FrankenPHP в worker-режиме через Octane, выполните:
```console
PATH="$PWD:$PATH" frankenphp php-cli artisan octane:frankenphp
```
> [!CAUTION]
> Для работы команды автономный бинарник **обязательно** должен быть назван `frankenphp`, так как Octane требует наличия программы с именем `frankenphp` в PATH.

12
docs/ru/mercure.md Normal file
View File

@@ -0,0 +1,12 @@
# Real-time режим
FrankenPHP поставляется с встроенным хабом [Mercure](https://mercure.rocks)!
Mercure позволяет отправлять события в режиме реального времени на все подключённые устройства: они мгновенно получат JavaScript-событие.
Не требуются JS-библиотеки или SDK!
![Mercure](../mercure-hub.png)
Чтобы включить хаб Mercure, обновите `Caddyfile` в соответствии с инструкциями [на сайте Mercure](https://mercure.rocks/docs/hub/config).
Для отправки обновлений Mercure из вашего кода мы рекомендуем использовать [Symfony Mercure Component](https://symfony.com/components/Mercure) (для его использования не требуется полный стек Symfony).

15
docs/ru/metrics.md Normal file
View File

@@ -0,0 +1,15 @@
# Метрики
При включении [метрик Caddy](https://caddyserver.com/docs/metrics) FrankenPHP предоставляет следующие метрики:
- `frankenphp_[worker]_total_workers`: Общее количество worker-скриптов.
- `frankenphp_[worker]_busy_workers`: Количество worker-скриптов, которые в данный момент обрабатывают запрос.
- `frankenphp_[worker]_worker_request_time`: Время, затраченное всеми worker-скриптами на обработку запросов.
- `frankenphp_[worker]_worker_request_count`: Количество запросов, обработанных всеми worker-скриптами.
- `frankenphp_[worker]_ready_workers`: Количество worker-скриптов, которые вызвали `frankenphp_handle_request` хотя бы один раз.
- `frankenphp_[worker]_worker_crashes`: Количество случаев неожиданного завершения worker-скриптов.
- `frankenphp_[worker]_worker_restarts`: Количество случаев, когда worker-скрипт был перезапущен целенаправленно.
- `frankenphp_total_threads`: Общее количество потоков PHP.
- `frankenphp_busy_threads`: Количество потоков PHP, которые в данный момент обрабатывают запрос (работающие worker-скрипты всегда используют поток).
Для метрик worker-скриптов плейсхолдер `[worker]` заменяется на путь к Worker-скрипту, указанному в Caddyfile.

101
docs/ru/performance.md Normal file
View File

@@ -0,0 +1,101 @@
# Производительность
По умолчанию FrankenPHP предлагает хороший баланс между производительностью и удобством использования.
Однако, используя подходящую конфигурацию, можно существенно улучшить производительность.
## Количество потоков и worker-скриптов
По умолчанию FrankenPHP запускает потоков и worker-скриптов (в worker режиме) вдвое больше, чем количество доступных процессорных ядер.
Оптимальные значения зависят от структуры вашего приложения, его функциональности и аппаратного обеспечения.
Мы настоятельно рекомендуем изменить эти значения.
Чтобы найти подходящие параметры, лучше всего провести нагрузочные тесты, имитирующие реальный трафик.
Хорошими инструментами для этого являются [k6](https://k6.io) и [Gatling](https://gatling.io).
Чтобы настроить количество потоков, используйте опцию `num_threads` в директивах `php_server` и `php`.
Для изменения количества worker-скриптов используйте опцию `num` в секции `worker` директивы `frankenphp`.
## Worker режим
Включение [Worker режима](worker.md) значительно улучшает производительность,
но ваше приложение должно быть адаптировано для совместимости с этим режимом:
необходимо создать worker-скрипт и убедиться, что приложение не имеет утечек памяти.
## Избегайте использования musl
Статические бинарники, которые мы предоставляем, а также Alpine Linux-вариант официальных Docker-образов используют [библиотеку musl libc](https://musl.libc.org).
Известно, что PHP [значительно медленнее работает](https://gitlab.alpinelinux.org/alpine/aports/-/issues/14381) с этой библиотекой по сравнению с традиционной GNU libc, особенно при компиляции в ZTS режиме (потокобезопасный режим), который требуется для FrankenPHP.
Кроме того, [некоторые ошибки проявляются исключительно при использовании musl](https://github.com/php/php-src/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3ABug+musl).
В производственной среде настоятельно рекомендуется использовать glibc.
Это можно сделать, используя Debian Docker-образы (по умолчанию) и [компилируя FrankenPHP из исходников](compile.md).
В качестве альтернативы мы предоставляем статические бинарники, скомпилированные с [аллокатором mimalloc](https://github.com/microsoft/mimalloc), что делает FrankenPHP+musl быстрее (но всё же медленнее, чем FrankenPHP+glibc).
## Настройка среды выполнения Go
FrankenPHP написан на языке Go.
В большинстве случаев среда выполнения Go не требует особой настройки, но в некоторых ситуациях специфическая конфигурация может улучшить производительность.
Рекомендуется установить переменную окружения `GODEBUG` в значение `cgocheck=0` (по умолчанию в Docker-образах FrankenPHP).
Если вы запускаете FrankenPHP в контейнерах (Docker, Kubernetes, LXC и т.д.) и ограничиваете доступную память, установите переменную окружения `GOMEMLIMIT` в значение доступного объёма памяти.
Для более детальной информации ознакомьтесь с [документацией Go по этой теме](https://pkg.go.dev/runtime#hdr-Environment_Variables).
## `file_server`
По умолчанию директива `php_server` автоматически настраивает файловый сервер для обслуживания статических файлов (ресурсов), хранящихся в корневой директории.
Эта функция удобна, но имеет издержки. Чтобы отключить её, используйте следующую конфигурацию:
```caddyfile
php_server {
file_server off
}
```
## Плейсхолдеры
Вы можете использовать [плейсхолдеры](https://caddyserver.com/docs/conventions#placeholders) в директивах `root` и `env`.
Однако это предотвращает кеширование значений и существенно снижает производительность.
По возможности избегайте использования плейсхолдеров в этих директивах.
## `resolve_root_symlink`
По умолчанию, если корневая директория документа является символьной ссылкой, она автоматически разрешается FrankenPHP (это необходимо для корректной работы PHP).
Если корневая директория документа не является символьной ссылкой, вы можете отключить эту функцию.
```caddyfile
php_server {
resolve_root_symlink false
}
```
Это улучшит производительность, если директива `root` содержит [плейсхолдеры](https://caddyserver.com/docs/conventions#placeholders).
В остальных случаях прирост производительности будет минимальным.
## Логи
Логирование, безусловно, полезно, но требует операций ввода-вывода и выделения памяти, что значительно снижает производительность.
Убедитесь, что вы [правильно настроили уровень логирования](https://caddyserver.com/docs/caddyfile/options#log) и логируете только необходимое.
## Производительность PHP
FrankenPHP использует официальный интерпретатор PHP.
Все стандартные оптимизации производительности PHP применимы к FrankenPHP.
В частности:
- убедитесь, что [OPcache](https://www.php.net/manual/en/book.opcache.php) установлен, включён и настроен должным образом;
- включите [оптимизацию автозагрузки Composer](https://getcomposer.org/doc/articles/autoloader-optimization.md);
- убедитесь, что кеш `realpath` достаточно велик для нужд вашего приложения;
- используйте [предварительную загрузку](https://www.php.net/manual/en/opcache.preloading.php).
Для более детальной информации ознакомьтесь с [документацией Symfony о производительности](https://symfony.com/doc/current/performance.html) (большинство советов полезны даже если вы не используете Symfony).

125
docs/ru/production.md Normal file
View File

@@ -0,0 +1,125 @@
# Деплой в продакшен
В этом руководстве мы рассмотрим, как развернуть PHP-приложение на одном сервере с использованием Docker Compose.
Если вы используете Symfony, рекомендуется прочитать раздел "[Deploy in production](https://github.com/dunglas/symfony-docker/blob/main/docs/production.md)" документации проекта Symfony Docker (в котором используется FrankenPHP).
Если вы используете API Platform (который также работает с FrankenPHP), ознакомьтесь с [документацией по деплою этого фреймворка](https://api-platform.com/docs/deployment/).
## Подготовка приложения
Сначала создайте файл `Dockerfile` в корневой директории вашего PHP-проекта:
```dockerfile
FROM dunglas/frankenphp
# Замените "your-domain-name.example.com" на ваш домен
ENV SERVER_NAME=your-domain-name.example.com
# Если вы хотите отключить HTTPS, используйте вместо этого:
#ENV SERVER_NAME=:80
# Включите настройки PHP для продакшн
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
# Скопируйте файлы PHP вашего проекта в публичную директорию
COPY . /app/public
# Если вы используете Symfony или Laravel, необходимо скопировать весь проект:
#COPY . /app
```
Ознакомьтесь с разделом "[Создание кастомных Docker-образов](docker.md)" для получения дополнительных подробностей и настроек, а также для установки PHP-расширений и модулей Caddy.
Если ваш проект использует Composer, убедитесь, что он включён в Docker-образ, и установите все зависимости.
Затем добавьте файл `compose.yaml`:
```yaml
services:
php:
image: dunglas/frankenphp
restart: always
ports:
- "80:80" # HTTP
- "443:443" # HTTPS
- "443:443/udp" # HTTP/3
volumes:
- caddy_data:/data
- caddy_config:/config
# Томы, необходимые для сертификатов и конфигурации Caddy
volumes:
caddy_data:
caddy_config:
```
> [!NOTE]
>
> Примеры выше предназначены для использования в продакшне.
> В процессе разработки вы можете использовать том для монтирования, другую конфигурацию PHP и другое значение для переменной окружения `SERVER_NAME`.
>
> Посмотрите проект [Symfony Docker](https://github.com/dunglas/symfony-docker) (который использует FrankenPHP) для более сложного примера с использованием мультистейдж-образов, Composer, дополнительных PHP-расширений и т.д.
Наконец, если вы используете Git, закоммитьте эти файлы и отправьте их в репозиторий.
## Подготовка сервера
Для деплоя приложения в продакшн требуется сервер. В этом руководстве мы будем использовать виртуальную машину, предоставляемую DigitalOcean, но подойдёт любой Linux-сервер.
Если у вас уже есть Linux-сервер с установленным Docker, вы можете сразу перейти к [следующему разделу](#настройка-доменного-имени).
В противном случае, используйте [эту ссылку](https://m.do.co/c/5d8aabe3ab80), чтобы получить $200 на баланс, создайте аккаунт, затем нажмите "Create a Droplet".
Перейдите во вкладку "Marketplace" в разделе "Choose an image" и найдите приложение "Docker". Это создаст сервер на Ubuntu с установленными Docker и Docker Compose.
Для тестов подойдут самые дешёвые тарифы. Для реального продакшна выберите тариф из раздела "general purpose" в зависимости от ваших потребностей.
![Деплой FrankenPHP на DigitalOcean с Docker](../digitalocean-droplet.png)
После этого подключитесь к серверу через SSH:
```console
ssh root@<droplet-ip>
```
## Настройка доменного имени
В большинстве случаев вам потребуется связать доменное имя с вашим сайтом.
Создайте запись DNS типа `A`, указывающую на IP вашего сервера:
```dns
your-domain-name.example.com. IN A 207.154.233.113
```
Пример настройки через DigitalOcean ("Networking" > "Domains"):
![Настройка DNS в DigitalOcean](../digitalocean-dns.png)
> [!NOTE]
>
> Let's Encrypt, сервис, используемый FrankenPHP для автоматической генерации TLS-сертификатов, не поддерживает использование IP-адресов. Для работы необходим домен.
## Деплой
Скопируйте ваш проект на сервер с помощью `git clone`, `scp` или любого другого инструмента.
Если вы используете GitHub, настройте [ключи развёртывания](https://docs.github.com/en/free-pro-team@latest/developers/overview/managing-deploy-keys#deploy-keys).
Пример с использованием Git:
```console
git clone git@github.com:<username>/<project-name>.git
```
Перейдите в директорию проекта и запустите приложение в режиме продакшн:
```console
docker compose up -d --wait
```
Сервер готов, а HTTPS-сертификат был автоматически сгенерирован. Перейдите на `https://your-domain-name.example.com` и наслаждайтесь!
> [!CAUTION]
>
> Docker может кэшировать слои. Убедитесь, что вы используете актуальную сборку, или используйте опцию `--no-cache` для предотвращения проблем с кэшем.
## Деплой на несколько узлов
Если вам нужно развернуть приложение на кластер машин, используйте [Docker Swarm](https://docs.docker.com/engine/swarm/stack-deploy/), который совместим с предоставленными файлами Compose.
Для деплоя на Kubernetes ознакомьтесь с [Helm-чартом API Platform](https://api-platform.com/docs/deployment/kubernetes/), который использует FrankenPHP.

100
docs/ru/static.md Normal file
View File

@@ -0,0 +1,100 @@
# Создание статических бинарных файлов
Вместо использования локальной установки библиотеки PHP, можно создать статическую сборку FrankenPHP благодаря проекту [static-php-cli](https://github.com/crazywhalecc/static-php-cli) (несмотря на название, проект поддерживает все SAPI, а не только CLI).
С помощью этого метода создаётся единый переносимый бинарник, который включает PHP-интерпретатор, веб-сервер Caddy и FrankenPHP!
FrankenPHP также поддерживает [встраивание PHP-приложений в статический бинарный файл](embed.md).
## Linux
Мы предоставляем Docker-образ для сборки статического бинарника для Linux:
```console
docker buildx bake --load static-builder
docker cp $(docker create --name static-builder-musl dunglas/frankenphp:static-builder-musl):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp ; docker rm static-builder
```
Созданный статический бинарный файл называется `frankenphp` и будет доступен в текущей директории.
Чтобы собрать статический бинарный файл без Docker, используйте инструкции для macOS — они подходят и для Linux.
### Дополнительные расширения
По умолчанию компилируются самые популярные PHP-расширения.
Чтобы уменьшить размер бинарного файла и сократить возможные векторы атак, можно указать список расширений, которые следует включить в сборку, используя Docker-аргумент `PHP_EXTENSIONS`.
Например, выполните следующую команду, чтобы собрать только расширение `opcache`:
```console
docker buildx bake --load --set static-builder.args.PHP_EXTENSIONS=opcache,pdo_sqlite static-builder
# ...
```
Чтобы добавить библиотеки, расширяющие функциональность включённых расширений, используйте Docker-аргумент `PHP_EXTENSION_LIBS`:
```console
docker buildx bake \
--load \
--set static-builder.args.PHP_EXTENSIONS=gd \
--set static-builder.args.PHP_EXTENSION_LIBS=libjpeg,libwebp \
static-builder
```
### Дополнительные модули Caddy
Чтобы добавить дополнительные модули Caddy или передать аргументы в [xcaddy](https://github.com/caddyserver/xcaddy), используйте Docker-аргумент `XCADDY_ARGS`:
```console
docker buildx bake \
--load \
--set static-builder.args.XCADDY_ARGS="--with github.com/darkweak/souin/plugins/caddy --with github.com/dunglas/caddy-cbrotli --with github.com/dunglas/mercure/caddy --with github.com/dunglas/vulcain/caddy" \
static-builder
```
В этом примере добавляются модуль HTTP-кэширования [Souin](https://souin.io) для Caddy, а также модули [cbrotli](https://github.com/dunglas/caddy-cbrotli), [Mercure](https://mercure.rocks) и [Vulcain](https://vulcain.rocks).
> [!TIP]
>
> Модули cbrotli, Mercure и Vulcain включены по умолчанию, если `XCADDY_ARGS` пуст или не установлен.
> Если вы изменяете значение `XCADDY_ARGS`, добавьте их явно, если хотите включить их в сборку.
См. также, как [настроить сборку](#настройка-сборки).
### Токен GitHub
Если вы достигли лимита запросов к API GitHub, задайте личный токен доступа GitHub в переменной окружения `GITHUB_TOKEN`:
```console
GITHUB_TOKEN="xxx" docker --load buildx bake static-builder
# ...
```
## macOS
Запустите следующий скрипт, чтобы создать статический бинарный файл для macOS (должен быть установлен [Homebrew](https://brew.sh/)):
```console
git clone https://github.com/dunglas/frankenphp
cd frankenphp
./build-static.sh
```
Примечание: этот скрипт также работает на Linux (и, вероятно, на других Unix-системах) и используется внутри предоставленного Docker-образа для статической сборки.
## Настройка сборки
Следующие переменные окружения можно передать в `docker build` и скрипт `build-static.sh`, чтобы настроить статическую сборку:
- `FRANKENPHP_VERSION`: версия FrankenPHP
- `PHP_VERSION`: версия PHP
- `PHP_EXTENSIONS`: PHP-расширения для сборки ([список поддерживаемых расширений](https://static-php.dev/en/guide/extensions.html))
- `PHP_EXTENSION_LIBS`: дополнительные библиотеки, добавляющие функциональность расширениям
- `XCADDY_ARGS`: аргументы для [xcaddy](https://github.com/caddyserver/xcaddy), например, для добавления модулей Caddy
- `EMBED`: путь к PHP-приложению для встраивания в бинарник
- `CLEAN`: если задано, libphp и все его зависимости будут пересобраны с нуля (без кэша)
- `NO_COMPRESS`: отключает сжатие результирующего бинарника с помощью UPX
- `DEBUG_SYMBOLS`: если задано, отладочные символы не будут удалены и будут добавлены в бинарник
- `MIMALLOC`: (экспериментально, только для Linux) заменяет musl's mallocng на [mimalloc](https://github.com/microsoft/mimalloc) для повышения производительности
- `RELEASE`: (только для мейнтейнеров) если задано, бинарник будет загружен на GitHub

159
docs/ru/worker.md Normal file
View File

@@ -0,0 +1,159 @@
# Worker режим в FrankenPHP
Загрузите приложение один раз и держите его в памяти.
FrankenPHP обрабатывает входящие запросы за несколько миллисекунд.
## Запуск worker-скриптов
### Docker
Установите значение переменной окружения `FRANKENPHP_CONFIG` на `worker /path/to/your/worker/script.php`:
```console
docker run \
-e FRANKENPHP_CONFIG="worker /app/path/to/your/worker/script.php" \
-v $PWD:/app \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
### Автономный бинарный файл
Используйте опцию `--worker` команды `php-server`, чтобы обслуживать содержимое текущей директории через worker-скрипт:
```console
frankenphp php-server --worker /path/to/your/worker/script.php
```
Если ваше PHP-приложение [встроено в бинарник](embed.md), вы можете добавить пользовательский `Caddyfile` в корневую директорию приложения.
Он будет использоваться автоматически.
Также можно настроить [автоматический перезапуск worker-скрипта при изменении файлов](config.md#отслеживание-изменений-файлов) с помощью опции `--watch`.
Следующая команда выполнит перезапуск, если будет изменён любой файл с расширением `.php` в директории `/path/to/your/app/` или её поддиректориях:
```console
frankenphp php-server --worker /path/to/your/worker/script.php --watch="/path/to/your/app/**/*.php"
```
## Symfony Runtime
Worker режим FrankenPHP поддерживается компонентом [Symfony Runtime](https://symfony.com/doc/current/components/runtime.html).
Чтобы запустить любое Symfony-приложение в worker режиме, установите пакет FrankenPHP для [PHP Runtime](https://github.com/php-runtime/runtime):
```console
composer require runtime/frankenphp-symfony
```
Запустите сервер приложения, задав переменную окружения `APP_RUNTIME` для использования FrankenPHP Symfony Runtime:
```console
docker run \
-e FRANKENPHP_CONFIG="worker ./public/index.php" \
-e APP_RUNTIME=Runtime\\FrankenPhpSymfony\\Runtime \
-v $PWD:/app \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
## Laravel Octane
Подробнее см. в [документации](laravel.md#laravel-octane).
## Пользовательские приложения
Следующий пример показывает, как создать собственный worker-скрипт без использования сторонних библиотек:
```php
<?php
// public/index.php
// Предотвращает завершение worker-скрипта при разрыве соединения клиента
ignore_user_abort(true);
// Инициализация приложения
require __DIR__.'/vendor/autoload.php';
$myApp = new \App\Kernel();
$myApp->boot();
// Обработчик запросов за пределами цикла для повышения производительности
$handler = static function () use ($myApp) {
// Выполняется при обработке запроса.
// Суперглобальные переменные, php://input и другие данные обновляются для каждого запроса.
echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER);
};
$maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0);
for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests) {
$keepRunning = \frankenphp_handle_request($handler);
// Действия после отправки HTTP-ответа
$myApp->terminate();
// Вызов сборщика мусора, чтобы снизить вероятность его запуска в процессе генерации страницы
gc_collect_cycles();
if (!$keepRunning) break;
}
// Завершение
$myApp->shutdown();
```
Запустите приложение, настроив worker-скрипт с помощью переменной окружения `FRANKENPHP_CONFIG`:
```console
docker run \
-e FRANKENPHP_CONFIG="worker ./public/index.php" \
-v $PWD:/app \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
## Настройка количества worker-скриптов
По умолчанию запускается по 2 worker-скрипта на каждый CPU.
Вы можете задать своё значение:
```console
docker run \
-e FRANKENPHP_CONFIG="worker ./public/index.php 42" \
-v $PWD:/app \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
### Перезапуск worker-скрипта после определённого количества запросов
PHP изначально не предназначался для долгоживущих процессов, поэтому некоторые библиотеки и устаревший код могут приводить к утечкам памяти.
Для этого можно настроить автоматический перезапуск worker-скрипта после обработки определённого количества запросов.
В предыдущем примере максимальное количество запросов задаётся с помощью переменной окружения `MAX_REQUESTS`.
### Сбои worker-скрипта
Если worker-скрипт завершится с ненулевым кодом выхода, FrankenPHP перезапустит его с использованием экспоненциальной задержки.
Если worker-скрипт проработает дольше, чем время последней задержки \* 2, он будет считаться стабильным, и задержка сбросится.
Однако, если worker-скрипт продолжает завершаться с ненулевым кодом выхода в течение короткого промежутка времени (например, из-за опечатки в коде), FrankenPHP завершит работу с ошибкой: `too many consecutive failures`.
## Поведение суперглобальных переменных
[PHP суперглобальные переменные](https://www.php.net/manual/en/language.variables.superglobals.php) (`$_SERVER`, `$_ENV`, `$_GET` и т.д.) ведут себя следующим образом:
- до первого вызова `frankenphp_handle_request()` суперглобальные переменные содержат значения, связанные с самим worker-скриптом
- во время и после вызова `frankenphp_handle_request()` суперглобальные переменные содержат значения, сгенерированные на основе обработанного HTTP-запроса, каждый вызов изменяет значения суперглобальных переменных
Чтобы получить доступ к суперглобальным переменным worker-скрипта внутри колбэка, необходимо скопировать их и импортировать копию в область видимости колбэка:
```php
<?php
// Копирование $_SERVER worker-скрипта перед первым вызовом frankenphp_handle_request()
$workerServer = $_SERVER;
$handler = static function () use ($workerServer) {
var_dump($_SERVER); // $_SERVER для запроса
var_dump($workerServer); // $_SERVER worker-скрипта
};
// ...
```

View File

@@ -1,46 +1,74 @@
# Create a Static Build
Instead of using a local installation of the PHP library,
it's possible to create a static build of FrankenPHP thanks to the great [static-php-cli project](https://github.com/crazywhalecc/static-php-cli) (despite its name, this project support all SAPIs, not only CLI).
it's possible to create a static or mostly static build of FrankenPHP thanks to the great [static-php-cli project](https://github.com/crazywhalecc/static-php-cli) (despite its name, this project supports all SAPIs, not only CLI).
With this method, a single, portable, binary will contain the PHP interpreter, the Caddy web server and FrankenPHP!
With this method, a single, portable, binary will contain the PHP interpreter, the Caddy web server, and FrankenPHP!
Fully static native executables require no dependencies at all and can even be run on [`scratch` Docker image](https://docs.docker.com/build/building/base-images/#create-a-minimal-base-image-using-scratch).
However, they can't load dynamic PHP extensions (such as Xdebug) and have some limitations because they are using the musl libc.
Mostly static binaries only require `glibc` and can load dynamic extensions.
When possible, we recommend using glibc-based, mostly static builds.
FrankenPHP also supports [embedding the PHP app in the static binary](embed.md).
## Linux
We provide a Docker image to build a Linux static binary:
We provide Docker images to build static Linux binaries:
### musl-Based, Fully Static Build
For a fully-static binary that runs on any Linux distribution without dependencies but doesn't support dynamic loading of extensions:
```console
docker buildx bake --load static-builder
docker cp $(docker create --name static-builder dunglas/frankenphp:static-builder):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp ; docker rm static-builder
docker buildx bake --load static-builder-musl
docker cp $(docker create --name static-builder-musl dunglas/frankenphp:static-builder-musl):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp ; docker rm static-builder-musl
```
The resulting static binary is named `frankenphp` and is available in the current directory.
For better performance in heavily concurrent scenarios, consider using the [mimalloc](https://github.com/microsoft/mimalloc) allocator.
If you want to build the static binary without Docker, take a look at the macOS instructions, which also works for Linux.
```console
docker buildx bake --load --set static-builder-musl.args.MIMALLOC=1 static-builder-musl
```
### glibc-Based, Mostly Static Build (With Dynamic Extension Support)
For a binary that supports loading PHP extensions dynamically while still having the selected extensions compiled statically:
```console
docker buildx bake --load static-builder-gnu
docker cp $(docker create --name static-builder-gnu dunglas/frankenphp:static-builder-gnu):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp ; docker rm static-builder-gnu
```
This binary supports all glibc versions 2.17 and superior but does not run on musl-based systems (like Alpine Linux).
The resulting mostly static (except `glibc`) binary is named `frankenphp` and is available in the current directory.
If you want to build the static binary without Docker, take a look at the macOS instructions, which also work for Linux.
### Custom Extensions
By default, most popular PHP extensions are compiled.
By default, the most popular PHP extensions are compiled.
To reduce the size of the binary and to reduce the attack surface, you can choose the list of extensions to build using the `PHP_EXTENSIONS` Docker ARG.
For instance, run the following command to only build the `opcache` extension:
```console
docker buildx bake --load --set static-builder.args.PHP_EXTENSIONS=opcache,pdo_sqlite static-builder
docker buildx bake --load --set static-builder-musl.args.PHP_EXTENSIONS=opcache,pdo_sqlite static-builder-musl
# ...
```
To add libraries enabling additional functionality to the extensions you've enabled, you can pass use the `PHP_EXTENSION_LIBS` Docker ARG:
To add libraries enabling additional functionality to the extensions you've enabled, you can pass the `PHP_EXTENSION_LIBS` Docker ARG:
```console
docker buildx bake \
--load \
--set static-builder.args.PHP_EXTENSIONS=gd \
--set static-builder.args.PHP_EXTENSION_LIBS=libjpeg,libwebp \
static-builder
--set static-builder-musl.args.PHP_EXTENSIONS=gd \
--set static-builder-musl.args.PHP_EXTENSION_LIBS=libjpeg,libwebp \
static-builder-musl
```
### Extra Caddy Modules
@@ -50,15 +78,15 @@ To add extra Caddy modules or pass other arguments to [xcaddy](https://github.co
```console
docker buildx bake \
--load \
--set static-builder.args.XCADDY_ARGS="--with github.com/darkweak/souin/plugins/caddy --with github.com/dunglas/caddy-cbrotli --with github.com/dunglas/mercure/caddy --with github.com/dunglas/vulcain/caddy" \
static-builder
--set static-builder-musl.args.XCADDY_ARGS="--with github.com/darkweak/souin/plugins/caddy --with github.com/dunglas/caddy-cbrotli --with github.com/dunglas/mercure/caddy --with github.com/dunglas/vulcain/caddy" \
static-builder-musl
```
In this example, we add the [Souin](https://souin.io) HTTP cache module for Caddy as well as the [cbrotli](https://github.com/dunglas/caddy-cbrotli), [Mercure](https://mercure.rocks) and [Vulcain](https://vulcain.rocks) modules.
> [!TIP]
>
> The cbrotli, Mercure and Vulcain modules are included by default if `XCADDY_ARGS` is empty or not set.
> The cbrotli, Mercure, and Vulcain modules are included by default if `XCADDY_ARGS` is empty or not set.
> If you customize the value of `XCADDY_ARGS`, you must include them explicitly if you want them to be included.
See also how to [customize the build](#customizing-the-build)
@@ -68,7 +96,7 @@ See also how to [customize the build](#customizing-the-build)
If you hit the GitHub API rate limit, set a GitHub Personal Access Token in an environment variable named `GITHUB_TOKEN`:
```console
GITHUB_TOKEN="xxx" docker --load buildx bake static-builder
GITHUB_TOKEN="xxx" docker --load buildx bake static-builder-musl
# ...
```
@@ -82,21 +110,52 @@ cd frankenphp
./build-static.sh
```
Note: this script also works on Linux (and probably on other Unixes), and is used internally by the Docker based static builder we provide.
Note: this script also works on Linux (and probably on other Unixes), and is used internally by the Docker images we provide.
## Customizing The Build
The following environment variables can be passed to `docker build` and to the `build-static.sh`
script to customize the static build:
* `FRANKENPHP_VERSION`: the version of FrankenPHP to use
* `PHP_VERSION`: the version of PHP to use
* `PHP_EXTENSIONS`: the PHP extensions to build ([list of supported extensions](https://static-php.dev/en/guide/extensions.html))
* `PHP_EXTENSION_LIBS`: extra libraries to build that add features to the extensions
* `XCADDY_ARGS`: arguments to pass to [xcaddy](https://github.com/caddyserver/xcaddy), for instance to add extra Caddy modules
* `EMBED`: path of the PHP application to embed in the binary
* `CLEAN`: when set, libphp and all its dependencies are built from scratch (no cache)
* `NO_COMPRESS`: don't compress the resulting binary using UPX
* `DEBUG_SYMBOLS`: when set, debug-symbols will not be stripped and will be added within the binary
* `MIMALLOC`: (experimental, Linux-only) replace musl's mallocng by [mimalloc](https://github.com/microsoft/mimalloc) for improved performance
* `RELEASE`: (maintainers only) when set, the resulting binary will be uploaded on GitHub
- `FRANKENPHP_VERSION`: the version of FrankenPHP to use
- `PHP_VERSION`: the version of PHP to use
- `PHP_EXTENSIONS`: the PHP extensions to build ([list of supported extensions](https://static-php.dev/en/guide/extensions.html))
- `PHP_EXTENSION_LIBS`: extra libraries to build that add features to the extensions
- `XCADDY_ARGS`: arguments to pass to [xcaddy](https://github.com/caddyserver/xcaddy), for instance to add extra Caddy modules
- `EMBED`: path of the PHP application to embed in the binary
- `CLEAN`: when set, libphp and all its dependencies are built from scratch (no cache)
- `NO_COMPRESS`: don't compress the resulting binary using UPX
- `DEBUG_SYMBOLS`: when set, debug-symbols will not be stripped and will be added to the binary
- `MIMALLOC`: (experimental, Linux-only) replace musl's mallocng by [mimalloc](https://github.com/microsoft/mimalloc) for improved performance. We only recommend using this for musl targeting builds, for glibc prefer disabling this option and using [`LD_PRELOAD`](https://microsoft.github.io/mimalloc/overrides.html) when you run your binary instead.
- `RELEASE`: (maintainers only) when set, the resulting binary will be uploaded on GitHub
## Extensions
With the glibc or macOS-based binaries, you can load PHP extensions dynamically. However, these extensions will have to be compiled with ZTS support.
Since most package managers do not currently offer ZTS versions of their extensions, you will have to compile them yourself.
For this, you can build and run the `static-builder-gnu` Docker container, remote into it, and compile the extensions with `./configure --with-php-config=/go/src/app/dist/static-php-cli/buildroot/bin/php-config`.
Example steps for [the Xdebug extension](https://xdebug.org):
```console
docker build -t gnu-ext -f static-builder-gnu.Dockerfile --build-arg FRANKENPHP_VERSION=1.0 .
docker create --name static-builder-gnu -it gnu-ext /bin/sh
docker start static-builder-gnu
docker exec -it static-builder-gnu /bin/sh
cd /go/src/app/dist/static-php-cli/buildroot/bin
git clone https://github.com/xdebug/xdebug.git && cd xdebug
source scl_source enable devtoolset-10
../phpize
./configure --with-php-config=/go/src/app/dist/static-php-cli/buildroot/bin/php-config
make
exit
docker cp static-builder-gnu:/go/src/app/dist/static-php-cli/buildroot/bin/xdebug/modules/xdebug.so xdebug-zts.so
docker cp static-builder-gnu:/go/src/app/dist/frankenphp-linux-$(uname -m) ./frankenphp
docker stop static-builder-gnu
docker rm static-builder-gnu
docker rmi gnu-ext
```
This will have created `frankenphp` and `xdebug-zts.so` in the current directory.
If you move the `xdebug-zts.so` into your extension directory, add `zend_extension=xdebug-zts.so` to your php.ini and run FrankenPHP, it will load Xdebug.

View File

@@ -1,202 +1,206 @@
# Katkıda Bulunmak
## PHP Derleme
### Docker ile (Linux)
Geliştirme Ortamı için Docker İmajını Oluşturun:
```console
docker build -t frankenphp-dev -f dev.Dockerfile .
docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -p 8080:8080 -p 443:443 -p 443:443/udp -v $PWD:/go/src/app -it frankenphp-dev
```
İmaj genel geliştirme araçlarını (Go, GDB, Valgrind, Neovim...) içerir.
Docker sürümü 23.0'dan düşükse, derleme dockerignore [pattern issue](https://github.com/moby/moby/pull/42676) tarafından başarısız olur. Dizinleri `.dockerignore` dosyasına ekleyin.
```patch
!testdata/*.php
!testdata/*.txt
+!caddy
+!internal
```
### Docker olmadan (Linux ve macOS)
[Kaynaklardan derlemek için talimatları izleyin](https://frankenphp.dev/docs/compile/) ve `--debug` yapılandırma seçeneğini geçirin.
## Test senaryolarını çalıştırma
```console
go test -tags watcher -race -v ./...
```
## Caddy modülü
FrankenPHP Caddy modülü ile Caddy'yi oluşturun:
```console
cd caddy/frankenphp/
go build
cd ../../
```
Caddy'yi FrankenPHP Caddy modülü ile çalıştırın:
```console
cd testdata/
../caddy/frankenphp/frankenphp run
```
Sunucu `127.0.0.1:8080` adresini dinliyor:
```console
curl -vk https://localhost/phpinfo.php
```
## Minimal test sunucusu
Minimal test sunucusunu oluşturun:
```console
cd internal/testserver/
go build
cd ../../
```
Test sunucusunu çalıştırın:
```console
cd testdata/
../internal/testserver/testserver
```
Sunucu `127.0.0.1:8080` adresini dinliyor:
```console
curl -v http://127.0.0.1:8080/phpinfo.php
```
## Docker İmajlarını Yerel Olarak Oluşturma
Bake (pişirme) planını yazdırın:
```console
docker buildx bake -f docker-bake.hcl --print
```
Yerel olarak amd64 için FrankenPHP görüntüleri oluşturun:
```console
docker buildx bake -f docker-bake.hcl --pull --load --set "*.platform=linux/amd64"
```
Yerel olarak arm64 için FrankenPHP görüntüleri oluşturun:
```console
docker buildx bake -f docker-bake.hcl --pull --load --set "*.platform=linux/arm64"
```
FrankenPHP imajlarını arm64 ve amd64 için sıfırdan oluşturun ve Docker Hub'a gönderin:
```console
docker buildx bake -f docker-bake.hcl --pull --no-cache --push
```
## Statik Derlemelerle Segmentasyon Hatalarında Hata Ayıklama
1. FrankenPHP binary dosyasının hata ayıklama sürümünü GitHub'dan indirin veya hata ayıklama seçeneklerini kullanarak özel statik derlemenizi oluşturun:
```console
docker buildx bake \
--load \
--set static-builder.args.DEBUG_SYMBOLS=1 \
--set "static-builder.platform=linux/amd64" \
static-builder
docker cp $(docker create --name static-builder dunglas/frankenphp:static-builder):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp
```
2. Mevcut `frankenphp` sürümünüzü hata ayıklama FrankenPHP çalıştırılabilir dosyasıyla değiştirin
3. FrankenPHP'yi her zamanki gibi başlatın (alternatif olarak FrankenPHP'yi doğrudan GDB ile başlatabilirsiniz: `gdb --args frankenphp run`)
4. GDB ile sürece bağlanın:
```console
gdb -p `pidof frankenphp`
```
5. Gerekirse, GDB kabuğuna `continue` yazın
6. FrankenPHP'nin çökmesini sağlayın
7. GDB kabuğuna `bt` yazın
8. Çıktıyı kopyalayın
## GitHub Eylemlerinde Segmentasyon Hatalarında Hata Ayıklama
1. `.github/workflows/tests.yml` dosyasınıın
2. PHP hata ayıklama seçeneklerini etkinleştirin
```patch
- uses: shivammathur/setup-php@v2
# ...
env:
phpts: ts
+ debug: true
```
3. Konteynere bağlanmak için `tmate`i etkinleştirin
```patch
-
name: Set CGO flags
run: echo "CGO_CFLAGS=$(php-config --includes)" >> "$GITHUB_ENV"
+ -
+ run: |
+ sudo apt install gdb
+ mkdir -p /home/runner/.config/gdb/
+ printf "set auto-load safe-path /\nhandle SIG34 nostop noprint pass" > /home/runner/.config/gdb/gdbinit
+ -
+ uses: mxschmitt/action-tmate@v3
```
4. Konteynere bağlanın
5. `frankenphp.go` dosyasınıın
6. `cgosymbolizer`'ı etkinleştirin
```patch
- //_ "github.com/ianlancetaylor/cgosymbolizer"
+ _ "github.com/ianlancetaylor/cgosymbolizer"
```
7. Modülü indirin: `go get`
8. Konteynerde GDB ve benzerlerini kullanabilirsiniz:
```console
go test -tags watcher -c -ldflags=-w
gdb --args frankenphp.test -test.run ^MyTest$
```
9. Hata düzeltildiğinde, tüm bu değişiklikleri geri alın
## Misc Dev Resources
* [uWSGI içine PHP gömme](https://github.com/unbit/uwsgi/blob/master/plugins/php/php_plugin.c)
* [NGINX Unit'te PHP gömme](https://github.com/nginx/unit/blob/master/src/nxt_php_sapi.c)
* [Go (go-php) içinde PHP gömme](https://github.com/deuill/go-php)
* [Go'da PHP gömme (GoEmPHP)](https://github.com/mikespook/goemphp)
* [C++'da PHP gömme](https://gist.github.com/paresy/3cbd4c6a469511ac7479aa0e7c42fea7)
* [Sara Golemon tarafından PHP'yi Genişletme ve Yerleştirme](https://books.google.fr/books?id=zMbGvK17_tYC&pg=PA254&lpg=PA254#v=onepage&q&f=false)
* [TSRMLS_CC de neyin nesi?](http://blog.golemon.com/2006/06/what-heck-is-tsrmlscc-anyway.html)
* [Mac'te PHP gömme](https://gist.github.com/jonnywang/61427ffc0e8dde74fff40f479d147db4)
* [SDL bağları](https://pkg.go.dev/github.com/veandco/go-sdl2@v0.4.21/sdl#Main)
## Docker ile İlgili Kaynaklar
* [Pişirme (bake) dosya tanımı](https://docs.docker.com/build/customize/bake/file-definition/)
* [docker buildx build](https://docs.docker.com/engine/reference/commandline/buildx_build/)
## Faydalı Komut
```console
apk add strace util-linux gdb
strace -e 'trace=!futex,epoll_ctl,epoll_pwait,tgkill,rt_sigreturn' -p 1
```
# Katkıda Bulunmak
## PHP Derleme
### Docker ile (Linux)
Geliştirme Ortamı için Docker İmajını Oluşturun:
```console
docker build -t frankenphp-dev -f dev.Dockerfile .
docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -p 8080:8080 -p 443:443 -p 443:443/udp -v $PWD:/go/src/app -it frankenphp-dev
```
İmaj genel geliştirme araçlarını (Go, GDB, Valgrind, Neovim...) içerir ve aşağıdaki php ayar konumlarını kullanır
- php.ini: `/etc/frankenphp/php.ini` Varsayılan olarak geliştirme ön ayarlarına sahip bir php.ini dosyası sağlanır.
- ek yapılandırma dosyaları: `/etc/frankenphp/php.d/*.ini`
- php uzantıları: `/usr/lib/frankenphp/modules/`
Docker sürümünüz 23.0'dan düşükse, derleme dockerignore [pattern issue](https://github.com/moby/moby/pull/42676) nedeniyle başarısız olacaktır. Dizinleri `.dockerignore` dosyasına ekleyin.
```patch
!testdata/*.php
!testdata/*.txt
+!caddy
+!internal
```
### Docker olmadan (Linux ve macOS)
[Kaynaklardan derlemek için talimatları izleyin](https://frankenphp.dev/docs/compile/) ve `--debug` yapılandırma seçeneğini geçirin.
## Test senaryolarını çalıştırma
```console
go test -tags watcher -race -v ./...
```
## Caddy modülü
FrankenPHP Caddy modülü ile Caddy'yi oluşturun:
```console
cd caddy/frankenphp/
go build
cd ../../
```
Caddy'yi FrankenPHP Caddy modülü ile çalıştırın:
```console
cd testdata/
../caddy/frankenphp/frankenphp run
```
Sunucu `127.0.0.1:8080` adresini dinliyor:
```console
curl -vk https://localhost/phpinfo.php
```
## Minimal test sunucusu
Minimal test sunucusunu oluşturun:
```console
cd internal/testserver/
go build
cd ../../
```
Test sunucusunu çalıştırın:
```console
cd testdata/
../internal/testserver/testserver
```
Sunucu `127.0.0.1:8080` adresini dinliyor:
```console
curl -v http://127.0.0.1:8080/phpinfo.php
```
## Docker İmajlarını Yerel Olarak Oluşturma
Bake (pişirme) planını yazdırın:
```console
docker buildx bake -f docker-bake.hcl --print
```
Yerel olarak amd64 için FrankenPHP görüntüleri oluşturun:
```console
docker buildx bake -f docker-bake.hcl --pull --load --set "*.platform=linux/amd64"
```
Yerel olarak arm64 için FrankenPHP görüntüleri oluşturun:
```console
docker buildx bake -f docker-bake.hcl --pull --load --set "*.platform=linux/arm64"
```
FrankenPHP imajlarını arm64 ve amd64 için sıfırdan oluşturun ve Docker Hub'a gönderin:
```console
docker buildx bake -f docker-bake.hcl --pull --no-cache --push
```
## Statik Derlemelerle Segmentasyon Hatalarında Hata Ayıklama
1. FrankenPHP binary dosyasının hata ayıklama sürümünü GitHub'dan indirin veya hata ayıklama seçeneklerini kullanarak özel statik derlemenizi oluşturun:
```console
docker buildx bake \
--load \
--set static-builder.args.DEBUG_SYMBOLS=1 \
--set "static-builder.platform=linux/amd64" \
static-builder
docker cp $(docker create --name static-builder-musl dunglas/frankenphp:static-builder-musl):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp
```
2. Mevcut `frankenphp` sürümünüzü hata ayıklama FrankenPHP çalıştırılabilir dosyasıyla değiştirin
3. FrankenPHP'yi her zamanki gibi başlatın (alternatif olarak FrankenPHP'yi doğrudan GDB ile başlatabilirsiniz: `gdb --args frankenphp run`)
4. GDB ile sürece bağlanın:
```console
gdb -p `pidof frankenphp`
```
5. Gerekirse, GDB kabuğuna `continue` yazın
6. FrankenPHP'nin çökmesini sağlayın
7. GDB kabuğuna `bt` yazın
8. Çıktıyı kopyalayın
## GitHub Eylemlerinde Segmentasyon Hatalarında Hata Ayıklama
1. `.github/workflows/tests.yml` dosyasınıın
2. PHP hata ayıklama seçeneklerini etkinleştirin
```patch
- uses: shivammathur/setup-php@v2
# ...
env:
phpts: ts
+ debug: true
```
3. Konteynere bağlanmak için `tmate`i etkinleştirin
```patch
-
name: Set CGO flags
run: echo "CGO_CFLAGS=$(php-config --includes)" >> "$GITHUB_ENV"
+ -
+ run: |
+ sudo apt install gdb
+ mkdir -p /home/runner/.config/gdb/
+ printf "set auto-load safe-path /\nhandle SIG34 nostop noprint pass" > /home/runner/.config/gdb/gdbinit
+ -
+ uses: mxschmitt/action-tmate@v3
```
4. Konteynere bağlanın
5. `frankenphp.go` dosyasınıın
6. `cgosymbolizer`'ı etkinleştirin
```patch
- //_ "github.com/ianlancetaylor/cgosymbolizer"
+ _ "github.com/ianlancetaylor/cgosymbolizer"
```
7. Modülü indirin: `go get`
8. Konteynerde GDB ve benzerlerini kullanabilirsiniz:
```console
go test -tags watcher -c -ldflags=-w
gdb --args frankenphp.dev.test -test.run ^MyTest$
```
9. Hata düzeltildiğinde, tüm bu değişiklikleri geri alın
## Misc Dev Resources
- [uWSGI içine PHP gömme](https://github.com/unbit/uwsgi/blob/master/plugins/php/php_plugin.c)
- [NGINX Unit'te PHP gömme](https://github.com/nginx/unit/blob/master/src/nxt_php_sapi.c)
- [Go (go-php) içinde PHP gömme](https://github.com/deuill/go-php)
- [Go'da PHP gömme (GoEmPHP)](https://github.com/mikespook/goemphp)
- [C++'da PHP gömme](https://gist.github.com/paresy/3cbd4c6a469511ac7479aa0e7c42fea7)
- [Sara Golemon tarafından PHP'yi Genişletme ve Yerleştirme](https://books.google.fr/books?id=zMbGvK17_tYC&pg=PA254&lpg=PA254#v=onepage&q&f=false)
- [TSRMLS_CC de neyin nesi?](http://blog.golemon.com/2006/06/what-heck-is-tsrmlscc-anyway.html)
- [Mac'te PHP gömme](https://gist.github.com/jonnywang/61427ffc0e8dde74fff40f479d147db4)
- [SDL bağları](https://pkg.go.dev/github.com/veandco/go-sdl2@v0.4.21/sdl#Main)
## Docker ile İlgili Kaynaklar
- [Pişirme (bake) dosya tanımı](https://docs.docker.com/build/customize/bake/file-definition/)
- [docker buildx build](https://docs.docker.com/engine/reference/commandline/buildx_build/)
## Faydalı Komut
```console
apk add strace util-linux gdb
strace -e 'trace=!futex,epoll_ctl,epoll_pwait,tgkill,rt_sigreturn' -p 1
```

View File

@@ -1,78 +1,78 @@
# FrankenPHP: PHP için Modern Uygulama Sunucusu
<h1 align="center"><a href="https://frankenphp.dev"><img src="../../frankenphp.png" alt="FrankenPHP" width="600"></a></h1>
FrankenPHP, [Caddy](https://caddyserver.com/) web sunucusunun üzerine inşa edilmiş PHP için modern bir uygulama sunucusudur.
FrankenPHP, çarpıcı özellikleri sayesinde PHP uygulamalarınıza süper güçler kazandırır: [Early Hints*](https://frankenphp.dev/docs/early-hints/), [worker modu](https://frankenphp.dev/docs/worker/), [real-time yetenekleri](https://frankenphp.dev/docs/mercure/), otomatik HTTPS, HTTP/2 ve HTTP/3 desteği...
FrankenPHP herhangi bir PHP uygulaması ile çalışır ve worker modu ile resmi entegrasyonları sayesinde Laravel ve Symfony projelerinizi her zamankinden daha performanslı hale getirir.
FrankenPHP, PHP'yi `net/http` kullanarak herhangi bir uygulamaya yerleştirmek için bağımsız bir Go kütüphanesi olarak da kullanılabilir.
[*Frankenphp.dev*](https://frankenphp.dev) adresinden ve bu slayt üzerinden daha fazlasını öğrenin:
<a href="https://dunglas.dev/2022/10/frankenphp-the-modern-php-app-server-written-in-go/"><img src="https://dunglas.dev/wp-content/uploads/2022/10/frankenphp.png" alt="Slides" width="600"></a>
## Başlarken
### Docker
```console
docker run -v $PWD:/app/public \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
`https://localhost` adresine gidin ve keyfini çıkarın!
> [!TIP]
>
> `https://127.0.0.1` kullanmaya çalışmayın. `https://localhost` kullanın ve kendinden imzalı sertifikayı kabul edin.
> Kullanılacak alan adını değiştirmek için [`SERVER_NAME` ortam değişkenini](https://frankenphp.dev/tr/docs/config#ortam-değişkenleri) kullanın.
### Binary Çıktısı
Docker kullanmayı tercih etmiyorsanız, Linux ve macOS için bağımsız FrankenPHP binary dosyası sağlıyoruz
[PHP 8.4](https://www.php.net/releases/8.4/en.php) ve en popüler PHP eklentilerini de içermekte: [FrankenPHP](https://github.com/dunglas/frankenphp/releases) indirin
Geçerli dizinin içeriğini başlatmak için çalıştırın:
```console
./frankenphp php-server
```
Ayrıca aşağıdaki tek komut satırı ile de çalıştırabilirsiniz:
```console
./frankenphp php-cli /path/to/your/script.php
```
## Docs
* [Worker modu](worker.md)
* [Early Hints desteği (103 HTTP durum kodu)](early-hints.md)
* [Real-time](mercure.md)
* [Konfigürasyon](config.md)
* [Docker imajları](docker.md)
* [Production'a dağıtım](production.md)
* [**Bağımsız** kendiliğinden çalıştırılabilir PHP uygulamaları oluşturma](embed.md)
* [Statik binary'leri oluşturma](static.md)
* [Kaynak dosyalarından derleme](config.md)
* [Laravel entegrasyonu](laravel.md)
* [Bilinen sorunlar](known-issues.md)
* [Demo uygulama (Symfony) ve kıyaslamalar](https://github.com/dunglas/frankenphp-demo)
* [Go kütüphane dokümantasonu](https://pkg.go.dev/github.com/dunglas/frankenphp)
* [Katkıda bulunma ve hata ayıklama](CONTRIBUTING.md)
## Örnekler ve İskeletler
* [Symfony](https://github.com/dunglas/symfony-docker)
* [API Platform](https://api-platform.com/docs/distribution/)
* [Laravel](https://frankenphp.dev/docs/laravel/)
* [Sulu](https://sulu.io/blog/running-sulu-with-frankenphp)
* [WordPress](https://github.com/StephenMiracle/frankenwp)
* [Drupal](https://github.com/dunglas/frankenphp-drupal)
* [Joomla](https://github.com/alexandreelise/frankenphp-joomla)
* [TYPO3](https://github.com/ochorocho/franken-typo3)
* [Magento2](https://github.com/ekino/frankenphp-magento2)
# FrankenPHP: PHP için Modern Uygulama Sunucusu
<h1 align="center"><a href="https://frankenphp.dev"><img src="../../frankenphp.png" alt="FrankenPHP" width="600"></a></h1>
FrankenPHP, [Caddy](https://caddyserver.com/) web sunucusunun üzerine inşa edilmiş PHP için modern bir uygulama sunucusudur.
FrankenPHP, çarpıcı özellikleri sayesinde PHP uygulamalarınıza süper güçler kazandırır: [Early Hints\*](https://frankenphp.dev/docs/early-hints/), [worker modu](https://frankenphp.dev/docs/worker/), [real-time yetenekleri](https://frankenphp.dev/docs/mercure/), otomatik HTTPS, HTTP/2 ve HTTP/3 desteği...
FrankenPHP herhangi bir PHP uygulaması ile çalışır ve worker modu ile resmi entegrasyonları sayesinde Laravel ve Symfony projelerinizi her zamankinden daha performanslı hale getirir.
FrankenPHP, PHP'yi `net/http` kullanarak herhangi bir uygulamaya yerleştirmek için bağımsız bir Go kütüphanesi olarak da kullanılabilir.
[_Frankenphp.dev_](https://frankenphp.dev) adresinden ve bu slayt üzerinden daha fazlasını öğrenin:
<a href="https://dunglas.dev/2022/10/frankenphp-the-modern-php-app-server-written-in-go/"><img src="https://dunglas.dev/wp-content/uploads/2022/10/frankenphp.png" alt="Slides" width="600"></a>
## Başlarken
### Docker
```console
docker run -v $PWD:/app/public \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
`https://localhost` adresine gidin ve keyfini çıkarın!
> [!TIP]
>
> `https://127.0.0.1` kullanmaya çalışmayın. `https://localhost` kullanın ve kendinden imzalı sertifikayı kabul edin.
> Kullanılacak alan adını değiştirmek için [`SERVER_NAME` ortam değişkenini](https://frankenphp.dev/tr/docs/config#ortam-değişkenleri) kullanın.
### Binary Çıktısı
Docker kullanmayı tercih etmiyorsanız, Linux ve macOS için bağımsız FrankenPHP binary dosyası sağlıyoruz
[PHP 8.4](https://www.php.net/releases/8.4/en.php) ve en popüler PHP eklentilerini de içermekte: [FrankenPHP](https://github.com/dunglas/frankenphp/releases) indirin
Geçerli dizinin içeriğini başlatmak için çalıştırın:
```console
./frankenphp php-server
```
Ayrıca aşağıdaki tek komut satırı ile de çalıştırabilirsiniz:
```console
./frankenphp php-cli /path/to/your/script.php
```
## Docs
- [Worker modu](worker.md)
- [Early Hints desteği (103 HTTP durum kodu)](early-hints.md)
- [Real-time](mercure.md)
- [Konfigürasyon](config.md)
- [Docker imajları](docker.md)
- [Production'a dağıtım](production.md)
- [**Bağımsız** kendiliğinden çalıştırılabilir PHP uygulamaları oluşturma](embed.md)
- [Statik binary'leri oluşturma](static.md)
- [Kaynak dosyalarından derleme](config.md)
- [Laravel entegrasyonu](laravel.md)
- [Bilinen sorunlar](known-issues.md)
- [Demo uygulama (Symfony) ve kıyaslamalar](https://github.com/dunglas/frankenphp-demo)
- [Go kütüphane dokümantasonu](https://pkg.go.dev/github.com/dunglas/frankenphp)
- [Katkıda bulunma ve hata ayıklama](CONTRIBUTING.md)
## Örnekler ve İskeletler
- [Symfony](https://github.com/dunglas/symfony-docker)
- [API Platform](https://api-platform.com/docs/distribution/)
- [Laravel](https://frankenphp.dev/docs/laravel/)
- [Sulu](https://sulu.io/blog/running-sulu-with-frankenphp)
- [WordPress](https://github.com/StephenMiracle/frankenwp)
- [Drupal](https://github.com/dunglas/frankenphp-drupal)
- [Joomla](https://github.com/alexandreelise/frankenphp-joomla)
- [TYPO3](https://github.com/ochorocho/franken-typo3)
- [Magento2](https://github.com/ekino/frankenphp-magento2)

View File

@@ -1,100 +1,100 @@
# Kaynak Kodlardan Derleme
Bu doküman, PHP'yi dinamik bir kütüphane olarak yükleyecek bir FrankenPHP yapısının nasıl oluşturulacağınııklamaktadır.
Önerilen yöntem bu şekildedir.
Alternatif olarak, [statik yapılar oluşturma](static.md) da mümkündür.
## PHP'yi yükleyin
FrankenPHP, PHP 8.2 ve üstü ile uyumludur.
İlk olarak, [PHP'nin kaynaklarını edinin](https://www.php.net/downloads.php) ve bunları çıkarın:
```console
tar xf php-*
cd php-*/
```
Ardından, PHP'yi platformunuz için yapılandırın.
Bu şekilde yapılandırma gereklidir, ancak başka opsiyonlar da ekleyebilirsiniz (örn. ekstra uzantılar)
İhtiyaç halinde.
### Linux
```console
./configure \
--enable-embed \
--enable-zts \
--disable-zend-signals \
--enable-zend-max-execution-timers
```
### Mac
Yüklemek için [Homebrew](https://brew.sh/) paket yöneticisini kullanın
`libiconv`, `bison`, `re2c` ve `pkg-config`:
```console
brew install libiconv bison re2c pkg-config
echo 'export PATH="/opt/homebrew/opt/bison/bin:$PATH"' >> ~/.zshrc
```
Ardından yapılandırma betiğini çalıştırın:
```console
./configure \
--enable-embed=static \
--enable-zts \
--disable-zend-signals \
--disable-opcache-jit \
--enable-static \
--enable-shared=no \
--with-iconv=/opt/homebrew/opt/libiconv/
```
## PHP Derleyin
Son olarak, PHP'yi derleyin ve kurun:
```console
make -j"$(getconf _NPROCESSORS_ONLN)"
sudo make install
```
## Go Uygulamasını Derleyin
Artık Go kütüphanesini kullanabilir ve Caddy yapımızı derleyebilirsiniz:
```console
curl -L https://github.com/dunglas/frankenphp/archive/refs/heads/main.tar.gz | tar xz
cd frankenphp-main/caddy/frankenphp
CGO_CFLAGS=$(php-config --includes) CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" go build
```
### Xcaddy kullanımı
Alternatif olarak, FrankenPHP'yi [özel Caddy modülleri](https://caddyserver.com/docs/modules/) ile derlemek için [xcaddy](https://github.com/caddyserver/xcaddy) kullanın:
```console
CGO_ENABLED=1 \
XCADDY_GO_BUILD_FLAGS="-ldflags '-w -s'" \
xcaddy build \
--output frankenphp \
--with github.com/dunglas/frankenphp/caddy \
--with github.com/dunglas/caddy-cbrotli \
--with github.com/dunglas/mercure/caddy \
--with github.com/dunglas/vulcain/caddy
# Add extra Caddy modules here
```
> [!TIP]
>
> Eğer musl libc (Alpine Linux'ta varsayılan) ve Symfony kullanıyorsanız,
> varsayılan yığın boyutunu artırmanız gerekebilir.
> Aksi takdirde, şu tarz hatalar alabilirsiniz `PHP Fatal error: Maximum call stack size of 83360 bytes reached during compilation. Try splitting expression`
>
> Bunu yapmak için, `XCADDY_GO_BUILD_FLAGS` ortam değişkenini bu şekilde değiştirin
> `XCADDY_GO_BUILD_FLAGS=$'-ldflags "-w -s -extldflags \'-Wl,-z,stack-size=0x80000\'"'`
> (yığın boyutunun değerini uygulamanızın ihtiyaçlarına göre değiştirin).
# Kaynak Kodlardan Derleme
Bu doküman, PHP'yi dinamik bir kütüphane olarak yükleyecek bir FrankenPHP yapısının nasıl oluşturulacağınııklamaktadır.
Önerilen yöntem bu şekildedir.
Alternatif olarak, [statik yapılar oluşturma](static.md) da mümkündür.
## PHP'yi yükleyin
FrankenPHP, PHP 8.2 ve üstü ile uyumludur.
İlk olarak, [PHP'nin kaynaklarını edinin](https://www.php.net/downloads.php) ve bunları çıkarın:
```console
tar xf php-*
cd php-*/
```
Ardından, PHP'yi platformunuz için yapılandırın.
Bu şekilde yapılandırma gereklidir, ancak başka opsiyonlar da ekleyebilirsiniz (örn. ekstra uzantılar)
İhtiyaç halinde.
### Linux
```console
./configure \
--enable-embed \
--enable-zts \
--disable-zend-signals \
--enable-zend-max-execution-timers
```
### Mac
Yüklemek için [Homebrew](https://brew.sh/) paket yöneticisini kullanın
`libiconv`, `bison`, `re2c` ve `pkg-config`:
```console
brew install libiconv bison re2c pkg-config
echo 'export PATH="/opt/homebrew/opt/bison/bin:$PATH"' >> ~/.zshrc
```
Ardından yapılandırma betiğini çalıştırın:
```console
./configure \
--enable-embed=static \
--enable-zts \
--disable-zend-signals \
--disable-opcache-jit \
--enable-static \
--enable-shared=no \
--with-iconv=/opt/homebrew/opt/libiconv/
```
## PHP Derleyin
Son olarak, PHP'yi derleyin ve kurun:
```console
make -j"$(getconf _NPROCESSORS_ONLN)"
sudo make install
```
## Go Uygulamasını Derleyin
Artık Go kütüphanesini kullanabilir ve Caddy yapımızı derleyebilirsiniz:
```console
curl -L https://github.com/dunglas/frankenphp/archive/refs/heads/main.tar.gz | tar xz
cd frankenphp-main/caddy/frankenphp
CGO_CFLAGS=$(php-config --includes) CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" go build
```
### Xcaddy kullanımı
Alternatif olarak, FrankenPHP'yi [özel Caddy modülleri](https://caddyserver.com/docs/modules/) ile derlemek için [xcaddy](https://github.com/caddyserver/xcaddy) kullanın:
```console
CGO_ENABLED=1 \
XCADDY_GO_BUILD_FLAGS="-ldflags '-w -s'" \
xcaddy build \
--output frankenphp \
--with frankenphp.dev/caddy \
--with github.com/dunglas/caddy-cbrotli \
--with github.com/dunglas/mercure/caddy \
--with github.com/dunglas/vulcain/caddy
# Add extra Caddy modules here
```
> [!TIP]
>
> Eğer musl libc (Alpine Linux'ta varsayılan) ve Symfony kullanıyorsanız,
> varsayılan yığın boyutunu artırmanız gerekebilir.
> Aksi takdirde, şu tarz hatalar alabilirsiniz `PHP Fatal error: Maximum call stack size of 83360 bytes reached during compilation. Try splitting expression`
>
> Bunu yapmak için, `XCADDY_GO_BUILD_FLAGS` ortam değişkenini bu şekilde değiştirin
> `XCADDY_GO_BUILD_FLAGS=$'-ldflags "-w -s -extldflags \'-Wl,-z,stack-size=0x80000\'"'`
> (yığın boyutunun değerini uygulamanızın ihtiyaçlarına göre değiştirin).

View File

@@ -1,163 +1,180 @@
# Konfigürasyon
FrankenPHP, Caddy'nin yanı sıra Mercure ve Vulcain modülleri [Caddy tarafından desteklenen formatlar](https://caddyserver.com/docs/getting-started#your-first-config) kullanılarak yapılandırılabilir.
Docker imajlarında] (docker.md), `Caddyfile` `/etc/caddy/Caddyfile` adresinde bulunur.
Statik ikili, başlatıldığı dizinde `Caddyfile` dosyasını arayacaktır.
PHP'nin kendisi [bir `php.ini` dosyası kullanılarak yapılandırılabilir](https://www.php.net/manual/tr/configuration.file.php).
Varsayılan olarak, Docker imajlarıyla birlikte verilen PHP ve statik ikili dosyada bulunan PHP, FrankenPHP'nin başlatıldığı dizinde ve `/usr/local/etc/php/` içinde bir `php.ini` dosyası arayacaktır. Ayrıca `.ini` ile biten tüm dosyaları `/usr/local/etc/php/conf.d/` dizininden yükleyecektir.
Öntanımlı olarak `php.ini` dosyası yoktur, PHP projesi tarafından sağlanan resmi bir şablonu kopyalamanız gerekir.
Docker'da şablonlar imajlar içinde sağlanır:
```dockerfile
FROM dunglas/frankenphp
# Developement:
RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini
# Veya production:
RUN cp $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini
```
Docker kullanmıyorsanız, [PHP kaynak kodu](https://github.com/php/php-src/) ile birlikte verilen `php.ini-production` veya `php.ini-development` dosyalarından birini kopyalayın.
## Caddyfile Konfigürasyonu
FrankenPHP yürütücüsünü kaydetmek için `frankenphp` [global seçenek](https://caddyserver.com/docs/caddyfile/concepts#global-options) ayarlanmalıdır, ardından PHP uygulamanızı sunmak için site blokları içinde `php_server` veya `php` [HTTP yönergeleri](https://caddyserver.com/docs/caddyfile/concepts#directives) kullanılabilir.
Minimal örnek:
```caddyfile
{
# FrankenPHP'yi aktif et
frankenphp
}
localhost {
# Sıkıştırmayı etkinleştir (isteğe bağlı)
encode zstd br gzip
# Geçerli dizindeki PHP dosyalarını çalıştırın ve varlıkları sunun
php_server
}
```
İsteğe bağlı olarak, oluşturulacak iş parçacığı sayısı ve sunucuyla birlikte başlatılacak [işçi betikleri] (worker.md) global seçenek altında belirtilebilir.
```caddyfile
{
frankenphp {
num_threads <num_threads> # Başlatılacak PHP iş parçacığı sayısını ayarlar. Varsayılan: Mevcut CPU çekirdek sayısının 2 katı.
worker {
file <path> # Çalışan komut dosyasının yolunu ayarlar.
num <num> # Başlatılacak PHP iş parçacığı sayısını ayarlar, varsayılan değer mevcut CPU çekirdek sayısının 2 katıdır.
env <key> <value> # Ek bir ortam değişkenini verilen değere ayarlar. Birden fazla ortam değişkeni için birden fazla kez belirtilebilir.
}
}
}
# ...
```
Alternatif olarak, `worker` seçeneğinin tek satırlık kısa formunu kullanabilirsiniz:
```caddyfile
{
frankenphp {
worker <file> <num>
}
}
# ...
```
Aynı sunucuda birden fazla uygulamaya hizmet veriyorsanız birden fazla işçi de tanımlayabilirsiniz:
```caddyfile
{
frankenphp {
worker /path/to/app/public/index.php <num>
worker /path/to/other/public/index.php <num>
}
}
app.example.com {
root * /path/to/app/public
php_server
}
other.example.com {
root * /path/to/other/public
php_server
}
# ...
```
Genellikle ihtiyacınız olan şey `php_server` yönergesini kullanmaktır,
ancak tam kontrole ihtiyacınız varsa, daha düşük seviyeli `php` yönergesini kullanabilirsiniz:
php_server` yönergesini kullanmak bu yapılandırmay ile aynıdır:
```caddyfile
route {
# Dizin istekleri için sondaki eğik çizgiyi, diğer adıyla taksim işaretini ekleyin
@canonicalPath {
file {path}/index.php
not path */
}
redir @canonicalPath {path}/ 308
# İstenen dosya mevcut değilse, dizin dosyalarını deneyin
@indexFiles file {
try_files {path} {path}/index.php index.php
split_path .php
}
rewrite @indexFiles {http.matchers.file.relative}
# FrankenPHP!
@phpFiles path *.php
php @phpFiles
file_server
}
```
php_server` ve `php` yönergeleri aşağıdaki seçeneklere sahiptir:
```caddyfile
php_server [<matcher>] {
root <directory> # Sitenin kök klasörünü ayarlar. Öntanımlı: `root` yönergesi.
split_path <delim...> # URI'yi iki parçaya bölmek için alt dizgeleri ayarlar. İlk eşleşen alt dizge "yol bilgisini" yoldan ayırmak için kullanılır. İlk parça eşleşen alt dizeyle sonlandırılır ve gerçek kaynak (CGI betiği) adı olarak kabul edilir. İkinci parça betiğin kullanması için PATH_INFO olarak ayarlanacaktır. Varsayılan: `.php`
resolve_root_symlink false # Varsa, sembolik bir bağlantıyı değerlendirerek `root` dizininin gerçek değerine çözümlenmesini devre dışı bırakır (varsayılan olarak etkindir).
env <key> <value> # Ek bir ortam değişkenini verilen değere ayarlar. Birden fazla ortam değişkeni için birden fazla kez belirtilebilir.
}
```
## Ortam Değişkenleri
Aşağıdaki ortam değişkenleri `Caddyfile` içinde değişiklik yapmadan Caddy yönergelerini entegre etmek için kullanılabilir:
* `SERVER_NAME`: değiştirin [dinlenecek adresleri](https://caddyserver.com/docs/caddyfile/concepts#addresses), sağlanan ana bilgisayar adları oluşturulan TLS sertifikası için de kullanılacaktır
* `CADDY_GLOBAL_OPTIONS`: entegre edin [global seçenekler](https://caddyserver.com/docs/caddyfile/options)
* `FRANKENPHP_CONFIG`: `frankenphp` yönergesi altına yapılandırma entegre edin
FPM ve CLI SAPI'lerinde olduğu gibi, ortam değişkenleri varsayılan olarak `$_SERVER` süper globalinde gösterilir.
[`variables_order`'a ait PHP yönergesinin](https://www.php.net/manual/en/ini.core.php#ini.variables-order) `S` değeri bu yönergede `E`'nin başka bir yere yerleştirilmesinden bağımsız olarak her zaman `ES` ile eş değerdir.
## PHP konfigürasyonu
Ek olarak [PHP yapılandırma dosyalarını](https://www.php.net/manual/en/configuration.file.php#configuration.file.scan) yüklemek için
`PHP_INI_SCAN_DIR` ortam değişkeni kullanılabilir.
Ayarlandığında, PHP verilen dizinlerde bulunan `.ini` uzantılı tüm dosyaları yükleyecektir.
## Hata Ayıklama Modunu Etkinleştirin
Docker imajını kullanırken, hata ayıklama modunu etkinleştirmek için `CADDY_GLOBAL_OPTIONS` ortam değişkenini `debug` olarak ayarlayın:
```console
docker run -v $PWD:/app/public \
-e CADDY_GLOBAL_OPTIONS=debug \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
# Konfigürasyon
FrankenPHP, Caddy'nin yanı sıra Mercure ve Vulcain modülleri [Caddy tarafından desteklenen formatlar](https://caddyserver.com/docs/getting-started#your-first-config) kullanılarak yapılandırılabilir.
Docker imajlarında] (docker.md), `Caddyfile` `/etc/frankenphp/Caddyfile` adresinde bulunur.
Statik ikili, başlatıldığı dizinde `Caddyfile` dosyasını arayacaktır.
PHP'nin kendisi [bir `php.ini` dosyası kullanılarak yapılandırılabilir](https://www.php.net/manual/tr/configuration.file.php).
PHP yorumlayıcısı aşağıdaki konumlarda arama yapacaktır:
Docker:
- php.ini: `/usr/local/etc/php/php.ini` Varsayılan olarak php.ini sağlanmaz.
- ek yapılandırma dosyaları: `/usr/local/etc/php/conf.d/*.ini`
- php uzantıları: `/usr/local/lib/php/extensions/no-debug-zts-<YYYYMMDD>/`
- PHP projesi tarafından sağlanan resmi bir şablonu kopyalamalısınız:
```dockerfile
FROM dunglas/frankenphp
# Developement:
RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini
# Veya production:
RUN cp $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini
```
FrankenPHP kurulumu (.rpm veya .deb):
- php.ini: `/etc/frankenphp/php.ini` Varsayılan olarak üretim ön ayarlarına sahip bir php.ini dosyası sağlanır.
- ek yapılandırma dosyaları: `/etc/frankenphp/php.d/*.ini`
- php uzantıları: `/usr/lib/frankenphp/modules/`
Statik ikili:
- php.ini: `frankenphp run` veya `frankenphp php-server` komutunun çalıştırıldığı dizin, ardından `/etc/frankenphp/php.ini`
- ek yapılandırma dosyaları: `/etc/frankenphp/php.d/*.ini`
- php uzantıları: yüklenemez
- [PHP kaynak kodu](https://github.com/php/php-src/) ile birlikte verilen `php.ini-production` veya `php.ini-development` dosyalarından birini kopyalayın.
## Caddyfile Konfigürasyonu
PHP uygulamanızı sunmak için site blokları içinde `php_server` veya `php` [HTTP yönergeleri](https://caddyserver.com/docs/caddyfile/concepts#directives) kullanılabilir.
Minimal örnek:
```caddyfile
localhost {
# Sıkıştırmayı etkinleştir (isteğe bağlı)
encode zstd br gzip
# Geçerli dizindeki PHP dosyalarını çalıştırın ve varlıkları sunun
php_server
}
```
FrankenPHP'yi global seçenek kullanarak açıkça yapılandırabilirsiniz:
`frankenphp` [global seçenek](https://caddyserver.com/docs/caddyfile/concepts#global-options) FrankenPHP'yi yapılandırmak için kullanılabilir.
```caddyfile
{
frankenphp {
num_threads <num_threads> # Başlatılacak PHP iş parçacığı sayısını ayarlar. Varsayılan: Mevcut CPU çekirdek sayısının 2 katı.
worker {
file <path> # Çalışan komut dosyasının yolunu ayarlar.
num <num> # Başlatılacak PHP iş parçacığı sayısını ayarlar, varsayılan değer mevcut CPU çekirdek sayısının 2 katıdır.
env <key> <value> # Ek bir ortam değişkenini verilen değere ayarlar. Birden fazla ortam değişkeni için birden fazla kez belirtilebilir.
}
}
}
# ...
```
Alternatif olarak, `worker` seçeneğinin tek satırlık kısa formunu kullanabilirsiniz:
```caddyfile
{
frankenphp {
worker <file> <num>
}
}
# ...
```
Aynı sunucuda birden fazla uygulamaya hizmet veriyorsanız birden fazla işçi de tanımlayabilirsiniz:
```caddyfile
app.example.com {
php_server {
root /path/to/app/public
worker index.php <num>
}
}
other.example.com {
php_server {
root /path/to/other/public
worker index.php <num>
}
}
# ...
```
Genellikle ihtiyacınız olan şey `php_server` yönergesini kullanmaktır,
ancak tam kontrole ihtiyacınız varsa, daha düşük seviyeli `php` yönergesini kullanabilirsiniz:
php_server` yönergesini kullanmak bu yapılandırmay ile aynıdır:
```caddyfile
route {
# Dizin istekleri için sondaki eğik çizgiyi, diğer adıyla taksim işaretini ekleyin
@canonicalPath {
file {path}/index.php
not path */
}
redir @canonicalPath {path}/ 308
# İstenen dosya mevcut değilse, dizin dosyalarını deneyin
@indexFiles file {
try_files {path} {path}/index.php index.php
split_path .php
}
rewrite @indexFiles {http.matchers.file.relative}
# FrankenPHP!
@phpFiles path *.php
php @phpFiles
file_server
}
```
php_server`ve`php` yönergeleri aşağıdaki seçeneklere sahiptir:
```caddyfile
php_server [<matcher>] {
root <directory> # Sitenin kök klasörünü ayarlar. Öntanımlı: `root` yönergesi.
split_path <delim...> # URI'yi iki parçaya bölmek için alt dizgeleri ayarlar. İlk eşleşen alt dizge "yol bilgisini" yoldan ayırmak için kullanılır. İlk parça eşleşen alt dizeyle sonlandırılır ve gerçek kaynak (CGI betiği) adı olarak kabul edilir. İkinci parça betiğin kullanması için PATH_INFO olarak ayarlanacaktır. Varsayılan: `.php`
resolve_root_symlink false # Varsa, sembolik bir bağlantıyı değerlendirerek `root` dizininin gerçek değerine çözümlenmesini devre dışı bırakır (varsayılan olarak etkindir).
env <key> <value> # Ek bir ortam değişkenini verilen değere ayarlar. Birden fazla ortam değişkeni için birden fazla kez belirtilebilir.
file_server off # Yerleşik file_server yönergesini devre dışı bırakır.
worker { # Bu sunucuya özgü bir worker oluşturur. Birden fazla worker için birden fazla kez belirtilebilir.
file <path> # Worker betiğinin yolunu ayarlar, php_server köküne göre göreceli olabilir
num <num> # Başlatılacak PHP iş parçacığı sayısını ayarlar, varsayılan değer mevcut CPU çekirdek sayısının 2 katıdır
name <name> # Worker için günlüklerde ve metriklerde kullanılan bir ad ayarlar. Varsayılan: worker dosyasının mutlak yolu. Bir php_server bloğunda tanımlandığında her zaman m# ile başlar.
watch <path> # Dosya değişikliklerini izlemek için yolu ayarlar. Birden fazla yol için birden fazla kez belirtilebilir.
env <key> <value> # Ek bir ortam değişkenini verilen değere ayarlar. Birden fazla ortam değişkeni için birden fazla kez belirtilebilir. Bu worker için ortam değişkenleri ayrıca php_server üst öğesinden devralınır, ancak burada geçersiz kılınabilir.
}
worker <other_file> <num> # Global frankenphp bloğundaki gibi kısa formu da kullanabilirsiniz.
}
```
## Ortam Değişkenleri
Aşağıdaki ortam değişkenleri `Caddyfile` içinde değişiklik yapmadan Caddy yönergelerini entegre etmek için kullanılabilir:
- `SERVER_NAME`: değiştirin [dinlenecek adresleri](https://caddyserver.com/docs/caddyfile/concepts#addresses), sağlanan ana bilgisayar adları oluşturulan TLS sertifikası için de kullanılacaktır
- `CADDY_GLOBAL_OPTIONS`: entegre edin [global seçenekler](https://caddyserver.com/docs/caddyfile/options)
- `FRANKENPHP_CONFIG`: `frankenphp` yönergesi altına yapılandırma entegre edin
FPM ve CLI SAPI'lerinde olduğu gibi, ortam değişkenleri varsayılan olarak `$_SERVER` süper globalinde gösterilir.
[`variables_order`'a ait PHP yönergesinin](https://www.php.net/manual/en/ini.core.php#ini.variables-order) `S` değeri bu yönergede `E`'nin başka bir yere yerleştirilmesinden bağımsız olarak her zaman `ES` ile eş değerdir.
## PHP konfigürasyonu
Ek olarak [PHP yapılandırma dosyalarını](https://www.php.net/manual/en/configuration.file.php#configuration.file.scan) yüklemek için
`PHP_INI_SCAN_DIR` ortam değişkeni kullanılabilir.
Ayarlandığında, PHP verilen dizinlerde bulunan `.ini` uzantılı tüm dosyaları yükleyecektir.
## Hata Ayıklama Modunu Etkinleştirin
Docker imajını kullanırken, hata ayıklama modunu etkinleştirmek için `CADDY_GLOBAL_OPTIONS` ortam değişkenini `debug` olarak ayarlayın:
```console
docker run -v $PWD:/app/public \
-e CADDY_GLOBAL_OPTIONS=debug \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```

View File

@@ -1,171 +1,171 @@
# Özel Docker İmajı Oluşturma
[Resmi PHP imajları](https://hub.docker.com/_/php/) temel alınarak [FrankenPHP Docker imajları](https://hub.docker.com/r/dunglas/frankenphp) hazırlanmıştır. Popüler mimariler için Debian ve Alpine Linux varyantları sağlanmıştır. Debian dağıtımı tavsiye edilir.
PHP 8.2, 8.3 ve 8.4 için varyantlar sağlanmıştır. [Etiketlere göz atın](https://hub.docker.com/r/dunglas/frankenphp/tags).
## İmajlar Nasıl Kullanılır
Projenizde bir `Dockerfile` oluşturun:
```dockerfile
FROM dunglas/frankenphp
COPY . /app/public
```
Ardından, Docker imajını oluşturmak ve çalıştırmak için bu komutları çalıştırın:
```console
docker build -t my-php-app .
docker run -it --rm --name my-running-app my-php-app
```
## Daha Fazla PHP Eklentisi Nasıl Kurulur
[Docker-php-extension-installer`](https://github.com/mlocati/docker-php-extension-installer) betiği temel imajda sağlanmıştır.
Ek PHP eklentileri eklemek ise gerçekten kolaydır:
```dockerfile
FROM dunglas/frankenphp
# buraya istenilen eklentileri ekleyin:
RUN install-php-extensions \
pdo_mysql \
gd \
intl \
zip \
opcache
```
## Daha Fazla Caddy Modülü Nasıl Kurulur
FrankenPHP, Caddy'nin üzerine inşa edilmiştir ve tüm [Caddy modülleri](https://caddyserver.com/docs/modules/) FrankenPHP ile kullanılabilir.
Özel Caddy modüllerini kurmanın en kolay yolu [xcaddy](https://github.com/caddyserver/xcaddy) kullanmaktır:
```dockerfile
FROM dunglas/frankenphp:builder AS builder
# xcaddy'yi derleyen imaja kopyalayın
COPY --from=caddy:builder /usr/bin/xcaddy /usr/bin/xcaddy
# FrankenPHP oluşturmak için CGO etkinleştirilmelidir
RUN CGO_ENABLED=1 \
XCADDY_SETCAP=1 \
XCADDY_GO_BUILD_FLAGS="-ldflags='-w -s' -tags=nobadger,nomysql,nopgx" \
CGO_CFLAGS=$(php-config --includes) \
CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \
xcaddy build \
--output /usr/local/bin/frankenphp \
--with github.com/dunglas/frankenphp=./ \
--with github.com/dunglas/frankenphp/caddy=./caddy/ \
--with github.com/dunglas/caddy-cbrotli \
# Mercure ve Vulcain resmi yapıya dahil edilmiştir, ancak bunları kaldırmaktan çekinmeyin
--with github.com/dunglas/mercure/caddy \
--with github.com/dunglas/vulcain/caddy
# Buraya ekstra Caddy modülleri ekleyin
FROM dunglas/frankenphp AS runner
# Resmi binary dosyayı özel modüllerinizi içeren binary dosyayla değiştirin
COPY --from=builder /usr/local/bin/frankenphp /usr/local/bin/frankenphp
```
FrankenPHP tarafından sağlanan `builder` imajı `libphp`'nin derlenmiş bir sürümünü içerir.
[Derleyici imajları](https://hub.docker.com/r/dunglas/frankenphp/tags?name=builder) hem Debian hem de Alpine için FrankenPHP ve PHP'nin tüm sürümleri için sağlanmıştır.
> [!TIP]
>
> Eğer Alpine Linux ve Symfony kullanıyorsanız,
> [varsayılan yığın boyutunu artırmanız](compile.md#xcaddy-kullanımı) gerekebilir.
## Varsayılan Olarak Worker Modunun Etkinleştirilmesi
FrankenPHP'yi bir worker betiği ile başlatmak için `FRANKENPHP_CONFIG` ortam değişkenini ayarlayın:
```dockerfile
FROM dunglas/frankenphp
# ...
ENV FRANKENPHP_CONFIG="worker ./public/index.php"
```
## Geliştirme Sürecinde Yığın (Volume) Kullanma
FrankenPHP ile kolayca geliştirme yapmak için, uygulamanın kaynak kodunu içeren dizini ana bilgisayarınızdan Docker konteynerine bir yığın (volume) olarak bağlayın:
```console
docker run -v $PWD:/app/public -p 80:80 -p 443:443 -p 443:443/udp --tty my-php-app
```
> [!TIP]
>
> `--tty` seçeneği JSON günlükleri yerine insan tarafından okunabilir güzel günlüklere sahip olmayı sağlar.
Docker Compose ile:
```yaml
# compose.yaml
services:
php:
image: dunglas/frankenphp
# özel bir Dockerfile kullanmak istiyorsanız aşağıdaki yorum satırını kaldırın
#build: .
# bunu bir production ortamında çalıştırmak istiyorsanız aşağıdaki yorum satırını kaldırın
# restart: always
ports:
- "80:80" # HTTP
- "443:443" # HTTPS
- "443:443/udp" # HTTP/3
volumes:
- ./:/app/public
- caddy_data:/data
- caddy_config:/config
# production ortamda aşağıdaki satırı yorum satırı yapın, geliştirme ortamında insan tarafından okunabilir güzel günlüklere sahip olmanızı sağlar
tty: true
# Caddy sertifikaları ve yapılandırması için gereken yığınlar (volumes)
volumes:
caddy_data:
caddy_config:
```
## Root Olmayan Kullanıcı Olarak Çalıştırma
FrankenPHP, Docker'da root olmayan kullanıcı olarak çalışabilir.
İşte bunu yapan örnek bir `Dockerfile`:
```dockerfile
FROM dunglas/frankenphp
ARG USER=appuser
RUN \
# Alpine tabanlı dağıtımlar için "adduser -D ${USER}" kullanın
useradd ${USER}; \
# 80 ve 443 numaralı bağlantı noktalarına bağlanmak için ek özellik ekleyin
setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp; \
# /data/caddy ve /config/caddy dosyalarına yazma erişimi verin
chown -R ${USER}:${USER} /data/caddy && chown -R ${USER}:${USER} /config/caddy;
USER ${USER}
```
## Güncellemeler
Docker imajları oluşturulur:
* Yeni bir sürüm etiketlendiğinde
* Her gün UTC ile saat 4'te Resmi PHP imajlarının yeni sürümleri mevcutsa
## Geliştirme Sürümleri
Geliştirme sürümleri [`dunglas/frankenphp-dev`](https://hub.docker.com/repository/docker/dunglas/frankenphp-dev) Docker deposunda mevcuttur.
GitHub deposunun ana dalına her commit yapıldığında yeni bir derleme tetiklenir.
`latest*` etiketleri `main` dalının başına işaret eder.
`sha-<hash-du-commit-git>` biçimindeki etiketler de kullanılabilir.
# Özel Docker İmajı Oluşturma
[Resmi PHP imajları](https://hub.docker.com/_/php/) temel alınarak [FrankenPHP Docker imajları](https://hub.docker.com/r/dunglas/frankenphp) hazırlanmıştır. Popüler mimariler için Debian ve Alpine Linux varyantları sağlanmıştır. Debian dağıtımı tavsiye edilir.
PHP 8.2, 8.3 ve 8.4 için varyantlar sağlanmıştır. [Etiketlere göz atın](https://hub.docker.com/r/dunglas/frankenphp/tags).
## İmajlar Nasıl Kullanılır
Projenizde bir `Dockerfile` oluşturun:
```dockerfile
FROM dunglas/frankenphp
COPY . /app/public
```
Ardından, Docker imajını oluşturmak ve çalıştırmak için bu komutları çalıştırın:
```console
docker build -t my-php-app .
docker run -it --rm --name my-running-app my-php-app
```
## Daha Fazla PHP Eklentisi Nasıl Kurulur
[Docker-php-extension-installer`](https://github.com/mlocati/docker-php-extension-installer) betiği temel imajda sağlanmıştır.
Ek PHP eklentileri eklemek ise gerçekten kolaydır:
```dockerfile
FROM dunglas/frankenphp
# buraya istenilen eklentileri ekleyin:
RUN install-php-extensions \
pdo_mysql \
gd \
intl \
zip \
opcache
```
## Daha Fazla Caddy Modülü Nasıl Kurulur
FrankenPHP, Caddy'nin üzerine inşa edilmiştir ve tüm [Caddy modülleri](https://caddyserver.com/docs/modules/) FrankenPHP ile kullanılabilir.
Özel Caddy modüllerini kurmanın en kolay yolu [xcaddy](https://github.com/caddyserver/xcaddy) kullanmaktır:
```dockerfile
FROM dunglas/frankenphp:builder AS builder
# xcaddy'yi derleyen imaja kopyalayın
COPY --from=caddy:builder /usr/bin/xcaddy /usr/bin/xcaddy
# FrankenPHP oluşturmak için CGO etkinleştirilmelidir
RUN CGO_ENABLED=1 \
XCADDY_SETCAP=1 \
XCADDY_GO_BUILD_FLAGS="-ldflags='-w -s' -tags=nobadger,nomysql,nopgx" \
CGO_CFLAGS=$(php-config --includes) \
CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \
xcaddy build \
--output /usr/local/bin/frankenphp \
--with frankenphp.dev=./ \
--with frankenphp.dev/caddy=./caddy/ \
--with github.com/dunglas/caddy-cbrotli \
# Mercure ve Vulcain resmi yapıya dahil edilmiştir, ancak bunları kaldırmaktan çekinmeyin
--with github.com/dunglas/mercure/caddy \
--with github.com/dunglas/vulcain/caddy
# Buraya ekstra Caddy modülleri ekleyin
FROM dunglas/frankenphp AS runner
# Resmi binary dosyayı özel modüllerinizi içeren binary dosyayla değiştirin
COPY --from=builder /usr/local/bin/frankenphp /usr/local/bin/frankenphp
```
FrankenPHP tarafından sağlanan `builder` imajı `libphp`'nin derlenmiş bir sürümünü içerir.
[Derleyici imajları](https://hub.docker.com/r/dunglas/frankenphp/tags?name=builder) hem Debian hem de Alpine için FrankenPHP ve PHP'nin tüm sürümleri için sağlanmıştır.
> [!TIP]
>
> Eğer Alpine Linux ve Symfony kullanıyorsanız,
> [varsayılan yığın boyutunu artırmanız](compile.md#xcaddy-kullanımı) gerekebilir.
## Varsayılan Olarak Worker Modunun Etkinleştirilmesi
FrankenPHP'yi bir worker betiği ile başlatmak için `FRANKENPHP_CONFIG` ortam değişkenini ayarlayın:
```dockerfile
FROM dunglas/frankenphp
# ...
ENV FRANKENPHP_CONFIG="worker ./public/index.php"
```
## Geliştirme Sürecinde Yığın (Volume) Kullanma
FrankenPHP ile kolayca geliştirme yapmak için, uygulamanın kaynak kodunu içeren dizini ana bilgisayarınızdan Docker konteynerine bir yığın (volume) olarak bağlayın:
```console
docker run -v $PWD:/app/public -p 80:80 -p 443:443 -p 443:443/udp --tty my-php-app
```
> [!TIP]
>
> `--tty` seçeneği JSON günlükleri yerine insan tarafından okunabilir güzel günlüklere sahip olmayı sağlar.
Docker Compose ile:
```yaml
# compose.yaml
services:
php:
image: dunglas/frankenphp
# özel bir Dockerfile kullanmak istiyorsanız aşağıdaki yorum satırını kaldırın
#build: .
# bunu bir production ortamında çalıştırmak istiyorsanız aşağıdaki yorum satırını kaldırın
# restart: always
ports:
- "80:80" # HTTP
- "443:443" # HTTPS
- "443:443/udp" # HTTP/3
volumes:
- ./:/app/public
- caddy_data:/data
- caddy_config:/config
# production ortamda aşağıdaki satırı yorum satırı yapın, geliştirme ortamında insan tarafından okunabilir güzel günlüklere sahip olmanızı sağlar
tty: true
# Caddy sertifikaları ve yapılandırması için gereken yığınlar (volumes)
volumes:
caddy_data:
caddy_config:
```
## Root Olmayan Kullanıcı Olarak Çalıştırma
FrankenPHP, Docker'da root olmayan kullanıcı olarak çalışabilir.
İşte bunu yapan örnek bir `Dockerfile`:
```dockerfile
FROM dunglas/frankenphp
ARG USER=appuser
RUN \
# Alpine tabanlı dağıtımlar için "adduser -D ${USER}" kullanın
useradd ${USER}; \
# 80 ve 443 numaralı bağlantı noktalarına bağlanmak için ek özellik ekleyin
setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp; \
# /data/caddy ve /config/caddy dosyalarına yazma erişimi verin
chown -R ${USER}:${USER} /data/caddy && chown -R ${USER}:${USER} /config/caddy;
USER ${USER}
```
## Güncellemeler
Docker imajları oluşturulur:
- Yeni bir sürüm etiketlendiğinde
- Her gün UTC ile saat 4'te Resmi PHP imajlarının yeni sürümleri mevcutsa
## Geliştirme Sürümleri
Geliştirme sürümleri [`dunglas/frankenphp-dev`](https://hub.docker.com/repository/docker/dunglas/frankenphp-dev) Docker deposunda mevcuttur.
GitHub deposunun ana dalına her commit yapıldığında yeni bir derleme tetiklenir.
`latest*` etiketleri `main` dalının başına işaret eder.
`sha-<hash-du-commit-git>` biçimindeki etiketler de kullanılabilir.

View File

@@ -1,21 +1,21 @@
# Early Hints
FrankenPHP [103 Early Hints durum kodunu](https://developer.chrome.com/blog/early-hints/) yerel olarak destekler.
Early Hints kullanmak web sayfalarınızın yüklenme süresini %30 oranında artırabilir.
```php
<?php
header('Link: </style.css>; rel=preload; as=style');
headers_send(103);
// yavaş algoritmalarınız ve SQL sorgularınız 🤪
echo <<<'HTML'
<!DOCTYPE html>
<title>Hello FrankenPHP</title>
<link rel="stylesheet" href="style.css">
HTML;
```
Early Hints hem normal hem de [worker](worker.md) modları tarafından desteklenir.
# Early Hints
FrankenPHP [103 Early Hints durum kodunu](https://developer.chrome.com/blog/early-hints/) yerel olarak destekler.
Early Hints kullanmak web sayfalarınızın yüklenme süresini %30 oranında artırabilir.
```php
<?php
header('Link: </style.css>; rel=preload; as=style');
headers_send(103);
// yavaş algoritmalarınız ve SQL sorgularınız 🤪
echo <<<'HTML'
<!DOCTYPE html>
<title>Hello FrankenPHP</title>
<link rel="stylesheet" href="style.css">
HTML;
```
Early Hints hem normal hem de [worker](worker.md) modları tarafından desteklenir.

View File

@@ -1,132 +1,132 @@
# Binary Dosyası Olarak PHP Uygulamaları
FrankenPHP, PHP uygulamalarının kaynak kodunu ve varlıklarını statik, kendi kendine yeten bir binary dosyaya yerleştirme yeteneğine sahiptir.
Bu özellik sayesinde PHP uygulamaları, uygulamanın kendisini, PHP yorumlayıcısını ve üretim düzeyinde bir web sunucusu olan Caddy'yi içeren bağımsız bir binary dosyalar olarak çıktısı alınabilir ve dağıtılabilir.
Bu özellik hakkında daha fazla bilgi almak için [Kévin tarafından SymfonyCon 2023'te yapılan sunuma](https://dunglas.dev/2023/12/php-and-symfony-apps-as-standalone-binaries/) göz atabilirsiniz.
## Preparing Your App
Bağımsız binary dosyayı oluşturmadan önce uygulamanızın gömülmeye hazır olduğundan emin olun.
Örneğin muhtemelen şunları yapmak istersiniz:
* Uygulamanın üretim bağımlılıklarını yükleyin
* Otomatik yükleyiciyi boşaltın
* Uygulamanızın üretim modunu etkinleştirin (varsa)
* Nihai binary dosyanızın boyutunu küçültmek için `.git` veya testler gibi gerekli olmayan dosyaları çıkarın
Örneğin, bir Symfony uygulaması için aşağıdaki komutları kullanabilirsiniz:
```console
# .git/, vb. dosyalarından kurtulmak için projeyi dışa aktarın
mkdir $TMPDIR/my-prepared-app
git archive HEAD | tar -x -C $TMPDIR/my-prepared-app
cd $TMPDIR/my-prepared-app
# Uygun ortam değişkenlerini ayarlayın
echo APP_ENV=prod > .env.local
echo APP_DEBUG=0 >> .env.local
# Testleri kaldırın
rm -Rf tests/
# Bağımlılıkları yükleyin
composer install --ignore-platform-reqs --no-dev -a
# .env'yi optimize edin
composer dump-env prod
```
## Linux Binary'si Oluşturma
Bir Linux binary çıktısı almanın en kolay yolu, sağladığımız Docker tabanlı derleyiciyi kullanmaktır.
1. Hazırladığınız uygulamanın deposunda `static-build.Dockerfile` adlı bir dosya oluşturun:
```dockerfile
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder
# Uygulamanızı kopyalayın
WORKDIR /go/src/app/dist/app
COPY . .
# Statik binary dosyasını oluşturun, yalnızca istediğiniz PHP eklentilerini seçtiğinizden emin olun
WORKDIR /go/src/app/
RUN EMBED=dist/app/ \
PHP_EXTENSIONS=ctype,iconv,pdo_sqlite \
./build-static.sh
```
> [!CAUTION]
>
> Bazı `.dockerignore` dosyaları (örneğin varsayılan [Symfony Docker `.dockerignore`](https://github.com/dunglas/symfony-docker/blob/main/.dockerignore))
> `vendor/` dizinini ve `.env` dosyalarını yok sayacaktır. Derlemeden önce `.dockerignore` dosyasını ayarladığınızdan veya kaldırdığınızdan emin olun.
2. Derleyin:
```console
docker build -t static-app -f static-build.Dockerfile .
```
3. Binary dosyasını çıkarın:
```console
docker cp $(docker create --name static-app-tmp static-app):/go/src/app/dist/frankenphp-linux-x86_64 my-app ; docker rm static-app-tmp
```
Elde edilen binary dosyası, geçerli dizindeki `my-app` adlı dosyadır.
## Diğer İşletim Sistemleri için Binary Çıktısı Alma
Docker kullanmak istemiyorsanız veya bir macOS binary dosyası oluşturmak istiyorsanız, sağladığımız kabuk betiğini kullanın:
```console
git clone https://github.com/dunglas/frankenphp
cd frankenphp
EMBED=/path/to/your/app \
PHP_EXTENSIONS=ctype,iconv,pdo_sqlite \
./build-static.sh
```
Elde edilen binary dosyası `dist/` dizinindeki `frankenphp-<os>-<arch>` adlı dosyadır.
## Binary Dosyasını Kullanma
İşte bu kadar! `my-app` dosyası (veya diğer işletim sistemlerinde `dist/frankenphp-<os>-<arch>`) bağımsız uygulamanızı içerir!
Web uygulamasını başlatmak için çalıştırın:
```console
./my-app php-server
```
Uygulamanız bir [worker betiği](worker.md) içeriyorsa, worker'ı aşağıdaki gibi bir şeyle başlatın:
```console
./my-app php-server --worker public/index.php
```
HTTPS (Let's Encrypt sertifikası otomatik olarak oluşturulur), HTTP/2 ve HTTP/3'ü etkinleştirmek için kullanılacak alan adını belirtin:
```console
./my-app php-server --domain localhost
```
Ayrıca binary dosyanıza gömülü PHP CLI betiklerini de çalıştırabilirsiniz:
```console
./my-app php-cli bin/console
```
## Yapıyı Özelleştirme
Binary dosyasının nasıl özelleştirileceğini (uzantılar, PHP sürümü...) görmek için [Statik derleme dokümanını okuyun](static.md).
## Binary Dosyasının Dağıtılması
Linux'ta, oluşturulan ikili dosya [UPX](https://upx.github.io) kullanılarak sıkıştırılır.
Mac'te, göndermeden önce dosyanın boyutunu küçültmek için sıkıştırabilirsiniz.
Biz `xz` öneririz.
# Binary Dosyası Olarak PHP Uygulamaları
FrankenPHP, PHP uygulamalarının kaynak kodunu ve varlıklarını statik, kendi kendine yeten bir binary dosyaya yerleştirme yeteneğine sahiptir.
Bu özellik sayesinde PHP uygulamaları, uygulamanın kendisini, PHP yorumlayıcısını ve üretim düzeyinde bir web sunucusu olan Caddy'yi içeren bağımsız bir binary dosyalar olarak çıktısı alınabilir ve dağıtılabilir.
Bu özellik hakkında daha fazla bilgi almak için [Kévin tarafından SymfonyCon 2023'te yapılan sunuma](https://dunglas.dev/2023/12/php-and-symfony-apps-as-standalone-binaries/) göz atabilirsiniz.
## Preparing Your App
Bağımsız binary dosyayı oluşturmadan önce uygulamanızın gömülmeye hazır olduğundan emin olun.
Örneğin muhtemelen şunları yapmak istersiniz:
- Uygulamanın üretim bağımlılıklarını yükleyin
- Otomatik yükleyiciyi boşaltın
- Uygulamanızın üretim modunu etkinleştirin (varsa)
- Nihai binary dosyanızın boyutunu küçültmek için `.git` veya testler gibi gerekli olmayan dosyaları çıkarın
Örneğin, bir Symfony uygulaması için aşağıdaki komutları kullanabilirsiniz:
```console
# .git/, vb. dosyalarından kurtulmak için projeyi dışa aktarın
mkdir $TMPDIR/my-prepared-app
git archive HEAD | tar -x -C $TMPDIR/my-prepared-app
cd $TMPDIR/my-prepared-app
# Uygun ortam değişkenlerini ayarlayın
echo APP_ENV=prod > .env.local
echo APP_DEBUG=0 >> .env.local
# Testleri kaldırın
rm -Rf tests/
# Bağımlılıkları yükleyin
composer install --ignore-platform-reqs --no-dev -a
# .env'yi optimize edin
composer dump-env prod
```
## Linux Binary'si Oluşturma
Bir Linux binary çıktısı almanın en kolay yolu, sağladığımız Docker tabanlı derleyiciyi kullanmaktır.
1. Hazırladığınız uygulamanın deposunda `static-build.Dockerfile` adlı bir dosya oluşturun:
```dockerfile
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder
# Uygulamanızı kopyalayın
WORKDIR /go/src/app/dist/app
COPY . .
# Statik binary dosyasını oluşturun, yalnızca istediğiniz PHP eklentilerini seçtiğinizden emin olun
WORKDIR /go/src/app/
RUN EMBED=dist/app/ \
PHP_EXTENSIONS=ctype,iconv,pdo_sqlite \
./build-static.sh
```
> [!CAUTION]
>
> Bazı `.dockerignore` dosyaları (örneğin varsayılan [Symfony Docker `.dockerignore`](https://github.com/dunglas/symfony-docker/blob/main/.dockerignore))
> `vendor/` dizinini ve `.env` dosyalarını yok sayacaktır. Derlemeden önce `.dockerignore` dosyasını ayarladığınızdan veya kaldırdığınızdan emin olun.
2. Derleyin:
```console
docker build -t static-app -f static-build.Dockerfile .
```
3. Binary dosyasını çıkarın:
```console
docker cp $(docker create --name static-app-tmp static-app):/go/src/app/dist/frankenphp-linux-x86_64 my-app ; docker rm static-app-tmp
```
Elde edilen binary dosyası, geçerli dizindeki `my-app` adlı dosyadır.
## Diğer İşletim Sistemleri için Binary Çıktısı Alma
Docker kullanmak istemiyorsanız veya bir macOS binary dosyası oluşturmak istiyorsanız, sağladığımız kabuk betiğini kullanın:
```console
git clone https://github.com/dunglas/frankenphp
cd frankenphp
EMBED=/path/to/your/app \
PHP_EXTENSIONS=ctype,iconv,pdo_sqlite \
./build-static.sh
```
Elde edilen binary dosyası `dist/` dizinindeki `frankenphp-<os>-<arch>` adlı dosyadır.
## Binary Dosyasını Kullanma
İşte bu kadar! `my-app` dosyası (veya diğer işletim sistemlerinde `dist/frankenphp-<os>-<arch>`) bağımsız uygulamanızı içerir!
Web uygulamasını başlatmak için çalıştırın:
```console
./my-app php-server
```
Uygulamanız bir [worker betiği](worker.md) içeriyorsa, worker'ı aşağıdaki gibi bir şeyle başlatın:
```console
./my-app php-server --worker public/index.php
```
HTTPS (Let's Encrypt sertifikası otomatik olarak oluşturulur), HTTP/2 ve HTTP/3'ü etkinleştirmek için kullanılacak alan adını belirtin:
```console
./my-app php-server --domain localhost
```
Ayrıca binary dosyanıza gömülü PHP CLI betiklerini de çalıştırabilirsiniz:
```console
./my-app php-cli bin/console
```
## Yapıyı Özelleştirme
Binary dosyasının nasıl özelleştirileceğini (uzantılar, PHP sürümü...) görmek için [Statik derleme dokümanını okuyun](static.md).
## Binary Dosyasının Dağıtılması
Linux'ta, oluşturulan ikili dosya [UPX](https://upx.github.io) kullanılarak sıkıştırılır.
Mac'te, göndermeden önce dosyanın boyutunu küçültmek için sıkıştırabilirsiniz.
Biz `xz` öneririz.

View File

@@ -1,31 +1,31 @@
# GitHub Actions Kullanma
Bu depo Docker imajını [Docker Hub](https://hub.docker.com/r/dunglas/frankenphp) üzerinde derler ve dağıtır.
Bu durum onaylanan her çekme (pull) isteğinde veya çatallandıktan (fork) sonra gerçekleşir.
## GitHub Eylemlerini Ayarlama
Depo ayarlarında, gizli değerler altında aşağıdaki gizli değerleri ekleyin:
- `REGISTRY_LOGIN_SERVER`: Kullanılacak Docker Registry bilgisi (örneğin `docker.io`).
- `REGISTRY_USERNAME`: Giriş yapmak için kullanılacak kullanıcı adı (örn. `dunglas`).
- `REGISTRY_PASSWORD`: Oturum açmak için kullanılacak parola (örn. bir erişim anahtarı).
- `IMAGE_NAME`: İmajın adı (örn. `dunglas/frankenphp`).
## İmajı Oluşturma ve Dağıtma
1. Bir Çekme (pull) İsteği oluşturun veya çatala (forka) dağıtın.
2. GitHub Actions imajı oluşturacak ve tüm testleri çalıştıracaktır.
3. Derleme başarılı olursa, görüntü `pr-x` (burada `x` PR numarasıdır) etiketi kullanılarak ilgili saklanan yere (registry'e) gönderilir.
## İmajı Dağıtma
1. Çekme (pull) isteği birleştirildikten sonra, GitHub Actions testleri tekrar çalıştıracak ve yeni bir imaj oluşturacaktır.
2. Derleme başarılı olursa, `main` etiketi Docker Registry'de güncellenecektir.
## Bültenler
1. Depoda yeni bir etiket oluşturun.
2. GitHub Actions imajı oluşturacak ve tüm testleri çalıştıracaktır.
3. Derleme başarılı olursa, etiket adı etiket olarak kullanılarak imaj saklanan yere (registry'e) gönderilir (örneğin `v1.2.3` ve `v1.2` oluşturulur).
4. `latest` etiketi de güncellenecektir.
# GitHub Actions Kullanma
Bu depo Docker imajını [Docker Hub](https://hub.docker.com/r/dunglas/frankenphp) üzerinde derler ve dağıtır.
Bu durum onaylanan her çekme (pull) isteğinde veya çatallandıktan (fork) sonra gerçekleşir.
## GitHub Eylemlerini Ayarlama
Depo ayarlarında, gizli değerler altında aşağıdaki gizli değerleri ekleyin:
- `REGISTRY_LOGIN_SERVER`: Kullanılacak Docker Registry bilgisi (örneğin `docker.io`).
- `REGISTRY_USERNAME`: Giriş yapmak için kullanılacak kullanıcı adı (örn. `dunglas`).
- `REGISTRY_PASSWORD`: Oturum açmak için kullanılacak parola (örn. bir erişim anahtarı).
- `IMAGE_NAME`: İmajın adı (örn. `dunglas/frankenphp`).
## İmajı Oluşturma ve Dağıtma
1. Bir Çekme (pull) İsteği oluşturun veya çatala (forka) dağıtın.
2. GitHub Actions imajı oluşturacak ve tüm testleri çalıştıracaktır.
3. Derleme başarılı olursa, görüntü `pr-x` (burada `x` PR numarasıdır) etiketi kullanılarak ilgili saklanan yere (registry'e) gönderilir.
## İmajı Dağıtma
1. Çekme (pull) isteği birleştirildikten sonra, GitHub Actions testleri tekrar çalıştıracak ve yeni bir imaj oluşturacaktır.
2. Derleme başarılı olursa, `main` etiketi Docker Registry'de güncellenecektir.
## Bültenler
1. Depoda yeni bir etiket oluşturun.
2. GitHub Actions imajı oluşturacak ve tüm testleri çalıştıracaktır.
3. Derleme başarılı olursa, etiket adı etiket olarak kullanılarak imaj saklanan yere (registry'e) gönderilir (örneğin `v1.2.3` ve `v1.2` oluşturulur).
4. `latest` etiketi de güncellenecektir.

View File

@@ -1,107 +1,107 @@
# Bilinen Sorunlar
## Desteklenmeyen PHP Eklentileri
Aşağıdaki eklentilerin FrankenPHP ile uyumlu olmadığı bilinmektedir:
| Adı | Nedeni | Alternatifleri |
|-------------------------------------------------------------|----------------------------|----------------------------------------------------------------------------------------------------------------------|
| [imap](https://www.php.net/manual/en/imap.installation.php) | İş parçacığı güvenli değil | [javanile/php-imap2](https://github.com/javanile/php-imap2), [webklex/php-imap](https://github.com/Webklex/php-imap) |
## Sorunlu PHP Eklentileri
Aşağıdaki eklentiler FrankenPHP ile kullanıldığında bilinen hatalara ve beklenmeyen davranışlara sahiptir:
| Adı | Problem |
|-----------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
## get_browser
[get_browser()](https://www.php.net/manual/en/function.get-browser.php) fonksiyonu bir süre sonra kötü performans gösteriyor gibi görünüyor. Geçici bir çözüm, statik oldukları için User-Agent başına sonuçları önbelleğe almaktır (örneğin [APCu](https://www.php.net/manual/en/book.apcu.php) ile).
## Binary Çıktısı ve Alpine Tabanlı Docker İmajları
Binary çıktısı ve Alpine tabanlı Docker imajları (dunglas/frankenphp:*-alpine), daha küçük bir binary boyutu korumak için glibc ve arkadaşları yerine musl libc kullanır. Bu durum bazı uyumluluk sorunlarına yol açabilir. Özellikle, glob seçeneği GLOB_BRACE mevcut değildir.
## Docker ile `https://127.0.0.1` Kullanımı
FrankenPHP varsayılan olarak `localhost` için bir TLS sertifikası oluşturur.
Bu, yerel geliştirme için en kolay ve önerilen seçenektir.
Bunun yerine ana bilgisayar olarak `127.0.0.1` kullanmak istiyorsanız, sunucu adını `127.0.0.1` şeklinde ayarlayarak bunun için bir sertifika oluşturacak yapılandırma yapmak mümkündür.
Ne yazık ki, [ağ sistemi](https://docs.docker.com/network/) nedeniyle Docker kullanırken bu yeterli değildir.
`Curl: (35) LibreSSL/3.3.6: error:1404B438:SSL routines:ST_CONNECT:tlsv1 alert internal error`'a benzer bir TLS hatası alırsınız.
Linux kullanıyorsanız, [ana bilgisayar ağ sürücüsünü](https://docs.docker.com/network/network-tutorial-host/) kullanmak bir çözümdür:
```console
docker run \
-e SERVER_NAME="127.0.0.1" \
-v $PWD:/app/public \
--network host \
dunglas/frankenphp
```
Ana bilgisayar ağ sürücüsü Mac ve Windows'ta desteklenmez. Bu platformlarda, konteynerin IP adresini tahmin etmeniz ve bunu sunucu adlarına dahil etmeniz gerekecektir.
`docker network inspect bridge`'i çalıştırın ve `IPv4Address` anahtarının altındaki son atanmış IP adresini belirlemek için `Containers` anahtarına bakın ve bir artırın. Eğer hiçbir konteyner çalışmıyorsa, ilk atanan IP adresi genellikle `172.17.0.2`dir.
Ardından, bunu `SERVER_NAME` ortam değişkenine ekleyin:
```console
docker run \
-e SERVER_NAME="127.0.0.1, 172.17.0.3" \
-v $PWD:/app/public \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
> [!CAUTION]
>
> 172.17.0.3`ü konteynerinize atanacak IP ile değiştirdiğinizden emin olun.
Artık ana makineden `https://127.0.0.1` adresine erişebilmeniz gerekir.
Eğer durum böyle değilse, sorunu anlamaya çalışmak için FrankenPHP'yi hata ayıklama modunda başlatın:
```console
docker run \
-e CADDY_GLOBAL_OPTIONS="debug" \
-e SERVER_NAME="127.0.0.1" \
-v $PWD:/app/public \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
## `@php` Referanslı Composer Betikler
[Composer betikleri](https://getcomposer.org/doc/articles/scripts.md) bazı görevler için bir PHP binary çalıştırmak isteyebilir, örneğin [bir Laravel projesinde](laravel.md) `@php artisan package:discover --ansi` çalıştırmak. Bu [şu anda mümkün değil](https://github.com/dunglas/frankenphp/issues/483#issuecomment-1899890915) ve 2 nedeni var:
* Composer FrankenPHP binary dosyasını nasıl çağıracağını bilmiyor;
* Composer, FrankenPHP'nin henüz desteklemediği `-d` bayrağını kullanarak PHP ayarlarını komuta ekleyebilir.
Geçici bir çözüm olarak, `/usr/local/bin/php` içinde desteklenmeyen parametreleri silen ve ardından FrankenPHP'yi çağıran bir kabuk betiği oluşturabiliriz:
```bash
#!/bin/bash
args=("$@")
index=0
for i in "$@"
do
if [ "$i" == "-d" ]; then
unset 'args[$index]'
unset 'args[$index+1]'
fi
index=$((index+1))
done
/usr/local/bin/frankenphp php-cli ${args[@]}
```
Ardından `PHP_BINARY` ortam değişkenini PHP betiğimizin yoluna ayarlayın ve Composer bu yolla çalışacaktır:
```bash
export PHP_BINARY=/usr/local/bin/php
composer install
```
# Bilinen Sorunlar
## Desteklenmeyen PHP Eklentileri
Aşağıdaki eklentilerin FrankenPHP ile uyumlu olmadığı bilinmektedir:
| Adı | Nedeni | Alternatifleri |
| ----------------------------------------------------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| [imap](https://www.php.net/manual/en/imap.installation.php) | İş parçacığı güvenli değil | [javanile/php-imap2](https://github.com/javanile/php-imap2), [webklex/php-imap](https://github.com/Webklex/php-imap) |
## Sorunlu PHP Eklentileri
Aşağıdaki eklentiler FrankenPHP ile kullanıldığında bilinen hatalara ve beklenmeyen davranışlara sahiptir:
| Adı | Problem |
| --- | ------- |
## get_browser
[get_browser()](https://www.php.net/manual/en/function.get-browser.php) fonksiyonu bir süre sonra kötü performans gösteriyor gibi görünüyor. Geçici bir çözüm, statik oldukları için User-Agent başına sonuçları önbelleğe almaktır (örneğin [APCu](https://www.php.net/manual/en/book.apcu.php) ile).
## Binary Çıktısı ve Alpine Tabanlı Docker İmajları
Binary çıktısı ve Alpine tabanlı Docker imajları (dunglas/frankenphp:\*-alpine), daha küçük bir binary boyutu korumak için glibc ve arkadaşları yerine musl libc kullanır. Bu durum bazı uyumluluk sorunlarına yol açabilir. Özellikle, glob seçeneği GLOB_BRACE mevcut değildir.
## Docker ile `https://127.0.0.1` Kullanımı
FrankenPHP varsayılan olarak `localhost` için bir TLS sertifikası oluşturur.
Bu, yerel geliştirme için en kolay ve önerilen seçenektir.
Bunun yerine ana bilgisayar olarak `127.0.0.1` kullanmak istiyorsanız, sunucu adını `127.0.0.1` şeklinde ayarlayarak bunun için bir sertifika oluşturacak yapılandırma yapmak mümkündür.
Ne yazık ki, [ağ sistemi](https://docs.docker.com/network/) nedeniyle Docker kullanırken bu yeterli değildir.
`Curl: (35) LibreSSL/3.3.6: error:1404B438:SSL routines:ST_CONNECT:tlsv1 alert internal error`'a benzer bir TLS hatası alırsınız.
Linux kullanıyorsanız, [ana bilgisayar ağ sürücüsünü](https://docs.docker.com/network/network-tutorial-host/) kullanmak bir çözümdür:
```console
docker run \
-e SERVER_NAME="127.0.0.1" \
-v $PWD:/app/public \
--network host \
dunglas/frankenphp
```
Ana bilgisayar ağ sürücüsü Mac ve Windows'ta desteklenmez. Bu platformlarda, konteynerin IP adresini tahmin etmeniz ve bunu sunucu adlarına dahil etmeniz gerekecektir.
`docker network inspect bridge`'i çalıştırın ve `IPv4Address` anahtarının altındaki son atanmış IP adresini belirlemek için `Containers` anahtarına bakın ve bir artırın. Eğer hiçbir konteyner çalışmıyorsa, ilk atanan IP adresi genellikle `172.17.0.2`dir.
Ardından, bunu `SERVER_NAME` ortam değişkenine ekleyin:
```console
docker run \
-e SERVER_NAME="127.0.0.1, 172.17.0.3" \
-v $PWD:/app/public \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
> [!CAUTION]
>
> 172.17.0.3`ü konteynerinize atanacak IP ile değiştirdiğinizden emin olun.
Artık ana makineden `https://127.0.0.1` adresine erişebilmeniz gerekir.
Eğer durum böyle değilse, sorunu anlamaya çalışmak için FrankenPHP'yi hata ayıklama modunda başlatın:
```console
docker run \
-e CADDY_GLOBAL_OPTIONS="debug" \
-e SERVER_NAME="127.0.0.1" \
-v $PWD:/app/public \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
## `@php` Referanslı Composer Betikler
[Composer betikleri](https://getcomposer.org/doc/articles/scripts.md) bazı görevler için bir PHP binary çalıştırmak isteyebilir, örneğin [bir Laravel projesinde](laravel.md) `@php artisan package:discover --ansi` çalıştırmak. Bu [şu anda mümkün değil](https://github.com/dunglas/frankenphp/issues/483#issuecomment-1899890915) ve 2 nedeni var:
- Composer FrankenPHP binary dosyasını nasıl çağıracağını bilmiyor;
- Composer, FrankenPHP'nin henüz desteklemediği `-d` bayrağını kullanarak PHP ayarlarını komuta ekleyebilir.
Geçici bir çözüm olarak, `/usr/local/bin/php` içinde desteklenmeyen parametreleri silen ve ardından FrankenPHP'yi çağıran bir kabuk betiği oluşturabiliriz:
```bash
#!/bin/bash
args=("$@")
index=0
for i in "$@"
do
if [ "$i" == "-d" ]; then
unset 'args[$index]'
unset 'args[$index+1]'
fi
index=$((index+1))
done
/usr/local/bin/frankenphp php-cli ${args[@]}
```
Ardından `PHP_BINARY` ortam değişkenini PHP betiğimizin yoluna ayarlayın ve Composer bu yolla çalışacaktır:
```bash
export PHP_BINARY=/usr/local/bin/php
composer install
```

View File

@@ -1,74 +1,74 @@
# Laravel
## Docker
Bir [Laravel](https://laravel.com) web uygulamasını FrankenPHP ile çalıştırmak, projeyi resmi Docker imajının `/app` dizinine monte etmek kadar kolaydır.
Bu komutu Laravel uygulamanızın ana dizininden çalıştırın:
```console
docker run -p 80:80 -p 443:443 -p 443:443/udp -v $PWD:/app dunglas/frankenphp
```
And tadını çıkarın!
## Yerel Kurulum
Alternatif olarak, Laravel projelerinizi FrankenPHP ile yerel makinenizden çalıştırabilirsiniz:
1. [Sisteminize karşılık gelen binary dosyayı indirin](https://github.com/dunglas/frankenphp/releases)
2. Aşağıdaki yapılandırmayı Laravel projenizin kök dizinindeki `Caddyfile` adlı bir dosyaya ekleyin:
```caddyfile
{
frankenphp
}
# Sunucunuzun alan adı
localhost {
# Webroot'u public/ dizinine ayarlayın
root * public/
# Sıkıştırmayı etkinleştir (isteğe bağlı)
encode zstd br gzip
# PHP dosyalarını public/ dizininden çalıştırın ve varlıkları sunun
php_server
}
```
3. FrankenPHP'yi Laravel projenizin kök dizininden başlatın: `frankenphp run`
## Laravel Octane
Octane, Composer paket yöneticisi aracılığıyla kurulabilir:
```console
composer require laravel/octane
```
Octane'ı kurduktan sonra, Octane'ın yapılandırma dosyasını uygulamanıza yükleyecek olan `octane:install` Artisan komutunu çalıştırabilirsiniz:
```console
php artisan octane:install --server=frankenphp
```
Octane sunucusu `octane:frankenphp` Artisan komutu aracılığıyla başlatılabilir.
```console
php artisan octane:frankenphp
```
`octane:frankenphp` komutu aşağıdaki seçenekleri alabilir:
* `--host`: Sunucunun bağlanması gereken IP adresi (varsayılan: `127.0.0.1`)
* `--port`: Sunucunun erişilebilir olması gereken port (varsayılan: `8000`)
* `--admin-port`: Yönetici sunucusunun erişilebilir olması gereken port (varsayılan: `2019`)
* `--workers`: İstekleri işlemek için hazır olması gereken worker sayısı (varsayılan: `auto`)
* `--max-requests`: Sunucu yeniden yüklenmeden önce işlenecek istek sayısı (varsayılan: `500`)
* `--caddyfile`: FrankenPHP `Caddyfile` dosyasının yolu
* `--https`: HTTPS, HTTP/2 ve HTTP/3'ü etkinleştirin ve sertifikaları otomatik olarak oluşturup yenileyin
* `--http-redirect`: HTTP'den HTTPS'ye yeniden yönlendirmeyi etkinleştir (yalnızca --https geçilirse etkinleştirilir)
* `--watch`: Uygulamada kod değişikliği olduğunda sunucuyu otomatik olarak yeniden yükle
* `--poll`: Dosyaları bir ağ üzerinden izlemek için izleme sırasında dosya sistemi yoklamasını kullanın
* `--log-level`: Belirtilen günlük seviyesinde veya üzerinde günlük mesajları
Laravel Octane hakkında daha fazla bilgi edinmek için [Laravel Octane resmi belgelerine](https://laravel.com/docs/octane) göz atın.
# Laravel
## Docker
Bir [Laravel](https://laravel.com) web uygulamasını FrankenPHP ile çalıştırmak, projeyi resmi Docker imajının `/app` dizinine monte etmek kadar kolaydır.
Bu komutu Laravel uygulamanızın ana dizininden çalıştırın:
```console
docker run -p 80:80 -p 443:443 -p 443:443/udp -v $PWD:/app dunglas/frankenphp
```
And tadını çıkarın!
## Yerel Kurulum
Alternatif olarak, Laravel projelerinizi FrankenPHP ile yerel makinenizden çalıştırabilirsiniz:
1. [Sisteminize karşılık gelen binary dosyayı indirin](https://github.com/dunglas/frankenphp/releases)
2. Aşağıdaki yapılandırmayı Laravel projenizin kök dizinindeki `Caddyfile` adlı bir dosyaya ekleyin:
```caddyfile
{
frankenphp
}
# Sunucunuzun alan adı
localhost {
# Webroot'u public/ dizinine ayarlayın
root public/
# Sıkıştırmayı etkinleştir (isteğe bağlı)
encode zstd br gzip
# PHP dosyalarını public/ dizininden çalıştırın ve varlıkları sunun
php_server
}
```
3. FrankenPHP'yi Laravel projenizin kök dizininden başlatın: `frankenphp run`
## Laravel Octane
Octane, Composer paket yöneticisi aracılığıyla kurulabilir:
```console
composer require laravel/octane
```
Octane'ı kurduktan sonra, Octane'ın yapılandırma dosyasını uygulamanıza yükleyecek olan `octane:install` Artisan komutunu çalıştırabilirsiniz:
```console
php artisan octane:install --server=frankenphp
```
Octane sunucusu `octane:frankenphp` Artisan komutu aracılığıyla başlatılabilir.
```console
php artisan octane:frankenphp
```
`octane:frankenphp` komutu aşağıdaki seçenekleri alabilir:
- `--host`: Sunucunun bağlanması gereken IP adresi (varsayılan: `127.0.0.1`)
- `--port`: Sunucunun erişilebilir olması gereken port (varsayılan: `8000`)
- `--admin-port`: Yönetici sunucusunun erişilebilir olması gereken port (varsayılan: `2019`)
- `--workers`: İstekleri işlemek için hazır olması gereken worker sayısı (varsayılan: `auto`)
- `--max-requests`: Sunucu yeniden yüklenmeden önce işlenecek istek sayısı (varsayılan: `500`)
- `--caddyfile`: FrankenPHP `Caddyfile` dosyasının yolu
- `--https`: HTTPS, HTTP/2 ve HTTP/3'ü etkinleştirin ve sertifikaları otomatik olarak oluşturup yenileyin
- `--http-redirect`: HTTP'den HTTPS'ye yeniden yönlendirmeyi etkinleştir (yalnızca --https geçilirse etkinleştirilir)
- `--watch`: Uygulamada kod değişikliği olduğunda sunucuyu otomatik olarak yeniden yükle
- `--poll`: Dosyaları bir ağ üzerinden izlemek için izleme sırasında dosya sistemi yoklamasını kullanın
- `--log-level`: Belirtilen günlük seviyesinde veya üzerinde günlük mesajları
Laravel Octane hakkında daha fazla bilgi edinmek için [Laravel Octane resmi belgelerine](https://laravel.com/docs/octane) göz atın.

View File

@@ -1,12 +1,12 @@
# Gerçek Zamanlı
FrankenPHP yerleşik bir [Mercure](https://mercure.rocks) hub ile birlikte gelir!
Mercure, olayları tüm bağlı cihazlara gerçek zamanlı olarak göndermeye olanak tanır: anında bir JavaScript olayı alırlar.
JS kütüphanesi veya SDK gerekmez!
![Mercure](../mercure-hub.png)
Mercure hub'ını etkinleştirmek için [Mercure'ün sitesinde](https://mercure.rocks/docs/hub/config) açıklandığı gibi `Caddyfile`'ı güncelleyin.
Mercure güncellemelerini kodunuzdan göndermek için [Symfony Mercure Bileşenini](https://symfony.com/components/Mercure) öneririz (kullanmak için Symfony tam yığın çerçevesine ihtiyacınız yoktur).
# Gerçek Zamanlı
FrankenPHP yerleşik bir [Mercure](https://mercure.rocks) hub ile birlikte gelir!
Mercure, olayları tüm bağlı cihazlara gerçek zamanlı olarak göndermeye olanak tanır: anında bir JavaScript olayı alırlar.
JS kütüphanesi veya SDK gerekmez!
![Mercure](../mercure-hub.png)
Mercure hub'ını etkinleştirmek için [Mercure'ün sitesinde](https://mercure.rocks/docs/hub/config) açıklandığı gibi `Caddyfile`'ı güncelleyin.
Mercure güncellemelerini kodunuzdan göndermek için [Symfony Mercure Bileşenini](https://symfony.com/components/Mercure) öneririz (kullanmak için Symfony tam yığın çerçevesine ihtiyacınız yoktur).

View File

@@ -1,139 +1,139 @@
# Production Ortamına Dağıtım
Bu dokümanda, Docker Compose kullanarak bir PHP uygulamasını tek bir sunucuya nasıl dağıtacağımızı öğreneceğiz.
Symfony kullanıyorsanız, Symfony Docker projesinin (FrankenPHP kullanan) "[Production ortamına dağıtım](https://github.com/dunglas/symfony-docker/blob/main/docs/production.md)" dokümanını okumayı tercih edebilirsiniz.
API Platform (FrankenPHP de kullanır) tercih ediyorsanız, [çerçevenin dağıtım dokümanına](https://api-platform.com/docs/deployment/) bakabilirsiniz.
## Uygulamanızı Hazırlama
İlk olarak, PHP projenizin kök dizininde bir `Dockerfile` oluşturun:
```dockerfile
FROM dunglas/frankenphp
# "your-domain-name.example.com" yerine kendi alan adınızı yazdığınızdan emin olun
ENV SERVER_NAME=your-domain-name.example.com
# HTTPS'yi devre dışı bırakmak istiyorsanız, bunun yerine bu değeri kullanın:
#ENV SERVER_NAME=:80
# PHP production ayarlarını etkinleştirin
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
# Projenizin PHP dosyalarını genel dizine kopyalayın
COPY . /app/public
# Symfony veya Laravel kullanıyorsanız, bunun yerine tüm projeyi kopyalamanız gerekir:
#COPY . /app
```
Daha fazla ayrıntı ve seçenek için "[Özel Docker İmajı Oluşturma](docker.md)" bölümüne bakın,
ve yapılandırmayı nasıl özelleştireceğinizi öğrenmek için PHP eklentilerini ve Caddy modüllerini yükleyin.
Projeniz Composer kullanıyorsa,
Docker imajına dahil ettiğinizden ve bağımlılıklarınızı yüklediğinizden emin olun.
Ardından, bir `compose.yaml` dosyası ekleyin:
```yaml
services:
php:
image: dunglas/frankenphp
restart: always
ports:
- "80:80" # HTTP
- "443:443" # HTTPS
- "443:443/udp" # HTTP/3
volumes:
- caddy_data:/data
- caddy_config:/config
# Caddy sertifikaları ve yapılandırması için gereken yığınlar (volumes)
volumes:
caddy_data:
caddy_config:
```
> [!NOTE]
>
> Önceki örnekler production kullanımı için tasarlanmıştır.
> Geliştirme aşamasında, bir yığın (volume), farklı bir PHP yapılandırması ve `SERVER_NAME` ortam değişkeni için farklı bir değer kullanmak isteyebilirsiniz.
>
> (FrankenPHP kullanan) çok aşamalı Composer, ekstra PHP eklentileri vb. içeren imajlara başvuran daha gelişmiş bir örnek için [Symfony Docker](https://github.com/dunglas/symfony-docker) projesine bir göz atın.
Son olarak, eğer Git kullanıyorsanız, bu dosyaları commit edin ve push edin.
## Sunucu Hazırlama
Uygulamanızı production ortamına dağıtmak için bir sunucuya ihtiyacınız vardır.
Bu dokümanda, DigitalOcean tarafından sağlanan bir sanal makine kullanacağız, ancak herhangi bir Linux sunucusu çalışabilir.
Docker yüklü bir Linux sunucunuz varsa, doğrudan [bir sonraki bölüme](#alan-adı-yapılandırma) geçebilirsiniz.
Aksi takdirde, 200 $ ücretsiz kredi almak için [bu ortaklık bağlantısını](https://m.do.co/c/5d8aabe3ab80) kullanın, bir hesap oluşturun ve ardından "Create a Droplet" seçeneğine tıklayın.
Ardından, "Bir imaj seçin" bölümünün altındaki "Marketplace" sekmesine tıklayın ve "Docker" adlı uygulamayı bulun.
Bu, Docker ve Docker Compose'un en son sürümlerinin zaten yüklü olduğu bir Ubuntu sunucusu sağlayacaktır!
Test amaçlı kullanım için en ucuz planlar yeterli olacaktır.
Gerçek production kullanımı için, muhtemelen ihtiyaçlarınıza uyacak şekilde "genel amaçlı" bölümünden bir plan seçmek isteyeceksiniz.
![Docker ile DigitalOcean FrankenPHP](../digitalocean-droplet.png)
Diğer ayarlar için varsayılanları koruyabilir veya ihtiyaçlarınıza göre değiştirebilirsiniz.
SSH anahtarınızı eklemeyi veya bir parola oluşturmayı unutmayın, ardından "Sonlandır ve oluştur" düğmesine basın.
Ardından, Droplet'iniz hazırlanırken birkaç saniye bekleyin.
Droplet'iniz hazır olduğunda, bağlanmak için SSH kullanın:
```console
ssh root@<droplet-ip>
```
## Alan Adı Yapılandırma
Çoğu durumda sitenizle bir alan adını ilişkilendirmek isteyeceksiniz.
Henüz bir alan adınız yoksa, bir kayıt şirketi aracılığıyla bir alan adı satın almanız gerekir.
Daha sonra alan adınız için sunucunuzun IP adresini işaret eden `A` türünde bir DNS kaydı oluşturun:
```dns
your-domain-name.example.com. IN A 207.154.233.113
```
DigitalOcean Alan Adları hizmetiyle ilgili örnek ("Networking" > "Domains"):
![DigitalOcean'da DNS Yapılandırma](../digitalocean-dns.png)
> [!NOTE]
>
> FrankenPHP tarafından varsayılan olarak otomatik olarak TLS sertifikası oluşturmak için kullanılan hizmet olan Let's Encrypt, direkt IP adreslerinin kullanılmasını desteklemez. Let's Encrypt'i kullanmak için alan adı kullanmak zorunludur.
## Dağıtım
Projenizi `git clone`, `scp` veya ihtiyacınıza uygun başka bir araç kullanarak sunucuya kopyalayın.
GitHub kullanıyorsanız [bir dağıtım anahtarı](https://docs.github.com/en/free-pro-team@latest/developers/overview/managing-deploy-keys#deploy-keys) kullanmak isteyebilirsiniz.
Dağıtım anahtarları ayrıca [GitLab tarafından desteklenir](https://docs.gitlab.com/ee/user/project/deploy_keys/).
Git ile örnek:
```console
git clone git@github.com:<username>/<project-name>.git
```
Projenizi içeren dizine gidin (`<proje-adı>`) ve uygulamayı production modunda başlatın:
```console
docker compose up -d --wait
```
Sunucunuz hazır ve çalışıyor. Sizin için otomatik olarak bir HTTPS sertifikası oluşturuldu.
`https://your-domain-name.example.com` adresine gidin ve keyfini çıkarın!
> [!CAUTION]
>
> Docker bir önbellek katmanına sahip olabilir, her dağıtım için doğru derlemeye sahip olduğunuzdan emin olun veya önbellek sorununu önlemek için projenizi `--no-cache` seçeneği ile yeniden oluşturun.
## Birden Fazla Düğümde Dağıtım
Uygulamanızı bir makine kümesine dağıtmak istiyorsanız, [Docker Swarm](https://docs.docker.com/engine/swarm/stack-deploy/) kullanabilirsiniz,
sağlanan Compose dosyaları ile uyumludur.
Kubernetes üzerinde dağıtım yapmak için FrankenPHP kullanan [API Platformu ile sağlanan Helm grafiğine](https://api-platform.com/docs/deployment/kubernetes/) göz atın.
# Production Ortamına Dağıtım
Bu dokümanda, Docker Compose kullanarak bir PHP uygulamasını tek bir sunucuya nasıl dağıtacağımızı öğreneceğiz.
Symfony kullanıyorsanız, Symfony Docker projesinin (FrankenPHP kullanan) "[Production ortamına dağıtım](https://github.com/dunglas/symfony-docker/blob/main/docs/production.md)" dokümanını okumayı tercih edebilirsiniz.
API Platform (FrankenPHP de kullanır) tercih ediyorsanız, [çerçevenin dağıtım dokümanına](https://api-platform.com/docs/deployment/) bakabilirsiniz.
## Uygulamanızı Hazırlama
İlk olarak, PHP projenizin kök dizininde bir `Dockerfile` oluşturun:
```dockerfile
FROM dunglas/frankenphp
# "your-domain-name.example.com" yerine kendi alan adınızı yazdığınızdan emin olun
ENV SERVER_NAME=your-domain-name.example.com
# HTTPS'yi devre dışı bırakmak istiyorsanız, bunun yerine bu değeri kullanın:
#ENV SERVER_NAME=:80
# PHP production ayarlarını etkinleştirin
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
# Projenizin PHP dosyalarını genel dizine kopyalayın
COPY . /app/public
# Symfony veya Laravel kullanıyorsanız, bunun yerine tüm projeyi kopyalamanız gerekir:
#COPY . /app
```
Daha fazla ayrıntı ve seçenek için "[Özel Docker İmajı Oluşturma](docker.md)" bölümüne bakın,
ve yapılandırmayı nasıl özelleştireceğinizi öğrenmek için PHP eklentilerini ve Caddy modüllerini yükleyin.
Projeniz Composer kullanıyorsa,
Docker imajına dahil ettiğinizden ve bağımlılıklarınızı yüklediğinizden emin olun.
Ardından, bir `compose.yaml` dosyası ekleyin:
```yaml
services:
php:
image: dunglas/frankenphp
restart: always
ports:
- "80:80" # HTTP
- "443:443" # HTTPS
- "443:443/udp" # HTTP/3
volumes:
- caddy_data:/data
- caddy_config:/config
# Caddy sertifikaları ve yapılandırması için gereken yığınlar (volumes)
volumes:
caddy_data:
caddy_config:
```
> [!NOTE]
>
> Önceki örnekler production kullanımı için tasarlanmıştır.
> Geliştirme aşamasında, bir yığın (volume), farklı bir PHP yapılandırması ve `SERVER_NAME` ortam değişkeni için farklı bir değer kullanmak isteyebilirsiniz.
>
> (FrankenPHP kullanan) çok aşamalı Composer, ekstra PHP eklentileri vb. içeren imajlara başvuran daha gelişmiş bir örnek için [Symfony Docker](https://github.com/dunglas/symfony-docker) projesine bir göz atın.
Son olarak, eğer Git kullanıyorsanız, bu dosyaları commit edin ve push edin.
## Sunucu Hazırlama
Uygulamanızı production ortamına dağıtmak için bir sunucuya ihtiyacınız vardır.
Bu dokümanda, DigitalOcean tarafından sağlanan bir sanal makine kullanacağız, ancak herhangi bir Linux sunucusu çalışabilir.
Docker yüklü bir Linux sunucunuz varsa, doğrudan [bir sonraki bölüme](#alan-adı-yapılandırma) geçebilirsiniz.
Aksi takdirde, 200 $ ücretsiz kredi almak için [bu ortaklık bağlantısını](https://m.do.co/c/5d8aabe3ab80) kullanın, bir hesap oluşturun ve ardından "Create a Droplet" seçeneğine tıklayın.
Ardından, "Bir imaj seçin" bölümünün altındaki "Marketplace" sekmesine tıklayın ve "Docker" adlı uygulamayı bulun.
Bu, Docker ve Docker Compose'un en son sürümlerinin zaten yüklü olduğu bir Ubuntu sunucusu sağlayacaktır!
Test amaçlı kullanım için en ucuz planlar yeterli olacaktır.
Gerçek production kullanımı için, muhtemelen ihtiyaçlarınıza uyacak şekilde "genel amaçlı" bölümünden bir plan seçmek isteyeceksiniz.
![Docker ile DigitalOcean FrankenPHP](../digitalocean-droplet.png)
Diğer ayarlar için varsayılanları koruyabilir veya ihtiyaçlarınıza göre değiştirebilirsiniz.
SSH anahtarınızı eklemeyi veya bir parola oluşturmayı unutmayın, ardından "Sonlandır ve oluştur" düğmesine basın.
Ardından, Droplet'iniz hazırlanırken birkaç saniye bekleyin.
Droplet'iniz hazır olduğunda, bağlanmak için SSH kullanın:
```console
ssh root@<droplet-ip>
```
## Alan Adı Yapılandırma
Çoğu durumda sitenizle bir alan adını ilişkilendirmek isteyeceksiniz.
Henüz bir alan adınız yoksa, bir kayıt şirketi aracılığıyla bir alan adı satın almanız gerekir.
Daha sonra alan adınız için sunucunuzun IP adresini işaret eden `A` türünde bir DNS kaydı oluşturun:
```dns
your-domain-name.example.com. IN A 207.154.233.113
```
DigitalOcean Alan Adları hizmetiyle ilgili örnek ("Networking" > "Domains"):
![DigitalOcean'da DNS Yapılandırma](../digitalocean-dns.png)
> [!NOTE]
>
> FrankenPHP tarafından varsayılan olarak otomatik olarak TLS sertifikası oluşturmak için kullanılan hizmet olan Let's Encrypt, direkt IP adreslerinin kullanılmasını desteklemez. Let's Encrypt'i kullanmak için alan adı kullanmak zorunludur.
## Dağıtım
Projenizi `git clone`, `scp` veya ihtiyacınıza uygun başka bir araç kullanarak sunucuya kopyalayın.
GitHub kullanıyorsanız [bir dağıtım anahtarı](https://docs.github.com/en/free-pro-team@latest/developers/overview/managing-deploy-keys#deploy-keys) kullanmak isteyebilirsiniz.
Dağıtım anahtarları ayrıca [GitLab tarafından desteklenir](https://docs.gitlab.com/ee/user/project/deploy_keys/).
Git ile örnek:
```console
git clone git@github.com:<username>/<project-name>.git
```
Projenizi içeren dizine gidin (`<proje-adı>`) ve uygulamayı production modunda başlatın:
```console
docker compose up -d --wait
```
Sunucunuz hazır ve çalışıyor. Sizin için otomatik olarak bir HTTPS sertifikası oluşturuldu.
`https://your-domain-name.example.com` adresine gidin ve keyfini çıkarın!
> [!CAUTION]
>
> Docker bir önbellek katmanına sahip olabilir, her dağıtım için doğru derlemeye sahip olduğunuzdan emin olun veya önbellek sorununu önlemek için projenizi `--no-cache` seçeneği ile yeniden oluşturun.
## Birden Fazla Düğümde Dağıtım
Uygulamanızı bir makine kümesine dağıtmak istiyorsanız, [Docker Swarm](https://docs.docker.com/engine/swarm/stack-deploy/) kullanabilirsiniz,
sağlanan Compose dosyaları ile uyumludur.
Kubernetes üzerinde dağıtım yapmak için FrankenPHP kullanan [API Platformu ile sağlanan Helm grafiğine](https://api-platform.com/docs/deployment/kubernetes/) göz atın.

View File

@@ -1,100 +1,100 @@
# Statik Yapı Oluşturun
PHP kütüphanesinin yerel kurulumunu kullanmak yerine,
harika [static-php-cli projesi](https://github.com/crazywhalecc/static-php-cli) sayesinde FrankenPHP'nin statik bir yapısını oluşturmak mümkündür (adına rağmen, bu proje sadece CLI'yi değil, tüm SAPI'leri destekler).
Bu yöntemle, tek, taşınabilir bir ikili PHP yorumlayıcısını, Caddy web sunucusunu ve FrankenPHP'yi içerecektir!
FrankenPHP ayrıca [PHP uygulamasının statik binary gömülmesini](embed.md) destekler.
## Linux
Linux statik binary dosyası oluşturmak için bir Docker imajı sağlıyoruz:
```console
docker buildx bake --load static-builder
docker cp $(docker create --name static-builder dunglas/frankenphp:static-builder):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp ; docker rm static-builder
```
Elde edilen statik binary `frankenphp` olarak adlandırılır ve geçerli dizinde kullanılabilir.
Statik binary dosyasını Docker olmadan oluşturmak istiyorsanız, Linux için de çalışan macOS talimatlarına bir göz atın.
### Özel Eklentiler
Varsayılan olarak, en popüler PHP eklentileri zaten derlenir.
Binary dosyanın boyutunu küçültmek ve saldırı yüzeyini azaltmak için `PHP_EXTENSIONS` Docker ARG'sini kullanarak derlenecek eklentilerin listesini seçebilirsiniz.
Örneğin, yalnızca `opcache` eklentisini derlemek için aşağıdaki komutu çalıştırın:
```console
docker buildx bake --load --set static-builder.args.PHP_EXTENSIONS=opcache,pdo_sqlite static-builder
# ...
```
Etkinleştirdiğiniz eklentilere ek işlevler sağlayan kütüphaneler eklemek için `PHP_EXTENSION_LIBS` Docker ARG'sini kullanabilirsiniz:
```console
docker buildx bake \
--load \
--set static-builder.args.PHP_EXTENSIONS=gd \
--set static-builder.args.PHP_EXTENSION_LIBS=libjpeg,libwebp \
static-builder
```
### Ekstra Caddy Modülleri
Ekstra Caddy modülleri eklemek veya [xcaddy](https://github.com/caddyserver/xcaddy) adresine başka argümanlar iletmek için `XCADDY_ARGS` Docker ARG'sini kullanın:
```console
docker buildx bake \
--load \
--set static-builder.args.XCADDY_ARGS="--with github.com/darkweak/souin/plugins/caddy --with github.com/dunglas/caddy-cbrotli --with github.com/dunglas/mercure/caddy --with github.com/dunglas/vulcain/caddy" \
static-builder
```
Bu örnekte, Caddy için [Souin](https://souin.io) HTTP önbellek modülünün yanı sıra [cbrotli](https://github.com/dunglas/caddy-cbrotli), [Mercure](https://mercure.rocks) ve [Vulcain](https://vulcain.rocks) modüllerini ekliyoruz.
> [!TIP]
>
> cbrotli, Mercure ve Vulcain modülleri, `XCADDY_ARGS` boşsa veya ayarlanmamışsa varsayılan olarak dahil edilir.
> Eğer `XCADDY_ARGS` değerini özelleştirirseniz, dahil edilmelerini istiyorsanız bunlarııkça dahil etmelisiniz.
Derlemeyi nasıl [özelleştireceğinize](#yapıyı-özelleştirme) de bakın.
### GitHub Token
GitHub API kullanım limitine ulaşırsanız, `GITHUB_TOKEN` adlı bir ortam değişkeninde bir GitHub Personal Access Token ayarlayın:
```console
GITHUB_TOKEN="xxx" docker --load buildx bake static-builder
# ...
```
## macOS
macOS için statik bir binary oluşturmak için aşağıdaki betiği çalıştırın ([Homebrew](https://brew.sh/) yüklü olmalıdır):
```console
git clone https://github.com/dunglas/frankenphp
cd frankenphp
./build-static.sh
```
Not: Bu betik Linux'ta (ve muhtemelen diğer Unix'lerde) da çalışır ve sağladığımız Docker tabanlı statik derleyici tarafından dahili olarak kullanılır.
## Yapıyı Özelleştirme
Aşağıdaki ortam değişkenleri `docker build` ve `build-static.sh` dosyalarına aktarılabilir
statik derlemeyi özelleştirmek için betik:
* `FRANKENPHP_VERSION`: kullanılacak FrankenPHP sürümü
* `PHP_VERSION`: kullanılacak PHP sürümü
* `PHP_EXTENSIONS`: oluşturulacak PHP eklentileri ([desteklenen eklentiler listesi](https://static-php.dev/en/guide/extensions.html))
* `PHP_EXTENSION_LIBS`: eklentilere özellikler ekleyen oluşturulacak ekstra kütüphaneler
* `XCADDY_ARGS`: [xcaddy](https://github.com/caddyserver/xcaddy) adresine iletilecek argümanlar, örneğin ekstra Caddy modülleri eklemek için
* `EMBED`: binary dosyaya gömülecek PHP uygulamasının yolu
* `CLEAN`: ayarlandığında, libphp ve tüm bağımlılıkları sıfırdan oluşturulur (önbellek yok)
* `DEBUG_SYMBOLS`: ayarlandığında, hata ayıklama sembolleri ayıklanmayacak ve binary dosyaya eklenecektir
* `RELEASE`: (yalnızca bakımcılar) ayarlandığında, ortaya çıkan binary dosya GitHub'a yüklenecektir
# Statik Yapı Oluşturun
PHP kütüphanesinin yerel kurulumunu kullanmak yerine,
harika [static-php-cli projesi](https://github.com/crazywhalecc/static-php-cli) sayesinde FrankenPHP'nin statik bir yapısını oluşturmak mümkündür (adına rağmen, bu proje sadece CLI'yi değil, tüm SAPI'leri destekler).
Bu yöntemle, tek, taşınabilir bir ikili PHP yorumlayıcısını, Caddy web sunucusunu ve FrankenPHP'yi içerecektir!
FrankenPHP ayrıca [PHP uygulamasının statik binary gömülmesini](embed.md) destekler.
## Linux
Linux statik binary dosyası oluşturmak için bir Docker imajı sağlıyoruz:
```console
docker buildx bake --load static-builder
docker cp $(docker create --name static-builder-musl dunglas/frankenphp:static-builder-musl):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp ; docker rm static-builder
```
Elde edilen statik binary `frankenphp` olarak adlandırılır ve geçerli dizinde kullanılabilir.
Statik binary dosyasını Docker olmadan oluşturmak istiyorsanız, Linux için de çalışan macOS talimatlarına bir göz atın.
### Özel Eklentiler
Varsayılan olarak, en popüler PHP eklentileri zaten derlenir.
Binary dosyanın boyutunu küçültmek ve saldırı yüzeyini azaltmak için `PHP_EXTENSIONS` Docker ARG'sini kullanarak derlenecek eklentilerin listesini seçebilirsiniz.
Örneğin, yalnızca `opcache` eklentisini derlemek için aşağıdaki komutu çalıştırın:
```console
docker buildx bake --load --set static-builder.args.PHP_EXTENSIONS=opcache,pdo_sqlite static-builder
# ...
```
Etkinleştirdiğiniz eklentilere ek işlevler sağlayan kütüphaneler eklemek için `PHP_EXTENSION_LIBS` Docker ARG'sini kullanabilirsiniz:
```console
docker buildx bake \
--load \
--set static-builder.args.PHP_EXTENSIONS=gd \
--set static-builder.args.PHP_EXTENSION_LIBS=libjpeg,libwebp \
static-builder
```
### Ekstra Caddy Modülleri
Ekstra Caddy modülleri eklemek veya [xcaddy](https://github.com/caddyserver/xcaddy) adresine başka argümanlar iletmek için `XCADDY_ARGS` Docker ARG'sini kullanın:
```console
docker buildx bake \
--load \
--set static-builder.args.XCADDY_ARGS="--with github.com/darkweak/souin/plugins/caddy --with github.com/dunglas/caddy-cbrotli --with github.com/dunglas/mercure/caddy --with github.com/dunglas/vulcain/caddy" \
static-builder
```
Bu örnekte, Caddy için [Souin](https://souin.io) HTTP önbellek modülünün yanı sıra [cbrotli](https://github.com/dunglas/caddy-cbrotli), [Mercure](https://mercure.rocks) ve [Vulcain](https://vulcain.rocks) modüllerini ekliyoruz.
> [!TIP]
>
> cbrotli, Mercure ve Vulcain modülleri, `XCADDY_ARGS` boşsa veya ayarlanmamışsa varsayılan olarak dahil edilir.
> Eğer `XCADDY_ARGS` değerini özelleştirirseniz, dahil edilmelerini istiyorsanız bunlarııkça dahil etmelisiniz.
Derlemeyi nasıl [özelleştireceğinize](#yapıyı-özelleştirme) de bakın.
### GitHub Token
GitHub API kullanım limitine ulaşırsanız, `GITHUB_TOKEN` adlı bir ortam değişkeninde bir GitHub Personal Access Token ayarlayın:
```console
GITHUB_TOKEN="xxx" docker --load buildx bake static-builder
# ...
```
## macOS
macOS için statik bir binary oluşturmak için aşağıdaki betiği çalıştırın ([Homebrew](https://brew.sh/) yüklü olmalıdır):
```console
git clone https://github.com/dunglas/frankenphp
cd frankenphp
./build-static.sh
```
Not: Bu betik Linux'ta (ve muhtemelen diğer Unix'lerde) da çalışır ve sağladığımız Docker tabanlı statik derleyici tarafından dahili olarak kullanılır.
## Yapıyı Özelleştirme
Aşağıdaki ortam değişkenleri `docker build` ve `build-static.sh` dosyalarına aktarılabilir
statik derlemeyi özelleştirmek için betik:
- `FRANKENPHP_VERSION`: kullanılacak FrankenPHP sürümü
- `PHP_VERSION`: kullanılacak PHP sürümü
- `PHP_EXTENSIONS`: oluşturulacak PHP eklentileri ([desteklenen eklentiler listesi](https://static-php.dev/en/guide/extensions.html))
- `PHP_EXTENSION_LIBS`: eklentilere özellikler ekleyen oluşturulacak ekstra kütüphaneler
- `XCADDY_ARGS`: [xcaddy](https://github.com/caddyserver/xcaddy) adresine iletilecek argümanlar, örneğin ekstra Caddy modülleri eklemek için
- `EMBED`: binary dosyaya gömülecek PHP uygulamasının yolu
- `CLEAN`: ayarlandığında, libphp ve tüm bağımlılıkları sıfırdan oluşturulur (önbellek yok)
- `DEBUG_SYMBOLS`: ayarlandığında, hata ayıklama sembolleri ayıklanmayacak ve binary dosyaya eklenecektir
- `RELEASE`: (yalnızca bakımcılar) ayarlandığında, ortaya çıkan binary dosya GitHub'a yüklenecektir

View File

@@ -1,121 +1,124 @@
# FrankenPHP Worker'ları Kullanma
Uygulamanızı bir kez önyükleyin ve bellekte tutun.
FrankenPHP gelen istekleri birkaç milisaniye içinde halledecektir.
## Çalışan Komut Dosyalarının Başlatılması
### Docker
`FRANKENPHP_CONFIG` ortam değişkeninin değerini `worker /path/to/your/worker/script.php` olarak ayarlayın:
```console
docker run \
-e FRANKENPHP_CONFIG="worker /app/path/to/your/worker/script.php" \
-v $PWD:/app \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
### Binary Çıktısı
Geçerli dizinin içeriğini bir worker kullanarak sunmak için `php-server` komutunun `--worker` seçeneğini kullanın:
```console
frankenphp php-server --worker /path/to/your/worker/script.php
```
PHP uygulamanız [binary dosyaya gömülü](embed.md) ise, uygulamanın kök dizinine özel bir `Caddyfile` ekleyebilirsiniz.
Otomatik olarak kullanılacaktır.
## Symfony Çalışma Zamanı
FrankenPHP'nin worker modu [Symfony Runtime Component](https://symfony.com/doc/current/components/runtime.html) tarafından desteklenmektedir.
Herhangi bir Symfony uygulamasını bir worker'da başlatmak için [PHP Runtime](https://github.com/php-runtime/runtime)'ın FrankenPHP paketini yükleyin:
```console
composer require runtime/frankenphp-symfony
```
FrankenPHP Symfony Runtime'ı kullanmak için `APP_RUNTIME` ortam değişkenini tanımlayarak uygulama sunucunuzu başlatın:
```console
docker run \
-e FRANKENPHP_CONFIG="worker ./public/index.php" \
-e APP_RUNTIME=Runtime\\FrankenPhpSymfony\\Runtime \
-v $PWD:/app \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
## Laravel Octane
Bkz. [ilgili doküman](laravel.md#laravel-octane).
## Özel Uygulamalar
Aşağıdaki örnek, üçüncü taraf bir kütüphaneye güvenmeden kendi çalışan kodunuzu nasıl oluşturacağınızı göstermektedir:
```php
<?php
// public/index.php
// Bir istemci bağlantısı kesildiğinde alt komut dosyasının sonlandırılmasını önleyin
ignore_user_abort(true);
// Uygulamanızı önyükleyin
require __DIR__.'/vendor/autoload.php';
$myApp = new \App\Kernel();
$myApp->boot();
// Daha iyi performans için döngü dışında işleyici (daha az iş yapıyor)
$handler = static function () use ($myApp) {
// Bir istek alındığında çağrılır,
// superglobals, php://input ve benzerleri sıfırlanır
echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER);
};
for ($nbRequests = 0, $running = true; isset($_SERVER['MAX_REQUESTS']) && ($nbRequests < ((int)$_SERVER['MAX_REQUESTS'])) && $running; ++$nbRequests) {
$running = \frankenphp_handle_request($handler);
// HTTP yanıtını gönderdikten sonra bir şey yapın
$myApp->terminate();
// Bir sayfa oluşturmanın ortasında tetiklenme olasılığını azaltmak için çöp toplayıcıyı çağırın
gc_collect_cycles();
}
// Temizleme
$myApp->shutdown();
```
Ardından, uygulamanızı başlatın ve çalışanınızı yapılandırmak için `FRANKENPHP_CONFIG` ortam değişkenini kullanın:
```console
docker run \
-e FRANKENPHP_CONFIG="worker ./public/index.php" \
-v $PWD:/app \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
Varsayılan olarak, CPU başına 2 worker başlatılır.
Başlatılacak worker sayısını da yapılandırabilirsiniz:
```console
docker run \
-e FRANKENPHP_CONFIG="worker ./public/index.php 42" \
-v $PWD:/app \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
### Belirli Sayıda İstekten Sonra Worker'ı Yeniden Başlatın
<!-- textlint-disable -->
PHP başlangıçta uzun süreli işlemler için tasarlanmadığından, hala bellek sızdıran birçok kütüphane ve eski kod vardır.
<!-- textlint-enable -->
Bu tür kodları worker modunda kullanmak için geçici bir çözüm, belirli sayıda isteği işledikten sonra worker betiğini yeniden başlatmaktır:
Önceki worker kod parçacığı, `MAX_REQUESTS` adlı bir ortam değişkeni ayarlayarak işlenecek maksimum istek sayısını yapılandırmaya izin verir.
# FrankenPHP Worker'ları Kullanma
Uygulamanızı bir kez önyükleyin ve bellekte tutun.
FrankenPHP gelen istekleri birkaç milisaniye içinde halledecektir.
## Çalışan Komut Dosyalarının Başlatılması
### Docker
`FRANKENPHP_CONFIG` ortam değişkeninin değerini `worker /path/to/your/worker/script.php` olarak ayarlayın:
```console
docker run \
-e FRANKENPHP_CONFIG="worker /app/path/to/your/worker/script.php" \
-v $PWD:/app \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
### Binary Çıktısı
Geçerli dizinin içeriğini bir worker kullanarak sunmak için `php-server` komutunun `--worker` seçeneğini kullanın:
```console
frankenphp php-server --worker /path/to/your/worker/script.php
```
PHP uygulamanız [binary dosyaya gömülü](embed.md) ise, uygulamanın kök dizinine özel bir `Caddyfile` ekleyebilirsiniz.
Otomatik olarak kullanılacaktır.
## Symfony Çalışma Zamanı
FrankenPHP'nin worker modu [Symfony Runtime Component](https://symfony.com/doc/current/components/runtime.html) tarafından desteklenmektedir.
Herhangi bir Symfony uygulamasını bir worker'da başlatmak için [PHP Runtime](https://github.com/php-runtime/runtime)'ın FrankenPHP paketini yükleyin:
```console
composer require runtime/frankenphp-symfony
```
FrankenPHP Symfony Runtime'ı kullanmak için `APP_RUNTIME` ortam değişkenini tanımlayarak uygulama sunucunuzu başlatın:
```console
docker run \
-e FRANKENPHP_CONFIG="worker ./public/index.php" \
-e APP_RUNTIME=Runtime\\FrankenPhpSymfony\\Runtime \
-v $PWD:/app \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
## Laravel Octane
Bkz. [ilgili doküman](laravel.md#laravel-octane).
## Özel Uygulamalar
Aşağıdaki örnek, üçüncü taraf bir kütüphaneye güvenmeden kendi çalışan kodunuzu nasıl oluşturacağınızı göstermektedir:
```php
<?php
// public/index.php
// Bir istemci bağlantısı kesildiğinde alt komut dosyasının sonlandırılmasını önleyin
ignore_user_abort(true);
// Uygulamanızı önyükleyin
require __DIR__.'/vendor/autoload.php';
$myApp = new \App\Kernel();
$myApp->boot();
// Daha iyi performans için döngü dışında işleyici (daha az iş yapıyor)
$handler = static function () use ($myApp) {
// Bir istek alındığında çağrılır,
// superglobals, php://input ve benzerleri sıfırlanır
echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER);
};
for ($nbRequests = 0, $running = true; isset($_SERVER['MAX_REQUESTS']) && ($nbRequests < ((int)$_SERVER['MAX_REQUESTS'])) && $running; ++$nbRequests) {
$running = \frankenphp_handle_request($handler);
// HTTP yanıtını gönderdikten sonra bir şey yapın
$myApp->terminate();
// Bir sayfa oluşturmanın ortasında tetiklenme olasılığını azaltmak için çöp toplayıcıyı çağırın
gc_collect_cycles();
}
// Temizleme
$myApp->shutdown();
```
Ardından, uygulamanızı başlatın ve çalışanınızı yapılandırmak için `FRANKENPHP_CONFIG` ortam değişkenini kullanın:
```console
docker run \
-e FRANKENPHP_CONFIG="worker ./public/index.php" \
-v $PWD:/app \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
Varsayılan olarak, CPU başına 2 worker başlatılır.
Başlatılacak worker sayısını da yapılandırabilirsiniz:
```console
docker run \
-e FRANKENPHP_CONFIG="worker ./public/index.php 42" \
-v $PWD:/app \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
### Belirli Sayıda İstekten Sonra Worker'ı Yeniden Başlatın
<!-- textlint-disable -->
PHP başlangıçta uzun süreli işlemler için tasarlanmadığından, hala bellek sızdıran birçok kütüphane ve eski kod vardır.
<!-- textlint-enable -->
Bu tür kodları worker modunda kullanmak için geçici bir çözüm, belirli sayıda isteği işledikten sonra worker betiğini yeniden başlatmaktır:
Önceki worker kod parçacığı, `MAX_REQUESTS` adlı bir ortam değişkeni ayarlayarak işlenecek maksimum istek sayısını yapılandırmaya izin verir.

View File

@@ -32,7 +32,7 @@ It's also possible to [restart the worker on file changes](config.md#watching-fo
The following command will trigger a restart if any file ending in `.php` in the `/path/to/your/app/` directory or subdirectories is modified:
```console
frankenphp php-server --worker /path/to/your/worker/script.php --watch "/path/to/your/app/**/*.php"
frankenphp php-server --worker /path/to/your/worker/script.php --watch="/path/to/your/app/**/*.php"
```
## Symfony Runtime
@@ -128,10 +128,20 @@ A workaround to using this type of code in worker mode is to restart the worker
The previous worker snippet allows configuring a maximum number of request to handle by setting an environment variable named `MAX_REQUESTS`.
### Restart Workers Manually
While it's possible to restart workers [on file changes](config.md#watching-for-file-changes), it's also possible to restart all workers
gracefully via the [Caddy admin API](https://caddyserver.com/docs/api). If the admin is enabled in your
[Caddyfile](config.md#caddyfile-config), you can ping the restart endpoint with a simple POST request like this:
```console
curl -X POST http://localhost:2019/frankenphp/workers/restart
```
### Worker Failures
If a worker script crashes with a non-zero exit code, FrankenPHP will restart it with an exponential backoff strategy.
If the worker script stays up longer than the last backoff * 2,
If the worker script stays up longer than the last backoff \* 2,
it will not penalize the worker script and restart it again.
However, if the worker script continues to fail with a non-zero exit code in a short period of time
(for example, having a typo in a script), FrankenPHP will crash with the error: `too many consecutive failures`.
@@ -141,8 +151,8 @@ However, if the worker script continues to fail with a non-zero exit code in a s
[PHP superglobals](https://www.php.net/manual/en/language.variables.superglobals.php) (`$_SERVER`, `$_ENV`, `$_GET`...)
behave as follows:
* before the first call to `frankenphp_handle_request()`, superglobals contain values bound to the worker script itself
* during and after the call to `frankenphp_handle_request()`, superglobals contain values generated from the processed HTTP request, each call to `frankenphp_handle_request()` changes the superglobals values
- before the first call to `frankenphp_handle_request()`, superglobals contain values bound to the worker script itself
- during and after the call to `frankenphp_handle_request()`, superglobals contain values generated from the processed HTTP request, each call to `frankenphp_handle_request()` changes the superglobals values
To access the superglobals of the worker script inside the callback, you must copy them and import the copy in the scope of the callback:

71
docs/x-sendfile.md Normal file
View File

@@ -0,0 +1,71 @@
# Efficiently Serving Large Static Files (`X-Sendfile`/`X-Accel-Redirect`)
Usually, static files can be served directly by the web server,
but sometimes it's necessary to execute some PHP code before sending them:
access control, statistics, custom HTTP headers...
Unfortunately, using PHP to serve large static files is inefficient compared to
direct use of the web server (memory overload, reduced performance...).
FrankenPHP lets you delegate the sending of static files to the web server
**after** executing customized PHP code.
To do this, your PHP application simply needs to define a custom HTTP header
containing the path of the file to be served. FrankenPHP takes care of the rest.
This feature is known as **`X-Sendfile`** for Apache, and **`X-Accel-Redirect`** for NGINX.
In the following examples, we assume that the document root of the project is the `public/` directory.
and that we want to use PHP to serve files stored outside the `public/` directory,
from a directory named `private-files/`.
## Configuration
First, add the following configuration to your `Caddyfile` to enable this feature:
```patch
root public/
# ...
+ # Needed for Symfony, Laravel and other projects using the Symfony HttpFoundation component
+ request_header X-Sendfile-Type x-accel-redirect
+ request_header X-Accel-Mapping ../private-files=/private-files
+
+ intercept {
+ @accel header X-Accel-Redirect *
+ handle_response @accel {
+ root private-files/
+ rewrite * {resp.header.X-Accel-Redirect}
+ method * GET
+
+ # Remove the X-Accel-Redirect header set by PHP for increased security
+ header -X-Accel-Redirect
+
+ file_server
+ }
+ }
php_server
```
## Plain PHP
Set the relative file path (from `private-files/`) as the value of the `X-Accel-Redirect` header:
```php
header('X-Accel-Redirect: file.txt');
```
## Projects using the Symfony HttpFoundation component (Symfony, Laravel, Drupal...)
Symfony HttpFoundation [natively supports this feature](https://symfony.com/doc/current/components/http_foundation.html#serving-files).
It will automatically determine the correct value for the `X-Accel-Redirect` header and add it to the response.
```php
use Symfony\Component\HttpFoundation\BinaryFileResponse;
BinaryFileResponse::trustXSendfileTypeHeader();
$response = new BinaryFileResponse(__DIR__.'/../private-files/file.txt');
// ...
```

Some files were not shown because too many files have changed in this diff Show More