├── .gitignore
├── README.md
├── package-lock.json
├── package.json
└── src
├── fpm.js
├── index.js
└── php.js
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PHP Version Manager
2 |
3 |
4 |
5 |
6 |
7 | ## Installation
8 |
9 | ```bash
10 | npm install --global php-version-manager
11 | ```
12 |
13 | ## Usage
14 |
15 | ```bash
16 | pvm --help
17 |
18 | # output
19 | Usage: pvm [options] [command]
20 |
21 | PHP Version Manager version x.x.x
22 |
23 | Options:
24 | -v, --version Output the current application version
25 | -h, --help output usage information
26 |
27 | Commands:
28 | status|s Show current PHP version status
29 | ls List available PHP versions
30 | use|u Switch PHP version
31 | xdebug|x [sapi] [status] Manage XDebug status
32 | restart|r Restart PHP-FPM and NGINX
33 |
34 | ```
35 |
36 | ### Status
37 |
38 | Show current PHP version and XDebug status
39 |
40 | ```bash
41 | pvm status
42 |
43 | # output
44 | PHP Version Manager version x.x.x
45 |
46 | PHP: 7.3 CLI: OFF FPM: OFF
47 | ```
48 |
49 | ### List versions
50 |
51 | List installed PHP versions
52 |
53 | ```bash
54 | pvm ls
55 |
56 | # output
57 | 5.6
58 | 7.0
59 | 7.1
60 | 7.2
61 | 7.3
62 | ```
63 |
64 | ### Switch PHP version
65 |
66 | ```
67 | pvm use 7.2
68 | ```
69 |
70 | ### Manage XDebug
71 |
72 | ```bash
73 | # Toggle XDebug for cli and fpm
74 | pvm xdebug
75 |
76 | # Enable XDebug for cli and fpm
77 | pvm xdebug on
78 |
79 | # Disable XDebug for cli and fpm
80 | pvm xdebug off
81 |
82 | # Enable XDebug for cli only
83 | pvm xdebug cli on
84 |
85 | # Disable XDebug for fpm only
86 | pvm xdebug fpm off
87 |
88 | # Toggle XDebug for cli
89 | pvm xdebug cli
90 |
91 | # Toggle XDebug for fpm
92 | pvm xdebug fpm
93 | ```
94 |
95 | ### Restart Nginx and PHP-FPM
96 |
97 | ```bash
98 | pvm restart
99 | ```
100 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "php-version-manager",
3 | "version": "0.3.1",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "ansi-styles": {
8 | "version": "3.2.1",
9 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
10 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
11 | "requires": {
12 | "color-convert": "^1.9.0"
13 | }
14 | },
15 | "chalk": {
16 | "version": "2.4.2",
17 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
18 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
19 | "requires": {
20 | "ansi-styles": "^3.2.1",
21 | "escape-string-regexp": "^1.0.5",
22 | "supports-color": "^5.3.0"
23 | }
24 | },
25 | "color-convert": {
26 | "version": "1.9.3",
27 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
28 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
29 | "requires": {
30 | "color-name": "1.1.3"
31 | }
32 | },
33 | "color-name": {
34 | "version": "1.1.3",
35 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
36 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
37 | },
38 | "commander": {
39 | "version": "3.0.2",
40 | "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz",
41 | "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow=="
42 | },
43 | "escape-string-regexp": {
44 | "version": "1.0.5",
45 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
46 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
47 | },
48 | "has-flag": {
49 | "version": "3.0.0",
50 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
51 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
52 | },
53 | "prettier": {
54 | "version": "1.18.2",
55 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.18.2.tgz",
56 | "integrity": "sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==",
57 | "dev": true
58 | },
59 | "supports-color": {
60 | "version": "5.5.0",
61 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
62 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
63 | "requires": {
64 | "has-flag": "^3.0.0"
65 | }
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "php-version-manager",
3 | "version": "0.3.1",
4 | "description": "Linux command line tool for managing locally installed PHP versions.",
5 | "main": "src/index.js",
6 | "scripts": {
7 | "test": "echo \"no test specified\" && exit 0"
8 | },
9 | "keywords": [
10 | "cli",
11 | "php",
12 | "pvm"
13 | ],
14 | "author": "Chris ",
15 | "license": "MIT",
16 | "dependencies": {
17 | "chalk": "^2.4.2",
18 | "commander": "^3.0.2"
19 | },
20 | "devDependencies": {
21 | "prettier": "1.18.2"
22 | },
23 | "bin": {
24 | "pvm": "./src/index.js"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/fpm.js:
--------------------------------------------------------------------------------
1 | const { execSync } = require("child_process");
2 | const { versions } = require("./php");
3 |
4 | /**
5 | * Restart PHP-FPM and NGINX services
6 | *
7 | * @return {boolean}
8 | */
9 | const restart = () => {
10 | versions().forEach(version => {
11 | execSync(`sudo /usr/sbin/service php${version}-fpm restart`);
12 | });
13 | execSync("sudo /usr/sbin/service nginx restart");
14 | return true;
15 | };
16 |
17 | module.exports = {
18 | restart
19 | };
20 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const commander = require("commander");
4 | const chalk = require("chalk");
5 | const php = require("./php");
6 | const fpm = require("./fpm");
7 | const applicationVersion = require("../package.json").version;
8 |
9 | if (process.argv.length === 2) {
10 | process.argv.push("status");
11 | }
12 |
13 | const renderStatus = () => {
14 | console.log(
15 | chalk`\n {green PHP Version Manager} version {yellow ${applicationVersion}}\n`
16 | );
17 |
18 | const version = php.current();
19 | const cli = php.moduleStatus(version, "cli", "xdebug");
20 | const fpm = php.moduleStatus(version, "fpm", "xdebug");
21 |
22 | const phpText = "PHP: " + chalk.blue.bold(version);
23 | const cliText =
24 | "CLI: " + (cli ? chalk.green.bold("ON") : chalk.red.bold("OFF"));
25 | const fpmText =
26 | "FPM: " + (fpm ? chalk.green.bold("ON") : chalk.red.bold("OFF"));
27 |
28 | console.log(" " + [phpText, cliText, fpmText].join(" ") + "\n");
29 | };
30 |
31 | const program = new commander.Command();
32 |
33 | program
34 | .name("pvm")
35 | .version(
36 | applicationVersion,
37 | "-v, --version",
38 | "output the current application version"
39 | )
40 | .usage("[command] [options]")
41 | .description(
42 | chalk`{green PHP Version Manager} version {yellow ${applicationVersion}}`
43 | );
44 |
45 | program
46 | .command("status")
47 | .alias("s")
48 | .description("Show current PHP version status")
49 | .action(() => {
50 | renderStatus();
51 |
52 | process.exit(0);
53 | });
54 |
55 | program
56 | .command("ls")
57 | .description("List available PHP versions")
58 | .action(() => {
59 | const currentVersion = php.current();
60 | php.versions().forEach(version => {
61 | if (version === currentVersion) {
62 | console.log(chalk.green(version));
63 | } else {
64 | console.log(version);
65 | }
66 | });
67 |
68 | process.exit(0);
69 | });
70 |
71 | program
72 | .command("use ")
73 | .alias("u")
74 | .description("Switch PHP version")
75 | .action(version => {
76 | if (/^\d\d$/.test(version)) {
77 | version = version.slice(0, 1) + "." + version.slice(1);
78 | }
79 |
80 | if (php.use(version)) {
81 | console.log("Restarting PHP-FPM and NGINX");
82 | fpm.restart();
83 | }
84 |
85 | renderStatus();
86 | });
87 |
88 | program
89 | .command("xdebug [sapi] [status]")
90 | .alias("x")
91 | .description("Manage XDebug status")
92 | .action((sapi, status) => {
93 | const enableOptions = ["1", "on", "yes", "y"];
94 | const disableOptions = ["0", "off", "no", "n"];
95 |
96 | if (
97 | status === undefined &&
98 | (enableOptions.includes(sapi) || disableOptions.includes(sapi))
99 | ) {
100 | status = sapi;
101 | sapi = undefined;
102 | }
103 |
104 | if (sapi !== undefined && !["fpm", "cli"].includes(sapi)) {
105 | throw new Error(`Invalid SAPI \'${sapi}\', specify \'fpm\' or \'cli\'`);
106 | }
107 |
108 | if (status === undefined) {
109 | php.moduleToggle("xdebug", sapi);
110 | } else if (enableOptions.includes(status)) {
111 | php.moduleEnable("xdebug", sapi);
112 | } else if (disableOptions.includes(status)) {
113 | php.moduleDisable("xdebug", sapi);
114 | } else {
115 | throw new Error(`Invalid status \'${status}\'`);
116 | }
117 |
118 | switch (status) {
119 | case undefined:
120 | break;
121 | case "1":
122 | case "on":
123 | case "yes":
124 | case "y":
125 | php.moduleEnable("xdebug", sapi);
126 | break;
127 | case "0":
128 | case "off":
129 | case "no":
130 | case "n":
131 | php.moduleDisable("xdebug", sapi);
132 | break;
133 | default:
134 | throw new Error(`Invalid status \'${status}\'`);
135 | }
136 |
137 | if (sapi === "fpm" || sapi === undefined) {
138 | console.log("Restarting PHP-FPM and NGINX");
139 | fpm.restart();
140 | }
141 |
142 | renderStatus();
143 |
144 | process.exit(0);
145 | });
146 |
147 | program
148 | .command("restart")
149 | .alias("r")
150 | .description("Restart PHP-FPM and NGINX")
151 | .action(() => {
152 | console.log("Restarting PHP-FPM and NGINX");
153 | fpm.restart();
154 | process.exit(0);
155 | });
156 |
157 | program.parse(process.argv);
158 |
159 | if (!program.args.length) renderStatus();
160 |
--------------------------------------------------------------------------------
/src/php.js:
--------------------------------------------------------------------------------
1 | const { execSync } = require("child_process");
2 |
3 | /**
4 | * Get currently active PHP version number
5 | *
6 | * @return {string}
7 | */
8 | const current = () =>
9 | execSync('php -v | head -n 1 | cut -d " " -f 2 | cut -f1-2 -d"."')
10 | .toString()
11 | .trim();
12 |
13 | /**
14 | * Get all installed PHP version numbers
15 | *
16 | * @return {Array.string}
17 | */
18 | const versions = () =>
19 | execSync("find /usr/bin -name 'php*.*' -type f | cut -b 13- | sort -g")
20 | .toString()
21 | .trim()
22 | .split("\n");
23 |
24 | /**
25 | * Switch default PHP Version
26 | *
27 | * @param {string} version PHP Version number
28 | *
29 | * @return {bool|string}
30 | */
31 | const use = version => {
32 | if (!/^\d\.\d$/.test(version) || !versions().includes(version)) {
33 | throw new Error(`Invalid version number "${version}"`);
34 | }
35 |
36 | if (version === current()) {
37 | return false;
38 | }
39 |
40 | execSync(
41 | `sudo /usr/bin/update-alternatives --set php /usr/bin/php${version}`
42 | );
43 |
44 | return current();
45 | };
46 |
47 | /**
48 | * Get the status of a PHP module
49 | *
50 | * @param {string} version PHP Version
51 | * @param {string} sapi SAPI name (cli or fpm)
52 | * @param {string} module PHP Module name
53 | *
54 | * @return {boolean}
55 | */
56 | const moduleStatus = (version, sapi, module) => {
57 | try {
58 | execSync(`phpquery -v ${version} -s ${sapi} -m ${module}`);
59 | return true;
60 | } catch (error) {
61 | return false;
62 | }
63 | };
64 |
65 | /**
66 | * Enable PHP Module
67 | *
68 | * @param {string} module
69 | * @param {string|undefined} sapi
70 | */
71 | const moduleEnable = (module, sapi) => {
72 | sapi = sapi ? `-s ${sapi}` : "";
73 | execSync(`sudo /usr/sbin/phpenmod ${sapi} ${module}`);
74 | };
75 |
76 | /**
77 | * Disable PHP Module
78 | *
79 | * @param {string} module
80 | * @param {string|undefined} sapi
81 | */
82 | const moduleDisable = (module, sapi) => {
83 | sapi = sapi ? `-s ${sapi}` : "";
84 | execSync(`sudo /usr/sbin/phpdismod ${sapi} ${module}`);
85 | };
86 |
87 | const moduleToggle = (module, sapi) => {
88 | const currentVersion = current();
89 |
90 | let cliStatus;
91 | let fpmStatus;
92 |
93 | if (sapi === "cli" || sapi === undefined) {
94 | cliStatus = moduleStatus(currentVersion, "cli", module);
95 | }
96 |
97 | if (sapi === "fpm" || sapi === undefined) {
98 | fpmStatus = moduleStatus(currentVersion, "fpm", module);
99 | }
100 |
101 | if (cliStatus === true || fpmStatus === true) {
102 | moduleDisable(module, sapi);
103 | return false;
104 | } else {
105 | moduleEnable(module, sapi);
106 | return true;
107 | }
108 | };
109 |
110 | module.exports = {
111 | current,
112 | versions,
113 | use,
114 | moduleStatus,
115 | moduleEnable,
116 | moduleDisable,
117 | moduleToggle
118 | };
119 |
--------------------------------------------------------------------------------