Skip to content

Commit

Permalink
Merge branch 'main' into no-handles
Browse files Browse the repository at this point in the history
# Conflicts:
#	.github/workflows/sanitizers.yaml
#	.github/workflows/tests.yaml
#	build-static.sh
#	dev-alpine.Dockerfile
#	dev.Dockerfile
#	frankenphp.go
#	static-builder.Dockerfile
#	worker.go
  • Loading branch information
a.stecher committed Oct 7, 2024
2 parents 6261930 + 56d2f99 commit e1b9afd
Show file tree
Hide file tree
Showing 15 changed files with 610 additions and 8 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/sanitizers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ jobs:
name: Compile edant/watcher
run: |
cd edant/watcher/watcher-c/
clang -o libwatcher.so ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -Wall -Wextra -fPIC -shared ${{ matrix.sanitizer == 'msan' && '-fsanitize=memory -fno-omit-frame-pointer -fno-optimize-sibling-calls' || '' }}
"$CC" -o libwatcher.so ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -Wall -Wextra -fPIC -shared ${{ matrix.sanitizer == 'msan' && '-fsanitize=memory -fno-omit-frame-pointer -fno-optimize-sibling-calls' || '' }}
sudo cp libwatcher.so /usr/local/lib/libwatcher.so
sudo ldconfig
-
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:
name: Compile edant/watcher
run: |
cd edant/watcher/watcher-c/
gcc -o libwatcher.so ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -O3 -Wall -Wextra -fPIC -shared
cc -o libwatcher.so ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -O3 -Wall -Wextra -fPIC -shared
sudo cp libwatcher.so /usr/local/lib/libwatcher.so
sudo ldconfig
-
Expand Down
9 changes: 9 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,15 @@ RUN gcc -o libwatcher.so ./src/watcher-c.cpp -I ./include -I ../include -std=c++
cp libwatcher.so /usr/local/lib/libwatcher.so && \
ldconfig /usr/local/lib

# install edant/watcher (necessary for file watching)
ARG EDANT_WATCHER_VERSION=next
WORKDIR /usr/local/src/watcher
RUN curl -L https://github.com/e-dant/watcher/archive/refs/heads/$EDANT_WATCHER_VERSION.tar.gz | tar xz
WORKDIR /usr/local/src/watcher/watcher-$EDANT_WATCHER_VERSION/watcher-c
RUN cc -o libwatcher.so ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -O3 -Wall -Wextra -fPIC -shared && \
cp libwatcher.so /usr/local/lib/libwatcher.so && \
ldconfig /usr/local/lib

# 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
Expand Down
9 changes: 9 additions & 0 deletions alpine.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,15 @@ RUN gcc -o libwatcher.so ./src/watcher-c.cpp -I ./include -I ../include -std=c++
cp libwatcher.so /usr/local/lib/libwatcher.so && \
ldconfig /usr/local/lib

# install edant/watcher (necessary for file watching)
ARG EDANT_WATCHER_VERSION=next
WORKDIR /usr/local/src/watcher
RUN curl -L https://github.com/e-dant/watcher/archive/refs/heads/$EDANT_WATCHER_VERSION.tar.gz | tar xz
WORKDIR /usr/local/src/watcher/watcher-$EDANT_WATCHER_VERSION/watcher-c
RUN cc -o libwatcher.so ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -O3 -Wall -Wextra -fPIC -shared && \
cp libwatcher.so /usr/local/lib/libwatcher.so && \
ldconfig /usr/local/lib

# 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
Expand Down
2 changes: 1 addition & 1 deletion build-static.sh
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ fi
# install edant/watcher for file watching (static version)
git clone --branch="${EDANT_WATCHER_VERSION:-next}" https://github.com/e-dant/watcher watcher
cd watcher/watcher-c
gcc -c -o libwatcher.o ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -Wall -Wextra -fPIC
cc -c -o libwatcher.o ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -Wall -Wextra -fPIC
ar rcs libwatcher.a libwatcher.o
cp libwatcher.a "../../buildroot/lib/libwatcher.a"
cd ../../
Expand Down
2 changes: 1 addition & 1 deletion dev-alpine.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ ARG EDANT_WATCHER_VERSION=next
WORKDIR /usr/local/src/watcher
RUN git clone --branch=$EDANT_WATCHER_VERSION https://github.com/e-dant/watcher .
WORKDIR /usr/local/src/watcher/watcher-c
RUN gcc -o libwatcher.so ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -O3 -Wall -Wextra -fPIC -shared && \
RUN cc -o libwatcher.so ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -O3 -Wall -Wextra -fPIC -shared && \
cp libwatcher.so /usr/local/lib/libwatcher.so && \
ldconfig /usr/local/lib

Expand Down
2 changes: 1 addition & 1 deletion dev.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ ARG EDANT_WATCHER_VERSION=next
WORKDIR /usr/local/src/watcher
RUN git clone --branch=$EDANT_WATCHER_VERSION https://github.com/e-dant/watcher .
WORKDIR /usr/local/src/watcher/watcher-c
RUN gcc -o libwatcher.so ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -O3 -Wall -Wextra -fPIC -shared && \
RUN cc -o libwatcher.so ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -O3 -Wall -Wextra -fPIC -shared && \
cp libwatcher.so /usr/local/lib/libwatcher.so && \
ldconfig /usr/local/lib

Expand Down
4 changes: 4 additions & 0 deletions frankenphp.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,10 @@ func Init(options ...Option) error {
return err
}

