Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit d66d01b

Browse filesBrowse files
Add support for pie
1 parent 6925d21 commit d66d01b
Copy full SHA for d66d01b

File tree

8 files changed

+220
-37
lines changed
Filter options

8 files changed

+220
-37
lines changed

‎commands/root.go

Copy file name to clipboardExpand all lines: commands/root.go
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ func CommonCommands() []*console.Command {
5252
localCommands := []*console.Command{
5353
binConsoleWrapper,
5454
composerWrapper,
55+
pieWrapper,
5556
phpWrapper,
5657
bookCheckReqsCmd,
5758
bookCheckoutCmd,
@@ -143,6 +144,7 @@ func WelcomeAction(c *console.Context) error {
143144
localServerStopCmd,
144145
localSecurityCheckCmd,
145146
composerWrapper,
147+
pieWrapper,
146148
binConsoleWrapper,
147149
phpWrapper,
148150
})

‎commands/wrappers.go

Copy file name to clipboardExpand all lines: commands/wrappers.go
+10Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,16 @@ var (
3737
return console.IncorrectUsageError{ParentError: errors.New(`This command can only be run as "symfony composer"`)}
3838
},
3939
}
40+
pieWrapper = &console.Command{
41+
Usage: "Runs PIE",
42+
Hidden: console.Hide,
43+
// we use an alias to avoid the command being shown in the help but
44+
// still be available for completion
45+
Aliases: []*console.Alias{{Name: "pie"}},
46+
Action: func(c *console.Context) error {
47+
return console.IncorrectUsageError{ParentError: errors.New(`This command can only be run as "symfony pie"`)}
48+
},
49+
}
4050
binConsoleWrapper = &console.Command{
4151
Usage: "Runs the Symfony Console (bin/console) for current project",
4252
Hidden: console.Hide,

‎local/php/assert.go

Copy file name to clipboard
+30Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package php
2+
3+
import (
4+
"bufio"
5+
"bytes"
6+
"os"
7+
)
8+
9+
// checkIsPHPScript checks that the provided file is indeed a phar/PHP script (not a .bat file)
10+
func checkIsPHPScript(path string) bool {
11+
if path == "" {
12+
return false
13+
}
14+
file, err := os.Open(path)
15+
if err != nil {
16+
return false
17+
}
18+
defer file.Close()
19+
reader := bufio.NewReader(file)
20+
byteSlice, _, err := reader.ReadLine()
21+
if err != nil {
22+
return false
23+
}
24+
25+
if bytes.Equal(byteSlice, []byte("<?php")) {
26+
return true
27+
}
28+
29+
return bytes.HasPrefix(byteSlice, []byte("#!/")) && bytes.HasSuffix(byteSlice, []byte("php"))
30+
}

‎local/php/composer_test.go renamed to ‎local/php/assert_test.go

Copy file name to clipboardExpand all lines: local/php/assert_test.go
+7-7Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,24 @@ import (
2525
. "gopkg.in/check.v1"
2626
)
2727

28-
type ComposerSuite struct{}
28+
type AssertSuite struct{}
2929

30-
var _ = Suite(&ComposerSuite{})
30+
var _ = Suite(&AssertSuite{})
3131

32-
func (s *ComposerSuite) TestIsComposerPHPScript(c *C) {
32+
func (s *AssertSuite) TestAssertIsPHPScript(c *C) {
3333
dir, err := filepath.Abs("testdata/php_scripts")
3434
c.Assert(err, IsNil)
3535

36-
c.Assert(isPHPScript(""), Equals, false)
37-
c.Assert(isPHPScript(filepath.Join(dir, "unknown")), Equals, false)
38-
c.Assert(isPHPScript(filepath.Join(dir, "invalid")), Equals, false)
36+
c.Assert(checkIsPHPScript(""), Equals, false)
37+
c.Assert(checkIsPHPScript(filepath.Join(dir, "unknown")), Equals, false)
38+
c.Assert(checkIsPHPScript(filepath.Join(dir, "invalid")), Equals, false)
3939

4040
for _, validScripts := range []string{
4141
"usual-one",
4242
"debian-style",
4343
"custom-one",
4444
"plain-one.php",
4545
} {
46-
c.Assert(isPHPScript(filepath.Join(dir, validScripts)), Equals, true)
46+
c.Assert(checkIsPHPScript(filepath.Join(dir, validScripts)), Equals, true)
4747
}
4848
}

‎local/php/composer.go

Copy file name to clipboardExpand all lines: local/php/composer.go
+1-25Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
package php
2121

2222
import (
23-
"bufio"
2423
"bytes"
2524
"crypto/sha512"
2625
"encoding/hex"
@@ -77,7 +76,7 @@ func Composer(dir string, args, env []string, stdout, stderr, logger io.Writer,
7776
if composerPath := os.Getenv("SYMFONY_COMPOSER_PATH"); composerPath != "" {
7877
debugLogger.Debug().Str("SYMFONY_COMPOSER_PATH", composerPath).Msg("SYMFONY_COMPOSER_PATH has been defined. User is taking control over Composer detection and execution.")
7978
e.Args = append([]string{composerPath}, args...)
80-
} else if path, err := e.findComposer(composerBin); err == nil && isPHPScript(path) {
79+
} else if path, err := e.findComposer(composerBin); err == nil && checkIsPHPScript(path) {
8180
e.Args = append([]string{"php", path}, args...)
8281
} else {
8382
reason := "No Composer installation found."
@@ -108,29 +107,6 @@ func Composer(dir string, args, env []string, stdout, stderr, logger io.Writer,
108107
return ComposerResult{}
109108
}
110109

111-
// isPHPScript checks that the composer file is indeed a phar/PHP script (not a .bat file)
112-
func isPHPScript(path string) bool {
113-
if path == "" {
114-
return false
115-
}
116-
file, err := os.Open(path)
117-
if err != nil {
118-
return false
119-
}
120-
defer file.Close()
121-
reader := bufio.NewReader(file)
122-
byteSlice, _, err := reader.ReadLine()
123-
if err != nil {
124-
return false
125-
}
126-
127-
if bytes.Equal(byteSlice, []byte("<?php")) {
128-
return true
129-
}
130-
131-
return bytes.HasPrefix(byteSlice, []byte("#!/")) && bytes.HasSuffix(byteSlice, []byte("php"))
132-
}
133-
134110
func composerVersion() int {
135111
var lock struct {
136112
Version string `json:"plugin-api-version"`

‎local/php/executor.go

Copy file name to clipboardExpand all lines: local/php/executor.go
+20Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,26 @@ func (e *Executor) findComposer(extraBin string) (string, error) {
417417
return findComposer(extraBin, e.Logger)
418418
}
419419

420+
// findPie locates the PIE binary depending on the configuration
421+
func (e *Executor) findPie() (string, error) {
422+
if scriptDir, err := e.DetectScriptDir(); err == nil {
423+
for _, file := range []string{"pie.phar", "pie"} {
424+
path := filepath.Join(scriptDir, file)
425+
e.Logger.Debug().Str("source", "PIE").Msgf(`Looking for PIE under "%s"`, path)
426+
d, err := os.Stat(path)
427+
if err != nil {
428+
continue
429+
}
430+
if m := d.Mode(); !m.IsDir() {
431+
e.Logger.Debug().Str("source", "PIE").Msgf(`Found potential PIE as "%s"`, path)
432+
return path, nil
433+
}
434+
}
435+
}
436+
437+
return findPie(e.Logger)
438+
}
439+
420440
// Execute executes the right version of PHP depending on the configuration
421441
func (e *Executor) Execute(loadDotEnv bool) int {
422442
if err := e.Config(loadDotEnv); err != nil {

‎local/php/pie.go

Copy file name to clipboard
+137Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/*
2+
* Copyright (c) 2021-present Fabien Potencier <fabien@symfony.com>
3+
*
4+
* This file is part of Symfony CLI project
5+
*
6+
* This program is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU Affero General Public License as
8+
* published by the Free Software Foundation, either version 3 of the
9+
* License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU Affero General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Affero General Public License
17+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
package php
21+
22+
import (
23+
"fmt"
24+
"io"
25+
"net/http"
26+
"os"
27+
"path/filepath"
28+
29+
"github.com/pkg/errors"
30+
"github.com/rs/zerolog"
31+
"github.com/symfony-cli/symfony-cli/util"
32+
)
33+
34+
type PieResult struct {
35+
code int
36+
error error
37+
}
38+
39+
func (p PieResult) Error() string {
40+
if p.error != nil {
41+
return p.error.Error()
42+
}
43+
44+
return ""
45+
}
46+
47+
func (p PieResult) ExitCode() int {
48+
return p.code
49+
}
50+
51+
func Pie(dir string, args, env []string, stdout, stderr, logger io.Writer, debugLogger zerolog.Logger) PieResult {
52+
e := &Executor{
53+
Dir: dir,
54+
BinName: "php",
55+
Stdout: stdout,
56+
Stderr: stderr,
57+
SkipNbArgs: -1,
58+
ExtraEnv: env,
59+
Logger: debugLogger,
60+
}
61+
62+
if piePath := os.Getenv("SYMFONY_PIE_PATH"); piePath != "" {
63+
debugLogger.Debug().Str("SYMFONY_PIE_PATH", piePath).Msg("SYMFONY_PIE_PATH has been defined. User is taking control over PIE detection and execution.")
64+
e.Args = append([]string{piePath}, args...)
65+
} else if path, err := e.findPie(); err == nil && checkIsPHPScript(path) {
66+
e.Args = append([]string{"php", path}, args...)
67+
} else {
68+
reason := "No PIE installation found."
69+
if path != "" {
70+
reason = fmt.Sprintf("Detected PIE file (%s) is not a valid PHAR or PHP script.", path)
71+
}
72+
fmt.Fprintln(logger, " WARNING:", reason)
73+
fmt.Fprintln(logger, " Downloading PIE for you, but it is recommended to install PIE yourself, instructions available at https://github.com/php/pie")
74+
// we don't store it under bin/ to avoid it being found by findPie as we want to only use it as a fallback
75+
binDir := filepath.Join(util.GetHomeDir(), "pie")
76+
if path, err = downloadPie(binDir); err != nil {
77+
return PieResult{
78+
code: 1,
79+
error: errors.Wrap(err, "unable to find pie, get it at https://github.com/php/pie"),
80+
}
81+
}
82+
e.Args = append([]string{"php", path}, args...)
83+
fmt.Fprintf(logger, " (running %s)\n\n", e.CommandLine())
84+
}
85+
86+
ret := e.Execute(false)
87+
if ret != 0 {
88+
return PieResult{
89+
code: ret,
90+
error: errors.Errorf("unable to run %s", e.CommandLine()),
91+
}
92+
}
93+
return PieResult{}
94+
}
95+
96+
func findPie(logger zerolog.Logger) (string, error) {
97+
for _, file := range []string{"pie", "pie.phar"} {
98+
logger.Debug().Str("source", "PIE").Msgf(`Looking for PIE in the PATH as "%s"`, file)
99+
if pharPath, _ := LookPath(file); pharPath != "" {
100+
logger.Debug().Str("source", "PIE").Msgf(`Found potential PIE as "%s"`, pharPath)
101+
return pharPath, nil
102+
}
103+
}
104+
105+
return "", os.ErrNotExist
106+
}
107+
108+
func downloadPie(dir string) (string, error) {
109+
if err := os.MkdirAll(dir, 0755); err != nil {
110+
return "", err
111+
}
112+
path := filepath.Join(dir, "pie.phar")
113+
if _, err := os.Stat(path); err == nil {
114+
return path, nil
115+
}
116+
117+
piePhar, err := downloadPiePhar()
118+
if err != nil {
119+
return "", err
120+
}
121+
122+
err = os.WriteFile(path, piePhar, 0755)
123+
if err != nil {
124+
return "", err
125+
}
126+
127+
return path, nil
128+
}
129+
130+
func downloadPiePhar() ([]byte, error) {
131+
resp, err := http.Get("https://github.com/php/pie/releases/latest/download/pie.phar")
132+
if err != nil {
133+
return nil, err
134+
}
135+
defer resp.Body.Close()
136+
return io.ReadAll(resp.Body)
137+
}

‎main.go

Copy file name to clipboardExpand all lines: main.go
+13-5Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,19 @@ func main() {
7979
os.Exit(executor.Execute(false))
8080
}
8181
}
82-
// called via "symfony composer"?
83-
if len(args) >= 2 && args[1] == "composer" {
84-
res := php.Composer("", args[2:], getCliExtraEnv(), os.Stdout, os.Stderr, os.Stderr, terminal.Logger)
85-
terminal.Eprintln(res.Error())
86-
os.Exit(res.ExitCode())
82+
// called via "symfony composer" or "symfony pie"?
83+
if len(args) >= 2 {
84+
if args[1] == "composer" {
85+
res := php.Composer("", args[2:], getCliExtraEnv(), os.Stdout, os.Stderr, os.Stderr, terminal.Logger)
86+
terminal.Eprintln(res.Error())
87+
os.Exit(res.ExitCode())
88+
}
89+
90+
if args[1] == "pie" {
91+
res := php.Pie("", args[2:], getCliExtraEnv(), os.Stdout, os.Stderr, os.Stderr, terminal.Logger)
92+
terminal.Eprintln(res.Error())
93+
os.Exit(res.ExitCode())
94+
}
8795
}
8896

8997
for _, env := range []string{"BRANCH", "ENV", "APPLICATION_NAME"} {

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.