Skip to content

Commit adda22c

Browse files
authored
External php Update
1 parent 8028c0a commit adda22c

File tree

20 files changed

+1703
-0
lines changed

20 files changed

+1703
-0
lines changed

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2021 Harry Bayliss
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# PVM for Windows
2+
3+
> [!TIP]
4+
> Looking for the 0.x (composer) version? See the [v0 branch](https://github.com/hjbdev/pvm/tree/v0).
5+
6+
Removing the hassle of changing PHP versions in the CLI on Windows.
7+
8+
This package has a much more niche use case than nvm does. When developing on Windows and using the integrated terminal, it's quite difficult to get those terminals to _actually_ listen to PATH changes.
9+
10+
This utility changes that.
11+
12+
## Installation
13+
14+
> [!WARNING]
15+
> version lower than 1.3.0 will have only pvm.exe
16+
> version 1.3.0 or higher will include pvm-setup.exe but can still get pvm.exe from source
17+
18+
### Installer
19+
> Download the latest pvm installer from the releases page (>= 1.3.0).
20+
21+
### Manual Installation
22+
> Create the folder `%UserProfile%\.pvm\bin` (e.g. `C:\Users\Harry\.pvm\bin`) and drop the pvm.exe in there. Add the folder to your PATH.
23+
24+
## Commands
25+
26+
```
27+
pvm list
28+
```
29+
30+
Will list out all the available PHP versions you have installed
31+
32+
```
33+
pvm install 8
34+
```
35+
36+
> [!NOTE]
37+
> The install command will automatically determine the newest minor/patch versions if they are not specified
38+
39+
Will install PHP 8 at the latest minor and patch.
40+
41+
```
42+
pvm use 8.2
43+
```
44+
45+
> [!NOTE]
46+
> Versions must have major.minor specified in the *use* command. If a .patch version is omitted, newest available patch version is chosen.
47+
48+
Will switch your currently active PHP version to PHP 8.2 latest patch.
49+
50+
```
51+
pvm uninstall 8.2.9
52+
```
53+
54+
> [!NOTE]
55+
> Versions must have major.minor.patch specified in the *uninstall* command. If a .patch version is omitted, it will not uninstalling.
56+
57+
Will uninstall PHP version to PHP 8.2.9
58+
59+
## Composer support
60+
61+
`pvm` now installs also composer with each php version installed.
62+
It will install Composer latest stable release for PHP >= 7.2 and Composer latest 2.2.x LTS for PHP < 7.2.
63+
You'll be able to invoke composer from terminal as it is intended:
64+
```shell
65+
composer --version
66+
```
67+
68+
## Build this project
69+
70+
To compile this project use:
71+
72+
```shell
73+
GOOS=windows GOARCH=amd64 go build -o pvm.exe
74+
```

pvm-setup.exe

5.89 MB
Binary file not shown.

pvm-setup.iss

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
; Script generated by the Inno Setup Script Wizard.
2+
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
3+
4+
#define MyAppName "PVM"
5+
#define MyAppVersion "1.3.1"
6+
#define MyAppPublisher "hjb.dev"
7+
#define MyAppURL "https://github.com/hjbdev/pvm"
8+
#define MyAppExeName "pvm.exe"
9+
10+
[Setup]
11+
; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
12+
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
13+
AppId={{DA006438-7CC4-41E4-B2BF-2AD84AAA18E9}
14+
AppName={#MyAppName}
15+
AppVersion={#MyAppVersion}
16+
;AppVerName={#MyAppName} {#MyAppVersion}
17+
AppPublisher={#MyAppPublisher}
18+
AppPublisherURL={#MyAppURL}
19+
AppSupportURL={#MyAppURL}
20+
AppUpdatesURL={#MyAppURL}
21+
DefaultDirName={%USERPROFILE}\.pvm
22+
UninstallDisplayIcon={app}\{#MyAppExeName}
23+
; "ArchitecturesAllowed=x64compatible" specifies that Setup cannot run
24+
; on anything but x64 and Windows 11 on Arm.
25+
ArchitecturesAllowed=x64os
26+
; "ArchitecturesInstallIn64BitMode=x64compatible" requests that the
27+
; install be done in "64-bit mode" on x64 or Windows 11 on Arm,
28+
; meaning it should use the native 64-bit Program Files directory and
29+
; the 64-bit view of the registry.
30+
ArchitecturesInstallIn64BitMode=x64compatible
31+
DefaultGroupName={#MyAppName}
32+
AllowNoIcons=yes
33+
LicenseFile=LICENSE
34+
InfoAfterFile=README.md
35+
; Uncomment the following line to run in non administrative install mode (install for current user only).
36+
PrivilegesRequired=lowest
37+
PrivilegesRequiredOverridesAllowed=dialog
38+
OutputDir=.
39+
OutputBaseFilename=pvm-setup
40+
SolidCompression=yes
41+
WizardStyle=modern
42+
43+
[Languages]
44+
Name: "english"; MessagesFile: "compiler:Default.isl"
45+
46+
[Files]
47+
Source: "src\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
48+
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
49+
50+
[Dirs]
51+
Name: "{app}\bin"
52+
Name: "{app}\versions"
53+
54+
[UninstallDelete]
55+
Type: filesandordirs; Name: "{app}"
56+
57+
[Code]
58+
var
59+
path: string;
60+
61+
procedure InitializePath();
62+
begin
63+
if RegQueryStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', path) then
64+
begin
65+
if Pos(ExpandConstant('{app}'), path) = 0 then
66+
begin
67+
// Check if PATH is empty
68+
if path <> '' then
69+
path := path + ';';
70+
71+
// Add the app directory and bin directory to the PATH
72+
path := path + ExpandConstant('{app}') + ';' + ExpandConstant('{app}') + '\bin';
73+
74+
// Remove extra semicolons (e.g., ;; becomes ; or ;;; becomes ;)
75+
StringChangeEx(path, ';;', ';', True);
76+
77+
// Remove any leading semicolon at the start of the path if present
78+
if (Pos(';', path) = 1) then
79+
path := Copy(path, 2, Length(path) - 1);
80+
81+
// Ensure there's no trailing semicolon at the end of the path
82+
if (Pos(';', path) = Length(path)) then
83+
path := Copy(path, 1, Length(path) - 1);
84+
85+
// Write the added path back to the registry
86+
RegWriteExpandStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', path);
87+
end;
88+
end
89+
else
90+
begin
91+
MsgBox('Unable to read PATH environment variable.', mbError, MB_OK);
92+
end;
93+
end;
94+
95+
procedure RemovePath();
96+
begin
97+
if RegQueryStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', path) then
98+
begin
99+
// Remove the app directory and bin directory from the PATH
100+
StringChangeEx(path, ExpandConstant('{app}') + ';', '', True);
101+
StringChangeEx(path, ExpandConstant('{app}') + '\bin' + ';', '', True);
102+
103+
// Remove extra semicolons (e.g., ;; becomes ; or ;;; becomes ;)
104+
StringChangeEx(path, ';;', ';', True);
105+
106+
// Remove any leading semicolon at the start of the path if present
107+
if (Pos(';', path) = 1) then
108+
path := Copy(path, 2, Length(path) - 1);
109+
110+
// Ensure there's no trailing semicolon at the end of the path
111+
if (Pos(';', path) = Length(path)) then
112+
path := Copy(path, 1, Length(path) - 1);
113+
114+
// Write the cleaned path back to the registry
115+
RegWriteExpandStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', path);
116+
end
117+
else
118+
begin
119+
MsgBox('Unable to read PATH environment variable.', mbError, MB_OK);
120+
end;
121+
end;
122+
123+
procedure CurStepChanged(CurStep: TSetupStep);
124+
begin
125+
if CurStep = ssPostInstall then
126+
begin
127+
InitializePath();
128+
end
129+
end;
130+
131+
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
132+
begin
133+
if CurUninstallStep = usUninstall then
134+
begin
135+
RemovePath();
136+
end;
137+
end;
138+
139+

src/commands/add.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package commands
2+
3+
import (
4+
"fmt"
5+
"hjbdev/pvm/common"
6+
"hjbdev/pvm/theme"
7+
"os"
8+
"os/exec"
9+
"path/filepath"
10+
"regexp"
11+
"strings"
12+
)
13+
14+
func Add(args []string) {
15+
if len(args) < 1 {
16+
theme.Error("You must specify a path of external php.")
17+
return
18+
}
19+
20+
addPath := args[0]
21+
22+
// Verify that the specified path exists and contains a php executable
23+
phpPath := filepath.Join(addPath, "php.exe")
24+
if _, err := os.Stat(phpPath); os.IsNotExist(err) {
25+
theme.Error(fmt.Sprintf("The file php.exe was not found in the specified path: %s", addPath))
26+
return
27+
}
28+
29+
// Run the "php -v" command to get the PHP version
30+
cmd := exec.Command(phpPath, "-v")
31+
output, err := cmd.CombinedOutput()
32+
if err != nil {
33+
theme.Error(fmt.Sprintf("failed to execute php -v: %v", err))
34+
}
35+
36+
// Parse the version from the output
37+
// The output usually looks like: PHP 7.4.3 (cli) (built: Mar 4 2020 22:44:12) (ZTS)
38+
versionPattern := `PHP\s+([0-9]+\.[0-9]+\.[0-9]+)`
39+
re := regexp.MustCompile(versionPattern)
40+
matches := re.FindStringSubmatch(string(output))
41+
if len(matches) < 2 {
42+
theme.Error("failed to parse PHP version from output")
43+
}
44+
45+
// Determine if the version is thread-safe (TS) or non-thread-safe (NTS)
46+
threadSafe := ""
47+
if strings.Contains(strings.ToLower(phpPath), "nts") {
48+
threadSafe = " nts"
49+
}
50+
51+
// Add to versions.json
52+
common.AddToVersionsJson(phpPath, matches[1]+threadSafe, "external")
53+
54+
theme.Success(fmt.Sprintf("Finished add PHP %s", phpPath))
55+
}

src/commands/help.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package commands
2+
3+
import (
4+
"fmt"
5+
"hjbdev/pvm/theme"
6+
)
7+
8+
func Help(notFoundError bool) {
9+
theme.Title("pvm: PHP Version Manager")
10+
theme.Info("Version 1.3.1")
11+
12+
if notFoundError {
13+
theme.Error("Error: Command not found.")
14+
fmt.Println()
15+
}
16+
17+
fmt.Println("Usage:")
18+
fmt.Println(" pvm [command] [options]")
19+
fmt.Println()
20+
fmt.Println("Available Commands:")
21+
22+
commands := map[string]string{
23+
"help": "Display help information about pvm commands.",
24+
"install": "Install a specific PHP version.",
25+
"uninstall": "Uninstall a specific PHP version.",
26+
"list-remote": "List available PHP versions from remote repositories.",
27+
"list": "List installed PHP versions.",
28+
"use": "Switch to a specific installed PHP version.",
29+
"add": "Add a custom PHP version source.",
30+
"remove": "Remove a custom PHP version source.",
31+
}
32+
33+
for cmd, desc := range commands {
34+
fmt.Printf(" %-12s %s\n", cmd, desc)
35+
}
36+
37+
fmt.Println()
38+
fmt.Println("For detailed usage of a specific command, use:")
39+
fmt.Println(" pvm help [command]")
40+
}

0 commit comments

Comments
 (0)