request_options.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. package frankenphp
  2. import (
  3. "github.com/dunglas/frankenphp/internal/fastabs"
  4. "path/filepath"
  5. "sync"
  6. "sync/atomic"
  7. "go.uber.org/zap"
  8. )
  9. // RequestOption instances allow to configure a FrankenPHP Request.
  10. type RequestOption func(h *FrankenPHPContext) error
  11. var (
  12. documentRootCache sync.Map
  13. documentRootCacheLen atomic.Uint32
  14. )
  15. // WithRequestDocumentRoot sets the root directory of the PHP application.
  16. // if resolveSymlink is true, oath declared as root directory will be resolved
  17. // to its absolute value after the evaluation of any symbolic links.
  18. // Due to the nature of PHP opcache, root directory path is cached: when
  19. // using a symlinked directory as root this could generate errors when
  20. // symlink is changed without PHP being restarted; enabling this
  21. // directive will set $_SERVER['DOCUMENT_ROOT'] to the real directory path.
  22. func WithRequestDocumentRoot(documentRoot string, resolveSymlink bool) RequestOption {
  23. return func(o *FrankenPHPContext) (err error) {
  24. v, ok := documentRootCache.Load(documentRoot)
  25. if !ok {
  26. // make sure file root is absolute
  27. v, err = fastabs.FastAbs(documentRoot)
  28. if err != nil {
  29. return err
  30. }
  31. // prevent the cache to grow forever, this is a totally arbitrary value
  32. if documentRootCacheLen.Load() < 1024 {
  33. documentRootCache.LoadOrStore(documentRoot, v)
  34. documentRootCacheLen.Add(1)
  35. }
  36. }
  37. if resolveSymlink {
  38. if v, err = filepath.EvalSymlinks(v.(string)); err != nil {
  39. return err
  40. }
  41. }
  42. o.documentRoot = v.(string)
  43. return nil
  44. }
  45. }
  46. // WithRequestResolvedDocumentRoot is similar to WithRequestDocumentRoot
  47. // but doesn't do any checks or resolving on the path to improve performance.
  48. func WithRequestResolvedDocumentRoot(documentRoot string) RequestOption {
  49. return func(o *FrankenPHPContext) error {
  50. o.documentRoot = documentRoot
  51. return nil
  52. }
  53. }
  54. // WithRequestSplitPath contains a list of split path strings.
  55. //
  56. // The path in the URL will be split into two, with the first piece ending
  57. // with the value of splitPath. The first piece will be assumed as the
  58. // actual resource (CGI script) name, and the second piece will be set to
  59. // PATH_INFO for the CGI script to use.
  60. //
  61. // Future enhancements should be careful to avoid CVE-2019-11043,
  62. // which can be mitigated with use of a try_files-like behavior
  63. // that 404s if the FastCGI path info is not found.
  64. func WithRequestSplitPath(splitPath []string) RequestOption {
  65. return func(o *FrankenPHPContext) error {
  66. o.splitPath = splitPath
  67. return nil
  68. }
  69. }
  70. type PreparedEnv = map[string]string
  71. func PrepareEnv(env map[string]string) PreparedEnv {
  72. preparedEnv := make(PreparedEnv, len(env))
  73. for k, v := range env {
  74. preparedEnv[k+"\x00"] = v
  75. }
  76. return preparedEnv
  77. }
  78. // WithRequestEnv set CGI-like environment variables that will be available in $_SERVER.
  79. // Values set with WithEnv always have priority over automatically populated values.
  80. func WithRequestEnv(env map[string]string) RequestOption {
  81. return WithRequestPreparedEnv(PrepareEnv(env))
  82. }
  83. func WithRequestPreparedEnv(env PreparedEnv) RequestOption {
  84. return func(o *FrankenPHPContext) error {
  85. o.env = env
  86. return nil
  87. }
  88. }
  89. // WithRequestLogger sets the logger associated with the current request
  90. func WithRequestLogger(logger *zap.Logger) RequestOption {
  91. return func(o *FrankenPHPContext) error {
  92. o.logger = logger
  93. return nil
  94. }
  95. }