if err := restartWorkersOnFileChanges(opt.workers); err != nil {
return err
}

if c := logger.Check(zapcore.InfoLevel, "FrankenPHP started 🐘"); c != nil {
c.Write(zap.String("php_version", Version().Version), zap.Int("num_threads", opt.numThreads))
}
Expand Down
166 changes: 166 additions & 0 deletions internal/watcher/watch_pattern.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package watcher

import (
"go.uber.org/zap"
"path/filepath"
"strings"
)

type watchPattern struct {
dir string
patterns []string
trigger chan struct{}
}

func parseFilePatterns(filePatterns []string) ([]*watchPattern, error) {
watchPatterns := make([]*watchPattern, 0, len(filePatterns))
for _, filePattern := range filePatterns {
watchPattern, err := parseFilePattern(filePattern)
if err != nil {
return nil, err
}
watchPatterns = append(watchPatterns, watchPattern)
}
return watchPatterns, nil
}

// this method prepares the watchPattern struct for a single file pattern (aka /path/*pattern)
// TODO: using '/' is more efficient than filepath functions, but does not work on windows
func parseFilePattern(filePattern string) (*watchPattern, error) {
w := &watchPattern{}

// first we clean the pattern
absPattern, err := filepath.Abs(filePattern)
if err != nil {
return nil, err
}
w.dir = absPattern

// then we split the pattern to determine where the directory ends and the pattern starts
splitPattern := strings.Split(absPattern, "/")
patternWithoutDir := ""
for i, part := range splitPattern {
isFilename := i == len(splitPattern)-1 && strings.Contains(part, ".")
isGlobCharacter := strings.ContainsAny(part, "[*?{")
if isFilename || isGlobCharacter {
patternWithoutDir = filepath.Join(splitPattern[i:]...)
w.dir = filepath.Join(splitPattern[:i]...)
break
}
}

// now we split the pattern according to the recursive '**' syntax
w.patterns = strings.Split(patternWithoutDir, "**")
for i, pattern := range w.patterns {
w.patterns[i] = strings.Trim(pattern, "/")
}

// finally, we remove the trailing slash and add leading slash
w.dir = "/" + strings.Trim(w.dir, "/")

return w, nil
}

func (watchPattern *watchPattern) allowReload(fileName string, eventType int, pathType int) bool {
if !isValidEventType(eventType) || !isValidPathType(pathType, fileName) {
return false
}

return isValidPattern(fileName, watchPattern.dir, watchPattern.patterns)
}

// 0:rename,1:modify,2:create,3:destroy,4:owner,5:other,
func isValidEventType(eventType int) bool {
return eventType <= 3
}

// 0:dir,1:file,2:hard_link,3:sym_link,4:watcher,5:other,
func isValidPathType(pathType int, fileName string) bool {
if pathType == 4 {
logger.Debug("special edant/watcher event", zap.String("fileName", fileName))
}
return pathType <= 2
}

func isValidPattern(fileName string, dir string, patterns []string) bool {
// first we remove the dir from the pattern
if !strings.HasPrefix(fileName, dir) {
return false
}
fileNameWithoutDir := strings.TrimLeft(fileName, dir+"/")

// if the pattern has size 1 we can match it directly against the filename
if len(patterns) == 1 {
return matchBracketPattern(patterns[0], fileNameWithoutDir)
}

return matchPatterns(patterns, fileNameWithoutDir)
}

func matchPatterns(patterns []string, fileName string) bool {
partsToMatch := strings.Split(fileName, "/")
cursor := 0

// if there are multiple patterns due to '**' we need to match them individually
for i, pattern := range patterns {
patternSize := strings.Count(pattern, "/") + 1

// if we are at the last pattern we will start matching from the end of the filename
if i == len(patterns)-1 {
cursor = len(partsToMatch) - patternSize
}

// the cursor will move through the fileName until the pattern matches
for j := cursor; j < len(partsToMatch); j++ {
cursor = j
subPattern := strings.Join(partsToMatch[j:j+patternSize], "/")
if matchBracketPattern(pattern, subPattern) {
cursor = j + patternSize - 1
break
}
if cursor > len(partsToMatch)-patternSize-1 {
return false
}
}
}

return true
}

// we also check for the following bracket syntax: /path/*.{php,twig,yaml}
func matchBracketPattern(pattern string, fileName string) bool {
openingBracket := strings.Index(pattern, "{")
closingBracket := strings.Index(pattern, "}")

// if there are no brackets we can match regularly
if openingBracket == -1 || closingBracket == -1 {
return matchPattern(pattern, fileName)
}

beforeTheBrackets := pattern[:openingBracket]
betweenTheBrackets := pattern[openingBracket+1 : closingBracket]
afterTheBrackets := pattern[closingBracket+1:]

// all bracket entries are checked individually, only one needs to match
// *.{php,twig,yaml} -> *.php, *.twig, *.yaml
for _, pattern := range strings.Split(betweenTheBrackets, ",") {
if matchPattern(beforeTheBrackets+pattern+afterTheBrackets, fileName) {
return true
}
}

return false
}

func matchPattern(pattern string, fileName string) bool {
if pattern == "" {
return true
}
patternMatches, err := filepath.Match(pattern, fileName)
if err != nil {
logger.Error("failed to match filename", zap.String("file", fileName), zap.Error(err))
return false
}

return patternMatches
}
Loading

0 comments on commit e1b9afd

Please sign in to comment.